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