Addition of PaaS
[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.get("queuePosition") is not None and op.get("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
2627 logger.debug("")
2628 pdu = {}
2629
2630 if not descriptor_file:
2631 if not update:
2632 if not name:
2633 raise ClientException(
2634 'in absence of descriptor file, option "--name" is mandatory'
2635 )
2636 if not pdu_type:
2637 raise ClientException(
2638 'in absence of descriptor file, option "--pdu_type" is mandatory'
2639 )
2640 if not interface:
2641 raise ClientException(
2642 'in absence of descriptor file, option "--interface" is mandatory (at least once)'
2643 )
2644 if not vim_account:
2645 raise ClientException(
2646 'in absence of descriptor file, option "--vim_account" is mandatory (at least once)'
2647 )
2648 else:
2649 with open(descriptor_file, "r") as df:
2650 pdu = yaml.safe_load(df.read())
2651 if name:
2652 pdu["name"] = name
2653 if pdu_type:
2654 pdu["type"] = pdu_type
2655 if description:
2656 pdu["description"] = description
2657 if vim_account:
2658 pdu["vim_accounts"] = vim_account
2659 if interface:
2660 ifaces_list = []
2661 for iface in interface:
2662 new_iface = {k: v for k, v in [i.split("=") for i in iface.split(",")]}
2663 new_iface["mgmt"] = new_iface.get("mgmt", "false").lower() == "true"
2664 ifaces_list.append(new_iface)
2665 pdu["interfaces"] = ifaces_list
2666 return pdu
2667
2668
2669 ####################
2670 # UPDATE operations
2671 ####################
2672
2673
2674 def nsd_update(ctx, name, content):
2675 logger.debug("")
2676 # try:
2677 check_client_version(ctx.obj, ctx.command.name)
2678 ctx.obj.nsd.update(name, content)
2679 # except ClientException as e:
2680 # print(str(e))
2681 # exit(1)
2682
2683
2684 @cli_osm.command(name="nsd-update", short_help="updates a NSD/NSpkg")
2685 @click.argument("name")
2686 @click.option(
2687 "--content",
2688 default=None,
2689 help="filename with the NSD/NSpkg replacing the current one",
2690 )
2691 @click.pass_context
2692 def nsd_update1(ctx, name, content):
2693 """updates a NSD/NSpkg
2694
2695 NAME: name or ID of the NSD/NSpkg
2696 """
2697 logger.debug("")
2698 nsd_update(ctx, name, content)
2699
2700
2701 @cli_osm.command(name="nspkg-update", short_help="updates a NSD/NSpkg")
2702 @click.argument("name")
2703 @click.option(
2704 "--content",
2705 default=None,
2706 help="filename with the NSD/NSpkg replacing the current one",
2707 )
2708 @click.pass_context
2709 def nsd_update2(ctx, name, content):
2710 """updates a NSD/NSpkg
2711
2712 NAME: name or ID of the NSD/NSpkg
2713 """
2714 logger.debug("")
2715 nsd_update(ctx, name, content)
2716
2717
2718 def vnfd_update(ctx, name, content):
2719 logger.debug("")
2720 # try:
2721 check_client_version(ctx.obj, ctx.command.name)
2722 ctx.obj.vnfd.update(name, content)
2723 # except ClientException as e:
2724 # print(str(e))
2725 # exit(1)
2726
2727
2728 @cli_osm.command(name="vnfd-update", short_help="updates a new VNFD/VNFpkg")
2729 @click.argument("name")
2730 @click.option(
2731 "--content",
2732 default=None,
2733 help="filename with the VNFD/VNFpkg replacing the current one",
2734 )
2735 @click.pass_context
2736 def vnfd_update1(ctx, name, content):
2737 """updates a VNFD/VNFpkg
2738
2739 NAME: name or ID of the VNFD/VNFpkg
2740 """
2741 logger.debug("")
2742 vnfd_update(ctx, name, content)
2743
2744
2745 @cli_osm.command(name="vnfpkg-update", short_help="updates a VNFD/VNFpkg")
2746 @click.argument("name")
2747 @click.option(
2748 "--content",
2749 default=None,
2750 help="filename with the VNFD/VNFpkg replacing the current one",
2751 )
2752 @click.pass_context
2753 def vnfd_update2(ctx, name, content):
2754 """updates a VNFD/VNFpkg
2755
2756 NAME: VNFD yaml file or VNFpkg tar.gz file
2757 """
2758 logger.debug("")
2759 vnfd_update(ctx, name, content)
2760
2761
2762 @cli_osm.command(name="nfpkg-update", short_help="updates a NFpkg")
2763 @click.argument("name")
2764 @click.option(
2765 "--content", default=None, help="filename with the NFpkg replacing the current one"
2766 )
2767 @click.pass_context
2768 def nfpkg_update(ctx, name, content):
2769 """updates a NFpkg
2770
2771 NAME: NF Descriptor yaml file or NFpkg tar.gz file
2772 """
2773 logger.debug("")
2774 vnfd_update(ctx, name, content)
2775
2776
2777 def nst_update(ctx, name, content):
2778 logger.debug("")
2779 # try:
2780 check_client_version(ctx.obj, ctx.command.name)
2781 ctx.obj.nst.update(name, content)
2782 # except ClientException as e:
2783 # print(str(e))
2784 # exit(1)
2785
2786
2787 @cli_osm.command(name="nst-update", short_help="updates a Network Slice Template (NST)")
2788 @click.argument("name")
2789 @click.option(
2790 "--content",
2791 default=None,
2792 help="filename with the NST/NSTpkg replacing the current one",
2793 )
2794 @click.pass_context
2795 def nst_update1(ctx, name, content):
2796 """updates a Network Slice Template (NST)
2797
2798 NAME: name or ID of the NSD/NSpkg
2799 """
2800 logger.debug("")
2801 nst_update(ctx, name, content)
2802
2803
2804 @cli_osm.command(
2805 name="netslice-template-update", short_help="updates a Network Slice Template (NST)"
2806 )
2807 @click.argument("name")
2808 @click.option(
2809 "--content",
2810 default=None,
2811 help="filename with the NST/NSTpkg replacing the current one",
2812 )
2813 @click.pass_context
2814 def nst_update2(ctx, name, content):
2815 """updates a Network Slice Template (NST)
2816
2817 NAME: name or ID of the NSD/NSpkg
2818 """
2819 logger.debug("")
2820 nst_update(ctx, name, content)
2821
2822
2823 ####################
2824 # DELETE operations
2825 ####################
2826
2827
2828 def nsd_delete(ctx, name, force):
2829 logger.debug("")
2830 # try:
2831 if not force:
2832 ctx.obj.nsd.delete(name)
2833 else:
2834 check_client_version(ctx.obj, "--force")
2835 ctx.obj.nsd.delete(name, force)
2836 # except ClientException as e:
2837 # print(str(e))
2838 # exit(1)
2839
2840
2841 @cli_osm.command(name="nsd-delete", short_help="deletes a NSD/NSpkg")
2842 @click.argument("name")
2843 @click.option(
2844 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
2845 )
2846 @click.pass_context
2847 def nsd_delete1(ctx, name, force):
2848 """deletes a NSD/NSpkg
2849
2850 NAME: name or ID of the NSD/NSpkg to be deleted
2851 """
2852 logger.debug("")
2853 nsd_delete(ctx, name, force)
2854
2855
2856 @cli_osm.command(name="nspkg-delete", short_help="deletes a NSD/NSpkg")
2857 @click.argument("name")
2858 @click.option(
2859 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
2860 )
2861 @click.pass_context
2862 def nsd_delete2(ctx, name, force):
2863 """deletes a NSD/NSpkg
2864
2865 NAME: name or ID of the NSD/NSpkg to be deleted
2866 """
2867 logger.debug("")
2868 nsd_delete(ctx, name, force)
2869
2870
2871 def vnfd_delete(ctx, name, force):
2872 logger.debug("")
2873 # try:
2874 if not force:
2875 ctx.obj.vnfd.delete(name)
2876 else:
2877 check_client_version(ctx.obj, "--force")
2878 ctx.obj.vnfd.delete(name, force)
2879 # except ClientException as e:
2880 # print(str(e))
2881 # exit(1)
2882
2883
2884 @cli_osm.command(name="vnfd-delete", short_help="deletes a VNFD/VNFpkg")
2885 @click.argument("name")
2886 @click.option(
2887 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
2888 )
2889 @click.pass_context
2890 def vnfd_delete1(ctx, name, force):
2891 """deletes a VNFD/VNFpkg
2892
2893 NAME: name or ID of the VNFD/VNFpkg to be deleted
2894 """
2895 logger.debug("")
2896 vnfd_delete(ctx, name, force)
2897
2898
2899 @cli_osm.command(name="vnfpkg-delete", short_help="deletes a VNFD/VNFpkg")
2900 @click.argument("name")
2901 @click.option(
2902 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
2903 )
2904 @click.pass_context
2905 def vnfd_delete2(ctx, name, force):
2906 """deletes a VNFD/VNFpkg
2907
2908 NAME: name or ID of the VNFD/VNFpkg to be deleted
2909 """
2910 logger.debug("")
2911 vnfd_delete(ctx, name, force)
2912
2913
2914 @cli_osm.command(name="nfpkg-delete", short_help="deletes a NFpkg")
2915 @click.argument("name")
2916 @click.option(
2917 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
2918 )
2919 @click.pass_context
2920 def nfpkg_delete(ctx, name, force):
2921 """deletes a NFpkg
2922
2923 NAME: name or ID of the NFpkg to be deleted
2924 """
2925 logger.debug("")
2926 vnfd_delete(ctx, name, force)
2927
2928
2929 @cli_osm.command(name="ns-delete", short_help="deletes a NS instance")
2930 @click.argument("name")
2931 @click.option(
2932 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
2933 )
2934 @click.option(
2935 "--config",
2936 default=None,
2937 help="specific yaml configuration for the termination, e.g. '{autoremove: False, timeout_ns_terminate: "
2938 "600, skip_terminate_primitives: True}'",
2939 )
2940 @click.option(
2941 "--wait",
2942 required=False,
2943 default=False,
2944 is_flag=True,
2945 help="do not return the control immediately, but keep it "
2946 "until the operation is completed, or timeout",
2947 )
2948 @click.pass_context
2949 def ns_delete(ctx, name, force, config, wait):
2950 """deletes a NS instance
2951
2952 NAME: name or ID of the NS instance to be deleted
2953 """
2954 logger.debug("")
2955 # try:
2956 if not force:
2957 ctx.obj.ns.delete(name, config=config, wait=wait)
2958 else:
2959 check_client_version(ctx.obj, "--force")
2960 ctx.obj.ns.delete(name, force, config=config, wait=wait)
2961 # except ClientException as e:
2962 # print(str(e))
2963 # exit(1)
2964
2965
2966 def nst_delete(ctx, name, force):
2967 logger.debug("")
2968 # try:
2969 check_client_version(ctx.obj, ctx.command.name)
2970 ctx.obj.nst.delete(name, force)
2971 # except ClientException as e:
2972 # print(str(e))
2973 # exit(1)
2974
2975
2976 @cli_osm.command(name="nst-delete", short_help="deletes a Network Slice Template (NST)")
2977 @click.argument("name")
2978 @click.option(
2979 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
2980 )
2981 @click.pass_context
2982 def nst_delete1(ctx, name, force):
2983 """deletes a Network Slice Template (NST)
2984
2985 NAME: name or ID of the NST/NSTpkg to be deleted
2986 """
2987 logger.debug("")
2988 nst_delete(ctx, name, force)
2989
2990
2991 @cli_osm.command(
2992 name="netslice-template-delete", short_help="deletes a Network Slice Template (NST)"
2993 )
2994 @click.argument("name")
2995 @click.option(
2996 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
2997 )
2998 @click.pass_context
2999 def nst_delete2(ctx, name, force):
3000 """deletes a Network Slice Template (NST)
3001
3002 NAME: name or ID of the NST/NSTpkg to be deleted
3003 """
3004 logger.debug("")
3005 nst_delete(ctx, name, force)
3006
3007
3008 def nsi_delete(ctx, name, force, wait):
3009 logger.debug("")
3010 # try:
3011 check_client_version(ctx.obj, ctx.command.name)
3012 ctx.obj.nsi.delete(name, force, wait=wait)
3013 # except ClientException as e:
3014 # print(str(e))
3015 # exit(1)
3016
3017
3018 @cli_osm.command(name="nsi-delete", short_help="deletes a Network Slice Instance (NSI)")
3019 @click.argument("name")
3020 @click.option(
3021 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
3022 )
3023 @click.option(
3024 "--wait",
3025 required=False,
3026 default=False,
3027 is_flag=True,
3028 help="do not return the control immediately, but keep it "
3029 "until the operation is completed, or timeout",
3030 )
3031 @click.pass_context
3032 def nsi_delete1(ctx, name, force, wait):
3033 """deletes a Network Slice Instance (NSI)
3034
3035 NAME: name or ID of the Network Slice instance to be deleted
3036 """
3037 logger.debug("")
3038 nsi_delete(ctx, name, force, wait=wait)
3039
3040
3041 @cli_osm.command(
3042 name="netslice-instance-delete", short_help="deletes a Network Slice Instance (NSI)"
3043 )
3044 @click.argument("name")
3045 @click.option(
3046 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
3047 )
3048 @click.pass_context
3049 def nsi_delete2(ctx, name, force, wait):
3050 """deletes a Network Slice Instance (NSI)
3051
3052 NAME: name or ID of the Network Slice instance to be deleted
3053 """
3054 logger.debug("")
3055 nsi_delete(ctx, name, force, wait=wait)
3056
3057
3058 @cli_osm.command(
3059 name="pdu-delete", short_help="deletes a Physical Deployment Unit (PDU)"
3060 )
3061 @click.argument("name")
3062 @click.option(
3063 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
3064 )
3065 @click.pass_context
3066 def pdu_delete(ctx, name, force):
3067 """deletes a Physical Deployment Unit (PDU)
3068
3069 NAME: name or ID of the PDU to be deleted
3070 """
3071 logger.debug("")
3072 # try:
3073 check_client_version(ctx.obj, ctx.command.name)
3074 ctx.obj.pdu.delete(name, force)
3075 # except ClientException as e:
3076 # print(str(e))
3077 # exit(1)
3078
3079
3080 #################
3081 # VIM operations
3082 #################
3083
3084
3085 def _check_ca_cert(vim_config: dict) -> None:
3086 """
3087 Checks if the VIM has a CA certificate.
3088 In that case, reads the content and add it to the config
3089 : param vim_config: configuration provided with the VIM creation
3090 : return: None
3091 """
3092
3093 if vim_config.get("ca_cert"):
3094 with open(vim_config["ca_cert"], "r") as cert_f:
3095 vim_config["ca_cert_content"] = str(cert_f.read())
3096 del vim_config["ca_cert"]
3097
3098
3099 @cli_osm.command(name="vim-create", short_help="creates a new VIM account")
3100 @click.option("--name", required=True, help="Name to create datacenter")
3101 @click.option("--user", default=None, help="VIM username")
3102 @click.option("--password", default=None, help="VIM password")
3103 @click.option("--auth_url", default=None, help="VIM url")
3104 @click.option(
3105 "--tenant", "--project", "tenant", default=None, help="VIM tenant/project name"
3106 )
3107 @click.option("--config", default=None, help="VIM specific config parameters")
3108 @click.option(
3109 "--config_file",
3110 default=None,
3111 help="VIM specific config parameters in YAML or JSON file",
3112 )
3113 @click.option("--account_type", default="openstack", help="VIM type")
3114 @click.option("--description", default=None, help="human readable description")
3115 @click.option(
3116 "--sdn_controller",
3117 default=None,
3118 help="Name or id of the SDN controller associated to this VIM account",
3119 )
3120 @click.option(
3121 "--sdn_port_mapping",
3122 default=None,
3123 help="File describing the port mapping between compute nodes' ports and switch ports",
3124 )
3125 @click.option(
3126 "--wait",
3127 required=False,
3128 default=False,
3129 is_flag=True,
3130 help="do not return the control immediately, but keep it "
3131 "until the operation is completed, or timeout",
3132 )
3133 @click.option("--vca", default=None, help="VCA to be used in this VIM account")
3134 @click.option(
3135 "--creds", default=None, help="credentials file (only applycable for GCP VIM type)"
3136 )
3137 @click.option(
3138 "--prometheus_config_file",
3139 default=None,
3140 help="Prometheus configuration to get VIM data",
3141 )
3142 @click.pass_context
3143 def vim_create(
3144 ctx,
3145 name,
3146 user,
3147 password,
3148 auth_url,
3149 tenant,
3150 config,
3151 config_file,
3152 account_type,
3153 description,
3154 sdn_controller,
3155 sdn_port_mapping,
3156 wait,
3157 vca,
3158 creds,
3159 prometheus_config_file,
3160 ):
3161 """creates a new VIM account"""
3162 logger.debug("")
3163 # try:
3164 if sdn_controller:
3165 check_client_version(ctx.obj, "--sdn_controller")
3166 if sdn_port_mapping:
3167 check_client_version(ctx.obj, "--sdn_port_mapping")
3168 vim = {}
3169 if prometheus_config_file:
3170 with open(prometheus_config_file) as prometheus_file:
3171 prometheus_config_dict = json.load(prometheus_file)
3172 vim["prometheus-config"] = prometheus_config_dict
3173
3174 vim["vim-username"] = user
3175 vim["vim-password"] = password
3176 vim["vim-url"] = auth_url
3177 vim["vim-tenant-name"] = tenant
3178 vim["vim-type"] = account_type
3179 vim["description"] = description
3180 if vca:
3181 vim["vca"] = vca
3182 vim_config = create_config(config_file, config)
3183 _check_ca_cert(vim_config)
3184 if creds:
3185 with open(creds, "r") as cf:
3186 vim_config["credentials"] = yaml.safe_load(cf.read())
3187 ctx.obj.vim.create(
3188 name, vim, vim_config, sdn_controller, sdn_port_mapping, wait=wait
3189 )
3190 # except ClientException as e:
3191 # print(str(e))
3192 # exit(1)
3193
3194
3195 @cli_osm.command(name="vim-update", short_help="updates a VIM account")
3196 @click.argument("name")
3197 @click.option("--newname", help="New name for the VIM account")
3198 @click.option("--user", help="VIM username")
3199 @click.option("--password", help="VIM password")
3200 @click.option("--auth_url", help="VIM url")
3201 @click.option("--tenant", help="VIM tenant name")
3202 @click.option("--config", help="VIM specific config parameters")
3203 @click.option(
3204 "--config_file",
3205 default=None,
3206 help="VIM specific config parameters in YAML or JSON file",
3207 )
3208 @click.option("--account_type", help="VIM type")
3209 @click.option("--description", help="human readable description")
3210 @click.option(
3211 "--sdn_controller",
3212 default=None,
3213 help="Name or id of the SDN controller to be associated with this VIM"
3214 "account. Use empty string to disassociate",
3215 )
3216 @click.option(
3217 "--sdn_port_mapping",
3218 default=None,
3219 help="File describing the port mapping between compute nodes' ports and switch ports",
3220 )
3221 @click.option(
3222 "--wait",
3223 required=False,
3224 default=False,
3225 is_flag=True,
3226 help="do not return the control immediately, but keep it "
3227 "until the operation is completed, or timeout",
3228 )
3229 @click.option(
3230 "--creds", default=None, help="credentials file (only applycable for GCP VIM type)"
3231 )
3232 @click.option(
3233 "--prometheus_config_file",
3234 default=None,
3235 help="Prometheus configuration to get VIM data",
3236 )
3237 @click.pass_context
3238 def vim_update(
3239 ctx,
3240 name,
3241 newname,
3242 user,
3243 password,
3244 auth_url,
3245 tenant,
3246 config,
3247 config_file,
3248 account_type,
3249 description,
3250 sdn_controller,
3251 sdn_port_mapping,
3252 wait,
3253 creds,
3254 prometheus_config_file,
3255 ):
3256 """updates a VIM account
3257
3258 NAME: name or ID of the VIM account
3259 """
3260 logger.debug("")
3261 # try:
3262 check_client_version(ctx.obj, ctx.command.name)
3263 vim = {}
3264 if newname:
3265 vim["name"] = newname
3266 if user:
3267 vim["vim_user"] = user
3268 if password:
3269 vim["vim_password"] = password
3270 if auth_url:
3271 vim["vim_url"] = auth_url
3272 if tenant:
3273 vim["vim-tenant-name"] = tenant
3274 if account_type:
3275 vim["vim_type"] = account_type
3276 if description:
3277 vim["description"] = description
3278 vim_config = None
3279 if config or config_file:
3280 vim_config = create_config(config_file, config)
3281 _check_ca_cert(vim_config)
3282 if creds:
3283 with open(creds, "r") as cf:
3284 vim_config["credentials"] = yaml.safe_load(cf.read())
3285 if prometheus_config_file:
3286 with open(prometheus_config_file) as prometheus_file:
3287 prometheus_config_dict = json.load(prometheus_file)
3288 vim["prometheus-config"] = prometheus_config_dict
3289 logger.info(f"VIM: {vim}, VIM config: {vim_config}")
3290 ctx.obj.vim.update(
3291 name, vim, vim_config, sdn_controller, sdn_port_mapping, wait=wait
3292 )
3293 # except ClientException as e:
3294 # print(str(e))
3295 # exit(1)
3296
3297
3298 @cli_osm.command(name="vim-delete", short_help="deletes a VIM account")
3299 @click.argument("name")
3300 @click.option(
3301 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
3302 )
3303 @click.option(
3304 "--wait",
3305 required=False,
3306 default=False,
3307 is_flag=True,
3308 help="do not return the control immediately, but keep it "
3309 "until the operation is completed, or timeout",
3310 )
3311 @click.pass_context
3312 def vim_delete(ctx, name, force, wait):
3313 """deletes a VIM account
3314
3315 NAME: name or ID of the VIM account to be deleted
3316 """
3317 logger.debug("")
3318 # try:
3319 if not force:
3320 ctx.obj.vim.delete(name, wait=wait)
3321 else:
3322 check_client_version(ctx.obj, "--force")
3323 ctx.obj.vim.delete(name, force, wait=wait)
3324 # except ClientException as e:
3325 # print(str(e))
3326 # exit(1)
3327
3328
3329 @cli_osm.command(name="vim-list", short_help="list all VIM accounts")
3330 # @click.option('--ro_update/--no_ro_update',
3331 # default=False,
3332 # help='update list from RO')
3333 @click.option(
3334 "--filter",
3335 default=None,
3336 multiple=True,
3337 help="restricts the list to the VIM accounts matching the filter",
3338 )
3339 @click.option(
3340 "--long",
3341 is_flag=True,
3342 help="get more details of the NS (project, vim, deployment status, configuration status.",
3343 )
3344 @click.pass_context
3345 def vim_list(ctx, filter, long):
3346 """list all VIM accounts"""
3347 logger.debug("")
3348 if filter:
3349 filter = "&".join(filter)
3350 check_client_version(ctx.obj, "--filter")
3351 # if ro_update:
3352 # check_client_version(ctx.obj, '--ro_update', 'v1')
3353 fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
3354 if fullclassname == "osmclient.sol005.client.Client":
3355 resp = ctx.obj.vim.list(filter)
3356 # else:
3357 # resp = ctx.obj.vim.list(ro_update)
3358 if long:
3359 table = PrettyTable(
3360 ["vim name", "uuid", "project", "operational state", "error details"]
3361 )
3362 project_list = ctx.obj.project.list()
3363 else:
3364 table = PrettyTable(["vim name", "uuid", "operational state"])
3365 for vim in resp:
3366 if long:
3367 if "vim_password" in vim:
3368 vim["vim_password"] = "********"
3369 if "config" in vim and "credentials" in vim["config"]:
3370 vim["config"]["credentials"] = "********"
3371 logger.debug("VIM details: {}".format(yaml.safe_dump(vim)))
3372 vim_state = vim["_admin"].get("operationalState", "-")
3373 error_details = "N/A"
3374 if vim_state == "ERROR":
3375 error_details = vim["_admin"].get("detailed-status", "Not found")
3376 project_id, project_name = get_project(project_list, vim)
3377 # project_info = '{} ({})'.format(project_name, project_id)
3378 project_info = project_name
3379 table.add_row(
3380 [
3381 vim["name"],
3382 vim["uuid"],
3383 project_info,
3384 vim_state,
3385 wrap_text(text=error_details, width=80),
3386 ]
3387 )
3388 else:
3389 table.add_row(
3390 [vim["name"], vim["uuid"], vim["_admin"].get("operationalState", "-")]
3391 )
3392 table.align = "l"
3393 print(table)
3394
3395
3396 @cli_osm.command(name="vim-show", short_help="shows the details of a VIM account")
3397 @click.argument("name")
3398 @click.option(
3399 "--filter",
3400 multiple=True,
3401 help="restricts the information to the fields in the filter",
3402 )
3403 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
3404 @click.pass_context
3405 def vim_show(ctx, name, filter, literal):
3406 """shows the details of a VIM account
3407
3408 NAME: name or ID of the VIM account
3409 """
3410 logger.debug("")
3411 # try:
3412 resp = ctx.obj.vim.get(name)
3413 if "vim_password" in resp:
3414 resp["vim_password"] = "********"
3415 if "config" in resp and "credentials" in resp["config"]:
3416 resp["config"]["credentials"] = "********"
3417 # except ClientException as e:
3418 # print(str(e))
3419 # exit(1)
3420
3421 if literal:
3422 print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
3423 return
3424 table = PrettyTable(["key", "attribute"])
3425 for k, v in list(resp.items()):
3426 if not filter or k in filter:
3427 table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)])
3428 table.align = "l"
3429 print(table)
3430
3431
3432 ####################
3433 # WIM operations
3434 ####################
3435
3436
3437 @cli_osm.command(name="wim-create", short_help="creates a new WIM account")
3438 @click.option("--name", prompt=True, help="Name for the WIM account")
3439 @click.option("--user", help="WIM username")
3440 @click.option("--password", help="WIM password")
3441 @click.option("--url", prompt=True, help="WIM url")
3442 # @click.option('--tenant',
3443 # help='wIM tenant name')
3444 @click.option("--config", default=None, help="WIM specific config parameters")
3445 @click.option("--wim_type", help="WIM type")
3446 @click.option("--description", default=None, help="human readable description")
3447 @click.option(
3448 "--wim_port_mapping",
3449 default=None,
3450 help="File describing the port mapping between DC edge (datacenters, switches, ports) and WAN edge "
3451 "(WAN service endpoint id and info)",
3452 )
3453 @click.option(
3454 "--wait",
3455 required=False,
3456 default=False,
3457 is_flag=True,
3458 help="do not return the control immediately, but keep it "
3459 "until the operation is completed, or timeout",
3460 )
3461 @click.pass_context
3462 def wim_create(
3463 ctx,
3464 name,
3465 user,
3466 password,
3467 url,
3468 # tenant,
3469 config,
3470 wim_type,
3471 description,
3472 wim_port_mapping,
3473 wait,
3474 ):
3475 """creates a new WIM account"""
3476 logger.debug("")
3477 # try:
3478 check_client_version(ctx.obj, ctx.command.name)
3479 # if sdn_controller:
3480 # check_client_version(ctx.obj, '--sdn_controller')
3481 # if sdn_port_mapping:
3482 # check_client_version(ctx.obj, '--sdn_port_mapping')
3483 wim = {}
3484 if user:
3485 wim["user"] = user
3486 if password:
3487 wim["password"] = password
3488 if url:
3489 wim["wim_url"] = url
3490 # if tenant: wim['tenant'] = tenant
3491 wim["wim_type"] = wim_type
3492 if description:
3493 wim["description"] = description
3494 if config:
3495 wim["config"] = config
3496 ctx.obj.wim.create(name, wim, wim_port_mapping, wait=wait)
3497 # except ClientException as e:
3498 # print(str(e))
3499 # exit(1)
3500
3501
3502 @cli_osm.command(name="wim-update", short_help="updates a WIM account")
3503 @click.argument("name")
3504 @click.option("--newname", help="New name for the WIM account")
3505 @click.option("--user", help="WIM username")
3506 @click.option("--password", help="WIM password")
3507 @click.option("--url", help="WIM url")
3508 @click.option("--config", help="WIM specific config parameters")
3509 @click.option("--wim_type", help="WIM type")
3510 @click.option("--description", help="human readable description")
3511 @click.option(
3512 "--wim_port_mapping",
3513 default=None,
3514 help="File describing the port mapping between DC edge (datacenters, switches, ports) and WAN edge "
3515 "(WAN service endpoint id and info)",
3516 )
3517 @click.option(
3518 "--wait",
3519 required=False,
3520 default=False,
3521 is_flag=True,
3522 help="do not return the control immediately, but keep it until the operation is completed, or timeout",
3523 )
3524 @click.pass_context
3525 def wim_update(
3526 ctx,
3527 name,
3528 newname,
3529 user,
3530 password,
3531 url,
3532 config,
3533 wim_type,
3534 description,
3535 wim_port_mapping,
3536 wait,
3537 ):
3538 """updates a WIM account
3539
3540 NAME: name or ID of the WIM account
3541 """
3542 logger.debug("")
3543 # try:
3544 check_client_version(ctx.obj, ctx.command.name)
3545 wim = {}
3546 if newname:
3547 wim["name"] = newname
3548 if user:
3549 wim["user"] = user
3550 if password:
3551 wim["password"] = password
3552 if url:
3553 wim["url"] = url
3554 # if tenant: wim['tenant'] = tenant
3555 if wim_type:
3556 wim["wim_type"] = wim_type
3557 if description:
3558 wim["description"] = description
3559 if config:
3560 wim["config"] = config
3561 ctx.obj.wim.update(name, wim, wim_port_mapping, wait=wait)
3562 # except ClientException as e:
3563 # print(str(e))
3564 # exit(1)
3565
3566
3567 @cli_osm.command(name="wim-delete", short_help="deletes a WIM account")
3568 @click.argument("name")
3569 @click.option(
3570 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
3571 )
3572 @click.option(
3573 "--wait",
3574 required=False,
3575 default=False,
3576 is_flag=True,
3577 help="do not return the control immediately, but keep it until the operation is completed, or timeout",
3578 )
3579 @click.pass_context
3580 def wim_delete(ctx, name, force, wait):
3581 """deletes a WIM account
3582
3583 NAME: name or ID of the WIM account to be deleted
3584 """
3585 logger.debug("")
3586 # try:
3587 check_client_version(ctx.obj, ctx.command.name)
3588 ctx.obj.wim.delete(name, force, wait=wait)
3589 # except ClientException as e:
3590 # print(str(e))
3591 # exit(1)
3592
3593
3594 @cli_osm.command(name="wim-list", short_help="list all WIM accounts")
3595 @click.option(
3596 "--filter",
3597 default=None,
3598 multiple=True,
3599 help="restricts the list to the WIM accounts matching the filter",
3600 )
3601 @click.pass_context
3602 def wim_list(ctx, filter):
3603 """list all WIM accounts"""
3604 logger.debug("")
3605 # try:
3606 check_client_version(ctx.obj, ctx.command.name)
3607 if filter:
3608 filter = "&".join(filter)
3609 resp = ctx.obj.wim.list(filter)
3610 table = PrettyTable(["wim name", "uuid"])
3611 for wim in resp:
3612 table.add_row([wim["name"], wim["uuid"]])
3613 table.align = "l"
3614 print(table)
3615 # except ClientException as e:
3616 # print(str(e))
3617 # exit(1)
3618
3619
3620 @cli_osm.command(name="wim-show", short_help="shows the details of a WIM account")
3621 @click.argument("name")
3622 @click.pass_context
3623 def wim_show(ctx, name):
3624 """shows the details of a WIM account
3625
3626 NAME: name or ID of the WIM account
3627 """
3628 logger.debug("")
3629 # try:
3630 check_client_version(ctx.obj, ctx.command.name)
3631 resp = ctx.obj.wim.get(name)
3632 if "password" in resp:
3633 resp["password"] = "********"
3634 # except ClientException as e:
3635 # print(str(e))
3636 # exit(1)
3637
3638 table = PrettyTable(["key", "attribute"])
3639 for k, v in list(resp.items()):
3640 table.add_row([k, json.dumps(v, indent=2)])
3641 table.align = "l"
3642 print(table)
3643
3644
3645 ####################
3646 # SDN controller operations
3647 ####################
3648
3649
3650 @cli_osm.command(name="sdnc-create", short_help="creates a new SDN controller")
3651 @click.option("--name", prompt=True, help="Name to create sdn controller")
3652 @click.option("--type", prompt=True, help="SDN controller type")
3653 @click.option(
3654 "--sdn_controller_version", # hidden=True,
3655 help="Deprecated. Use --config {version: sdn_controller_version}",
3656 )
3657 @click.option("--url", help="URL in format http[s]://HOST:IP/")
3658 @click.option("--ip_address", help="Deprecated. Use --url") # hidden=True,
3659 @click.option("--port", help="Deprecated. Use --url") # hidden=True,
3660 @click.option(
3661 "--switch_dpid", help="Deprecated. Use --config {switch_id: DPID}" # hidden=True,
3662 )
3663 @click.option(
3664 "--config",
3665 help="Extra information for SDN in yaml format, as {switch_id: identity used for the plugin (e.g. DPID: "
3666 "Openflow Datapath ID), version: version}",
3667 )
3668 @click.option("--user", help="SDN controller username")
3669 @click.option(
3670 "--password",
3671 hide_input=True,
3672 confirmation_prompt=True,
3673 help="SDN controller password",
3674 )
3675 @click.option("--description", default=None, help="human readable description")
3676 @click.option(
3677 "--wait",
3678 required=False,
3679 default=False,
3680 is_flag=True,
3681 help="do not return the control immediately, but keep it until the operation is completed, or timeout",
3682 )
3683 @click.pass_context
3684 def sdnc_create(ctx, **kwargs):
3685 """creates a new SDN controller"""
3686 logger.debug("")
3687 sdncontroller = {
3688 x: kwargs[x]
3689 for x in kwargs
3690 if kwargs[x] and x not in ("wait", "ip_address", "port", "switch_dpid")
3691 }
3692 if kwargs.get("port"):
3693 print("option '--port' is deprecated, use '--url' instead")
3694 sdncontroller["port"] = int(kwargs["port"])
3695 if kwargs.get("ip_address"):
3696 print("option '--ip_address' is deprecated, use '--url' instead")
3697 sdncontroller["ip"] = kwargs["ip_address"]
3698 if kwargs.get("switch_dpid"):
3699 print(
3700 "option '--switch_dpid' is deprecated, use '--config={switch_id: id|DPID}' instead"
3701 )
3702 sdncontroller["dpid"] = kwargs["switch_dpid"]
3703 if kwargs.get("sdn_controller_version"):
3704 print(
3705 "option '--sdn_controller_version' is deprecated, use '--config={version: SDN_CONTROLLER_VERSION}'"
3706 " instead"
3707 )
3708 # try:
3709 check_client_version(ctx.obj, ctx.command.name)
3710 ctx.obj.sdnc.create(kwargs["name"], sdncontroller, wait=kwargs["wait"])
3711 # except ClientException as e:
3712 # print(str(e))
3713 # exit(1)
3714
3715
3716 @cli_osm.command(name="sdnc-update", short_help="updates an SDN controller")
3717 @click.argument("name")
3718 @click.option("--newname", help="New name for the SDN controller")
3719 @click.option("--description", default=None, help="human readable description")
3720 @click.option("--type", help="SDN controller type")
3721 @click.option("--url", help="URL in format http[s]://HOST:IP/")
3722 @click.option(
3723 "--config",
3724 help="Extra information for SDN in yaml format, as "
3725 "{switch_id: identity used for the plugin (e.g. DPID: "
3726 "Openflow Datapath ID), version: version}",
3727 )
3728 @click.option("--user", help="SDN controller username")
3729 @click.option("--password", help="SDN controller password")
3730 @click.option("--ip_address", help="Deprecated. Use --url") # hidden=True
3731 @click.option("--port", help="Deprecated. Use --url") # hidden=True
3732 @click.option(
3733 "--switch_dpid", help="Deprecated. Use --config {switch_dpid: DPID}"
3734 ) # hidden=True
3735 @click.option(
3736 "--sdn_controller_version", help="Deprecated. Use --config {version: VERSION}"
3737 ) # hidden=True
3738 @click.option(
3739 "--wait",
3740 required=False,
3741 default=False,
3742 is_flag=True,
3743 help="do not return the control immediately, but keep it until the operation is completed, or timeout",
3744 )
3745 @click.pass_context
3746 def sdnc_update(ctx, **kwargs):
3747 """updates an SDN controller
3748
3749 NAME: name or ID of the SDN controller
3750 """
3751 logger.debug("")
3752 sdncontroller = {
3753 x: kwargs[x]
3754 for x in kwargs
3755 if kwargs[x]
3756 and x not in ("wait", "ip_address", "port", "switch_dpid", "new_name")
3757 }
3758 if kwargs.get("newname"):
3759 sdncontroller["name"] = kwargs["newname"]
3760 if kwargs.get("port"):
3761 print("option '--port' is deprecated, use '--url' instead")
3762 sdncontroller["port"] = int(kwargs["port"])
3763 if kwargs.get("ip_address"):
3764 print("option '--ip_address' is deprecated, use '--url' instead")
3765 sdncontroller["ip"] = kwargs["ip_address"]
3766 if kwargs.get("switch_dpid"):
3767 print(
3768 "option '--switch_dpid' is deprecated, use '--config={switch_id: id|DPID}' instead"
3769 )
3770 sdncontroller["dpid"] = kwargs["switch_dpid"]
3771 if kwargs.get("sdn_controller_version"):
3772 print(
3773 "option '--sdn_controller_version' is deprecated, use '---config={version: SDN_CONTROLLER_VERSION}'"
3774 " instead"
3775 )
3776
3777 # try:
3778 check_client_version(ctx.obj, ctx.command.name)
3779 ctx.obj.sdnc.update(kwargs["name"], sdncontroller, wait=kwargs["wait"])
3780 # except ClientException as e:
3781 # print(str(e))
3782 # exit(1)
3783
3784
3785 @cli_osm.command(name="sdnc-delete", short_help="deletes an SDN controller")
3786 @click.argument("name")
3787 @click.option(
3788 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
3789 )
3790 @click.option(
3791 "--wait",
3792 required=False,
3793 default=False,
3794 is_flag=True,
3795 help="do not return the control immediately, but keep it until the operation is completed, or timeout",
3796 )
3797 @click.pass_context
3798 def sdnc_delete(ctx, name, force, wait):
3799 """deletes an SDN controller
3800
3801 NAME: name or ID of the SDN controller to be deleted
3802 """
3803 logger.debug("")
3804 # try:
3805 check_client_version(ctx.obj, ctx.command.name)
3806 ctx.obj.sdnc.delete(name, force, wait=wait)
3807 # except ClientException as e:
3808 # print(str(e))
3809 # exit(1)
3810
3811
3812 @cli_osm.command(name="sdnc-list", short_help="list all SDN controllers")
3813 @click.option(
3814 "--filter",
3815 default=None,
3816 multiple=True,
3817 help="restricts the list to the SDN controllers matching the filter with format: 'k[.k..]=v[&k[.k]=v2]'",
3818 )
3819 @click.pass_context
3820 def sdnc_list(ctx, filter):
3821 """list all SDN controllers"""
3822 logger.debug("")
3823 # try:
3824 check_client_version(ctx.obj, ctx.command.name)
3825 if filter:
3826 filter = "&".join(filter)
3827 resp = ctx.obj.sdnc.list(filter)
3828 # except ClientException as e:
3829 # print(str(e))
3830 # exit(1)
3831 table = PrettyTable(["sdnc name", "id"])
3832 for sdnc in resp:
3833 table.add_row([sdnc["name"], sdnc["_id"]])
3834 table.align = "l"
3835 print(table)
3836
3837
3838 @cli_osm.command(name="sdnc-show", short_help="shows the details of an SDN controller")
3839 @click.argument("name")
3840 @click.pass_context
3841 def sdnc_show(ctx, name):
3842 """shows the details of an SDN controller
3843
3844 NAME: name or ID of the SDN controller
3845 """
3846 logger.debug("")
3847 # try:
3848 check_client_version(ctx.obj, ctx.command.name)
3849 resp = ctx.obj.sdnc.get(name)
3850 # except ClientException as e:
3851 # print(str(e))
3852 # exit(1)
3853
3854 table = PrettyTable(["key", "attribute"])
3855 for k, v in list(resp.items()):
3856 table.add_row([k, json.dumps(v, indent=2)])
3857 table.align = "l"
3858 print(table)
3859
3860
3861 ###########################
3862 # K8s cluster operations
3863 ###########################
3864
3865
3866 @cli_osm.command(name="k8scluster-add", short_help="adds a K8s cluster to OSM")
3867 @click.argument("name")
3868 @click.option(
3869 "--creds", prompt=True, help="credentials file, i.e. a valid `.kube/config` file"
3870 )
3871 @click.option("--version", prompt=True, help="Kubernetes version")
3872 @click.option(
3873 "--vim", prompt=True, help="VIM target, the VIM where the cluster resides"
3874 )
3875 @click.option(
3876 "--k8s-nets",
3877 prompt=True,
3878 help='''list of VIM networks, in JSON inline format, where the cluster is
3879 accessible via L3 routing, e.g. "{(k8s_net1:vim_network1) [,(k8s_net2:vim_network2) ...]}"''',
3880 )
3881 @click.option(
3882 "--init-helm2/--skip-helm2", required=False, default=True, help="Initialize helm v2"
3883 )
3884 @click.option(
3885 "--init-helm3/--skip-helm3", required=False, default=True, help="Initialize helm v3"
3886 )
3887 @click.option(
3888 "--init-jujubundle/--skip-jujubundle",
3889 required=False,
3890 default=True,
3891 help="Initialize juju-bundle",
3892 )
3893 @click.option("--description", default=None, help="human readable description")
3894 @click.option(
3895 "--namespace",
3896 default="kube-system",
3897 help="namespace to be used for its operation, defaults to `kube-system`",
3898 )
3899 @click.option(
3900 "--wait",
3901 required=False,
3902 default=False,
3903 is_flag=True,
3904 help="do not return the control immediately, but keep it "
3905 "until the operation is completed, or timeout",
3906 )
3907 @click.option(
3908 "--cni",
3909 default=None,
3910 help="list of CNI plugins, in JSON inline format, used in the cluster",
3911 )
3912 # @click.option('--skip-init',
3913 # is_flag=True,
3914 # help='If set, K8s cluster is assumed to be ready for its use with OSM')
3915 # @click.option('--wait',
3916 # is_flag=True,
3917 # help='do not return the control immediately, but keep it until the operation is completed, or timeout')
3918 @click.pass_context
3919 def k8scluster_add(
3920 ctx,
3921 name,
3922 creds,
3923 version,
3924 vim,
3925 k8s_nets,
3926 init_helm2,
3927 init_helm3,
3928 init_jujubundle,
3929 description,
3930 namespace,
3931 wait,
3932 cni,
3933 ):
3934 """adds a K8s cluster to OSM
3935
3936 NAME: name of the K8s cluster
3937 """
3938 # try:
3939 check_client_version(ctx.obj, ctx.command.name)
3940 cluster = {}
3941 cluster["name"] = name
3942 with open(creds, "r") as cf:
3943 cluster["credentials"] = yaml.safe_load(cf.read())
3944 cluster["k8s_version"] = version
3945 cluster["vim_account"] = vim
3946 cluster["nets"] = yaml.safe_load(k8s_nets)
3947 if not (init_helm2 and init_jujubundle and init_helm3):
3948 cluster["deployment_methods"] = {
3949 "helm-chart": init_helm2,
3950 "juju-bundle": init_jujubundle,
3951 "helm-chart-v3": init_helm3,
3952 }
3953 if description:
3954 cluster["description"] = description
3955 if namespace:
3956 cluster["namespace"] = namespace
3957 if cni:
3958 cluster["cni"] = yaml.safe_load(cni)
3959 ctx.obj.k8scluster.create(name, cluster, wait)
3960 # except ClientException as e:
3961 # print(str(e))
3962 # exit(1)
3963
3964
3965 @cli_osm.command(name="k8scluster-update", short_help="updates a K8s cluster")
3966 @click.argument("name")
3967 @click.option("--newname", help="New name for the K8s cluster")
3968 @click.option("--creds", help="credentials file, i.e. a valid `.kube/config` file")
3969 @click.option("--version", help="Kubernetes version")
3970 @click.option("--vim", help="VIM target, the VIM where the cluster resides")
3971 @click.option(
3972 "--k8s-nets",
3973 help='''list of VIM networks, in JSON inline format, where the cluster is accessible
3974 via L3 routing, e.g. "{(k8s_net1:vim_network1) [,(k8s_net2:vim_network2) ...]}"''',
3975 )
3976 @click.option("--description", help="human readable description")
3977 @click.option(
3978 "--namespace",
3979 help="namespace to be used for its operation, defaults to `kube-system`",
3980 )
3981 @click.option(
3982 "--wait",
3983 required=False,
3984 default=False,
3985 is_flag=True,
3986 help="do not return the control immediately, but keep it "
3987 "until the operation is completed, or timeout",
3988 )
3989 @click.option(
3990 "--cni", help="list of CNI plugins, in JSON inline format, used in the cluster"
3991 )
3992 @click.pass_context
3993 def k8scluster_update(
3994 ctx, name, newname, creds, version, vim, k8s_nets, description, namespace, wait, cni
3995 ):
3996 """updates a K8s cluster
3997
3998 NAME: name or ID of the K8s cluster
3999 """
4000 # try:
4001 check_client_version(ctx.obj, ctx.command.name)
4002 cluster = {}
4003 if newname:
4004 cluster["name"] = newname
4005 if creds:
4006 with open(creds, "r") as cf:
4007 cluster["credentials"] = yaml.safe_load(cf.read())
4008 if version:
4009 cluster["k8s_version"] = version
4010 if vim:
4011 cluster["vim_account"] = vim
4012 if k8s_nets:
4013 cluster["nets"] = yaml.safe_load(k8s_nets)
4014 if description:
4015 cluster["description"] = description
4016 if namespace:
4017 cluster["namespace"] = namespace
4018 if cni:
4019 cluster["cni"] = yaml.safe_load(cni)
4020 ctx.obj.k8scluster.update(name, cluster, wait)
4021 # except ClientException as e:
4022 # print(str(e))
4023 # exit(1)
4024
4025
4026 @cli_osm.command(name="k8scluster-delete", short_help="deletes a K8s cluster")
4027 @click.argument("name")
4028 @click.option(
4029 "--force", is_flag=True, help="forces the deletion from the DB (not recommended)"
4030 )
4031 @click.option(
4032 "--wait",
4033 required=False,
4034 default=False,
4035 is_flag=True,
4036 help="do not return the control immediately, but keep it "
4037 "until the operation is completed, or timeout",
4038 )
4039 @click.pass_context
4040 def k8scluster_delete(ctx, name, force, wait):
4041 """deletes a K8s cluster
4042
4043 NAME: name or ID of the K8s cluster to be deleted
4044 """
4045 # try:
4046 check_client_version(ctx.obj, ctx.command.name)
4047 ctx.obj.k8scluster.delete(name, force, wait)
4048 # except ClientException as e:
4049 # print(str(e))
4050 # exit(1)
4051
4052
4053 @cli_osm.command(name="k8scluster-list")
4054 @click.option(
4055 "--filter",
4056 default=None,
4057 multiple=True,
4058 help="restricts the list to the K8s clusters matching the filter",
4059 )
4060 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
4061 @click.option("--long", is_flag=True, help="get more details")
4062 @click.pass_context
4063 def k8scluster_list(ctx, filter, literal, long):
4064 """list all K8s clusters"""
4065 # try:
4066 check_client_version(ctx.obj, ctx.command.name)
4067 if filter:
4068 filter = "&".join(filter)
4069 resp = ctx.obj.k8scluster.list(filter)
4070 if literal:
4071 print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
4072 return
4073 if long:
4074 table = PrettyTable(
4075 [
4076 "Name",
4077 "Id",
4078 "Project",
4079 "Version",
4080 "VIM",
4081 "K8s-nets",
4082 "Deployment methods",
4083 "Operational State",
4084 "Op. state (details)",
4085 "Description",
4086 "Detailed status",
4087 ]
4088 )
4089 project_list = ctx.obj.project.list()
4090 else:
4091 table = PrettyTable(
4092 ["Name", "Id", "VIM", "Operational State", "Op. state details"]
4093 )
4094 try:
4095 vim_list = ctx.obj.vim.list()
4096 except Exception:
4097 vim_list = []
4098 for cluster in resp:
4099 logger.debug("Cluster details: {}".format(yaml.safe_dump(cluster)))
4100 vim_name = get_vim_name(vim_list, cluster["vim_account"])
4101 # vim_info = '{} ({})'.format(vim_name,cluster['vim_account'])
4102 vim_info = vim_name
4103 op_state_details = "Helm: {}\nJuju: {}".format(
4104 cluster["_admin"].get("helm-chart", {}).get("operationalState", "-"),
4105 cluster["_admin"].get("juju-bundle", {}).get("operationalState", "-"),
4106 )
4107 if long:
4108 project_id, project_name = get_project(project_list, cluster)
4109 # project_info = '{} ({})'.format(project_name, project_id)
4110 project_info = project_name
4111 detailed_status = cluster["_admin"].get("detailed-status", "-")
4112 table.add_row(
4113 [
4114 cluster["name"],
4115 cluster["_id"],
4116 project_info,
4117 cluster["k8s_version"],
4118 vim_info,
4119 json.dumps(cluster["nets"]),
4120 json.dumps(cluster["deployment_methods"]),
4121 cluster["_admin"]["operationalState"],
4122 op_state_details,
4123 trunc_text(cluster.get("description") or "", 40),
4124 wrap_text(text=detailed_status, width=40),
4125 ]
4126 )
4127 else:
4128 table.add_row(
4129 [
4130 cluster["name"],
4131 cluster["_id"],
4132 vim_info,
4133 cluster["_admin"]["operationalState"],
4134 op_state_details,
4135 ]
4136 )
4137 table.align = "l"
4138 print(table)
4139 # except ClientException as e:
4140 # print(str(e))
4141 # exit(1)
4142
4143
4144 @cli_osm.command(
4145 name="k8scluster-show", short_help="shows the details of a K8s cluster"
4146 )
4147 @click.argument("name")
4148 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
4149 @click.pass_context
4150 def k8scluster_show(ctx, name, literal):
4151 """shows the details of a K8s cluster
4152
4153 NAME: name or ID of the K8s cluster
4154 """
4155 # try:
4156 resp = ctx.obj.k8scluster.get(name)
4157 if literal:
4158 print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
4159 return
4160 table = PrettyTable(["key", "attribute"])
4161 for k, v in list(resp.items()):
4162 table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)])
4163 table.align = "l"
4164 print(table)
4165 # except ClientException as e:
4166 # print(str(e))
4167 # exit(1)
4168
4169
4170 ###########################
4171 # VCA operations
4172 ###########################
4173
4174
4175 @cli_osm.command(name="vca-add", short_help="adds a VCA (Juju controller) to OSM")
4176 @click.argument("name")
4177 @click.option(
4178 "--endpoints",
4179 prompt=True,
4180 help="Comma-separated list of IP or hostnames of the Juju controller",
4181 )
4182 @click.option("--user", prompt=True, help="Username with admin priviledges")
4183 @click.option("--secret", prompt=True, help="Password of the specified username")
4184 @click.option("--cacert", prompt=True, help="CA certificate")
4185 @click.option(
4186 "--lxd-cloud",
4187 prompt=True,
4188 help="Name of the cloud that will be used for LXD containers (LXD proxy charms)",
4189 )
4190 @click.option(
4191 "--lxd-credentials",
4192 prompt=True,
4193 help="Name of the cloud credentialsto be used for the LXD cloud",
4194 )
4195 @click.option(
4196 "--k8s-cloud",
4197 prompt=True,
4198 help="Name of the cloud that will be used for K8s containers (K8s proxy charms)",
4199 )
4200 @click.option(
4201 "--k8s-credentials",
4202 prompt=True,
4203 help="Name of the cloud credentialsto be used for the K8s cloud",
4204 )
4205 @click.option("--model-config", default={}, help="Configuration options for the models")
4206 @click.option("--description", default=None, help="human readable description")
4207 @click.pass_context
4208 def vca_add(
4209 ctx,
4210 name,
4211 endpoints,
4212 user,
4213 secret,
4214 cacert,
4215 lxd_cloud,
4216 lxd_credentials,
4217 k8s_cloud,
4218 k8s_credentials,
4219 model_config,
4220 description,
4221 ):
4222 """adds a VCA to OSM
4223
4224 NAME: name of the VCA
4225 """
4226 check_client_version(ctx.obj, ctx.command.name)
4227 vca = {}
4228 vca["name"] = name
4229 vca["endpoints"] = endpoints.split(",")
4230 vca["user"] = user
4231 vca["secret"] = secret
4232 vca["cacert"] = cacert
4233 vca["lxd-cloud"] = lxd_cloud
4234 vca["lxd-credentials"] = lxd_credentials
4235 vca["k8s-cloud"] = k8s_cloud
4236 vca["k8s-credentials"] = k8s_credentials
4237 if description:
4238 vca["description"] = description
4239 if model_config:
4240 model_config = load(model_config)
4241 vca["model-config"] = model_config
4242 ctx.obj.vca.create(name, vca)
4243
4244
4245 def load(data: Any):
4246 if os.path.isfile(data):
4247 return load_file(data)
4248 else:
4249 try:
4250 return json.loads(data)
4251 except ValueError as e:
4252 raise ClientException(e)
4253
4254
4255 def load_file(file_path: str) -> Dict:
4256 content = None
4257 with open(file_path, "r") as f:
4258 content = f.read()
4259 try:
4260 return yaml.safe_load(content)
4261 except yaml.scanner.ScannerError:
4262 pass
4263 try:
4264 return json.loads(content)
4265 except ValueError:
4266 pass
4267 raise ClientException(f"{file_path} must be a valid yaml or json file")
4268
4269
4270 @cli_osm.command(name="vca-update", short_help="updates a K8s cluster")
4271 @click.argument("name")
4272 @click.option(
4273 "--endpoints", help="Comma-separated list of IP or hostnames of the Juju controller"
4274 )
4275 @click.option("--user", help="Username with admin priviledges")
4276 @click.option("--secret", help="Password of the specified username")
4277 @click.option("--cacert", help="CA certificate")
4278 @click.option(
4279 "--lxd-cloud",
4280 help="Name of the cloud that will be used for LXD containers (LXD proxy charms)",
4281 )
4282 @click.option(
4283 "--lxd-credentials",
4284 help="Name of the cloud credentialsto be used for the LXD cloud",
4285 )
4286 @click.option(
4287 "--k8s-cloud",
4288 help="Name of the cloud that will be used for K8s containers (K8s proxy charms)",
4289 )
4290 @click.option(
4291 "--k8s-credentials",
4292 help="Name of the cloud credentialsto be used for the K8s cloud",
4293 )
4294 @click.option("--model-config", help="Configuration options for the models")
4295 @click.option("--description", default=None, help="human readable description")
4296 @click.pass_context
4297 def vca_update(
4298 ctx,
4299 name,
4300 endpoints,
4301 user,
4302 secret,
4303 cacert,
4304 lxd_cloud,
4305 lxd_credentials,
4306 k8s_cloud,
4307 k8s_credentials,
4308 model_config,
4309 description,
4310 ):
4311 """updates a K8s cluster
4312
4313 NAME: name or ID of the K8s cluster
4314 """
4315 check_client_version(ctx.obj, ctx.command.name)
4316 vca = {}
4317 vca["name"] = name
4318 if endpoints:
4319 vca["endpoints"] = endpoints.split(",")
4320 if user:
4321 vca["user"] = user
4322 if secret:
4323 vca["secret"] = secret
4324 if cacert:
4325 vca["cacert"] = cacert
4326 if lxd_cloud:
4327 vca["lxd-cloud"] = lxd_cloud
4328 if lxd_credentials:
4329 vca["lxd-credentials"] = lxd_credentials
4330 if k8s_cloud:
4331 vca["k8s-cloud"] = k8s_cloud
4332 if k8s_credentials:
4333 vca["k8s-credentials"] = k8s_credentials
4334 if description:
4335 vca["description"] = description
4336 if model_config:
4337 model_config = load(model_config)
4338 vca["model-config"] = model_config
4339 ctx.obj.vca.update(name, vca)
4340
4341
4342 @cli_osm.command(name="vca-delete", short_help="deletes a K8s cluster")
4343 @click.argument("name")
4344 @click.option(
4345 "--force", is_flag=True, help="forces the deletion from the DB (not recommended)"
4346 )
4347 @click.pass_context
4348 def vca_delete(ctx, name, force):
4349 """deletes a K8s cluster
4350
4351 NAME: name or ID of the K8s cluster to be deleted
4352 """
4353 check_client_version(ctx.obj, ctx.command.name)
4354 ctx.obj.vca.delete(name, force=force)
4355
4356
4357 @cli_osm.command(name="vca-list")
4358 @click.option(
4359 "--filter",
4360 default=None,
4361 multiple=True,
4362 help="restricts the list to the VCAs matching the filter",
4363 )
4364 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
4365 @click.option("--long", is_flag=True, help="get more details")
4366 @click.pass_context
4367 def vca_list(ctx, filter, literal, long):
4368 """list VCAs"""
4369 check_client_version(ctx.obj, ctx.command.name)
4370 if filter:
4371 filter = "&".join(filter)
4372 resp = ctx.obj.vca.list(filter)
4373 if literal:
4374 print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
4375 return
4376 if long:
4377 table = PrettyTable(
4378 ["Name", "Id", "Project", "Operational State", "Detailed Status"]
4379 )
4380 project_list = ctx.obj.project.list()
4381 else:
4382 table = PrettyTable(["Name", "Id", "Operational State"])
4383 for vca in resp:
4384 logger.debug("VCA details: {}".format(yaml.safe_dump(vca)))
4385 if long:
4386 project_id, project_name = get_project(project_list, vca)
4387 detailed_status = vca.get("_admin", {}).get("detailed-status", "-")
4388 table.add_row(
4389 [
4390 vca["name"],
4391 vca["_id"],
4392 project_name,
4393 vca.get("_admin", {}).get("operationalState", "-"),
4394 wrap_text(text=detailed_status, width=40),
4395 ]
4396 )
4397 else:
4398 table.add_row(
4399 [
4400 vca["name"],
4401 vca["_id"],
4402 vca.get("_admin", {}).get("operationalState", "-"),
4403 ]
4404 )
4405 table.align = "l"
4406 print(table)
4407
4408
4409 @cli_osm.command(name="vca-show", short_help="shows the details of a K8s cluster")
4410 @click.argument("name")
4411 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
4412 @click.pass_context
4413 def vca_show(ctx, name, literal):
4414 """shows the details of a K8s cluster
4415
4416 NAME: name or ID of the K8s cluster
4417 """
4418 # try:
4419 resp = ctx.obj.vca.get(name)
4420 if literal:
4421 print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
4422 return
4423 table = PrettyTable(["key", "attribute"])
4424 for k, v in list(resp.items()):
4425 table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)])
4426 table.align = "l"
4427 print(table)
4428
4429
4430 ###########################
4431 # PaaS operations
4432 ###########################
4433
4434
4435 @cli_osm.command(name="paas-add", short_help="adds a PaaS to OSM.")
4436 @click.argument("name")
4437 @click.option(
4438 "--paas_type",
4439 type=click.Choice(["juju"]),
4440 default="juju",
4441 prompt=True,
4442 help="Type of PaaS that can be used. (For the moment, only juju is supported).",
4443 )
4444 @click.option(
4445 "--endpoints",
4446 prompt=True,
4447 help="Comma-separated list of IP or hostnames of the PaaS.",
4448 )
4449 @click.option("--user", prompt=True, help="Username with admin priviledges.")
4450 @click.option("--secret", prompt=True, help="Password of the specified username.")
4451 @click.option(
4452 "--config", default={}, help="Extra configuration needed by PaaS service."
4453 )
4454 @click.option("--description", default=None, help="Human readable description.")
4455 @click.pass_context
4456 def paas_add(ctx, name, paas_type, endpoints, user, secret, config, description):
4457 """adds a PaaS to OSM.
4458 Args:
4459 name (str): Name of the new PaaS.
4460 """
4461 check_client_version(ctx.obj, ctx.command.name)
4462 paas = {
4463 "name": name,
4464 "paas_type": paas_type,
4465 "endpoints": endpoints.split(","),
4466 "user": user,
4467 "secret": secret,
4468 }
4469 if description:
4470 paas["description"] = description
4471 if config:
4472 config = load(config)
4473 paas["config"] = config
4474 ctx.obj.paas.create(paas)
4475
4476
4477 @cli_osm.command(name="paas-update", short_help="updates a PaaS")
4478 @click.argument("name")
4479 @click.option("--newname", help="New name for the PaaS")
4480 @click.option(
4481 "--paas_type",
4482 type=click.Choice(["juju"]),
4483 help="Type of PaaS that can be used. (For the moment, only juju is supported)",
4484 )
4485 @click.option(
4486 "--endpoints", help="Comma-separated list of IP or hostnames of the Juju controller"
4487 )
4488 @click.option("--user", help="Username with admin priviledges")
4489 @click.option("--secret", help="Password of the specified username")
4490 @click.option("--config", help="Extra configuration needed by PaaS service")
4491 @click.option("--description", default=None, help="Human readable description")
4492 @click.pass_context
4493 def paas_update(
4494 ctx, name, newname, paas_type, endpoints, user, secret, config, description
4495 ):
4496 """updates a PaaS.
4497 Args:
4498 name (str): Name or ID of the PaaS to update.
4499 """
4500 check_client_version(ctx.obj, ctx.command.name)
4501 paas = {}
4502 if newname:
4503 paas["name"] = newname
4504 if paas_type:
4505 paas["paas_type"] = paas_type
4506 if endpoints:
4507 paas["endpoints"] = endpoints.split(",")
4508 if user:
4509 paas["user"] = user
4510 if secret:
4511 paas["secret"] = secret
4512 if description:
4513 paas["description"] = description
4514 if config:
4515 config = load(config)
4516 paas["config"] = config
4517 ctx.obj.paas.update(name, paas)
4518
4519
4520 @cli_osm.command(name="paas-delete", short_help="deletes a PaaS")
4521 @click.argument("name")
4522 @click.option(
4523 "--force", is_flag=True, help="forces the deletion from the DB (not recommended)"
4524 )
4525 @click.pass_context
4526 def paas_delete(ctx, name, force):
4527 """deletes a PaaS.
4528
4529 Args:
4530 name (str): Name or ID of the PaaS to delete.
4531 """
4532 check_client_version(ctx.obj, ctx.command.name)
4533 ctx.obj.paas.delete(name, force=force)
4534
4535
4536 @cli_osm.command(name="paas-list")
4537 @click.option(
4538 "--filter",
4539 default=None,
4540 multiple=True,
4541 help="Restricts the list to the PaaS matching the filter",
4542 )
4543 @click.option("--literal", is_flag=True, help="Print literally, no pretty table")
4544 @click.option("--long", is_flag=True, help="get more details")
4545 @click.pass_context
4546 def paas_list(ctx, filter, literal, long):
4547 """List PaaSs"""
4548 check_client_version(ctx.obj, ctx.command.name)
4549 if filter:
4550 filter = "&".join(filter)
4551 resp = ctx.obj.paas.list(filter)
4552 if literal:
4553 print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
4554 return
4555
4556 table = _get_paas_table_header(long)
4557 project_list = ctx.obj.project.list()
4558 for paas in resp:
4559 logger.debug("PaaS details: {}".format(yaml.safe_dump(paas)))
4560 if long:
4561 _add_paas_long_row(table, paas, project_list)
4562 else:
4563 _add_paas_row(table, paas)
4564 table.align = "l"
4565 print(table)
4566
4567
4568 def _get_paas_table_header(long):
4569 if long:
4570 return PrettyTable(
4571 ["Name", "Id", "Project", "Operational State", "Detailed Status"]
4572 )
4573 return PrettyTable(["Name", "Id", "Operational State"])
4574
4575
4576 def _add_paas_long_row(table, paas, project_list):
4577 _, project_name = get_project(project_list, paas)
4578 detailed_status = paas.get("_admin", {}).get("detailed-status", "-")
4579 table.add_row(
4580 [
4581 paas["name"],
4582 paas["_id"],
4583 project_name,
4584 paas.get("_admin", {}).get("operationalState", "-"),
4585 wrap_text(text=detailed_status, width=40),
4586 ]
4587 )
4588
4589
4590 def _add_paas_row(table, paas):
4591 table.add_row(
4592 [paas["name"], paas["_id"], paas.get("_admin", {}).get("operationalState", "-")]
4593 )
4594
4595
4596 @cli_osm.command(name="paas-show", short_help="Shows the details of a PaaS")
4597 @click.argument("name")
4598 @click.option("--literal", is_flag=True, help="Print literally, no pretty table")
4599 @click.pass_context
4600 def paas_show(ctx, name, literal):
4601 """Shows the details of a PaaS.
4602
4603 Args:
4604 name (str): Name or ID of the PaaS to show.
4605 """
4606 resp = ctx.obj.paas.get(name)
4607 if literal:
4608 print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
4609 return
4610 table = PrettyTable(["key", "attribute"])
4611 for k, v in list(resp.items()):
4612 table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)])
4613 table.align = "l"
4614 print(table)
4615
4616
4617 ###########################
4618 # Repo operations
4619 ###########################
4620
4621
4622 @cli_osm.command(name="repo-add", short_help="adds a repo to OSM")
4623 @click.argument("name")
4624 @click.argument("uri")
4625 @click.option(
4626 "--type",
4627 type=click.Choice(["helm-chart", "juju-bundle", "osm"]),
4628 default="osm",
4629 help="type of repo (helm-chart for Helm Charts, juju-bundle for Juju Bundles, osm for OSM Repositories)",
4630 )
4631 @click.option("--description", default=None, help="human readable description")
4632 @click.option(
4633 "--user", default=None, help="OSM repository: The username of the OSM repository"
4634 )
4635 @click.option(
4636 "--password",
4637 default=None,
4638 help="OSM repository: The password of the OSM repository",
4639 )
4640 # @click.option('--wait',
4641 # is_flag=True,
4642 # help='do not return the control immediately, but keep it until the operation is completed, or timeout')
4643 @click.pass_context
4644 def repo_add(ctx, **kwargs):
4645 """adds a repo to OSM
4646
4647 NAME: name of the repo
4648 URI: URI of the repo
4649 """
4650 # try:
4651 kwargs = {k: v for k, v in kwargs.items() if v is not None}
4652 repo = kwargs
4653 repo["url"] = repo.pop("uri")
4654 if repo["type"] in ["helm-chart", "juju-bundle"]:
4655 ctx.obj.repo.create(repo["name"], repo)
4656 else:
4657 ctx.obj.osmrepo.create(repo["name"], repo)
4658 # except ClientException as e:
4659 # print(str(e))
4660 # exit(1)
4661
4662
4663 @cli_osm.command(name="repo-update", short_help="updates a repo in OSM")
4664 @click.argument("name")
4665 @click.option("--newname", help="New name for the repo")
4666 @click.option("--uri", help="URI of the repo")
4667 @click.option("--description", help="human readable description")
4668 # @click.option('--wait',
4669 # is_flag=True,
4670 # help='do not return the control immediately, but keep it until the operation is completed, or timeout')
4671 @click.pass_context
4672 def repo_update(ctx, name, newname, uri, description):
4673 """updates a repo in OSM
4674
4675 NAME: name of the repo
4676 """
4677 # try:
4678 check_client_version(ctx.obj, ctx.command.name)
4679 repo = {}
4680 if newname:
4681 repo["name"] = newname
4682 if uri:
4683 repo["uri"] = uri
4684 if description:
4685 repo["description"] = description
4686 try:
4687 ctx.obj.repo.update(name, repo)
4688 except NotFound:
4689 ctx.obj.osmrepo.update(name, repo)
4690
4691 # except ClientException as e:
4692 # print(str(e))
4693 # exit(1)
4694
4695
4696 @cli_osm.command(
4697 name="repo-index", short_help="Index a repository from a folder with artifacts"
4698 )
4699 @click.option(
4700 "--origin", default=".", help="origin path where the artifacts are located"
4701 )
4702 @click.option(
4703 "--destination", default=".", help="destination path where the index is deployed"
4704 )
4705 @click.pass_context
4706 def repo_index(ctx, origin, destination):
4707 """Index a repository
4708
4709 NAME: name or ID of the repo to be deleted
4710 """
4711 check_client_version(ctx.obj, ctx.command.name)
4712 ctx.obj.osmrepo.repo_index(origin, destination)
4713
4714
4715 @cli_osm.command(name="repo-delete", short_help="deletes a repo")
4716 @click.argument("name")
4717 @click.option(
4718 "--force", is_flag=True, help="forces the deletion from the DB (not recommended)"
4719 )
4720 # @click.option('--wait',
4721 # is_flag=True,
4722 # help='do not return the control immediately, but keep it until the operation is completed, or timeout')
4723 @click.pass_context
4724 def repo_delete(ctx, name, force):
4725 """deletes a repo
4726
4727 NAME: name or ID of the repo to be deleted
4728 """
4729 logger.debug("")
4730 try:
4731 ctx.obj.repo.delete(name, force=force)
4732 except NotFound:
4733 ctx.obj.osmrepo.delete(name, force=force)
4734 # except ClientException as e:
4735 # print(str(e))
4736 # exit(1)
4737
4738
4739 @cli_osm.command(name="repo-list")
4740 @click.option(
4741 "--filter",
4742 default=None,
4743 multiple=True,
4744 help="restricts the list to the repos matching the filter",
4745 )
4746 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
4747 @click.pass_context
4748 def repo_list(ctx, filter, literal):
4749 """list all repos"""
4750 # try:
4751 # K8s Repositories
4752 check_client_version(ctx.obj, ctx.command.name)
4753 if filter:
4754 filter = "&".join(filter)
4755 resp = ctx.obj.repo.list(filter)
4756 resp += ctx.obj.osmrepo.list(filter)
4757 if literal:
4758 print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
4759 return
4760 table = PrettyTable(["Name", "Id", "Type", "URI", "Description"])
4761 for repo in resp:
4762 # cluster['k8s-nets'] = json.dumps(yaml.safe_load(cluster['k8s-nets']))
4763 table.add_row(
4764 [
4765 repo["name"],
4766 repo["_id"],
4767 repo["type"],
4768 repo["url"],
4769 trunc_text(repo.get("description") or "", 40),
4770 ]
4771 )
4772 table.align = "l"
4773 print(table)
4774
4775 # except ClientException as e:
4776 # print(str(e))
4777 # exit(1)
4778
4779
4780 @cli_osm.command(name="repo-show", short_help="shows the details of a repo")
4781 @click.argument("name")
4782 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
4783 @click.pass_context
4784 def repo_show(ctx, name, literal):
4785 """shows the details of a repo
4786
4787 NAME: name or ID of the repo
4788 """
4789 try:
4790 resp = ctx.obj.repo.get(name)
4791 except NotFound:
4792 resp = ctx.obj.osmrepo.get(name)
4793
4794 if literal:
4795 if resp:
4796 print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
4797 return
4798 table = PrettyTable(["key", "attribute"])
4799 if resp:
4800 for k, v in list(resp.items()):
4801 table.add_row([k, json.dumps(v, indent=2)])
4802
4803 table.align = "l"
4804 print(table)
4805 # except ClientException as e:
4806 # print(str(e))
4807 # exit(1)
4808
4809
4810 ####################
4811 # Project mgmt operations
4812 ####################
4813
4814
4815 @cli_osm.command(name="project-create", short_help="creates a new project")
4816 @click.argument("name")
4817 # @click.option('--description',
4818 # default='no description',
4819 # help='human readable description')
4820 @click.option("--domain-name", "domain_name", default=None, help="assign to a domain")
4821 @click.option(
4822 "--quotas",
4823 "quotas",
4824 multiple=True,
4825 default=None,
4826 help="provide quotas. Can be used several times: 'quota1=number[,quota2=number,...]'. Quotas can be one "
4827 "of vnfds, nsds, nsts, pdus, nsrs, nsis, vim_accounts, wim_accounts, sdns, k8sclusters, k8srepos",
4828 )
4829 @click.pass_context
4830 def project_create(ctx, name, domain_name, quotas):
4831 """Creates a new project
4832
4833 NAME: name of the project
4834 DOMAIN_NAME: optional domain name for the project when keystone authentication is used
4835 QUOTAS: set quotas for the project
4836 """
4837 logger.debug("")
4838 project = {"name": name}
4839 if domain_name:
4840 project["domain_name"] = domain_name
4841 quotas_dict = _process_project_quotas(quotas)
4842 if quotas_dict:
4843 project["quotas"] = quotas_dict
4844
4845 # try:
4846 check_client_version(ctx.obj, ctx.command.name)
4847 ctx.obj.project.create(name, project)
4848 # except ClientException as e:
4849 # print(str(e))
4850 # exit(1)
4851
4852
4853 def _process_project_quotas(quota_list):
4854 quotas_dict = {}
4855 if not quota_list:
4856 return quotas_dict
4857 try:
4858 for quota in quota_list:
4859 for single_quota in quota.split(","):
4860 k, v = single_quota.split("=")
4861 quotas_dict[k] = None if v in ("None", "null", "") else int(v)
4862 except (ValueError, TypeError):
4863 raise ClientException(
4864 "invalid format for 'quotas'. Use 'k1=v1,v1=v2'. v must be a integer or null"
4865 )
4866 return quotas_dict
4867
4868
4869 @cli_osm.command(name="project-delete", short_help="deletes a project")
4870 @click.argument("name")
4871 # @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
4872 @click.pass_context
4873 def project_delete(ctx, name):
4874 """deletes a project
4875
4876 NAME: name or ID of the project to be deleted
4877 """
4878 logger.debug("")
4879 # try:
4880 check_client_version(ctx.obj, ctx.command.name)
4881 ctx.obj.project.delete(name)
4882 # except ClientException as e:
4883 # print(str(e))
4884 # exit(1)
4885
4886
4887 @cli_osm.command(name="project-list", short_help="list all projects")
4888 @click.option(
4889 "--filter",
4890 default=None,
4891 multiple=True,
4892 help="restricts the list to the projects matching the filter",
4893 )
4894 @click.pass_context
4895 def project_list(ctx, filter):
4896 """list all projects"""
4897 logger.debug("")
4898 # try:
4899 check_client_version(ctx.obj, ctx.command.name)
4900 if filter:
4901 filter = "&".join(filter)
4902 resp = ctx.obj.project.list(filter)
4903 # except ClientException as e:
4904 # print(str(e))
4905 # exit(1)
4906 table = PrettyTable(["name", "id"])
4907 for proj in resp:
4908 table.add_row([proj["name"], proj["_id"]])
4909 table.align = "l"
4910 print(table)
4911
4912
4913 @cli_osm.command(name="project-show", short_help="shows the details of a project")
4914 @click.argument("name")
4915 @click.pass_context
4916 def project_show(ctx, name):
4917 """shows the details of a project
4918
4919 NAME: name or ID of the project
4920 """
4921 logger.debug("")
4922 # try:
4923 check_client_version(ctx.obj, ctx.command.name)
4924 resp = ctx.obj.project.get(name)
4925 # except ClientException as e:
4926 # print(str(e))
4927 # exit(1)
4928
4929 table = PrettyTable(["key", "attribute"])
4930 for k, v in resp.items():
4931 table.add_row([k, json.dumps(v, indent=2)])
4932 table.align = "l"
4933 print(table)
4934
4935
4936 @cli_osm.command(
4937 name="project-update", short_help="updates a project (only the name can be updated)"
4938 )
4939 @click.argument("project")
4940 @click.option("--name", default=None, help="new name for the project")
4941 @click.option(
4942 "--quotas",
4943 "quotas",
4944 multiple=True,
4945 default=None,
4946 help="change quotas. Can be used several times: 'quota1=number|empty[,quota2=...]' "
4947 "(use empty to reset quota to default",
4948 )
4949 @click.pass_context
4950 def project_update(ctx, project, name, quotas):
4951 """
4952 Update a project name
4953
4954 :param ctx:
4955 :param project: id or name of the project to modify
4956 :param name: new name for the project
4957 :param quotas: change quotas of the project
4958 :return:
4959 """
4960 logger.debug("")
4961 project_changes = {}
4962 if name:
4963 project_changes["name"] = name
4964 quotas_dict = _process_project_quotas(quotas)
4965 if quotas_dict:
4966 project_changes["quotas"] = quotas_dict
4967
4968 # try:
4969 check_client_version(ctx.obj, ctx.command.name)
4970 ctx.obj.project.update(project, project_changes)
4971 # except ClientException as e:
4972 # print(str(e))
4973
4974
4975 ####################
4976 # User mgmt operations
4977 ####################
4978
4979
4980 @cli_osm.command(name="user-create", short_help="creates a new user")
4981 @click.argument("username")
4982 @click.option(
4983 "--password",
4984 prompt=True,
4985 hide_input=True,
4986 confirmation_prompt=True,
4987 help="user password",
4988 )
4989 @click.option(
4990 "--projects",
4991 # prompt="Comma separate list of projects",
4992 multiple=True,
4993 callback=lambda ctx, param, value: "".join(value).split(",")
4994 if all(len(x) == 1 for x in value)
4995 else value,
4996 help="list of project ids that the user belongs to",
4997 )
4998 @click.option(
4999 "--project-role-mappings",
5000 "project_role_mappings",
5001 default=None,
5002 multiple=True,
5003 help="assign role(s) in a project. Can be used several times: 'project,role1[,role2,...]'",
5004 )
5005 @click.option("--domain-name", "domain_name", default=None, help="assign to a domain")
5006 @click.pass_context
5007 def user_create(ctx, username, password, projects, project_role_mappings, domain_name):
5008 """Creates a new user
5009
5010 \b
5011 USERNAME: name of the user
5012 PASSWORD: password of the user
5013 PROJECTS: projects assigned to user (internal only)
5014 PROJECT_ROLE_MAPPING: roles in projects assigned to user (keystone)
5015 DOMAIN_NAME: optional domain name for the user when keystone authentication is used
5016 """
5017 logger.debug("")
5018 user = {}
5019 user["username"] = username
5020 user["password"] = password
5021 user["projects"] = projects
5022 user["project_role_mappings"] = project_role_mappings
5023 if domain_name:
5024 user["domain_name"] = domain_name
5025
5026 # try:
5027 check_client_version(ctx.obj, ctx.command.name)
5028 ctx.obj.user.create(username, user)
5029 # except ClientException as e:
5030 # print(str(e))
5031 # exit(1)
5032
5033
5034 @cli_osm.command(name="user-update", short_help="updates user information")
5035 @click.argument("username")
5036 @click.option(
5037 "--password",
5038 # prompt=True,
5039 # hide_input=True,
5040 # confirmation_prompt=True,
5041 help="user password",
5042 )
5043 @click.option("--set-username", "set_username", default=None, help="change username")
5044 @click.option(
5045 "--set-project",
5046 "set_project",
5047 default=None,
5048 multiple=True,
5049 help="create/replace the roles for this project: 'project,role1[,role2,...]'",
5050 )
5051 @click.option(
5052 "--remove-project",
5053 "remove_project",
5054 default=None,
5055 multiple=True,
5056 help="removes project from user: 'project'",
5057 )
5058 @click.option(
5059 "--add-project-role",
5060 "add_project_role",
5061 default=None,
5062 multiple=True,
5063 help="assign role(s) in a project. Can be used several times: 'project,role1[,role2,...]'",
5064 )
5065 @click.option(
5066 "--remove-project-role",
5067 "remove_project_role",
5068 default=None,
5069 multiple=True,
5070 help="remove role(s) in a project. Can be used several times: 'project,role1[,role2,...]'",
5071 )
5072 @click.option("--change_password", "change_password", help="user's current password")
5073 @click.option(
5074 "--new_password",
5075 "new_password",
5076 help="user's new password to update in expiry condition",
5077 )
5078 @click.pass_context
5079 def user_update(
5080 ctx,
5081 username,
5082 password,
5083 set_username,
5084 set_project,
5085 remove_project,
5086 add_project_role,
5087 remove_project_role,
5088 change_password,
5089 new_password,
5090 ):
5091 """Update a user information
5092
5093 \b
5094 USERNAME: name of the user
5095 PASSWORD: new password
5096 SET_USERNAME: new username
5097 SET_PROJECT: creating mappings for project/role(s)
5098 REMOVE_PROJECT: deleting mappings for project/role(s)
5099 ADD_PROJECT_ROLE: adding mappings for project/role(s)
5100 REMOVE_PROJECT_ROLE: removing mappings for project/role(s)
5101 CHANGE_PASSWORD: user's current password to change
5102 NEW_PASSWORD: user's new password to update in expiry condition
5103 """
5104 logger.debug("")
5105 user = {}
5106 user["password"] = password
5107 user["username"] = set_username
5108 user["set-project"] = set_project
5109 user["remove-project"] = remove_project
5110 user["add-project-role"] = add_project_role
5111 user["remove-project-role"] = remove_project_role
5112 user["change_password"] = change_password
5113 user["new_password"] = new_password
5114
5115 # try:
5116 check_client_version(ctx.obj, ctx.command.name)
5117 if not user.get("change_password"):
5118 ctx.obj.user.update(username, user)
5119 else:
5120 ctx.obj.user.update(username, user, pwd_change=True)
5121 # except ClientException as e:
5122 # print(str(e))
5123 # exit(1)
5124
5125
5126 @cli_osm.command(name="user-delete", short_help="deletes a user")
5127 @click.argument("name")
5128 # @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
5129 @click.pass_context
5130 def user_delete(ctx, name):
5131 """deletes a user
5132
5133 \b
5134 NAME: name or ID of the user to be deleted
5135 """
5136 logger.debug("")
5137 # try:
5138 check_client_version(ctx.obj, ctx.command.name)
5139 ctx.obj.user.delete(name)
5140 # except ClientException as e:
5141 # print(str(e))
5142 # exit(1)
5143
5144
5145 @cli_osm.command(name="user-list", short_help="list all users")
5146 @click.option(
5147 "--filter",
5148 default=None,
5149 multiple=True,
5150 help="restricts the list to the users matching the filter",
5151 )
5152 @click.pass_context
5153 def user_list(ctx, filter):
5154 """list all users"""
5155 # try:
5156 check_client_version(ctx.obj, ctx.command.name)
5157 if filter:
5158 filter = "&".join(filter)
5159 resp = ctx.obj.user.list(filter)
5160 # except ClientException as e:
5161 # print(str(e))
5162 # exit(1)
5163 table = PrettyTable(["name", "id"])
5164 for user in resp:
5165 table.add_row([user["username"], user["_id"]])
5166 table.align = "l"
5167 print(table)
5168
5169
5170 @cli_osm.command(name="user-show", short_help="shows the details of a user")
5171 @click.argument("name")
5172 @click.pass_context
5173 def user_show(ctx, name):
5174 """shows the details of a user
5175
5176 NAME: name or ID of the user
5177 """
5178 logger.debug("")
5179 # try:
5180 check_client_version(ctx.obj, ctx.command.name)
5181 resp = ctx.obj.user.get(name)
5182 if "password" in resp:
5183 resp["password"] = "********"
5184 # except ClientException as e:
5185 # print(str(e))
5186 # exit(1)
5187
5188 table = PrettyTable(["key", "attribute"])
5189 for k, v in resp.items():
5190 table.add_row([k, json.dumps(v, indent=2)])
5191 table.align = "l"
5192 print(table)
5193
5194
5195 ####################
5196 # Fault Management operations
5197 ####################
5198
5199
5200 @cli_osm.command(name="ns-alarm-create")
5201 @click.argument("name")
5202 @click.option("--ns", prompt=True, help="NS instance id or name")
5203 @click.option(
5204 "--vnf", prompt=True, help="VNF name (VNF member index as declared in the NSD)"
5205 )
5206 @click.option("--vdu", prompt=True, help="VDU name (VDU name as declared in the VNFD)")
5207 @click.option("--metric", prompt=True, help="Name of the metric (e.g. cpu_utilization)")
5208 @click.option(
5209 "--severity",
5210 default="WARNING",
5211 help="severity of the alarm (WARNING, MINOR, MAJOR, CRITICAL, INDETERMINATE)",
5212 )
5213 @click.option(
5214 "--threshold_value",
5215 prompt=True,
5216 help="threshold value that, when crossed, an alarm is triggered",
5217 )
5218 @click.option(
5219 "--threshold_operator",
5220 prompt=True,
5221 help="threshold operator describing the comparison (GE, LE, GT, LT, EQ)",
5222 )
5223 @click.option(
5224 "--statistic",
5225 default="AVERAGE",
5226 help="statistic (AVERAGE, MINIMUM, MAXIMUM, COUNT, SUM)",
5227 )
5228 @click.pass_context
5229 def ns_alarm_create(
5230 ctx,
5231 name,
5232 ns,
5233 vnf,
5234 vdu,
5235 metric,
5236 severity,
5237 threshold_value,
5238 threshold_operator,
5239 statistic,
5240 ):
5241 """creates a new alarm for a NS instance"""
5242 # TODO: Check how to validate threshold_value.
5243 # Should it be an integer (1-100), percentage, or decimal (0.01-1.00)?
5244 logger.debug("")
5245 # try:
5246 ns_instance = ctx.obj.ns.get(ns)
5247 alarm = {}
5248 alarm["alarm_name"] = name
5249 alarm["ns_id"] = ns_instance["_id"]
5250 alarm["correlation_id"] = ns_instance["_id"]
5251 alarm["vnf_member_index"] = vnf
5252 alarm["vdu_name"] = vdu
5253 alarm["metric_name"] = metric
5254 alarm["severity"] = severity
5255 alarm["threshold_value"] = int(threshold_value)
5256 alarm["operation"] = threshold_operator
5257 alarm["statistic"] = statistic
5258 check_client_version(ctx.obj, ctx.command.name)
5259 ctx.obj.ns.create_alarm(alarm)
5260 # except ClientException as e:
5261 # print(str(e))
5262 # exit(1)
5263
5264
5265 # @cli_osm.command(name='ns-alarm-delete')
5266 # @click.argument('name')
5267 # @click.pass_context
5268 # def ns_alarm_delete(ctx, name):
5269 # """deletes an alarm
5270 #
5271 # NAME: name of the alarm to be deleted
5272 # """
5273 # try:
5274 # check_client_version(ctx.obj, ctx.command.name)
5275 # ctx.obj.ns.delete_alarm(name)
5276 # except ClientException as e:
5277 # print(str(e))
5278 # exit(1)
5279
5280
5281 ####################
5282 # Performance Management operations
5283 ####################
5284
5285
5286 @cli_osm.command(
5287 name="ns-metric-export",
5288 short_help="exports a metric to the internal OSM bus, which can be read by other apps",
5289 )
5290 @click.option("--ns", prompt=True, help="NS instance id or name")
5291 @click.option(
5292 "--vnf", prompt=True, help="VNF name (VNF member index as declared in the NSD)"
5293 )
5294 @click.option("--vdu", prompt=True, help="VDU name (VDU name as declared in the VNFD)")
5295 @click.option("--metric", prompt=True, help="name of the metric (e.g. cpu_utilization)")
5296 # @click.option('--period', default='1w',
5297 # help='metric collection period (e.g. 20s, 30m, 2h, 3d, 1w)')
5298 @click.option(
5299 "--interval", help="periodic interval (seconds) to export metrics continuously"
5300 )
5301 @click.pass_context
5302 def ns_metric_export(ctx, ns, vnf, vdu, metric, interval):
5303 """exports a metric to the internal OSM bus, which can be read by other apps"""
5304 # TODO: Check how to validate interval.
5305 # Should it be an integer (seconds), or should a suffix (s,m,h,d,w) also be permitted?
5306 logger.debug("")
5307 # try:
5308 ns_instance = ctx.obj.ns.get(ns)
5309 metric_data = {}
5310 metric_data["ns_id"] = ns_instance["_id"]
5311 metric_data["correlation_id"] = ns_instance["_id"]
5312 metric_data["vnf_member_index"] = vnf
5313 metric_data["vdu_name"] = vdu
5314 metric_data["metric_name"] = metric
5315 metric_data["collection_unit"] = "WEEK"
5316 metric_data["collection_period"] = 1
5317 check_client_version(ctx.obj, ctx.command.name)
5318 if not interval:
5319 print("{}".format(ctx.obj.ns.export_metric(metric_data)))
5320 else:
5321 i = 1
5322 while True:
5323 print("{} {}".format(ctx.obj.ns.export_metric(metric_data), i))
5324 time.sleep(int(interval))
5325 i += 1
5326 # except ClientException as e:
5327 # print(str(e))
5328 # exit(1)
5329
5330
5331 #################
5332 # Subscription operations
5333 #################
5334
5335
5336 @cli_osm.command(
5337 name="subscription-create",
5338 short_help="creates a new subscription to a specific event",
5339 )
5340 @click.option(
5341 "--event_type",
5342 # type=click.Choice(['ns', 'nspkg', 'vnfpkg'], case_sensitive=False))
5343 type=click.Choice(["ns"], case_sensitive=False),
5344 help="event type to be subscribed (for the moment, only ns is supported)",
5345 )
5346 @click.option("--event", default=None, help="specific yaml configuration for the event")
5347 @click.option(
5348 "--event_file", default=None, help="specific yaml configuration file for the event"
5349 )
5350 @click.pass_context
5351 def subscription_create(ctx, event_type, event, event_file):
5352 """creates a new subscription to a specific event"""
5353 logger.debug("")
5354 check_client_version(ctx.obj, ctx.command.name)
5355 if event_file:
5356 if event:
5357 raise ClientException(
5358 '"--event" option is incompatible with "--event_file" option'
5359 )
5360 with open(event_file, "r") as cf:
5361 event = cf.read()
5362 ctx.obj.subscription.create(event_type, event)
5363
5364
5365 @cli_osm.command(name="subscription-delete", short_help="deletes a subscription")
5366 @click.option(
5367 "--event_type",
5368 # type=click.Choice(['ns', 'nspkg', 'vnfpkg'], case_sensitive=False))
5369 type=click.Choice(["ns"], case_sensitive=False),
5370 help="event type to be subscribed (for the moment, only ns is supported)",
5371 )
5372 @click.argument("subscription_id")
5373 @click.option(
5374 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
5375 )
5376 @click.pass_context
5377 def subscription_delete(ctx, event_type, subscription_id, force):
5378 """deletes a subscription
5379
5380 SUBSCRIPTION_ID: ID of the subscription to be deleted
5381 """
5382 logger.debug("")
5383 check_client_version(ctx.obj, ctx.command.name)
5384 ctx.obj.subscription.delete(event_type, subscription_id, force)
5385
5386
5387 @cli_osm.command(name="subscription-list", short_help="list all subscriptions")
5388 @click.option(
5389 "--event_type",
5390 # type=click.Choice(['ns', 'nspkg', 'vnfpkg'], case_sensitive=False))
5391 type=click.Choice(["ns"], case_sensitive=False),
5392 help="event type to be subscribed (for the moment, only ns is supported)",
5393 )
5394 @click.option(
5395 "--filter",
5396 default=None,
5397 multiple=True,
5398 help="restricts the list to the subscriptions matching the filter",
5399 )
5400 @click.pass_context
5401 def subscription_list(ctx, event_type, filter):
5402 """list all subscriptions"""
5403 logger.debug("")
5404 check_client_version(ctx.obj, ctx.command.name)
5405 if filter:
5406 filter = "&".join(filter)
5407 resp = ctx.obj.subscription.list(event_type, filter)
5408 table = PrettyTable(["id", "filter", "CallbackUri"])
5409 for sub in resp:
5410 table.add_row(
5411 [
5412 sub["_id"],
5413 wrap_text(text=json.dumps(sub["filter"], indent=2), width=70),
5414 sub["CallbackUri"],
5415 ]
5416 )
5417 table.align = "l"
5418 print(table)
5419
5420
5421 @cli_osm.command(
5422 name="subscription-show", short_help="shows the details of a subscription"
5423 )
5424 @click.argument("subscription_id")
5425 @click.option(
5426 "--event_type",
5427 # type=click.Choice(['ns', 'nspkg', 'vnfpkg'], case_sensitive=False))
5428 type=click.Choice(["ns"], case_sensitive=False),
5429 help="event type to be subscribed (for the moment, only ns is supported)",
5430 )
5431 @click.option(
5432 "--filter",
5433 multiple=True,
5434 help="restricts the information to the fields in the filter",
5435 )
5436 @click.pass_context
5437 def subscription_show(ctx, event_type, subscription_id, filter):
5438 """shows the details of a subscription
5439
5440 SUBSCRIPTION_ID: ID of the subscription
5441 """
5442 logger.debug("")
5443 # try:
5444 resp = ctx.obj.subscription.get(subscription_id)
5445 table = PrettyTable(["key", "attribute"])
5446 for k, v in list(resp.items()):
5447 if not filter or k in filter:
5448 table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)])
5449 table.align = "l"
5450 print(table)
5451
5452
5453 ####################
5454 # Other operations
5455 ####################
5456
5457
5458 @cli_osm.command(name="version", short_help="shows client and server versions")
5459 @click.pass_context
5460 def get_version(ctx):
5461 """shows client and server versions"""
5462 # try:
5463 check_client_version(ctx.obj, "version")
5464 print("Server version: {}".format(ctx.obj.get_version()))
5465 print(
5466 "Client version: {}".format(pkg_resources.get_distribution("osmclient").version)
5467 )
5468 # except ClientException as e:
5469 # print(str(e))
5470 # exit(1)
5471
5472
5473 @cli_osm.command(
5474 name="upload-package", short_help="uploads a VNF package or NS package"
5475 )
5476 @click.argument("filename")
5477 @click.option(
5478 "--skip-charm-build",
5479 default=False,
5480 is_flag=True,
5481 help="the charm will not be compiled, it is assumed to already exist",
5482 )
5483 @click.pass_context
5484 def upload_package(ctx, filename, skip_charm_build):
5485 """uploads a vnf package or ns package
5486
5487 filename: vnf or ns package folder, or vnf or ns package file (tar.gz)
5488 """
5489 logger.debug("")
5490 # try:
5491 ctx.obj.package.upload(filename, skip_charm_build=skip_charm_build)
5492 fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
5493 if fullclassname != "osmclient.sol005.client.Client":
5494 ctx.obj.package.wait_for_upload(filename)
5495 # except ClientException as e:
5496 # print(str(e))
5497 # exit(1)
5498
5499
5500 # @cli_osm.command(name='ns-scaling-show')
5501 # @click.argument('ns_name')
5502 # @click.pass_context
5503 # def show_ns_scaling(ctx, ns_name):
5504 # """shows the status of a NS scaling operation
5505 #
5506 # NS_NAME: name of the NS instance being scaled
5507 # """
5508 # try:
5509 # check_client_version(ctx.obj, ctx.command.name, 'v1')
5510 # resp = ctx.obj.ns.list()
5511 # except ClientException as e:
5512 # print(str(e))
5513 # exit(1)
5514 #
5515 # table = PrettyTable(
5516 # ['group-name',
5517 # 'instance-id',
5518 # 'operational status',
5519 # 'create-time',
5520 # 'vnfr ids'])
5521 #
5522 # for ns in resp:
5523 # if ns_name == ns['name']:
5524 # nsopdata = ctx.obj.ns.get_opdata(ns['id'])
5525 # scaling_records = nsopdata['nsr:nsr']['scaling-group-record']
5526 # for record in scaling_records:
5527 # if 'instance' in record:
5528 # instances = record['instance']
5529 # for inst in instances:
5530 # table.add_row(
5531 # [record['scaling-group-name-ref'],
5532 # inst['instance-id'],
5533 # inst['op-status'],
5534 # time.strftime('%Y-%m-%d %H:%M:%S',
5535 # time.localtime(
5536 # inst['create-time'])),
5537 # inst['vnfrs']])
5538 # table.align = 'l'
5539 # print(table)
5540
5541
5542 # @cli_osm.command(name='ns-scale')
5543 # @click.argument('ns_name')
5544 # @click.option('--ns_scale_group', prompt=True)
5545 # @click.option('--index', prompt=True)
5546 # @click.option('--wait',
5547 # required=False,
5548 # default=False,
5549 # is_flag=True,
5550 # help='do not return the control immediately, but keep it \
5551 # until the operation is completed, or timeout')
5552 # @click.pass_context
5553 # def ns_scale(ctx, ns_name, ns_scale_group, index, wait):
5554 # """scales NS
5555 #
5556 # NS_NAME: name of the NS instance to be scaled
5557 # """
5558 # try:
5559 # check_client_version(ctx.obj, ctx.command.name, 'v1')
5560 # ctx.obj.ns.scale(ns_name, ns_scale_group, index, wait=wait)
5561 # except ClientException as e:
5562 # print(str(e))
5563 # exit(1)
5564
5565
5566 # @cli_osm.command(name='config-agent-list')
5567 # @click.pass_context
5568 # def config_agent_list(ctx):
5569 # """list config agents"""
5570 # try:
5571 # check_client_version(ctx.obj, ctx.command.name, 'v1')
5572 # except ClientException as e:
5573 # print(str(e))
5574 # exit(1)
5575 # table = PrettyTable(['name', 'account-type', 'details'])
5576 # for account in ctx.obj.vca.list():
5577 # table.add_row(
5578 # [account['name'],
5579 # account['account-type'],
5580 # account['juju']])
5581 # table.align = 'l'
5582 # print(table)
5583
5584
5585 # @cli_osm.command(name='config-agent-delete')
5586 # @click.argument('name')
5587 # @click.pass_context
5588 # def config_agent_delete(ctx, name):
5589 # """deletes a config agent
5590 #
5591 # NAME: name of the config agent to be deleted
5592 # """
5593 # try:
5594 # check_client_version(ctx.obj, ctx.command.name, 'v1')
5595 # ctx.obj.vca.delete(name)
5596 # except ClientException as e:
5597 # print(str(e))
5598 # exit(1)
5599
5600
5601 # @cli_osm.command(name='config-agent-add')
5602 # @click.option('--name',
5603 # prompt=True)
5604 # @click.option('--account_type',
5605 # prompt=True)
5606 # @click.option('--server',
5607 # prompt=True)
5608 # @click.option('--user',
5609 # prompt=True)
5610 # @click.option('--secret',
5611 # prompt=True,
5612 # hide_input=True,
5613 # confirmation_prompt=True)
5614 # @click.pass_context
5615 # def config_agent_add(ctx, name, account_type, server, user, secret):
5616 # """adds a config agent"""
5617 # try:
5618 # check_client_version(ctx.obj, ctx.command.name, 'v1')
5619 # ctx.obj.vca.create(name, account_type, server, user, secret)
5620 # except ClientException as e:
5621 # print(str(e))
5622 # exit(1)
5623
5624
5625 # @cli_osm.command(name='ro-dump')
5626 # @click.pass_context
5627 # def ro_dump(ctx):
5628 # """shows RO agent information"""
5629 # check_client_version(ctx.obj, ctx.command.name, 'v1')
5630 # resp = ctx.obj.vim.get_resource_orchestrator()
5631 # table = PrettyTable(['key', 'attribute'])
5632 # for k, v in list(resp.items()):
5633 # table.add_row([k, json.dumps(v, indent=2)])
5634 # table.align = 'l'
5635 # print(table)
5636
5637
5638 # @cli_osm.command(name='vcs-list')
5639 # @click.pass_context
5640 # def vcs_list(ctx):
5641 # check_client_version(ctx.obj, ctx.command.name, 'v1')
5642 # resp = ctx.obj.utils.get_vcs_info()
5643 # table = PrettyTable(['component name', 'state'])
5644 # for component in resp:
5645 # table.add_row([component['component_name'], component['state']])
5646 # table.align = 'l'
5647 # print(table)
5648
5649
5650 @cli_osm.command(
5651 name="ns-action", short_help="executes an action/primitive over a NS instance"
5652 )
5653 @click.argument("ns_name")
5654 @click.option(
5655 "--vnf_name",
5656 default=None,
5657 help="member-vnf-index if the target is a vnf instead of a ns)",
5658 )
5659 @click.option("--kdu_name", default=None, help="kdu-name if the target is a kdu)")
5660 @click.option("--vdu_id", default=None, help="vdu-id if the target is a vdu")
5661 @click.option(
5662 "--vdu_count", default=None, type=int, help="number of vdu instance of this vdu_id"
5663 )
5664 @click.option("--action_name", prompt=True, help="action name")
5665 @click.option("--params", default=None, help="action params in YAML/JSON inline string")
5666 @click.option("--params_file", default=None, help="YAML/JSON file with action params")
5667 @click.option(
5668 "--timeout", required=False, default=None, type=int, help="timeout in seconds"
5669 )
5670 @click.option(
5671 "--wait",
5672 required=False,
5673 default=False,
5674 is_flag=True,
5675 help="do not return the control immediately, but keep it until the operation is completed, or timeout",
5676 )
5677 @click.pass_context
5678 def ns_action(
5679 ctx,
5680 ns_name,
5681 vnf_name,
5682 kdu_name,
5683 vdu_id,
5684 vdu_count,
5685 action_name,
5686 params,
5687 params_file,
5688 timeout,
5689 wait,
5690 ):
5691 """executes an action/primitive over a NS instance
5692
5693 NS_NAME: name or ID of the NS instance
5694 """
5695 logger.debug("")
5696 # try:
5697 check_client_version(ctx.obj, ctx.command.name)
5698 op_data = {}
5699 if vnf_name:
5700 op_data["member_vnf_index"] = vnf_name
5701 if kdu_name:
5702 op_data["kdu_name"] = kdu_name
5703 if vdu_id:
5704 op_data["vdu_id"] = vdu_id
5705 if vdu_count is not None:
5706 op_data["vdu_count_index"] = vdu_count
5707 if timeout:
5708 op_data["timeout_ns_action"] = timeout
5709 op_data["primitive"] = action_name
5710 if params_file:
5711 with open(params_file, "r") as pf:
5712 params = pf.read()
5713 if params:
5714 op_data["primitive_params"] = yaml.safe_load(params)
5715 else:
5716 op_data["primitive_params"] = {}
5717 print(ctx.obj.ns.exec_op(ns_name, op_name="action", op_data=op_data, wait=wait))
5718
5719 # except ClientException as e:
5720 # print(str(e))
5721 # exit(1)
5722
5723
5724 @cli_osm.command(
5725 name="vnf-scale", short_help="executes a VNF scale (adding/removing VDUs)"
5726 )
5727 @click.argument("ns_name")
5728 @click.argument("vnf_name")
5729 @click.option(
5730 "--scaling-group", prompt=True, help="scaling-group-descriptor name to use"
5731 )
5732 @click.option(
5733 "--scale-in", default=False, is_flag=True, help="performs a scale in operation"
5734 )
5735 @click.option(
5736 "--scale-out",
5737 default=False,
5738 is_flag=True,
5739 help="performs a scale out operation (by default)",
5740 )
5741 @click.option(
5742 "--timeout", required=False, default=None, type=int, help="timeout in seconds"
5743 )
5744 @click.option(
5745 "--wait",
5746 required=False,
5747 default=False,
5748 is_flag=True,
5749 help="do not return the control immediately, but keep it until the operation is completed, or timeout",
5750 )
5751 @click.pass_context
5752 def vnf_scale(
5753 ctx, ns_name, vnf_name, scaling_group, scale_in, scale_out, timeout, wait
5754 ):
5755 """
5756 Executes a VNF scale (adding/removing VDUs)
5757
5758 \b
5759 NS_NAME: name or ID of the NS instance.
5760 VNF_NAME: member-vnf-index in the NS to be scaled.
5761 """
5762 logger.debug("")
5763 # try:
5764 check_client_version(ctx.obj, ctx.command.name)
5765 if not scale_in and not scale_out:
5766 scale_out = True
5767 ctx.obj.ns.scale_vnf(
5768 ns_name, vnf_name, scaling_group, scale_in, scale_out, wait, timeout
5769 )
5770 # except ClientException as e:
5771 # print(str(e))
5772 # exit(1)
5773
5774
5775 @cli_osm.command(
5776 name="ns-update", short_help="executes an update of a Network Service."
5777 )
5778 @click.argument("ns_name")
5779 @click.option(
5780 "--updatetype", required=True, type=str, help="available types: CHANGE_VNFPKG"
5781 )
5782 @click.option(
5783 "--config",
5784 required=True,
5785 type=str,
5786 help="extra information for update operation as YAML/JSON inline string as --config"
5787 " '{changeVnfPackageData:[{vnfInstanceId: xxx, vnfdId: yyy}]}'",
5788 )
5789 @click.option(
5790 "--timeout", required=False, default=None, type=int, help="timeout in seconds"
5791 )
5792 @click.option(
5793 "--wait",
5794 required=False,
5795 default=False,
5796 is_flag=True,
5797 help="do not return the control immediately, but keep it until the operation is completed, or timeout",
5798 )
5799 @click.pass_context
5800 def update(ctx, ns_name, updatetype, config, timeout, wait):
5801 """Executes an update of a Network Service.
5802
5803 The update will check new revisions of the Network Functions that are part of the
5804 Network Service, and it will update them if needed.
5805 Sample update command: osm ns-update ns_instance_id --updatetype CHANGE_VNFPKG
5806 --config '{changeVnfPackageData: [{vnfInstanceId: id_x,vnfdId: id_y}]}' --timeout 300 --wait
5807
5808 NS_NAME: Network service instance name or ID.
5809
5810 """
5811 op_data = {"timeout": timeout, "updateType": updatetype}
5812 if config:
5813 op_data["config"] = yaml.safe_load(config)
5814
5815 check_client_version(ctx.obj, ctx.command.name)
5816 ctx.obj.ns.update(ns_name, op_data, wait=wait)
5817
5818
5819 def iterator_split(iterator, separators):
5820 """
5821 Splits a tuple or list into several lists whenever a separator is found
5822 For instance, the following tuple will be separated with the separator "--vnf" as follows.
5823 From:
5824 ("--vnf", "A", "--cause", "cause_A", "--vdu", "vdu_A1", "--vnf", "B", "--cause", "cause_B", ...
5825 "--vdu", "vdu_B1", "--count_index", "1", "--run-day1", "--vdu", "vdu_B1", "--count_index", "2")
5826 To:
5827 [
5828 ("--vnf", "A", "--cause", "cause_A", "--vdu", "vdu_A1"),
5829 ("--vnf", "B", "--cause", "cause_B", "--vdu", "vdu_B1", "--count_index", "1", "--run-day1", ...
5830 "--vdu", "vdu_B1", "--count_index", "2")
5831 ]
5832
5833 Returns as many lists as separators are found
5834 """
5835 logger.debug("")
5836 if iterator[0] not in separators:
5837 raise ClientException(f"Expected one of {separators}. Received: {iterator[0]}.")
5838 list_of_lists = []
5839 first = 0
5840 for i in range(len(iterator)):
5841 if iterator[i] in separators:
5842 if i == first:
5843 continue
5844 if i - first < 2:
5845 raise ClientException(
5846 f"Expected at least one argument after separator (possible separators: {separators})."
5847 )
5848 list_of_lists.append(list(iterator[first:i]))
5849 first = i
5850 if (len(iterator) - first) < 2:
5851 raise ClientException(
5852 f"Expected at least one argument after separator (possible separators: {separators})."
5853 )
5854 else:
5855 list_of_lists.append(list(iterator[first : len(iterator)]))
5856 # logger.debug(f"List of lists: {list_of_lists}")
5857 return list_of_lists
5858
5859
5860 def process_common_heal_params(heal_vnf_dict, args):
5861 logger.debug("")
5862 current_item = "vnf"
5863 i = 0
5864 while i < len(args):
5865 if args[i] == "--cause":
5866 if (i + 1 >= len(args)) or args[i + 1].startswith("--"):
5867 raise ClientException("No cause was provided after --cause")
5868 heal_vnf_dict["cause"] = args[i + 1]
5869 i = i + 2
5870 continue
5871 if args[i] == "--run-day1":
5872 if current_item == "vnf":
5873 if "additionalParams" not in heal_vnf_dict:
5874 heal_vnf_dict["additionalParams"] = {}
5875 heal_vnf_dict["additionalParams"]["run-day1"] = True
5876 else:
5877 # if current_item == "vdu"
5878 heal_vnf_dict["additionalParams"]["vdu"][-1]["run-day1"] = True
5879 i = i + 1
5880 continue
5881 if args[i] == "--vdu":
5882 if "additionalParams" not in heal_vnf_dict:
5883 heal_vnf_dict["additionalParams"] = {}
5884 heal_vnf_dict["additionalParams"]["vdu"] = []
5885 if (i + 1 >= len(args)) or args[i + 1].startswith("--"):
5886 raise ClientException("No VDU ID was provided after --vdu")
5887 heal_vnf_dict["additionalParams"]["vdu"].append({"vdu-id": args[i + 1]})
5888 current_item = "vdu"
5889 i = i + 2
5890 continue
5891 if args[i] == "--count-index":
5892 if current_item == "vnf":
5893 raise ClientException(
5894 "Option --count-index only applies to VDU, not to VNF"
5895 )
5896 if (i + 1 >= len(args)) or args[i + 1].startswith("--"):
5897 raise ClientException("No count index was provided after --count-index")
5898 heal_vnf_dict["additionalParams"]["vdu"][-1]["count-index"] = int(
5899 args[i + 1]
5900 )
5901 i = i + 2
5902 continue
5903 i = i + 1
5904 return
5905
5906
5907 def process_ns_heal_params(ctx, param, value):
5908 """
5909 Processes the params in the command ns-heal
5910 Click does not allow advanced patterns for positional options like this:
5911 --vnf volumes_vnf --cause "Heal several_volumes-VM of several_volumes_vnf"
5912 --vdu several_volumes-VM
5913 --vnf charm_vnf --cause "Heal two VMs of native_manual_scale_charm_vnf"
5914 --vdu mgmtVM --count-index 1 --run-day1
5915 --vdu mgmtVM --count-index 2
5916
5917 It returns the dictionary with all the params stored in ctx.params["heal_params"]
5918 """
5919 logger.debug("")
5920 # logger.debug(f"Args: {value}")
5921 if param.name != "args":
5922 raise ClientException(f"Unexpected param: {param.name}")
5923 # Split the tuple "value" by "--vnf"
5924 vnfs = iterator_split(value, ["--vnf"])
5925 logger.debug(f"VNFs: {vnfs}")
5926 heal_dict = {}
5927 heal_dict["healVnfData"] = []
5928 for vnf in vnfs:
5929 # logger.debug(f"VNF: {vnf}")
5930 heal_vnf = {}
5931 if vnf[1].startswith("--"):
5932 raise ClientException("Expected a VNF_ID after --vnf")
5933 heal_vnf["vnfInstanceId"] = vnf[1]
5934 process_common_heal_params(heal_vnf, vnf[2:])
5935 heal_dict["healVnfData"].append(heal_vnf)
5936 ctx.params["heal_params"] = heal_dict
5937 return
5938
5939
5940 @cli_osm.command(
5941 name="ns-heal",
5942 short_help="heals (recreates) VNFs or VDUs of a NS instance",
5943 context_settings=dict(ignore_unknown_options=True),
5944 )
5945 @click.argument("ns_name")
5946 @click.argument(
5947 "args", nargs=-1, type=click.UNPROCESSED, callback=process_ns_heal_params
5948 )
5949 @click.option("--timeout", type=int, default=None, help="timeout in seconds")
5950 @click.option(
5951 "--wait",
5952 default=False,
5953 is_flag=True,
5954 help="do not return the control immediately, but keep it until the operation is completed, or timeout",
5955 )
5956 @click.pass_context
5957 def ns_heal(ctx, ns_name, args, heal_params, timeout, wait):
5958 """heals (recreates) VNFs or VDUs of a NS instance
5959
5960 NS_NAME: name or ID of the NS instance
5961
5962 \b
5963 Options:
5964 --vnf TEXT VNF instance ID or VNF id in the NS [required]
5965 --cause TEXT human readable cause of the healing
5966 --run-day1 indicates whether or not to run day1 primitives for the VNF/VDU
5967 --vdu TEXT vdu-id
5968 --count-index INTEGER count-index
5969
5970 \b
5971 Example:
5972 osm ns-heal NS_NAME|NS_ID --vnf volumes_vnf --cause "Heal several_volumes-VM of several_volumes_vnf"
5973 --vdu several_volumes-VM
5974 --vnf charm_vnf --cause "Heal two VMs of native_manual_scale_charm_vnf"
5975 --vdu mgmtVM --count-index 1 --run-day1
5976 --vdu mgmtVM --count-index 2
5977 """
5978 logger.debug("")
5979 heal_dict = ctx.params["heal_params"]
5980 logger.debug(f"Heal dict:\n{yaml.safe_dump(heal_dict)}")
5981 # replace VNF id in the NS by the VNF instance ID
5982 for vnf in heal_dict["healVnfData"]:
5983 vnf_id = vnf["vnfInstanceId"]
5984 if not validate_uuid4(vnf_id):
5985 vnf_filter = f"member-vnf-index-ref={vnf_id}"
5986 vnf_list = ctx.obj.vnf.list(ns=ns_name, filter=vnf_filter)
5987 if len(vnf_list) == 0:
5988 raise ClientException(
5989 f"No VNF found in NS {ns_name} with filter {vnf_filter}"
5990 )
5991 elif len(vnf_list) == 1:
5992 vnf["vnfInstanceId"] = vnf_list[0]["_id"]
5993 else:
5994 raise ClientException(
5995 f"More than 1 VNF found in NS {ns_name} with filter {vnf_filter}"
5996 )
5997 logger.debug(f"Heal dict:\n{yaml.safe_dump(heal_dict)}")
5998 check_client_version(ctx.obj, ctx.command.name)
5999 ctx.obj.ns.heal(ns_name, heal_dict, wait, timeout)
6000 exit(0)
6001
6002
6003 def process_vnf_heal_params(ctx, param, value):
6004 """
6005 Processes the params in the command vnf-heal
6006 Click does not allow advanced patterns for positional options like this:
6007 --vdu mgmtVM --count-index 1 --run-day1 --vdu mgmtVM --count-index 2
6008
6009 It returns the dictionary with all the params stored in ctx.params["heal_params"]
6010 """
6011 logger.debug("")
6012 # logger.debug(f"Args: {value}")
6013 if param.name != "args":
6014 raise ClientException(f"Unexpected param: {param.name}")
6015 # Split the tuple "value" by "--vnf"
6016 vnf = value
6017 heal_dict = {}
6018 heal_dict["healVnfData"] = []
6019 logger.debug(f"VNF: {vnf}")
6020 heal_vnf = {"vnfInstanceId": "id_to_be_substituted"}
6021 process_common_heal_params(heal_vnf, vnf)
6022 heal_dict["healVnfData"].append(heal_vnf)
6023 ctx.params["heal_params"] = heal_dict
6024 return
6025
6026
6027 @cli_osm.command(
6028 name="vnf-heal",
6029 short_help="heals (recreates) a VNF instance or the VDUs of a VNF instance",
6030 context_settings=dict(ignore_unknown_options=True),
6031 )
6032 @click.argument("vnf_name")
6033 @click.argument(
6034 "args", nargs=-1, type=click.UNPROCESSED, callback=process_vnf_heal_params
6035 )
6036 @click.option("--timeout", type=int, default=None, help="timeout in seconds")
6037 @click.option(
6038 "--wait",
6039 default=False,
6040 is_flag=True,
6041 help="do not return the control immediately, but keep it until the operation is completed, or timeout",
6042 )
6043 @click.pass_context
6044 def vnf_heal2(ctx, vnf_name, args, heal_params, timeout, wait):
6045 """heals (recreates) a VNF instance or the VDUs of a VNF instance
6046
6047 VNF_NAME: name or ID of the VNF instance
6048
6049 \b
6050 Options:
6051 --cause TEXT human readable cause of the healing of the VNF
6052 --run-day1 indicates whether or not to run day1 primitives for the VNF/VDU
6053 --vdu TEXT vdu-id
6054 --count-index INTEGER count-index
6055
6056 \b
6057 Example:
6058 osm vnf-heal VNF_INSTANCE_ID --vdu mgmtVM --count-index 1 --run-day1
6059 --vdu mgmtVM --count-index 2
6060 """
6061 logger.debug("")
6062 heal_dict = ctx.params["heal_params"]
6063 heal_dict["healVnfData"][-1]["vnfInstanceId"] = vnf_name
6064 logger.debug(f"Heal dict:\n{yaml.safe_dump(heal_dict)}")
6065 check_client_version(ctx.obj, ctx.command.name)
6066 vnfr = ctx.obj.vnf.get(vnf_name)
6067 ns_id = vnfr["nsr-id-ref"]
6068 ctx.obj.ns.heal(ns_id, heal_dict, wait, timeout)
6069 exit(0)
6070
6071
6072 @cli_osm.command(name="alarm-show", short_help="show alarm details")
6073 @click.argument("uuid")
6074 @click.pass_context
6075 def alarm_show(ctx, uuid):
6076 """Show alarm's detail information"""
6077
6078 check_client_version(ctx.obj, ctx.command.name)
6079 resp = ctx.obj.ns.get_alarm(uuid=uuid)
6080 alarm_filter = [
6081 "uuid",
6082 "name",
6083 "metric",
6084 "statistic",
6085 "threshold",
6086 "operation",
6087 "ns-id",
6088 "vnf-id",
6089 "vdu_name",
6090 "action",
6091 "status",
6092 ]
6093 table = PrettyTable(["key", "attribute"])
6094 try:
6095 # Arrange and return the response data
6096 alarm = resp.replace("ObjectId", "")
6097 for key in alarm_filter:
6098 if key == "uuid":
6099 value = alarm.get(key)
6100 key = "alarm-id"
6101 elif key == "name":
6102 value = alarm.get(key)
6103 key = "alarm-name"
6104 elif key == "ns-id":
6105 value = alarm["tags"].get("ns_id")
6106 elif key == "vdu_name":
6107 value = alarm["tags"].get("vdu_name")
6108 elif key == "status":
6109 value = alarm["alarm_status"]
6110 else:
6111 value = alarm[key]
6112 table.add_row([key, wrap_text(text=json.dumps(value, indent=2), width=100)])
6113 table.align = "l"
6114 print(table)
6115 except Exception:
6116 print(resp)
6117
6118
6119 # List alarm
6120 @cli_osm.command(name="alarm-list", short_help="list all alarms")
6121 @click.option(
6122 "--ns_id", default=None, required=False, help="List out alarm for given ns id"
6123 )
6124 @click.pass_context
6125 def alarm_list(ctx, ns_id):
6126 """list all alarm"""
6127
6128 check_client_version(ctx.obj, ctx.command.name)
6129 project_name = os.getenv("OSM_PROJECT", "admin")
6130 resp = ctx.obj.ns.get_alarm(project_name=project_name, ns_id=ns_id)
6131
6132 table = PrettyTable(
6133 ["alarm-id", "metric", "threshold", "operation", "action", "status"]
6134 )
6135 if resp:
6136 # return the response data in a table
6137 resp = resp.replace("ObjectId", "")
6138 for alarm in resp:
6139 table.add_row(
6140 [
6141 wrap_text(text=str(alarm["uuid"]), width=38),
6142 alarm["metric"],
6143 alarm["threshold"],
6144 alarm["operation"],
6145 wrap_text(text=alarm["action"], width=25),
6146 alarm["alarm_status"],
6147 ]
6148 )
6149 table.align = "l"
6150 print(table)
6151
6152
6153 # Update alarm
6154 @cli_osm.command(name="alarm-update", short_help="Update a alarm")
6155 @click.argument("uuid")
6156 @click.option("--threshold", default=None, help="Alarm threshold")
6157 @click.option("--is_enable", default=None, type=bool, help="enable or disable alarm")
6158 @click.pass_context
6159 def alarm_update(ctx, uuid, threshold, is_enable):
6160 """
6161 Update alarm
6162
6163 """
6164 if not threshold and is_enable is None:
6165 raise ClientException(
6166 "Please provide option to update i.e threshold or is_enable"
6167 )
6168 ctx.obj.ns.update_alarm(uuid, threshold, is_enable)
6169
6170
6171 ##############################
6172 # Role Management Operations #
6173 ##############################
6174
6175
6176 @cli_osm.command(name="role-create", short_help="creates a new role")
6177 @click.argument("name")
6178 @click.option("--permissions", default=None, help="role permissions using a dictionary")
6179 @click.pass_context
6180 def role_create(ctx, name, permissions):
6181 """
6182 Creates a new role.
6183
6184 \b
6185 NAME: Name or ID of the role.
6186 DEFINITION: Definition of grant/denial of access to resources.
6187 """
6188 logger.debug("")
6189 # try:
6190 check_client_version(ctx.obj, ctx.command.name)
6191 ctx.obj.role.create(name, permissions)
6192 # except ClientException as e:
6193 # print(str(e))
6194 # exit(1)
6195
6196
6197 @cli_osm.command(name="role-update", short_help="updates a role")
6198 @click.argument("name")
6199 @click.option("--set-name", default=None, help="change name of rle")
6200 # @click.option('--permissions',
6201 # default=None,
6202 # help='provide a yaml format dictionary with incremental changes. Values can be bool or None to delete')
6203 @click.option(
6204 "--add",
6205 default=None,
6206 help="yaml format dictionary with permission: True/False to access grant/denial",
6207 )
6208 @click.option("--remove", default=None, help="yaml format list to remove a permission")
6209 @click.pass_context
6210 def role_update(ctx, name, set_name, add, remove):
6211 """
6212 Updates a role.
6213
6214 \b
6215 NAME: Name or ID of the role.
6216 DEFINITION: Definition overwrites the old definition.
6217 ADD: Grant/denial of access to resource to add.
6218 REMOVE: Grant/denial of access to resource to remove.
6219 """
6220 logger.debug("")
6221 # try:
6222 check_client_version(ctx.obj, ctx.command.name)
6223 ctx.obj.role.update(name, set_name, None, add, remove)
6224 # except ClientException as e:
6225 # print(str(e))
6226 # exit(1)
6227
6228
6229 @cli_osm.command(name="role-delete", short_help="deletes a role")
6230 @click.argument("name")
6231 # @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
6232 @click.pass_context
6233 def role_delete(ctx, name):
6234 """
6235 Deletes a role.
6236
6237 \b
6238 NAME: Name or ID of the role.
6239 """
6240 logger.debug("")
6241 # try:
6242 check_client_version(ctx.obj, ctx.command.name)
6243 ctx.obj.role.delete(name)
6244 # except ClientException as e:
6245 # print(str(e))
6246 # exit(1)
6247
6248
6249 @cli_osm.command(name="role-list", short_help="list all roles")
6250 @click.option(
6251 "--filter",
6252 default=None,
6253 multiple=True,
6254 help="restricts the list to the projects matching the filter",
6255 )
6256 @click.pass_context
6257 def role_list(ctx, filter):
6258 """
6259 List all roles.
6260 """
6261 logger.debug("")
6262 # try:
6263 check_client_version(ctx.obj, ctx.command.name)
6264 if filter:
6265 filter = "&".join(filter)
6266 resp = ctx.obj.role.list(filter)
6267 # except ClientException as e:
6268 # print(str(e))
6269 # exit(1)
6270 table = PrettyTable(["name", "id"])
6271 for role in resp:
6272 table.add_row([role["name"], role["_id"]])
6273 table.align = "l"
6274 print(table)
6275
6276
6277 @cli_osm.command(name="role-show", short_help="show specific role")
6278 @click.argument("name")
6279 @click.pass_context
6280 def role_show(ctx, name):
6281 """
6282 Shows the details of a role.
6283
6284 \b
6285 NAME: Name or ID of the role.
6286 """
6287 logger.debug("")
6288 # try:
6289 check_client_version(ctx.obj, ctx.command.name)
6290 resp = ctx.obj.role.get(name)
6291 # except ClientException as e:
6292 # print(str(e))
6293 # exit(1)
6294
6295 table = PrettyTable(["key", "attribute"])
6296 for k, v in resp.items():
6297 table.add_row([k, json.dumps(v, indent=2)])
6298 table.align = "l"
6299 print(table)
6300
6301
6302 @cli_osm.command(name="package-create", short_help="Create empty NS package structure")
6303 @click.argument("package-type")
6304 @click.argument("package-name")
6305 @click.option(
6306 "--base-directory",
6307 default=".",
6308 help=('(NS/VNF/NST) Set the location for package creation. Default: "."'),
6309 )
6310 @click.option(
6311 "--image",
6312 default="image-name",
6313 help='(VNF) Set the name of the vdu image. Default "image-name"',
6314 )
6315 @click.option(
6316 "--vdus", default=1, help="(VNF) Set the number of vdus in a VNF. Default 1"
6317 )
6318 @click.option(
6319 "--vcpu", default=1, help="(VNF) Set the number of virtual CPUs in a vdu. Default 1"
6320 )
6321 @click.option(
6322 "--memory",
6323 default=1024,
6324 help="(VNF) Set the memory size (MB) of the vdu. Default 1024",
6325 )
6326 @click.option(
6327 "--storage", default=10, help="(VNF) Set the disk size (GB) of the vdu. Default 10"
6328 )
6329 @click.option(
6330 "--interfaces",
6331 default=0,
6332 help="(VNF) Set the number of additional interfaces apart from the management interface. Default 0",
6333 )
6334 @click.option(
6335 "--vendor", default="OSM", help='(NS/VNF) Set the descriptor vendor. Default "OSM"'
6336 )
6337 @click.option(
6338 "--override",
6339 default=False,
6340 is_flag=True,
6341 help="(NS/VNF/NST) Flag for overriding the package if exists.",
6342 )
6343 @click.option(
6344 "--detailed",
6345 is_flag=True,
6346 default=False,
6347 help="(NS/VNF/NST) Flag for generating descriptor .yaml with all possible commented options",
6348 )
6349 @click.option(
6350 "--netslice-subnets", default=1, help="(NST) Number of netslice subnets. Default 1"
6351 )
6352 @click.option(
6353 "--netslice-vlds", default=1, help="(NST) Number of netslice vlds. Default 1"
6354 )
6355 @click.option(
6356 "--old",
6357 default=False,
6358 is_flag=True,
6359 help="Flag to create a descriptor using the previous OSM format (pre SOL006, OSM<9)",
6360 )
6361 @click.pass_context
6362 def package_create(
6363 ctx,
6364 package_type,
6365 base_directory,
6366 package_name,
6367 override,
6368 image,
6369 vdus,
6370 vcpu,
6371 memory,
6372 storage,
6373 interfaces,
6374 vendor,
6375 detailed,
6376 netslice_subnets,
6377 netslice_vlds,
6378 old,
6379 ):
6380 """
6381 Creates an OSM NS, VNF, NST package
6382
6383 \b
6384 PACKAGE_TYPE: Package to be created: NS, VNF or NST.
6385 PACKAGE_NAME: Name of the package to create the folder with the content.
6386 """
6387
6388 # try:
6389 logger.debug("")
6390 check_client_version(ctx.obj, ctx.command.name)
6391 print(
6392 "Creating the {} structure: {}/{}".format(
6393 package_type.upper(), base_directory, package_name
6394 )
6395 )
6396 resp = ctx.obj.package_tool.create(
6397 package_type,
6398 base_directory,
6399 package_name,
6400 override=override,
6401 image=image,
6402 vdus=vdus,
6403 vcpu=vcpu,
6404 memory=memory,
6405 storage=storage,
6406 interfaces=interfaces,
6407 vendor=vendor,
6408 detailed=detailed,
6409 netslice_subnets=netslice_subnets,
6410 netslice_vlds=netslice_vlds,
6411 old=old,
6412 )
6413 print(resp)
6414 # except ClientException as inst:
6415 # print("ERROR: {}".format(inst))
6416 # exit(1)
6417
6418
6419 @cli_osm.command(
6420 name="package-validate", short_help="Validate descriptors given a base directory"
6421 )
6422 @click.argument("base-directory", default=".", required=False)
6423 @click.option(
6424 "--recursive/--no-recursive",
6425 default=True,
6426 help="The activated recursive option will validate the yaml files"
6427 " within the indicated directory and in its subdirectories",
6428 )
6429 @click.option(
6430 "--old",
6431 is_flag=True,
6432 default=False,
6433 help="Validates also the descriptors using the previous OSM format (pre SOL006)",
6434 )
6435 @click.pass_context
6436 def package_validate(ctx, base_directory, recursive, old):
6437 """
6438 Validate descriptors given a base directory.
6439
6440 \b
6441 BASE_DIRECTORY: Base folder for NS, VNF or NST package.
6442 """
6443 # try:
6444 logger.debug("")
6445 check_client_version(ctx.obj, ctx.command.name)
6446 results = ctx.obj.package_tool.validate(base_directory, recursive, old)
6447 table = PrettyTable()
6448 table.field_names = ["TYPE", "PATH", "VALID", "ERROR"]
6449 # Print the dictionary generated by the validation function
6450 for result in results:
6451 table.add_row(
6452 [result["type"], result["path"], result["valid"], result["error"]]
6453 )
6454 table.sortby = "VALID"
6455 table.align["PATH"] = "l"
6456 table.align["TYPE"] = "l"
6457 table.align["ERROR"] = "l"
6458 print(table)
6459 # except ClientException as inst:
6460 # print("ERROR: {}".format(inst))
6461 # exit(1)
6462
6463
6464 @cli_osm.command(
6465 name="package-translate", short_help="Translate descriptors given a base directory"
6466 )
6467 @click.argument("base-directory", default=".", required=False)
6468 @click.option(
6469 "--recursive/--no-recursive",
6470 default=True,
6471 help="The activated recursive option will translate the yaml files"
6472 " within the indicated directory and in its subdirectories",
6473 )
6474 @click.option(
6475 "--dryrun",
6476 is_flag=True,
6477 default=False,
6478 help="Do not translate yet, only make a dry-run to test translation",
6479 )
6480 @click.pass_context
6481 def package_translate(ctx, base_directory, recursive, dryrun):
6482 """
6483 Translate descriptors given a base directory.
6484
6485 \b
6486 BASE_DIRECTORY: Stub folder for NS, VNF or NST package.
6487 """
6488 logger.debug("")
6489 check_client_version(ctx.obj, ctx.command.name)
6490 results = ctx.obj.package_tool.translate(base_directory, recursive, dryrun)
6491 table = PrettyTable()
6492 table.field_names = [
6493 "CURRENT TYPE",
6494 "NEW TYPE",
6495 "PATH",
6496 "VALID",
6497 "TRANSLATED",
6498 "ERROR",
6499 ]
6500 # Print the dictionary generated by the validation function
6501 for result in results:
6502 table.add_row(
6503 [
6504 result["current type"],
6505 result["new type"],
6506 result["path"],
6507 result["valid"],
6508 result["translated"],
6509 result["error"],
6510 ]
6511 )
6512 table.sortby = "TRANSLATED"
6513 table.align["PATH"] = "l"
6514 table.align["TYPE"] = "l"
6515 table.align["ERROR"] = "l"
6516 print(table)
6517 # except ClientException as inst:
6518 # print("ERROR: {}".format(inst))
6519 # exit(1)
6520
6521
6522 @cli_osm.command(name="package-build", short_help="Build the tar.gz of the package")
6523 @click.argument("package-folder")
6524 @click.option(
6525 "--skip-validation", default=False, is_flag=True, help="skip package validation"
6526 )
6527 @click.option(
6528 "--skip-charm-build",
6529 default=False,
6530 is_flag=True,
6531 help="the charm will not be compiled, it is assumed to already exist",
6532 )
6533 @click.pass_context
6534 def package_build(ctx, package_folder, skip_validation, skip_charm_build):
6535 """
6536 Build the package NS, VNF given the package_folder.
6537
6538 \b
6539 PACKAGE_FOLDER: Folder of the NS, VNF or NST to be packaged
6540 """
6541 # try:
6542 logger.debug("")
6543 check_client_version(ctx.obj, ctx.command.name)
6544 results = ctx.obj.package_tool.build(
6545 package_folder,
6546 skip_validation=skip_validation,
6547 skip_charm_build=skip_charm_build,
6548 )
6549 print(results)
6550 # except ClientException as inst:
6551 # print("ERROR: {}".format(inst))
6552 # exit(1)
6553
6554
6555 @cli_osm.command(
6556 name="descriptor-translate",
6557 short_help="Translate input descriptor file from Rel EIGHT OSM descriptors to SOL006 and prints in standard output",
6558 )
6559 @click.argument("descriptor-file", required=True)
6560 @click.pass_context
6561 def descriptor_translate(ctx, descriptor_file):
6562 """
6563 Translate input descriptor.
6564
6565 \b
6566 DESCRIPTOR_FILE: Descriptor file for NS, VNF or Network Slice.
6567 """
6568 logger.debug("")
6569 check_client_version(ctx.obj, ctx.command.name)
6570 result = ctx.obj.package_tool.descriptor_translate(descriptor_file)
6571 print(result)
6572
6573
6574 def cli():
6575 try:
6576 cli_osm() # pylint: disable=no-value-for-parameter
6577 exit(0)
6578 except pycurl.error as exc:
6579 print(exc)
6580 print(
6581 'Maybe "--hostname" option or OSM_HOSTNAME environment variable needs to be specified'
6582 )
6583 except (ClientException, NotFound) as exc:
6584 print("ERROR: {}".format(exc))
6585 except (FileNotFoundError, PermissionError) as exc:
6586 print("Cannot open file: {}".format(exc))
6587 except yaml.YAMLError as exc:
6588 print("Invalid YAML format: {}".format(exc))
6589 exit(1)
6590 # TODO capture other controlled exceptions here
6591 # TODO remove the ClientException captures from all places, unless they do something different
6592
6593
6594 if __name__ == "__main__":
6595 cli()