Fix black errors
[osm/osmclient.git] / osmclient / scripts / osm.py
1 # Copyright 2017-2018 Sandvine
2 # Copyright 2018 Telefonica
3 #
4 # All Rights Reserved.
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License"); you may
7 # not use this file except in compliance with the License. You may obtain
8 # a copy of the License at
9 #
10 # http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15 # License for the specific language governing permissions and limitations
16 # under the License.
17 """
18 OSM shell/cli
19 """
20
21 import click
22 from osmclient import client
23 from osmclient.common.exceptions import ClientException, NotFound
24 from osmclient.common.utils import validate_uuid4
25 from prettytable import PrettyTable
26 import yaml
27 import json
28 import time
29 import pycurl
30 import os
31 import textwrap
32 import pkg_resources
33 import logging
34 from datetime import datetime
35 from typing import Any, Dict
36
37
38 def wrap_text(text, width):
39 wrapper = textwrap.TextWrapper(width=width)
40 lines = text.splitlines()
41 return "\n".join(map(wrapper.fill, lines))
42
43
44 def trunc_text(text, length):
45 if len(text) > length:
46 return text[: (length - 3)] + "..."
47 else:
48 return text
49
50
51 def check_client_version(obj, what, version="sol005"):
52 """
53 Checks the version of the client object and raises error if it not the expected.
54
55 :param obj: the client object
56 :what: the function or command under evaluation (used when an error is raised)
57 :return: -
58 :raises ClientError: if the specified version does not match the client version
59 """
60 logger.debug("")
61 fullclassname = obj.__module__ + "." + obj.__class__.__name__
62 message = 'The following commands or options are only supported with the option "--sol005": {}'.format(
63 what
64 )
65 if version == "v1":
66 message = 'The following commands or options are not supported when using option "--sol005": {}'.format(
67 what
68 )
69 if fullclassname != "osmclient.{}.client.Client".format(version):
70 raise ClientException(message)
71 return
72
73
74 def get_project(project_list, item):
75 # project_list = ctx.obj.project.list()
76 item_project_list = item.get("_admin", {}).get("projects_read")
77 project_id = "None"
78 project_name = "None"
79 if item_project_list:
80 for p1 in item_project_list:
81 project_id = p1
82 for p2 in project_list:
83 if p2["_id"] == project_id:
84 project_name = p2["name"]
85 return project_id, project_name
86 return project_id, project_name
87
88
89 def get_vim_name(vim_list, vim_id):
90 vim_name = "-"
91 for v in vim_list:
92 if v["uuid"] == vim_id:
93 vim_name = v["name"]
94 break
95 return vim_name
96
97
98 def create_config(config_file, json_string):
99 """
100 Combines a YAML or JSON file with a JSON string into a Python3 structure
101 It loads the YAML or JSON file 'cfile' into a first dictionary.
102 It loads the JSON string into a second dictionary.
103 Then it updates the first dictionary with the info in the second dictionary.
104 If the field is present in both cfile and cdict, the field in cdict prevails.
105 If both cfile and cdict are None, it returns an empty dict (i.e. {})
106 """
107 config = {}
108 if config_file:
109 with open(config_file, "r") as cf:
110 config = yaml.safe_load(cf.read())
111 if json_string:
112 cdict = yaml.safe_load(json_string)
113 for k, v in cdict.items():
114 config[k] = v
115 return config
116
117
118 @click.group(
119 context_settings=dict(help_option_names=["-h", "--help"], max_content_width=160)
120 )
121 @click.option(
122 "--hostname",
123 default="127.0.0.1",
124 envvar="OSM_HOSTNAME",
125 help="hostname of server. " + "Also can set OSM_HOSTNAME in environment",
126 )
127 @click.option(
128 "--user",
129 default=None,
130 envvar="OSM_USER",
131 help="user (defaults to admin). " + "Also can set OSM_USER in environment",
132 )
133 @click.option(
134 "--password",
135 default=None,
136 envvar="OSM_PASSWORD",
137 help="password (defaults to admin). " + "Also can set OSM_PASSWORD in environment",
138 )
139 @click.option(
140 "--project",
141 default=None,
142 envvar="OSM_PROJECT",
143 help="project (defaults to admin). " + "Also can set OSM_PROJECT in environment",
144 )
145 @click.option(
146 "-v",
147 "--verbose",
148 count=True,
149 help="increase verbosity (-v INFO, -vv VERBOSE, -vvv DEBUG)",
150 )
151 @click.option("--all-projects", default=None, is_flag=True, help="include all projects")
152 @click.option(
153 "--public/--no-public",
154 default=None,
155 help="flag for public items (packages, instances, VIM accounts, etc.)",
156 )
157 @click.option(
158 "--project-domain-name",
159 "project_domain_name",
160 default=None,
161 envvar="OSM_PROJECT_DOMAIN_NAME",
162 help="project domain name for keystone authentication (default to None). "
163 + "Also can set OSM_PROJECT_DOMAIN_NAME in environment",
164 )
165 @click.option(
166 "--user-domain-name",
167 "user_domain_name",
168 default=None,
169 envvar="OSM_USER_DOMAIN_NAME",
170 help="user domain name for keystone authentication (default to None). "
171 + "Also can set OSM_USER_DOMAIN_NAME in environment",
172 )
173 @click.pass_context
174 def cli_osm(ctx, **kwargs):
175 global logger
176 hostname = kwargs.pop("hostname", None)
177 if hostname is None:
178 print(
179 (
180 "either hostname option or OSM_HOSTNAME "
181 + "environment variable needs to be specified"
182 )
183 )
184 exit(1)
185 # Remove None values
186 kwargs = {k: v for k, v in kwargs.items() if v is not None}
187 sol005 = os.getenv("OSM_SOL005", True)
188 ctx.obj = client.Client(host=hostname, sol005=sol005, **kwargs)
189 logger = logging.getLogger("osmclient")
190
191
192 ####################
193 # LIST operations
194 ####################
195
196
197 @cli_osm.command(name="ns-list", short_help="list all NS instances")
198 @click.option(
199 "--filter",
200 default=None,
201 multiple=True,
202 help="restricts the list to the NS instances matching the filter.",
203 )
204 @click.option(
205 "--long",
206 is_flag=True,
207 help="get more details of the NS (project, vim, deployment status, configuration status.",
208 )
209 @click.pass_context
210 def ns_list(ctx, filter, long):
211 """list all NS instances
212
213 \b
214 Options:
215 --filter filterExpr Restricts the list to the NS instances matching the filter
216
217 \b
218 filterExpr consists of one or more strings formatted according to "simpleFilterExpr",
219 concatenated using the "&" character:
220
221 \b
222 filterExpr := <simpleFilterExpr>["&"<simpleFilterExpr>]*
223 simpleFilterExpr := <attrName>["."<attrName>]*["."<op>]"="<value>[","<value>]*
224 op := "eq" | "neq" | "gt" | "lt" | "gte" | "lte" | "cont" | "ncont"
225 attrName := string
226 value := scalar value
227
228 \b
229 where:
230 * zero or more occurrences
231 ? zero or one occurrence
232 [] grouping of expressions to be used with ? and *
233 "" quotation marks for marking string constants
234 <> name separator
235
236 \b
237 "AttrName" is the name of one attribute in the data type that defines the representation
238 of the resource. The dot (".") character in "simpleFilterExpr" allows concatenation of
239 <attrName> entries to filter by attributes deeper in the hierarchy of a structured document.
240 "Op" stands for the comparison operator. If the expression has concatenated <attrName>
241 entries, it means that the operator "op" is applied to the attribute addressed by the last
242 <attrName> entry included in the concatenation. All simple filter expressions are combined
243 by the "AND" logical operator. In a concatenation of <attrName> entries in a <simpleFilterExpr>,
244 the rightmost "attrName" entry in a "simpleFilterExpr" is called "leaf attribute". The
245 concatenation of all "attrName" entries except the leaf attribute is called the "attribute
246 prefix". If an attribute referenced in an expression is an array, an object that contains a
247 corresponding array shall be considered to match the expression if any of the elements in the
248 array matches all expressions that have the same attribute prefix.
249
250 \b
251 Filter examples:
252 --filter admin-status=ENABLED
253 --filter nsd-ref=<NSD_NAME>
254 --filter nsd.vendor=<VENDOR>
255 --filter nsd.vendor=<VENDOR>&nsd-ref=<NSD_NAME>
256 --filter nsd.constituent-vnfd.vnfd-id-ref=<VNFD_NAME>
257 """
258
259 def summarize_deployment_status(status_dict):
260 # Nets
261 summary = ""
262 if not status_dict:
263 return summary
264 n_nets = 0
265 status_nets = {}
266 net_list = status_dict.get("nets", [])
267 for net in net_list:
268 n_nets += 1
269 if net["status"] not in status_nets:
270 status_nets[net["status"]] = 1
271 else:
272 status_nets[net["status"]] += 1
273 message = "Nets: "
274 for k, v in status_nets.items():
275 message += "{}:{},".format(k, v)
276 message += "TOTAL:{}".format(n_nets)
277 summary += "{}".format(message)
278 # VMs and VNFs
279 n_vms = 0
280 status_vms = {}
281 status_vnfs = {}
282 vnf_list = status_dict["vnfs"]
283 for vnf in vnf_list:
284 member_vnf_index = vnf["member_vnf_index"]
285 if member_vnf_index not in status_vnfs:
286 status_vnfs[member_vnf_index] = {}
287 for vm in vnf["vms"]:
288 n_vms += 1
289 if vm["status"] not in status_vms:
290 status_vms[vm["status"]] = 1
291 else:
292 status_vms[vm["status"]] += 1
293 if vm["status"] not in status_vnfs[member_vnf_index]:
294 status_vnfs[member_vnf_index][vm["status"]] = 1
295 else:
296 status_vnfs[member_vnf_index][vm["status"]] += 1
297 message = "VMs: "
298 for k, v in status_vms.items():
299 message += "{}:{},".format(k, v)
300 message += "TOTAL:{}".format(n_vms)
301 summary += "\n{}".format(message)
302 summary += "\nNFs:"
303 for k, v in status_vnfs.items():
304 total = 0
305 message = "\n {} VMs: ".format(k)
306 for k2, v2 in v.items():
307 message += "{}:{},".format(k2, v2)
308 total += v2
309 message += "TOTAL:{}".format(total)
310 summary += message
311 return summary
312
313 def summarize_config_status(ee_list):
314 summary = ""
315 if not ee_list:
316 return summary
317 n_ee = 0
318 status_ee = {}
319 for ee in ee_list:
320 n_ee += 1
321 if ee["elementType"] not in status_ee:
322 status_ee[ee["elementType"]] = {}
323 status_ee[ee["elementType"]][ee["status"]] = 1
324 continue
325 if ee["status"] in status_ee[ee["elementType"]]:
326 status_ee[ee["elementType"]][ee["status"]] += 1
327 else:
328 status_ee[ee["elementType"]][ee["status"]] = 1
329 for elementType in ["KDU", "VDU", "PDU", "VNF", "NS"]:
330 if elementType in status_ee:
331 message = ""
332 total = 0
333 for k, v in status_ee[elementType].items():
334 message += "{}:{},".format(k, v)
335 total += v
336 message += "TOTAL:{}\n".format(total)
337 summary += "{}: {}".format(elementType, message)
338 summary += "TOTAL Exec. Env.: {}".format(n_ee)
339 return summary
340
341 logger.debug("")
342 if filter:
343 check_client_version(ctx.obj, "--filter")
344 filter = "&".join(filter)
345 resp = ctx.obj.ns.list(filter)
346 else:
347 resp = ctx.obj.ns.list()
348 if long:
349 table = PrettyTable(
350 [
351 "ns instance name",
352 "id",
353 "date",
354 "ns state",
355 "current operation",
356 "error details",
357 "project",
358 "vim (inst param)",
359 "deployment status",
360 "configuration status",
361 ]
362 )
363 project_list = ctx.obj.project.list()
364 try:
365 vim_list = ctx.obj.vim.list()
366 except Exception:
367 vim_list = []
368 else:
369 table = PrettyTable(
370 [
371 "ns instance name",
372 "id",
373 "date",
374 "ns state",
375 "current operation",
376 "error details",
377 ]
378 )
379 for ns in resp:
380 fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
381 if fullclassname == "osmclient.sol005.client.Client":
382 nsr = ns
383 logger.debug("NS info: {}".format(nsr))
384 nsr_name = nsr["name"]
385 nsr_id = nsr["_id"]
386 date = datetime.fromtimestamp(nsr["create-time"]).strftime(
387 "%Y-%m-%dT%H:%M:%S"
388 )
389 ns_state = nsr.get("nsState", nsr["_admin"]["nsState"])
390 if long:
391 deployment_status = summarize_deployment_status(
392 nsr.get("deploymentStatus")
393 )
394 config_status = summarize_config_status(nsr.get("configurationStatus"))
395 project_id, project_name = get_project(project_list, nsr)
396 # project = '{} ({})'.format(project_name, project_id)
397 project = project_name
398 vim_id = nsr.get("datacenter")
399 vim_name = get_vim_name(vim_list, vim_id)
400
401 # vim = '{} ({})'.format(vim_name, vim_id)
402 vim = vim_name
403 if "currentOperation" in nsr:
404 current_operation = "{} ({})".format(
405 nsr["currentOperation"], nsr["currentOperationID"]
406 )
407 else:
408 current_operation = "{} ({})".format(
409 nsr["_admin"].get("current-operation", "-"),
410 nsr["_admin"]["nslcmop"],
411 )
412 error_details = "N/A"
413 if (
414 ns_state == "BROKEN"
415 or ns_state == "DEGRADED"
416 or ("currentOperation" not in nsr and nsr.get("errorDescription"))
417 ):
418 error_details = "{}\nDetail: {}".format(
419 nsr["errorDescription"], nsr["errorDetail"]
420 )
421 else:
422 nsopdata = ctx.obj.ns.get_opdata(ns["id"])
423 nsr = nsopdata["nsr:nsr"]
424 nsr_name = nsr["name-ref"]
425 nsr_id = nsr["ns-instance-config-ref"]
426 date = "-"
427 project = "-"
428 deployment_status = (
429 nsr["operational-status"]
430 if "operational-status" in nsr
431 else "Not found"
432 )
433 ns_state = deployment_status
434 config_status = nsr.get("config-status", "Not found")
435 current_operation = "Unknown"
436 error_details = nsr.get("detailed-status", "Not found")
437 if config_status == "config_not_needed":
438 config_status = "configured (no charms)"
439
440 if long:
441 table.add_row(
442 [
443 nsr_name,
444 nsr_id,
445 date,
446 ns_state,
447 current_operation,
448 wrap_text(text=error_details, width=40),
449 project,
450 vim,
451 deployment_status,
452 config_status,
453 ]
454 )
455 else:
456 table.add_row(
457 [
458 nsr_name,
459 nsr_id,
460 date,
461 ns_state,
462 current_operation,
463 wrap_text(text=error_details, width=40),
464 ]
465 )
466 table.align = "l"
467 print(table)
468 print('To get the history of all operations over a NS, run "osm ns-op-list NS_ID"')
469 print(
470 'For more details on the current operation, run "osm ns-op-show OPERATION_ID"'
471 )
472
473
474 def nsd_list(ctx, filter, long):
475 logger.debug("")
476 if filter:
477 check_client_version(ctx.obj, "--filter")
478 filter = "&".join(filter)
479 resp = ctx.obj.nsd.list(filter)
480 else:
481 resp = ctx.obj.nsd.list()
482 # print(yaml.safe_dump(resp))
483 fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
484 if fullclassname == "osmclient.sol005.client.Client":
485 if long:
486 table = PrettyTable(
487 [
488 "nsd name",
489 "id",
490 "onboarding state",
491 "operational state",
492 "usage state",
493 "date",
494 "last update",
495 ]
496 )
497 else:
498 table = PrettyTable(["nsd name", "id"])
499 for nsd in resp:
500 name = nsd.get("id", "-")
501 if long:
502 onb_state = nsd["_admin"].get("onboardingState", "-")
503 op_state = nsd["_admin"].get("operationalState", "-")
504 usage_state = nsd["_admin"].get("usageState", "-")
505 date = datetime.fromtimestamp(nsd["_admin"]["created"]).strftime(
506 "%Y-%m-%dT%H:%M:%S"
507 )
508 last_update = datetime.fromtimestamp(
509 nsd["_admin"]["modified"]
510 ).strftime("%Y-%m-%dT%H:%M:%S")
511 table.add_row(
512 [
513 name,
514 nsd["_id"],
515 onb_state,
516 op_state,
517 usage_state,
518 date,
519 last_update,
520 ]
521 )
522 else:
523 table.add_row([name, nsd["_id"]])
524 else:
525 table = PrettyTable(["nsd name", "id"])
526 for nsd in resp:
527 table.add_row([nsd["name"], nsd["id"]])
528 table.align = "l"
529 print(table)
530
531
532 @cli_osm.command(name="nsd-list", short_help="list all NS packages")
533 @click.option(
534 "--filter",
535 default=None,
536 multiple=True,
537 help="restricts the list to the NSD/NSpkg matching the filter",
538 )
539 @click.option("--long", is_flag=True, help="get more details")
540 @click.pass_context
541 def nsd_list1(ctx, filter, long):
542 """list all NSD/NS pkg in the system"""
543 logger.debug("")
544 nsd_list(ctx, filter, long)
545
546
547 @cli_osm.command(name="nspkg-list", short_help="list all NS packages")
548 @click.option(
549 "--filter",
550 default=None,
551 multiple=True,
552 help="restricts the list to the NSD/NSpkg matching the filter",
553 )
554 @click.option("--long", is_flag=True, help="get more details")
555 @click.pass_context
556 def nsd_list2(ctx, filter, long):
557 """list all NS packages"""
558 logger.debug("")
559 nsd_list(ctx, filter, long)
560
561
562 def pkg_repo_list(ctx, pkgtype, filter, repo, long):
563 resp = ctx.obj.osmrepo.pkg_list(pkgtype, filter, repo)
564 if long:
565 table = PrettyTable(
566 ["nfpkg name", "vendor", "version", "latest", "description", "repository"]
567 )
568 else:
569 table = PrettyTable(["nfpkg name", "repository"])
570 for vnfd in resp:
571 name = vnfd.get("id", vnfd.get("name", "-"))
572 repository = vnfd.get("repository")
573 if long:
574 vendor = vnfd.get("provider", vnfd.get("vendor"))
575 version = vnfd.get("version")
576 description = vnfd.get("description")
577 latest = vnfd.get("latest")
578 table.add_row([name, vendor, version, latest, description, repository])
579 else:
580 table.add_row([name, repository])
581 table.align = "l"
582 print(table)
583
584
585 def vnfd_list(ctx, nf_type, filter, long):
586 logger.debug("")
587 if nf_type:
588 check_client_version(ctx.obj, "--nf_type")
589 elif filter:
590 check_client_version(ctx.obj, "--filter")
591 if filter:
592 filter = "&".join(filter)
593 if nf_type:
594 if nf_type == "vnf":
595 nf_filter = "_admin.type=vnfd"
596 elif nf_type == "pnf":
597 nf_filter = "_admin.type=pnfd"
598 elif nf_type == "hnf":
599 nf_filter = "_admin.type=hnfd"
600 else:
601 raise ClientException(
602 'wrong value for "--nf_type" option, allowed values: vnf, pnf, hnf'
603 )
604 if filter:
605 filter = "{}&{}".format(nf_filter, filter)
606 else:
607 filter = nf_filter
608 if filter:
609 resp = ctx.obj.vnfd.list(filter)
610 else:
611 resp = ctx.obj.vnfd.list()
612 # print(yaml.safe_dump(resp))
613 fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
614 if fullclassname == "osmclient.sol005.client.Client":
615 if long:
616 table = PrettyTable(
617 [
618 "nfpkg name",
619 "id",
620 "desc type",
621 "vendor",
622 "version",
623 "onboarding state",
624 "operational state",
625 "usage state",
626 "date",
627 "last update",
628 ]
629 )
630 else:
631 table = PrettyTable(["nfpkg name", "id", "desc type"])
632 for vnfd in resp:
633 name = vnfd.get("id", vnfd.get("name", "-"))
634 descriptor_type = "sol006" if "product-name" in vnfd else "rel8"
635 if long:
636 onb_state = vnfd["_admin"].get("onboardingState", "-")
637 op_state = vnfd["_admin"].get("operationalState", "-")
638 vendor = vnfd.get("provider", vnfd.get("vendor"))
639 version = vnfd.get("version")
640 usage_state = vnfd["_admin"].get("usageState", "-")
641 date = datetime.fromtimestamp(vnfd["_admin"]["created"]).strftime(
642 "%Y-%m-%dT%H:%M:%S"
643 )
644 last_update = datetime.fromtimestamp(
645 vnfd["_admin"]["modified"]
646 ).strftime("%Y-%m-%dT%H:%M:%S")
647 table.add_row(
648 [
649 name,
650 vnfd["_id"],
651 descriptor_type,
652 vendor,
653 version,
654 onb_state,
655 op_state,
656 usage_state,
657 date,
658 last_update,
659 ]
660 )
661 else:
662 table.add_row([name, vnfd["_id"], descriptor_type])
663 else:
664 table = PrettyTable(["nfpkg name", "id"])
665 for vnfd in resp:
666 table.add_row([vnfd["name"], vnfd["id"]])
667 table.align = "l"
668 print(table)
669
670
671 @cli_osm.command(name="vnfd-list", short_help="list all xNF packages (VNF, HNF, PNF)")
672 @click.option("--nf_type", help="type of NF (vnf, pnf, hnf)")
673 @click.option(
674 "--filter",
675 default=None,
676 multiple=True,
677 help="restricts the list to the NF pkg matching the filter",
678 )
679 @click.option("--long", is_flag=True, help="get more details")
680 @click.pass_context
681 def vnfd_list1(ctx, nf_type, filter, long):
682 """list all xNF packages (VNF, HNF, PNF)"""
683 logger.debug("")
684 vnfd_list(ctx, nf_type, filter, long)
685
686
687 @cli_osm.command(name="vnfpkg-list", short_help="list all xNF packages (VNF, HNF, PNF)")
688 @click.option("--nf_type", help="type of NF (vnf, pnf, hnf)")
689 @click.option(
690 "--filter",
691 default=None,
692 multiple=True,
693 help="restricts the list to the NFpkg matching the filter",
694 )
695 @click.option("--long", is_flag=True, help="get more details")
696 @click.pass_context
697 def vnfd_list2(ctx, nf_type, filter, long):
698 """list all xNF packages (VNF, HNF, PNF)"""
699 logger.debug("")
700 vnfd_list(ctx, nf_type, filter, long)
701
702
703 @cli_osm.command(name="nfpkg-list", short_help="list all xNF packages (VNF, HNF, PNF)")
704 @click.option("--nf_type", help="type of NF (vnf, pnf, hnf)")
705 @click.option(
706 "--filter",
707 default=None,
708 multiple=True,
709 help="restricts the list to the NFpkg matching the filter",
710 )
711 @click.option("--long", is_flag=True, help="get more details")
712 @click.pass_context
713 def nfpkg_list(ctx, nf_type, filter, long):
714 """list all xNF packages (VNF, HNF, PNF)"""
715 logger.debug("")
716 # try:
717 check_client_version(ctx.obj, ctx.command.name)
718 vnfd_list(ctx, nf_type, filter, long)
719 # except ClientException as e:
720 # print(str(e))
721 # exit(1)
722
723
724 @cli_osm.command(
725 name="vnfpkg-repo-list", short_help="list all xNF from OSM repositories"
726 )
727 @click.option(
728 "--filter",
729 default=None,
730 multiple=True,
731 help="restricts the list to the NFpkg matching the filter",
732 )
733 @click.option(
734 "--repo", default=None, help="restricts the list to a particular OSM repository"
735 )
736 @click.option("--long", is_flag=True, help="get more details")
737 @click.pass_context
738 def nfpkg_repo_list1(ctx, filter, repo, long):
739 """list xNF packages from OSM repositories"""
740 pkgtype = "vnf"
741 pkg_repo_list(ctx, pkgtype, filter, repo, long)
742
743
744 @cli_osm.command(
745 name="nfpkg-repo-list", short_help="list all xNF from OSM repositories"
746 )
747 @click.option(
748 "--filter",
749 default=None,
750 multiple=True,
751 help="restricts the list to the NFpkg matching the filter",
752 )
753 @click.option(
754 "--repo", default=None, help="restricts the list to a particular OSM repository"
755 )
756 @click.option("--long", is_flag=True, help="get more details")
757 @click.pass_context
758 def nfpkg_repo_list2(ctx, filter, repo, long):
759 """list xNF packages from OSM repositories"""
760 pkgtype = "vnf"
761 pkg_repo_list(ctx, pkgtype, filter, repo, long)
762
763
764 def vnf_list(ctx, ns, filter, long):
765 # try:
766 if ns or filter:
767 if ns:
768 check_client_version(ctx.obj, "--ns")
769 if filter:
770 filter = "&".join(filter)
771 check_client_version(ctx.obj, "--filter")
772 resp = ctx.obj.vnf.list(ns, filter)
773 else:
774 resp = ctx.obj.vnf.list()
775 # except ClientException as e:
776 # print(str(e))
777 # exit(1)
778 fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
779 if fullclassname == "osmclient.sol005.client.Client":
780 field_names = [
781 "vnf id",
782 "name",
783 "ns id",
784 "vnf member index",
785 "vnfd name",
786 "vim account id",
787 "ip address",
788 ]
789 if long:
790 field_names = [
791 "vnf id",
792 "name",
793 "ns id",
794 "vnf member index",
795 "vnfd name",
796 "vim account id",
797 "ip address",
798 "date",
799 "last update",
800 ]
801 table = PrettyTable(field_names)
802 for vnfr in resp:
803 name = vnfr["name"] if "name" in vnfr else "-"
804 new_row = [
805 vnfr["_id"],
806 name,
807 vnfr["nsr-id-ref"],
808 vnfr["member-vnf-index-ref"],
809 vnfr["vnfd-ref"],
810 vnfr["vim-account-id"],
811 vnfr["ip-address"],
812 ]
813 if long:
814 date = datetime.fromtimestamp(vnfr["_admin"]["created"]).strftime(
815 "%Y-%m-%dT%H:%M:%S"
816 )
817 last_update = datetime.fromtimestamp(
818 vnfr["_admin"]["modified"]
819 ).strftime("%Y-%m-%dT%H:%M:%S")
820 new_row.extend([date, last_update])
821 table.add_row(new_row)
822 else:
823 table = PrettyTable(["vnf name", "id", "operational status", "config status"])
824 for vnfr in resp:
825 if "mgmt-interface" not in vnfr:
826 vnfr["mgmt-interface"] = {}
827 vnfr["mgmt-interface"]["ip-address"] = None
828 table.add_row(
829 [
830 vnfr["name"],
831 vnfr["id"],
832 vnfr["operational-status"],
833 vnfr["config-status"],
834 ]
835 )
836 table.align = "l"
837 print(table)
838
839
840 @cli_osm.command(name="vnf-list", short_help="list all NF instances")
841 @click.option(
842 "--ns", default=None, help="NS instance id or name to restrict the NF list"
843 )
844 @click.option(
845 "--filter",
846 default=None,
847 multiple=True,
848 help="restricts the list to the NF instances matching the filter.",
849 )
850 @click.option("--long", is_flag=True, help="get more details")
851 @click.pass_context
852 def vnf_list1(ctx, ns, filter, long):
853 """list all NF instances"""
854 logger.debug("")
855 vnf_list(ctx, ns, filter, long)
856
857
858 @cli_osm.command(name="nsd-repo-list", short_help="list all NS from OSM repositories")
859 @click.option(
860 "--filter",
861 default=None,
862 multiple=True,
863 help="restricts the list to the NS matching the filter",
864 )
865 @click.option(
866 "--repo", default=None, help="restricts the list to a particular OSM repository"
867 )
868 @click.option("--long", is_flag=True, help="get more details")
869 @click.pass_context
870 def nspkg_repo_list(ctx, filter, repo, long):
871 """list xNF packages from OSM repositories"""
872 pkgtype = "ns"
873 pkg_repo_list(ctx, pkgtype, filter, repo, long)
874
875
876 @cli_osm.command(name="nspkg-repo-list", short_help="list all NS from OSM repositories")
877 @click.option(
878 "--filter",
879 default=None,
880 multiple=True,
881 help="restricts the list to the NS matching the filter",
882 )
883 @click.option(
884 "--repo", default=None, help="restricts the list to a particular OSM repository"
885 )
886 @click.option("--long", is_flag=True, help="get more details")
887 @click.pass_context
888 def nspkg_repo_list2(ctx, filter, repo, long):
889 """list xNF packages from OSM repositories"""
890 pkgtype = "ns"
891 pkg_repo_list(ctx, pkgtype, filter, repo, long)
892
893
894 @cli_osm.command(name="nf-list", short_help="list all NF instances")
895 @click.option(
896 "--ns", default=None, help="NS instance id or name to restrict the NF list"
897 )
898 @click.option(
899 "--filter",
900 default=None,
901 multiple=True,
902 help="restricts the list to the NF instances matching the filter.",
903 )
904 @click.option("--long", is_flag=True, help="get more details")
905 @click.pass_context
906 def nf_list(ctx, ns, filter, long):
907 """list all NF instances
908
909 \b
910 Options:
911 --ns TEXT NS instance id or name to restrict the VNF list
912 --filter filterExpr Restricts the list to the VNF instances matching the filter
913
914 \b
915 filterExpr consists of one or more strings formatted according to "simpleFilterExpr",
916 concatenated using the "&" character:
917
918 \b
919 filterExpr := <simpleFilterExpr>["&"<simpleFilterExpr>]*
920 simpleFilterExpr := <attrName>["."<attrName>]*["."<op>]"="<value>[","<value>]*
921 op := "eq" | "neq" | "gt" | "lt" | "gte" | "lte" | "cont" | "ncont"
922 attrName := string
923 value := scalar value
924
925 \b
926 where:
927 * zero or more occurrences
928 ? zero or one occurrence
929 [] grouping of expressions to be used with ? and *
930 "" quotation marks for marking string constants
931 <> name separator
932
933 \b
934 "AttrName" is the name of one attribute in the data type that defines the representation
935 of the resource. The dot (".") character in "simpleFilterExpr" allows concatenation of
936 <attrName> entries to filter by attributes deeper in the hierarchy of a structured document.
937 "Op" stands for the comparison operator. If the expression has concatenated <attrName>
938 entries, it means that the operator "op" is applied to the attribute addressed by the last
939 <attrName> entry included in the concatenation. All simple filter expressions are combined
940 by the "AND" logical operator. In a concatenation of <attrName> entries in a <simpleFilterExpr>,
941 the rightmost "attrName" entry in a "simpleFilterExpr" is called "leaf attribute". The
942 concatenation of all "attrName" entries except the leaf attribute is called the "attribute
943 prefix". If an attribute referenced in an expression is an array, an object that contains a
944 corresponding array shall be considered to match the expression if any of the elements in the
945 array matches all expressions that have the same attribute prefix.
946
947 \b
948 Filter examples:
949 --filter vim-account-id=<VIM_ACCOUNT_ID>
950 --filter vnfd-ref=<VNFD_NAME>
951 --filter vdur.ip-address=<IP_ADDRESS>
952 --filter vnfd-ref=<VNFD_NAME>,vdur.ip-address=<IP_ADDRESS>
953 """
954 logger.debug("")
955 vnf_list(ctx, ns, filter, long)
956
957
958 @cli_osm.command(
959 name="ns-op-list", short_help="shows the history of operations over a NS instance"
960 )
961 @click.argument("name")
962 @click.option(
963 "--long", is_flag=True, help="get more details of the NS operation (date, )."
964 )
965 @click.pass_context
966 def ns_op_list(ctx, name, long):
967 """shows the history of operations over a NS instance
968
969 NAME: name or ID of the NS instance
970 """
971
972 def formatParams(params):
973 if params["lcmOperationType"] == "instantiate":
974 params.pop("nsDescription")
975 params.pop("nsName")
976 params.pop("nsdId")
977 params.pop("nsr_id")
978 elif params["lcmOperationType"] == "action":
979 params.pop("primitive")
980 params.pop("lcmOperationType")
981 params.pop("nsInstanceId")
982 return params
983
984 logger.debug("")
985 # try:
986 check_client_version(ctx.obj, ctx.command.name)
987 resp = ctx.obj.ns.list_op(name)
988 # except ClientException as e:
989 # print(str(e))
990 # exit(1)
991
992 if long:
993 table = PrettyTable(
994 [
995 "id",
996 "operation",
997 "action_name",
998 "operation_params",
999 "status",
1000 "date",
1001 "last update",
1002 "detail",
1003 ]
1004 )
1005 else:
1006 table = PrettyTable(
1007 ["id", "operation", "action_name", "status", "date", "detail"]
1008 )
1009
1010 # print(yaml.safe_dump(resp))
1011 for op in resp:
1012 action_name = "N/A"
1013 if op["lcmOperationType"] == "action":
1014 action_name = op["operationParams"]["primitive"]
1015 detail = "-"
1016 if op["operationState"] == "PROCESSING":
1017 if op["queuePosition"] is not None and op["queuePosition"] > 0:
1018 detail = "In queue. Current position: {}".format(op["queuePosition"])
1019 elif op["lcmOperationType"] in ("instantiate", "terminate"):
1020 if op["stage"]:
1021 detail = op["stage"]
1022 elif op["operationState"] in ("FAILED", "FAILED_TEMP"):
1023 detail = op.get("errorMessage", "-")
1024 date = datetime.fromtimestamp(op["startTime"]).strftime("%Y-%m-%dT%H:%M:%S")
1025 last_update = datetime.fromtimestamp(op["statusEnteredTime"]).strftime(
1026 "%Y-%m-%dT%H:%M:%S"
1027 )
1028 if long:
1029 table.add_row(
1030 [
1031 op["id"],
1032 op["lcmOperationType"],
1033 action_name,
1034 wrap_text(
1035 text=json.dumps(formatParams(op["operationParams"]), indent=2),
1036 width=50,
1037 ),
1038 op["operationState"],
1039 date,
1040 last_update,
1041 wrap_text(text=detail, width=50),
1042 ]
1043 )
1044 else:
1045 table.add_row(
1046 [
1047 op["id"],
1048 op["lcmOperationType"],
1049 action_name,
1050 op["operationState"],
1051 date,
1052 wrap_text(text=detail or "", width=50),
1053 ]
1054 )
1055 table.align = "l"
1056 print(table)
1057
1058
1059 def nsi_list(ctx, filter):
1060 """list all Network Slice Instances"""
1061 logger.debug("")
1062 # try:
1063 check_client_version(ctx.obj, ctx.command.name)
1064 if filter:
1065 filter = "&".join(filter)
1066 resp = ctx.obj.nsi.list(filter)
1067 # except ClientException as e:
1068 # print(str(e))
1069 # exit(1)
1070 table = PrettyTable(
1071 [
1072 "netslice instance name",
1073 "id",
1074 "operational status",
1075 "config status",
1076 "detailed status",
1077 ]
1078 )
1079 for nsi in resp:
1080 nsi_name = nsi["name"]
1081 nsi_id = nsi["_id"]
1082 opstatus = (
1083 nsi["operational-status"] if "operational-status" in nsi else "Not found"
1084 )
1085 configstatus = nsi["config-status"] if "config-status" in nsi else "Not found"
1086 detailed_status = (
1087 nsi["detailed-status"] if "detailed-status" in nsi else "Not found"
1088 )
1089 if configstatus == "config_not_needed":
1090 configstatus = "configured (no charms)"
1091 table.add_row([nsi_name, nsi_id, opstatus, configstatus, detailed_status])
1092 table.align = "l"
1093 print(table)
1094
1095
1096 @cli_osm.command(name="nsi-list", short_help="list all Network Slice Instances (NSI)")
1097 @click.option(
1098 "--filter",
1099 default=None,
1100 multiple=True,
1101 help="restricts the list to the Network Slice Instances matching the filter",
1102 )
1103 @click.pass_context
1104 def nsi_list1(ctx, filter):
1105 """list all Network Slice Instances (NSI)"""
1106 logger.debug("")
1107 nsi_list(ctx, filter)
1108
1109
1110 @cli_osm.command(
1111 name="netslice-instance-list", short_help="list all Network Slice Instances (NSI)"
1112 )
1113 @click.option(
1114 "--filter",
1115 default=None,
1116 multiple=True,
1117 help="restricts the list to the Network Slice Instances matching the filter",
1118 )
1119 @click.pass_context
1120 def nsi_list2(ctx, filter):
1121 """list all Network Slice Instances (NSI)"""
1122 logger.debug("")
1123 nsi_list(ctx, filter)
1124
1125
1126 def nst_list(ctx, filter):
1127 logger.debug("")
1128 # try:
1129 check_client_version(ctx.obj, ctx.command.name)
1130 if filter:
1131 filter = "&".join(filter)
1132 resp = ctx.obj.nst.list(filter)
1133 # except ClientException as e:
1134 # print(str(e))
1135 # exit(1)
1136 # print(yaml.safe_dump(resp))
1137 table = PrettyTable(["nst name", "id"])
1138 for nst in resp:
1139 name = nst["name"] if "name" in nst else "-"
1140 table.add_row([name, nst["_id"]])
1141 table.align = "l"
1142 print(table)
1143
1144
1145 @cli_osm.command(name="nst-list", short_help="list all Network Slice Templates (NST)")
1146 @click.option(
1147 "--filter",
1148 default=None,
1149 multiple=True,
1150 help="restricts the list to the NST matching the filter",
1151 )
1152 @click.pass_context
1153 def nst_list1(ctx, filter):
1154 """list all Network Slice Templates (NST) in the system"""
1155 logger.debug("")
1156 nst_list(ctx, filter)
1157
1158
1159 @cli_osm.command(
1160 name="netslice-template-list", short_help="list all Network Slice Templates (NST)"
1161 )
1162 @click.option(
1163 "--filter",
1164 default=None,
1165 multiple=True,
1166 help="restricts the list to the NST matching the filter",
1167 )
1168 @click.pass_context
1169 def nst_list2(ctx, filter):
1170 """list all Network Slice Templates (NST) in the system"""
1171 logger.debug("")
1172 nst_list(ctx, filter)
1173
1174
1175 def nsi_op_list(ctx, name):
1176 logger.debug("")
1177 # try:
1178 check_client_version(ctx.obj, ctx.command.name)
1179 resp = ctx.obj.nsi.list_op(name)
1180 # except ClientException as e:
1181 # print(str(e))
1182 # exit(1)
1183 table = PrettyTable(["id", "operation", "status"])
1184 for op in resp:
1185 table.add_row([op["id"], op["lcmOperationType"], op["operationState"]])
1186 table.align = "l"
1187 print(table)
1188
1189
1190 @cli_osm.command(
1191 name="nsi-op-list",
1192 short_help="shows the history of operations over a Network Slice Instance (NSI)",
1193 )
1194 @click.argument("name")
1195 @click.pass_context
1196 def nsi_op_list1(ctx, name):
1197 """shows the history of operations over a Network Slice Instance (NSI)
1198
1199 NAME: name or ID of the Network Slice Instance
1200 """
1201 logger.debug("")
1202 nsi_op_list(ctx, name)
1203
1204
1205 @cli_osm.command(
1206 name="netslice-instance-op-list",
1207 short_help="shows the history of operations over a Network Slice Instance (NSI)",
1208 )
1209 @click.argument("name")
1210 @click.pass_context
1211 def nsi_op_list2(ctx, name):
1212 """shows the history of operations over a Network Slice Instance (NSI)
1213
1214 NAME: name or ID of the Network Slice Instance
1215 """
1216 logger.debug("")
1217 nsi_op_list(ctx, name)
1218
1219
1220 @cli_osm.command(name="pdu-list", short_help="list all Physical Deployment Units (PDU)")
1221 @click.option(
1222 "--filter",
1223 default=None,
1224 multiple=True,
1225 help="restricts the list to the Physical Deployment Units matching the filter",
1226 )
1227 @click.pass_context
1228 def pdu_list(ctx, filter):
1229 """list all Physical Deployment Units (PDU)"""
1230 logger.debug("")
1231 # try:
1232 check_client_version(ctx.obj, ctx.command.name)
1233 if filter:
1234 filter = "&".join(filter)
1235 resp = ctx.obj.pdu.list(filter)
1236 # except ClientException as e:
1237 # print(str(e))
1238 # exit(1)
1239 table = PrettyTable(["pdu name", "id", "type", "mgmt ip address"])
1240 for pdu in resp:
1241 pdu_name = pdu["name"]
1242 pdu_id = pdu["_id"]
1243 pdu_type = pdu["type"]
1244 pdu_ipaddress = "None"
1245 for iface in pdu["interfaces"]:
1246 if iface["mgmt"]:
1247 pdu_ipaddress = iface["ip-address"]
1248 break
1249 table.add_row([pdu_name, pdu_id, pdu_type, pdu_ipaddress])
1250 table.align = "l"
1251 print(table)
1252
1253
1254 ####################
1255 # SHOW operations
1256 ####################
1257
1258
1259 def nsd_show(ctx, name, literal):
1260 logger.debug("")
1261 # try:
1262 resp = ctx.obj.nsd.get(name)
1263 # resp = ctx.obj.nsd.get_individual(name)
1264 # except ClientException as e:
1265 # print(str(e))
1266 # exit(1)
1267
1268 if literal:
1269 print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
1270 return
1271
1272 table = PrettyTable(["field", "value"])
1273 for k, v in list(resp.items()):
1274 table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)])
1275 table.align = "l"
1276 print(table)
1277
1278
1279 @cli_osm.command(name="nsd-show", short_help="shows the details of a NS package")
1280 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1281 @click.argument("name")
1282 @click.pass_context
1283 def nsd_show1(ctx, name, literal):
1284 """shows the content of a NSD
1285
1286 NAME: name or ID of the NSD/NSpkg
1287 """
1288 logger.debug("")
1289 nsd_show(ctx, name, literal)
1290
1291
1292 @cli_osm.command(name="nspkg-show", short_help="shows the details of a NS package")
1293 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1294 @click.argument("name")
1295 @click.pass_context
1296 def nsd_show2(ctx, name, literal):
1297 """shows the content of a NSD
1298
1299 NAME: name or ID of the NSD/NSpkg
1300 """
1301 logger.debug("")
1302 nsd_show(ctx, name, literal)
1303
1304
1305 def vnfd_show(ctx, name, literal):
1306 logger.debug("")
1307 # try:
1308 resp = ctx.obj.vnfd.get(name)
1309 # resp = ctx.obj.vnfd.get_individual(name)
1310 # except ClientException as e:
1311 # print(str(e))
1312 # exit(1)
1313
1314 if literal:
1315 print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
1316 return
1317
1318 table = PrettyTable(["field", "value"])
1319 for k, v in list(resp.items()):
1320 table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)])
1321 table.align = "l"
1322 print(table)
1323
1324
1325 def pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal):
1326 logger.debug("")
1327 if filter:
1328 filter = "&".join(filter)
1329 # try:
1330 resp = ctx.obj.osmrepo.pkg_get(pkgtype, name, repo, version, filter)
1331
1332 if literal:
1333 print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
1334 return
1335 pkgtype += "d"
1336 catalog = pkgtype + "-catalog"
1337 full_catalog = pkgtype + ":" + catalog
1338 if resp.get(catalog):
1339 resp = resp.pop(catalog)[pkgtype][0]
1340 elif resp.get(full_catalog):
1341 resp = resp.pop(full_catalog)[pkgtype][0]
1342
1343 table = PrettyTable(["field", "value"])
1344 for k, v in list(resp.items()):
1345 table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)])
1346 table.align = "l"
1347 print(table)
1348
1349
1350 @cli_osm.command(name="vnfd-show", short_help="shows the details of a NF package")
1351 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1352 @click.argument("name")
1353 @click.pass_context
1354 def vnfd_show1(ctx, name, literal):
1355 """shows the content of a VNFD
1356
1357 NAME: name or ID of the VNFD/VNFpkg
1358 """
1359 logger.debug("")
1360 vnfd_show(ctx, name, literal)
1361
1362
1363 @cli_osm.command(name="vnfpkg-show", short_help="shows the details of a NF package")
1364 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1365 @click.argument("name")
1366 @click.pass_context
1367 def vnfd_show2(ctx, name, literal):
1368 """shows the content of a VNFD
1369
1370 NAME: name or ID of the VNFD/VNFpkg
1371 """
1372 logger.debug("")
1373 vnfd_show(ctx, name, literal)
1374
1375
1376 @cli_osm.command(
1377 name="vnfpkg-repo-show",
1378 short_help="shows the details of a NF package in an OSM repository",
1379 )
1380 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1381 @click.option("--repo", required=True, help="Repository name")
1382 @click.argument("name")
1383 @click.option("--filter", default=None, multiple=True, help="filter by fields")
1384 @click.option("--version", default="latest", help="package version")
1385 @click.pass_context
1386 def vnfd_show3(ctx, name, repo, version, literal=None, filter=None):
1387 """shows the content of a VNFD in a repository
1388
1389 NAME: name or ID of the VNFD/VNFpkg
1390 """
1391 pkgtype = "vnf"
1392 pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal)
1393
1394
1395 @cli_osm.command(
1396 name="nsd-repo-show",
1397 short_help="shows the details of a NS package in an OSM repository",
1398 )
1399 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1400 @click.option("--repo", required=True, help="Repository name")
1401 @click.argument("name")
1402 @click.option("--filter", default=None, multiple=True, help="filter by fields")
1403 @click.option("--version", default="latest", help="package version")
1404 @click.pass_context
1405 def nsd_repo_show(ctx, name, repo, version, literal=None, filter=None):
1406 """shows the content of a VNFD in a repository
1407
1408 NAME: name or ID of the VNFD/VNFpkg
1409 """
1410 pkgtype = "ns"
1411 pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal)
1412
1413
1414 @cli_osm.command(
1415 name="nspkg-repo-show",
1416 short_help="shows the details of a NS package in an OSM repository",
1417 )
1418 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1419 @click.option("--repo", required=True, help="Repository name")
1420 @click.argument("name")
1421 @click.option("--filter", default=None, multiple=True, help="filter by fields")
1422 @click.option("--version", default="latest", help="package version")
1423 @click.pass_context
1424 def nsd_repo_show2(ctx, name, repo, version, literal=None, filter=None):
1425 """shows the content of a VNFD in a repository
1426
1427 NAME: name or ID of the VNFD/VNFpkg
1428 """
1429 pkgtype = "ns"
1430 pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal)
1431
1432
1433 @cli_osm.command(name="nfpkg-show", short_help="shows the details of a NF package")
1434 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1435 @click.argument("name")
1436 @click.pass_context
1437 def nfpkg_show(ctx, name, literal):
1438 """shows the content of a NF Descriptor
1439
1440 NAME: name or ID of the NFpkg
1441 """
1442 logger.debug("")
1443 vnfd_show(ctx, name, literal)
1444
1445
1446 @cli_osm.command(
1447 name="nfpkg-repo-show",
1448 short_help="shows the details of a NF package in an OSM repository",
1449 )
1450 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1451 @click.option("--repo", required=True, help="Repository name")
1452 @click.argument("name")
1453 @click.option("--filter", default=None, multiple=True, help="filter by fields")
1454 @click.option("--version", default="latest", help="package version")
1455 @click.pass_context
1456 def vnfd_show4(ctx, name, repo, version, literal=None, filter=None):
1457 """shows the content of a VNFD in a repository
1458
1459 NAME: name or ID of the VNFD/VNFpkg
1460 """
1461 pkgtype = "vnf"
1462 pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal)
1463
1464
1465 @cli_osm.command(name="ns-show", short_help="shows the info of a NS instance")
1466 @click.argument("name")
1467 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1468 @click.option(
1469 "--filter",
1470 multiple=True,
1471 help="restricts the information to the fields in the filter",
1472 )
1473 @click.pass_context
1474 def ns_show(ctx, name, literal, filter):
1475 """shows the info of a NS instance
1476
1477 NAME: name or ID of the NS instance
1478 """
1479 logger.debug("")
1480 # try:
1481 ns = ctx.obj.ns.get(name)
1482 # except ClientException as e:
1483 # print(str(e))
1484 # exit(1)
1485
1486 if literal:
1487 print(yaml.safe_dump(ns, indent=4, default_flow_style=False))
1488 return
1489
1490 table = PrettyTable(["field", "value"])
1491
1492 for k, v in list(ns.items()):
1493 if not filter or k in filter:
1494 table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)])
1495
1496 fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
1497 if fullclassname != "osmclient.sol005.client.Client":
1498 nsopdata = ctx.obj.ns.get_opdata(ns["id"])
1499 nsr_optdata = nsopdata["nsr:nsr"]
1500 for k, v in list(nsr_optdata.items()):
1501 if not filter or k in filter:
1502 table.add_row([k, wrap_text(json.dumps(v, indent=2), width=100)])
1503 table.align = "l"
1504 print(table)
1505
1506
1507 @cli_osm.command(name="vnf-show", short_help="shows the info of a VNF instance")
1508 @click.argument("name")
1509 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1510 @click.option(
1511 "--filter",
1512 multiple=True,
1513 help="restricts the information to the fields in the filter",
1514 )
1515 @click.option("--kdu", default=None, help="KDU name (whose status will be shown)")
1516 @click.pass_context
1517 def vnf_show(ctx, name, literal, filter, kdu):
1518 """shows the info of a VNF instance
1519
1520 NAME: name or ID of the VNF instance
1521 """
1522
1523 def print_kdu_status(op_info_status):
1524 """print KDU status properly formatted"""
1525 try:
1526 op_status = yaml.safe_load(op_info_status)
1527 if (
1528 "namespace" in op_status
1529 and "info" in op_status
1530 and "last_deployed" in op_status["info"]
1531 and "status" in op_status["info"]
1532 and "code" in op_status["info"]["status"]
1533 and "resources" in op_status["info"]["status"]
1534 and "seconds" in op_status["info"]["last_deployed"]
1535 ):
1536 last_deployed_time = datetime.fromtimestamp(
1537 op_status["info"]["last_deployed"]["seconds"]
1538 ).strftime("%a %b %d %I:%M:%S %Y")
1539 print("LAST DEPLOYED: {}".format(last_deployed_time))
1540 print("NAMESPACE: {}".format(op_status["namespace"]))
1541 status_code = "UNKNOWN"
1542 if op_status["info"]["status"]["code"] == 1:
1543 status_code = "DEPLOYED"
1544 print("STATUS: {}".format(status_code))
1545 print()
1546 print("RESOURCES:")
1547 print(op_status["info"]["status"]["resources"])
1548 if "notes" in op_status["info"]["status"]:
1549 print("NOTES:")
1550 print(op_status["info"]["status"]["notes"])
1551 else:
1552 print(op_info_status)
1553 except Exception:
1554 print(op_info_status)
1555
1556 logger.debug("")
1557 if kdu:
1558 if literal:
1559 raise ClientException(
1560 '"--literal" option is incompatible with "--kdu" option'
1561 )
1562 if filter:
1563 raise ClientException(
1564 '"--filter" option is incompatible with "--kdu" option'
1565 )
1566
1567 # try:
1568 check_client_version(ctx.obj, ctx.command.name)
1569 resp = ctx.obj.vnf.get(name)
1570
1571 if kdu:
1572 ns_id = resp["nsr-id-ref"]
1573 op_data = {}
1574 op_data["member_vnf_index"] = resp["member-vnf-index-ref"]
1575 op_data["kdu_name"] = kdu
1576 op_data["primitive"] = "status"
1577 op_data["primitive_params"] = {}
1578 op_id = ctx.obj.ns.exec_op(ns_id, op_name="action", op_data=op_data, wait=False)
1579 t = 0
1580 while t < 30:
1581 op_info = ctx.obj.ns.get_op(op_id)
1582 if op_info["operationState"] == "COMPLETED":
1583 print_kdu_status(op_info["detailed-status"])
1584 return
1585 time.sleep(5)
1586 t += 5
1587 print("Could not determine KDU status")
1588 return
1589
1590 if literal:
1591 print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
1592 return
1593
1594 table = PrettyTable(["field", "value"])
1595 for k, v in list(resp.items()):
1596 if not filter or k in filter:
1597 table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)])
1598 table.align = "l"
1599 print(table)
1600 # except ClientException as e:
1601 # print(str(e))
1602 # exit(1)
1603
1604
1605 # @cli_osm.command(name='vnf-monitoring-show')
1606 # @click.argument('vnf_name')
1607 # @click.pass_context
1608 # def vnf_monitoring_show(ctx, vnf_name):
1609 # try:
1610 # check_client_version(ctx.obj, ctx.command.name, 'v1')
1611 # resp = ctx.obj.vnf.get_monitoring(vnf_name)
1612 # except ClientException as e:
1613 # print(str(e))
1614 # exit(1)
1615 #
1616 # table = PrettyTable(['vnf name', 'monitoring name', 'value', 'units'])
1617 # if resp is not None:
1618 # for monitor in resp:
1619 # table.add_row(
1620 # [vnf_name,
1621 # monitor['name'],
1622 # monitor['value-integer'],
1623 # monitor['units']])
1624 # table.align = 'l'
1625 # print(table)
1626
1627
1628 # @cli_osm.command(name='ns-monitoring-show')
1629 # @click.argument('ns_name')
1630 # @click.pass_context
1631 # def ns_monitoring_show(ctx, ns_name):
1632 # try:
1633 # check_client_version(ctx.obj, ctx.command.name, 'v1')
1634 # resp = ctx.obj.ns.get_monitoring(ns_name)
1635 # except ClientException as e:
1636 # print(str(e))
1637 # exit(1)
1638 #
1639 # table = PrettyTable(['vnf name', 'monitoring name', 'value', 'units'])
1640 # for key, val in list(resp.items()):
1641 # for monitor in val:
1642 # table.add_row(
1643 # [key,
1644 # monitor['name'],
1645 # monitor['value-integer'],
1646 # monitor['units']])
1647 # table.align = 'l'
1648 # print(table)
1649
1650
1651 @cli_osm.command(name="ns-op-show", short_help="shows the info of a NS operation")
1652 @click.argument("id")
1653 @click.option(
1654 "--filter",
1655 multiple=True,
1656 help="restricts the information to the fields in the filter",
1657 )
1658 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1659 @click.pass_context
1660 def ns_op_show(ctx, id, filter, literal):
1661 """shows the detailed info of a NS operation
1662
1663 ID: operation identifier
1664 """
1665 logger.debug("")
1666 # try:
1667 check_client_version(ctx.obj, ctx.command.name)
1668 op_info = ctx.obj.ns.get_op(id)
1669 # except ClientException as e:
1670 # print(str(e))
1671 # exit(1)
1672
1673 if literal:
1674 print(yaml.safe_dump(op_info, indent=4, default_flow_style=False))
1675 return
1676
1677 table = PrettyTable(["field", "value"])
1678 for k, v in list(op_info.items()):
1679 if not filter or k in filter:
1680 table.add_row([k, wrap_text(json.dumps(v, indent=2), 100)])
1681 table.align = "l"
1682 print(table)
1683
1684
1685 def nst_show(ctx, name, literal):
1686 logger.debug("")
1687 # try:
1688 check_client_version(ctx.obj, ctx.command.name)
1689 resp = ctx.obj.nst.get(name)
1690 # resp = ctx.obj.nst.get_individual(name)
1691 # except ClientException as e:
1692 # print(str(e))
1693 # exit(1)
1694
1695 if literal:
1696 print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
1697 return
1698
1699 table = PrettyTable(["field", "value"])
1700 for k, v in list(resp.items()):
1701 table.add_row([k, wrap_text(json.dumps(v, indent=2), 100)])
1702 table.align = "l"
1703 print(table)
1704
1705
1706 @cli_osm.command(
1707 name="nst-show", short_help="shows the content of a Network Slice Template (NST)"
1708 )
1709 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1710 @click.argument("name")
1711 @click.pass_context
1712 def nst_show1(ctx, name, literal):
1713 """shows the content of a Network Slice Template (NST)
1714
1715 NAME: name or ID of the NST
1716 """
1717 logger.debug("")
1718 nst_show(ctx, name, literal)
1719
1720
1721 @cli_osm.command(
1722 name="netslice-template-show",
1723 short_help="shows the content of a Network Slice Template (NST)",
1724 )
1725 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1726 @click.argument("name")
1727 @click.pass_context
1728 def nst_show2(ctx, name, literal):
1729 """shows the content of a Network Slice Template (NST)
1730
1731 NAME: name or ID of the NST
1732 """
1733 logger.debug("")
1734 nst_show(ctx, name, literal)
1735
1736
1737 def nsi_show(ctx, name, literal, filter):
1738 logger.debug("")
1739 # try:
1740 check_client_version(ctx.obj, ctx.command.name)
1741 nsi = ctx.obj.nsi.get(name)
1742 # except ClientException as e:
1743 # print(str(e))
1744 # exit(1)
1745
1746 if literal:
1747 print(yaml.safe_dump(nsi, indent=4, default_flow_style=False))
1748 return
1749
1750 table = PrettyTable(["field", "value"])
1751
1752 for k, v in list(nsi.items()):
1753 if not filter or k in filter:
1754 table.add_row([k, json.dumps(v, indent=2)])
1755
1756 table.align = "l"
1757 print(table)
1758
1759
1760 @cli_osm.command(
1761 name="nsi-show", short_help="shows the content of a Network Slice Instance (NSI)"
1762 )
1763 @click.argument("name")
1764 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1765 @click.option(
1766 "--filter",
1767 multiple=True,
1768 help="restricts the information to the fields in the filter",
1769 )
1770 @click.pass_context
1771 def nsi_show1(ctx, name, literal, filter):
1772 """shows the content of a Network Slice Instance (NSI)
1773
1774 NAME: name or ID of the Network Slice Instance
1775 """
1776 logger.debug("")
1777 nsi_show(ctx, name, literal, filter)
1778
1779
1780 @cli_osm.command(
1781 name="netslice-instance-show",
1782 short_help="shows the content of a Network Slice Instance (NSI)",
1783 )
1784 @click.argument("name")
1785 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1786 @click.option(
1787 "--filter",
1788 multiple=True,
1789 help="restricts the information to the fields in the filter",
1790 )
1791 @click.pass_context
1792 def nsi_show2(ctx, name, literal, filter):
1793 """shows the content of a Network Slice Instance (NSI)
1794
1795 NAME: name or ID of the Network Slice Instance
1796 """
1797 logger.debug("")
1798 nsi_show(ctx, name, literal, filter)
1799
1800
1801 def nsi_op_show(ctx, id, filter):
1802 logger.debug("")
1803 # try:
1804 check_client_version(ctx.obj, ctx.command.name)
1805 op_info = ctx.obj.nsi.get_op(id)
1806 # except ClientException as e:
1807 # print(str(e))
1808 # exit(1)
1809
1810 table = PrettyTable(["field", "value"])
1811 for k, v in list(op_info.items()):
1812 if not filter or k in filter:
1813 table.add_row([k, json.dumps(v, indent=2)])
1814 table.align = "l"
1815 print(table)
1816
1817
1818 @cli_osm.command(
1819 name="nsi-op-show",
1820 short_help="shows the info of an operation over a Network Slice Instance(NSI)",
1821 )
1822 @click.argument("id")
1823 @click.option(
1824 "--filter",
1825 multiple=True,
1826 help="restricts the information to the fields in the filter",
1827 )
1828 @click.pass_context
1829 def nsi_op_show1(ctx, id, filter):
1830 """shows the info of an operation over a Network Slice Instance(NSI)
1831
1832 ID: operation identifier
1833 """
1834 logger.debug("")
1835 nsi_op_show(ctx, id, filter)
1836
1837
1838 @cli_osm.command(
1839 name="netslice-instance-op-show",
1840 short_help="shows the info of an operation over a Network Slice Instance(NSI)",
1841 )
1842 @click.argument("id")
1843 @click.option(
1844 "--filter",
1845 multiple=True,
1846 help="restricts the information to the fields in the filter",
1847 )
1848 @click.pass_context
1849 def nsi_op_show2(ctx, id, filter):
1850 """shows the info of an operation over a Network Slice Instance(NSI)
1851
1852 ID: operation identifier
1853 """
1854 logger.debug("")
1855 nsi_op_show(ctx, id, filter)
1856
1857
1858 @cli_osm.command(
1859 name="pdu-show", short_help="shows the content of a Physical Deployment Unit (PDU)"
1860 )
1861 @click.argument("name")
1862 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1863 @click.option(
1864 "--filter",
1865 multiple=True,
1866 help="restricts the information to the fields in the filter",
1867 )
1868 @click.pass_context
1869 def pdu_show(ctx, name, literal, filter):
1870 """shows the content of a Physical Deployment Unit (PDU)
1871
1872 NAME: name or ID of the PDU
1873 """
1874 logger.debug("")
1875 # try:
1876 check_client_version(ctx.obj, ctx.command.name)
1877 pdu = ctx.obj.pdu.get(name)
1878 # except ClientException as e:
1879 # print(str(e))
1880 # exit(1)
1881
1882 if literal:
1883 print(yaml.safe_dump(pdu, indent=4, default_flow_style=False))
1884 return
1885
1886 table = PrettyTable(["field", "value"])
1887
1888 for k, v in list(pdu.items()):
1889 if not filter or k in filter:
1890 table.add_row([k, json.dumps(v, indent=2)])
1891
1892 table.align = "l"
1893 print(table)
1894
1895
1896 ####################
1897 # CREATE operations
1898 ####################
1899
1900
1901 def nsd_create(ctx, filename, overwrite, skip_charm_build, repo, vendor, version):
1902 logger.debug("")
1903 # try:
1904 check_client_version(ctx.obj, ctx.command.name)
1905 if repo:
1906 filename = ctx.obj.osmrepo.get_pkg("ns", filename, repo, vendor, version)
1907 ctx.obj.nsd.create(filename, overwrite=overwrite, skip_charm_build=skip_charm_build)
1908 # except ClientException as e:
1909 # print(str(e))
1910 # exit(1)
1911
1912
1913 @cli_osm.command(name="nsd-create", short_help="creates a new NSD/NSpkg")
1914 @click.argument("filename")
1915 @click.option(
1916 "--overwrite",
1917 "overwrite",
1918 default=None, # hidden=True,
1919 help="Deprecated. Use override",
1920 )
1921 @click.option(
1922 "--override",
1923 "overwrite",
1924 default=None,
1925 help="overrides fields in descriptor, format: "
1926 '"key1.key2...=value[;key3...=value;...]"',
1927 )
1928 @click.option(
1929 "--skip-charm-build",
1930 default=False,
1931 is_flag=True,
1932 help="The charm will not be compiled, it is assumed to already exist",
1933 )
1934 @click.option("--repo", default=None, help="[repository]: Repository name")
1935 @click.option("--vendor", default=None, help="[repository]: filter by vendor]")
1936 @click.option(
1937 "--version",
1938 default="latest",
1939 help="[repository]: filter by version. Default: latest",
1940 )
1941 @click.pass_context
1942 def nsd_create1(ctx, filename, overwrite, skip_charm_build, repo, vendor, version):
1943 """onboards a new NSpkg (alias of nspkg-create) (TO BE DEPRECATED)
1944
1945 \b
1946 FILENAME: NF Package tar.gz file, NF Descriptor YAML file or NF Package folder
1947 If FILENAME is a file (NF Package tar.gz or NF Descriptor YAML), it is onboarded.
1948 If FILENAME is an NF Package folder, it is built and then onboarded.
1949 """
1950 logger.debug("")
1951 nsd_create(
1952 ctx,
1953 filename,
1954 overwrite=overwrite,
1955 skip_charm_build=skip_charm_build,
1956 repo=repo,
1957 vendor=vendor,
1958 version=version,
1959 )
1960
1961
1962 @cli_osm.command(name="nspkg-create", short_help="creates a new NSD/NSpkg")
1963 @click.argument("filename")
1964 @click.option(
1965 "--overwrite",
1966 "overwrite",
1967 default=None, # hidden=True,
1968 help="Deprecated. Use override",
1969 )
1970 @click.option(
1971 "--override",
1972 "overwrite",
1973 default=None,
1974 help="overrides fields in descriptor, format: "
1975 '"key1.key2...=value[;key3...=value;...]"',
1976 )
1977 @click.option(
1978 "--skip-charm-build",
1979 default=False,
1980 is_flag=True,
1981 help="The charm will not be compiled, it is assumed to already exist",
1982 )
1983 @click.option("--repo", default=None, help="[repository]: Repository name")
1984 @click.option("--vendor", default=None, help="[repository]: filter by vendor]")
1985 @click.option(
1986 "--version",
1987 default="latest",
1988 help="[repository]: filter by version. Default: latest",
1989 )
1990 @click.pass_context
1991 def nsd_pkg_create(ctx, filename, overwrite, skip_charm_build, repo, vendor, version):
1992 """onboards a new NSpkg
1993 \b
1994 FILENAME: NF Package tar.gz file, NF Descriptor YAML file or NF Package folder
1995 If FILENAME is a file (NF Package tar.gz or NF Descriptor YAML), it is onboarded.
1996 If FILENAME is an NF Package folder, it is built and then onboarded.
1997 """
1998 logger.debug("")
1999 nsd_create(
2000 ctx,
2001 filename,
2002 overwrite=overwrite,
2003 skip_charm_build=skip_charm_build,
2004 repo=repo,
2005 vendor=vendor,
2006 version=version,
2007 )
2008
2009
2010 def vnfd_create(
2011 ctx,
2012 filename,
2013 overwrite,
2014 skip_charm_build,
2015 override_epa,
2016 override_nonepa,
2017 override_paravirt,
2018 repo,
2019 vendor,
2020 version,
2021 ):
2022 logger.debug("")
2023 # try:
2024 check_client_version(ctx.obj, ctx.command.name)
2025 if repo:
2026 filename = ctx.obj.osmrepo.get_pkg("vnf", filename, repo, vendor, version)
2027 ctx.obj.vnfd.create(
2028 filename,
2029 overwrite=overwrite,
2030 skip_charm_build=skip_charm_build,
2031 override_epa=override_epa,
2032 override_nonepa=override_nonepa,
2033 override_paravirt=override_paravirt,
2034 )
2035 # except ClientException as e:
2036 # print(str(e))
2037 # exit(1)
2038
2039
2040 @cli_osm.command(name="vnfd-create", short_help="creates a new VNFD/VNFpkg")
2041 @click.argument("filename")
2042 @click.option(
2043 "--overwrite", "overwrite", default=None, help="overwrite deprecated, use override"
2044 )
2045 @click.option(
2046 "--override",
2047 "overwrite",
2048 default=None,
2049 help="overrides fields in descriptor, format: "
2050 '"key1.key2...=value[;key3...=value;...]"',
2051 )
2052 @click.option(
2053 "--skip-charm-build",
2054 default=False,
2055 is_flag=True,
2056 help="The charm will not be compiled, it is assumed to already exist",
2057 )
2058 @click.option(
2059 "--override-epa",
2060 required=False,
2061 default=False,
2062 is_flag=True,
2063 help="adds guest-epa parameters to all VDU",
2064 )
2065 @click.option(
2066 "--override-nonepa",
2067 required=False,
2068 default=False,
2069 is_flag=True,
2070 help="removes all guest-epa parameters from all VDU",
2071 )
2072 @click.option(
2073 "--override-paravirt",
2074 required=False,
2075 default=False,
2076 is_flag=True,
2077 help="overrides all VDU interfaces to PARAVIRT",
2078 )
2079 @click.option("--repo", default=None, help="[repository]: Repository name")
2080 @click.option("--vendor", default=None, help="[repository]: filter by vendor]")
2081 @click.option(
2082 "--version",
2083 default="latest",
2084 help="[repository]: filter by version. Default: latest",
2085 )
2086 @click.pass_context
2087 def vnfd_create1(
2088 ctx,
2089 filename,
2090 overwrite,
2091 skip_charm_build,
2092 override_epa,
2093 override_nonepa,
2094 override_paravirt,
2095 repo,
2096 vendor,
2097 version,
2098 ):
2099 """creates a new VNFD/VNFpkg
2100 \b
2101 FILENAME: NF Package tar.gz file, NF Descriptor YAML file or NF Package folder
2102 If FILENAME is a file (NF Package tar.gz or NF Descriptor YAML), it is onboarded.
2103 If FILENAME is an NF Package folder, it is built and then onboarded.
2104 """
2105 logger.debug("")
2106 vnfd_create(
2107 ctx,
2108 filename,
2109 overwrite=overwrite,
2110 skip_charm_build=skip_charm_build,
2111 override_epa=override_epa,
2112 override_nonepa=override_nonepa,
2113 override_paravirt=override_paravirt,
2114 repo=repo,
2115 vendor=vendor,
2116 version=version,
2117 )
2118
2119
2120 @cli_osm.command(name="vnfpkg-create", short_help="creates a new VNFD/VNFpkg")
2121 @click.argument("filename")
2122 @click.option(
2123 "--overwrite",
2124 "overwrite",
2125 default=None, # hidden=True,
2126 help="Deprecated. Use override",
2127 )
2128 @click.option(
2129 "--override",
2130 "overwrite",
2131 default=None,
2132 help="overrides fields in descriptor, format: "
2133 '"key1.key2...=value[;key3...=value;...]"',
2134 )
2135 @click.option(
2136 "--skip-charm-build",
2137 default=False,
2138 is_flag=True,
2139 help="The charm will not be compiled, it is assumed to already exist",
2140 )
2141 @click.option(
2142 "--override-epa",
2143 required=False,
2144 default=False,
2145 is_flag=True,
2146 help="adds guest-epa parameters to all VDU",
2147 )
2148 @click.option(
2149 "--override-nonepa",
2150 required=False,
2151 default=False,
2152 is_flag=True,
2153 help="removes all guest-epa parameters from all VDU",
2154 )
2155 @click.option(
2156 "--override-paravirt",
2157 required=False,
2158 default=False,
2159 is_flag=True,
2160 help="overrides all VDU interfaces to PARAVIRT",
2161 )
2162 @click.option("--repo", default=None, help="[repository]: Repository name")
2163 @click.option("--vendor", default=None, help="[repository]: filter by vendor]")
2164 @click.option(
2165 "--version",
2166 default="latest",
2167 help="[repository]: filter by version. Default: latest",
2168 )
2169 @click.pass_context
2170 def vnfd_create2(
2171 ctx,
2172 filename,
2173 overwrite,
2174 skip_charm_build,
2175 override_epa,
2176 override_nonepa,
2177 override_paravirt,
2178 repo,
2179 vendor,
2180 version,
2181 ):
2182 """creates a new VNFD/VNFpkg
2183 \b
2184 FILENAME: NF Package tar.gz file, NF Descriptor YAML file or NF Package folder
2185 If FILENAME is a file (NF Package tar.gz or NF Descriptor YAML), it is onboarded.
2186 If FILENAME is an NF Package folder, it is built and then onboarded.
2187 """
2188 logger.debug("")
2189 vnfd_create(
2190 ctx,
2191 filename,
2192 overwrite=overwrite,
2193 skip_charm_build=skip_charm_build,
2194 override_epa=override_epa,
2195 override_nonepa=override_nonepa,
2196 override_paravirt=override_paravirt,
2197 repo=repo,
2198 vendor=vendor,
2199 version=version,
2200 )
2201
2202
2203 @cli_osm.command(name="nfpkg-create", short_help="creates a new NFpkg")
2204 @click.argument("filename")
2205 @click.option(
2206 "--overwrite",
2207 "overwrite",
2208 default=None, # hidden=True,
2209 help="Deprecated. Use override",
2210 )
2211 @click.option(
2212 "--override",
2213 "overwrite",
2214 default=None,
2215 help="overrides fields in descriptor, format: "
2216 '"key1.key2...=value[;key3...=value;...]"',
2217 )
2218 @click.option(
2219 "--skip-charm-build",
2220 default=False,
2221 is_flag=True,
2222 help="The charm will not be compiled, it is assumed to already exist",
2223 )
2224 @click.option(
2225 "--override-epa",
2226 required=False,
2227 default=False,
2228 is_flag=True,
2229 help="adds guest-epa parameters to all VDU",
2230 )
2231 @click.option(
2232 "--override-nonepa",
2233 required=False,
2234 default=False,
2235 is_flag=True,
2236 help="removes all guest-epa parameters from all VDU",
2237 )
2238 @click.option(
2239 "--override-paravirt",
2240 required=False,
2241 default=False,
2242 is_flag=True,
2243 help="overrides all VDU interfaces to PARAVIRT",
2244 )
2245 @click.option("--repo", default=None, help="[repository]: Repository name")
2246 @click.option("--vendor", default=None, help="[repository]: filter by vendor]")
2247 @click.option(
2248 "--version",
2249 default="latest",
2250 help="[repository]: filter by version. Default: latest",
2251 )
2252 @click.pass_context
2253 def nfpkg_create(
2254 ctx,
2255 filename,
2256 overwrite,
2257 skip_charm_build,
2258 override_epa,
2259 override_nonepa,
2260 override_paravirt,
2261 repo,
2262 vendor,
2263 version,
2264 ):
2265 """creates a new NFpkg
2266
2267 \b
2268 FILENAME: NF Package tar.gz file, NF Descriptor YAML file or NF Package folder
2269 If FILENAME is a file (NF Package tar.gz or NF Descriptor YAML), it is onboarded.
2270 If FILENAME is an NF Package folder, it is built and then onboarded.
2271 """
2272 logger.debug("")
2273 vnfd_create(
2274 ctx,
2275 filename,
2276 overwrite=overwrite,
2277 skip_charm_build=skip_charm_build,
2278 override_epa=override_epa,
2279 override_nonepa=override_nonepa,
2280 override_paravirt=override_paravirt,
2281 repo=repo,
2282 vendor=vendor,
2283 version=version,
2284 )
2285
2286
2287 @cli_osm.command(name="ns-create", short_help="creates a new Network Service instance")
2288 @click.option("--ns_name", prompt=True, help="name of the NS instance")
2289 @click.option("--nsd_name", prompt=True, help="name of the NS descriptor")
2290 @click.option(
2291 "--vim_account",
2292 prompt=True,
2293 help="default VIM account id or name for the deployment",
2294 )
2295 @click.option("--admin_status", default="ENABLED", help="administration status")
2296 @click.option(
2297 "--ssh_keys",
2298 default=None,
2299 help="comma separated list of public key files to inject to vnfs",
2300 )
2301 @click.option("--config", default=None, help="ns specific yaml configuration")
2302 @click.option("--config_file", default=None, help="ns specific yaml configuration file")
2303 @click.option(
2304 "--wait",
2305 required=False,
2306 default=False,
2307 is_flag=True,
2308 help="do not return the control immediately, but keep it "
2309 "until the operation is completed, or timeout",
2310 )
2311 @click.option("--timeout", default=None, help="ns deployment timeout")
2312 @click.pass_context
2313 def ns_create(
2314 ctx,
2315 nsd_name,
2316 ns_name,
2317 vim_account,
2318 admin_status,
2319 ssh_keys,
2320 config,
2321 config_file,
2322 wait,
2323 timeout,
2324 ):
2325 """creates a new NS instance"""
2326 logger.debug("")
2327 # try:
2328 if config_file:
2329 check_client_version(ctx.obj, "--config_file")
2330 if config:
2331 raise ClientException(
2332 '"--config" option is incompatible with "--config_file" option'
2333 )
2334 with open(config_file, "r") as cf:
2335 config = cf.read()
2336 ctx.obj.ns.create(
2337 nsd_name,
2338 ns_name,
2339 config=config,
2340 ssh_keys=ssh_keys,
2341 account=vim_account,
2342 wait=wait,
2343 timeout=timeout,
2344 )
2345 # except ClientException as e:
2346 # print(str(e))
2347 # exit(1)
2348
2349
2350 def nst_create(ctx, filename, overwrite):
2351 logger.debug("")
2352 # try:
2353 check_client_version(ctx.obj, ctx.command.name)
2354 ctx.obj.nst.create(filename, overwrite)
2355 # except ClientException as e:
2356 # print(str(e))
2357 # exit(1)
2358
2359
2360 @cli_osm.command(
2361 name="nst-create", short_help="creates a new Network Slice Template (NST)"
2362 )
2363 @click.argument("filename")
2364 @click.option(
2365 "--overwrite",
2366 "overwrite",
2367 default=None, # hidden=True,
2368 help="Deprecated. Use override",
2369 )
2370 @click.option(
2371 "--override",
2372 "overwrite",
2373 default=None,
2374 help="overrides fields in descriptor, format: "
2375 '"key1.key2...=value[;key3...=value;...]"',
2376 )
2377 @click.pass_context
2378 def nst_create1(ctx, filename, overwrite):
2379 """creates a new Network Slice Template (NST)
2380
2381 FILENAME: NST package folder, NST yaml file or NSTpkg tar.gz file
2382 """
2383 logger.debug("")
2384 nst_create(ctx, filename, overwrite)
2385
2386
2387 @cli_osm.command(
2388 name="netslice-template-create",
2389 short_help="creates a new Network Slice Template (NST)",
2390 )
2391 @click.argument("filename")
2392 @click.option(
2393 "--overwrite",
2394 "overwrite",
2395 default=None, # hidden=True,
2396 help="Deprecated. Use override",
2397 )
2398 @click.option(
2399 "--override",
2400 "overwrite",
2401 default=None,
2402 help="overrides fields in descriptor, format: "
2403 '"key1.key2...=value[;key3...=value;...]"',
2404 )
2405 @click.pass_context
2406 def nst_create2(ctx, filename, overwrite):
2407 """creates a new Network Slice Template (NST)
2408
2409 FILENAME: NST yaml file or NSTpkg tar.gz file
2410 """
2411 logger.debug("")
2412 nst_create(ctx, filename, overwrite)
2413
2414
2415 def nsi_create(
2416 ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file, wait
2417 ):
2418 """creates a new Network Slice Instance (NSI)"""
2419 logger.debug("")
2420 # try:
2421 check_client_version(ctx.obj, ctx.command.name)
2422 if config_file:
2423 if config:
2424 raise ClientException(
2425 '"--config" option is incompatible with "--config_file" option'
2426 )
2427 with open(config_file, "r") as cf:
2428 config = cf.read()
2429 ctx.obj.nsi.create(
2430 nst_name,
2431 nsi_name,
2432 config=config,
2433 ssh_keys=ssh_keys,
2434 account=vim_account,
2435 wait=wait,
2436 )
2437 # except ClientException as e:
2438 # print(str(e))
2439 # exit(1)
2440
2441
2442 @cli_osm.command(name="nsi-create", short_help="creates a new Network Slice Instance")
2443 @click.option("--nsi_name", prompt=True, help="name of the Network Slice Instance")
2444 @click.option("--nst_name", prompt=True, help="name of the Network Slice Template")
2445 @click.option(
2446 "--vim_account",
2447 prompt=True,
2448 help="default VIM account id or name for the deployment",
2449 )
2450 @click.option(
2451 "--ssh_keys", default=None, help="comma separated list of keys to inject to vnfs"
2452 )
2453 @click.option(
2454 "--config",
2455 default=None,
2456 help="Netslice specific yaml configuration:\n"
2457 "netslice_subnet: [\n"
2458 "id: TEXT, vim_account: TEXT,\n"
2459 "vnf: [member-vnf-index: TEXT, vim_account: TEXT]\n"
2460 "vld: [name: TEXT, vim-network-name: TEXT or DICT with vim_account, vim_net entries]\n"
2461 "additionalParamsForNsi: {param: value, ...}\n"
2462 "additionalParamsForsubnet: [{id: SUBNET_ID, additionalParamsForNs: {}, additionalParamsForVnf: {}}]\n"
2463 "],\n"
2464 "netslice-vld: [name: TEXT, vim-network-name: TEXT or DICT with vim_account, vim_net entries]",
2465 )
2466 @click.option(
2467 "--config_file", default=None, help="nsi specific yaml configuration file"
2468 )
2469 @click.option(
2470 "--wait",
2471 required=False,
2472 default=False,
2473 is_flag=True,
2474 help="do not return the control immediately, but keep it "
2475 "until the operation is completed, or timeout",
2476 )
2477 @click.pass_context
2478 def nsi_create1(
2479 ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file, wait
2480 ):
2481 """creates a new Network Slice Instance (NSI)"""
2482 logger.debug("")
2483 nsi_create(
2484 ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file, wait=wait
2485 )
2486
2487
2488 @cli_osm.command(
2489 name="netslice-instance-create", short_help="creates a new Network Slice Instance"
2490 )
2491 @click.option("--nsi_name", prompt=True, help="name of the Network Slice Instance")
2492 @click.option("--nst_name", prompt=True, help="name of the Network Slice Template")
2493 @click.option(
2494 "--vim_account",
2495 prompt=True,
2496 help="default VIM account id or name for the deployment",
2497 )
2498 @click.option(
2499 "--ssh_keys", default=None, help="comma separated list of keys to inject to vnfs"
2500 )
2501 @click.option(
2502 "--config",
2503 default=None,
2504 help="Netslice specific yaml configuration:\n"
2505 "netslice_subnet: [\n"
2506 "id: TEXT, vim_account: TEXT,\n"
2507 "vnf: [member-vnf-index: TEXT, vim_account: TEXT]\n"
2508 "vld: [name: TEXT, vim-network-name: TEXT or DICT with vim_account, vim_net entries]"
2509 "],\n"
2510 "netslice-vld: [name: TEXT, vim-network-name: TEXT or DICT with vim_account, vim_net entries]",
2511 )
2512 @click.option(
2513 "--config_file", default=None, help="nsi specific yaml configuration file"
2514 )
2515 @click.option(
2516 "--wait",
2517 required=False,
2518 default=False,
2519 is_flag=True,
2520 help="do not return the control immediately, but keep it "
2521 "until the operation is completed, or timeout",
2522 )
2523 @click.pass_context
2524 def nsi_create2(
2525 ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file, wait
2526 ):
2527 """creates a new Network Slice Instance (NSI)"""
2528 logger.debug("")
2529 nsi_create(
2530 ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file, wait=wait
2531 )
2532
2533
2534 @cli_osm.command(
2535 name="pdu-create", short_help="adds a new Physical Deployment Unit to the catalog"
2536 )
2537 @click.option("--name", help="name of the Physical Deployment Unit")
2538 @click.option("--pdu_type", help="type of PDU (e.g. router, firewall, FW001)")
2539 @click.option(
2540 "--interface",
2541 help="interface(s) of the PDU: name=<NAME>,mgmt=<true|false>,ip-address=<IP_ADDRESS>"
2542 + "[,type=<overlay|underlay>][,mac-address=<MAC_ADDRESS>][,vim-network-name=<VIM_NET_NAME>]",
2543 multiple=True,
2544 )
2545 @click.option("--description", help="human readable description")
2546 @click.option(
2547 "--vim_account",
2548 help="list of VIM accounts (in the same VIM) that can reach this PDU\n"
2549 + "The format for multiple VIMs is --vim_account <vim_account_id_1> "
2550 + "--vim_account <vim_account_id_2> ... --vim_account <vim_account_id_N>",
2551 multiple=True,
2552 )
2553 @click.option(
2554 "--descriptor_file",
2555 default=None,
2556 help="PDU descriptor file (as an alternative to using the other arguments)",
2557 )
2558 @click.pass_context
2559 def pdu_create(
2560 ctx, name, pdu_type, interface, description, vim_account, descriptor_file
2561 ):
2562 """creates a new Physical Deployment Unit (PDU)"""
2563 logger.debug("")
2564
2565 check_client_version(ctx.obj, ctx.command.name)
2566
2567 pdu = create_pdu_dictionary(
2568 name, pdu_type, interface, description, vim_account, descriptor_file
2569 )
2570 ctx.obj.pdu.create(pdu)
2571
2572
2573 ########################
2574 # UPDATE PDU operation #
2575 ########################
2576
2577
2578 @cli_osm.command(
2579 name="pdu-update", short_help="updates a Physical Deployment Unit to the catalog"
2580 )
2581 @click.argument("name")
2582 @click.option("--newname", help="New name for the Physical Deployment Unit")
2583 @click.option("--pdu_type", help="type of PDU (e.g. router, firewall, FW001)")
2584 @click.option(
2585 "--interface",
2586 help="interface(s) of the PDU: name=<NAME>,mgmt=<true|false>,ip-address=<IP_ADDRESS>"
2587 + "[,type=<overlay|underlay>][,mac-address=<MAC_ADDRESS>][,vim-network-name=<VIM_NET_NAME>]",
2588 multiple=True,
2589 )
2590 @click.option("--description", help="human readable description")
2591 @click.option(
2592 "--vim_account",
2593 help="list of VIM accounts (in the same VIM) that can reach this PDU\n"
2594 + "The format for multiple VIMs is --vim_account <vim_account_id_1> "
2595 + "--vim_account <vim_account_id_2> ... --vim_account <vim_account_id_N>",
2596 multiple=True,
2597 )
2598 @click.option(
2599 "--descriptor_file",
2600 default=None,
2601 help="PDU descriptor file (as an alternative to using the other arguments)",
2602 )
2603 @click.pass_context
2604 def pdu_update(
2605 ctx, name, newname, pdu_type, interface, description, vim_account, descriptor_file
2606 ):
2607 """Updates a new Physical Deployment Unit (PDU)"""
2608 logger.debug("")
2609
2610 check_client_version(ctx.obj, ctx.command.name)
2611
2612 update = True
2613
2614 if not newname:
2615 newname = name
2616
2617 pdu = create_pdu_dictionary(
2618 newname, pdu_type, interface, description, vim_account, descriptor_file, update
2619 )
2620 ctx.obj.pdu.update(name, pdu)
2621
2622
2623 def create_pdu_dictionary(
2624 name, pdu_type, interface, description, vim_account, descriptor_file, update=False
2625 ):
2626 logger.debug("")
2627 pdu = {}
2628
2629 if not descriptor_file:
2630 if not update:
2631 if not name:
2632 raise ClientException(
2633 'in absence of descriptor file, option "--name" is mandatory'
2634 )
2635 if not pdu_type:
2636 raise ClientException(
2637 'in absence of descriptor file, option "--pdu_type" is mandatory'
2638 )
2639 if not interface:
2640 raise ClientException(
2641 'in absence of descriptor file, option "--interface" is mandatory (at least once)'
2642 )
2643 if not vim_account:
2644 raise ClientException(
2645 'in absence of descriptor file, option "--vim_account" is mandatory (at least once)'
2646 )
2647 else:
2648 with open(descriptor_file, "r") as df:
2649 pdu = yaml.safe_load(df.read())
2650 if name:
2651 pdu["name"] = name
2652 if pdu_type:
2653 pdu["type"] = pdu_type
2654 if description:
2655 pdu["description"] = description
2656 if vim_account:
2657 pdu["vim_accounts"] = vim_account
2658 if interface:
2659 ifaces_list = []
2660 for iface in interface:
2661 new_iface = {k: v for k, v in [i.split("=") for i in iface.split(",")]}
2662 new_iface["mgmt"] = new_iface.get("mgmt", "false").lower() == "true"
2663 ifaces_list.append(new_iface)
2664 pdu["interfaces"] = ifaces_list
2665 return pdu
2666
2667
2668 ####################
2669 # UPDATE operations
2670 ####################
2671
2672
2673 def nsd_update(ctx, name, content):
2674 logger.debug("")
2675 # try:
2676 check_client_version(ctx.obj, ctx.command.name)
2677 ctx.obj.nsd.update(name, content)
2678 # except ClientException as e:
2679 # print(str(e))
2680 # exit(1)
2681
2682
2683 @cli_osm.command(name="nsd-update", short_help="updates a NSD/NSpkg")
2684 @click.argument("name")
2685 @click.option(
2686 "--content",
2687 default=None,
2688 help="filename with the NSD/NSpkg replacing the current one",
2689 )
2690 @click.pass_context
2691 def nsd_update1(ctx, name, content):
2692 """updates a NSD/NSpkg
2693
2694 NAME: name or ID of the NSD/NSpkg
2695 """
2696 logger.debug("")
2697 nsd_update(ctx, name, content)
2698
2699
2700 @cli_osm.command(name="nspkg-update", short_help="updates a NSD/NSpkg")
2701 @click.argument("name")
2702 @click.option(
2703 "--content",
2704 default=None,
2705 help="filename with the NSD/NSpkg replacing the current one",
2706 )
2707 @click.pass_context
2708 def nsd_update2(ctx, name, content):
2709 """updates a NSD/NSpkg
2710
2711 NAME: name or ID of the NSD/NSpkg
2712 """
2713 logger.debug("")
2714 nsd_update(ctx, name, content)
2715
2716
2717 def vnfd_update(ctx, name, content):
2718 logger.debug("")
2719 # try:
2720 check_client_version(ctx.obj, ctx.command.name)
2721 ctx.obj.vnfd.update(name, content)
2722 # except ClientException as e:
2723 # print(str(e))
2724 # exit(1)
2725
2726
2727 @cli_osm.command(name="vnfd-update", short_help="updates a new VNFD/VNFpkg")
2728 @click.argument("name")
2729 @click.option(
2730 "--content",
2731 default=None,
2732 help="filename with the VNFD/VNFpkg replacing the current one",
2733 )
2734 @click.pass_context
2735 def vnfd_update1(ctx, name, content):
2736 """updates a VNFD/VNFpkg
2737
2738 NAME: name or ID of the VNFD/VNFpkg
2739 """
2740 logger.debug("")
2741 vnfd_update(ctx, name, content)
2742
2743
2744 @cli_osm.command(name="vnfpkg-update", short_help="updates a VNFD/VNFpkg")
2745 @click.argument("name")
2746 @click.option(
2747 "--content",
2748 default=None,
2749 help="filename with the VNFD/VNFpkg replacing the current one",
2750 )
2751 @click.pass_context
2752 def vnfd_update2(ctx, name, content):
2753 """updates a VNFD/VNFpkg
2754
2755 NAME: VNFD yaml file or VNFpkg tar.gz file
2756 """
2757 logger.debug("")
2758 vnfd_update(ctx, name, content)
2759
2760
2761 @cli_osm.command(name="nfpkg-update", short_help="updates a NFpkg")
2762 @click.argument("name")
2763 @click.option(
2764 "--content", default=None, help="filename with the NFpkg replacing the current one"
2765 )
2766 @click.pass_context
2767 def nfpkg_update(ctx, name, content):
2768 """updates a NFpkg
2769
2770 NAME: NF Descriptor yaml file or NFpkg tar.gz file
2771 """
2772 logger.debug("")
2773 vnfd_update(ctx, name, content)
2774
2775
2776 def nst_update(ctx, name, content):
2777 logger.debug("")
2778 # try:
2779 check_client_version(ctx.obj, ctx.command.name)
2780 ctx.obj.nst.update(name, content)
2781 # except ClientException as e:
2782 # print(str(e))
2783 # exit(1)
2784
2785
2786 @cli_osm.command(name="nst-update", short_help="updates a Network Slice Template (NST)")
2787 @click.argument("name")
2788 @click.option(
2789 "--content",
2790 default=None,
2791 help="filename with the NST/NSTpkg replacing the current one",
2792 )
2793 @click.pass_context
2794 def nst_update1(ctx, name, content):
2795 """updates a Network Slice Template (NST)
2796
2797 NAME: name or ID of the NSD/NSpkg
2798 """
2799 logger.debug("")
2800 nst_update(ctx, name, content)
2801
2802
2803 @cli_osm.command(
2804 name="netslice-template-update", short_help="updates a Network Slice Template (NST)"
2805 )
2806 @click.argument("name")
2807 @click.option(
2808 "--content",
2809 default=None,
2810 help="filename with the NST/NSTpkg replacing the current one",
2811 )
2812 @click.pass_context
2813 def nst_update2(ctx, name, content):
2814 """updates a Network Slice Template (NST)
2815
2816 NAME: name or ID of the NSD/NSpkg
2817 """
2818 logger.debug("")
2819 nst_update(ctx, name, content)
2820
2821
2822 ####################
2823 # DELETE operations
2824 ####################
2825
2826
2827 def nsd_delete(ctx, name, force):
2828 logger.debug("")
2829 # try:
2830 if not force:
2831 ctx.obj.nsd.delete(name)
2832 else:
2833 check_client_version(ctx.obj, "--force")
2834 ctx.obj.nsd.delete(name, force)
2835 # except ClientException as e:
2836 # print(str(e))
2837 # exit(1)
2838
2839
2840 @cli_osm.command(name="nsd-delete", short_help="deletes a NSD/NSpkg")
2841 @click.argument("name")
2842 @click.option(
2843 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
2844 )
2845 @click.pass_context
2846 def nsd_delete1(ctx, name, force):
2847 """deletes a NSD/NSpkg
2848
2849 NAME: name or ID of the NSD/NSpkg to be deleted
2850 """
2851 logger.debug("")
2852 nsd_delete(ctx, name, force)
2853
2854
2855 @cli_osm.command(name="nspkg-delete", short_help="deletes a NSD/NSpkg")
2856 @click.argument("name")
2857 @click.option(
2858 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
2859 )
2860 @click.pass_context
2861 def nsd_delete2(ctx, name, force):
2862 """deletes a NSD/NSpkg
2863
2864 NAME: name or ID of the NSD/NSpkg to be deleted
2865 """
2866 logger.debug("")
2867 nsd_delete(ctx, name, force)
2868
2869
2870 def vnfd_delete(ctx, name, force):
2871 logger.debug("")
2872 # try:
2873 if not force:
2874 ctx.obj.vnfd.delete(name)
2875 else:
2876 check_client_version(ctx.obj, "--force")
2877 ctx.obj.vnfd.delete(name, force)
2878 # except ClientException as e:
2879 # print(str(e))
2880 # exit(1)
2881
2882
2883 @cli_osm.command(name="vnfd-delete", short_help="deletes a VNFD/VNFpkg")
2884 @click.argument("name")
2885 @click.option(
2886 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
2887 )
2888 @click.pass_context
2889 def vnfd_delete1(ctx, name, force):
2890 """deletes a VNFD/VNFpkg
2891
2892 NAME: name or ID of the VNFD/VNFpkg to be deleted
2893 """
2894 logger.debug("")
2895 vnfd_delete(ctx, name, force)
2896
2897
2898 @cli_osm.command(name="vnfpkg-delete", short_help="deletes a VNFD/VNFpkg")
2899 @click.argument("name")
2900 @click.option(
2901 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
2902 )
2903 @click.pass_context
2904 def vnfd_delete2(ctx, name, force):
2905 """deletes a VNFD/VNFpkg
2906
2907 NAME: name or ID of the VNFD/VNFpkg to be deleted
2908 """
2909 logger.debug("")
2910 vnfd_delete(ctx, name, force)
2911
2912
2913 @cli_osm.command(name="nfpkg-delete", short_help="deletes a NFpkg")
2914 @click.argument("name")
2915 @click.option(
2916 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
2917 )
2918 @click.pass_context
2919 def nfpkg_delete(ctx, name, force):
2920 """deletes a NFpkg
2921
2922 NAME: name or ID of the NFpkg to be deleted
2923 """
2924 logger.debug("")
2925 vnfd_delete(ctx, name, force)
2926
2927
2928 @cli_osm.command(name="ns-delete", short_help="deletes a NS instance")
2929 @click.argument("name")
2930 @click.option(
2931 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
2932 )
2933 @click.option(
2934 "--config",
2935 default=None,
2936 help="specific yaml configuration for the termination, e.g. '{autoremove: False, timeout_ns_terminate: "
2937 "600, skip_terminate_primitives: True}'",
2938 )
2939 @click.option(
2940 "--wait",
2941 required=False,
2942 default=False,
2943 is_flag=True,
2944 help="do not return the control immediately, but keep it "
2945 "until the operation is completed, or timeout",
2946 )
2947 @click.pass_context
2948 def ns_delete(ctx, name, force, config, wait):
2949 """deletes a NS instance
2950
2951 NAME: name or ID of the NS instance to be deleted
2952 """
2953 logger.debug("")
2954 # try:
2955 if not force:
2956 ctx.obj.ns.delete(name, config=config, wait=wait)
2957 else:
2958 check_client_version(ctx.obj, "--force")
2959 ctx.obj.ns.delete(name, force, config=config, wait=wait)
2960 # except ClientException as e:
2961 # print(str(e))
2962 # exit(1)
2963
2964
2965 def nst_delete(ctx, name, force):
2966 logger.debug("")
2967 # try:
2968 check_client_version(ctx.obj, ctx.command.name)
2969 ctx.obj.nst.delete(name, force)
2970 # except ClientException as e:
2971 # print(str(e))
2972 # exit(1)
2973
2974
2975 @cli_osm.command(name="nst-delete", short_help="deletes a Network Slice Template (NST)")
2976 @click.argument("name")
2977 @click.option(
2978 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
2979 )
2980 @click.pass_context
2981 def nst_delete1(ctx, name, force):
2982 """deletes a Network Slice Template (NST)
2983
2984 NAME: name or ID of the NST/NSTpkg to be deleted
2985 """
2986 logger.debug("")
2987 nst_delete(ctx, name, force)
2988
2989
2990 @cli_osm.command(
2991 name="netslice-template-delete", short_help="deletes a Network Slice Template (NST)"
2992 )
2993 @click.argument("name")
2994 @click.option(
2995 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
2996 )
2997 @click.pass_context
2998 def nst_delete2(ctx, name, force):
2999 """deletes a Network Slice Template (NST)
3000
3001 NAME: name or ID of the NST/NSTpkg to be deleted
3002 """
3003 logger.debug("")
3004 nst_delete(ctx, name, force)
3005
3006
3007 def nsi_delete(ctx, name, force, wait):
3008 logger.debug("")
3009 # try:
3010 check_client_version(ctx.obj, ctx.command.name)
3011 ctx.obj.nsi.delete(name, force, wait=wait)
3012 # except ClientException as e:
3013 # print(str(e))
3014 # exit(1)
3015
3016
3017 @cli_osm.command(name="nsi-delete", short_help="deletes a Network Slice Instance (NSI)")
3018 @click.argument("name")
3019 @click.option(
3020 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
3021 )
3022 @click.option(
3023 "--wait",
3024 required=False,
3025 default=False,
3026 is_flag=True,
3027 help="do not return the control immediately, but keep it "
3028 "until the operation is completed, or timeout",
3029 )
3030 @click.pass_context
3031 def nsi_delete1(ctx, name, force, wait):
3032 """deletes a Network Slice Instance (NSI)
3033
3034 NAME: name or ID of the Network Slice instance to be deleted
3035 """
3036 logger.debug("")
3037 nsi_delete(ctx, name, force, wait=wait)
3038
3039
3040 @cli_osm.command(
3041 name="netslice-instance-delete", short_help="deletes a Network Slice Instance (NSI)"
3042 )
3043 @click.argument("name")
3044 @click.option(
3045 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
3046 )
3047 @click.pass_context
3048 def nsi_delete2(ctx, name, force, wait):
3049 """deletes a Network Slice Instance (NSI)
3050
3051 NAME: name or ID of the Network Slice instance to be deleted
3052 """
3053 logger.debug("")
3054 nsi_delete(ctx, name, force, wait=wait)
3055
3056
3057 @cli_osm.command(
3058 name="pdu-delete", short_help="deletes a Physical Deployment Unit (PDU)"
3059 )
3060 @click.argument("name")
3061 @click.option(
3062 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
3063 )
3064 @click.pass_context
3065 def pdu_delete(ctx, name, force):
3066 """deletes a Physical Deployment Unit (PDU)
3067
3068 NAME: name or ID of the PDU to be deleted
3069 """
3070 logger.debug("")
3071 # try:
3072 check_client_version(ctx.obj, ctx.command.name)
3073 ctx.obj.pdu.delete(name, force)
3074 # except ClientException as e:
3075 # print(str(e))
3076 # exit(1)
3077
3078
3079 #################
3080 # VIM operations
3081 #################
3082
3083
3084 @cli_osm.command(name="vim-create", short_help="creates a new VIM account")
3085 @click.option("--name", required=True, help="Name to create datacenter")
3086 @click.option("--user", default=None, help="VIM username")
3087 @click.option("--password", default=None, help="VIM password")
3088 @click.option("--auth_url", default=None, help="VIM url")
3089 @click.option(
3090 "--tenant", "--project", "tenant", default=None, help="VIM tenant/project name"
3091 )
3092 @click.option("--config", default=None, help="VIM specific config parameters")
3093 @click.option(
3094 "--config_file",
3095 default=None,
3096 help="VIM specific config parameters in YAML or JSON file",
3097 )
3098 @click.option("--account_type", default="openstack", help="VIM type")
3099 @click.option("--description", default=None, help="human readable description")
3100 @click.option(
3101 "--sdn_controller",
3102 default=None,
3103 help="Name or id of the SDN controller associated to this VIM account",
3104 )
3105 @click.option(
3106 "--sdn_port_mapping",
3107 default=None,
3108 help="File describing the port mapping between compute nodes' ports and switch ports",
3109 )
3110 @click.option(
3111 "--wait",
3112 required=False,
3113 default=False,
3114 is_flag=True,
3115 help="do not return the control immediately, but keep it "
3116 "until the operation is completed, or timeout",
3117 )
3118 @click.option("--vca", default=None, help="VCA to be used in this VIM account")
3119 @click.option(
3120 "--creds", default=None, help="credentials file (only applycable for GCP VIM type)"
3121 )
3122 @click.option(
3123 "--prometheus_config_file",
3124 default=None,
3125 help="Prometheus configuration to get VIM data",
3126 )
3127 @click.pass_context
3128 def vim_create(
3129 ctx,
3130 name,
3131 user,
3132 password,
3133 auth_url,
3134 tenant,
3135 config,
3136 config_file,
3137 account_type,
3138 description,
3139 sdn_controller,
3140 sdn_port_mapping,
3141 wait,
3142 vca,
3143 creds,
3144 prometheus_config_file,
3145 ):
3146 """creates a new VIM account"""
3147 logger.debug("")
3148 # try:
3149 if sdn_controller:
3150 check_client_version(ctx.obj, "--sdn_controller")
3151 if sdn_port_mapping:
3152 check_client_version(ctx.obj, "--sdn_port_mapping")
3153 vim = {}
3154 if prometheus_config_file:
3155 with open(prometheus_config_file) as prometheus_file:
3156 prometheus_config_dict = json.load(prometheus_file)
3157 vim["prometheus-config"] = prometheus_config_dict
3158
3159 vim["vim-username"] = user
3160 vim["vim-password"] = password
3161 vim["vim-url"] = auth_url
3162 vim["vim-tenant-name"] = tenant
3163 vim["vim-type"] = account_type
3164 vim["description"] = description
3165 if vca:
3166 vim["vca"] = vca
3167 vim_config = create_config(config_file, config)
3168 if creds:
3169 with open(creds, "r") as cf:
3170 vim_config["credentials"] = yaml.safe_load(cf.read())
3171 ctx.obj.vim.create(
3172 name, vim, vim_config, sdn_controller, sdn_port_mapping, wait=wait
3173 )
3174 # except ClientException as e:
3175 # print(str(e))
3176 # exit(1)
3177
3178
3179 @cli_osm.command(name="vim-update", short_help="updates a VIM account")
3180 @click.argument("name")
3181 @click.option("--newname", help="New name for the VIM account")
3182 @click.option("--user", help="VIM username")
3183 @click.option("--password", help="VIM password")
3184 @click.option("--auth_url", help="VIM url")
3185 @click.option("--tenant", help="VIM tenant name")
3186 @click.option("--config", help="VIM specific config parameters")
3187 @click.option(
3188 "--config_file",
3189 default=None,
3190 help="VIM specific config parameters in YAML or JSON file",
3191 )
3192 @click.option("--account_type", help="VIM type")
3193 @click.option("--description", help="human readable description")
3194 @click.option(
3195 "--sdn_controller",
3196 default=None,
3197 help="Name or id of the SDN controller to be associated with this VIM"
3198 "account. Use empty string to disassociate",
3199 )
3200 @click.option(
3201 "--sdn_port_mapping",
3202 default=None,
3203 help="File describing the port mapping between compute nodes' ports and switch ports",
3204 )
3205 @click.option(
3206 "--wait",
3207 required=False,
3208 default=False,
3209 is_flag=True,
3210 help="do not return the control immediately, but keep it "
3211 "until the operation is completed, or timeout",
3212 )
3213 @click.option(
3214 "--creds", default=None, help="credentials file (only applycable for GCP VIM type)"
3215 )
3216 @click.option(
3217 "--prometheus_config_file",
3218 default=None,
3219 help="Prometheus configuration to get VIM data",
3220 )
3221 @click.pass_context
3222 def vim_update(
3223 ctx,
3224 name,
3225 newname,
3226 user,
3227 password,
3228 auth_url,
3229 tenant,
3230 config,
3231 config_file,
3232 account_type,
3233 description,
3234 sdn_controller,
3235 sdn_port_mapping,
3236 wait,
3237 creds,
3238 prometheus_config_file,
3239 ):
3240 """updates a VIM account
3241
3242 NAME: name or ID of the VIM account
3243 """
3244 logger.debug("")
3245 # try:
3246 check_client_version(ctx.obj, ctx.command.name)
3247 vim = {}
3248 if newname:
3249 vim["name"] = newname
3250 if user:
3251 vim["vim_user"] = user
3252 if password:
3253 vim["vim_password"] = password
3254 if auth_url:
3255 vim["vim_url"] = auth_url
3256 if tenant:
3257 vim["vim-tenant-name"] = tenant
3258 if account_type:
3259 vim["vim_type"] = account_type
3260 if description:
3261 vim["description"] = description
3262 vim_config = None
3263 if config or config_file:
3264 vim_config = create_config(config_file, config)
3265 if creds:
3266 with open(creds, "r") as cf:
3267 vim_config["credentials"] = yaml.safe_load(cf.read())
3268 if prometheus_config_file:
3269 with open(prometheus_config_file) as prometheus_file:
3270 prometheus_config_dict = json.load(prometheus_file)
3271 vim["prometheus-config"] = prometheus_config_dict
3272 logger.info(f"VIM: {vim}, VIM config: {vim_config}")
3273 ctx.obj.vim.update(
3274 name, vim, vim_config, sdn_controller, sdn_port_mapping, wait=wait
3275 )
3276 # except ClientException as e:
3277 # print(str(e))
3278 # exit(1)
3279
3280
3281 @cli_osm.command(name="vim-delete", short_help="deletes a VIM account")
3282 @click.argument("name")
3283 @click.option(
3284 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
3285 )
3286 @click.option(
3287 "--wait",
3288 required=False,
3289 default=False,
3290 is_flag=True,
3291 help="do not return the control immediately, but keep it "
3292 "until the operation is completed, or timeout",
3293 )
3294 @click.pass_context
3295 def vim_delete(ctx, name, force, wait):
3296 """deletes a VIM account
3297
3298 NAME: name or ID of the VIM account to be deleted
3299 """
3300 logger.debug("")
3301 # try:
3302 if not force:
3303 ctx.obj.vim.delete(name, wait=wait)
3304 else:
3305 check_client_version(ctx.obj, "--force")
3306 ctx.obj.vim.delete(name, force, wait=wait)
3307 # except ClientException as e:
3308 # print(str(e))
3309 # exit(1)
3310
3311
3312 @cli_osm.command(name="vim-list", short_help="list all VIM accounts")
3313 # @click.option('--ro_update/--no_ro_update',
3314 # default=False,
3315 # help='update list from RO')
3316 @click.option(
3317 "--filter",
3318 default=None,
3319 multiple=True,
3320 help="restricts the list to the VIM accounts matching the filter",
3321 )
3322 @click.option(
3323 "--long",
3324 is_flag=True,
3325 help="get more details of the NS (project, vim, deployment status, configuration status.",
3326 )
3327 @click.pass_context
3328 def vim_list(ctx, filter, long):
3329 """list all VIM accounts"""
3330 logger.debug("")
3331 if filter:
3332 filter = "&".join(filter)
3333 check_client_version(ctx.obj, "--filter")
3334 # if ro_update:
3335 # check_client_version(ctx.obj, '--ro_update', 'v1')
3336 fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
3337 if fullclassname == "osmclient.sol005.client.Client":
3338 resp = ctx.obj.vim.list(filter)
3339 # else:
3340 # resp = ctx.obj.vim.list(ro_update)
3341 if long:
3342 table = PrettyTable(
3343 ["vim name", "uuid", "project", "operational state", "error details"]
3344 )
3345 project_list = ctx.obj.project.list()
3346 else:
3347 table = PrettyTable(["vim name", "uuid", "operational state"])
3348 for vim in resp:
3349 if long:
3350 if "vim_password" in vim:
3351 vim["vim_password"] = "********"
3352 if "config" in vim and "credentials" in vim["config"]:
3353 vim["config"]["credentials"] = "********"
3354 logger.debug("VIM details: {}".format(yaml.safe_dump(vim)))
3355 vim_state = vim["_admin"].get("operationalState", "-")
3356 error_details = "N/A"
3357 if vim_state == "ERROR":
3358 error_details = vim["_admin"].get("detailed-status", "Not found")
3359 project_id, project_name = get_project(project_list, vim)
3360 # project_info = '{} ({})'.format(project_name, project_id)
3361 project_info = project_name
3362 table.add_row(
3363 [
3364 vim["name"],
3365 vim["uuid"],
3366 project_info,
3367 vim_state,
3368 wrap_text(text=error_details, width=80),
3369 ]
3370 )
3371 else:
3372 table.add_row(
3373 [vim["name"], vim["uuid"], vim["_admin"].get("operationalState", "-")]
3374 )
3375 table.align = "l"
3376 print(table)
3377
3378
3379 @cli_osm.command(name="vim-show", short_help="shows the details of a VIM account")
3380 @click.argument("name")
3381 @click.option(
3382 "--filter",
3383 multiple=True,
3384 help="restricts the information to the fields in the filter",
3385 )
3386 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
3387 @click.pass_context
3388 def vim_show(ctx, name, filter, literal):
3389 """shows the details of a VIM account
3390
3391 NAME: name or ID of the VIM account
3392 """
3393 logger.debug("")
3394 # try:
3395 resp = ctx.obj.vim.get(name)
3396 if "vim_password" in resp:
3397 resp["vim_password"] = "********"
3398 if "config" in resp and "credentials" in resp["config"]:
3399 resp["config"]["credentials"] = "********"
3400 # except ClientException as e:
3401 # print(str(e))
3402 # exit(1)
3403
3404 if literal:
3405 print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
3406 return
3407 table = PrettyTable(["key", "attribute"])
3408 for k, v in list(resp.items()):
3409 if not filter or k in filter:
3410 table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)])
3411 table.align = "l"
3412 print(table)
3413
3414
3415 ####################
3416 # WIM operations
3417 ####################
3418
3419
3420 @cli_osm.command(name="wim-create", short_help="creates a new WIM account")
3421 @click.option("--name", prompt=True, help="Name for the WIM account")
3422 @click.option("--user", help="WIM username")
3423 @click.option("--password", help="WIM password")
3424 @click.option("--url", prompt=True, help="WIM url")
3425 # @click.option('--tenant',
3426 # help='wIM tenant name')
3427 @click.option("--config", default=None, help="WIM specific config parameters")
3428 @click.option("--wim_type", help="WIM type")
3429 @click.option("--description", default=None, help="human readable description")
3430 @click.option(
3431 "--wim_port_mapping",
3432 default=None,
3433 help="File describing the port mapping between DC edge (datacenters, switches, ports) and WAN edge "
3434 "(WAN service endpoint id and info)",
3435 )
3436 @click.option(
3437 "--wait",
3438 required=False,
3439 default=False,
3440 is_flag=True,
3441 help="do not return the control immediately, but keep it "
3442 "until the operation is completed, or timeout",
3443 )
3444 @click.pass_context
3445 def wim_create(
3446 ctx,
3447 name,
3448 user,
3449 password,
3450 url,
3451 # tenant,
3452 config,
3453 wim_type,
3454 description,
3455 wim_port_mapping,
3456 wait,
3457 ):
3458 """creates a new WIM account"""
3459 logger.debug("")
3460 # try:
3461 check_client_version(ctx.obj, ctx.command.name)
3462 # if sdn_controller:
3463 # check_client_version(ctx.obj, '--sdn_controller')
3464 # if sdn_port_mapping:
3465 # check_client_version(ctx.obj, '--sdn_port_mapping')
3466 wim = {}
3467 if user:
3468 wim["user"] = user
3469 if password:
3470 wim["password"] = password
3471 if url:
3472 wim["wim_url"] = url
3473 # if tenant: wim['tenant'] = tenant
3474 wim["wim_type"] = wim_type
3475 if description:
3476 wim["description"] = description
3477 if config:
3478 wim["config"] = config
3479 ctx.obj.wim.create(name, wim, wim_port_mapping, wait=wait)
3480 # except ClientException as e:
3481 # print(str(e))
3482 # exit(1)
3483
3484
3485 @cli_osm.command(name="wim-update", short_help="updates a WIM account")
3486 @click.argument("name")
3487 @click.option("--newname", help="New name for the WIM account")
3488 @click.option("--user", help="WIM username")
3489 @click.option("--password", help="WIM password")
3490 @click.option("--url", help="WIM url")
3491 @click.option("--config", help="WIM specific config parameters")
3492 @click.option("--wim_type", help="WIM type")
3493 @click.option("--description", help="human readable description")
3494 @click.option(
3495 "--wim_port_mapping",
3496 default=None,
3497 help="File describing the port mapping between DC edge (datacenters, switches, ports) and WAN edge "
3498 "(WAN service endpoint id and info)",
3499 )
3500 @click.option(
3501 "--wait",
3502 required=False,
3503 default=False,
3504 is_flag=True,
3505 help="do not return the control immediately, but keep it until the operation is completed, or timeout",
3506 )
3507 @click.pass_context
3508 def wim_update(
3509 ctx,
3510 name,
3511 newname,
3512 user,
3513 password,
3514 url,
3515 config,
3516 wim_type,
3517 description,
3518 wim_port_mapping,
3519 wait,
3520 ):
3521 """updates a WIM account
3522
3523 NAME: name or ID of the WIM account
3524 """
3525 logger.debug("")
3526 # try:
3527 check_client_version(ctx.obj, ctx.command.name)
3528 wim = {}
3529 if newname:
3530 wim["name"] = newname
3531 if user:
3532 wim["user"] = user
3533 if password:
3534 wim["password"] = password
3535 if url:
3536 wim["url"] = url
3537 # if tenant: wim['tenant'] = tenant
3538 if wim_type:
3539 wim["wim_type"] = wim_type
3540 if description:
3541 wim["description"] = description
3542 if config:
3543 wim["config"] = config
3544 ctx.obj.wim.update(name, wim, wim_port_mapping, wait=wait)
3545 # except ClientException as e:
3546 # print(str(e))
3547 # exit(1)
3548
3549
3550 @cli_osm.command(name="wim-delete", short_help="deletes a WIM account")
3551 @click.argument("name")
3552 @click.option(
3553 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
3554 )
3555 @click.option(
3556 "--wait",
3557 required=False,
3558 default=False,
3559 is_flag=True,
3560 help="do not return the control immediately, but keep it until the operation is completed, or timeout",
3561 )
3562 @click.pass_context
3563 def wim_delete(ctx, name, force, wait):
3564 """deletes a WIM account
3565
3566 NAME: name or ID of the WIM account to be deleted
3567 """
3568 logger.debug("")
3569 # try:
3570 check_client_version(ctx.obj, ctx.command.name)
3571 ctx.obj.wim.delete(name, force, wait=wait)
3572 # except ClientException as e:
3573 # print(str(e))
3574 # exit(1)
3575
3576
3577 @cli_osm.command(name="wim-list", short_help="list all WIM accounts")
3578 @click.option(
3579 "--filter",
3580 default=None,
3581 multiple=True,
3582 help="restricts the list to the WIM accounts matching the filter",
3583 )
3584 @click.pass_context
3585 def wim_list(ctx, filter):
3586 """list all WIM accounts"""
3587 logger.debug("")
3588 # try:
3589 check_client_version(ctx.obj, ctx.command.name)
3590 if filter:
3591 filter = "&".join(filter)
3592 resp = ctx.obj.wim.list(filter)
3593 table = PrettyTable(["wim name", "uuid"])
3594 for wim in resp:
3595 table.add_row([wim["name"], wim["uuid"]])
3596 table.align = "l"
3597 print(table)
3598 # except ClientException as e:
3599 # print(str(e))
3600 # exit(1)
3601
3602
3603 @cli_osm.command(name="wim-show", short_help="shows the details of a WIM account")
3604 @click.argument("name")
3605 @click.pass_context
3606 def wim_show(ctx, name):
3607 """shows the details of a WIM account
3608
3609 NAME: name or ID of the WIM account
3610 """
3611 logger.debug("")
3612 # try:
3613 check_client_version(ctx.obj, ctx.command.name)
3614 resp = ctx.obj.wim.get(name)
3615 if "password" in resp:
3616 resp["password"] = "********"
3617 # except ClientException as e:
3618 # print(str(e))
3619 # exit(1)
3620
3621 table = PrettyTable(["key", "attribute"])
3622 for k, v in list(resp.items()):
3623 table.add_row([k, json.dumps(v, indent=2)])
3624 table.align = "l"
3625 print(table)
3626
3627
3628 ####################
3629 # SDN controller operations
3630 ####################
3631
3632
3633 @cli_osm.command(name="sdnc-create", short_help="creates a new SDN controller")
3634 @click.option("--name", prompt=True, help="Name to create sdn controller")
3635 @click.option("--type", prompt=True, help="SDN controller type")
3636 @click.option(
3637 "--sdn_controller_version", # hidden=True,
3638 help="Deprecated. Use --config {version: sdn_controller_version}",
3639 )
3640 @click.option("--url", help="URL in format http[s]://HOST:IP/")
3641 @click.option("--ip_address", help="Deprecated. Use --url") # hidden=True,
3642 @click.option("--port", help="Deprecated. Use --url") # hidden=True,
3643 @click.option(
3644 "--switch_dpid", help="Deprecated. Use --config {switch_id: DPID}" # hidden=True,
3645 )
3646 @click.option(
3647 "--config",
3648 help="Extra information for SDN in yaml format, as {switch_id: identity used for the plugin (e.g. DPID: "
3649 "Openflow Datapath ID), version: version}",
3650 )
3651 @click.option("--user", help="SDN controller username")
3652 @click.option(
3653 "--password",
3654 hide_input=True,
3655 confirmation_prompt=True,
3656 help="SDN controller password",
3657 )
3658 @click.option("--description", default=None, help="human readable description")
3659 @click.option(
3660 "--wait",
3661 required=False,
3662 default=False,
3663 is_flag=True,
3664 help="do not return the control immediately, but keep it until the operation is completed, or timeout",
3665 )
3666 @click.pass_context
3667 def sdnc_create(ctx, **kwargs):
3668 """creates a new SDN controller"""
3669 logger.debug("")
3670 sdncontroller = {
3671 x: kwargs[x]
3672 for x in kwargs
3673 if kwargs[x] and x not in ("wait", "ip_address", "port", "switch_dpid")
3674 }
3675 if kwargs.get("port"):
3676 print("option '--port' is deprecated, use '--url' instead")
3677 sdncontroller["port"] = int(kwargs["port"])
3678 if kwargs.get("ip_address"):
3679 print("option '--ip_address' is deprecated, use '--url' instead")
3680 sdncontroller["ip"] = kwargs["ip_address"]
3681 if kwargs.get("switch_dpid"):
3682 print(
3683 "option '--switch_dpid' is deprecated, use '--config={switch_id: id|DPID}' instead"
3684 )
3685 sdncontroller["dpid"] = kwargs["switch_dpid"]
3686 if kwargs.get("sdn_controller_version"):
3687 print(
3688 "option '--sdn_controller_version' is deprecated, use '--config={version: SDN_CONTROLLER_VERSION}'"
3689 " instead"
3690 )
3691 # try:
3692 check_client_version(ctx.obj, ctx.command.name)
3693 ctx.obj.sdnc.create(kwargs["name"], sdncontroller, wait=kwargs["wait"])
3694 # except ClientException as e:
3695 # print(str(e))
3696 # exit(1)
3697
3698
3699 @cli_osm.command(name="sdnc-update", short_help="updates an SDN controller")
3700 @click.argument("name")
3701 @click.option("--newname", help="New name for the SDN controller")
3702 @click.option("--description", default=None, help="human readable description")
3703 @click.option("--type", help="SDN controller type")
3704 @click.option("--url", help="URL in format http[s]://HOST:IP/")
3705 @click.option(
3706 "--config",
3707 help="Extra information for SDN in yaml format, as "
3708 "{switch_id: identity used for the plugin (e.g. DPID: "
3709 "Openflow Datapath ID), version: version}",
3710 )
3711 @click.option("--user", help="SDN controller username")
3712 @click.option("--password", help="SDN controller password")
3713 @click.option("--ip_address", help="Deprecated. Use --url") # hidden=True
3714 @click.option("--port", help="Deprecated. Use --url") # hidden=True
3715 @click.option(
3716 "--switch_dpid", help="Deprecated. Use --config {switch_dpid: DPID}"
3717 ) # hidden=True
3718 @click.option(
3719 "--sdn_controller_version", help="Deprecated. Use --config {version: VERSION}"
3720 ) # hidden=True
3721 @click.option(
3722 "--wait",
3723 required=False,
3724 default=False,
3725 is_flag=True,
3726 help="do not return the control immediately, but keep it until the operation is completed, or timeout",
3727 )
3728 @click.pass_context
3729 def sdnc_update(ctx, **kwargs):
3730 """updates an SDN controller
3731
3732 NAME: name or ID of the SDN controller
3733 """
3734 logger.debug("")
3735 sdncontroller = {
3736 x: kwargs[x]
3737 for x in kwargs
3738 if kwargs[x]
3739 and x not in ("wait", "ip_address", "port", "switch_dpid", "new_name")
3740 }
3741 if kwargs.get("newname"):
3742 sdncontroller["name"] = kwargs["newname"]
3743 if kwargs.get("port"):
3744 print("option '--port' is deprecated, use '--url' instead")
3745 sdncontroller["port"] = int(kwargs["port"])
3746 if kwargs.get("ip_address"):
3747 print("option '--ip_address' is deprecated, use '--url' instead")
3748 sdncontroller["ip"] = kwargs["ip_address"]
3749 if kwargs.get("switch_dpid"):
3750 print(
3751 "option '--switch_dpid' is deprecated, use '--config={switch_id: id|DPID}' instead"
3752 )
3753 sdncontroller["dpid"] = kwargs["switch_dpid"]
3754 if kwargs.get("sdn_controller_version"):
3755 print(
3756 "option '--sdn_controller_version' is deprecated, use '---config={version: SDN_CONTROLLER_VERSION}'"
3757 " instead"
3758 )
3759
3760 # try:
3761 check_client_version(ctx.obj, ctx.command.name)
3762 ctx.obj.sdnc.update(kwargs["name"], sdncontroller, wait=kwargs["wait"])
3763 # except ClientException as e:
3764 # print(str(e))
3765 # exit(1)
3766
3767
3768 @cli_osm.command(name="sdnc-delete", short_help="deletes an SDN controller")
3769 @click.argument("name")
3770 @click.option(
3771 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
3772 )
3773 @click.option(
3774 "--wait",
3775 required=False,
3776 default=False,
3777 is_flag=True,
3778 help="do not return the control immediately, but keep it until the operation is completed, or timeout",
3779 )
3780 @click.pass_context
3781 def sdnc_delete(ctx, name, force, wait):
3782 """deletes an SDN controller
3783
3784 NAME: name or ID of the SDN controller to be deleted
3785 """
3786 logger.debug("")
3787 # try:
3788 check_client_version(ctx.obj, ctx.command.name)
3789 ctx.obj.sdnc.delete(name, force, wait=wait)
3790 # except ClientException as e:
3791 # print(str(e))
3792 # exit(1)
3793
3794
3795 @cli_osm.command(name="sdnc-list", short_help="list all SDN controllers")
3796 @click.option(
3797 "--filter",
3798 default=None,
3799 multiple=True,
3800 help="restricts the list to the SDN controllers matching the filter with format: 'k[.k..]=v[&k[.k]=v2]'",
3801 )
3802 @click.pass_context
3803 def sdnc_list(ctx, filter):
3804 """list all SDN controllers"""
3805 logger.debug("")
3806 # try:
3807 check_client_version(ctx.obj, ctx.command.name)
3808 if filter:
3809 filter = "&".join(filter)
3810 resp = ctx.obj.sdnc.list(filter)
3811 # except ClientException as e:
3812 # print(str(e))
3813 # exit(1)
3814 table = PrettyTable(["sdnc name", "id"])
3815 for sdnc in resp:
3816 table.add_row([sdnc["name"], sdnc["_id"]])
3817 table.align = "l"
3818 print(table)
3819
3820
3821 @cli_osm.command(name="sdnc-show", short_help="shows the details of an SDN controller")
3822 @click.argument("name")
3823 @click.pass_context
3824 def sdnc_show(ctx, name):
3825 """shows the details of an SDN controller
3826
3827 NAME: name or ID of the SDN controller
3828 """
3829 logger.debug("")
3830 # try:
3831 check_client_version(ctx.obj, ctx.command.name)
3832 resp = ctx.obj.sdnc.get(name)
3833 # except ClientException as e:
3834 # print(str(e))
3835 # exit(1)
3836
3837 table = PrettyTable(["key", "attribute"])
3838 for k, v in list(resp.items()):
3839 table.add_row([k, json.dumps(v, indent=2)])
3840 table.align = "l"
3841 print(table)
3842
3843
3844 ###########################
3845 # K8s cluster operations
3846 ###########################
3847
3848
3849 @cli_osm.command(name="k8scluster-add", short_help="adds a K8s cluster to OSM")
3850 @click.argument("name")
3851 @click.option(
3852 "--creds", prompt=True, help="credentials file, i.e. a valid `.kube/config` file"
3853 )
3854 @click.option("--version", prompt=True, help="Kubernetes version")
3855 @click.option(
3856 "--vim", prompt=True, help="VIM target, the VIM where the cluster resides"
3857 )
3858 @click.option(
3859 "--k8s-nets",
3860 prompt=True,
3861 help='''list of VIM networks, in JSON inline format, where the cluster is
3862 accessible via L3 routing, e.g. "{(k8s_net1:vim_network1) [,(k8s_net2:vim_network2) ...]}"''',
3863 )
3864 @click.option(
3865 "--init-helm2/--skip-helm2",
3866 required=False,
3867 default=True,
3868 help="Initialize helm v2",
3869 )
3870 @click.option(
3871 "--init-helm3/--skip-helm3",
3872 required=False,
3873 default=True,
3874 help="Initialize helm v3",
3875 )
3876 @click.option(
3877 "--init-jujubundle/--skip-jujubundle",
3878 required=False,
3879 default=True,
3880 help="Initialize juju-bundle",
3881 )
3882 @click.option("--description", default=None, help="human readable description")
3883 @click.option(
3884 "--namespace",
3885 default="kube-system",
3886 help="namespace to be used for its operation, defaults to `kube-system`",
3887 )
3888 @click.option(
3889 "--wait",
3890 required=False,
3891 default=False,
3892 is_flag=True,
3893 help="do not return the control immediately, but keep it "
3894 "until the operation is completed, or timeout",
3895 )
3896 @click.option(
3897 "--cni",
3898 default=None,
3899 help="list of CNI plugins, in JSON inline format, used in the cluster",
3900 )
3901 # @click.option('--skip-init',
3902 # is_flag=True,
3903 # help='If set, K8s cluster is assumed to be ready for its use with OSM')
3904 # @click.option('--wait',
3905 # is_flag=True,
3906 # help='do not return the control immediately, but keep it until the operation is completed, or timeout')
3907 @click.pass_context
3908 def k8scluster_add(
3909 ctx,
3910 name,
3911 creds,
3912 version,
3913 vim,
3914 k8s_nets,
3915 init_helm2,
3916 init_helm3,
3917 init_jujubundle,
3918 description,
3919 namespace,
3920 wait,
3921 cni,
3922 ):
3923 """adds a K8s cluster to OSM
3924
3925 NAME: name of the K8s cluster
3926 """
3927 # try:
3928 check_client_version(ctx.obj, ctx.command.name)
3929 cluster = {}
3930 cluster["name"] = name
3931 with open(creds, "r") as cf:
3932 cluster["credentials"] = yaml.safe_load(cf.read())
3933 cluster["k8s_version"] = version
3934 cluster["vim_account"] = vim
3935 cluster["nets"] = yaml.safe_load(k8s_nets)
3936 if not (init_helm2 and init_jujubundle and init_helm3):
3937 cluster["deployment_methods"] = {
3938 "helm-chart": init_helm2,
3939 "juju-bundle": init_jujubundle,
3940 "helm-chart-v3": init_helm3,
3941 }
3942 if description:
3943 cluster["description"] = description
3944 if namespace:
3945 cluster["namespace"] = namespace
3946 if cni:
3947 cluster["cni"] = yaml.safe_load(cni)
3948 ctx.obj.k8scluster.create(name, cluster, wait)
3949 # except ClientException as e:
3950 # print(str(e))
3951 # exit(1)
3952
3953
3954 @cli_osm.command(name="k8scluster-update", short_help="updates a K8s cluster")
3955 @click.argument("name")
3956 @click.option("--newname", help="New name for the K8s cluster")
3957 @click.option("--creds", help="credentials file, i.e. a valid `.kube/config` file")
3958 @click.option("--version", help="Kubernetes version")
3959 @click.option("--vim", help="VIM target, the VIM where the cluster resides")
3960 @click.option(
3961 "--k8s-nets",
3962 help='''list of VIM networks, in JSON inline format, where the cluster is accessible
3963 via L3 routing, e.g. "{(k8s_net1:vim_network1) [,(k8s_net2:vim_network2) ...]}"''',
3964 )
3965 @click.option("--description", help="human readable description")
3966 @click.option(
3967 "--namespace",
3968 help="namespace to be used for its operation, defaults to `kube-system`",
3969 )
3970 @click.option(
3971 "--wait",
3972 required=False,
3973 default=False,
3974 is_flag=True,
3975 help="do not return the control immediately, but keep it "
3976 "until the operation is completed, or timeout",
3977 )
3978 @click.option(
3979 "--cni", help="list of CNI plugins, in JSON inline format, used in the cluster"
3980 )
3981 @click.pass_context
3982 def k8scluster_update(
3983 ctx, name, newname, creds, version, vim, k8s_nets, description, namespace, wait, cni
3984 ):
3985 """updates a K8s cluster
3986
3987 NAME: name or ID of the K8s cluster
3988 """
3989 # try:
3990 check_client_version(ctx.obj, ctx.command.name)
3991 cluster = {}
3992 if newname:
3993 cluster["name"] = newname
3994 if creds:
3995 with open(creds, "r") as cf:
3996 cluster["credentials"] = yaml.safe_load(cf.read())
3997 if version:
3998 cluster["k8s_version"] = version
3999 if vim:
4000 cluster["vim_account"] = vim
4001 if k8s_nets:
4002 cluster["nets"] = yaml.safe_load(k8s_nets)
4003 if description:
4004 cluster["description"] = description
4005 if namespace:
4006 cluster["namespace"] = namespace
4007 if cni:
4008 cluster["cni"] = yaml.safe_load(cni)
4009 ctx.obj.k8scluster.update(name, cluster, wait)
4010 # except ClientException as e:
4011 # print(str(e))
4012 # exit(1)
4013
4014
4015 @cli_osm.command(name="k8scluster-delete", short_help="deletes a K8s cluster")
4016 @click.argument("name")
4017 @click.option(
4018 "--force", is_flag=True, help="forces the deletion from the DB (not recommended)"
4019 )
4020 @click.option(
4021 "--wait",
4022 required=False,
4023 default=False,
4024 is_flag=True,
4025 help="do not return the control immediately, but keep it "
4026 "until the operation is completed, or timeout",
4027 )
4028 @click.pass_context
4029 def k8scluster_delete(ctx, name, force, wait):
4030 """deletes a K8s cluster
4031
4032 NAME: name or ID of the K8s cluster to be deleted
4033 """
4034 # try:
4035 check_client_version(ctx.obj, ctx.command.name)
4036 ctx.obj.k8scluster.delete(name, force, wait)
4037 # except ClientException as e:
4038 # print(str(e))
4039 # exit(1)
4040
4041
4042 @cli_osm.command(name="k8scluster-list")
4043 @click.option(
4044 "--filter",
4045 default=None,
4046 multiple=True,
4047 help="restricts the list to the K8s clusters matching the filter",
4048 )
4049 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
4050 @click.option("--long", is_flag=True, help="get more details")
4051 @click.pass_context
4052 def k8scluster_list(ctx, filter, literal, long):
4053 """list all K8s clusters"""
4054 # try:
4055 check_client_version(ctx.obj, ctx.command.name)
4056 if filter:
4057 filter = "&".join(filter)
4058 resp = ctx.obj.k8scluster.list(filter)
4059 if literal:
4060 print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
4061 return
4062 if long:
4063 table = PrettyTable(
4064 [
4065 "Name",
4066 "Id",
4067 "Project",
4068 "Version",
4069 "VIM",
4070 "K8s-nets",
4071 "Deployment methods",
4072 "Operational State",
4073 "Op. state (details)",
4074 "Description",
4075 "Detailed status",
4076 ]
4077 )
4078 project_list = ctx.obj.project.list()
4079 else:
4080 table = PrettyTable(
4081 ["Name", "Id", "VIM", "Operational State", "Op. state details"]
4082 )
4083 try:
4084 vim_list = ctx.obj.vim.list()
4085 except Exception:
4086 vim_list = []
4087 for cluster in resp:
4088 logger.debug("Cluster details: {}".format(yaml.safe_dump(cluster)))
4089 vim_name = get_vim_name(vim_list, cluster["vim_account"])
4090 # vim_info = '{} ({})'.format(vim_name,cluster['vim_account'])
4091 vim_info = vim_name
4092 op_state_details = "Helm: {}\nJuju: {}".format(
4093 cluster["_admin"].get("helm-chart", {}).get("operationalState", "-"),
4094 cluster["_admin"].get("juju-bundle", {}).get("operationalState", "-"),
4095 )
4096 if long:
4097 project_id, project_name = get_project(project_list, cluster)
4098 # project_info = '{} ({})'.format(project_name, project_id)
4099 project_info = project_name
4100 detailed_status = cluster["_admin"].get("detailed-status", "-")
4101 table.add_row(
4102 [
4103 cluster["name"],
4104 cluster["_id"],
4105 project_info,
4106 cluster["k8s_version"],
4107 vim_info,
4108 json.dumps(cluster["nets"]),
4109 json.dumps(cluster["deployment_methods"]),
4110 cluster["_admin"]["operationalState"],
4111 op_state_details,
4112 trunc_text(cluster.get("description") or "", 40),
4113 wrap_text(text=detailed_status, width=40),
4114 ]
4115 )
4116 else:
4117 table.add_row(
4118 [
4119 cluster["name"],
4120 cluster["_id"],
4121 vim_info,
4122 cluster["_admin"]["operationalState"],
4123 op_state_details,
4124 ]
4125 )
4126 table.align = "l"
4127 print(table)
4128 # except ClientException as e:
4129 # print(str(e))
4130 # exit(1)
4131
4132
4133 @cli_osm.command(
4134 name="k8scluster-show", short_help="shows the details of a K8s cluster"
4135 )
4136 @click.argument("name")
4137 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
4138 @click.pass_context
4139 def k8scluster_show(ctx, name, literal):
4140 """shows the details of a K8s cluster
4141
4142 NAME: name or ID of the K8s cluster
4143 """
4144 # try:
4145 resp = ctx.obj.k8scluster.get(name)
4146 if literal:
4147 print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
4148 return
4149 table = PrettyTable(["key", "attribute"])
4150 for k, v in list(resp.items()):
4151 table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)])
4152 table.align = "l"
4153 print(table)
4154 # except ClientException as e:
4155 # print(str(e))
4156 # exit(1)
4157
4158
4159 ###########################
4160 # VCA operations
4161 ###########################
4162
4163
4164 @cli_osm.command(name="vca-add", short_help="adds a VCA (Juju controller) to OSM")
4165 @click.argument("name")
4166 @click.option(
4167 "--endpoints",
4168 prompt=True,
4169 help="Comma-separated list of IP or hostnames of the Juju controller",
4170 )
4171 @click.option("--user", prompt=True, help="Username with admin priviledges")
4172 @click.option("--secret", prompt=True, help="Password of the specified username")
4173 @click.option("--cacert", prompt=True, help="CA certificate")
4174 @click.option(
4175 "--lxd-cloud",
4176 prompt=True,
4177 help="Name of the cloud that will be used for LXD containers (LXD proxy charms)",
4178 )
4179 @click.option(
4180 "--lxd-credentials",
4181 prompt=True,
4182 help="Name of the cloud credentialsto be used for the LXD cloud",
4183 )
4184 @click.option(
4185 "--k8s-cloud",
4186 prompt=True,
4187 help="Name of the cloud that will be used for K8s containers (K8s proxy charms)",
4188 )
4189 @click.option(
4190 "--k8s-credentials",
4191 prompt=True,
4192 help="Name of the cloud credentialsto be used for the K8s cloud",
4193 )
4194 @click.option(
4195 "--model-config",
4196 default={},
4197 help="Configuration options for the models",
4198 )
4199 @click.option("--description", default=None, help="human readable description")
4200 @click.pass_context
4201 def vca_add(
4202 ctx,
4203 name,
4204 endpoints,
4205 user,
4206 secret,
4207 cacert,
4208 lxd_cloud,
4209 lxd_credentials,
4210 k8s_cloud,
4211 k8s_credentials,
4212 model_config,
4213 description,
4214 ):
4215 """adds a VCA to OSM
4216
4217 NAME: name of the VCA
4218 """
4219 check_client_version(ctx.obj, ctx.command.name)
4220 vca = {}
4221 vca["name"] = name
4222 vca["endpoints"] = endpoints.split(",")
4223 vca["user"] = user
4224 vca["secret"] = secret
4225 vca["cacert"] = cacert
4226 vca["lxd-cloud"] = lxd_cloud
4227 vca["lxd-credentials"] = lxd_credentials
4228 vca["k8s-cloud"] = k8s_cloud
4229 vca["k8s-credentials"] = k8s_credentials
4230 if description:
4231 vca["description"] = description
4232 if model_config:
4233 model_config = load(model_config)
4234 vca["model-config"] = model_config
4235 ctx.obj.vca.create(name, vca)
4236
4237
4238 def load(data: Any):
4239 if os.path.isfile(data):
4240 return load_file(data)
4241 else:
4242 try:
4243 return json.loads(data)
4244 except ValueError as e:
4245 raise ClientException(e)
4246
4247
4248 def load_file(file_path: str) -> Dict:
4249 content = None
4250 with open(file_path, "r") as f:
4251 content = f.read()
4252 try:
4253 return yaml.safe_load(content)
4254 except yaml.scanner.ScannerError:
4255 pass
4256 try:
4257 return json.loads(content)
4258 except ValueError:
4259 pass
4260 raise ClientException(f"{file_path} must be a valid yaml or json file")
4261
4262
4263 @cli_osm.command(name="vca-update", short_help="updates a K8s cluster")
4264 @click.argument("name")
4265 @click.option(
4266 "--endpoints", help="Comma-separated list of IP or hostnames of the Juju controller"
4267 )
4268 @click.option("--user", help="Username with admin priviledges")
4269 @click.option("--secret", help="Password of the specified username")
4270 @click.option("--cacert", help="CA certificate")
4271 @click.option(
4272 "--lxd-cloud",
4273 help="Name of the cloud that will be used for LXD containers (LXD proxy charms)",
4274 )
4275 @click.option(
4276 "--lxd-credentials",
4277 help="Name of the cloud credentialsto be used for the LXD cloud",
4278 )
4279 @click.option(
4280 "--k8s-cloud",
4281 help="Name of the cloud that will be used for K8s containers (K8s proxy charms)",
4282 )
4283 @click.option(
4284 "--k8s-credentials",
4285 help="Name of the cloud credentialsto be used for the K8s cloud",
4286 )
4287 @click.option(
4288 "--model-config",
4289 help="Configuration options for the models",
4290 )
4291 @click.option("--description", default=None, help="human readable description")
4292 @click.pass_context
4293 def vca_update(
4294 ctx,
4295 name,
4296 endpoints,
4297 user,
4298 secret,
4299 cacert,
4300 lxd_cloud,
4301 lxd_credentials,
4302 k8s_cloud,
4303 k8s_credentials,
4304 model_config,
4305 description,
4306 ):
4307 """updates a K8s cluster
4308
4309 NAME: name or ID of the K8s cluster
4310 """
4311 check_client_version(ctx.obj, ctx.command.name)
4312 vca = {}
4313 vca["name"] = name
4314 if endpoints:
4315 vca["endpoints"] = endpoints.split(",")
4316 if user:
4317 vca["user"] = user
4318 if secret:
4319 vca["secret"] = secret
4320 if cacert:
4321 vca["cacert"] = cacert
4322 if lxd_cloud:
4323 vca["lxd-cloud"] = lxd_cloud
4324 if lxd_credentials:
4325 vca["lxd-credentials"] = lxd_credentials
4326 if k8s_cloud:
4327 vca["k8s-cloud"] = k8s_cloud
4328 if k8s_credentials:
4329 vca["k8s-credentials"] = k8s_credentials
4330 if description:
4331 vca["description"] = description
4332 if model_config:
4333 model_config = load(model_config)
4334 vca["model-config"] = model_config
4335 ctx.obj.vca.update(name, vca)
4336
4337
4338 @cli_osm.command(name="vca-delete", short_help="deletes a K8s cluster")
4339 @click.argument("name")
4340 @click.option(
4341 "--force", is_flag=True, help="forces the deletion from the DB (not recommended)"
4342 )
4343 @click.pass_context
4344 def vca_delete(ctx, name, force):
4345 """deletes a K8s cluster
4346
4347 NAME: name or ID of the K8s cluster to be deleted
4348 """
4349 check_client_version(ctx.obj, ctx.command.name)
4350 ctx.obj.vca.delete(name, force=force)
4351
4352
4353 @cli_osm.command(name="vca-list")
4354 @click.option(
4355 "--filter",
4356 default=None,
4357 multiple=True,
4358 help="restricts the list to the VCAs matching the filter",
4359 )
4360 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
4361 @click.option("--long", is_flag=True, help="get more details")
4362 @click.pass_context
4363 def vca_list(ctx, filter, literal, long):
4364 """list VCAs"""
4365 check_client_version(ctx.obj, ctx.command.name)
4366 if filter:
4367 filter = "&".join(filter)
4368 resp = ctx.obj.vca.list(filter)
4369 if literal:
4370 print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
4371 return
4372 if long:
4373 table = PrettyTable(
4374 ["Name", "Id", "Project", "Operational State", "Detailed Status"]
4375 )
4376 project_list = ctx.obj.project.list()
4377 else:
4378 table = PrettyTable(["Name", "Id", "Operational State"])
4379 for vca in resp:
4380 logger.debug("VCA details: {}".format(yaml.safe_dump(vca)))
4381 if long:
4382 project_id, project_name = get_project(project_list, vca)
4383 detailed_status = vca.get("_admin", {}).get("detailed-status", "-")
4384 table.add_row(
4385 [
4386 vca["name"],
4387 vca["_id"],
4388 project_name,
4389 vca.get("_admin", {}).get("operationalState", "-"),
4390 wrap_text(text=detailed_status, width=40),
4391 ]
4392 )
4393 else:
4394 table.add_row(
4395 [
4396 vca["name"],
4397 vca["_id"],
4398 vca.get("_admin", {}).get("operationalState", "-"),
4399 ]
4400 )
4401 table.align = "l"
4402 print(table)
4403
4404
4405 @cli_osm.command(name="vca-show", short_help="shows the details of a K8s cluster")
4406 @click.argument("name")
4407 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
4408 @click.pass_context
4409 def vca_show(ctx, name, literal):
4410 """shows the details of a K8s cluster
4411
4412 NAME: name or ID of the K8s cluster
4413 """
4414 # try:
4415 resp = ctx.obj.vca.get(name)
4416 if literal:
4417 print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
4418 return
4419 table = PrettyTable(["key", "attribute"])
4420 for k, v in list(resp.items()):
4421 table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)])
4422 table.align = "l"
4423 print(table)
4424
4425
4426 ###########################
4427 # Repo operations
4428 ###########################
4429
4430
4431 @cli_osm.command(name="repo-add", short_help="adds a repo to OSM")
4432 @click.argument("name")
4433 @click.argument("uri")
4434 @click.option(
4435 "--type",
4436 type=click.Choice(["helm-chart", "juju-bundle", "osm"]),
4437 default="osm",
4438 help="type of repo (helm-chart for Helm Charts, juju-bundle for Juju Bundles, osm for OSM Repositories)",
4439 )
4440 @click.option("--description", default=None, help="human readable description")
4441 @click.option(
4442 "--user", default=None, help="OSM repository: The username of the OSM repository"
4443 )
4444 @click.option(
4445 "--password",
4446 default=None,
4447 help="OSM repository: The password of the OSM repository",
4448 )
4449 # @click.option('--wait',
4450 # is_flag=True,
4451 # help='do not return the control immediately, but keep it until the operation is completed, or timeout')
4452 @click.pass_context
4453 def repo_add(ctx, **kwargs):
4454 """adds a repo to OSM
4455
4456 NAME: name of the repo
4457 URI: URI of the repo
4458 """
4459 # try:
4460 kwargs = {k: v for k, v in kwargs.items() if v is not None}
4461 repo = kwargs
4462 repo["url"] = repo.pop("uri")
4463 if repo["type"] in ["helm-chart", "juju-bundle"]:
4464 ctx.obj.repo.create(repo["name"], repo)
4465 else:
4466 ctx.obj.osmrepo.create(repo["name"], repo)
4467 # except ClientException as e:
4468 # print(str(e))
4469 # exit(1)
4470
4471
4472 @cli_osm.command(name="repo-update", short_help="updates a repo in OSM")
4473 @click.argument("name")
4474 @click.option("--newname", help="New name for the repo")
4475 @click.option("--uri", help="URI of the repo")
4476 @click.option("--description", help="human readable description")
4477 # @click.option('--wait',
4478 # is_flag=True,
4479 # help='do not return the control immediately, but keep it until the operation is completed, or timeout')
4480 @click.pass_context
4481 def repo_update(ctx, name, newname, uri, description):
4482 """updates a repo in OSM
4483
4484 NAME: name of the repo
4485 """
4486 # try:
4487 check_client_version(ctx.obj, ctx.command.name)
4488 repo = {}
4489 if newname:
4490 repo["name"] = newname
4491 if uri:
4492 repo["uri"] = uri
4493 if description:
4494 repo["description"] = description
4495 try:
4496 ctx.obj.repo.update(name, repo)
4497 except NotFound:
4498 ctx.obj.osmrepo.update(name, repo)
4499
4500 # except ClientException as e:
4501 # print(str(e))
4502 # exit(1)
4503
4504
4505 @cli_osm.command(
4506 name="repo-index", short_help="Index a repository from a folder with artifacts"
4507 )
4508 @click.option(
4509 "--origin", default=".", help="origin path where the artifacts are located"
4510 )
4511 @click.option(
4512 "--destination", default=".", help="destination path where the index is deployed"
4513 )
4514 @click.pass_context
4515 def repo_index(ctx, origin, destination):
4516 """Index a repository
4517
4518 NAME: name or ID of the repo to be deleted
4519 """
4520 check_client_version(ctx.obj, ctx.command.name)
4521 ctx.obj.osmrepo.repo_index(origin, destination)
4522
4523
4524 @cli_osm.command(name="repo-delete", short_help="deletes a repo")
4525 @click.argument("name")
4526 @click.option(
4527 "--force", is_flag=True, help="forces the deletion from the DB (not recommended)"
4528 )
4529 # @click.option('--wait',
4530 # is_flag=True,
4531 # help='do not return the control immediately, but keep it until the operation is completed, or timeout')
4532 @click.pass_context
4533 def repo_delete(ctx, name, force):
4534 """deletes a repo
4535
4536 NAME: name or ID of the repo to be deleted
4537 """
4538 logger.debug("")
4539 try:
4540 ctx.obj.repo.delete(name, force=force)
4541 except NotFound:
4542 ctx.obj.osmrepo.delete(name, force=force)
4543 # except ClientException as e:
4544 # print(str(e))
4545 # exit(1)
4546
4547
4548 @cli_osm.command(name="repo-list")
4549 @click.option(
4550 "--filter",
4551 default=None,
4552 multiple=True,
4553 help="restricts the list to the repos matching the filter",
4554 )
4555 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
4556 @click.pass_context
4557 def repo_list(ctx, filter, literal):
4558 """list all repos"""
4559 # try:
4560 # K8s Repositories
4561 check_client_version(ctx.obj, ctx.command.name)
4562 if filter:
4563 filter = "&".join(filter)
4564 resp = ctx.obj.repo.list(filter)
4565 resp += ctx.obj.osmrepo.list(filter)
4566 if literal:
4567 print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
4568 return
4569 table = PrettyTable(["Name", "Id", "Type", "URI", "Description"])
4570 for repo in resp:
4571 # cluster['k8s-nets'] = json.dumps(yaml.safe_load(cluster['k8s-nets']))
4572 table.add_row(
4573 [
4574 repo["name"],
4575 repo["_id"],
4576 repo["type"],
4577 repo["url"],
4578 trunc_text(repo.get("description") or "", 40),
4579 ]
4580 )
4581 table.align = "l"
4582 print(table)
4583
4584 # except ClientException as e:
4585 # print(str(e))
4586 # exit(1)
4587
4588
4589 @cli_osm.command(name="repo-show", short_help="shows the details of a repo")
4590 @click.argument("name")
4591 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
4592 @click.pass_context
4593 def repo_show(ctx, name, literal):
4594 """shows the details of a repo
4595
4596 NAME: name or ID of the repo
4597 """
4598 try:
4599 resp = ctx.obj.repo.get(name)
4600 except NotFound:
4601 resp = ctx.obj.osmrepo.get(name)
4602
4603 if literal:
4604 if resp:
4605 print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
4606 return
4607 table = PrettyTable(["key", "attribute"])
4608 if resp:
4609 for k, v in list(resp.items()):
4610 table.add_row([k, json.dumps(v, indent=2)])
4611
4612 table.align = "l"
4613 print(table)
4614 # except ClientException as e:
4615 # print(str(e))
4616 # exit(1)
4617
4618
4619 ####################
4620 # Project mgmt operations
4621 ####################
4622
4623
4624 @cli_osm.command(name="project-create", short_help="creates a new project")
4625 @click.argument("name")
4626 # @click.option('--description',
4627 # default='no description',
4628 # help='human readable description')
4629 @click.option("--domain-name", "domain_name", default=None, help="assign to a domain")
4630 @click.option(
4631 "--quotas",
4632 "quotas",
4633 multiple=True,
4634 default=None,
4635 help="provide quotas. Can be used several times: 'quota1=number[,quota2=number,...]'. Quotas can be one "
4636 "of vnfds, nsds, nsts, pdus, nsrs, nsis, vim_accounts, wim_accounts, sdns, k8sclusters, k8srepos",
4637 )
4638 @click.pass_context
4639 def project_create(ctx, name, domain_name, quotas):
4640 """Creates a new project
4641
4642 NAME: name of the project
4643 DOMAIN_NAME: optional domain name for the project when keystone authentication is used
4644 QUOTAS: set quotas for the project
4645 """
4646 logger.debug("")
4647 project = {"name": name}
4648 if domain_name:
4649 project["domain_name"] = domain_name
4650 quotas_dict = _process_project_quotas(quotas)
4651 if quotas_dict:
4652 project["quotas"] = quotas_dict
4653
4654 # try:
4655 check_client_version(ctx.obj, ctx.command.name)
4656 ctx.obj.project.create(name, project)
4657 # except ClientException as e:
4658 # print(str(e))
4659 # exit(1)
4660
4661
4662 def _process_project_quotas(quota_list):
4663 quotas_dict = {}
4664 if not quota_list:
4665 return quotas_dict
4666 try:
4667 for quota in quota_list:
4668 for single_quota in quota.split(","):
4669 k, v = single_quota.split("=")
4670 quotas_dict[k] = None if v in ("None", "null", "") else int(v)
4671 except (ValueError, TypeError):
4672 raise ClientException(
4673 "invalid format for 'quotas'. Use 'k1=v1,v1=v2'. v must be a integer or null"
4674 )
4675 return quotas_dict
4676
4677
4678 @cli_osm.command(name="project-delete", short_help="deletes a project")
4679 @click.argument("name")
4680 # @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
4681 @click.pass_context
4682 def project_delete(ctx, name):
4683 """deletes a project
4684
4685 NAME: name or ID of the project to be deleted
4686 """
4687 logger.debug("")
4688 # try:
4689 check_client_version(ctx.obj, ctx.command.name)
4690 ctx.obj.project.delete(name)
4691 # except ClientException as e:
4692 # print(str(e))
4693 # exit(1)
4694
4695
4696 @cli_osm.command(name="project-list", short_help="list all projects")
4697 @click.option(
4698 "--filter",
4699 default=None,
4700 multiple=True,
4701 help="restricts the list to the projects matching the filter",
4702 )
4703 @click.pass_context
4704 def project_list(ctx, filter):
4705 """list all projects"""
4706 logger.debug("")
4707 # try:
4708 check_client_version(ctx.obj, ctx.command.name)
4709 if filter:
4710 filter = "&".join(filter)
4711 resp = ctx.obj.project.list(filter)
4712 # except ClientException as e:
4713 # print(str(e))
4714 # exit(1)
4715 table = PrettyTable(["name", "id"])
4716 for proj in resp:
4717 table.add_row([proj["name"], proj["_id"]])
4718 table.align = "l"
4719 print(table)
4720
4721
4722 @cli_osm.command(name="project-show", short_help="shows the details of a project")
4723 @click.argument("name")
4724 @click.pass_context
4725 def project_show(ctx, name):
4726 """shows the details of a project
4727
4728 NAME: name or ID of the project
4729 """
4730 logger.debug("")
4731 # try:
4732 check_client_version(ctx.obj, ctx.command.name)
4733 resp = ctx.obj.project.get(name)
4734 # except ClientException as e:
4735 # print(str(e))
4736 # exit(1)
4737
4738 table = PrettyTable(["key", "attribute"])
4739 for k, v in resp.items():
4740 table.add_row([k, json.dumps(v, indent=2)])
4741 table.align = "l"
4742 print(table)
4743
4744
4745 @cli_osm.command(
4746 name="project-update", short_help="updates a project (only the name can be updated)"
4747 )
4748 @click.argument("project")
4749 @click.option("--name", default=None, help="new name for the project")
4750 @click.option(
4751 "--quotas",
4752 "quotas",
4753 multiple=True,
4754 default=None,
4755 help="change quotas. Can be used several times: 'quota1=number|empty[,quota2=...]' "
4756 "(use empty to reset quota to default",
4757 )
4758 @click.pass_context
4759 def project_update(ctx, project, name, quotas):
4760 """
4761 Update a project name
4762
4763 :param ctx:
4764 :param project: id or name of the project to modify
4765 :param name: new name for the project
4766 :param quotas: change quotas of the project
4767 :return:
4768 """
4769 logger.debug("")
4770 project_changes = {}
4771 if name:
4772 project_changes["name"] = name
4773 quotas_dict = _process_project_quotas(quotas)
4774 if quotas_dict:
4775 project_changes["quotas"] = quotas_dict
4776
4777 # try:
4778 check_client_version(ctx.obj, ctx.command.name)
4779 ctx.obj.project.update(project, project_changes)
4780 # except ClientException as e:
4781 # print(str(e))
4782
4783
4784 ####################
4785 # User mgmt operations
4786 ####################
4787
4788
4789 @cli_osm.command(name="user-create", short_help="creates a new user")
4790 @click.argument("username")
4791 @click.option(
4792 "--password",
4793 prompt=True,
4794 hide_input=True,
4795 confirmation_prompt=True,
4796 help="user password",
4797 )
4798 @click.option(
4799 "--projects",
4800 # prompt="Comma separate list of projects",
4801 multiple=True,
4802 callback=lambda ctx, param, value: "".join(value).split(",")
4803 if all(len(x) == 1 for x in value)
4804 else value,
4805 help="list of project ids that the user belongs to",
4806 )
4807 @click.option(
4808 "--project-role-mappings",
4809 "project_role_mappings",
4810 default=None,
4811 multiple=True,
4812 help="assign role(s) in a project. Can be used several times: 'project,role1[,role2,...]'",
4813 )
4814 @click.option("--domain-name", "domain_name", default=None, help="assign to a domain")
4815 @click.pass_context
4816 def user_create(ctx, username, password, projects, project_role_mappings, domain_name):
4817 """Creates a new user
4818
4819 \b
4820 USERNAME: name of the user
4821 PASSWORD: password of the user
4822 PROJECTS: projects assigned to user (internal only)
4823 PROJECT_ROLE_MAPPING: roles in projects assigned to user (keystone)
4824 DOMAIN_NAME: optional domain name for the user when keystone authentication is used
4825 """
4826 logger.debug("")
4827 user = {}
4828 user["username"] = username
4829 user["password"] = password
4830 user["projects"] = projects
4831 user["project_role_mappings"] = project_role_mappings
4832 if domain_name:
4833 user["domain_name"] = domain_name
4834
4835 # try:
4836 check_client_version(ctx.obj, ctx.command.name)
4837 ctx.obj.user.create(username, user)
4838 # except ClientException as e:
4839 # print(str(e))
4840 # exit(1)
4841
4842
4843 @cli_osm.command(name="user-update", short_help="updates user information")
4844 @click.argument("username")
4845 @click.option(
4846 "--password",
4847 # prompt=True,
4848 # hide_input=True,
4849 # confirmation_prompt=True,
4850 help="user password",
4851 )
4852 @click.option("--set-username", "set_username", default=None, help="change username")
4853 @click.option(
4854 "--set-project",
4855 "set_project",
4856 default=None,
4857 multiple=True,
4858 help="create/replace the roles for this project: 'project,role1[,role2,...]'",
4859 )
4860 @click.option(
4861 "--remove-project",
4862 "remove_project",
4863 default=None,
4864 multiple=True,
4865 help="removes project from user: 'project'",
4866 )
4867 @click.option(
4868 "--add-project-role",
4869 "add_project_role",
4870 default=None,
4871 multiple=True,
4872 help="assign role(s) in a project. Can be used several times: 'project,role1[,role2,...]'",
4873 )
4874 @click.option(
4875 "--remove-project-role",
4876 "remove_project_role",
4877 default=None,
4878 multiple=True,
4879 help="remove role(s) in a project. Can be used several times: 'project,role1[,role2,...]'",
4880 )
4881 @click.option("--change_password", "change_password", help="user's current password")
4882 @click.option(
4883 "--new_password",
4884 "new_password",
4885 help="user's new password to update in expiry condition",
4886 )
4887 @click.pass_context
4888 def user_update(
4889 ctx,
4890 username,
4891 password,
4892 set_username,
4893 set_project,
4894 remove_project,
4895 add_project_role,
4896 remove_project_role,
4897 change_password,
4898 new_password,
4899 ):
4900 """Update a user information
4901
4902 \b
4903 USERNAME: name of the user
4904 PASSWORD: new password
4905 SET_USERNAME: new username
4906 SET_PROJECT: creating mappings for project/role(s)
4907 REMOVE_PROJECT: deleting mappings for project/role(s)
4908 ADD_PROJECT_ROLE: adding mappings for project/role(s)
4909 REMOVE_PROJECT_ROLE: removing mappings for project/role(s)
4910 CHANGE_PASSWORD: user's current password to change
4911 NEW_PASSWORD: user's new password to update in expiry condition
4912 """
4913 logger.debug("")
4914 user = {}
4915 user["password"] = password
4916 user["username"] = set_username
4917 user["set-project"] = set_project
4918 user["remove-project"] = remove_project
4919 user["add-project-role"] = add_project_role
4920 user["remove-project-role"] = remove_project_role
4921 user["change_password"] = change_password
4922 user["new_password"] = new_password
4923
4924 # try:
4925 check_client_version(ctx.obj, ctx.command.name)
4926 if not user.get("change_password"):
4927 ctx.obj.user.update(username, user)
4928 else:
4929 ctx.obj.user.update(username, user, pwd_change=True)
4930 # except ClientException as e:
4931 # print(str(e))
4932 # exit(1)
4933
4934
4935 @cli_osm.command(name="user-delete", short_help="deletes a user")
4936 @click.argument("name")
4937 # @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
4938 @click.pass_context
4939 def user_delete(ctx, name):
4940 """deletes a user
4941
4942 \b
4943 NAME: name or ID of the user to be deleted
4944 """
4945 logger.debug("")
4946 # try:
4947 check_client_version(ctx.obj, ctx.command.name)
4948 ctx.obj.user.delete(name)
4949 # except ClientException as e:
4950 # print(str(e))
4951 # exit(1)
4952
4953
4954 @cli_osm.command(name="user-list", short_help="list all users")
4955 @click.option(
4956 "--filter",
4957 default=None,
4958 multiple=True,
4959 help="restricts the list to the users matching the filter",
4960 )
4961 @click.pass_context
4962 def user_list(ctx, filter):
4963 """list all users"""
4964 # try:
4965 check_client_version(ctx.obj, ctx.command.name)
4966 if filter:
4967 filter = "&".join(filter)
4968 resp = ctx.obj.user.list(filter)
4969 # except ClientException as e:
4970 # print(str(e))
4971 # exit(1)
4972 table = PrettyTable(["name", "id"])
4973 for user in resp:
4974 table.add_row([user["username"], user["_id"]])
4975 table.align = "l"
4976 print(table)
4977
4978
4979 @cli_osm.command(name="user-show", short_help="shows the details of a user")
4980 @click.argument("name")
4981 @click.pass_context
4982 def user_show(ctx, name):
4983 """shows the details of a user
4984
4985 NAME: name or ID of the user
4986 """
4987 logger.debug("")
4988 # try:
4989 check_client_version(ctx.obj, ctx.command.name)
4990 resp = ctx.obj.user.get(name)
4991 if "password" in resp:
4992 resp["password"] = "********"
4993 # except ClientException as e:
4994 # print(str(e))
4995 # exit(1)
4996
4997 table = PrettyTable(["key", "attribute"])
4998 for k, v in resp.items():
4999 table.add_row([k, json.dumps(v, indent=2)])
5000 table.align = "l"
5001 print(table)
5002
5003
5004 ####################
5005 # Fault Management operations
5006 ####################
5007
5008
5009 @cli_osm.command(name="ns-alarm-create")
5010 @click.argument("name")
5011 @click.option("--ns", prompt=True, help="NS instance id or name")
5012 @click.option(
5013 "--vnf", prompt=True, help="VNF name (VNF member index as declared in the NSD)"
5014 )
5015 @click.option("--vdu", prompt=True, help="VDU name (VDU name as declared in the VNFD)")
5016 @click.option("--metric", prompt=True, help="Name of the metric (e.g. cpu_utilization)")
5017 @click.option(
5018 "--severity",
5019 default="WARNING",
5020 help="severity of the alarm (WARNING, MINOR, MAJOR, CRITICAL, INDETERMINATE)",
5021 )
5022 @click.option(
5023 "--threshold_value",
5024 prompt=True,
5025 help="threshold value that, when crossed, an alarm is triggered",
5026 )
5027 @click.option(
5028 "--threshold_operator",
5029 prompt=True,
5030 help="threshold operator describing the comparison (GE, LE, GT, LT, EQ)",
5031 )
5032 @click.option(
5033 "--statistic",
5034 default="AVERAGE",
5035 help="statistic (AVERAGE, MINIMUM, MAXIMUM, COUNT, SUM)",
5036 )
5037 @click.pass_context
5038 def ns_alarm_create(
5039 ctx,
5040 name,
5041 ns,
5042 vnf,
5043 vdu,
5044 metric,
5045 severity,
5046 threshold_value,
5047 threshold_operator,
5048 statistic,
5049 ):
5050 """creates a new alarm for a NS instance"""
5051 # TODO: Check how to validate threshold_value.
5052 # Should it be an integer (1-100), percentage, or decimal (0.01-1.00)?
5053 logger.debug("")
5054 # try:
5055 ns_instance = ctx.obj.ns.get(ns)
5056 alarm = {}
5057 alarm["alarm_name"] = name
5058 alarm["ns_id"] = ns_instance["_id"]
5059 alarm["correlation_id"] = ns_instance["_id"]
5060 alarm["vnf_member_index"] = vnf
5061 alarm["vdu_name"] = vdu
5062 alarm["metric_name"] = metric
5063 alarm["severity"] = severity
5064 alarm["threshold_value"] = int(threshold_value)
5065 alarm["operation"] = threshold_operator
5066 alarm["statistic"] = statistic
5067 check_client_version(ctx.obj, ctx.command.name)
5068 ctx.obj.ns.create_alarm(alarm)
5069 # except ClientException as e:
5070 # print(str(e))
5071 # exit(1)
5072
5073
5074 # @cli_osm.command(name='ns-alarm-delete')
5075 # @click.argument('name')
5076 # @click.pass_context
5077 # def ns_alarm_delete(ctx, name):
5078 # """deletes an alarm
5079 #
5080 # NAME: name of the alarm to be deleted
5081 # """
5082 # try:
5083 # check_client_version(ctx.obj, ctx.command.name)
5084 # ctx.obj.ns.delete_alarm(name)
5085 # except ClientException as e:
5086 # print(str(e))
5087 # exit(1)
5088
5089
5090 ####################
5091 # Performance Management operations
5092 ####################
5093
5094
5095 @cli_osm.command(
5096 name="ns-metric-export",
5097 short_help="exports a metric to the internal OSM bus, which can be read by other apps",
5098 )
5099 @click.option("--ns", prompt=True, help="NS instance id or name")
5100 @click.option(
5101 "--vnf", prompt=True, help="VNF name (VNF member index as declared in the NSD)"
5102 )
5103 @click.option("--vdu", prompt=True, help="VDU name (VDU name as declared in the VNFD)")
5104 @click.option("--metric", prompt=True, help="name of the metric (e.g. cpu_utilization)")
5105 # @click.option('--period', default='1w',
5106 # help='metric collection period (e.g. 20s, 30m, 2h, 3d, 1w)')
5107 @click.option(
5108 "--interval", help="periodic interval (seconds) to export metrics continuously"
5109 )
5110 @click.pass_context
5111 def ns_metric_export(ctx, ns, vnf, vdu, metric, interval):
5112 """exports a metric to the internal OSM bus, which can be read by other apps"""
5113 # TODO: Check how to validate interval.
5114 # Should it be an integer (seconds), or should a suffix (s,m,h,d,w) also be permitted?
5115 logger.debug("")
5116 # try:
5117 ns_instance = ctx.obj.ns.get(ns)
5118 metric_data = {}
5119 metric_data["ns_id"] = ns_instance["_id"]
5120 metric_data["correlation_id"] = ns_instance["_id"]
5121 metric_data["vnf_member_index"] = vnf
5122 metric_data["vdu_name"] = vdu
5123 metric_data["metric_name"] = metric
5124 metric_data["collection_unit"] = "WEEK"
5125 metric_data["collection_period"] = 1
5126 check_client_version(ctx.obj, ctx.command.name)
5127 if not interval:
5128 print("{}".format(ctx.obj.ns.export_metric(metric_data)))
5129 else:
5130 i = 1
5131 while True:
5132 print("{} {}".format(ctx.obj.ns.export_metric(metric_data), i))
5133 time.sleep(int(interval))
5134 i += 1
5135 # except ClientException as e:
5136 # print(str(e))
5137 # exit(1)
5138
5139
5140 #################
5141 # Subscription operations
5142 #################
5143
5144
5145 @cli_osm.command(
5146 name="subscription-create",
5147 short_help="creates a new subscription to a specific event",
5148 )
5149 @click.option(
5150 "--event_type",
5151 # type=click.Choice(['ns', 'nspkg', 'vnfpkg'], case_sensitive=False))
5152 type=click.Choice(["ns"], case_sensitive=False),
5153 help="event type to be subscribed (for the moment, only ns is supported)",
5154 )
5155 @click.option("--event", default=None, help="specific yaml configuration for the event")
5156 @click.option(
5157 "--event_file", default=None, help="specific yaml configuration file for the event"
5158 )
5159 @click.pass_context
5160 def subscription_create(ctx, event_type, event, event_file):
5161 """creates a new subscription to a specific event"""
5162 logger.debug("")
5163 check_client_version(ctx.obj, ctx.command.name)
5164 if event_file:
5165 if event:
5166 raise ClientException(
5167 '"--event" option is incompatible with "--event_file" option'
5168 )
5169 with open(event_file, "r") as cf:
5170 event = cf.read()
5171 ctx.obj.subscription.create(event_type, event)
5172
5173
5174 @cli_osm.command(name="subscription-delete", short_help="deletes a subscription")
5175 @click.option(
5176 "--event_type",
5177 # type=click.Choice(['ns', 'nspkg', 'vnfpkg'], case_sensitive=False))
5178 type=click.Choice(["ns"], case_sensitive=False),
5179 help="event type to be subscribed (for the moment, only ns is supported)",
5180 )
5181 @click.argument("subscription_id")
5182 @click.option(
5183 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
5184 )
5185 @click.pass_context
5186 def subscription_delete(ctx, event_type, subscription_id, force):
5187 """deletes a subscription
5188
5189 SUBSCRIPTION_ID: ID of the subscription to be deleted
5190 """
5191 logger.debug("")
5192 check_client_version(ctx.obj, ctx.command.name)
5193 ctx.obj.subscription.delete(event_type, subscription_id, force)
5194
5195
5196 @cli_osm.command(name="subscription-list", short_help="list all subscriptions")
5197 @click.option(
5198 "--event_type",
5199 # type=click.Choice(['ns', 'nspkg', 'vnfpkg'], case_sensitive=False))
5200 type=click.Choice(["ns"], case_sensitive=False),
5201 help="event type to be subscribed (for the moment, only ns is supported)",
5202 )
5203 @click.option(
5204 "--filter",
5205 default=None,
5206 multiple=True,
5207 help="restricts the list to the subscriptions matching the filter",
5208 )
5209 @click.pass_context
5210 def subscription_list(ctx, event_type, filter):
5211 """list all subscriptions"""
5212 logger.debug("")
5213 check_client_version(ctx.obj, ctx.command.name)
5214 if filter:
5215 filter = "&".join(filter)
5216 resp = ctx.obj.subscription.list(event_type, filter)
5217 table = PrettyTable(["id", "filter", "CallbackUri"])
5218 for sub in resp:
5219 table.add_row(
5220 [
5221 sub["_id"],
5222 wrap_text(text=json.dumps(sub["filter"], indent=2), width=70),
5223 sub["CallbackUri"],
5224 ]
5225 )
5226 table.align = "l"
5227 print(table)
5228
5229
5230 @cli_osm.command(
5231 name="subscription-show", short_help="shows the details of a subscription"
5232 )
5233 @click.argument("subscription_id")
5234 @click.option(
5235 "--event_type",
5236 # type=click.Choice(['ns', 'nspkg', 'vnfpkg'], case_sensitive=False))
5237 type=click.Choice(["ns"], case_sensitive=False),
5238 help="event type to be subscribed (for the moment, only ns is supported)",
5239 )
5240 @click.option(
5241 "--filter",
5242 multiple=True,
5243 help="restricts the information to the fields in the filter",
5244 )
5245 @click.pass_context
5246 def subscription_show(ctx, event_type, subscription_id, filter):
5247 """shows the details of a subscription
5248
5249 SUBSCRIPTION_ID: ID of the subscription
5250 """
5251 logger.debug("")
5252 # try:
5253 resp = ctx.obj.subscription.get(subscription_id)
5254 table = PrettyTable(["key", "attribute"])
5255 for k, v in list(resp.items()):
5256 if not filter or k in filter:
5257 table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)])
5258 table.align = "l"
5259 print(table)
5260
5261
5262 ####################
5263 # Other operations
5264 ####################
5265
5266
5267 @cli_osm.command(name="version", short_help="shows client and server versions")
5268 @click.pass_context
5269 def get_version(ctx):
5270 """shows client and server versions"""
5271 # try:
5272 check_client_version(ctx.obj, "version")
5273 print("Server version: {}".format(ctx.obj.get_version()))
5274 print(
5275 "Client version: {}".format(pkg_resources.get_distribution("osmclient").version)
5276 )
5277 # except ClientException as e:
5278 # print(str(e))
5279 # exit(1)
5280
5281
5282 @cli_osm.command(
5283 name="upload-package", short_help="uploads a VNF package or NS package"
5284 )
5285 @click.argument("filename")
5286 @click.option(
5287 "--skip-charm-build",
5288 default=False,
5289 is_flag=True,
5290 help="the charm will not be compiled, it is assumed to already exist",
5291 )
5292 @click.pass_context
5293 def upload_package(ctx, filename, skip_charm_build):
5294 """uploads a vnf package or ns package
5295
5296 filename: vnf or ns package folder, or vnf or ns package file (tar.gz)
5297 """
5298 logger.debug("")
5299 # try:
5300 ctx.obj.package.upload(filename, skip_charm_build=skip_charm_build)
5301 fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
5302 if fullclassname != "osmclient.sol005.client.Client":
5303 ctx.obj.package.wait_for_upload(filename)
5304 # except ClientException as e:
5305 # print(str(e))
5306 # exit(1)
5307
5308
5309 # @cli_osm.command(name='ns-scaling-show')
5310 # @click.argument('ns_name')
5311 # @click.pass_context
5312 # def show_ns_scaling(ctx, ns_name):
5313 # """shows the status of a NS scaling operation
5314 #
5315 # NS_NAME: name of the NS instance being scaled
5316 # """
5317 # try:
5318 # check_client_version(ctx.obj, ctx.command.name, 'v1')
5319 # resp = ctx.obj.ns.list()
5320 # except ClientException as e:
5321 # print(str(e))
5322 # exit(1)
5323 #
5324 # table = PrettyTable(
5325 # ['group-name',
5326 # 'instance-id',
5327 # 'operational status',
5328 # 'create-time',
5329 # 'vnfr ids'])
5330 #
5331 # for ns in resp:
5332 # if ns_name == ns['name']:
5333 # nsopdata = ctx.obj.ns.get_opdata(ns['id'])
5334 # scaling_records = nsopdata['nsr:nsr']['scaling-group-record']
5335 # for record in scaling_records:
5336 # if 'instance' in record:
5337 # instances = record['instance']
5338 # for inst in instances:
5339 # table.add_row(
5340 # [record['scaling-group-name-ref'],
5341 # inst['instance-id'],
5342 # inst['op-status'],
5343 # time.strftime('%Y-%m-%d %H:%M:%S',
5344 # time.localtime(
5345 # inst['create-time'])),
5346 # inst['vnfrs']])
5347 # table.align = 'l'
5348 # print(table)
5349
5350
5351 # @cli_osm.command(name='ns-scale')
5352 # @click.argument('ns_name')
5353 # @click.option('--ns_scale_group', prompt=True)
5354 # @click.option('--index', prompt=True)
5355 # @click.option('--wait',
5356 # required=False,
5357 # default=False,
5358 # is_flag=True,
5359 # help='do not return the control immediately, but keep it \
5360 # until the operation is completed, or timeout')
5361 # @click.pass_context
5362 # def ns_scale(ctx, ns_name, ns_scale_group, index, wait):
5363 # """scales NS
5364 #
5365 # NS_NAME: name of the NS instance to be scaled
5366 # """
5367 # try:
5368 # check_client_version(ctx.obj, ctx.command.name, 'v1')
5369 # ctx.obj.ns.scale(ns_name, ns_scale_group, index, wait=wait)
5370 # except ClientException as e:
5371 # print(str(e))
5372 # exit(1)
5373
5374
5375 # @cli_osm.command(name='config-agent-list')
5376 # @click.pass_context
5377 # def config_agent_list(ctx):
5378 # """list config agents"""
5379 # try:
5380 # check_client_version(ctx.obj, ctx.command.name, 'v1')
5381 # except ClientException as e:
5382 # print(str(e))
5383 # exit(1)
5384 # table = PrettyTable(['name', 'account-type', 'details'])
5385 # for account in ctx.obj.vca.list():
5386 # table.add_row(
5387 # [account['name'],
5388 # account['account-type'],
5389 # account['juju']])
5390 # table.align = 'l'
5391 # print(table)
5392
5393
5394 # @cli_osm.command(name='config-agent-delete')
5395 # @click.argument('name')
5396 # @click.pass_context
5397 # def config_agent_delete(ctx, name):
5398 # """deletes a config agent
5399 #
5400 # NAME: name of the config agent to be deleted
5401 # """
5402 # try:
5403 # check_client_version(ctx.obj, ctx.command.name, 'v1')
5404 # ctx.obj.vca.delete(name)
5405 # except ClientException as e:
5406 # print(str(e))
5407 # exit(1)
5408
5409
5410 # @cli_osm.command(name='config-agent-add')
5411 # @click.option('--name',
5412 # prompt=True)
5413 # @click.option('--account_type',
5414 # prompt=True)
5415 # @click.option('--server',
5416 # prompt=True)
5417 # @click.option('--user',
5418 # prompt=True)
5419 # @click.option('--secret',
5420 # prompt=True,
5421 # hide_input=True,
5422 # confirmation_prompt=True)
5423 # @click.pass_context
5424 # def config_agent_add(ctx, name, account_type, server, user, secret):
5425 # """adds a config agent"""
5426 # try:
5427 # check_client_version(ctx.obj, ctx.command.name, 'v1')
5428 # ctx.obj.vca.create(name, account_type, server, user, secret)
5429 # except ClientException as e:
5430 # print(str(e))
5431 # exit(1)
5432
5433
5434 # @cli_osm.command(name='ro-dump')
5435 # @click.pass_context
5436 # def ro_dump(ctx):
5437 # """shows RO agent information"""
5438 # check_client_version(ctx.obj, ctx.command.name, 'v1')
5439 # resp = ctx.obj.vim.get_resource_orchestrator()
5440 # table = PrettyTable(['key', 'attribute'])
5441 # for k, v in list(resp.items()):
5442 # table.add_row([k, json.dumps(v, indent=2)])
5443 # table.align = 'l'
5444 # print(table)
5445
5446
5447 # @cli_osm.command(name='vcs-list')
5448 # @click.pass_context
5449 # def vcs_list(ctx):
5450 # check_client_version(ctx.obj, ctx.command.name, 'v1')
5451 # resp = ctx.obj.utils.get_vcs_info()
5452 # table = PrettyTable(['component name', 'state'])
5453 # for component in resp:
5454 # table.add_row([component['component_name'], component['state']])
5455 # table.align = 'l'
5456 # print(table)
5457
5458
5459 @cli_osm.command(
5460 name="ns-action", short_help="executes an action/primitive over a NS instance"
5461 )
5462 @click.argument("ns_name")
5463 @click.option(
5464 "--vnf_name",
5465 default=None,
5466 help="member-vnf-index if the target is a vnf instead of a ns)",
5467 )
5468 @click.option("--kdu_name", default=None, help="kdu-name if the target is a kdu)")
5469 @click.option("--vdu_id", default=None, help="vdu-id if the target is a vdu")
5470 @click.option(
5471 "--vdu_count", default=None, type=int, help="number of vdu instance of this vdu_id"
5472 )
5473 @click.option("--action_name", prompt=True, help="action name")
5474 @click.option("--params", default=None, help="action params in YAML/JSON inline string")
5475 @click.option("--params_file", default=None, help="YAML/JSON file with action params")
5476 @click.option(
5477 "--timeout", required=False, default=None, type=int, help="timeout in seconds"
5478 )
5479 @click.option(
5480 "--wait",
5481 required=False,
5482 default=False,
5483 is_flag=True,
5484 help="do not return the control immediately, but keep it until the operation is completed, or timeout",
5485 )
5486 @click.pass_context
5487 def ns_action(
5488 ctx,
5489 ns_name,
5490 vnf_name,
5491 kdu_name,
5492 vdu_id,
5493 vdu_count,
5494 action_name,
5495 params,
5496 params_file,
5497 timeout,
5498 wait,
5499 ):
5500 """executes an action/primitive over a NS instance
5501
5502 NS_NAME: name or ID of the NS instance
5503 """
5504 logger.debug("")
5505 # try:
5506 check_client_version(ctx.obj, ctx.command.name)
5507 op_data = {}
5508 if vnf_name:
5509 op_data["member_vnf_index"] = vnf_name
5510 if kdu_name:
5511 op_data["kdu_name"] = kdu_name
5512 if vdu_id:
5513 op_data["vdu_id"] = vdu_id
5514 if vdu_count is not None:
5515 op_data["vdu_count_index"] = vdu_count
5516 if timeout:
5517 op_data["timeout_ns_action"] = timeout
5518 op_data["primitive"] = action_name
5519 if params_file:
5520 with open(params_file, "r") as pf:
5521 params = pf.read()
5522 if params:
5523 op_data["primitive_params"] = yaml.safe_load(params)
5524 else:
5525 op_data["primitive_params"] = {}
5526 print(ctx.obj.ns.exec_op(ns_name, op_name="action", op_data=op_data, wait=wait))
5527
5528 # except ClientException as e:
5529 # print(str(e))
5530 # exit(1)
5531
5532
5533 @cli_osm.command(
5534 name="vnf-scale", short_help="executes a VNF scale (adding/removing VDUs)"
5535 )
5536 @click.argument("ns_name")
5537 @click.argument("vnf_name")
5538 @click.option(
5539 "--scaling-group", prompt=True, help="scaling-group-descriptor name to use"
5540 )
5541 @click.option(
5542 "--scale-in", default=False, is_flag=True, help="performs a scale in operation"
5543 )
5544 @click.option(
5545 "--scale-out",
5546 default=False,
5547 is_flag=True,
5548 help="performs a scale out operation (by default)",
5549 )
5550 @click.option(
5551 "--timeout", required=False, default=None, type=int, help="timeout in seconds"
5552 )
5553 @click.option(
5554 "--wait",
5555 required=False,
5556 default=False,
5557 is_flag=True,
5558 help="do not return the control immediately, but keep it until the operation is completed, or timeout",
5559 )
5560 @click.pass_context
5561 def vnf_scale(
5562 ctx, ns_name, vnf_name, scaling_group, scale_in, scale_out, timeout, wait
5563 ):
5564 """
5565 Executes a VNF scale (adding/removing VDUs)
5566
5567 \b
5568 NS_NAME: name or ID of the NS instance.
5569 VNF_NAME: member-vnf-index in the NS to be scaled.
5570 """
5571 logger.debug("")
5572 # try:
5573 check_client_version(ctx.obj, ctx.command.name)
5574 if not scale_in and not scale_out:
5575 scale_out = True
5576 ctx.obj.ns.scale_vnf(
5577 ns_name, vnf_name, scaling_group, scale_in, scale_out, wait, timeout
5578 )
5579 # except ClientException as e:
5580 # print(str(e))
5581 # exit(1)
5582
5583
5584 @cli_osm.command(
5585 name="ns-update", short_help="executes an update of a Network Service."
5586 )
5587 @click.argument("ns_name")
5588 @click.option(
5589 "--updatetype", required=True, type=str, help="available types: CHANGE_VNFPKG"
5590 )
5591 @click.option(
5592 "--config",
5593 required=True,
5594 type=str,
5595 help="extra information for update operation as YAML/JSON inline string as --config"
5596 " '{changeVnfPackageData:[{vnfInstanceId: xxx, vnfdId: yyy}]}'",
5597 )
5598 @click.option(
5599 "--timeout", required=False, default=None, type=int, help="timeout in seconds"
5600 )
5601 @click.option(
5602 "--wait",
5603 required=False,
5604 default=False,
5605 is_flag=True,
5606 help="do not return the control immediately, but keep it until the operation is completed, or timeout",
5607 )
5608 @click.pass_context
5609 def update(ctx, ns_name, updatetype, config, timeout, wait):
5610 """Executes an update of a Network Service.
5611
5612 The update will check new revisions of the Network Functions that are part of the
5613 Network Service, and it will update them if needed.
5614 Sample update command: osm ns-update ns_instance_id --updatetype CHANGE_VNFPKG
5615 --config '{changeVnfPackageData: [{vnfInstanceId: id_x,vnfdId: id_y}]}' --timeout 300 --wait
5616
5617 NS_NAME: Network service instance name or ID.
5618
5619 """
5620 op_data = {
5621 "timeout": timeout,
5622 "updateType": updatetype,
5623 }
5624 if config:
5625 op_data["config"] = yaml.safe_load(config)
5626
5627 check_client_version(ctx.obj, ctx.command.name)
5628 ctx.obj.ns.update(ns_name, op_data, wait=wait)
5629
5630
5631 def iterator_split(iterator, separators):
5632 """
5633 Splits a tuple or list into several lists whenever a separator is found
5634 For instance, the following tuple will be separated with the separator "--vnf" as follows.
5635 From:
5636 ("--vnf", "A", "--cause", "cause_A", "--vdu", "vdu_A1", "--vnf", "B", "--cause", "cause_B", ...
5637 "--vdu", "vdu_B1", "--count_index", "1", "--run-day1", "--vdu", "vdu_B1", "--count_index", "2")
5638 To:
5639 [
5640 ("--vnf", "A", "--cause", "cause_A", "--vdu", "vdu_A1"),
5641 ("--vnf", "B", "--cause", "cause_B", "--vdu", "vdu_B1", "--count_index", "1", "--run-day1", ...
5642 "--vdu", "vdu_B1", "--count_index", "2")
5643 ]
5644
5645 Returns as many lists as separators are found
5646 """
5647 logger.debug("")
5648 if iterator[0] not in separators:
5649 raise ClientException(f"Expected one of {separators}. Received: {iterator[0]}.")
5650 list_of_lists = []
5651 first = 0
5652 for i in range(len(iterator)):
5653 if iterator[i] in separators:
5654 if i == first:
5655 continue
5656 if i - first < 2:
5657 raise ClientException(
5658 f"Expected at least one argument after separator (possible separators: {separators})."
5659 )
5660 list_of_lists.append(list(iterator[first:i]))
5661 first = i
5662 if (len(iterator) - first) < 2:
5663 raise ClientException(
5664 f"Expected at least one argument after separator (possible separators: {separators})."
5665 )
5666 else:
5667 list_of_lists.append(list(iterator[first : len(iterator)]))
5668 # logger.debug(f"List of lists: {list_of_lists}")
5669 return list_of_lists
5670
5671
5672 def process_common_heal_params(heal_vnf_dict, args):
5673 logger.debug("")
5674 current_item = "vnf"
5675 i = 0
5676 while i < len(args):
5677 if args[i] == "--cause":
5678 if (i + 1 >= len(args)) or args[i + 1].startswith("--"):
5679 raise ClientException("No cause was provided after --cause")
5680 heal_vnf_dict["cause"] = args[i + 1]
5681 i = i + 2
5682 continue
5683 if args[i] == "--run-day1":
5684 if current_item == "vnf":
5685 if "additionalParams" not in heal_vnf_dict:
5686 heal_vnf_dict["additionalParams"] = {}
5687 heal_vnf_dict["additionalParams"]["run-day1"] = True
5688 else:
5689 # if current_item == "vdu"
5690 heal_vnf_dict["additionalParams"]["vdu"][-1]["run-day1"] = True
5691 i = i + 1
5692 continue
5693 if args[i] == "--vdu":
5694 if "additionalParams" not in heal_vnf_dict:
5695 heal_vnf_dict["additionalParams"] = {}
5696 heal_vnf_dict["additionalParams"]["vdu"] = []
5697 if (i + 1 >= len(args)) or args[i + 1].startswith("--"):
5698 raise ClientException("No VDU ID was provided after --vdu")
5699 heal_vnf_dict["additionalParams"]["vdu"].append({"vdu-id": args[i + 1]})
5700 current_item = "vdu"
5701 i = i + 2
5702 continue
5703 if args[i] == "--count-index":
5704 if current_item == "vnf":
5705 raise ClientException(
5706 "Option --count-index only applies to VDU, not to VNF"
5707 )
5708 if (i + 1 >= len(args)) or args[i + 1].startswith("--"):
5709 raise ClientException("No count index was provided after --count-index")
5710 heal_vnf_dict["additionalParams"]["vdu"][-1]["count-index"] = int(
5711 args[i + 1]
5712 )
5713 i = i + 2
5714 continue
5715 i = i + 1
5716 return
5717
5718
5719 def process_ns_heal_params(ctx, param, value):
5720 """
5721 Processes the params in the command ns-heal
5722 Click does not allow advanced patterns for positional options like this:
5723 --vnf volumes_vnf --cause "Heal several_volumes-VM of several_volumes_vnf"
5724 --vdu several_volumes-VM
5725 --vnf charm_vnf --cause "Heal two VMs of native_manual_scale_charm_vnf"
5726 --vdu mgmtVM --count-index 1 --run-day1
5727 --vdu mgmtVM --count-index 2
5728
5729 It returns the dictionary with all the params stored in ctx.params["heal_params"]
5730 """
5731 logger.debug("")
5732 # logger.debug(f"Args: {value}")
5733 if param.name != "args":
5734 raise ClientException(f"Unexpected param: {param.name}")
5735 # Split the tuple "value" by "--vnf"
5736 vnfs = iterator_split(value, ["--vnf"])
5737 logger.debug(f"VNFs: {vnfs}")
5738 heal_dict = {}
5739 heal_dict["healVnfData"] = []
5740 for vnf in vnfs:
5741 # logger.debug(f"VNF: {vnf}")
5742 heal_vnf = {}
5743 if vnf[1].startswith("--"):
5744 raise ClientException("Expected a VNF_ID after --vnf")
5745 heal_vnf["vnfInstanceId"] = vnf[1]
5746 process_common_heal_params(heal_vnf, vnf[2:])
5747 heal_dict["healVnfData"].append(heal_vnf)
5748 ctx.params["heal_params"] = heal_dict
5749 return
5750
5751
5752 @cli_osm.command(
5753 name="ns-heal",
5754 short_help="heals (recreates) VNFs or VDUs of a NS instance",
5755 context_settings=dict(
5756 ignore_unknown_options=True,
5757 ),
5758 )
5759 @click.argument("ns_name")
5760 @click.argument(
5761 "args",
5762 nargs=-1,
5763 type=click.UNPROCESSED,
5764 callback=process_ns_heal_params,
5765 )
5766 @click.option("--timeout", type=int, default=None, help="timeout in seconds")
5767 @click.option(
5768 "--wait",
5769 default=False,
5770 is_flag=True,
5771 help="do not return the control immediately, but keep it until the operation is completed, or timeout",
5772 )
5773 @click.pass_context
5774 def ns_heal(ctx, ns_name, args, heal_params, timeout, wait):
5775 """heals (recreates) VNFs or VDUs of a NS instance
5776
5777 NS_NAME: name or ID of the NS instance
5778
5779 \b
5780 Options:
5781 --vnf TEXT VNF instance ID or VNF id in the NS [required]
5782 --cause TEXT human readable cause of the healing
5783 --run-day1 indicates whether or not to run day1 primitives for the VNF/VDU
5784 --vdu TEXT vdu-id
5785 --count-index INTEGER count-index
5786
5787 \b
5788 Example:
5789 osm ns-heal NS_NAME|NS_ID --vnf volumes_vnf --cause "Heal several_volumes-VM of several_volumes_vnf"
5790 --vdu several_volumes-VM
5791 --vnf charm_vnf --cause "Heal two VMs of native_manual_scale_charm_vnf"
5792 --vdu mgmtVM --count-index 1 --run-day1
5793 --vdu mgmtVM --count-index 2
5794 """
5795 logger.debug("")
5796 heal_dict = ctx.params["heal_params"]
5797 logger.debug(f"Heal dict:\n{yaml.safe_dump(heal_dict)}")
5798 # replace VNF id in the NS by the VNF instance ID
5799 for vnf in heal_dict["healVnfData"]:
5800 vnf_id = vnf["vnfInstanceId"]
5801 if not validate_uuid4(vnf_id):
5802 vnf_filter = f"member-vnf-index-ref={vnf_id}"
5803 vnf_list = ctx.obj.vnf.list(ns=ns_name, filter=vnf_filter)
5804 if len(vnf_list) == 0:
5805 raise ClientException(
5806 f"No VNF found in NS {ns_name} with filter {vnf_filter}"
5807 )
5808 elif len(vnf_list) == 1:
5809 vnf["vnfInstanceId"] = vnf_list[0]["_id"]
5810 else:
5811 raise ClientException(
5812 f"More than 1 VNF found in NS {ns_name} with filter {vnf_filter}"
5813 )
5814 logger.debug(f"Heal dict:\n{yaml.safe_dump(heal_dict)}")
5815 check_client_version(ctx.obj, ctx.command.name)
5816 ctx.obj.ns.heal(ns_name, heal_dict, wait, timeout)
5817 exit(0)
5818
5819
5820 def process_vnf_heal_params(ctx, param, value):
5821 """
5822 Processes the params in the command vnf-heal
5823 Click does not allow advanced patterns for positional options like this:
5824 --vdu mgmtVM --count-index 1 --run-day1 --vdu mgmtVM --count-index 2
5825
5826 It returns the dictionary with all the params stored in ctx.params["heal_params"]
5827 """
5828 logger.debug("")
5829 # logger.debug(f"Args: {value}")
5830 if param.name != "args":
5831 raise ClientException(f"Unexpected param: {param.name}")
5832 # Split the tuple "value" by "--vnf"
5833 vnf = value
5834 heal_dict = {}
5835 heal_dict["healVnfData"] = []
5836 logger.debug(f"VNF: {vnf}")
5837 heal_vnf = {"vnfInstanceId": "id_to_be_substituted"}
5838 process_common_heal_params(heal_vnf, vnf)
5839 heal_dict["healVnfData"].append(heal_vnf)
5840 ctx.params["heal_params"] = heal_dict
5841 return
5842
5843
5844 @cli_osm.command(
5845 name="vnf-heal",
5846 short_help="heals (recreates) a VNF instance or the VDUs of a VNF instance",
5847 context_settings=dict(
5848 ignore_unknown_options=True,
5849 ),
5850 )
5851 @click.argument("vnf_name")
5852 @click.argument(
5853 "args",
5854 nargs=-1,
5855 type=click.UNPROCESSED,
5856 callback=process_vnf_heal_params,
5857 )
5858 @click.option("--timeout", type=int, default=None, help="timeout in seconds")
5859 @click.option(
5860 "--wait",
5861 default=False,
5862 is_flag=True,
5863 help="do not return the control immediately, but keep it until the operation is completed, or timeout",
5864 )
5865 @click.pass_context
5866 def vnf_heal2(
5867 ctx,
5868 vnf_name,
5869 args,
5870 heal_params,
5871 timeout,
5872 wait,
5873 ):
5874 """heals (recreates) a VNF instance or the VDUs of a VNF instance
5875
5876 VNF_NAME: name or ID of the VNF instance
5877
5878 \b
5879 Options:
5880 --cause TEXT human readable cause of the healing of the VNF
5881 --run-day1 indicates whether or not to run day1 primitives for the VNF/VDU
5882 --vdu TEXT vdu-id
5883 --count-index INTEGER count-index
5884
5885 \b
5886 Example:
5887 osm vnf-heal VNF_INSTANCE_ID --vdu mgmtVM --count-index 1 --run-day1
5888 --vdu mgmtVM --count-index 2
5889 """
5890 logger.debug("")
5891 heal_dict = ctx.params["heal_params"]
5892 heal_dict["healVnfData"][-1]["vnfInstanceId"] = vnf_name
5893 logger.debug(f"Heal dict:\n{yaml.safe_dump(heal_dict)}")
5894 check_client_version(ctx.obj, ctx.command.name)
5895 vnfr = ctx.obj.vnf.get(vnf_name)
5896 ns_id = vnfr["nsr-id-ref"]
5897 ctx.obj.ns.heal(ns_id, heal_dict, wait, timeout)
5898 exit(0)
5899
5900
5901 @cli_osm.command(name="alarm-show", short_help="show alarm details")
5902 @click.argument("uuid")
5903 @click.pass_context
5904 def alarm_show(ctx, uuid):
5905 """Show alarm's detail information"""
5906
5907 check_client_version(ctx.obj, ctx.command.name)
5908 resp = ctx.obj.ns.get_alarm(uuid=uuid)
5909 alarm_filter = [
5910 "uuid",
5911 "name",
5912 "metric",
5913 "statistic",
5914 "threshold",
5915 "operation",
5916 "ns-id",
5917 "vnf-id",
5918 "vdu_name",
5919 "action",
5920 "status",
5921 ]
5922 table = PrettyTable(["key", "attribute"])
5923 try:
5924 # Arrange and return the response data
5925 alarm = resp.replace("ObjectId", "")
5926 for key in alarm_filter:
5927 if key == "uuid":
5928 value = alarm.get(key)
5929 key = "alarm-id"
5930 elif key == "name":
5931 value = alarm.get(key)
5932 key = "alarm-name"
5933 elif key == "ns-id":
5934 value = alarm["tags"].get("ns_id")
5935 elif key == "vdu_name":
5936 value = alarm["tags"].get("vdu_name")
5937 elif key == "status":
5938 value = alarm["alarm_status"]
5939 else:
5940 value = alarm[key]
5941 table.add_row([key, wrap_text(text=json.dumps(value, indent=2), width=100)])
5942 table.align = "l"
5943 print(table)
5944 except Exception:
5945 print(resp)
5946
5947
5948 # List alarm
5949 @cli_osm.command(name="alarm-list", short_help="list all alarms")
5950 @click.option(
5951 "--ns_id", default=None, required=False, help="List out alarm for given ns id"
5952 )
5953 @click.pass_context
5954 def alarm_list(ctx, ns_id):
5955 """list all alarm"""
5956
5957 check_client_version(ctx.obj, ctx.command.name)
5958 project_name = os.getenv("OSM_PROJECT", "admin")
5959 resp = ctx.obj.ns.get_alarm(project_name=project_name, ns_id=ns_id)
5960
5961 table = PrettyTable(
5962 ["alarm-id", "metric", "threshold", "operation", "action", "status"]
5963 )
5964 if resp:
5965 # return the response data in a table
5966 resp = resp.replace("ObjectId", "")
5967 for alarm in resp:
5968 table.add_row(
5969 [
5970 wrap_text(text=str(alarm["uuid"]), width=38),
5971 alarm["metric"],
5972 alarm["threshold"],
5973 alarm["operation"],
5974 wrap_text(text=alarm["action"], width=25),
5975 alarm["alarm_status"],
5976 ]
5977 )
5978 table.align = "l"
5979 print(table)
5980
5981
5982 # Update alarm
5983 @cli_osm.command(name="alarm-update", short_help="Update a alarm")
5984 @click.argument("uuid")
5985 @click.option("--threshold", default=None, help="Alarm threshold")
5986 @click.option("--is_enable", default=None, type=bool, help="enable or disable alarm")
5987 @click.pass_context
5988 def alarm_update(ctx, uuid, threshold, is_enable):
5989 """
5990 Update alarm
5991
5992 """
5993 if not threshold and is_enable is None:
5994 raise ClientException(
5995 "Please provide option to update i.e threshold or is_enable"
5996 )
5997 ctx.obj.ns.update_alarm(uuid, threshold, is_enable)
5998
5999
6000 ##############################
6001 # Role Management Operations #
6002 ##############################
6003
6004
6005 @cli_osm.command(name="role-create", short_help="creates a new role")
6006 @click.argument("name")
6007 @click.option("--permissions", default=None, help="role permissions using a dictionary")
6008 @click.pass_context
6009 def role_create(ctx, name, permissions):
6010 """
6011 Creates a new role.
6012
6013 \b
6014 NAME: Name or ID of the role.
6015 DEFINITION: Definition of grant/denial of access to resources.
6016 """
6017 logger.debug("")
6018 # try:
6019 check_client_version(ctx.obj, ctx.command.name)
6020 ctx.obj.role.create(name, permissions)
6021 # except ClientException as e:
6022 # print(str(e))
6023 # exit(1)
6024
6025
6026 @cli_osm.command(name="role-update", short_help="updates a role")
6027 @click.argument("name")
6028 @click.option("--set-name", default=None, help="change name of rle")
6029 # @click.option('--permissions',
6030 # default=None,
6031 # help='provide a yaml format dictionary with incremental changes. Values can be bool or None to delete')
6032 @click.option(
6033 "--add",
6034 default=None,
6035 help="yaml format dictionary with permission: True/False to access grant/denial",
6036 )
6037 @click.option("--remove", default=None, help="yaml format list to remove a permission")
6038 @click.pass_context
6039 def role_update(ctx, name, set_name, add, remove):
6040 """
6041 Updates a role.
6042
6043 \b
6044 NAME: Name or ID of the role.
6045 DEFINITION: Definition overwrites the old definition.
6046 ADD: Grant/denial of access to resource to add.
6047 REMOVE: Grant/denial of access to resource to remove.
6048 """
6049 logger.debug("")
6050 # try:
6051 check_client_version(ctx.obj, ctx.command.name)
6052 ctx.obj.role.update(name, set_name, None, add, remove)
6053 # except ClientException as e:
6054 # print(str(e))
6055 # exit(1)
6056
6057
6058 @cli_osm.command(name="role-delete", short_help="deletes a role")
6059 @click.argument("name")
6060 # @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
6061 @click.pass_context
6062 def role_delete(ctx, name):
6063 """
6064 Deletes a role.
6065
6066 \b
6067 NAME: Name or ID of the role.
6068 """
6069 logger.debug("")
6070 # try:
6071 check_client_version(ctx.obj, ctx.command.name)
6072 ctx.obj.role.delete(name)
6073 # except ClientException as e:
6074 # print(str(e))
6075 # exit(1)
6076
6077
6078 @cli_osm.command(name="role-list", short_help="list all roles")
6079 @click.option(
6080 "--filter",
6081 default=None,
6082 multiple=True,
6083 help="restricts the list to the projects matching the filter",
6084 )
6085 @click.pass_context
6086 def role_list(ctx, filter):
6087 """
6088 List all roles.
6089 """
6090 logger.debug("")
6091 # try:
6092 check_client_version(ctx.obj, ctx.command.name)
6093 if filter:
6094 filter = "&".join(filter)
6095 resp = ctx.obj.role.list(filter)
6096 # except ClientException as e:
6097 # print(str(e))
6098 # exit(1)
6099 table = PrettyTable(["name", "id"])
6100 for role in resp:
6101 table.add_row([role["name"], role["_id"]])
6102 table.align = "l"
6103 print(table)
6104
6105
6106 @cli_osm.command(name="role-show", short_help="show specific role")
6107 @click.argument("name")
6108 @click.pass_context
6109 def role_show(ctx, name):
6110 """
6111 Shows the details of a role.
6112
6113 \b
6114 NAME: Name or ID of the role.
6115 """
6116 logger.debug("")
6117 # try:
6118 check_client_version(ctx.obj, ctx.command.name)
6119 resp = ctx.obj.role.get(name)
6120 # except ClientException as e:
6121 # print(str(e))
6122 # exit(1)
6123
6124 table = PrettyTable(["key", "attribute"])
6125 for k, v in resp.items():
6126 table.add_row([k, json.dumps(v, indent=2)])
6127 table.align = "l"
6128 print(table)
6129
6130
6131 @cli_osm.command(name="package-create", short_help="Create empty NS package structure")
6132 @click.argument("package-type")
6133 @click.argument("package-name")
6134 @click.option(
6135 "--base-directory",
6136 default=".",
6137 help=('(NS/VNF/NST) Set the location for package creation. Default: "."'),
6138 )
6139 @click.option(
6140 "--image",
6141 default="image-name",
6142 help='(VNF) Set the name of the vdu image. Default "image-name"',
6143 )
6144 @click.option(
6145 "--vdus", default=1, help="(VNF) Set the number of vdus in a VNF. Default 1"
6146 )
6147 @click.option(
6148 "--vcpu", default=1, help="(VNF) Set the number of virtual CPUs in a vdu. Default 1"
6149 )
6150 @click.option(
6151 "--memory",
6152 default=1024,
6153 help="(VNF) Set the memory size (MB) of the vdu. Default 1024",
6154 )
6155 @click.option(
6156 "--storage", default=10, help="(VNF) Set the disk size (GB) of the vdu. Default 10"
6157 )
6158 @click.option(
6159 "--interfaces",
6160 default=0,
6161 help="(VNF) Set the number of additional interfaces apart from the management interface. Default 0",
6162 )
6163 @click.option(
6164 "--vendor", default="OSM", help='(NS/VNF) Set the descriptor vendor. Default "OSM"'
6165 )
6166 @click.option(
6167 "--override",
6168 default=False,
6169 is_flag=True,
6170 help="(NS/VNF/NST) Flag for overriding the package if exists.",
6171 )
6172 @click.option(
6173 "--detailed",
6174 is_flag=True,
6175 default=False,
6176 help="(NS/VNF/NST) Flag for generating descriptor .yaml with all possible commented options",
6177 )
6178 @click.option(
6179 "--netslice-subnets", default=1, help="(NST) Number of netslice subnets. Default 1"
6180 )
6181 @click.option(
6182 "--netslice-vlds", default=1, help="(NST) Number of netslice vlds. Default 1"
6183 )
6184 @click.option(
6185 "--old",
6186 default=False,
6187 is_flag=True,
6188 help="Flag to create a descriptor using the previous OSM format (pre SOL006, OSM<9)",
6189 )
6190 @click.pass_context
6191 def package_create(
6192 ctx,
6193 package_type,
6194 base_directory,
6195 package_name,
6196 override,
6197 image,
6198 vdus,
6199 vcpu,
6200 memory,
6201 storage,
6202 interfaces,
6203 vendor,
6204 detailed,
6205 netslice_subnets,
6206 netslice_vlds,
6207 old,
6208 ):
6209 """
6210 Creates an OSM NS, VNF, NST package
6211
6212 \b
6213 PACKAGE_TYPE: Package to be created: NS, VNF or NST.
6214 PACKAGE_NAME: Name of the package to create the folder with the content.
6215 """
6216
6217 # try:
6218 logger.debug("")
6219 check_client_version(ctx.obj, ctx.command.name)
6220 print(
6221 "Creating the {} structure: {}/{}".format(
6222 package_type.upper(), base_directory, package_name
6223 )
6224 )
6225 resp = ctx.obj.package_tool.create(
6226 package_type,
6227 base_directory,
6228 package_name,
6229 override=override,
6230 image=image,
6231 vdus=vdus,
6232 vcpu=vcpu,
6233 memory=memory,
6234 storage=storage,
6235 interfaces=interfaces,
6236 vendor=vendor,
6237 detailed=detailed,
6238 netslice_subnets=netslice_subnets,
6239 netslice_vlds=netslice_vlds,
6240 old=old,
6241 )
6242 print(resp)
6243 # except ClientException as inst:
6244 # print("ERROR: {}".format(inst))
6245 # exit(1)
6246
6247
6248 @cli_osm.command(
6249 name="package-validate", short_help="Validate descriptors given a base directory"
6250 )
6251 @click.argument("base-directory", default=".", required=False)
6252 @click.option(
6253 "--recursive/--no-recursive",
6254 default=True,
6255 help="The activated recursive option will validate the yaml files"
6256 " within the indicated directory and in its subdirectories",
6257 )
6258 @click.option(
6259 "--old",
6260 is_flag=True,
6261 default=False,
6262 help="Validates also the descriptors using the previous OSM format (pre SOL006)",
6263 )
6264 @click.pass_context
6265 def package_validate(ctx, base_directory, recursive, old):
6266 """
6267 Validate descriptors given a base directory.
6268
6269 \b
6270 BASE_DIRECTORY: Base folder for NS, VNF or NST package.
6271 """
6272 # try:
6273 logger.debug("")
6274 check_client_version(ctx.obj, ctx.command.name)
6275 results = ctx.obj.package_tool.validate(base_directory, recursive, old)
6276 table = PrettyTable()
6277 table.field_names = ["TYPE", "PATH", "VALID", "ERROR"]
6278 # Print the dictionary generated by the validation function
6279 for result in results:
6280 table.add_row(
6281 [result["type"], result["path"], result["valid"], result["error"]]
6282 )
6283 table.sortby = "VALID"
6284 table.align["PATH"] = "l"
6285 table.align["TYPE"] = "l"
6286 table.align["ERROR"] = "l"
6287 print(table)
6288 # except ClientException as inst:
6289 # print("ERROR: {}".format(inst))
6290 # exit(1)
6291
6292
6293 @cli_osm.command(
6294 name="package-translate", short_help="Translate descriptors given a base directory"
6295 )
6296 @click.argument("base-directory", default=".", required=False)
6297 @click.option(
6298 "--recursive/--no-recursive",
6299 default=True,
6300 help="The activated recursive option will translate the yaml files"
6301 " within the indicated directory and in its subdirectories",
6302 )
6303 @click.option(
6304 "--dryrun",
6305 is_flag=True,
6306 default=False,
6307 help="Do not translate yet, only make a dry-run to test translation",
6308 )
6309 @click.pass_context
6310 def package_translate(ctx, base_directory, recursive, dryrun):
6311 """
6312 Translate descriptors given a base directory.
6313
6314 \b
6315 BASE_DIRECTORY: Stub folder for NS, VNF or NST package.
6316 """
6317 logger.debug("")
6318 check_client_version(ctx.obj, ctx.command.name)
6319 results = ctx.obj.package_tool.translate(base_directory, recursive, dryrun)
6320 table = PrettyTable()
6321 table.field_names = [
6322 "CURRENT TYPE",
6323 "NEW TYPE",
6324 "PATH",
6325 "VALID",
6326 "TRANSLATED",
6327 "ERROR",
6328 ]
6329 # Print the dictionary generated by the validation function
6330 for result in results:
6331 table.add_row(
6332 [
6333 result["current type"],
6334 result["new type"],
6335 result["path"],
6336 result["valid"],
6337 result["translated"],
6338 result["error"],
6339 ]
6340 )
6341 table.sortby = "TRANSLATED"
6342 table.align["PATH"] = "l"
6343 table.align["TYPE"] = "l"
6344 table.align["ERROR"] = "l"
6345 print(table)
6346 # except ClientException as inst:
6347 # print("ERROR: {}".format(inst))
6348 # exit(1)
6349
6350
6351 @cli_osm.command(name="package-build", short_help="Build the tar.gz of the package")
6352 @click.argument("package-folder")
6353 @click.option(
6354 "--skip-validation", default=False, is_flag=True, help="skip package validation"
6355 )
6356 @click.option(
6357 "--skip-charm-build",
6358 default=False,
6359 is_flag=True,
6360 help="the charm will not be compiled, it is assumed to already exist",
6361 )
6362 @click.pass_context
6363 def package_build(ctx, package_folder, skip_validation, skip_charm_build):
6364 """
6365 Build the package NS, VNF given the package_folder.
6366
6367 \b
6368 PACKAGE_FOLDER: Folder of the NS, VNF or NST to be packaged
6369 """
6370 # try:
6371 logger.debug("")
6372 check_client_version(ctx.obj, ctx.command.name)
6373 results = ctx.obj.package_tool.build(
6374 package_folder,
6375 skip_validation=skip_validation,
6376 skip_charm_build=skip_charm_build,
6377 )
6378 print(results)
6379 # except ClientException as inst:
6380 # print("ERROR: {}".format(inst))
6381 # exit(1)
6382
6383
6384 @cli_osm.command(
6385 name="descriptor-translate",
6386 short_help="Translate input descriptor file from Rel EIGHT OSM descriptors to SOL006 and prints in standard output",
6387 )
6388 @click.argument("descriptor-file", required=True)
6389 @click.pass_context
6390 def descriptor_translate(ctx, descriptor_file):
6391 """
6392 Translate input descriptor.
6393
6394 \b
6395 DESCRIPTOR_FILE: Descriptor file for NS, VNF or Network Slice.
6396 """
6397 logger.debug("")
6398 check_client_version(ctx.obj, ctx.command.name)
6399 result = ctx.obj.package_tool.descriptor_translate(descriptor_file)
6400 print(result)
6401
6402
6403 def cli():
6404 try:
6405 cli_osm() # pylint: disable=no-value-for-parameter
6406 exit(0)
6407 except pycurl.error as exc:
6408 print(exc)
6409 print(
6410 'Maybe "--hostname" option or OSM_HOSTNAME environment variable needs to be specified'
6411 )
6412 except ClientException as exc:
6413 print("ERROR: {}".format(exc))
6414 except (FileNotFoundError, PermissionError) as exc:
6415 print("Cannot open file: {}".format(exc))
6416 except yaml.YAMLError as exc:
6417 print("Invalid YAML format: {}".format(exc))
6418 exit(1)
6419 # TODO capture other controlled exceptions here
6420 # TODO remove the ClientException captures from all places, unless they do something different
6421
6422
6423 if __name__ == "__main__":
6424 cli()