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