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