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