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