Code Coverage

Cobertura Coverage Report > osmclient.scripts >

osm.py

Trend

Classes100%
 
Lines42%
   
Conditionals100%
 

File Coverage summary

NameClassesLinesConditionals
osm.py
100%
1/1
42%
1041/2451
100%
0/0

Coverage Breakdown by Class

NameLinesConditionals
osm.py
42%
1041/2451
N/A

Source

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 1 """
18 OSM shell/cli
19 """
20
21 1 import click
22 1 from osmclient import client
23 1 from osmclient.common.exceptions import ClientException, NotFound
24 1 from prettytable import PrettyTable
25 1 import yaml
26 1 import json
27 1 import time
28 1 import pycurl
29 1 import os
30 1 import textwrap
31 1 import pkg_resources
32 1 import logging
33 1 from datetime import datetime
34 1 from typing import Any, Dict
35
36
37 1 def wrap_text(text, width):
38 1     wrapper = textwrap.TextWrapper(width=width)
39 1     lines = text.splitlines()
40 1     return "\n".join(map(wrapper.fill, lines))
41
42
43 1 def trunc_text(text, length):
44 0     if len(text) > length:
45 0         return text[: (length - 3)] + "..."
46     else:
47 0         return text
48
49
50 1 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 0     logger.debug("")
60 0     fullclassname = obj.__module__ + "." + obj.__class__.__name__
61 0     message = 'The following commands or options are only supported with the option "--sol005": {}'.format(
62         what
63     )
64 0     if version == "v1":
65 0         message = 'The following commands or options are not supported when using option "--sol005": {}'.format(
66             what
67         )
68 0     if fullclassname != "osmclient.{}.client.Client".format(version):
69 0         raise ClientException(message)
70 0     return
71
72
73 1 def get_project(project_list, item):
74     # project_list = ctx.obj.project.list()
75 0     item_project_list = item.get("_admin", {}).get("projects_read")
76 0     project_id = "None"
77 0     project_name = "None"
78 0     if item_project_list:
79 0         for p1 in item_project_list:
80 0             project_id = p1
81 0             for p2 in project_list:
82 0                 if p2["_id"] == project_id:
83 0                     project_name = p2["name"]
84 0                     return project_id, project_name
85 0     return project_id, project_name
86
87
88 1 def get_vim_name(vim_list, vim_id):
89 0     vim_name = "-"
90 0     for v in vim_list:
91 0         if v["uuid"] == vim_id:
92 0             vim_name = v["name"]
93 0             break
94 0     return vim_name
95
96
97 1 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 0     config = {}
107 0     if config_file:
108 0         with open(config_file, "r") as cf:
109 0             config = yaml.safe_load(cf.read())
110 0     if json_string:
111 0         cdict = yaml.safe_load(json_string)
112 0         for k, v in cdict.items():
113 0             config[k] = v
114 0     return config
115
116
117 1 @click.group(
118     context_settings=dict(help_option_names=["-h", "--help"], max_content_width=160)
119 )
120 1 @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 1 @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 1 @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 1 @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 1 @click.option(
150     "-v",
151     "--verbose",
152     count=True,
153     help="increase verbosity (-v INFO, -vv VERBOSE, -vvv DEBUG)",
154 )
155 1 @click.option("--all-projects", default=None, is_flag=True, help="include all projects")
156 1 @click.option(
157     "--public/--no-public",
158     default=None,
159     help="flag for public items (packages, instances, VIM accounts, etc.)",
160 )
161 1 @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 1 @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 1 @click.pass_context
198 1 def cli_osm(ctx, **kwargs):
199     global logger
200 1     hostname = kwargs.pop("hostname", None)
201 1     if hostname is None:
202 0         print(
203             (
204                 "either hostname option or OSM_HOSTNAME "
205                 + "environment variable needs to be specified"
206             )
207         )
208 0         exit(1)
209     # Remove None values
210 1     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 1     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 1     ctx.obj = client.Client(host=hostname, sol005=sol005, **kwargs)
231 1     logger = logging.getLogger("osmclient")
232
233
234 ####################
235 # LIST operations
236 ####################
237
238
239 1 @cli_osm.command(name="ns-list", short_help="list all NS instances")
240 1 @click.option(
241     "--filter",
242     default=None,
243     multiple=True,
244     help="restricts the list to the NS instances matching the filter.",
245 )
246 1 @click.option(
247     "--long",
248     is_flag=True,
249     help="get more details of the NS (project, vim, deployment status, configuration status.",
250 )
251 1 @click.pass_context
252 1 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 0     def summarize_deployment_status(status_dict):
302         # Nets
303 0         summary = ""
304 0         if not status_dict:
305 0             return summary
306 0         n_nets = 0
307 0         status_nets = {}
308 0         net_list = status_dict.get("nets", [])
309 0         for net in net_list:
310 0             n_nets += 1
311 0             if net["status"] not in status_nets:
312 0                 status_nets[net["status"]] = 1
313             else:
314 0                 status_nets[net["status"]] += 1
315 0         message = "Nets: "
316 0         for k, v in status_nets.items():
317 0             message += "{}:{},".format(k, v)
318 0         message += "TOTAL:{}".format(n_nets)
319 0         summary += "{}".format(message)
320         # VMs and VNFs
321 0         n_vms = 0
322 0         status_vms = {}
323 0         status_vnfs = {}
324 0         vnf_list = status_dict["vnfs"]
325 0         for vnf in vnf_list:
326 0             member_vnf_index = vnf["member_vnf_index"]
327 0             if member_vnf_index not in status_vnfs:
328 0                 status_vnfs[member_vnf_index] = {}
329 0             for vm in vnf["vms"]:
330 0                 n_vms += 1
331 0                 if vm["status"] not in status_vms:
332 0                     status_vms[vm["status"]] = 1
333                 else:
334 0                     status_vms[vm["status"]] += 1
335 0                 if vm["status"] not in status_vnfs[member_vnf_index]:
336 0                     status_vnfs[member_vnf_index][vm["status"]] = 1
337                 else:
338 0                     status_vnfs[member_vnf_index][vm["status"]] += 1
339 0         message = "VMs: "
340 0         for k, v in status_vms.items():
341 0             message += "{}:{},".format(k, v)
342 0         message += "TOTAL:{}".format(n_vms)
343 0         summary += "\n{}".format(message)
344 0         summary += "\nNFs:"
345 0         for k, v in status_vnfs.items():
346 0             total = 0
347 0             message = "\n  {} VMs: ".format(k)
348 0             for k2, v2 in v.items():
349 0                 message += "{}:{},".format(k2, v2)
350 0                 total += v2
351 0             message += "TOTAL:{}".format(total)
352 0         summary += message
353 0         return summary
354
355 0     def summarize_config_status(ee_list):
356 0         summary = ""
357 0         if not ee_list:
358 0             return summary
359 0         n_ee = 0
360 0         status_ee = {}
361 0         for ee in ee_list:
362 0             n_ee += 1
363 0             if ee["elementType"] not in status_ee:
364 0                 status_ee[ee["elementType"]] = {}
365 0                 status_ee[ee["elementType"]][ee["status"]] = 1
366 0                 continue
367 0             if ee["status"] in status_ee[ee["elementType"]]:
368 0                 status_ee[ee["elementType"]][ee["status"]] += 1
369             else:
370 0                 status_ee[ee["elementType"]][ee["status"]] = 1
371 0         for elementType in ["KDU", "VDU", "PDU", "VNF", "NS"]:
372 0             if elementType in status_ee:
373 0                 message = ""
374 0                 total = 0
375 0                 for k, v in status_ee[elementType].items():
376 0                     message += "{}:{},".format(k, v)
377 0                     total += v
378 0                 message += "TOTAL:{}\n".format(total)
379 0                 summary += "{}: {}".format(elementType, message)
380 0         summary += "TOTAL Exec. Env.: {}".format(n_ee)
381 0         return summary
382
383 0     logger.debug("")
384 0     if filter:
385 0         check_client_version(ctx.obj, "--filter")
386 0         filter = "&".join(filter)
387 0         resp = ctx.obj.ns.list(filter)
388     else:
389 0         resp = ctx.obj.ns.list()
390 0     if long:
391 0         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 0         project_list = ctx.obj.project.list()
406 0         try:
407 0             vim_list = ctx.obj.vim.list()
408 0         except Exception:
409 0             vim_list = []
410     else:
411 0         table = PrettyTable(
412             [
413                 "ns instance name",
414                 "id",
415                 "date",
416                 "ns state",
417                 "current operation",
418                 "error details",
419             ]
420         )
421 0     for ns in resp:
422 0         fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
423 0         if fullclassname == "osmclient.sol005.client.Client":
424 0             nsr = ns
425 0             logger.debug("NS info: {}".format(nsr))
426 0             nsr_name = nsr["name"]
427 0             nsr_id = nsr["_id"]
428 0             date = datetime.fromtimestamp(nsr["create-time"]).strftime(
429                 "%Y-%m-%dT%H:%M:%S"
430             )
431 0             ns_state = nsr.get("nsState", nsr["_admin"]["nsState"])
432 0             if long:
433 0                 deployment_status = summarize_deployment_status(
434                     nsr.get("deploymentStatus")
435                 )
436 0                 config_status = summarize_config_status(nsr.get("configurationStatus"))
437 0                 project_id, project_name = get_project(project_list, nsr)
438                 # project = '{} ({})'.format(project_name, project_id)
439 0                 project = project_name
440 0                 vim_id = nsr.get("datacenter")
441 0                 vim_name = get_vim_name(vim_list, vim_id)
442
443                 # vim = '{} ({})'.format(vim_name, vim_id)
444 0                 vim = vim_name
445 0             if "currentOperation" in nsr:
446 0                 current_operation = "{} ({})".format(
447                     nsr["currentOperation"], nsr["currentOperationID"]
448                 )
449             else:
450 0                 current_operation = "{} ({})".format(
451                     nsr["_admin"].get("current-operation", "-"),
452                     nsr["_admin"]["nslcmop"],
453                 )
454 0             error_details = "N/A"
455 0             if (
456                 ns_state == "BROKEN"
457                 or ns_state == "DEGRADED"
458                 or ("currentOperation" not in nsr and nsr.get("errorDescription"))
459             ):
460 0                 error_details = "{}\nDetail: {}".format(
461                     nsr["errorDescription"], nsr["errorDetail"]
462                 )
463         else:
464 0             nsopdata = ctx.obj.ns.get_opdata(ns["id"])
465 0             nsr = nsopdata["nsr:nsr"]
466 0             nsr_name = nsr["name-ref"]
467 0             nsr_id = nsr["ns-instance-config-ref"]
468 0             date = "-"
469 0             project = "-"
470 0             deployment_status = (
471                 nsr["operational-status"]
472                 if "operational-status" in nsr
473                 else "Not found"
474             )
475 0             ns_state = deployment_status
476 0             config_status = nsr.get("config-status", "Not found")
477 0             current_operation = "Unknown"
478 0             error_details = nsr.get("detailed-status", "Not found")
479 0             if config_status == "config_not_needed":
480 0                 config_status = "configured (no charms)"
481
482 0         if long:
483 0             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 0             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 0     table.align = "l"
509 0     print(table)
510 0     print('To get the history of all operations over a NS, run "osm ns-op-list NS_ID"')
511 0     print(
512         'For more details on the current operation, run "osm ns-op-show OPERATION_ID"'
513     )
514
515
516 1 def nsd_list(ctx, filter, long):
517 0     logger.debug("")
518 0     if filter:
519 0         check_client_version(ctx.obj, "--filter")
520 0         filter = "&".join(filter)
521 0         resp = ctx.obj.nsd.list(filter)
522     else:
523 0         resp = ctx.obj.nsd.list()
524     # print(yaml.safe_dump(resp))
525 0     fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
526 0     if fullclassname == "osmclient.sol005.client.Client":
527 0         if long:
528 0             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 0             table = PrettyTable(["nsd name", "id"])
541 0         for nsd in resp:
542 0             name = nsd.get("id", "-")
543 0             if long:
544 0                 onb_state = nsd["_admin"].get("onboardingState", "-")
545 0                 op_state = nsd["_admin"].get("operationalState", "-")
546 0                 usage_state = nsd["_admin"].get("usageState", "-")
547 0                 date = datetime.fromtimestamp(nsd["_admin"]["created"]).strftime(
548                     "%Y-%m-%dT%H:%M:%S"
549                 )
550 0                 last_update = datetime.fromtimestamp(
551                     nsd["_admin"]["modified"]
552                 ).strftime("%Y-%m-%dT%H:%M:%S")
553 0                 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 0                 table.add_row([name, nsd["_id"]])
566     else:
567 0         table = PrettyTable(["nsd name", "id"])
568 0         for nsd in resp:
569 0             table.add_row([nsd["name"], nsd["id"]])
570 0     table.align = "l"
571 0     print(table)
572
573
574 1 @cli_osm.command(name="nsd-list", short_help="list all NS packages")
575 1 @click.option(
576     "--filter",
577     default=None,
578     multiple=True,
579     help="restricts the list to the NSD/NSpkg matching the filter",
580 )
581 1 @click.option("--long", is_flag=True, help="get more details")
582 1 @click.pass_context
583 1 def nsd_list1(ctx, filter, long):
584     """list all NSD/NS pkg in the system"""
585 0     logger.debug("")
586 0     nsd_list(ctx, filter, long)
587
588
589 1 @cli_osm.command(name="nspkg-list", short_help="list all NS packages")
590 1 @click.option(
591     "--filter",
592     default=None,
593     multiple=True,
594     help="restricts the list to the NSD/NSpkg matching the filter",
595 )
596 1 @click.option("--long", is_flag=True, help="get more details")
597 1 @click.pass_context
598 1 def nsd_list2(ctx, filter, long):
599     """list all NS packages"""
600 0     logger.debug("")
601 0     nsd_list(ctx, filter, long)
602
603
604 1 def pkg_repo_list(ctx, pkgtype, filter, repo, long):
605 0     resp = ctx.obj.osmrepo.pkg_list(pkgtype, filter, repo)
606 0     if long:
607 0         table = PrettyTable(
608             ["nfpkg name", "vendor", "version", "latest", "description", "repository"]
609         )
610     else:
611 0         table = PrettyTable(["nfpkg name", "repository"])
612 0     for vnfd in resp:
613 0         name = vnfd.get("id", vnfd.get("name", "-"))
614 0         repository = vnfd.get("repository")
615 0         if long:
616 0             vendor = vnfd.get("provider", vnfd.get("vendor"))
617 0             version = vnfd.get("version")
618 0             description = vnfd.get("description")
619 0             latest = vnfd.get("latest")
620 0             table.add_row([name, vendor, version, latest, description, repository])
621         else:
622 0             table.add_row([name, repository])
623 0         table.align = "l"
624 0     print(table)
625
626
627 1 def vnfd_list(ctx, nf_type, filter, long):
628 0     logger.debug("")
629 0     if nf_type:
630 0         check_client_version(ctx.obj, "--nf_type")
631 0     elif filter:
632 0         check_client_version(ctx.obj, "--filter")
633 0     if filter:
634 0         filter = "&".join(filter)
635 0     if nf_type:
636 0         if nf_type == "vnf":
637 0             nf_filter = "_admin.type=vnfd"
638 0         elif nf_type == "pnf":
639 0             nf_filter = "_admin.type=pnfd"
640 0         elif nf_type == "hnf":
641 0             nf_filter = "_admin.type=hnfd"
642         else:
643 0             raise ClientException(
644                 'wrong value for "--nf_type" option, allowed values: vnf, pnf, hnf'
645             )
646 0         if filter:
647 0             filter = "{}&{}".format(nf_filter, filter)
648         else:
649 0             filter = nf_filter
650 0     if filter:
651 0         resp = ctx.obj.vnfd.list(filter)
652     else:
653 0         resp = ctx.obj.vnfd.list()
654     # print(yaml.safe_dump(resp))
655 0     fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
656 0     if fullclassname == "osmclient.sol005.client.Client":
657 0         if long:
658 0             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 0             table = PrettyTable(["nfpkg name", "id", "desc type"])
674 0         for vnfd in resp:
675 0             name = vnfd.get("id", vnfd.get("name", "-"))
676 0             descriptor_type = "sol006" if "product-name" in vnfd else "rel8"
677 0             if long:
678 0                 onb_state = vnfd["_admin"].get("onboardingState", "-")
679 0                 op_state = vnfd["_admin"].get("operationalState", "-")
680 0                 vendor = vnfd.get("provider", vnfd.get("vendor"))
681 0                 version = vnfd.get("version")
682 0                 usage_state = vnfd["_admin"].get("usageState", "-")
683 0                 date = datetime.fromtimestamp(vnfd["_admin"]["created"]).strftime(
684                     "%Y-%m-%dT%H:%M:%S"
685                 )
686 0                 last_update = datetime.fromtimestamp(
687                     vnfd["_admin"]["modified"]
688                 ).strftime("%Y-%m-%dT%H:%M:%S")
689 0                 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 0                 table.add_row([name, vnfd["_id"], descriptor_type])
705     else:
706 0         table = PrettyTable(["nfpkg name", "id"])
707 0         for vnfd in resp:
708 0             table.add_row([vnfd["name"], vnfd["id"]])
709 0     table.align = "l"
710 0     print(table)
711
712
713 1 @cli_osm.command(name="vnfd-list", short_help="list all xNF packages (VNF, HNF, PNF)")
714 1 @click.option("--nf_type", help="type of NF (vnf, pnf, hnf)")
715 1 @click.option(
716     "--filter",
717     default=None,
718     multiple=True,
719     help="restricts the list to the NF pkg matching the filter",
720 )
721 1 @click.option("--long", is_flag=True, help="get more details")
722 1 @click.pass_context
723 1 def vnfd_list1(ctx, nf_type, filter, long):
724     """list all xNF packages (VNF, HNF, PNF)"""
725 0     logger.debug("")
726 0     vnfd_list(ctx, nf_type, filter, long)
727
728
729 1 @cli_osm.command(name="vnfpkg-list", short_help="list all xNF packages (VNF, HNF, PNF)")
730 1 @click.option("--nf_type", help="type of NF (vnf, pnf, hnf)")
731 1 @click.option(
732     "--filter",
733     default=None,
734     multiple=True,
735     help="restricts the list to the NFpkg matching the filter",
736 )
737 1 @click.option("--long", is_flag=True, help="get more details")
738 1 @click.pass_context
739 1 def vnfd_list2(ctx, nf_type, filter, long):
740     """list all xNF packages (VNF, HNF, PNF)"""
741 0     logger.debug("")
742 0     vnfd_list(ctx, nf_type, filter, long)
743
744
745 1 @cli_osm.command(name="nfpkg-list", short_help="list all xNF packages (VNF, HNF, PNF)")
746 1 @click.option("--nf_type", help="type of NF (vnf, pnf, hnf)")
747 1 @click.option(
748     "--filter",
749     default=None,
750     multiple=True,
751     help="restricts the list to the NFpkg matching the filter",
752 )
753 1 @click.option("--long", is_flag=True, help="get more details")
754 1 @click.pass_context
755 1 def nfpkg_list(ctx, nf_type, filter, long):
756     """list all xNF packages (VNF, HNF, PNF)"""
757 0     logger.debug("")
758     # try:
759 0     check_client_version(ctx.obj, ctx.command.name)
760 0     vnfd_list(ctx, nf_type, filter, long)
761     # except ClientException as e:
762     #     print(str(e))
763     #     exit(1)
764
765
766 1 @cli_osm.command(
767     name="vnfpkg-repo-list", short_help="list all xNF from OSM repositories"
768 )
769 1 @click.option(
770     "--filter",
771     default=None,
772     multiple=True,
773     help="restricts the list to the NFpkg matching the filter",
774 )
775 1 @click.option(
776     "--repo", default=None, help="restricts the list to a particular OSM repository"
777 )
778 1 @click.option("--long", is_flag=True, help="get more details")
779 1 @click.pass_context
780 1 def nfpkg_repo_list1(ctx, filter, repo, long):
781     """list xNF packages from OSM repositories"""
782 0     pkgtype = "vnf"
783 0     pkg_repo_list(ctx, pkgtype, filter, repo, long)
784
785
786 1 @cli_osm.command(
787     name="nfpkg-repo-list", short_help="list all xNF from OSM repositories"
788 )
789 1 @click.option(
790     "--filter",
791     default=None,
792     multiple=True,
793     help="restricts the list to the NFpkg matching the filter",
794 )
795 1 @click.option(
796     "--repo", default=None, help="restricts the list to a particular OSM repository"
797 )
798 1 @click.option("--long", is_flag=True, help="get more details")
799 1 @click.pass_context
800 1 def nfpkg_repo_list2(ctx, filter, repo, long):
801     """list xNF packages from OSM repositories"""
802 0     pkgtype = "vnf"
803 0     pkg_repo_list(ctx, pkgtype, filter, repo, long)
804
805
806 1 def vnf_list(ctx, ns, filter, long):
807     # try:
808 0     if ns or filter:
809 0         if ns:
810 0             check_client_version(ctx.obj, "--ns")
811 0         if filter:
812 0             filter = "&".join(filter)
813 0             check_client_version(ctx.obj, "--filter")
814 0         resp = ctx.obj.vnf.list(ns, filter)
815     else:
816 0         resp = ctx.obj.vnf.list()
817     # except ClientException as e:
818     #     print(str(e))
819     #     exit(1)
820 0     fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
821 0     if fullclassname == "osmclient.sol005.client.Client":
822 0         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 0         if long:
832 0             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 0         table = PrettyTable(field_names)
844 0         for vnfr in resp:
845 0             name = vnfr["name"] if "name" in vnfr else "-"
846 0             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 0             if long:
856 0                 date = datetime.fromtimestamp(vnfr["_admin"]["created"]).strftime(
857                     "%Y-%m-%dT%H:%M:%S"
858                 )
859 0                 last_update = datetime.fromtimestamp(
860                     vnfr["_admin"]["modified"]
861                 ).strftime("%Y-%m-%dT%H:%M:%S")
862 0                 new_row.extend([date, last_update])
863 0             table.add_row(new_row)
864     else:
865 0         table = PrettyTable(["vnf name", "id", "operational status", "config status"])
866 0         for vnfr in resp:
867 0             if "mgmt-interface" not in vnfr:
868 0                 vnfr["mgmt-interface"] = {}
869 0                 vnfr["mgmt-interface"]["ip-address"] = None
870 0             table.add_row(
871                 [
872                     vnfr["name"],
873                     vnfr["id"],
874                     vnfr["operational-status"],
875                     vnfr["config-status"],
876                 ]
877             )
878 0     table.align = "l"
879 0     print(table)
880
881
882 1 @cli_osm.command(name="vnf-list", short_help="list all NF instances")
883 1 @click.option(
884     "--ns", default=None, help="NS instance id or name to restrict the NF list"
885 )
886 1 @click.option(
887     "--filter",
888     default=None,
889     multiple=True,
890     help="restricts the list to the NF instances matching the filter.",
891 )
892 1 @click.option("--long", is_flag=True, help="get more details")
893 1 @click.pass_context
894 1 def vnf_list1(ctx, ns, filter, long):
895     """list all NF instances"""
896 0     logger.debug("")
897 0     vnf_list(ctx, ns, filter, long)
898
899
900 1 @cli_osm.command(name="nsd-repo-list", short_help="list all NS from OSM repositories")
901 1 @click.option(
902     "--filter",
903     default=None,
904     multiple=True,
905     help="restricts the list to the NS matching the filter",
906 )
907 1 @click.option(
908     "--repo", default=None, help="restricts the list to a particular OSM repository"
909 )
910 1 @click.option("--long", is_flag=True, help="get more details")
911 1 @click.pass_context
912 1 def nspkg_repo_list(ctx, filter, repo, long):
913     """list xNF packages from OSM repositories"""
914 0     pkgtype = "ns"
915 0     pkg_repo_list(ctx, pkgtype, filter, repo, long)
916
917
918 1 @cli_osm.command(name="nspkg-repo-list", short_help="list all NS from OSM repositories")
919 1 @click.option(
920     "--filter",
921     default=None,
922     multiple=True,
923     help="restricts the list to the NS matching the filter",
924 )
925 1 @click.option(
926     "--repo", default=None, help="restricts the list to a particular OSM repository"
927 )
928 1 @click.option("--long", is_flag=True, help="get more details")
929 1 @click.pass_context
930 1 def nspkg_repo_list2(ctx, filter, repo, long):
931     """list xNF packages from OSM repositories"""
932 0     pkgtype = "ns"
933 0     pkg_repo_list(ctx, pkgtype, filter, repo, long)
934
935
936 1 @cli_osm.command(name="nf-list", short_help="list all NF instances")
937 1 @click.option(
938     "--ns", default=None, help="NS instance id or name to restrict the NF list"
939 )
940 1 @click.option(
941     "--filter",
942     default=None,
943     multiple=True,
944     help="restricts the list to the NF instances matching the filter.",
945 )
946 1 @click.option("--long", is_flag=True, help="get more details")
947 1 @click.pass_context
948 1 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 0     logger.debug("")
997 0     vnf_list(ctx, ns, filter, long)
998
999
1000 1 @cli_osm.command(
1001     name="ns-op-list", short_help="shows the history of operations over a NS instance"
1002 )
1003 1 @click.argument("name")
1004 1 @click.option(
1005     "--long", is_flag=True, help="get more details of the NS operation (date, )."
1006 )
1007 1 @click.pass_context
1008 1 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 0     def formatParams(params):
1015 0         if params["lcmOperationType"] == "instantiate":
1016 0             params.pop("nsDescription")
1017 0             params.pop("nsName")
1018 0             params.pop("nsdId")
1019 0             params.pop("nsr_id")
1020 0         elif params["lcmOperationType"] == "action":
1021 0             params.pop("primitive")
1022 0         params.pop("lcmOperationType")
1023 0         params.pop("nsInstanceId")
1024 0         return params
1025
1026 0     logger.debug("")
1027     # try:
1028 0     check_client_version(ctx.obj, ctx.command.name)
1029 0     resp = ctx.obj.ns.list_op(name)
1030     # except ClientException as e:
1031     #     print(str(e))
1032     #     exit(1)
1033
1034 0     if long:
1035 0         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 0         table = PrettyTable(
1049             ["id", "operation", "action_name", "status", "date", "detail"]
1050         )
1051
1052     # print(yaml.safe_dump(resp))
1053 0     for op in resp:
1054 0         action_name = "N/A"
1055 0         if op["lcmOperationType"] == "action":
1056 0             action_name = op["operationParams"]["primitive"]
1057 0         detail = "-"
1058 0         if op["operationState"] == "PROCESSING":
1059 0             if op["queuePosition"] is not None and op["queuePosition"] > 0:
1060 0                 detail = "In queue. Current position: {}".format(op["queuePosition"])
1061 0             elif op["lcmOperationType"] in ("instantiate", "terminate"):
1062 0                 if op["stage"]:
1063 0                     detail = op["stage"]
1064 0         elif op["operationState"] in ("FAILED", "FAILED_TEMP"):
1065 0             detail = op.get("errorMessage", "-")
1066 0         date = datetime.fromtimestamp(op["startTime"]).strftime("%Y-%m-%dT%H:%M:%S")
1067 0         last_update = datetime.fromtimestamp(op["statusEnteredTime"]).strftime(
1068             "%Y-%m-%dT%H:%M:%S"
1069         )
1070 0         if long:
1071 0             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 0             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 0     table.align = "l"
1098 0     print(table)
1099
1100
1101 1 def nsi_list(ctx, filter):
1102     """list all Network Slice Instances"""
1103 0     logger.debug("")
1104     # try:
1105 0     check_client_version(ctx.obj, ctx.command.name)
1106 0     if filter:
1107 0         filter = "&".join(filter)
1108 0     resp = ctx.obj.nsi.list(filter)
1109     # except ClientException as e:
1110     #     print(str(e))
1111     #     exit(1)
1112 0     table = PrettyTable(
1113         [
1114             "netslice instance name",
1115             "id",
1116             "operational status",
1117             "config status",
1118             "detailed status",
1119         ]
1120     )
1121 0     for nsi in resp:
1122 0         nsi_name = nsi["name"]
1123 0         nsi_id = nsi["_id"]
1124 0         opstatus = (
1125             nsi["operational-status"] if "operational-status" in nsi else "Not found"
1126         )
1127 0         configstatus = nsi["config-status"] if "config-status" in nsi else "Not found"
1128 0         detailed_status = (
1129             nsi["detailed-status"] if "detailed-status" in nsi else "Not found"
1130         )
1131 0         if configstatus == "config_not_needed":
1132 0             configstatus = "configured (no charms)"
1133 0         table.add_row([nsi_name, nsi_id, opstatus, configstatus, detailed_status])
1134 0     table.align = "l"
1135 0     print(table)
1136
1137
1138 1 @cli_osm.command(name="nsi-list", short_help="list all Network Slice Instances (NSI)")
1139 1 @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 1 @click.pass_context
1146 1 def nsi_list1(ctx, filter):
1147     """list all Network Slice Instances (NSI)"""
1148 0     logger.debug("")
1149 0     nsi_list(ctx, filter)
1150
1151
1152 1 @cli_osm.command(
1153     name="netslice-instance-list", short_help="list all Network Slice Instances (NSI)"
1154 )
1155 1 @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 1 @click.pass_context
1162 1 def nsi_list2(ctx, filter):
1163     """list all Network Slice Instances (NSI)"""
1164 0     logger.debug("")
1165 0     nsi_list(ctx, filter)
1166
1167
1168 1 def nst_list(ctx, filter):
1169 0     logger.debug("")
1170     # try:
1171 0     check_client_version(ctx.obj, ctx.command.name)
1172 0     if filter:
1173 0         filter = "&".join(filter)
1174 0     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 0     table = PrettyTable(["nst name", "id"])
1180 0     for nst in resp:
1181 0         name = nst["name"] if "name" in nst else "-"
1182 0         table.add_row([name, nst["_id"]])
1183 0     table.align = "l"
1184 0     print(table)
1185
1186
1187 1 @cli_osm.command(name="nst-list", short_help="list all Network Slice Templates (NST)")
1188 1 @click.option(
1189     "--filter",
1190     default=None,
1191     multiple=True,
1192     help="restricts the list to the NST matching the filter",
1193 )
1194 1 @click.pass_context
1195 1 def nst_list1(ctx, filter):
1196     """list all Network Slice Templates (NST) in the system"""
1197 0     logger.debug("")
1198 0     nst_list(ctx, filter)
1199
1200
1201 1 @cli_osm.command(
1202     name="netslice-template-list", short_help="list all Network Slice Templates (NST)"
1203 )
1204 1 @click.option(
1205     "--filter",
1206     default=None,
1207     multiple=True,
1208     help="restricts the list to the NST matching the filter",
1209 )
1210 1 @click.pass_context
1211 1 def nst_list2(ctx, filter):
1212     """list all Network Slice Templates (NST) in the system"""
1213 0     logger.debug("")
1214 0     nst_list(ctx, filter)
1215
1216
1217 1 def nsi_op_list(ctx, name):
1218 0     logger.debug("")
1219     # try:
1220 0     check_client_version(ctx.obj, ctx.command.name)
1221 0     resp = ctx.obj.nsi.list_op(name)
1222     # except ClientException as e:
1223     #     print(str(e))
1224     #     exit(1)
1225 0     table = PrettyTable(["id", "operation", "status"])
1226 0     for op in resp:
1227 0         table.add_row([op["id"], op["lcmOperationType"], op["operationState"]])
1228 0     table.align = "l"
1229 0     print(table)
1230
1231
1232 1 @cli_osm.command(
1233     name="nsi-op-list",
1234     short_help="shows the history of operations over a Network Slice Instance (NSI)",
1235 )
1236 1 @click.argument("name")
1237 1 @click.pass_context
1238 1 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 0     logger.debug("")
1244 0     nsi_op_list(ctx, name)
1245
1246
1247 1 @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 1 @click.argument("name")
1252 1 @click.pass_context
1253 1 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 0     logger.debug("")
1259 0     nsi_op_list(ctx, name)
1260
1261
1262 1 @cli_osm.command(name="pdu-list", short_help="list all Physical Deployment Units (PDU)")
1263 1 @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 1 @click.pass_context
1270 1 def pdu_list(ctx, filter):
1271     """list all Physical Deployment Units (PDU)"""
1272 0     logger.debug("")
1273     # try:
1274 0     check_client_version(ctx.obj, ctx.command.name)
1275 0     if filter:
1276 0         filter = "&".join(filter)
1277 0     resp = ctx.obj.pdu.list(filter)
1278     # except ClientException as e:
1279     #     print(str(e))
1280     #     exit(1)
1281 0     table = PrettyTable(["pdu name", "id", "type", "mgmt ip address"])
1282 0     for pdu in resp:
1283 0         pdu_name = pdu["name"]
1284 0         pdu_id = pdu["_id"]
1285 0         pdu_type = pdu["type"]
1286 0         pdu_ipaddress = "None"
1287 0         for iface in pdu["interfaces"]:
1288 0             if iface["mgmt"]:
1289 0                 pdu_ipaddress = iface["ip-address"]
1290 0                 break
1291 0         table.add_row([pdu_name, pdu_id, pdu_type, pdu_ipaddress])
1292 0     table.align = "l"
1293 0     print(table)
1294
1295
1296 ####################
1297 # SHOW operations
1298 ####################
1299
1300
1301 1 def nsd_show(ctx, name, literal):
1302 0     logger.debug("")
1303     # try:
1304 0     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 0     if literal:
1311 0         print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
1312 0         return
1313
1314 0     table = PrettyTable(["field", "value"])
1315 0     for k, v in list(resp.items()):
1316 0         table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)])
1317 0     table.align = "l"
1318 0     print(table)
1319
1320
1321 1 @cli_osm.command(name="nsd-show", short_help="shows the details of a NS package")
1322 1 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1323 1 @click.argument("name")
1324 1 @click.pass_context
1325 1 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 0     logger.debug("")
1331 0     nsd_show(ctx, name, literal)
1332
1333
1334 1 @cli_osm.command(name="nspkg-show", short_help="shows the details of a NS package")
1335 1 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1336 1 @click.argument("name")
1337 1 @click.pass_context
1338 1 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 0     logger.debug("")
1344 0     nsd_show(ctx, name, literal)
1345
1346
1347 1 def vnfd_show(ctx, name, literal):
1348 0     logger.debug("")
1349     # try:
1350 0     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 0     if literal:
1357 0         print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
1358 0         return
1359
1360 0     table = PrettyTable(["field", "value"])
1361 0     for k, v in list(resp.items()):
1362 0         table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)])
1363 0     table.align = "l"
1364 0     print(table)
1365
1366
1367 1 def pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal):
1368 0     logger.debug("")
1369 0     if filter:
1370 0         filter = "&".join(filter)
1371     # try:
1372 0     resp = ctx.obj.osmrepo.pkg_get(pkgtype, name, repo, version, filter)
1373
1374 0     if literal:
1375 0         print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
1376 0         return
1377 0     pkgtype += "d"
1378 0     catalog = pkgtype + "-catalog"
1379 0     full_catalog = pkgtype + ":" + catalog
1380 0     if resp.get(catalog):
1381 0         resp = resp.pop(catalog)[pkgtype][0]
1382 0     elif resp.get(full_catalog):
1383 0         resp = resp.pop(full_catalog)[pkgtype][0]
1384
1385 0     table = PrettyTable(["field", "value"])
1386 0     for k, v in list(resp.items()):
1387 0         table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)])
1388 0     table.align = "l"
1389 0     print(table)
1390
1391
1392 1 @cli_osm.command(name="vnfd-show", short_help="shows the details of a NF package")
1393 1 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1394 1 @click.argument("name")
1395 1 @click.pass_context
1396 1 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 0     logger.debug("")
1402 0     vnfd_show(ctx, name, literal)
1403
1404
1405 1 @cli_osm.command(name="vnfpkg-show", short_help="shows the details of a NF package")
1406 1 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1407 1 @click.argument("name")
1408 1 @click.pass_context
1409 1 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 0     logger.debug("")
1415 0     vnfd_show(ctx, name, literal)
1416
1417
1418 1 @cli_osm.command(
1419     name="vnfpkg-repo-show",
1420     short_help="shows the details of a NF package in an OSM repository",
1421 )
1422 1 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1423 1 @click.option("--repo", required=True, help="Repository name")
1424 1 @click.argument("name")
1425 1 @click.option("--filter", default=None, multiple=True, help="filter by fields")
1426 1 @click.option("--version", default="latest", help="package version")
1427 1 @click.pass_context
1428 1 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 0     pkgtype = "vnf"
1434 0     pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal)
1435
1436
1437 1 @cli_osm.command(
1438     name="nsd-repo-show",
1439     short_help="shows the details of a NS package in an OSM repository",
1440 )
1441 1 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1442 1 @click.option("--repo", required=True, help="Repository name")
1443 1 @click.argument("name")
1444 1 @click.option("--filter", default=None, multiple=True, help="filter by fields")
1445 1 @click.option("--version", default="latest", help="package version")
1446 1 @click.pass_context
1447 1 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 0     pkgtype = "ns"
1453 0     pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal)
1454
1455
1456 1 @cli_osm.command(
1457     name="nspkg-repo-show",
1458     short_help="shows the details of a NS package in an OSM repository",
1459 )
1460 1 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1461 1 @click.option("--repo", required=True, help="Repository name")
1462 1 @click.argument("name")
1463 1 @click.option("--filter", default=None, multiple=True, help="filter by fields")
1464 1 @click.option("--version", default="latest", help="package version")
1465 1 @click.pass_context
1466 1 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 0     pkgtype = "ns"
1472 0     pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal)
1473
1474
1475 1 @cli_osm.command(name="nfpkg-show", short_help="shows the details of a NF package")
1476 1 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1477 1 @click.argument("name")
1478 1 @click.pass_context
1479 1 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 0     logger.debug("")
1485 0     vnfd_show(ctx, name, literal)
1486
1487
1488 1 @cli_osm.command(
1489     name="nfpkg-repo-show",
1490     short_help="shows the details of a NF package in an OSM repository",
1491 )
1492 1 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1493 1 @click.option("--repo", required=True, help="Repository name")
1494 1 @click.argument("name")
1495 1 @click.option("--filter", default=None, multiple=True, help="filter by fields")
1496 1 @click.option("--version", default="latest", help="package version")
1497 1 @click.pass_context
1498 1 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 0     pkgtype = "vnf"
1504 0     pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal)
1505
1506
1507 1 @cli_osm.command(name="ns-show", short_help="shows the info of a NS instance")
1508 1 @click.argument("name")
1509 1 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1510 1 @click.option(
1511     "--filter",
1512     multiple=True,
1513     help="restricts the information to the fields in the filter",
1514 )
1515 1 @click.pass_context
1516 1 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 0     logger.debug("")
1522     # try:
1523 0     ns = ctx.obj.ns.get(name)
1524     # except ClientException as e:
1525     #     print(str(e))
1526     #     exit(1)
1527
1528 0     if literal:
1529 0         print(yaml.safe_dump(ns, indent=4, default_flow_style=False))
1530 0         return
1531
1532 0     table = PrettyTable(["field", "value"])
1533
1534 0     for k, v in list(ns.items()):
1535 0         if not filter or k in filter:
1536 0             table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)])
1537
1538 0     fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
1539 0     if fullclassname != "osmclient.sol005.client.Client":
1540 0         nsopdata = ctx.obj.ns.get_opdata(ns["id"])
1541 0         nsr_optdata = nsopdata["nsr:nsr"]
1542 0         for k, v in list(nsr_optdata.items()):
1543 0             if not filter or k in filter:
1544 0                 table.add_row([k, wrap_text(json.dumps(v, indent=2), width=100)])
1545 0     table.align = "l"
1546 0     print(table)
1547
1548
1549 1 @cli_osm.command(name="vnf-show", short_help="shows the info of a VNF instance")
1550 1 @click.argument("name")
1551 1 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1552 1 @click.option(
1553     "--filter",
1554     multiple=True,
1555     help="restricts the information to the fields in the filter",
1556 )
1557 1 @click.option("--kdu", default=None, help="KDU name (whose status will be shown)")
1558 1 @click.pass_context
1559 1 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 0     def print_kdu_status(op_info_status):
1566         """print KDU status properly formatted"""
1567 0         try:
1568 0             op_status = yaml.safe_load(op_info_status)
1569 0             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 0                 last_deployed_time = datetime.fromtimestamp(
1579                     op_status["info"]["last_deployed"]["seconds"]
1580                 ).strftime("%a %b %d %I:%M:%S %Y")
1581 0                 print("LAST DEPLOYED: {}".format(last_deployed_time))
1582 0                 print("NAMESPACE: {}".format(op_status["namespace"]))
1583 0                 status_code = "UNKNOWN"
1584 0                 if op_status["info"]["status"]["code"] == 1:
1585 0                     status_code = "DEPLOYED"
1586 0                 print("STATUS: {}".format(status_code))
1587 0                 print()
1588 0                 print("RESOURCES:")
1589 0                 print(op_status["info"]["status"]["resources"])
1590 0                 if "notes" in op_status["info"]["status"]:
1591 0                     print("NOTES:")
1592 0                     print(op_status["info"]["status"]["notes"])
1593             else:
1594 0                 print(op_info_status)
1595 0         except Exception:
1596 0             print(op_info_status)
1597
1598 0     logger.debug("")
1599 0     if kdu:
1600 0         if literal:
1601 0             raise ClientException(
1602                 '"--literal" option is incompatible with "--kdu" option'
1603             )
1604 0         if filter:
1605 0             raise ClientException(
1606                 '"--filter" option is incompatible with "--kdu" option'
1607             )
1608
1609     # try:
1610 0     check_client_version(ctx.obj, ctx.command.name)
1611 0     resp = ctx.obj.vnf.get(name)
1612
1613 0     if kdu:
1614 0         ns_id = resp["nsr-id-ref"]
1615 0         op_data = {}
1616 0         op_data["member_vnf_index"] = resp["member-vnf-index-ref"]
1617 0         op_data["kdu_name"] = kdu
1618 0         op_data["primitive"] = "status"
1619 0         op_data["primitive_params"] = {}
1620 0         op_id = ctx.obj.ns.exec_op(ns_id, op_name="action", op_data=op_data, wait=False)
1621 0         t = 0
1622 0         while t < 30:
1623 0             op_info = ctx.obj.ns.get_op(op_id)
1624 0             if op_info["operationState"] == "COMPLETED":
1625 0                 print_kdu_status(op_info["detailed-status"])
1626 0                 return
1627 0             time.sleep(5)
1628 0             t += 5
1629 0         print("Could not determine KDU status")
1630 0         return
1631
1632 0     if literal:
1633 0         print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
1634 0         return
1635
1636 0     table = PrettyTable(["field", "value"])
1637 0     for k, v in list(resp.items()):
1638 0         if not filter or k in filter:
1639 0             table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)])
1640 0     table.align = "l"
1641 0     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 1 @cli_osm.command(name="ns-op-show", short_help="shows the info of a NS operation")
1694 1 @click.argument("id")
1695 1 @click.option(
1696     "--filter",
1697     multiple=True,
1698     help="restricts the information to the fields in the filter",
1699 )
1700 1 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1701 1 @click.pass_context
1702 1 def ns_op_show(ctx, id, filter, literal):
1703     """shows the detailed info of a NS operation
1704
1705     ID: operation identifier
1706     """
1707 0     logger.debug("")
1708     # try:
1709 0     check_client_version(ctx.obj, ctx.command.name)
1710 0     op_info = ctx.obj.ns.get_op(id)
1711     # except ClientException as e:
1712     #     print(str(e))
1713     #     exit(1)
1714
1715 0     if literal:
1716 0         print(yaml.safe_dump(op_info, indent=4, default_flow_style=False))
1717 0         return
1718
1719 0     table = PrettyTable(["field", "value"])
1720 0     for k, v in list(op_info.items()):
1721 0         if not filter or k in filter:
1722 0             table.add_row([k, wrap_text(json.dumps(v, indent=2), 100)])
1723 0     table.align = "l"
1724 0     print(table)
1725
1726
1727 1 def nst_show(ctx, name, literal):
1728 0     logger.debug("")
1729     # try:
1730 0     check_client_version(ctx.obj, ctx.command.name)
1731 0     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 0     if literal:
1738 0         print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
1739 0         return
1740
1741 0     table = PrettyTable(["field", "value"])
1742 0     for k, v in list(resp.items()):
1743 0         table.add_row([k, wrap_text(json.dumps(v, indent=2), 100)])
1744 0     table.align = "l"
1745 0     print(table)
1746
1747
1748 1 @cli_osm.command(
1749     name="nst-show", short_help="shows the content of a Network Slice Template (NST)"
1750 )
1751 1 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1752 1 @click.argument("name")
1753 1 @click.pass_context
1754 1 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 0     logger.debug("")
1760 0     nst_show(ctx, name, literal)
1761
1762
1763 1 @cli_osm.command(
1764     name="netslice-template-show",
1765     short_help="shows the content of a Network Slice Template (NST)",
1766 )
1767 1 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1768 1 @click.argument("name")
1769 1 @click.pass_context
1770 1 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 0     logger.debug("")
1776 0     nst_show(ctx, name, literal)
1777
1778
1779 1 def nsi_show(ctx, name, literal, filter):
1780 0     logger.debug("")
1781     # try:
1782 0     check_client_version(ctx.obj, ctx.command.name)
1783 0     nsi = ctx.obj.nsi.get(name)
1784     # except ClientException as e:
1785     #     print(str(e))
1786     #     exit(1)
1787
1788 0     if literal:
1789 0         print(yaml.safe_dump(nsi, indent=4, default_flow_style=False))
1790 0         return
1791
1792 0     table = PrettyTable(["field", "value"])
1793
1794 0     for k, v in list(nsi.items()):
1795 0         if not filter or k in filter:
1796 0             table.add_row([k, json.dumps(v, indent=2)])
1797
1798 0     table.align = "l"
1799 0     print(table)
1800
1801
1802 1 @cli_osm.command(
1803     name="nsi-show", short_help="shows the content of a Network Slice Instance (NSI)"
1804 )
1805 1 @click.argument("name")
1806 1 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1807 1 @click.option(
1808     "--filter",
1809     multiple=True,
1810     help="restricts the information to the fields in the filter",
1811 )
1812 1 @click.pass_context
1813 1 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 0     logger.debug("")
1819 0     nsi_show(ctx, name, literal, filter)
1820
1821
1822 1 @cli_osm.command(
1823     name="netslice-instance-show",
1824     short_help="shows the content of a Network Slice Instance (NSI)",
1825 )
1826 1 @click.argument("name")
1827 1 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1828 1 @click.option(
1829     "--filter",
1830     multiple=True,
1831     help="restricts the information to the fields in the filter",
1832 )
1833 1 @click.pass_context
1834 1 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 0     logger.debug("")
1840 0     nsi_show(ctx, name, literal, filter)
1841
1842
1843 1 def nsi_op_show(ctx, id, filter):
1844 0     logger.debug("")
1845     # try:
1846 0     check_client_version(ctx.obj, ctx.command.name)
1847 0     op_info = ctx.obj.nsi.get_op(id)
1848     # except ClientException as e:
1849     #     print(str(e))
1850     #     exit(1)
1851
1852 0     table = PrettyTable(["field", "value"])
1853 0     for k, v in list(op_info.items()):
1854 0         if not filter or k in filter:
1855 0             table.add_row([k, json.dumps(v, indent=2)])
1856 0     table.align = "l"
1857 0     print(table)
1858
1859
1860 1 @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 1 @click.argument("id")
1865 1 @click.option(
1866     "--filter",
1867     multiple=True,
1868     help="restricts the information to the fields in the filter",
1869 )
1870 1 @click.pass_context
1871 1 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 0     logger.debug("")
1877 0     nsi_op_show(ctx, id, filter)
1878
1879
1880 1 @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 1 @click.argument("id")
1885 1 @click.option(
1886     "--filter",
1887     multiple=True,
1888     help="restricts the information to the fields in the filter",
1889 )
1890 1 @click.pass_context
1891 1 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 0     logger.debug("")
1897 0     nsi_op_show(ctx, id, filter)
1898
1899
1900 1 @cli_osm.command(
1901     name="pdu-show", short_help="shows the content of a Physical Deployment Unit (PDU)"
1902 )
1903 1 @click.argument("name")
1904 1 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
1905 1 @click.option(
1906     "--filter",
1907     multiple=True,
1908     help="restricts the information to the fields in the filter",
1909 )
1910 1 @click.pass_context
1911 1 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 0     logger.debug("")
1917     # try:
1918 0     check_client_version(ctx.obj, ctx.command.name)
1919 0     pdu = ctx.obj.pdu.get(name)
1920     # except ClientException as e:
1921     #     print(str(e))
1922     #     exit(1)
1923
1924 0     if literal:
1925 0         print(yaml.safe_dump(pdu, indent=4, default_flow_style=False))
1926 0         return
1927
1928 0     table = PrettyTable(["field", "value"])
1929
1930 0     for k, v in list(pdu.items()):
1931 0         if not filter or k in filter:
1932 0             table.add_row([k, json.dumps(v, indent=2)])
1933
1934 0     table.align = "l"
1935 0     print(table)
1936
1937
1938 ####################
1939 # CREATE operations
1940 ####################
1941
1942
1943 1 def nsd_create(ctx, filename, overwrite, skip_charm_build, repo, vendor, version):
1944 0     logger.debug("")
1945     # try:
1946 0     check_client_version(ctx.obj, ctx.command.name)
1947 0     if repo:
1948 0         filename = ctx.obj.osmrepo.get_pkg("ns", filename, repo, vendor, version)
1949 0     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 1 @cli_osm.command(name="nsd-create", short_help="creates a new NSD/NSpkg")
1956 1 @click.argument("filename")
1957 1 @click.option(
1958     "--overwrite",
1959     "overwrite",
1960     default=None,  # hidden=True,
1961     help="Deprecated. Use override",
1962 )
1963 1 @click.option(
1964     "--override",
1965     "overwrite",
1966     default=None,
1967     help="overrides fields in descriptor, format: "
1968     '"key1.key2...=value[;key3...=value;...]"',
1969 )
1970 1 @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 1 @click.option("--repo", default=None, help="[repository]: Repository name")
1977 1 @click.option("--vendor", default=None, help="[repository]: filter by vendor]")
1978 1 @click.option(
1979     "--version",
1980     default="latest",
1981     help="[repository]: filter by version. Default: latest",
1982 )
1983 1 @click.pass_context
1984 1 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 0     logger.debug("")
1993 0     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 1 @cli_osm.command(name="nspkg-create", short_help="creates a new NSD/NSpkg")
2005 1 @click.argument("filename")
2006 1 @click.option(
2007     "--overwrite",
2008     "overwrite",
2009     default=None,  # hidden=True,
2010     help="Deprecated. Use override",
2011 )
2012 1 @click.option(
2013     "--override",
2014     "overwrite",
2015     default=None,
2016     help="overrides fields in descriptor, format: "
2017     '"key1.key2...=value[;key3...=value;...]"',
2018 )
2019 1 @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 1 @click.option("--repo", default=None, help="[repository]: Repository name")
2026 1 @click.option("--vendor", default=None, help="[repository]: filter by vendor]")
2027 1 @click.option(
2028     "--version",
2029     default="latest",
2030     help="[repository]: filter by version. Default: latest",
2031 )
2032 1 @click.pass_context
2033 1 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 0     logger.debug("")
2041 0     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 1 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 0     logger.debug("")
2065     # try:
2066 0     check_client_version(ctx.obj, ctx.command.name)
2067 0     if repo:
2068 0         filename = ctx.obj.osmrepo.get_pkg("vnf", filename, repo, vendor, version)
2069 0     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 1 @cli_osm.command(name="vnfd-create", short_help="creates a new VNFD/VNFpkg")
2083 1 @click.argument("filename")
2084 1 @click.option(
2085     "--overwrite", "overwrite", default=None, help="overwrite deprecated, use override"
2086 )
2087 1 @click.option(
2088     "--override",
2089     "overwrite",
2090     default=None,
2091     help="overrides fields in descriptor, format: "
2092     '"key1.key2...=value[;key3...=value;...]"',
2093 )
2094 1 @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 1 @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 1 @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 1 @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 1 @click.option("--repo", default=None, help="[repository]: Repository name")
2122 1 @click.option("--vendor", default=None, help="[repository]: filter by vendor]")
2123 1 @click.option(
2124     "--version",
2125     default="latest",
2126     help="[repository]: filter by version. Default: latest",
2127 )
2128 1 @click.pass_context
2129 1 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 0     logger.debug("")
2148 0     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 1 @cli_osm.command(name="vnfpkg-create", short_help="creates a new VNFD/VNFpkg")
2163 1 @click.argument("filename")
2164 1 @click.option(
2165     "--overwrite",
2166     "overwrite",
2167     default=None,  # hidden=True,
2168     help="Deprecated. Use override",
2169 )
2170 1 @click.option(
2171     "--override",
2172     "overwrite",
2173     default=None,
2174     help="overrides fields in descriptor, format: "
2175     '"key1.key2...=value[;key3...=value;...]"',
2176 )
2177 1 @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 1 @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 1 @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 1 @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 1 @click.option("--repo", default=None, help="[repository]: Repository name")
2205 1 @click.option("--vendor", default=None, help="[repository]: filter by vendor]")
2206 1 @click.option(
2207     "--version",
2208     default="latest",
2209     help="[repository]: filter by version. Default: latest",
2210 )
2211 1 @click.pass_context
2212 1 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 0     logger.debug("")
2231 0     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 1 @cli_osm.command(name="nfpkg-create", short_help="creates a new NFpkg")
2246 1 @click.argument("filename")
2247 1 @click.option(
2248     "--overwrite",
2249     "overwrite",
2250     default=None,  # hidden=True,
2251     help="Deprecated. Use override",
2252 )
2253 1 @click.option(
2254     "--override",
2255     "overwrite",
2256     default=None,
2257     help="overrides fields in descriptor, format: "
2258     '"key1.key2...=value[;key3...=value;...]"',
2259 )
2260 1 @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 1 @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 1 @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 1 @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 1 @click.option("--repo", default=None, help="[repository]: Repository name")
2288 1 @click.option("--vendor", default=None, help="[repository]: filter by vendor]")
2289 1 @click.option(
2290     "--version",
2291     default="latest",
2292     help="[repository]: filter by version. Default: latest",
2293 )
2294 1 @click.pass_context
2295 1 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 0     logger.debug("")
2315 0     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 1 @cli_osm.command(name="ns-create", short_help="creates a new Network Service instance")
2330 1 @click.option("--ns_name", prompt=True, help="name of the NS instance")
2331 1 @click.option("--nsd_name", prompt=True, help="name of the NS descriptor")
2332 1 @click.option(
2333     "--vim_account",
2334     prompt=True,
2335     help="default VIM account id or name for the deployment",
2336 )
2337 1 @click.option("--admin_status", default="ENABLED", help="administration status")
2338 1 @click.option(
2339     "--ssh_keys",
2340     default=None,
2341     help="comma separated list of public key files to inject to vnfs",
2342 )
2343 1 @click.option("--config", default=None, help="ns specific yaml configuration")
2344 1 @click.option("--config_file", default=None, help="ns specific yaml configuration file")
2345 1 @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 1 @click.pass_context
2354 1 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 0     logger.debug("")
2367     # try:
2368 0     if config_file:
2369 0         check_client_version(ctx.obj, "--config_file")
2370 0         if config:
2371 0             raise ClientException(
2372                 '"--config" option is incompatible with "--config_file" option'
2373             )
2374 0         with open(config_file, "r") as cf:
2375 0             config = cf.read()
2376 0     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 1 def nst_create(ctx, filename, overwrite):
2390 0     logger.debug("")
2391     # try:
2392 0     check_client_version(ctx.obj, ctx.command.name)
2393 0     ctx.obj.nst.create(filename, overwrite)
2394     # except ClientException as e:
2395     #     print(str(e))
2396     #     exit(1)
2397
2398
2399 1 @cli_osm.command(
2400     name="nst-create", short_help="creates a new Network Slice Template (NST)"
2401 )
2402 1 @click.argument("filename")
2403 1 @click.option(
2404     "--overwrite",
2405     "overwrite",
2406     default=None,  # hidden=True,
2407     help="Deprecated. Use override",
2408 )
2409 1 @click.option(
2410     "--override",
2411     "overwrite",
2412     default=None,
2413     help="overrides fields in descriptor, format: "
2414     '"key1.key2...=value[;key3...=value;...]"',
2415 )
2416 1 @click.pass_context
2417 1 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 0     logger.debug("")
2423 0     nst_create(ctx, filename, overwrite)
2424
2425
2426 1 @cli_osm.command(
2427     name="netslice-template-create",
2428     short_help="creates a new Network Slice Template (NST)",
2429 )
2430 1 @click.argument("filename")
2431 1 @click.option(
2432     "--overwrite",
2433     "overwrite",
2434     default=None,  # hidden=True,
2435     help="Deprecated. Use override",
2436 )
2437 1 @click.option(
2438     "--override",
2439     "overwrite",
2440     default=None,
2441     help="overrides fields in descriptor, format: "
2442     '"key1.key2...=value[;key3...=value;...]"',
2443 )
2444 1 @click.pass_context
2445 1 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 0     logger.debug("")
2451 0     nst_create(ctx, filename, overwrite)
2452
2453
2454 1 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 0     logger.debug("")
2459     # try:
2460 0     check_client_version(ctx.obj, ctx.command.name)
2461 0     if config_file:
2462 0         if config:
2463 0             raise ClientException(
2464                 '"--config" option is incompatible with "--config_file" option'
2465             )
2466 0         with open(config_file, "r") as cf:
2467 0             config = cf.read()
2468 0     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 1 @cli_osm.command(name="nsi-create", short_help="creates a new Network Slice Instance")
2482 1 @click.option("--nsi_name", prompt=True, help="name of the Network Slice Instance")
2483 1 @click.option("--nst_name", prompt=True, help="name of the Network Slice Template")
2484 1 @click.option(
2485     "--vim_account",
2486     prompt=True,
2487     help="default VIM account id or name for the deployment",
2488 )
2489 1 @click.option(
2490     "--ssh_keys", default=None, help="comma separated list of keys to inject to vnfs"
2491 )
2492 1 @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 1 @click.option(
2506     "--config_file", default=None, help="nsi specific yaml configuration file"
2507 )
2508 1 @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 1 @click.pass_context
2517 1 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 0     logger.debug("")
2522 0     nsi_create(
2523         ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file, wait=wait
2524     )
2525
2526
2527 1 @cli_osm.command(
2528     name="netslice-instance-create", short_help="creates a new Network Slice Instance"
2529 )
2530 1 @click.option("--nsi_name", prompt=True, help="name of the Network Slice Instance")
2531 1 @click.option("--nst_name", prompt=True, help="name of the Network Slice Template")
2532 1 @click.option(
2533     "--vim_account",
2534     prompt=True,
2535     help="default VIM account id or name for the deployment",
2536 )
2537 1 @click.option(
2538     "--ssh_keys", default=None, help="comma separated list of keys to inject to vnfs"
2539 )
2540 1 @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 1 @click.option(
2552     "--config_file", default=None, help="nsi specific yaml configuration file"
2553 )
2554 1 @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 1 @click.pass_context
2563 1 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 0     logger.debug("")
2568 0     nsi_create(
2569         ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file, wait=wait
2570     )
2571
2572
2573 1 @cli_osm.command(
2574     name="pdu-create", short_help="adds a new Physical Deployment Unit to the catalog"
2575 )
2576 1 @click.option("--name", help="name of the Physical Deployment Unit")
2577 1 @click.option("--pdu_type", help="type of PDU (e.g. router, firewall, FW001)")
2578 1 @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 1 @click.option("--description", help="human readable description")
2585 1 @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 1 @click.option(
2593     "--descriptor_file",
2594     default=None,
2595     help="PDU descriptor file (as an alternative to using the other arguments)",
2596 )
2597 1 @click.pass_context
2598 1 def pdu_create(
2599     ctx, name, pdu_type, interface, description, vim_account, descriptor_file
2600 ):
2601     """creates a new Physical Deployment Unit (PDU)"""
2602 0     logger.debug("")
2603
2604 0     check_client_version(ctx.obj, ctx.command.name)
2605
2606 0     pdu = create_pdu_dictionary(
2607         name, pdu_type, interface, description, vim_account, descriptor_file
2608     )
2609 0     ctx.obj.pdu.create(pdu)
2610
2611
2612 ########################
2613 # UPDATE PDU operation #
2614 ########################
2615
2616
2617 1 @cli_osm.command(
2618     name="pdu-update", short_help="updates a Physical Deployment Unit to the catalog"
2619 )
2620 1 @click.argument("name")
2621 1 @click.option("--newname", help="New name for the Physical Deployment Unit")
2622 1 @click.option("--pdu_type", help="type of PDU (e.g. router, firewall, FW001)")
2623 1 @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 1 @click.option("--description", help="human readable description")
2630 1 @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 1 @click.option(
2638     "--descriptor_file",
2639     default=None,
2640     help="PDU descriptor file (as an alternative to using the other arguments)",
2641 )
2642 1 @click.pass_context
2643 1 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 0     logger.debug("")
2648
2649 0     check_client_version(ctx.obj, ctx.command.name)
2650
2651 0     update = True
2652
2653 0     if not newname:
2654 0         newname = name
2655
2656 0     pdu = create_pdu_dictionary(
2657         newname, pdu_type, interface, description, vim_account, descriptor_file, update
2658     )
2659 0     ctx.obj.pdu.update(name, pdu)
2660
2661
2662 1 def create_pdu_dictionary(
2663     name, pdu_type, interface, description, vim_account, descriptor_file, update=False
2664 ):
2665
2666 0     logger.debug("")
2667 0     pdu = {}
2668
2669 0     if not descriptor_file:
2670 0         if not update:
2671 0             if not name:
2672 0                 raise ClientException(
2673                     'in absence of descriptor file, option "--name" is mandatory'
2674                 )
2675 0             if not pdu_type:
2676 0                 raise ClientException(
2677                     'in absence of descriptor file, option "--pdu_type" is mandatory'
2678                 )
2679 0             if not interface:
2680 0                 raise ClientException(
2681                     'in absence of descriptor file, option "--interface" is mandatory (at least once)'
2682                 )
2683 0             if not vim_account:
2684 0                 raise ClientException(
2685                     'in absence of descriptor file, option "--vim_account" is mandatory (at least once)'
2686                 )
2687     else:
2688 0         with open(descriptor_file, "r") as df:
2689 0             pdu = yaml.safe_load(df.read())
2690 0     if name:
2691 0         pdu["name"] = name
2692 0     if pdu_type:
2693 0         pdu["type"] = pdu_type
2694 0     if description:
2695 0         pdu["description"] = description
2696 0     if vim_account:
2697 0         pdu["vim_accounts"] = vim_account
2698 0     if interface:
2699 0         ifaces_list = []
2700 0         for iface in interface:
2701 0             new_iface = {k: v for k, v in [i.split("=") for i in iface.split(",")]}
2702 0             new_iface["mgmt"] = new_iface.get("mgmt", "false").lower() == "true"
2703 0             ifaces_list.append(new_iface)
2704 0         pdu["interfaces"] = ifaces_list
2705 0     return pdu
2706
2707
2708 ####################
2709 # UPDATE operations
2710 ####################
2711
2712
2713 1 def nsd_update(ctx, name, content):
2714 0     logger.debug("")
2715     # try:
2716 0     check_client_version(ctx.obj, ctx.command.name)
2717 0     ctx.obj.nsd.update(name, content)
2718     # except ClientException as e:
2719     #     print(str(e))
2720     #     exit(1)
2721
2722
2723 1 @cli_osm.command(name="nsd-update", short_help="updates a NSD/NSpkg")
2724 1 @click.argument("name")
2725 1 @click.option(
2726     "--content",
2727     default=None,
2728     help="filename with the NSD/NSpkg replacing the current one",
2729 )
2730 1 @click.pass_context
2731 1 def nsd_update1(ctx, name, content):
2732     """updates a NSD/NSpkg
2733
2734     NAME: name or ID of the NSD/NSpkg
2735     """
2736 0     logger.debug("")
2737 0     nsd_update(ctx, name, content)
2738
2739
2740 1 @cli_osm.command(name="nspkg-update", short_help="updates a NSD/NSpkg")
2741 1 @click.argument("name")
2742 1 @click.option(
2743     "--content",
2744     default=None,
2745     help="filename with the NSD/NSpkg replacing the current one",
2746 )
2747 1 @click.pass_context
2748 1 def nsd_update2(ctx, name, content):
2749     """updates a NSD/NSpkg
2750
2751     NAME: name or ID of the NSD/NSpkg
2752     """
2753 0     logger.debug("")
2754 0     nsd_update(ctx, name, content)
2755
2756
2757 1 def vnfd_update(ctx, name, content):
2758 0     logger.debug("")
2759     # try:
2760 0     check_client_version(ctx.obj, ctx.command.name)
2761 0     ctx.obj.vnfd.update(name, content)
2762     # except ClientException as e:
2763     #     print(str(e))
2764     #     exit(1)
2765
2766
2767 1 @cli_osm.command(name="vnfd-update", short_help="updates a new VNFD/VNFpkg")
2768 1 @click.argument("name")
2769 1 @click.option(
2770     "--content",
2771     default=None,
2772     help="filename with the VNFD/VNFpkg replacing the current one",
2773 )
2774 1 @click.pass_context
2775 1 def vnfd_update1(ctx, name, content):
2776     """updates a VNFD/VNFpkg
2777
2778     NAME: name or ID of the VNFD/VNFpkg
2779     """
2780 0     logger.debug("")
2781 0     vnfd_update(ctx, name, content)
2782
2783
2784 1 @cli_osm.command(name="vnfpkg-update", short_help="updates a VNFD/VNFpkg")
2785 1 @click.argument("name")
2786 1 @click.option(
2787     "--content",
2788     default=None,
2789     help="filename with the VNFD/VNFpkg replacing the current one",
2790 )
2791 1 @click.pass_context
2792 1 def vnfd_update2(ctx, name, content):
2793     """updates a VNFD/VNFpkg
2794
2795     NAME: VNFD yaml file or VNFpkg tar.gz file
2796     """
2797 0     logger.debug("")
2798 0     vnfd_update(ctx, name, content)
2799
2800
2801 1 @cli_osm.command(name="nfpkg-update", short_help="updates a NFpkg")
2802 1 @click.argument("name")
2803 1 @click.option(
2804     "--content", default=None, help="filename with the NFpkg replacing the current one"
2805 )
2806 1 @click.pass_context
2807 1 def nfpkg_update(ctx, name, content):
2808     """updates a NFpkg
2809
2810     NAME: NF Descriptor yaml file or NFpkg tar.gz file
2811     """
2812 0     logger.debug("")
2813 0     vnfd_update(ctx, name, content)
2814
2815
2816 1 def nst_update(ctx, name, content):
2817 0     logger.debug("")
2818     # try:
2819 0     check_client_version(ctx.obj, ctx.command.name)
2820 0     ctx.obj.nst.update(name, content)
2821     # except ClientException as e:
2822     #     print(str(e))
2823     #     exit(1)
2824
2825
2826 1 @cli_osm.command(name="nst-update", short_help="updates a Network Slice Template (NST)")
2827 1 @click.argument("name")
2828 1 @click.option(
2829     "--content",
2830     default=None,
2831     help="filename with the NST/NSTpkg replacing the current one",
2832 )
2833 1 @click.pass_context
2834 1 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 0     logger.debug("")
2840 0     nst_update(ctx, name, content)
2841
2842
2843 1 @cli_osm.command(
2844     name="netslice-template-update", short_help="updates a Network Slice Template (NST)"
2845 )
2846 1 @click.argument("name")
2847 1 @click.option(
2848     "--content",
2849     default=None,
2850     help="filename with the NST/NSTpkg replacing the current one",
2851 )
2852 1 @click.pass_context
2853 1 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 0     logger.debug("")
2859 0     nst_update(ctx, name, content)
2860
2861
2862 ####################
2863 # DELETE operations
2864 ####################
2865
2866
2867 1 def nsd_delete(ctx, name, force):
2868 0     logger.debug("")
2869     # try:
2870 0     if not force:
2871 0         ctx.obj.nsd.delete(name)
2872     else:
2873 0         check_client_version(ctx.obj, "--force")
2874 0         ctx.obj.nsd.delete(name, force)
2875     # except ClientException as e:
2876     #     print(str(e))
2877     #     exit(1)
2878
2879
2880 1 @cli_osm.command(name="nsd-delete", short_help="deletes a NSD/NSpkg")
2881 1 @click.argument("name")
2882 1 @click.option(
2883     "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
2884 )
2885 1 @click.pass_context
2886 1 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 0     logger.debug("")
2892 0     nsd_delete(ctx, name, force)
2893
2894
2895 1 @cli_osm.command(name="nspkg-delete", short_help="deletes a NSD/NSpkg")
2896 1 @click.argument("name")
2897 1 @click.option(
2898     "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
2899 )
2900 1 @click.pass_context
2901 1 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 0     logger.debug("")
2907 0     nsd_delete(ctx, name, force)
2908
2909
2910 1 def vnfd_delete(ctx, name, force):
2911 0     logger.debug("")
2912     # try:
2913 0     if not force:
2914 0         ctx.obj.vnfd.delete(name)
2915     else:
2916 0         check_client_version(ctx.obj, "--force")
2917 0         ctx.obj.vnfd.delete(name, force)
2918     # except ClientException as e:
2919     #     print(str(e))
2920     #     exit(1)
2921
2922
2923 1 @cli_osm.command(name="vnfd-delete", short_help="deletes a VNFD/VNFpkg")
2924 1 @click.argument("name")
2925 1 @click.option(
2926     "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
2927 )
2928 1 @click.pass_context
2929 1 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 0     logger.debug("")
2935 0     vnfd_delete(ctx, name, force)
2936
2937
2938 1 @cli_osm.command(name="vnfpkg-delete", short_help="deletes a VNFD/VNFpkg")
2939 1 @click.argument("name")
2940 1 @click.option(
2941     "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
2942 )
2943 1 @click.pass_context
2944 1 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 0     logger.debug("")
2950 0     vnfd_delete(ctx, name, force)
2951
2952
2953 1 @cli_osm.command(name="nfpkg-delete", short_help="deletes a NFpkg")
2954 1 @click.argument("name")
2955 1 @click.option(
2956     "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
2957 )
2958 1 @click.pass_context
2959 1 def nfpkg_delete(ctx, name, force):
2960     """deletes a NFpkg
2961
2962     NAME: name or ID of the NFpkg to be deleted
2963     """
2964 0     logger.debug("")
2965 0     vnfd_delete(ctx, name, force)
2966
2967
2968 1 @cli_osm.command(name="ns-delete", short_help="deletes a NS instance")
2969 1 @click.argument("name")
2970 1 @click.option(
2971     "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
2972 )
2973 1 @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 1 @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 1 @click.pass_context
2988 1 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 0     logger.debug("")
2994     # try:
2995 0     if not force:
2996 0         ctx.obj.ns.delete(name, config=config, wait=wait)
2997     else:
2998 0         check_client_version(ctx.obj, "--force")
2999 0         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 1 def nst_delete(ctx, name, force):
3006 0     logger.debug("")
3007     # try:
3008 0     check_client_version(ctx.obj, ctx.command.name)
3009 0     ctx.obj.nst.delete(name, force)
3010     # except ClientException as e:
3011     #     print(str(e))
3012     #     exit(1)
3013
3014
3015 1 @cli_osm.command(name="nst-delete", short_help="deletes a Network Slice Template (NST)")
3016 1 @click.argument("name")
3017 1 @click.option(
3018     "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
3019 )
3020 1 @click.pass_context
3021 1 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 0     logger.debug("")
3027 0     nst_delete(ctx, name, force)
3028
3029
3030 1 @cli_osm.command(
3031     name="netslice-template-delete", short_help="deletes a Network Slice Template (NST)"
3032 )
3033 1 @click.argument("name")
3034 1 @click.option(
3035     "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
3036 )
3037 1 @click.pass_context
3038 1 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 0     logger.debug("")
3044 0     nst_delete(ctx, name, force)
3045
3046
3047 1 def nsi_delete(ctx, name, force, wait):
3048 0     logger.debug("")
3049     # try:
3050 0     check_client_version(ctx.obj, ctx.command.name)
3051 0     ctx.obj.nsi.delete(name, force, wait=wait)
3052     # except ClientException as e:
3053     #     print(str(e))
3054     #     exit(1)
3055
3056
3057 1 @cli_osm.command(name="nsi-delete", short_help="deletes a Network Slice Instance (NSI)")
3058 1 @click.argument("name")
3059 1 @click.option(
3060     "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
3061 )
3062 1 @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 1 @click.pass_context
3071 1 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 0     logger.debug("")
3077 0     nsi_delete(ctx, name, force, wait=wait)
3078
3079
3080 1 @cli_osm.command(
3081     name="netslice-instance-delete", short_help="deletes a Network Slice Instance (NSI)"
3082 )
3083 1 @click.argument("name")
3084 1 @click.option(
3085     "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
3086 )
3087 1 @click.pass_context
3088 1 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 0     logger.debug("")
3094 0     nsi_delete(ctx, name, force, wait=wait)
3095
3096
3097 1 @cli_osm.command(
3098     name="pdu-delete", short_help="deletes a Physical Deployment Unit (PDU)"
3099 )
3100 1 @click.argument("name")
3101 1 @click.option(
3102     "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
3103 )
3104 1 @click.pass_context
3105 1 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 0     logger.debug("")
3111     # try:
3112 0     check_client_version(ctx.obj, ctx.command.name)
3113 0     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 1 @cli_osm.command(name="vim-create", short_help="creates a new VIM account")
3125 1 @click.option("--name", required=True, help="Name to create datacenter")
3126 1 @click.option("--user", default=None, help="VIM username")
3127 1 @click.option("--password", default=None, help="VIM password")
3128 1 @click.option("--auth_url", default=None, help="VIM url")
3129 1 @click.option("--tenant", "--project", "tenant", default=None, help="VIM tenant/project name")
3130 1 @click.option("--config", default=None, help="VIM specific config parameters")
3131 1 @click.option("--config_file", default=None, help="VIM specific config parameters in YAML or JSON file")
3132 1 @click.option("--account_type", default="openstack", help="VIM type")
3133 1 @click.option("--description", default=None, help="human readable description")
3134 1 @click.option(
3135     "--sdn_controller",
3136     default=None,
3137     help="Name or id of the SDN controller associated to this VIM account",
3138 )
3139 1 @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 1 @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 1 @click.option("--vca", default=None, help="VCA to be used in this VIM account")
3153 1 @click.option("--creds", default=None, help="credentials file (only applycable for GCP VIM type)")
3154 1 @click.pass_context
3155 1 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 0     logger.debug("")
3174     # try:
3175 0     if sdn_controller:
3176 0         check_client_version(ctx.obj, "--sdn_controller")
3177 0     if sdn_port_mapping:
3178 0         check_client_version(ctx.obj, "--sdn_port_mapping")
3179 0     vim = {}
3180 0     vim["vim-username"] = user
3181 0     vim["vim-password"] = password
3182 0     vim["vim-url"] = auth_url
3183 0     vim["vim-tenant-name"] = tenant
3184 0     vim["vim-type"] = account_type
3185 0     vim["description"] = description
3186 0     if vca:
3187 0         vim["vca"] = vca
3188 0     vim_config = create_config(config_file, config)
3189 0     if creds:
3190 0         with open(creds, "r") as cf:
3191 0             vim_config["credentials"] = yaml.safe_load(cf.read())
3192 0     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 1 @cli_osm.command(name="vim-update", short_help="updates a VIM account")
3199 1 @click.argument("name")
3200 1 @click.option("--newname", help="New name for the VIM account")
3201 1 @click.option("--user", help="VIM username")
3202 1 @click.option("--password", help="VIM password")
3203 1 @click.option("--auth_url", help="VIM url")
3204 1 @click.option("--tenant", help="VIM tenant name")
3205 1 @click.option("--config", help="VIM specific config parameters")
3206 1 @click.option("--config_file", default=None, help="VIM specific config parameters in YAML or JSON file")
3207 1 @click.option("--account_type", help="VIM type")
3208 1 @click.option("--description", help="human readable description")
3209 1 @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 1 @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 1 @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 1 @click.option("--creds", default=None, help="credentials file (only applycable for GCP VIM type)")
3229 1 @click.pass_context
3230 1 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 0     logger.debug("")
3252     # try:
3253 0     check_client_version(ctx.obj, ctx.command.name)
3254 0     vim = {}
3255 0     if newname:
3256 0         vim["name"] = newname
3257 0     if user:
3258 0         vim["vim_user"] = user
3259 0     if password:
3260 0         vim["vim_password"] = password
3261 0     if auth_url:
3262 0         vim["vim_url"] = auth_url
3263 0     if tenant:
3264 0         vim["vim-tenant-name"] = tenant
3265 0     if account_type:
3266 0         vim["vim_type"] = account_type
3267 0     if description:
3268 0         vim["description"] = description
3269 0     vim_config = None
3270 0     if config or config_file:
3271 0         vim_config = create_config(config_file, config)
3272 0     if creds:
3273 0         with open(creds, "r") as cf:
3274 0             vim_config["credentials"] = yaml.safe_load(cf.read())
3275 0     logger.info(f"VIM: {vim}, VIM config: {vim_config}")
3276 0     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 1 @cli_osm.command(name="vim-delete", short_help="deletes a VIM account")
3283 1 @click.argument("name")
3284 1 @click.option(
3285     "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
3286 )
3287 1 @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 1 @click.pass_context
3296 1 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 0     logger.debug("")
3302     # try:
3303 0     if not force:
3304 0         ctx.obj.vim.delete(name, wait=wait)
3305     else:
3306 0         check_client_version(ctx.obj, "--force")
3307 0         ctx.obj.vim.delete(name, force, wait=wait)
3308     # except ClientException as e:
3309     #     print(str(e))
3310     #     exit(1)
3311
3312
3313 1 @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 1 @click.option(
3318     "--filter",
3319     default=None,
3320     multiple=True,
3321     help="restricts the list to the VIM accounts matching the filter",
3322 )
3323 1 @click.option(
3324     "--long",
3325     is_flag=True,
3326     help="get more details of the NS (project, vim, deployment status, configuration status.",
3327 )
3328 1 @click.pass_context
3329 1 def vim_list(ctx, filter, long):
3330     """list all VIM accounts"""
3331 0     logger.debug("")
3332 0     if filter:
3333 0         filter = "&".join(filter)
3334 0         check_client_version(ctx.obj, "--filter")
3335     #    if ro_update:
3336     #        check_client_version(ctx.obj, '--ro_update', 'v1')
3337 0     fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
3338 0     if fullclassname == "osmclient.sol005.client.Client":
3339 0         resp = ctx.obj.vim.list(filter)
3340     #    else:
3341     #        resp = ctx.obj.vim.list(ro_update)
3342 0     if long:
3343 0         table = PrettyTable(
3344             ["vim name", "uuid", "project", "operational state", "error details"]
3345         )
3346 0         project_list = ctx.obj.project.list()
3347     else:
3348 0         table = PrettyTable(["vim name", "uuid", "operational state"])
3349 0     for vim in resp:
3350 0         if long:
3351 0             if "vim_password" in vim:
3352 0                 vim["vim_password"] = "********"
3353 0             if "config" in vim and "credentials" in vim["config"]:
3354 0                 vim["config"]["credentials"] = "********"
3355 0             logger.debug("VIM details: {}".format(yaml.safe_dump(vim)))
3356 0             vim_state = vim["_admin"].get("operationalState", "-")
3357 0             error_details = "N/A"
3358 0             if vim_state == "ERROR":
3359 0                 error_details = vim["_admin"].get("detailed-status", "Not found")
3360 0             project_id, project_name = get_project(project_list, vim)
3361             # project_info = '{} ({})'.format(project_name, project_id)
3362 0             project_info = project_name
3363 0             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 0             table.add_row(
3374                 [vim["name"], vim["uuid"], vim["_admin"].get("operationalState", "-")]
3375             )
3376 0     table.align = "l"
3377 0     print(table)
3378
3379
3380 1 @cli_osm.command(name="vim-show", short_help="shows the details of a VIM account")
3381 1 @click.argument("name")
3382 1 @click.option(
3383     "--filter",
3384     multiple=True,
3385     help="restricts the information to the fields in the filter",
3386 )
3387 1 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
3388 1 @click.pass_context
3389 1 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 0     logger.debug("")
3395     # try:
3396 0     resp = ctx.obj.vim.get(name)
3397 0     if "vim_password" in resp:
3398 0         resp["vim_password"] = "********"
3399 0     if "config" in resp and "credentials" in resp["config"]:
3400 0         resp["config"]["credentials"] = "********"
3401     # except ClientException as e:
3402     #     print(str(e))
3403     #     exit(1)
3404
3405 0     if literal:
3406 0         print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
3407 0         return
3408 0     table = PrettyTable(["key", "attribute"])
3409 0     for k, v in list(resp.items()):
3410 0         if not filter or k in filter:
3411 0             table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)])
3412 0     table.align = "l"
3413 0     print(table)
3414
3415
3416 ####################
3417 # WIM operations
3418 ####################
3419
3420
3421 1 @cli_osm.command(name="wim-create", short_help="creates a new WIM account")
3422 1 @click.option("--name", prompt=True, help="Name for the WIM account")
3423 1 @click.option("--user", help="WIM username")
3424 1 @click.option("--password", help="WIM password")
3425 1 @click.option("--url", prompt=True, help="WIM url")
3426 # @click.option('--tenant',
3427 #               help='wIM tenant name')
3428 1 @click.option("--config", default=None, help="WIM specific config parameters")
3429 1 @click.option("--wim_type", help="WIM type")
3430 1 @click.option("--description", default=None, help="human readable description")
3431 1 @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 1 @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 1 @click.pass_context
3446 1 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 0     logger.debug("")
3461     # try:
3462 0     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 0     wim = {}
3468 0     if user:
3469 0         wim["user"] = user
3470 0     if password:
3471 0         wim["password"] = password
3472 0     if url:
3473 0         wim["wim_url"] = url
3474     # if tenant: wim['tenant'] = tenant
3475 0     wim["wim_type"] = wim_type
3476 0     if description:
3477 0         wim["description"] = description
3478 0     if config:
3479 0         wim["config"] = config
3480 0     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 1 @cli_osm.command(name="wim-update", short_help="updates a WIM account")
3487 1 @click.argument("name")
3488 1 @click.option("--newname", help="New name for the WIM account")
3489 1 @click.option("--user", help="WIM username")
3490 1 @click.option("--password", help="WIM password")
3491 1 @click.option("--url", help="WIM url")
3492 1 @click.option("--config", help="WIM specific config parameters")
3493 1 @click.option("--wim_type", help="WIM type")
3494 1 @click.option("--description", help="human readable description")
3495 1 @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 1 @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 1 @click.pass_context
3509 1 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 0     logger.debug("")
3527     # try:
3528 0     check_client_version(ctx.obj, ctx.command.name)
3529 0     wim = {}
3530 0     if newname:
3531 0         wim["name"] = newname
3532 0     if user:
3533 0         wim["user"] = user
3534 0     if password:
3535 0         wim["password"] = password
3536 0     if url:
3537 0         wim["url"] = url
3538     # if tenant: wim['tenant'] = tenant
3539 0     if wim_type:
3540 0         wim["wim_type"] = wim_type
3541 0     if description:
3542 0         wim["description"] = description
3543 0     if config:
3544 0         wim["config"] = config
3545 0     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 1 @cli_osm.command(name="wim-delete", short_help="deletes a WIM account")
3552 1 @click.argument("name")
3553 1 @click.option(
3554     "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
3555 )
3556 1 @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 1 @click.pass_context
3564 1 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 0     logger.debug("")
3570     # try:
3571 0     check_client_version(ctx.obj, ctx.command.name)
3572 0     ctx.obj.wim.delete(name, force, wait=wait)
3573     # except ClientException as e:
3574     #     print(str(e))
3575     #     exit(1)
3576
3577
3578 1 @cli_osm.command(name="wim-list", short_help="list all WIM accounts")
3579 1 @click.option(
3580     "--filter",
3581     default=None,
3582     multiple=True,
3583     help="restricts the list to the WIM accounts matching the filter",
3584 )
3585 1 @click.pass_context
3586 1 def wim_list(ctx, filter):
3587     """list all WIM accounts"""
3588 0     logger.debug("")
3589     # try:
3590 0     check_client_version(ctx.obj, ctx.command.name)
3591 0     if filter:
3592 0         filter = "&".join(filter)
3593 0     resp = ctx.obj.wim.list(filter)
3594 0     table = PrettyTable(["wim name", "uuid"])
3595 0     for wim in resp:
3596 0         table.add_row([wim["name"], wim["uuid"]])
3597 0     table.align = "l"
3598 0     print(table)
3599     # except ClientException as e:
3600     #     print(str(e))
3601     #     exit(1)
3602
3603
3604 1 @cli_osm.command(name="wim-show", short_help="shows the details of a WIM account")
3605 1 @click.argument("name")
3606 1 @click.pass_context
3607 1 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 0     logger.debug("")
3613     # try:
3614 0     check_client_version(ctx.obj, ctx.command.name)
3615 0     resp = ctx.obj.wim.get(name)
3616 0     if "password" in resp:
3617 0         resp["wim_password"] = "********"
3618     # except ClientException as e:
3619     #     print(str(e))
3620     #     exit(1)
3621
3622 0     table = PrettyTable(["key", "attribute"])
3623 0     for k, v in list(resp.items()):
3624 0         table.add_row([k, json.dumps(v, indent=2)])
3625 0     table.align = "l"
3626 0     print(table)
3627
3628
3629 ####################
3630 # SDN controller operations
3631 ####################
3632
3633
3634 1 @cli_osm.command(name="sdnc-create", short_help="creates a new SDN controller")
3635 1 @click.option("--name", prompt=True, help="Name to create sdn controller")
3636 1 @click.option("--type", prompt=True, help="SDN controller type")
3637 1 @click.option(
3638     "--sdn_controller_version",  # hidden=True,
3639     help="Deprecated. Use --config {version: sdn_controller_version}",
3640 )
3641 1 @click.option("--url", help="URL in format http[s]://HOST:IP/")
3642 1 @click.option("--ip_address", help="Deprecated. Use --url")  # hidden=True,
3643 1 @click.option("--port", help="Deprecated. Use --url")  # hidden=True,
3644 1 @click.option(
3645     "--switch_dpid", help="Deprecated. Use --config {switch_id: DPID}"  # hidden=True,
3646 )
3647 1 @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 1 @click.option("--user", help="SDN controller username")
3653 1 @click.option(
3654     "--password",
3655     hide_input=True,
3656     confirmation_prompt=True,
3657     help="SDN controller password",
3658 )
3659 1 @click.option("--description", default=None, help="human readable description")
3660 1 @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 1 @click.pass_context
3668 1 def sdnc_create(ctx, **kwargs):
3669     """creates a new SDN controller"""
3670 0     logger.debug("")
3671 0     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 0     if kwargs.get("port"):
3677 0         print("option '--port' is deprecated, use '--url' instead")
3678 0         sdncontroller["port"] = int(kwargs["port"])
3679 0     if kwargs.get("ip_address"):
3680 0         print("option '--ip_address' is deprecated, use '--url' instead")
3681 0         sdncontroller["ip"] = kwargs["ip_address"]
3682 0     if kwargs.get("switch_dpid"):
3683 0         print(
3684             "option '--switch_dpid' is deprecated, use '--config={switch_id: id|DPID}' instead"
3685         )
3686 0         sdncontroller["dpid"] = kwargs["switch_dpid"]
3687 0     if kwargs.get("sdn_controller_version"):
3688 0         print(
3689             "option '--sdn_controller_version' is deprecated, use '--config={version: SDN_CONTROLLER_VERSION}'"
3690             " instead"
3691         )
3692     # try:
3693 0     check_client_version(ctx.obj, ctx.command.name)
3694 0     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 1 @cli_osm.command(name="sdnc-update", short_help="updates an SDN controller")
3701 1 @click.argument("name")
3702 1 @click.option("--newname", help="New name for the SDN controller")
3703 1 @click.option("--description", default=None, help="human readable description")
3704 1 @click.option("--type", help="SDN controller type")
3705 1 @click.option("--url", help="URL in format http[s]://HOST:IP/")
3706 1 @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 1 @click.option("--user", help="SDN controller username")
3713 1 @click.option("--password", help="SDN controller password")
3714 1 @click.option("--ip_address", help="Deprecated. Use --url")  # hidden=True
3715 1 @click.option("--port", help="Deprecated. Use --url")  # hidden=True
3716 1 @click.option(
3717     "--switch_dpid", help="Deprecated. Use --config {switch_dpid: DPID}"
3718 )  # hidden=True
3719 1 @click.option(
3720     "--sdn_controller_version", help="Deprecated. Use --config {version: VERSION}"
3721 )  # hidden=True
3722 1 @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 1 @click.pass_context
3730 1 def sdnc_update(ctx, **kwargs):
3731     """updates an SDN controller
3732
3733     NAME: name or ID of the SDN controller
3734     """
3735 0     logger.debug("")
3736 0     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 0     if kwargs.get("newname"):
3743 0         sdncontroller["name"] = kwargs["newname"]
3744 0     if kwargs.get("port"):
3745 0         print("option '--port' is deprecated, use '--url' instead")
3746 0         sdncontroller["port"] = int(kwargs["port"])
3747 0     if kwargs.get("ip_address"):
3748 0         print("option '--ip_address' is deprecated, use '--url' instead")
3749 0         sdncontroller["ip"] = kwargs["ip_address"]
3750 0     if kwargs.get("switch_dpid"):
3751 0         print(
3752             "option '--switch_dpid' is deprecated, use '--config={switch_id: id|DPID}' instead"
3753         )
3754 0         sdncontroller["dpid"] = kwargs["switch_dpid"]
3755 0     if kwargs.get("sdn_controller_version"):
3756 0         print(
3757             "option '--sdn_controller_version' is deprecated, use '---config={version: SDN_CONTROLLER_VERSION}'"
3758             " instead"
3759         )
3760
3761     # try:
3762 0     check_client_version(ctx.obj, ctx.command.name)
3763 0     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 1 @cli_osm.command(name="sdnc-delete", short_help="deletes an SDN controller")
3770 1 @click.argument("name")
3771 1 @click.option(
3772     "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
3773 )
3774 1 @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 1 @click.pass_context
3782 1 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 0     logger.debug("")
3788     # try:
3789 0     check_client_version(ctx.obj, ctx.command.name)
3790 0     ctx.obj.sdnc.delete(name, force, wait=wait)
3791     # except ClientException as e:
3792     #     print(str(e))
3793     #     exit(1)
3794
3795
3796 1 @cli_osm.command(name="sdnc-list", short_help="list all SDN controllers")
3797 1 @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 1 @click.pass_context
3804 1 def sdnc_list(ctx, filter):
3805     """list all SDN controllers"""
3806 0     logger.debug("")
3807     # try:
3808 0     check_client_version(ctx.obj, ctx.command.name)
3809 0     if filter:
3810 0         filter = "&".join(filter)
3811 0     resp = ctx.obj.sdnc.list(filter)
3812     # except ClientException as e:
3813     #     print(str(e))
3814     #     exit(1)
3815 0     table = PrettyTable(["sdnc name", "id"])
3816 0     for sdnc in resp:
3817 0         table.add_row([sdnc["name"], sdnc["_id"]])
3818 0     table.align = "l"
3819 0     print(table)
3820
3821
3822 1 @cli_osm.command(name="sdnc-show", short_help="shows the details of an SDN controller")
3823 1 @click.argument("name")
3824 1 @click.pass_context
3825 1 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 0     logger.debug("")
3831     # try:
3832 0     check_client_version(ctx.obj, ctx.command.name)
3833 0     resp = ctx.obj.sdnc.get(name)
3834     # except ClientException as e:
3835     #     print(str(e))
3836     #     exit(1)
3837
3838 0     table = PrettyTable(["key", "attribute"])
3839 0     for k, v in list(resp.items()):
3840 0         table.add_row([k, json.dumps(v, indent=2)])
3841 0     table.align = "l"
3842 0     print(table)
3843
3844
3845 ###########################
3846 # K8s cluster operations
3847 ###########################
3848
3849
3850 1 @cli_osm.command(name="k8scluster-add", short_help="adds a K8s cluster to OSM")
3851 1 @click.argument("name")
3852 1 @click.option(
3853     "--creds", prompt=True, help="credentials file, i.e. a valid `.kube/config` file"
3854 )
3855 1 @click.option("--version", prompt=True, help="Kubernetes version")
3856 1 @click.option(
3857     "--vim", prompt=True, help="VIM target, the VIM where the cluster resides"
3858 )
3859 1 @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 1 @click.option("--description", default=None, help="human readable description")
3866 1 @click.option(
3867     "--namespace",
3868     default="kube-system",
3869     help="namespace to be used for its operation, defaults to `kube-system`",
3870 )
3871 1 @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 1 @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 1 @click.pass_context
3891 1 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 0     check_client_version(ctx.obj, ctx.command.name)
3900 0     cluster = {}
3901 0     cluster["name"] = name
3902 0     with open(creds, "r") as cf:
3903 0         cluster["credentials"] = yaml.safe_load(cf.read())
3904 0     cluster["k8s_version"] = version
3905 0     cluster["vim_account"] = vim
3906 0     cluster["nets"] = yaml.safe_load(k8s_nets)
3907 0     if description:
3908 0         cluster["description"] = description
3909 0     if namespace:
3910 0         cluster["namespace"] = namespace
3911 0     if cni:
3912 0         cluster["cni"] = yaml.safe_load(cni)
3913 0     ctx.obj.k8scluster.create(name, cluster, wait)
3914     # except ClientException as e:
3915     #     print(str(e))
3916     #     exit(1)
3917
3918
3919 1 @cli_osm.command(name="k8scluster-update", short_help="updates a K8s cluster")
3920 1 @click.argument("name")
3921 1 @click.option("--newname", help="New name for the K8s cluster")
3922 1 @click.option("--creds", help="credentials file, i.e. a valid `.kube/config` file")
3923 1 @click.option("--version", help="Kubernetes version")
3924 1 @click.option("--vim", help="VIM target, the VIM where the cluster resides")
3925 1 @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 1 @click.option("--description", help="human readable description")
3931 1 @click.option(
3932     "--namespace",
3933     help="namespace to be used for its operation, defaults to `kube-system`",
3934 )
3935 1 @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 1 @click.option(
3944     "--cni", help="list of CNI plugins, in JSON inline format, used in the cluster"
3945 )
3946 1 @click.pass_context
3947 1 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 0     check_client_version(ctx.obj, ctx.command.name)
3956 0     cluster = {}
3957 0     if newname:
3958 0         cluster["name"] = newname
3959 0     if creds:
3960 0         with open(creds, "r") as cf:
3961 0             cluster["credentials"] = yaml.safe_load(cf.read())
3962 0     if version:
3963 0         cluster["k8s_version"] = version
3964 0     if vim:
3965 0         cluster["vim_account"] = vim
3966 0     if k8s_nets:
3967 0         cluster["nets"] = yaml.safe_load(k8s_nets)
3968 0     if description:
3969 0         cluster["description"] = description
3970 0     if namespace:
3971 0         cluster["namespace"] = namespace
3972 0     if cni:
3973 0         cluster["cni"] = yaml.safe_load(cni)
3974 0     ctx.obj.k8scluster.update(name, cluster, wait)
3975     # except ClientException as e:
3976     #     print(str(e))
3977     #     exit(1)
3978
3979
3980 1 @cli_osm.command(name="k8scluster-delete", short_help="deletes a K8s cluster")
3981 1 @click.argument("name")
3982 1 @click.option(
3983     "--force", is_flag=True, help="forces the deletion from the DB (not recommended)"
3984 )
3985 1 @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 1 @click.pass_context
3994 1 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 0     check_client_version(ctx.obj, ctx.command.name)
4001 0     ctx.obj.k8scluster.delete(name, force, wait)
4002     # except ClientException as e:
4003     #     print(str(e))
4004     #     exit(1)
4005
4006
4007 1 @cli_osm.command(name="k8scluster-list")
4008 1 @click.option(
4009     "--filter",
4010     default=None,
4011     multiple=True,
4012     help="restricts the list to the K8s clusters matching the filter",
4013 )
4014 1 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
4015 1 @click.option("--long", is_flag=True, help="get more details")
4016 1 @click.pass_context
4017 1 def k8scluster_list(ctx, filter, literal, long):
4018     """list all K8s clusters"""
4019     # try:
4020 0     check_client_version(ctx.obj, ctx.command.name)
4021 0     if filter:
4022 0         filter = "&".join(filter)
4023 0     resp = ctx.obj.k8scluster.list(filter)
4024 0     if literal:
4025 0         print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
4026 0         return
4027 0     if long:
4028 0         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 0         project_list = ctx.obj.project.list()
4043     else:
4044 0         table = PrettyTable(
4045             ["Name", "Id", "VIM", "Operational State", "Op. state details"]
4046         )
4047 0     try:
4048 0         vim_list = ctx.obj.vim.list()
4049 0     except Exception:
4050 0         vim_list = []
4051 0     for cluster in resp:
4052 0         logger.debug("Cluster details: {}".format(yaml.safe_dump(cluster)))
4053 0         vim_name = get_vim_name(vim_list, cluster["vim_account"])
4054         # vim_info = '{} ({})'.format(vim_name,cluster['vim_account'])
4055 0         vim_info = vim_name
4056 0         op_state_details = "Helm: {}\nJuju: {}".format(
4057             cluster["_admin"].get("helm-chart", {}).get("operationalState", "-"),
4058             cluster["_admin"].get("juju-bundle", {}).get("operationalState", "-"),
4059         )
4060 0         if long:
4061 0             project_id, project_name = get_project(project_list, cluster)
4062             # project_info = '{} ({})'.format(project_name, project_id)
4063 0             project_info = project_name
4064 0             detailed_status = cluster["_admin"].get("detailed-status", "-")
4065 0             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 0             table.add_row(
4081                 [
4082                     cluster["name"],
4083                     cluster["_id"],
4084                     vim_info,
4085                     cluster["_admin"]["operationalState"],
4086                     op_state_details,
4087                 ]
4088             )
4089 0     table.align = "l"
4090 0     print(table)
4091     # except ClientException as e:
4092     #     print(str(e))
4093     #     exit(1)
4094
4095
4096 1 @cli_osm.command(
4097     name="k8scluster-show", short_help="shows the details of a K8s cluster"
4098 )
4099 1 @click.argument("name")
4100 1 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
4101 1 @click.pass_context
4102 1 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 0     resp = ctx.obj.k8scluster.get(name)
4109 0     if literal:
4110 0         print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
4111 0         return
4112 0     table = PrettyTable(["key", "attribute"])
4113 0     for k, v in list(resp.items()):
4114 0         table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)])
4115 0     table.align = "l"
4116 0     print(table)
4117     # except ClientException as e:
4118     #     print(str(e))
4119     #     exit(1)
4120
4121
4122 ###########################
4123 # VCA operations
4124 ###########################
4125
4126
4127 1 @cli_osm.command(name="vca-add", short_help="adds a VCA (Juju controller) to OSM")
4128 1 @click.argument("name")
4129 1 @click.option(
4130     "--endpoints",
4131     prompt=True,
4132     help="Comma-separated list of IP or hostnames of the Juju controller",
4133 )
4134 1 @click.option("--user", prompt=True, help="Username with admin priviledges")
4135 1 @click.option("--secret", prompt=True, help="Password of the specified username")
4136 1 @click.option("--cacert", prompt=True, help="CA certificate")
4137 1 @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 1 @click.option(
4143     "--lxd-credentials",
4144     prompt=True,
4145     help="Name of the cloud credentialsto be used for the LXD cloud",
4146 )
4147 1 @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 1 @click.option(
4153     "--k8s-credentials",
4154     prompt=True,
4155     help="Name of the cloud credentialsto be used for the K8s cloud",
4156 )
4157 1 @click.option(
4158     "--model-config",
4159     default={},
4160     help="Configuration options for the models",
4161 )
4162 1 @click.option("--description", default=None, help="human readable description")
4163 1 @click.pass_context
4164 1 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 1     check_client_version(ctx.obj, ctx.command.name)
4183 1     vca = {}
4184 1     vca["name"] = name
4185 1     vca["endpoints"] = endpoints.split(",")
4186 1     vca["user"] = user
4187 1     vca["secret"] = secret
4188 1     vca["cacert"] = cacert
4189 1     vca["lxd-cloud"] = lxd_cloud
4190 1     vca["lxd-credentials"] = lxd_credentials
4191 1     vca["k8s-cloud"] = k8s_cloud
4192 1     vca["k8s-credentials"] = k8s_credentials
4193 1     if description:
4194 1         vca["description"] = description
4195 1     if model_config:
4196 1         model_config = load(model_config)
4197 1         vca["model-config"] = model_config
4198 1     ctx.obj.vca.create(name, vca)
4199
4200
4201 1 def load(data: Any):
4202 1     if os.path.isfile(data):
4203 0         return load_file(data)
4204     else:
4205 1         try:
4206 1             return json.loads(data)
4207 0         except ValueError as e:
4208 0             raise ClientException(e)
4209
4210
4211 1 def load_file(file_path: str) -> Dict:
4212 0     content = None
4213 0     with open(file_path, "r") as f:
4214 0         content = f.read()
4215 0     try:
4216 0         return yaml.safe_load(content)
4217 0     except yaml.scanner.ScannerError:
4218 0         pass
4219 0     try:
4220 0         return json.loads(content)
4221 0     except ValueError:
4222 0         pass
4223 0     raise ClientException(f"{file_path} must be a valid yaml or json file")
4224
4225
4226 1 @cli_osm.command(name="vca-update", short_help="updates a K8s cluster")
4227 1 @click.argument("name")
4228 1 @click.option(
4229     "--endpoints", help="Comma-separated list of IP or hostnames of the Juju controller"
4230 )
4231 1 @click.option("--user", help="Username with admin priviledges")
4232 1 @click.option("--secret", help="Password of the specified username")
4233 1 @click.option("--cacert", help="CA certificate")
4234 1 @click.option(
4235     "--lxd-cloud",
4236     help="Name of the cloud that will be used for LXD containers (LXD proxy charms)",
4237 )
4238 1 @click.option(
4239     "--lxd-credentials",
4240     help="Name of the cloud credentialsto be used for the LXD cloud",
4241 )
4242 1 @click.option(
4243     "--k8s-cloud",
4244     help="Name of the cloud that will be used for K8s containers (K8s proxy charms)",
4245 )
4246 1 @click.option(
4247     "--k8s-credentials",
4248     help="Name of the cloud credentialsto be used for the K8s cloud",
4249 )
4250 1 @click.option(
4251     "--model-config",
4252     help="Configuration options for the models",
4253 )
4254 1 @click.option("--description", default=None, help="human readable description")
4255 1 @click.pass_context
4256 1 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 1     check_client_version(ctx.obj, ctx.command.name)
4275 1     vca = {}
4276 1     vca["name"] = name
4277 1     if endpoints:
4278 1         vca["endpoints"] = endpoints.split(",")
4279 1     if user:
4280 1         vca["user"] = user
4281 1     if secret:
4282 1         vca["secret"] = secret
4283 1     if cacert:
4284 1         vca["cacert"] = cacert
4285 1     if lxd_cloud:
4286 1         vca["lxd-cloud"] = lxd_cloud
4287 1     if lxd_credentials:
4288 1         vca["lxd-credentials"] = lxd_credentials
4289 1     if k8s_cloud:
4290 1         vca["k8s-cloud"] = k8s_cloud
4291 1     if k8s_credentials:
4292 1         vca["k8s-credentials"] = k8s_credentials
4293 1     if description:
4294 1         vca["description"] = description
4295 1     if model_config:
4296 1         model_config = load(model_config)
4297 1         vca["model-config"] = model_config
4298 1     ctx.obj.vca.update(name, vca)
4299
4300
4301 1 @cli_osm.command(name="vca-delete", short_help="deletes a K8s cluster")
4302 1 @click.argument("name")
4303 1 @click.option(
4304     "--force", is_flag=True, help="forces the deletion from the DB (not recommended)"
4305 )
4306 1 @click.pass_context
4307 1 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 1     check_client_version(ctx.obj, ctx.command.name)
4313 1     ctx.obj.vca.delete(name, force=force)
4314
4315
4316 1 @cli_osm.command(name="vca-list")
4317 1 @click.option(
4318     "--filter",
4319     default=None,
4320     multiple=True,
4321     help="restricts the list to the VCAs matching the filter",
4322 )
4323 1 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
4324 1 @click.option("--long", is_flag=True, help="get more details")
4325 1 @click.pass_context
4326 1 def vca_list(ctx, filter, literal, long):
4327     """list VCAs"""
4328 1     check_client_version(ctx.obj, ctx.command.name)
4329 1     if filter:
4330 1         filter = "&".join(filter)
4331 1     resp = ctx.obj.vca.list(filter)
4332 1     if literal:
4333 1         print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
4334 1         return
4335 1     if long:
4336 1         table = PrettyTable(
4337             ["Name", "Id", "Project", "Operational State", "Detailed Status"]
4338         )
4339 1         project_list = ctx.obj.project.list()
4340     else:
4341 1         table = PrettyTable(["Name", "Id", "Operational State"])
4342 1     for vca in resp:
4343 1         logger.debug("VCA details: {}".format(yaml.safe_dump(vca)))
4344 1         if long:
4345 1             project_id, project_name = get_project(project_list, vca)
4346 1             detailed_status = vca.get("_admin", {}).get("detailed-status", "-")
4347 1             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 1             table.add_row(
4358                 [
4359                     vca["name"],
4360                     vca["_id"],
4361                     vca.get("_admin", {}).get("operationalState", "-"),
4362                 ]
4363             )
4364 1     table.align = "l"
4365 1     print(table)
4366
4367
4368 1 @cli_osm.command(name="vca-show", short_help="shows the details of a K8s cluster")
4369 1 @click.argument("name")
4370 1 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
4371 1 @click.pass_context
4372 1 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 1     resp = ctx.obj.vca.get(name)
4379 1     if literal:
4380 1         print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
4381 1         return
4382 1     table = PrettyTable(["key", "attribute"])
4383 1     for k, v in list(resp.items()):
4384 1         table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)])
4385 1     table.align = "l"
4386 1     print(table)
4387
4388
4389 ###########################
4390 # Repo operations
4391 ###########################
4392
4393
4394 1 @cli_osm.command(name="repo-add", short_help="adds a repo to OSM")
4395 1 @click.argument("name")
4396 1 @click.argument("uri")
4397 1 @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 1 @click.option("--description", default=None, help="human readable description")
4404 1 @click.option(
4405     "--user", default=None, help="OSM repository: The username of the OSM repository"
4406 )
4407 1 @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 1 @click.pass_context
4416 1 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 0     kwargs = {k: v for k, v in kwargs.items() if v is not None}
4424 0     repo = kwargs
4425 0     repo["url"] = repo.pop("uri")
4426 0     if repo["type"] in ["helm-chart", "juju-bundle"]:
4427 0         ctx.obj.repo.create(repo["name"], repo)
4428     else:
4429 0         ctx.obj.osmrepo.create(repo["name"], repo)
4430     # except ClientException as e:
4431     #     print(str(e))
4432     #     exit(1)
4433
4434
4435 1 @cli_osm.command(name="repo-update", short_help="updates a repo in OSM")
4436 1 @click.argument("name")
4437 1 @click.option("--newname", help="New name for the repo")
4438 1 @click.option("--uri", help="URI of the repo")
4439 1 @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 1 @click.pass_context
4444 1 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 0     check_client_version(ctx.obj, ctx.command.name)
4451 0     repo = {}
4452 0     if newname:
4453 0         repo["name"] = newname
4454 0     if uri:
4455 0         repo["uri"] = uri
4456 0     if description:
4457 0         repo["description"] = description
4458 0     try:
4459 0         ctx.obj.repo.update(name, repo)
4460 0     except NotFound:
4461 0         ctx.obj.osmrepo.update(name, repo)
4462
4463     # except ClientException as e:
4464     #     print(str(e))
4465     #     exit(1)
4466
4467
4468 1 @cli_osm.command(
4469     name="repo-index", short_help="Index a repository from a folder with artifacts"
4470 )
4471 1 @click.option(
4472     "--origin", default=".", help="origin path where the artifacts are located"
4473 )
4474 1 @click.option(
4475     "--destination", default=".", help="destination path where the index is deployed"
4476 )
4477 1 @click.pass_context
4478 1 def repo_index(ctx, origin, destination):
4479     """Index a repository
4480
4481     NAME: name or ID of the repo to be deleted
4482     """
4483 0     check_client_version(ctx.obj, ctx.command.name)
4484 0     ctx.obj.osmrepo.repo_index(origin, destination)
4485
4486
4487 1 @cli_osm.command(name="repo-delete", short_help="deletes a repo")
4488 1 @click.argument("name")
4489 1 @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 1 @click.pass_context
4496 1 def repo_delete(ctx, name, force):
4497     """deletes a repo
4498
4499     NAME: name or ID of the repo to be deleted
4500     """
4501 0     logger.debug("")
4502 0     try:
4503 0         ctx.obj.repo.delete(name, force=force)
4504 0     except NotFound:
4505 0         ctx.obj.osmrepo.delete(name, force=force)
4506     # except ClientException as e:
4507     #     print(str(e))
4508     #     exit(1)
4509
4510
4511 1 @cli_osm.command(name="repo-list")
4512 1 @click.option(
4513     "--filter",
4514     default=None,
4515     multiple=True,
4516     help="restricts the list to the repos matching the filter",
4517 )
4518 1 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
4519 1 @click.pass_context
4520 1 def repo_list(ctx, filter, literal):
4521     """list all repos"""
4522     # try:
4523     # K8s Repositories
4524 0     check_client_version(ctx.obj, ctx.command.name)
4525 0     if filter:
4526 0         filter = "&".join(filter)
4527 0     resp = ctx.obj.repo.list(filter)
4528 0     resp += ctx.obj.osmrepo.list(filter)
4529 0     if literal:
4530 0         print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
4531 0         return
4532 0     table = PrettyTable(["Name", "Id", "Type", "URI", "Description"])
4533 0     for repo in resp:
4534         # cluster['k8s-nets'] = json.dumps(yaml.safe_load(cluster['k8s-nets']))
4535 0         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 0     table.align = "l"
4545 0     print(table)
4546
4547     # except ClientException as e:
4548     #     print(str(e))
4549     #     exit(1)
4550
4551
4552 1 @cli_osm.command(name="repo-show", short_help="shows the details of a repo")
4553 1 @click.argument("name")
4554 1 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
4555 1 @click.pass_context
4556 1 def repo_show(ctx, name, literal):
4557     """shows the details of a repo
4558
4559     NAME: name or ID of the repo
4560     """
4561 0     try:
4562 0         resp = ctx.obj.repo.get(name)
4563 0     except NotFound:
4564 0         resp = ctx.obj.osmrepo.get(name)
4565
4566 0     if literal:
4567 0         if resp:
4568 0             print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
4569 0         return
4570 0     table = PrettyTable(["key", "attribute"])
4571 0     if resp:
4572 0         for k, v in list(resp.items()):
4573 0             table.add_row([k, json.dumps(v, indent=2)])
4574
4575 0     table.align = "l"
4576 0     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 1 @cli_osm.command(name="project-create", short_help="creates a new project")
4588 1 @click.argument("name")
4589 # @click.option('--description',
4590 #              default='no description',
4591 #              help='human readable description')
4592 1 @click.option("--domain-name", "domain_name", default=None, help="assign to a domain")
4593 1 @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 1 @click.pass_context
4602 1 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 0     logger.debug("")
4610 0     project = {"name": name}
4611 0     if domain_name:
4612 0         project["domain_name"] = domain_name
4613 0     quotas_dict = _process_project_quotas(quotas)
4614 0     if quotas_dict:
4615 0         project["quotas"] = quotas_dict
4616
4617     # try:
4618 0     check_client_version(ctx.obj, ctx.command.name)
4619 0     ctx.obj.project.create(name, project)
4620     # except ClientException as e:
4621     #     print(str(e))
4622     #     exit(1)
4623
4624
4625 1 def _process_project_quotas(quota_list):
4626 0     quotas_dict = {}
4627 0     if not quota_list:
4628 0         return quotas_dict
4629 0     try:
4630 0         for quota in quota_list:
4631 0             for single_quota in quota.split(","):
4632 0                 k, v = single_quota.split("=")
4633 0                 quotas_dict[k] = None if v in ("None", "null", "") else int(v)
4634 0     except (ValueError, TypeError):
4635 0         raise ClientException(
4636             "invalid format for 'quotas'. Use 'k1=v1,v1=v2'. v must be a integer or null"
4637         )
4638 0     return quotas_dict
4639
4640
4641 1 @cli_osm.command(name="project-delete", short_help="deletes a project")
4642 1 @click.argument("name")
4643 # @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
4644 1 @click.pass_context
4645 1 def project_delete(ctx, name):
4646     """deletes a project
4647
4648     NAME: name or ID of the project to be deleted
4649     """
4650 0     logger.debug("")
4651     # try:
4652 0     check_client_version(ctx.obj, ctx.command.name)
4653 0     ctx.obj.project.delete(name)
4654     # except ClientException as e:
4655     #     print(str(e))
4656     #     exit(1)
4657
4658
4659 1 @cli_osm.command(name="project-list", short_help="list all projects")
4660 1 @click.option(
4661     "--filter",
4662     default=None,
4663     multiple=True,
4664     help="restricts the list to the projects matching the filter",
4665 )
4666 1 @click.pass_context
4667 1 def project_list(ctx, filter):
4668     """list all projects"""
4669 0     logger.debug("")
4670     # try:
4671 0     check_client_version(ctx.obj, ctx.command.name)
4672 0     if filter:
4673 0         filter = "&".join(filter)
4674 0     resp = ctx.obj.project.list(filter)
4675     # except ClientException as e:
4676     #     print(str(e))
4677     #     exit(1)
4678 0     table = PrettyTable(["name", "id"])
4679 0     for proj in resp:
4680 0         table.add_row([proj["name"], proj["_id"]])
4681 0     table.align = "l"
4682 0     print(table)
4683
4684
4685 1 @cli_osm.command(name="project-show", short_help="shows the details of a project")
4686 1 @click.argument("name")
4687 1 @click.pass_context
4688 1 def project_show(ctx, name):
4689     """shows the details of a project
4690
4691     NAME: name or ID of the project
4692     """
4693 0     logger.debug("")
4694     # try:
4695 0     check_client_version(ctx.obj, ctx.command.name)
4696 0     resp = ctx.obj.project.get(name)
4697     # except ClientException as e:
4698     #     print(str(e))
4699     #     exit(1)
4700
4701 0     table = PrettyTable(["key", "attribute"])
4702 0     for k, v in resp.items():
4703 0         table.add_row([k, json.dumps(v, indent=2)])
4704 0     table.align = "l"
4705 0     print(table)
4706
4707
4708 1 @cli_osm.command(
4709     name="project-update", short_help="updates a project (only the name can be updated)"
4710 )
4711 1 @click.argument("project")
4712 1 @click.option("--name", default=None, help="new name for the project")
4713 1 @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 1 @click.pass_context
4722 1 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 0     logger.debug("")
4733 0     project_changes = {}
4734 0     if name:
4735 0         project_changes["name"] = name
4736 0     quotas_dict = _process_project_quotas(quotas)
4737 0     if quotas_dict:
4738 0         project_changes["quotas"] = quotas_dict
4739
4740     # try:
4741 0     check_client_version(ctx.obj, ctx.command.name)
4742 0     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 1 @cli_osm.command(name="user-create", short_help="creates a new user")
4753 1 @click.argument("username")
4754 1 @click.option(
4755     "--password",
4756     prompt=True,
4757     hide_input=True,
4758     confirmation_prompt=True,
4759     help="user password",
4760 )
4761 1 @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 1 @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 1 @click.option("--domain-name", "domain_name", default=None, help="assign to a domain")
4778 1 @click.pass_context
4779 1 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 0     logger.debug("")
4790 0     user = {}
4791 0     user["username"] = username
4792 0     user["password"] = password
4793 0     user["projects"] = projects
4794 0     user["project_role_mappings"] = project_role_mappings
4795 0     if domain_name:
4796 0         user["domain_name"] = domain_name
4797
4798     # try:
4799 0     check_client_version(ctx.obj, ctx.command.name)
4800 0     ctx.obj.user.create(username, user)
4801     # except ClientException as e:
4802     #     print(str(e))
4803     #     exit(1)
4804
4805
4806 1 @cli_osm.command(name="user-update", short_help="updates user information")
4807 1 @click.argument("username")
4808 1 @click.option(
4809     "--password",
4810     # prompt=True,
4811     # hide_input=True,
4812     # confirmation_prompt=True,
4813     help="user password",
4814 )
4815 1 @click.option("--set-username", "set_username", default=None, help="change username")
4816 1 @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 1 @click.option(
4824     "--remove-project",
4825     "remove_project",
4826     default=None,
4827     multiple=True,
4828     help="removes project from user: 'project'",
4829 )
4830 1 @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 1 @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 1 @click.pass_context
4845 1 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 0     logger.debug("")
4867 0     user = {}
4868 0     user["password"] = password
4869 0     user["username"] = set_username
4870 0     user["set-project"] = set_project
4871 0     user["remove-project"] = remove_project
4872 0     user["add-project-role"] = add_project_role
4873 0     user["remove-project-role"] = remove_project_role
4874
4875     # try:
4876 0     check_client_version(ctx.obj, ctx.command.name)
4877 0     ctx.obj.user.update(username, user)
4878     # except ClientException as e:
4879     #     print(str(e))
4880     #     exit(1)
4881
4882
4883 1 @cli_osm.command(name="user-delete", short_help="deletes a user")
4884 1 @click.argument("name")
4885 # @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
4886 1 @click.pass_context
4887 1 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 0     logger.debug("")
4894     # try:
4895 0     check_client_version(ctx.obj, ctx.command.name)
4896 0     ctx.obj.user.delete(name)
4897     # except ClientException as e:
4898     #     print(str(e))
4899     #     exit(1)
4900
4901
4902 1 @cli_osm.command(name="user-list", short_help="list all users")
4903 1 @click.option(
4904     "--filter",
4905     default=None,
4906     multiple=True,
4907     help="restricts the list to the users matching the filter",
4908 )
4909 1 @click.pass_context
4910 1 def user_list(ctx, filter):
4911     """list all users"""
4912     # try:
4913 0     check_client_version(ctx.obj, ctx.command.name)
4914 0     if filter:
4915 0         filter = "&".join(filter)
4916 0     resp = ctx.obj.user.list(filter)
4917     # except ClientException as e:
4918     #     print(str(e))
4919     #     exit(1)
4920 0     table = PrettyTable(["name", "id"])
4921 0     for user in resp:
4922 0         table.add_row([user["username"], user["_id"]])
4923 0     table.align = "l"
4924 0     print(table)
4925
4926
4927 1 @cli_osm.command(name="user-show", short_help="shows the details of a user")
4928 1 @click.argument("name")
4929 1 @click.pass_context
4930 1 def user_show(ctx, name):
4931     """shows the details of a user
4932
4933     NAME: name or ID of the user
4934     """
4935 0     logger.debug("")
4936     # try:
4937 0     check_client_version(ctx.obj, ctx.command.name)
4938 0     resp = ctx.obj.user.get(name)
4939 0     if "password" in resp:
4940 0         resp["password"] = "********"
4941     # except ClientException as e:
4942     #     print(str(e))
4943     #     exit(1)
4944
4945 0     table = PrettyTable(["key", "attribute"])
4946 0     for k, v in resp.items():
4947 0         table.add_row([k, json.dumps(v, indent=2)])
4948 0     table.align = "l"
4949 0     print(table)
4950
4951
4952 ####################
4953 # Fault Management operations
4954 ####################
4955
4956
4957 1 @cli_osm.command(name="ns-alarm-create")
4958 1 @click.argument("name")
4959 1 @click.option("--ns", prompt=True, help="NS instance id or name")
4960 1 @click.option(
4961     "--vnf", prompt=True, help="VNF name (VNF member index as declared in the NSD)"
4962 )
4963 1 @click.option("--vdu", prompt=True, help="VDU name (VDU name as declared in the VNFD)")
4964 1 @click.option("--metric", prompt=True, help="Name of the metric (e.g. cpu_utilization)")
4965 1 @click.option(
4966     "--severity",
4967     default="WARNING",
4968     help="severity of the alarm (WARNING, MINOR, MAJOR, CRITICAL, INDETERMINATE)",
4969 )
4970 1 @click.option(
4971     "--threshold_value",
4972     prompt=True,
4973     help="threshold value that, when crossed, an alarm is triggered",
4974 )
4975 1 @click.option(
4976     "--threshold_operator",
4977     prompt=True,
4978     help="threshold operator describing the comparison (GE, LE, GT, LT, EQ)",
4979 )
4980 1 @click.option(
4981     "--statistic",
4982     default="AVERAGE",
4983     help="statistic (AVERAGE, MINIMUM, MAXIMUM, COUNT, SUM)",
4984 )
4985 1 @click.pass_context
4986 1 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 0     logger.debug("")
5002     # try:
5003 0     ns_instance = ctx.obj.ns.get(ns)
5004 0     alarm = {}
5005 0     alarm["alarm_name"] = name
5006 0     alarm["ns_id"] = ns_instance["_id"]
5007 0     alarm["correlation_id"] = ns_instance["_id"]
5008 0     alarm["vnf_member_index"] = vnf
5009 0     alarm["vdu_name"] = vdu
5010 0     alarm["metric_name"] = metric
5011 0     alarm["severity"] = severity
5012 0     alarm["threshold_value"] = int(threshold_value)
5013 0     alarm["operation"] = threshold_operator
5014 0     alarm["statistic"] = statistic
5015 0     check_client_version(ctx.obj, ctx.command.name)
5016 0     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 1 @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 1 @click.option("--ns", prompt=True, help="NS instance id or name")
5048 1 @click.option(
5049     "--vnf", prompt=True, help="VNF name (VNF member index as declared in the NSD)"
5050 )
5051 1 @click.option("--vdu", prompt=True, help="VDU name (VDU name as declared in the VNFD)")
5052 1 @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 1 @click.option(
5056     "--interval", help="periodic interval (seconds) to export metrics continuously"
5057 )
5058 1 @click.pass_context
5059 1 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 0     logger.debug("")
5064     # try:
5065 0     ns_instance = ctx.obj.ns.get(ns)
5066 0     metric_data = {}
5067 0     metric_data["ns_id"] = ns_instance["_id"]
5068 0     metric_data["correlation_id"] = ns_instance["_id"]
5069 0     metric_data["vnf_member_index"] = vnf
5070 0     metric_data["vdu_name"] = vdu
5071 0     metric_data["metric_name"] = metric
5072 0     metric_data["collection_unit"] = "WEEK"
5073 0     metric_data["collection_period"] = 1
5074 0     check_client_version(ctx.obj, ctx.command.name)
5075 0     if not interval:
5076 0         print("{}".format(ctx.obj.ns.export_metric(metric_data)))
5077     else:
5078 0         i = 1
5079         while True:
5080 0             print("{} {}".format(ctx.obj.ns.export_metric(metric_data), i))
5081 0             time.sleep(int(interval))
5082 0             i += 1
5083     # except ClientException as e:
5084     #     print(str(e))
5085     #     exit(1)
5086
5087
5088 #################
5089 # Subscription operations
5090 #################
5091
5092
5093 1 @cli_osm.command(
5094     name="subscription-create",
5095     short_help="creates a new subscription to a specific event",
5096 )
5097 1 @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 1 @click.option("--event", default=None, help="specific yaml configuration for the event")
5104 1 @click.option(
5105     "--event_file", default=None, help="specific yaml configuration file for the event"
5106 )
5107 1 @click.pass_context
5108 1 def subscription_create(ctx, event_type, event, event_file):
5109     """creates a new subscription to a specific event"""
5110 0     logger.debug("")
5111 0     check_client_version(ctx.obj, ctx.command.name)
5112 0     if event_file:
5113 0         if event:
5114 0             raise ClientException(
5115                 '"--event" option is incompatible with "--event_file" option'
5116             )
5117 0         with open(event_file, "r") as cf:
5118 0             event = cf.read()
5119 0     ctx.obj.subscription.create(event_type, event)
5120
5121
5122 1 @cli_osm.command(name="subscription-delete", short_help="deletes a subscription")
5123 1 @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 1 @click.argument("subscription_id")
5130 1 @click.option(
5131     "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
5132 )
5133 1 @click.pass_context
5134 1 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 0     logger.debug("")
5140 0     check_client_version(ctx.obj, ctx.command.name)
5141 0     ctx.obj.subscription.delete(event_type, subscription_id, force)
5142
5143
5144 1 @cli_osm.command(name="subscription-list", short_help="list all subscriptions")
5145 1 @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 1 @click.option(
5152     "--filter",
5153     default=None,
5154     multiple=True,
5155     help="restricts the list to the subscriptions matching the filter",
5156 )
5157 1 @click.pass_context
5158 1 def subscription_list(ctx, event_type, filter):
5159     """list all subscriptions"""
5160 0     logger.debug("")
5161 0     check_client_version(ctx.obj, ctx.command.name)
5162 0     if filter:
5163 0         filter = "&".join(filter)
5164 0     resp = ctx.obj.subscription.list(event_type, filter)
5165 0     table = PrettyTable(["id", "filter", "CallbackUri"])
5166 0     for sub in resp:
5167 0         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 0     table.align = "l"
5175 0     print(table)
5176
5177
5178 1 @cli_osm.command(
5179     name="subscription-show", short_help="shows the details of a subscription"
5180 )
5181 1 @click.argument("subscription_id")
5182 1 @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 1 @click.option(
5189     "--filter",
5190     multiple=True,
5191     help="restricts the information to the fields in the filter",
5192 )
5193 1 @click.pass_context
5194 1 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 0     logger.debug("")
5200     # try:
5201 0     resp = ctx.obj.subscription.get(subscription_id)
5202 0     table = PrettyTable(["key", "attribute"])
5203 0     for k, v in list(resp.items()):
5204 0         if not filter or k in filter:
5205 0             table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)])
5206 0     table.align = "l"
5207 0     print(table)
5208
5209
5210 ####################
5211 # Other operations
5212 ####################
5213
5214
5215 1 @cli_osm.command(name="version", short_help="shows client and server versions")
5216 1 @click.pass_context
5217 1 def get_version(ctx):
5218     """shows client and server versions"""
5219     # try:
5220 0     check_client_version(ctx.obj, "version")
5221 0     print("Server version: {}".format(ctx.obj.get_version()))
5222 0     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 1 @cli_osm.command(
5231     name="upload-package", short_help="uploads a VNF package or NS package"
5232 )
5233 1 @click.argument("filename")
5234 1 @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 1 @click.pass_context
5241 1 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 0     logger.debug("")
5247     # try:
5248 0     ctx.obj.package.upload(filename, skip_charm_build=skip_charm_build)
5249 0     fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
5250 0     if fullclassname != "osmclient.sol005.client.Client":
5251 0         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 1 @cli_osm.command(
5408     name="ns-action", short_help="executes an action/primitive over a NS instance"
5409 )
5410 1 @click.argument("ns_name")
5411 1 @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 1 @click.option("--kdu_name", default=None, help="kdu-name if the target is a kdu)")
5417 1 @click.option("--vdu_id", default=None, help="vdu-id if the target is a vdu")
5418 1 @click.option(
5419     "--vdu_count", default=None, type=int, help="number of vdu instance of this vdu_id"
5420 )
5421 1 @click.option("--action_name", prompt=True, help="action name")
5422 1 @click.option("--params", default=None, help="action params in YAML/JSON inline string")
5423 1 @click.option("--params_file", default=None, help="YAML/JSON file with action params")
5424 1 @click.option(
5425     "--timeout", required=False, default=None, type=int, help="timeout in seconds"
5426 )
5427 1 @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 1 @click.pass_context
5435 1 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 0     logger.debug("")
5453     # try:
5454 0     check_client_version(ctx.obj, ctx.command.name)
5455 0     op_data = {}
5456 0     if vnf_name:
5457 0         op_data["member_vnf_index"] = vnf_name
5458 0     if kdu_name:
5459 0         op_data["kdu_name"] = kdu_name
5460 0     if vdu_id:
5461 0         op_data["vdu_id"] = vdu_id
5462 0     if vdu_count is not None:
5463 0         op_data["vdu_count_index"] = vdu_count
5464 0     if timeout:
5465 0         op_data["timeout_ns_action"] = timeout
5466 0     op_data["primitive"] = action_name
5467 0     if params_file:
5468 0         with open(params_file, "r") as pf:
5469 0             params = pf.read()
5470 0     if params:
5471 0         op_data["primitive_params"] = yaml.safe_load(params)
5472     else:
5473 0         op_data["primitive_params"] = {}
5474 0     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 1 @cli_osm.command(
5482     name="vnf-scale", short_help="executes a VNF scale (adding/removing VDUs)"
5483 )
5484 1 @click.argument("ns_name")
5485 1 @click.argument("vnf_name")
5486 1 @click.option(
5487     "--scaling-group", prompt=True, help="scaling-group-descriptor name to use"
5488 )
5489 1 @click.option(
5490     "--scale-in", default=False, is_flag=True, help="performs a scale in operation"
5491 )
5492 1 @click.option(
5493     "--scale-out",
5494     default=False,
5495     is_flag=True,
5496     help="performs a scale out operation (by default)",
5497 )
5498 1 @click.option(
5499     "--timeout", required=False, default=None, type=int, help="timeout in seconds"
5500 )
5501 1 @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 1 @click.pass_context
5509 1 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 0     logger.debug("")
5520     # try:
5521 0     check_client_version(ctx.obj, ctx.command.name)
5522 0     if not scale_in and not scale_out:
5523 0         scale_out = True
5524 0     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 1 @cli_osm.command(name="alarm-show", short_help="show alarm details")
5533 1 @click.argument("uuid")
5534 1 @click.pass_context
5535 1 def alarm_show(ctx, uuid):
5536     """Show alarm's detail information"""
5537
5538 0     check_client_version(ctx.obj, ctx.command.name)
5539 0     resp = ctx.obj.ns.get_alarm(uuid=uuid)
5540 0     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 0     table = PrettyTable(["key", "attribute"])
5554 0     try:
5555         # Arrange and return the response data
5556 0         resp = resp.replace("ObjectId", "")
5557 0         alarm = eval(resp)
5558 0         for key in alarm_filter:
5559 0             if key == "uuid":
5560 0                 value = alarm.get(key)
5561 0                 key = "alarm-id"
5562 0             elif key == "name":
5563 0                 value = alarm.get(key)
5564 0                 key = "alarm-name"
5565 0             elif key == "ns-id":
5566 0                 value = alarm["tags"].get("ns_id")
5567 0             elif key == "vdu_name":
5568 0                 value = alarm["tags"].get("vdu_name")
5569 0             elif key == "status":
5570 0                 value = alarm["alarm_status"]
5571             else:
5572 0                 value = alarm[key]
5573 0             table.add_row([key, wrap_text(text=json.dumps(value, indent=2), width=100)])
5574 0         table.align = "l"
5575 0         print(table)
5576 0     except Exception:
5577 0         print(resp)
5578
5579
5580 # List alarm
5581 1 @cli_osm.command(name="alarm-list", short_help="list all alarms")
5582 1 @click.option(
5583     "--ns_id", default=None, required=False, help="List out alarm for given ns id"
5584 )
5585 1 @click.pass_context
5586 1 def alarm_list(ctx, ns_id):
5587     """list all alarm"""
5588
5589 0     check_client_version(ctx.obj, ctx.command.name)
5590 0     project_name = os.getenv("OSM_PROJECT", "admin")
5591 0     resp = ctx.obj.ns.get_alarm(project_name=project_name, ns_id=ns_id)
5592
5593 0     table = PrettyTable(
5594         ["alarm-id", "metric", "threshold", "operation", "action", "status"]
5595     )
5596 0     if resp:
5597         # return the response data in a table
5598 0         resp = resp.replace("ObjectId", "")
5599 0         resp = eval(resp)
5600 0         for alarm in resp:
5601 0             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 0     table.align = "l"
5612 0     print(table)
5613
5614
5615 # Update alarm
5616 1 @cli_osm.command(name="alarm-update", short_help="Update a alarm")
5617 1 @click.argument("uuid")
5618 1 @click.option("--threshold", default=None, help="Alarm threshold")
5619 1 @click.option("--is_enable", default=None, type=bool, help="enable or disable alarm")
5620 1 @click.pass_context
5621 1 def alarm_update(ctx, uuid, threshold, is_enable):
5622     """
5623     Update alarm
5624
5625     """
5626 0     if not threshold and is_enable is None:
5627 0         raise ClientException(
5628             "Please provide option to update i.e threshold or is_enable"
5629         )
5630 0     ctx.obj.ns.update_alarm(uuid, threshold, is_enable)
5631
5632
5633 ##############################
5634 # Role Management Operations #
5635 ##############################
5636
5637
5638 1 @cli_osm.command(name="role-create", short_help="creates a new role")
5639 1 @click.argument("name")
5640 1 @click.option("--permissions", default=None, help="role permissions using a dictionary")
5641 1 @click.pass_context
5642 1 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 0     logger.debug("")
5651     # try:
5652 0     check_client_version(ctx.obj, ctx.command.name)
5653 0     ctx.obj.role.create(name, permissions)
5654     # except ClientException as e:
5655     #     print(str(e))
5656     #     exit(1)
5657
5658
5659 1 @cli_osm.command(name="role-update", short_help="updates a role")
5660 1 @click.argument("name")
5661 1 @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 1 @click.option(
5666     "--add",
5667     default=None,
5668     help="yaml format dictionary with permission: True/False to access grant/denial",
5669 )
5670 1 @click.option("--remove", default=None, help="yaml format list to remove a permission")
5671 1 @click.pass_context
5672 1 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 0     logger.debug("")
5683     # try:
5684 0     check_client_version(ctx.obj, ctx.command.name)
5685 0     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 1 @cli_osm.command(name="role-delete", short_help="deletes a role")
5692 1 @click.argument("name")
5693 # @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
5694 1 @click.pass_context
5695 1 def role_delete(ctx, name):
5696     """
5697     Deletes a role.
5698
5699     \b
5700     NAME: Name or ID of the role.
5701     """
5702 0     logger.debug("")
5703     # try:
5704 0     check_client_version(ctx.obj, ctx.command.name)
5705 0     ctx.obj.role.delete(name)
5706     # except ClientException as e:
5707     #     print(str(e))
5708     #     exit(1)
5709
5710
5711 1 @cli_osm.command(name="role-list", short_help="list all roles")
5712 1 @click.option(
5713     "--filter",
5714     default=None,
5715     multiple=True,
5716     help="restricts the list to the projects matching the filter",
5717 )
5718 1 @click.pass_context
5719 1 def role_list(ctx, filter):
5720     """
5721     List all roles.
5722     """
5723 0     logger.debug("")
5724     # try:
5725 0     check_client_version(ctx.obj, ctx.command.name)
5726 0     if filter:
5727 0         filter = "&".join(filter)
5728 0     resp = ctx.obj.role.list(filter)
5729     # except ClientException as e:
5730     #     print(str(e))
5731     #     exit(1)
5732 0     table = PrettyTable(["name", "id"])
5733 0     for role in resp:
5734 0         table.add_row([role["name"], role["_id"]])
5735 0     table.align = "l"
5736 0     print(table)
5737
5738
5739 1 @cli_osm.command(name="role-show", short_help="show specific role")
5740 1 @click.argument("name")
5741 1 @click.pass_context
5742 1 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 0     logger.debug("")
5750     # try:
5751 0     check_client_version(ctx.obj, ctx.command.name)
5752 0     resp = ctx.obj.role.get(name)
5753     # except ClientException as e:
5754     #     print(str(e))
5755     #     exit(1)
5756
5757 0     table = PrettyTable(["key", "attribute"])
5758 0     for k, v in resp.items():
5759 0         table.add_row([k, json.dumps(v, indent=2)])
5760 0     table.align = "l"
5761 0     print(table)
5762
5763
5764 1 @cli_osm.command(name="package-create", short_help="Create empty NS package structure")
5765 1 @click.argument("package-type")
5766 1 @click.argument("package-name")
5767 1 @click.option(
5768     "--base-directory",
5769     default=".",
5770     help=('(NS/VNF/NST) Set the location for package creation. Default: "."'),
5771 )
5772 1 @click.option(
5773     "--image",
5774     default="image-name",
5775     help='(VNF) Set the name of the vdu image. Default "image-name"',
5776 )
5777 1 @click.option(
5778     "--vdus", default=1, help="(VNF) Set the number of vdus in a VNF. Default 1"
5779 )
5780 1 @click.option(
5781     "--vcpu", default=1, help="(VNF) Set the number of virtual CPUs in a vdu. Default 1"
5782 )
5783 1 @click.option(
5784     "--memory",
5785     default=1024,
5786     help="(VNF) Set the memory size (MB) of the vdu. Default 1024",
5787 )
5788 1 @click.option(
5789     "--storage", default=10, help="(VNF) Set the disk size (GB) of the vdu. Default 10"
5790 )
5791 1 @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 1 @click.option(
5797     "--vendor", default="OSM", help='(NS/VNF) Set the descriptor vendor. Default "OSM"'
5798 )
5799 1 @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 1 @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 1 @click.option(
5812     "--netslice-subnets", default=1, help="(NST) Number of netslice subnets. Default 1"
5813 )
5814 1 @click.option(
5815     "--netslice-vlds", default=1, help="(NST) Number of netslice vlds. Default 1"
5816 )
5817 1 @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 1 @click.pass_context
5824 1 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 0     logger.debug("")
5852 0     check_client_version(ctx.obj, ctx.command.name)
5853 0     print(
5854         "Creating the {} structure: {}/{}".format(
5855             package_type.upper(), base_directory, package_name
5856         )
5857     )
5858 0     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 0     print(resp)
5876     # except ClientException as inst:
5877     #     print("ERROR: {}".format(inst))
5878     #     exit(1)
5879
5880
5881 1 @cli_osm.command(
5882     name="package-validate", short_help="Validate descriptors given a base directory"
5883 )
5884 1 @click.argument("base-directory", default=".", required=False)
5885 1 @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 1 @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 1 @click.pass_context
5898 1 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 0     logger.debug("")
5907 0     check_client_version(ctx.obj, ctx.command.name)
5908 0     results = ctx.obj.package_tool.validate(base_directory, recursive, old)
5909 0     table = PrettyTable()
5910 0     table.field_names = ["TYPE", "PATH", "VALID", "ERROR"]
5911     # Print the dictionary generated by the validation function
5912 0     for result in results:
5913 0         table.add_row(
5914             [result["type"], result["path"], result["valid"], result["error"]]
5915         )
5916 0     table.sortby = "VALID"
5917 0     table.align["PATH"] = "l"
5918 0     table.align["TYPE"] = "l"
5919 0     table.align["ERROR"] = "l"
5920 0     print(table)
5921     # except ClientException as inst:
5922     #     print("ERROR: {}".format(inst))
5923     #     exit(1)
5924
5925
5926 1 @cli_osm.command(
5927     name="package-translate", short_help="Translate descriptors given a base directory"
5928 )
5929 1 @click.argument("base-directory", default=".", required=False)
5930 1 @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 1 @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 1 @click.pass_context
5943 1 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 0     logger.debug("")
5951 0     check_client_version(ctx.obj, ctx.command.name)
5952 0     results = ctx.obj.package_tool.translate(base_directory, recursive, dryrun)
5953 0     table = PrettyTable()
5954 0     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 0     for result in results:
5964 0         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 0     table.sortby = "TRANSLATED"
5975 0     table.align["PATH"] = "l"
5976 0     table.align["TYPE"] = "l"
5977 0     table.align["ERROR"] = "l"
5978 0     print(table)
5979     # except ClientException as inst:
5980     #     print("ERROR: {}".format(inst))
5981     #     exit(1)
5982
5983
5984 1 @cli_osm.command(name="package-build", short_help="Build the tar.gz of the package")
5985 1 @click.argument("package-folder")
5986 1 @click.option(
5987     "--skip-validation", default=False, is_flag=True, help="skip package validation"
5988 )
5989 1 @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 1 @click.pass_context
5996 1 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 0     logger.debug("")
6005 0     check_client_version(ctx.obj, ctx.command.name)
6006 0     results = ctx.obj.package_tool.build(
6007         package_folder,
6008         skip_validation=skip_validation,
6009         skip_charm_build=skip_charm_build,
6010     )
6011 0     print(results)
6012     # except ClientException as inst:
6013     #     print("ERROR: {}".format(inst))
6014     #     exit(1)
6015
6016
6017 1 @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 1 @click.argument("descriptor-file", required=True)
6022 1 @click.pass_context
6023 1 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 0     logger.debug("")
6031 0     check_client_version(ctx.obj, ctx.command.name)
6032 0     result = ctx.obj.package_tool.descriptor_translate(descriptor_file)
6033 0     print(result)
6034
6035
6036 1 def cli():
6037 0     try:
6038 0         cli_osm()
6039 0         exit(0)
6040 0     except pycurl.error as exc:
6041 0         print(exc)
6042 0         print(
6043             'Maybe "--hostname" option or OSM_HOSTNAME environment variable needs to be specified'
6044         )
6045 0     except ClientException as exc:
6046 0         print("ERROR: {}".format(exc))
6047 0     except (FileNotFoundError, PermissionError) as exc:
6048 0         print("Cannot open file: {}".format(exc))
6049 0     except yaml.YAMLError as exc:
6050 0         print("Invalid YAML format: {}".format(exc))
6051 0     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 1 if __name__ == "__main__":
6057 0     cli()