allow extra domain input for token authentication
[osm/osmclient.git] / osmclient / scripts / osm.py
1 # Copyright 2017-2018 Sandvine
2 # Copyright 2018 Telefonica
3 #
4 # All Rights Reserved.
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License"); you may
7 # not use this file except in compliance with the License. You may obtain
8 # a copy of the License at
9 #
10 # http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15 # License for the specific language governing permissions and limitations
16 # under the License.
17 """
18 OSM shell/cli
19 """
20
21 import click
22 from osmclient import client
23 from osmclient.common.exceptions import ClientException
24 from prettytable import PrettyTable
25 import yaml
26 import json
27 import time
28 import pycurl
29 import os
30 import textwrap
31 import pkg_resources
32 import logging
33 from datetime import datetime
34
35
36 # Global variables
37
38 CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'], max_content_width=160)
39
40 def wrap_text(text, width):
41 wrapper = textwrap.TextWrapper(width=width)
42 lines = text.splitlines()
43 return "\n".join(map(wrapper.fill, lines))
44
45
46 def trunc_text(text, length):
47 if len(text) > length:
48 return text[:(length - 3)] + '...'
49 else:
50 return text
51
52
53 def check_client_version(obj, what, version='sol005'):
54 """
55 Checks the version of the client object and raises error if it not the expected.
56
57 :param obj: the client object
58 :what: the function or command under evaluation (used when an error is raised)
59 :return: -
60 :raises ClientError: if the specified version does not match the client version
61 """
62 logger.debug("")
63 fullclassname = obj.__module__ + "." + obj.__class__.__name__
64 message = 'The following commands or options are only supported with the option "--sol005": {}'.format(what)
65 if version == 'v1':
66 message = 'The following commands or options are not supported when using option "--sol005": {}'.format(what)
67 if fullclassname != 'osmclient.{}.client.Client'.format(version):
68 raise ClientException(message)
69 return
70
71
72 @click.group(context_settings=dict(help_option_names=['-h', '--help'], max_content_width=160))
73 @click.option('--hostname',
74 default="127.0.0.1",
75 envvar='OSM_HOSTNAME',
76 help='hostname of server. ' +
77 'Also can set OSM_HOSTNAME in environment')
78 #@click.option('--sol005/--no-sol005',
79 # default=True,
80 # envvar='OSM_SOL005',
81 # help='Use ETSI NFV SOL005 API (default) or the previous SO API. ' +
82 # 'Also can set OSM_SOL005 in environment')
83 @click.option('--user',
84 default=None,
85 envvar='OSM_USER',
86 help='user (defaults to admin). ' +
87 'Also can set OSM_USER in environment')
88 @click.option('--password',
89 default=None,
90 envvar='OSM_PASSWORD',
91 help='password (defaults to admin). ' +
92 'Also can set OSM_PASSWORD in environment')
93 @click.option('--project',
94 default=None,
95 envvar='OSM_PROJECT',
96 help='project (defaults to admin). ' +
97 'Also can set OSM_PROJECT in environment')
98 @click.option('-v', '--verbose', count=True,
99 help='increase verbosity (-v INFO, -vv VERBOSE, -vvv DEBUG)')
100 @click.option('--all-projects',
101 default=None,
102 is_flag=True,
103 help='include all projects')
104 @click.option('--public/--no-public', default=None,
105 help='flag for public items (packages, instances, VIM accounts, etc.)')
106 @click.option('--project-domain-name', 'project_domain_name',
107 default=None,
108 envvar='OSM_PROJECT_DOMAIN_NAME',
109 help='project domain name for keystone authentication (default to None). ' +
110 'Also can set OSM_PROJECT_DOMAIN_NAME in environment')
111 @click.option('--user-domain-name', 'user_domain_name',
112 default=None,
113 envvar='OSM_USER_DOMAIN_NAME',
114 help='user domain name for keystone authentication (default to None). ' +
115 'Also can set OSM_USER_DOMAIN_NAME in environment')
116 #@click.option('--so-port',
117 # default=None,
118 # envvar='OSM_SO_PORT',
119 # help='hostname of server. ' +
120 # 'Also can set OSM_SO_PORT in environment')
121 #@click.option('--so-project',
122 # default=None,
123 # envvar='OSM_SO_PROJECT',
124 # help='Project Name in SO. ' +
125 # 'Also can set OSM_SO_PROJECT in environment')
126 #@click.option('--ro-hostname',
127 # default=None,
128 # envvar='OSM_RO_HOSTNAME',
129 # help='hostname of RO server. ' +
130 # 'Also can set OSM_RO_HOSTNAME in environment')
131 #@click.option('--ro-port',
132 # default=None,
133 # envvar='OSM_RO_PORT',
134 # help='hostname of RO server. ' +
135 # 'Also can set OSM_RO_PORT in environment')
136 @click.pass_context
137 def cli_osm(ctx, **kwargs):
138 global logger
139 hostname = kwargs.pop("hostname", None)
140 if hostname is None:
141 print((
142 "either hostname option or OSM_HOSTNAME " +
143 "environment variable needs to be specified"))
144 exit(1)
145 # Remove None values
146 kwargs = {k: v for k, v in kwargs.items() if v is not None}
147 # if so_port is not None:
148 # kwargs['so_port']=so_port
149 # if so_project is not None:
150 # kwargs['so_project']=so_project
151 # if ro_hostname is not None:
152 # kwargs['ro_host']=ro_hostname
153 # if ro_port is not None:
154 # kwargs['ro_port']=ro_port
155 sol005 = os.getenv('OSM_SOL005', True)
156 # if user is not None:
157 # kwargs['user']=user
158 # if password is not None:
159 # kwargs['password']=password
160 # if project is not None:
161 # kwargs['project']=project
162 # if all_projects:
163 # kwargs['all_projects']=all_projects
164 # if public is not None:
165 # kwargs['public']=public
166 ctx.obj = client.Client(host=hostname, sol005=sol005, **kwargs)
167 logger = logging.getLogger('osmclient')
168
169
170 ####################
171 # LIST operations
172 ####################
173
174 @cli_osm.command(name='ns-list', short_help='list all NS instances')
175 @click.option('--filter', default=None,
176 help='restricts the list to the NS instances matching the filter.')
177 @click.option('--long', is_flag=True,
178 help='get more details of the NS (project, vim, deployment status, configuration status.')
179 @click.pass_context
180 def ns_list(ctx, filter, long):
181 """list all NS instances
182
183 \b
184 Options:
185 --filter filterExpr Restricts the list to the NS instances matching the filter
186
187 \b
188 filterExpr consists of one or more strings formatted according to "simpleFilterExpr",
189 concatenated using the "&" character:
190
191 \b
192 filterExpr := <simpleFilterExpr>["&"<simpleFilterExpr>]*
193 simpleFilterExpr := <attrName>["."<attrName>]*["."<op>]"="<value>[","<value>]*
194 op := "eq" | "neq" | "gt" | "lt" | "gte" | "lte" | "cont" | "ncont"
195 attrName := string
196 value := scalar value
197
198 \b
199 where:
200 * zero or more occurrences
201 ? zero or one occurrence
202 [] grouping of expressions to be used with ? and *
203 "" quotation marks for marking string constants
204 <> name separator
205
206 \b
207 "AttrName" is the name of one attribute in the data type that defines the representation
208 of the resource. The dot (".") character in "simpleFilterExpr" allows concatenation of
209 <attrName> entries to filter by attributes deeper in the hierarchy of a structured document.
210 "Op" stands for the comparison operator. If the expression has concatenated <attrName>
211 entries, it means that the operator "op" is applied to the attribute addressed by the last
212 <attrName> entry included in the concatenation. All simple filter expressions are combined
213 by the "AND" logical operator. In a concatenation of <attrName> entries in a <simpleFilterExpr>,
214 the rightmost "attrName" entry in a "simpleFilterExpr" is called "leaf attribute". The
215 concatenation of all "attrName" entries except the leaf attribute is called the "attribute
216 prefix". If an attribute referenced in an expression is an array, an object that contains a
217 corresponding array shall be considered to match the expression if any of the elements in the
218 array matches all expressions that have the same attribute prefix.
219
220 \b
221 Filter examples:
222 --filter admin-status=ENABLED
223 --filter nsd-ref=<NSD_NAME>
224 --filter nsd.vendor=<VENDOR>
225 --filter nsd.vendor=<VENDOR>&nsd-ref=<NSD_NAME>
226 --filter nsd.constituent-vnfd.vnfd-id-ref=<VNFD_NAME>
227 """
228 def summarize_deployment_status(status_dict):
229 #Nets
230 summary = ""
231 n_nets = 0
232 status_nets = {}
233 net_list = status_dict['nets']
234 for net in net_list:
235 n_nets += 1
236 if net['status'] not in status_nets:
237 status_nets[net['status']] = 1
238 else:
239 status_nets[net['status']] +=1
240 message = "Nets: "
241 for k,v in status_nets.items():
242 message += "{}:{},".format(k,v)
243 message += "TOTAL:{}".format(n_nets)
244 summary += "{}".format(message)
245 #VMs and VNFs
246 n_vms = 0
247 status_vms = {}
248 status_vnfs = {}
249 vnf_list = status_dict['vnfs']
250 for vnf in vnf_list:
251 member_vnf_index = vnf['member_vnf_index']
252 if member_vnf_index not in status_vnfs:
253 status_vnfs[member_vnf_index] = {}
254 for vm in vnf['vms']:
255 n_vms += 1
256 if vm['status'] not in status_vms:
257 status_vms[vm['status']] = 1
258 else:
259 status_vms[vm['status']] +=1
260 if vm['status'] not in status_vnfs[member_vnf_index]:
261 status_vnfs[member_vnf_index][vm['status']] = 1
262 else:
263 status_vnfs[member_vnf_index][vm['status']] += 1
264 message = "VMs: "
265 for k,v in status_vms.items():
266 message += "{}:{},".format(k,v)
267 message += "TOTAL:{}".format(n_vms)
268 summary += "\n{}".format(message)
269 summary += "\nNFs:"
270 for k,v in status_vnfs.items():
271 total = 0
272 message = "\n {} VMs: ".format(k)
273 for k2,v2 in v.items():
274 message += "{}:{},".format(k2,v2)
275 total += v2
276 message += "TOTAL:{}".format(total)
277 summary += message
278 return summary
279
280 def summarize_config_status(ee_list):
281 n_ee = 0
282 status_ee = {}
283 for ee in ee_list:
284 n_ee += 1
285 if ee['elementType'] not in status_ee:
286 status_ee[ee['elementType']] = {}
287 status_ee[ee['elementType']][ee['status']] = 1
288 continue;
289 if ee['status'] in status_ee[ee['elementType']]:
290 status_ee[ee['elementType']][ee['status']] += 1
291 else:
292 status_ee[ee['elementType']][ee['status']] = 1
293 summary = ""
294 for elementType in ["KDU", "VDU", "PDU", "VNF", "NS"]:
295 if elementType in status_ee:
296 message = ""
297 total = 0
298 for k,v in status_ee[elementType].items():
299 message += "{}:{},".format(k,v)
300 total += v
301 message += "TOTAL:{}\n".format(total)
302 summary += "{}: {}".format(elementType, message)
303 summary += "TOTAL Exec. Env.: {}".format(n_ee)
304 return summary
305 logger.debug("")
306 if filter:
307 check_client_version(ctx.obj, '--filter')
308 resp = ctx.obj.ns.list(filter)
309 else:
310 resp = ctx.obj.ns.list()
311 if long:
312 table = PrettyTable(
313 ['ns instance name',
314 'id',
315 'date',
316 'ns state',
317 'current operation',
318 'error details',
319 'project',
320 'vim (inst param)',
321 'deployment status',
322 'configuration status'])
323 project_list = ctx.obj.project.list()
324 vim_list = ctx.obj.vim.list()
325 else:
326 table = PrettyTable(
327 ['ns instance name',
328 'id',
329 'date',
330 'ns state',
331 'current operation',
332 'error details'])
333 for ns in resp:
334 fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
335 if fullclassname == 'osmclient.sol005.client.Client':
336 nsr = ns
337 nsr_name = nsr['name']
338 nsr_id = nsr['_id']
339 date = datetime.fromtimestamp(nsr['create-time']).strftime("%Y-%m-%dT%H:%M:%S")
340 ns_state = nsr['nsState']
341 if long:
342 deployment_status = summarize_deployment_status(nsr['deploymentStatus'])
343 config_status = summarize_config_status(nsr['configurationStatus'])
344 project_id = nsr.get('_admin').get('projects_read')[0]
345 project_name = '-'
346 for p in project_list:
347 if p['_id'] == project_id:
348 project_name = p['name']
349 break
350 #project = '{} ({})'.format(project_name, project_id)
351 project = project_name
352 vim_id = nsr.get('datacenter')
353 vim_name = '-'
354 for v in vim_list:
355 if v['uuid'] == vim_id:
356 vim_name = v['name']
357 break
358 #vim = '{} ({})'.format(vim_name, vim_id)
359 vim = vim_name
360 current_operation = "{} ({})".format(nsr['currentOperation'],nsr['currentOperationID'])
361 error_details = "N/A"
362 if ns_state == "BROKEN" or ns_state == "DEGRADED":
363 error_details = "{}\nDetail: {}".format(nsr['errorDescription'],nsr['errorDetail'])
364 else:
365 nsopdata = ctx.obj.ns.get_opdata(ns['id'])
366 nsr = nsopdata['nsr:nsr']
367 nsr_name = nsr['name-ref']
368 nsr_id = nsr['ns-instance-config-ref']
369 date = '-'
370 project = '-'
371 deployment_status = nsr['operational-status'] if 'operational-status' in nsr else 'Not found'
372 ns_state = deployment_status
373 config_status = nsr['config-status'] if 'config-status' in nsr else 'Not found'
374 current_operation = "Unknown"
375 error_details = nsr['detailed-status'] if 'detailed-status' in nsr else 'Not found'
376 if config_status == "config_not_needed":
377 config_status = "configured (no charms)"
378
379 if long:
380 table.add_row(
381 [nsr_name,
382 nsr_id,
383 date,
384 ns_state,
385 current_operation,
386 wrap_text(text=error_details,width=40),
387 project,
388 vim,
389 deployment_status,
390 config_status])
391 else:
392 table.add_row(
393 [nsr_name,
394 nsr_id,
395 date,
396 ns_state,
397 current_operation,
398 wrap_text(text=error_details,width=40)])
399 table.align = 'l'
400 print(table)
401 print('To get the history of all operations over a NS, run "osm ns-op-list NS_ID"')
402 print('For more details on the current operation, run "osm ns-op-show OPERATION_ID"')
403
404 def nsd_list(ctx, filter, long):
405 logger.debug("")
406 if filter:
407 check_client_version(ctx.obj, '--filter')
408 resp = ctx.obj.nsd.list(filter)
409 else:
410 resp = ctx.obj.nsd.list()
411 # print(yaml.safe_dump(resp))
412 fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
413 if fullclassname == 'osmclient.sol005.client.Client':
414 if long:
415 table = PrettyTable(['nsd name', 'id', 'onboarding state', 'operational state',
416 'usage state', 'date', 'last update'])
417 else:
418 table = PrettyTable(['nsd name', 'id'])
419 for nsd in resp:
420 name = nsd.get('name','-')
421 if long:
422 onb_state = nsd['_admin'].get('onboardingState','-')
423 op_state = nsd['_admin'].get('operationalState','-')
424 usage_state = nsd['_admin'].get('usageState','-')
425 date = datetime.fromtimestamp(nsd['_admin']['created']).strftime("%Y-%m-%dT%H:%M:%S")
426 last_update = datetime.fromtimestamp(nsd['_admin']['modified']).strftime("%Y-%m-%dT%H:%M:%S")
427 table.add_row([name, nsd['_id'], onb_state, op_state, usage_state, date, last_update])
428 else:
429 table.add_row([name, nsd['_id']])
430 else:
431 table = PrettyTable(['nsd name', 'id'])
432 for nsd in resp:
433 table.add_row([nsd['name'], nsd['id']])
434 table.align = 'l'
435 print(table)
436
437
438 @cli_osm.command(name='nsd-list', short_help='list all NS packages')
439 @click.option('--filter', default=None,
440 help='restricts the list to the NSD/NSpkg matching the filter')
441 @click.option('--long', is_flag=True, help='get more details')
442 @click.pass_context
443 def nsd_list1(ctx, filter, long):
444 """list all NSD/NS pkg in the system"""
445 logger.debug("")
446 nsd_list(ctx, filter, long)
447
448
449 @cli_osm.command(name='nspkg-list', short_help='list all NS packages')
450 @click.option('--filter', default=None,
451 help='restricts the list to the NSD/NSpkg matching the filter')
452 @click.option('--long', is_flag=True, help='get more details')
453 @click.pass_context
454 def nsd_list2(ctx, filter, long):
455 """list all NS packages"""
456 logger.debug("")
457 nsd_list(ctx, filter, long)
458
459
460 def vnfd_list(ctx, nf_type, filter, long):
461 logger.debug("")
462 if nf_type:
463 check_client_version(ctx.obj, '--nf_type')
464 elif filter:
465 check_client_version(ctx.obj, '--filter')
466 if nf_type:
467 if nf_type == "vnf":
468 nf_filter = "_admin.type=vnfd"
469 elif nf_type == "pnf":
470 nf_filter = "_admin.type=pnfd"
471 elif nf_type == "hnf":
472 nf_filter = "_admin.type=hnfd"
473 else:
474 raise ClientException('wrong value for "--nf_type" option, allowed values: vnf, pnf, hnf')
475 if filter:
476 filter = '{}&{}'.format(nf_filter, filter)
477 else:
478 filter = nf_filter
479 if filter:
480 resp = ctx.obj.vnfd.list(filter)
481 else:
482 resp = ctx.obj.vnfd.list()
483 # print(yaml.safe_dump(resp))
484 fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
485 if fullclassname == 'osmclient.sol005.client.Client':
486 if long:
487 table = PrettyTable(['nfpkg name', 'id', 'onboarding state', 'operational state',
488 'usage state', 'date', 'last update'])
489 else:
490 table = PrettyTable(['nfpkg name', 'id'])
491 for vnfd in resp:
492 name = vnfd['name'] if 'name' in vnfd else '-'
493 if long:
494 onb_state = vnfd['_admin'].get('onboardingState','-')
495 op_state = vnfd['_admin'].get('operationalState','-')
496 usage_state = vnfd['_admin'].get('usageState','-')
497 date = datetime.fromtimestamp(vnfd['_admin']['created']).strftime("%Y-%m-%dT%H:%M:%S")
498 last_update = datetime.fromtimestamp(vnfd['_admin']['modified']).strftime("%Y-%m-%dT%H:%M:%S")
499 table.add_row([name, vnfd['_id'], onb_state, op_state, usage_state, date, last_update])
500 else:
501 table.add_row([name, vnfd['_id']])
502 else:
503 table = PrettyTable(['nfpkg name', 'id'])
504 for vnfd in resp:
505 table.add_row([vnfd['name'], vnfd['id']])
506 table.align = 'l'
507 print(table)
508
509
510 @cli_osm.command(name='vnfd-list', short_help='list all xNF packages (VNF, HNF, PNF)')
511 @click.option('--nf_type', help='type of NF (vnf, pnf, hnf)')
512 @click.option('--filter', default=None,
513 help='restricts the list to the NF pkg matching the filter')
514 @click.option('--long', is_flag=True, help='get more details')
515 @click.pass_context
516 def vnfd_list1(ctx, nf_type, filter, long):
517 """list all xNF packages (VNF, HNF, PNF)"""
518 logger.debug("")
519 vnfd_list(ctx, nf_type, filter, long)
520
521
522 @cli_osm.command(name='vnfpkg-list', short_help='list all xNF packages (VNF, HNF, PNF)')
523 @click.option('--nf_type', help='type of NF (vnf, pnf, hnf)')
524 @click.option('--filter', default=None,
525 help='restricts the list to the NFpkg matching the filter')
526 @click.option('--long', is_flag=True, help='get more details')
527 @click.pass_context
528 def vnfd_list2(ctx, nf_type, filter, long):
529 """list all xNF packages (VNF, HNF, PNF)"""
530 logger.debug("")
531 vnfd_list(ctx, nf_type, filter, long)
532
533
534 @cli_osm.command(name='nfpkg-list', short_help='list all xNF packages (VNF, HNF, PNF)')
535 @click.option('--nf_type', help='type of NF (vnf, pnf, hnf)')
536 @click.option('--filter', default=None,
537 help='restricts the list to the NFpkg matching the filter')
538 @click.option('--long', is_flag=True, help='get more details')
539 @click.pass_context
540 def nfpkg_list(ctx, nf_type, filter, long):
541 """list all xNF packages (VNF, HNF, PNF)"""
542 logger.debug("")
543 # try:
544 check_client_version(ctx.obj, ctx.command.name)
545 vnfd_list(ctx, nf_type, filter, long)
546 # except ClientException as e:
547 # print(str(e))
548 # exit(1)
549
550
551 def vnf_list(ctx, ns, filter, long):
552 # try:
553 if ns or filter:
554 if ns:
555 check_client_version(ctx.obj, '--ns')
556 if filter:
557 check_client_version(ctx.obj, '--filter')
558 resp = ctx.obj.vnf.list(ns, filter)
559 else:
560 resp = ctx.obj.vnf.list()
561 # except ClientException as e:
562 # print(str(e))
563 # exit(1)
564 fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
565 if fullclassname == 'osmclient.sol005.client.Client':
566 field_names = ['vnf id', 'name', 'ns id', 'vnf member index',
567 'vnfd name', 'vim account id', 'ip address']
568 if long:
569 field_names = ['vnf id', 'name', 'ns id', 'vnf member index',
570 'vnfd name', 'vim account id', 'ip address',
571 'date', 'last update']
572 table = PrettyTable(field_names)
573 for vnfr in resp:
574 name = vnfr['name'] if 'name' in vnfr else '-'
575 new_row = [vnfr['_id'], name, vnfr['nsr-id-ref'],
576 vnfr['member-vnf-index-ref'], vnfr['vnfd-ref'],
577 vnfr['vim-account-id'], vnfr['ip-address']]
578 if long:
579 date = datetime.fromtimestamp(vnfr['_admin']['created']).strftime("%Y-%m-%dT%H:%M:%S")
580 last_update = datetime.fromtimestamp(vnfr['_admin']['modified']).strftime("%Y-%m-%dT%H:%M:%S")
581 new_row.extend([date, last_update])
582 table.add_row(new_row)
583 else:
584 table = PrettyTable(
585 ['vnf name',
586 'id',
587 'operational status',
588 'config status'])
589 for vnfr in resp:
590 if 'mgmt-interface' not in vnfr:
591 vnfr['mgmt-interface'] = {}
592 vnfr['mgmt-interface']['ip-address'] = None
593 table.add_row(
594 [vnfr['name'],
595 vnfr['id'],
596 vnfr['operational-status'],
597 vnfr['config-status']])
598 table.align = 'l'
599 print(table)
600
601
602 @cli_osm.command(name='vnf-list', short_help='list all NF instances')
603 @click.option('--ns', default=None, help='NS instance id or name to restrict the NF list')
604 @click.option('--filter', default=None,
605 help='restricts the list to the NF instances matching the filter.')
606 @click.option('--long', is_flag=True, help='get more details')
607 @click.pass_context
608 def vnf_list1(ctx, ns, filter, long):
609 """list all NF instances"""
610 logger.debug("")
611 vnf_list(ctx, ns, filter, long)
612
613
614 @cli_osm.command(name='nf-list', short_help='list all NF instances')
615 @click.option('--ns', default=None, help='NS instance id or name to restrict the NF list')
616 @click.option('--filter', default=None,
617 help='restricts the list to the NF instances matching the filter.')
618 @click.option('--long', is_flag=True, help='get more details')
619 @click.pass_context
620 def nf_list(ctx, ns, filter, long):
621 """list all NF instances
622
623 \b
624 Options:
625 --ns TEXT NS instance id or name to restrict the VNF list
626 --filter filterExpr Restricts the list to the VNF instances matching the filter
627
628 \b
629 filterExpr consists of one or more strings formatted according to "simpleFilterExpr",
630 concatenated using the "&" character:
631
632 \b
633 filterExpr := <simpleFilterExpr>["&"<simpleFilterExpr>]*
634 simpleFilterExpr := <attrName>["."<attrName>]*["."<op>]"="<value>[","<value>]*
635 op := "eq" | "neq" | "gt" | "lt" | "gte" | "lte" | "cont" | "ncont"
636 attrName := string
637 value := scalar value
638
639 \b
640 where:
641 * zero or more occurrences
642 ? zero or one occurrence
643 [] grouping of expressions to be used with ? and *
644 "" quotation marks for marking string constants
645 <> name separator
646
647 \b
648 "AttrName" is the name of one attribute in the data type that defines the representation
649 of the resource. The dot (".") character in "simpleFilterExpr" allows concatenation of
650 <attrName> entries to filter by attributes deeper in the hierarchy of a structured document.
651 "Op" stands for the comparison operator. If the expression has concatenated <attrName>
652 entries, it means that the operator "op" is applied to the attribute addressed by the last
653 <attrName> entry included in the concatenation. All simple filter expressions are combined
654 by the "AND" logical operator. In a concatenation of <attrName> entries in a <simpleFilterExpr>,
655 the rightmost "attrName" entry in a "simpleFilterExpr" is called "leaf attribute". The
656 concatenation of all "attrName" entries except the leaf attribute is called the "attribute
657 prefix". If an attribute referenced in an expression is an array, an object that contains a
658 corresponding array shall be considered to match the expression if any of the elements in the
659 array matches all expressions that have the same attribute prefix.
660
661 \b
662 Filter examples:
663 --filter vim-account-id=<VIM_ACCOUNT_ID>
664 --filter vnfd-ref=<VNFD_NAME>
665 --filter vdur.ip-address=<IP_ADDRESS>
666 --filter vnfd-ref=<VNFD_NAME>,vdur.ip-address=<IP_ADDRESS>
667 """
668 logger.debug("")
669 vnf_list(ctx, ns, filter)
670
671
672 @cli_osm.command(name='ns-op-list', short_help='shows the history of operations over a NS instance')
673 @click.argument('name')
674 @click.option('--long', is_flag=True,
675 help='get more details of the NS operation (date, ).')
676 @click.pass_context
677 def ns_op_list(ctx, name, long):
678 """shows the history of operations over a NS instance
679
680 NAME: name or ID of the NS instance
681 """
682 def formatParams(params):
683 if params['lcmOperationType']=='instantiate':
684 params.pop('nsDescription')
685 params.pop('nsName')
686 params.pop('nsdId')
687 params.pop('nsr_id')
688 elif params['lcmOperationType']=='action':
689 params.pop('primitive')
690 params.pop('lcmOperationType')
691 params.pop('nsInstanceId')
692 return params
693
694 logger.debug("")
695 # try:
696 check_client_version(ctx.obj, ctx.command.name)
697 resp = ctx.obj.ns.list_op(name)
698 # except ClientException as e:
699 # print(str(e))
700 # exit(1)
701
702 if long:
703 table = PrettyTable(['id', 'operation', 'action_name', 'operation_params', 'status', 'date', 'last update', 'detail'])
704 else:
705 table = PrettyTable(['id', 'operation', 'action_name', 'status', 'date', 'detail'])
706
707 #print(yaml.safe_dump(resp))
708 for op in resp:
709 action_name = "N/A"
710 if op['lcmOperationType']=='action':
711 action_name = op['operationParams']['primitive']
712 detail = "-"
713 if op['operationState']=='PROCESSING':
714 if op['lcmOperationType']=='instantiate':
715 if op['stage']:
716 detail = op['stage']
717 else:
718 detail = "In queue. Current position: {}".format(op['queuePosition'])
719 elif op['operationState']=='FAILED' or op['operationState']=='FAILED_TEMP':
720 detail = op['errorMessage']
721 date = datetime.fromtimestamp(op['startTime']).strftime("%Y-%m-%dT%H:%M:%S")
722 last_update = datetime.fromtimestamp(op['statusEnteredTime']).strftime("%Y-%m-%dT%H:%M:%S")
723 if long:
724 table.add_row([op['id'],
725 op['lcmOperationType'],
726 action_name,
727 wrap_text(text=json.dumps(formatParams(op['operationParams']),indent=2),width=50),
728 op['operationState'],
729 date,
730 last_update,
731 wrap_text(text=detail,width=50)])
732 else:
733 table.add_row([op['id'], op['lcmOperationType'], action_name,
734 op['operationState'], date, wrap_text(text=detail,width=50)])
735 table.align = 'l'
736 print(table)
737
738
739 def nsi_list(ctx, filter):
740 """list all Network Slice Instances"""
741 logger.debug("")
742 # try:
743 check_client_version(ctx.obj, ctx.command.name)
744 resp = ctx.obj.nsi.list(filter)
745 # except ClientException as e:
746 # print(str(e))
747 # exit(1)
748 table = PrettyTable(
749 ['netslice instance name',
750 'id',
751 'operational status',
752 'config status',
753 'detailed status'])
754 for nsi in resp:
755 nsi_name = nsi['name']
756 nsi_id = nsi['_id']
757 opstatus = nsi['operational-status'] if 'operational-status' in nsi else 'Not found'
758 configstatus = nsi['config-status'] if 'config-status' in nsi else 'Not found'
759 detailed_status = nsi['detailed-status'] if 'detailed-status' in nsi else 'Not found'
760 if configstatus == "config_not_needed":
761 configstatus = "configured (no charms)"
762 table.add_row(
763 [nsi_name,
764 nsi_id,
765 opstatus,
766 configstatus,
767 detailed_status])
768 table.align = 'l'
769 print(table)
770
771
772 @cli_osm.command(name='nsi-list', short_help='list all Network Slice Instances (NSI)')
773 @click.option('--filter', default=None,
774 help='restricts the list to the Network Slice Instances matching the filter')
775 @click.pass_context
776 def nsi_list1(ctx, filter):
777 """list all Network Slice Instances (NSI)"""
778 logger.debug("")
779 nsi_list(ctx, filter)
780
781
782 @cli_osm.command(name='netslice-instance-list', short_help='list all Network Slice Instances (NSI)')
783 @click.option('--filter', default=None,
784 help='restricts the list to the Network Slice Instances matching the filter')
785 @click.pass_context
786 def nsi_list2(ctx, filter):
787 """list all Network Slice Instances (NSI)"""
788 logger.debug("")
789 nsi_list(ctx, filter)
790
791
792 def nst_list(ctx, filter):
793 logger.debug("")
794 # try:
795 check_client_version(ctx.obj, ctx.command.name)
796 resp = ctx.obj.nst.list(filter)
797 # except ClientException as e:
798 # print(str(e))
799 # exit(1)
800 # print(yaml.safe_dump(resp))
801 table = PrettyTable(['nst name', 'id'])
802 for nst in resp:
803 name = nst['name'] if 'name' in nst else '-'
804 table.add_row([name, nst['_id']])
805 table.align = 'l'
806 print(table)
807
808
809 @cli_osm.command(name='nst-list', short_help='list all Network Slice Templates (NST)')
810 @click.option('--filter', default=None,
811 help='restricts the list to the NST matching the filter')
812 @click.pass_context
813 def nst_list1(ctx, filter):
814 """list all Network Slice Templates (NST) in the system"""
815 logger.debug("")
816 nst_list(ctx, filter)
817
818
819 @cli_osm.command(name='netslice-template-list', short_help='list all Network Slice Templates (NST)')
820 @click.option('--filter', default=None,
821 help='restricts the list to the NST matching the filter')
822 @click.pass_context
823 def nst_list2(ctx, filter):
824 """list all Network Slice Templates (NST) in the system"""
825 logger.debug("")
826 nst_list(ctx, filter)
827
828
829 def nsi_op_list(ctx, name):
830 logger.debug("")
831 # try:
832 check_client_version(ctx.obj, ctx.command.name)
833 resp = ctx.obj.nsi.list_op(name)
834 # except ClientException as e:
835 # print(str(e))
836 # exit(1)
837 table = PrettyTable(['id', 'operation', 'status'])
838 for op in resp:
839 table.add_row([op['id'], op['lcmOperationType'],
840 op['operationState']])
841 table.align = 'l'
842 print(table)
843
844
845 @cli_osm.command(name='nsi-op-list', short_help='shows the history of operations over a Network Slice Instance (NSI)')
846 @click.argument('name')
847 @click.pass_context
848 def nsi_op_list1(ctx, name):
849 """shows the history of operations over a Network Slice Instance (NSI)
850
851 NAME: name or ID of the Network Slice Instance
852 """
853 logger.debug("")
854 nsi_op_list(ctx, name)
855
856
857 @cli_osm.command(name='netslice-instance-op-list', short_help='shows the history of operations over a Network Slice Instance (NSI)')
858 @click.argument('name')
859 @click.pass_context
860 def nsi_op_list2(ctx, name):
861 """shows the history of operations over a Network Slice Instance (NSI)
862
863 NAME: name or ID of the Network Slice Instance
864 """
865 logger.debug("")
866 nsi_op_list(ctx, name)
867
868
869 @cli_osm.command(name='pdu-list', short_help='list all Physical Deployment Units (PDU)')
870 @click.option('--filter', default=None,
871 help='restricts the list to the Physical Deployment Units matching the filter')
872 @click.pass_context
873 def pdu_list(ctx, filter):
874 """list all Physical Deployment Units (PDU)"""
875 logger.debug("")
876 # try:
877 check_client_version(ctx.obj, ctx.command.name)
878 resp = ctx.obj.pdu.list(filter)
879 # except ClientException as e:
880 # print(str(e))
881 # exit(1)
882 table = PrettyTable(
883 ['pdu name',
884 'id',
885 'type',
886 'mgmt ip address'])
887 for pdu in resp:
888 pdu_name = pdu['name']
889 pdu_id = pdu['_id']
890 pdu_type = pdu['type']
891 pdu_ipaddress = "None"
892 for iface in pdu['interfaces']:
893 if iface['mgmt']:
894 pdu_ipaddress = iface['ip-address']
895 break
896 table.add_row(
897 [pdu_name,
898 pdu_id,
899 pdu_type,
900 pdu_ipaddress])
901 table.align = 'l'
902 print(table)
903
904
905 ####################
906 # SHOW operations
907 ####################
908
909 def nsd_show(ctx, name, literal):
910 logger.debug("")
911 # try:
912 resp = ctx.obj.nsd.get(name)
913 # resp = ctx.obj.nsd.get_individual(name)
914 # except ClientException as e:
915 # print(str(e))
916 # exit(1)
917
918 if literal:
919 print(yaml.safe_dump(resp))
920 return
921
922 table = PrettyTable(['field', 'value'])
923 for k, v in list(resp.items()):
924 table.add_row([k, wrap_text(text=json.dumps(v, indent=2),width=100)])
925 table.align = 'l'
926 print(table)
927
928
929 @cli_osm.command(name='nsd-show', short_help='shows the content of a NSD')
930 @click.option('--literal', is_flag=True,
931 help='print literally, no pretty table')
932 @click.argument('name')
933 @click.pass_context
934 def nsd_show1(ctx, name, literal):
935 """shows the content of a NSD
936
937 NAME: name or ID of the NSD/NSpkg
938 """
939 logger.debug("")
940 nsd_show(ctx, name, literal)
941
942
943 @cli_osm.command(name='nspkg-show', short_help='shows the content of a NSD')
944 @click.option('--literal', is_flag=True,
945 help='print literally, no pretty table')
946 @click.argument('name')
947 @click.pass_context
948 def nsd_show2(ctx, name, literal):
949 """shows the content of a NSD
950
951 NAME: name or ID of the NSD/NSpkg
952 """
953 logger.debug("")
954 nsd_show(ctx, name, literal)
955
956
957 def vnfd_show(ctx, name, literal):
958 logger.debug("")
959 # try:
960 resp = ctx.obj.vnfd.get(name)
961 # resp = ctx.obj.vnfd.get_individual(name)
962 # except ClientException as e:
963 # print(str(e))
964 # exit(1)
965
966 if literal:
967 print(yaml.safe_dump(resp))
968 return
969
970 table = PrettyTable(['field', 'value'])
971 for k, v in list(resp.items()):
972 table.add_row([k, wrap_text(text=json.dumps(v, indent=2),width=100)])
973 table.align = 'l'
974 print(table)
975
976
977 @cli_osm.command(name='vnfd-show', short_help='shows the content of a VNFD')
978 @click.option('--literal', is_flag=True,
979 help='print literally, no pretty table')
980 @click.argument('name')
981 @click.pass_context
982 def vnfd_show1(ctx, name, literal):
983 """shows the content of a VNFD
984
985 NAME: name or ID of the VNFD/VNFpkg
986 """
987 logger.debug("")
988 vnfd_show(ctx, name, literal)
989
990
991 @cli_osm.command(name='vnfpkg-show', short_help='shows the content of a VNFD')
992 @click.option('--literal', is_flag=True,
993 help='print literally, no pretty table')
994 @click.argument('name')
995 @click.pass_context
996 def vnfd_show2(ctx, name, literal):
997 """shows the content of a VNFD
998
999 NAME: name or ID of the VNFD/VNFpkg
1000 """
1001 logger.debug("")
1002 vnfd_show(ctx, name, literal)
1003
1004
1005 @cli_osm.command(name='nfpkg-show', short_help='shows the content of a NF Descriptor')
1006 @click.option('--literal', is_flag=True,
1007 help='print literally, no pretty table')
1008 @click.argument('name')
1009 @click.pass_context
1010 def nfpkg_show(ctx, name, literal):
1011 """shows the content of a NF Descriptor
1012
1013 NAME: name or ID of the NFpkg
1014 """
1015 logger.debug("")
1016 vnfd_show(ctx, name, literal)
1017
1018
1019 @cli_osm.command(name='ns-show', short_help='shows the info of a NS instance')
1020 @click.argument('name')
1021 @click.option('--literal', is_flag=True,
1022 help='print literally, no pretty table')
1023 @click.option('--filter', default=None)
1024 @click.pass_context
1025 def ns_show(ctx, name, literal, filter):
1026 """shows the info of a NS instance
1027
1028 NAME: name or ID of the NS instance
1029 """
1030 logger.debug("")
1031 # try:
1032 ns = ctx.obj.ns.get(name)
1033 # except ClientException as e:
1034 # print(str(e))
1035 # exit(1)
1036
1037 if literal:
1038 print(yaml.safe_dump(ns))
1039 return
1040
1041 table = PrettyTable(['field', 'value'])
1042
1043 for k, v in list(ns.items()):
1044 if filter is None or filter in k:
1045 table.add_row([k, wrap_text(text=json.dumps(v, indent=2),width=100)])
1046
1047 fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
1048 if fullclassname != 'osmclient.sol005.client.Client':
1049 nsopdata = ctx.obj.ns.get_opdata(ns['id'])
1050 nsr_optdata = nsopdata['nsr:nsr']
1051 for k, v in list(nsr_optdata.items()):
1052 if filter is None or filter in k:
1053 table.add_row([k, wrap_text(json.dumps(v, indent=2),width=100)])
1054 table.align = 'l'
1055 print(table)
1056
1057
1058 @cli_osm.command(name='vnf-show', short_help='shows the info of a VNF instance')
1059 @click.argument('name')
1060 @click.option('--literal', is_flag=True,
1061 help='print literally, no pretty table')
1062 @click.option('--filter', default=None, help='restricts the information to the fields in the filter')
1063 @click.option('--kdu', default=None, help='KDU name (whose status will be shown)')
1064 @click.pass_context
1065 def vnf_show(ctx, name, literal, filter, kdu):
1066 """shows the info of a VNF instance
1067
1068 NAME: name or ID of the VNF instance
1069 """
1070 def print_kdu_status(op_info_status):
1071 """print KDU status properly formatted
1072 """
1073 try:
1074 op_status = yaml.safe_load(op_info_status)
1075 if "namespace" in op_status and "info" in op_status and \
1076 "last_deployed" in op_status["info"] and "status" in op_status["info"] and \
1077 "code" in op_status["info"]["status"] and "resources" in op_status["info"]["status"] and \
1078 "notes" in op_status["info"]["status"] and "seconds" in op_status["info"]["last_deployed"]:
1079 last_deployed_time = datetime.fromtimestamp(op_status["info"]["last_deployed"]["seconds"]).strftime("%a %b %d %I:%M:%S %Y")
1080 print("LAST DEPLOYED: {}".format(last_deployed_time))
1081 print("NAMESPACE: {}".format(op_status["namespace"]))
1082 status_code = "UNKNOWN"
1083 if op_status["info"]["status"]["code"]==1:
1084 status_code = "DEPLOYED"
1085 print("STATUS: {}".format(status_code))
1086 print()
1087 print("RESOURCES:")
1088 print(op_status["info"]["status"]["resources"])
1089 print("NOTES:")
1090 print(op_status["info"]["status"]["notes"])
1091 else:
1092 print(op_info_status)
1093 except Exception:
1094 print(op_info_status)
1095
1096 logger.debug("")
1097 if kdu:
1098 if literal:
1099 raise ClientException('"--literal" option is incompatible with "--kdu" option')
1100 if filter:
1101 raise ClientException('"--filter" option is incompatible with "--kdu" option')
1102
1103 # try:
1104 check_client_version(ctx.obj, ctx.command.name)
1105 resp = ctx.obj.vnf.get(name)
1106
1107 if kdu:
1108 ns_id = resp['nsr-id-ref']
1109 op_data={}
1110 op_data['member_vnf_index'] = resp['member-vnf-index-ref']
1111 op_data['kdu_name'] = kdu
1112 op_data['primitive'] = 'status'
1113 op_data['primitive_params'] = {}
1114 op_id = ctx.obj.ns.exec_op(ns_id, op_name='action', op_data=op_data, wait=False)
1115 t = 0
1116 while t<30:
1117 op_info = ctx.obj.ns.get_op(op_id)
1118 if op_info['operationState'] == 'COMPLETED':
1119 print_kdu_status(op_info['detailed-status'])
1120 return
1121 time.sleep(5)
1122 t += 5
1123 print ("Could not determine KDU status")
1124
1125 if literal:
1126 print(yaml.safe_dump(resp))
1127 return
1128
1129 table = PrettyTable(['field', 'value'])
1130
1131 for k, v in list(resp.items()):
1132 if filter is None or filter in k:
1133 table.add_row([k, wrap_text(text=json.dumps(v,indent=2),width=100)])
1134 table.align = 'l'
1135 print(table)
1136 # except ClientException as e:
1137 # print(str(e))
1138 # exit(1)
1139
1140
1141 #@cli_osm.command(name='vnf-monitoring-show')
1142 #@click.argument('vnf_name')
1143 #@click.pass_context
1144 #def vnf_monitoring_show(ctx, vnf_name):
1145 # try:
1146 # check_client_version(ctx.obj, ctx.command.name, 'v1')
1147 # resp = ctx.obj.vnf.get_monitoring(vnf_name)
1148 # except ClientException as e:
1149 # print(str(e))
1150 # exit(1)
1151 #
1152 # table = PrettyTable(['vnf name', 'monitoring name', 'value', 'units'])
1153 # if resp is not None:
1154 # for monitor in resp:
1155 # table.add_row(
1156 # [vnf_name,
1157 # monitor['name'],
1158 # monitor['value-integer'],
1159 # monitor['units']])
1160 # table.align = 'l'
1161 # print(table)
1162
1163
1164 #@cli_osm.command(name='ns-monitoring-show')
1165 #@click.argument('ns_name')
1166 #@click.pass_context
1167 #def ns_monitoring_show(ctx, ns_name):
1168 # try:
1169 # check_client_version(ctx.obj, ctx.command.name, 'v1')
1170 # resp = ctx.obj.ns.get_monitoring(ns_name)
1171 # except ClientException as e:
1172 # print(str(e))
1173 # exit(1)
1174 #
1175 # table = PrettyTable(['vnf name', 'monitoring name', 'value', 'units'])
1176 # for key, val in list(resp.items()):
1177 # for monitor in val:
1178 # table.add_row(
1179 # [key,
1180 # monitor['name'],
1181 # monitor['value-integer'],
1182 # monitor['units']])
1183 # table.align = 'l'
1184 # print(table)
1185
1186
1187 @cli_osm.command(name='ns-op-show', short_help='shows the info of a NS operation')
1188 @click.argument('id')
1189 @click.option('--filter', default=None)
1190 @click.option('--literal', is_flag=True,
1191 help='print literally, no pretty table')
1192 @click.pass_context
1193 def ns_op_show(ctx, id, filter, literal):
1194 """shows the detailed info of a NS operation
1195
1196 ID: operation identifier
1197 """
1198 logger.debug("")
1199 # try:
1200 check_client_version(ctx.obj, ctx.command.name)
1201 op_info = ctx.obj.ns.get_op(id)
1202 # except ClientException as e:
1203 # print(str(e))
1204 # exit(1)
1205
1206 if literal:
1207 print(yaml.safe_dump(op_info))
1208 return
1209
1210 table = PrettyTable(['field', 'value'])
1211 for k, v in list(op_info.items()):
1212 if filter is None or filter in k:
1213 table.add_row([k, wrap_text(json.dumps(v, indent=2), 100)])
1214 table.align = 'l'
1215 print(table)
1216
1217
1218 def nst_show(ctx, name, literal):
1219 logger.debug("")
1220 # try:
1221 check_client_version(ctx.obj, ctx.command.name)
1222 resp = ctx.obj.nst.get(name)
1223 #resp = ctx.obj.nst.get_individual(name)
1224 # except ClientException as e:
1225 # print(str(e))
1226 # exit(1)
1227
1228 if literal:
1229 print(yaml.safe_dump(resp))
1230 return
1231
1232 table = PrettyTable(['field', 'value'])
1233 for k, v in list(resp.items()):
1234 table.add_row([k, wrap_text(json.dumps(v, indent=2), 100)])
1235 table.align = 'l'
1236 print(table)
1237
1238
1239 @cli_osm.command(name='nst-show', short_help='shows the content of a Network Slice Template (NST)')
1240 @click.option('--literal', is_flag=True,
1241 help='print literally, no pretty table')
1242 @click.argument('name')
1243 @click.pass_context
1244 def nst_show1(ctx, name, literal):
1245 """shows the content of a Network Slice Template (NST)
1246
1247 NAME: name or ID of the NST
1248 """
1249 logger.debug("")
1250 nst_show(ctx, name, literal)
1251
1252
1253 @cli_osm.command(name='netslice-template-show', short_help='shows the content of a Network Slice Template (NST)')
1254 @click.option('--literal', is_flag=True,
1255 help='print literally, no pretty table')
1256 @click.argument('name')
1257 @click.pass_context
1258 def nst_show2(ctx, name, literal):
1259 """shows the content of a Network Slice Template (NST)
1260
1261 NAME: name or ID of the NST
1262 """
1263 logger.debug("")
1264 nst_show(ctx, name, literal)
1265
1266
1267 def nsi_show(ctx, name, literal, filter):
1268 logger.debug("")
1269 # try:
1270 check_client_version(ctx.obj, ctx.command.name)
1271 nsi = ctx.obj.nsi.get(name)
1272 # except ClientException as e:
1273 # print(str(e))
1274 # exit(1)
1275
1276 if literal:
1277 print(yaml.safe_dump(nsi))
1278 return
1279
1280 table = PrettyTable(['field', 'value'])
1281
1282 for k, v in list(nsi.items()):
1283 if filter is None or filter in k:
1284 table.add_row([k, json.dumps(v, indent=2)])
1285
1286 table.align = 'l'
1287 print(table)
1288
1289
1290 @cli_osm.command(name='nsi-show', short_help='shows the content of a Network Slice Instance (NSI)')
1291 @click.argument('name')
1292 @click.option('--literal', is_flag=True,
1293 help='print literally, no pretty table')
1294 @click.option('--filter', default=None)
1295 @click.pass_context
1296 def nsi_show1(ctx, name, literal, filter):
1297 """shows the content of a Network Slice Instance (NSI)
1298
1299 NAME: name or ID of the Network Slice Instance
1300 """
1301 logger.debug("")
1302 nsi_show(ctx, name, literal, filter)
1303
1304
1305 @cli_osm.command(name='netslice-instance-show', short_help='shows the content of a Network Slice Instance (NSI)')
1306 @click.argument('name')
1307 @click.option('--literal', is_flag=True,
1308 help='print literally, no pretty table')
1309 @click.option('--filter', default=None)
1310 @click.pass_context
1311 def nsi_show2(ctx, name, literal, filter):
1312 """shows the content of a Network Slice Instance (NSI)
1313
1314 NAME: name or ID of the Network Slice Instance
1315 """
1316 logger.debug("")
1317 nsi_show(ctx, name, literal, filter)
1318
1319
1320 def nsi_op_show(ctx, id, filter):
1321 logger.debug("")
1322 # try:
1323 check_client_version(ctx.obj, ctx.command.name)
1324 op_info = ctx.obj.nsi.get_op(id)
1325 # except ClientException as e:
1326 # print(str(e))
1327 # exit(1)
1328
1329 table = PrettyTable(['field', 'value'])
1330 for k, v in list(op_info.items()):
1331 if filter is None or filter in k:
1332 table.add_row([k, json.dumps(v, indent=2)])
1333 table.align = 'l'
1334 print(table)
1335
1336
1337 @cli_osm.command(name='nsi-op-show', short_help='shows the info of an operation over a Network Slice Instance(NSI)')
1338 @click.argument('id')
1339 @click.option('--filter', default=None)
1340 @click.pass_context
1341 def nsi_op_show1(ctx, id, filter):
1342 """shows the info of an operation over a Network Slice Instance(NSI)
1343
1344 ID: operation identifier
1345 """
1346 logger.debug("")
1347 nsi_op_show(ctx, id, filter)
1348
1349
1350 @cli_osm.command(name='netslice-instance-op-show', short_help='shows the info of an operation over a Network Slice Instance(NSI)')
1351 @click.argument('id')
1352 @click.option('--filter', default=None)
1353 @click.pass_context
1354 def nsi_op_show2(ctx, id, filter):
1355 """shows the info of an operation over a Network Slice Instance(NSI)
1356
1357 ID: operation identifier
1358 """
1359 logger.debug("")
1360 nsi_op_show(ctx, id, filter)
1361
1362
1363 @cli_osm.command(name='pdu-show', short_help='shows the content of a Physical Deployment Unit (PDU)')
1364 @click.argument('name')
1365 @click.option('--literal', is_flag=True,
1366 help='print literally, no pretty table')
1367 @click.option('--filter', default=None)
1368 @click.pass_context
1369 def pdu_show(ctx, name, literal, filter):
1370 """shows the content of a Physical Deployment Unit (PDU)
1371
1372 NAME: name or ID of the PDU
1373 """
1374 logger.debug("")
1375 # try:
1376 check_client_version(ctx.obj, ctx.command.name)
1377 pdu = ctx.obj.pdu.get(name)
1378 # except ClientException as e:
1379 # print(str(e))
1380 # exit(1)
1381
1382 if literal:
1383 print(yaml.safe_dump(pdu))
1384 return
1385
1386 table = PrettyTable(['field', 'value'])
1387
1388 for k, v in list(pdu.items()):
1389 if filter is None or filter in k:
1390 table.add_row([k, json.dumps(v, indent=2)])
1391
1392 table.align = 'l'
1393 print(table)
1394
1395
1396 ####################
1397 # CREATE operations
1398 ####################
1399
1400 def nsd_create(ctx, filename, overwrite, skip_charm_build):
1401 logger.debug("")
1402 # try:
1403 check_client_version(ctx.obj, ctx.command.name)
1404 ctx.obj.nsd.create(filename, overwrite=overwrite, skip_charm_build=skip_charm_build)
1405 # except ClientException as e:
1406 # print(str(e))
1407 # exit(1)
1408
1409
1410 @cli_osm.command(name='nsd-create', short_help='creates a new NSD/NSpkg')
1411 @click.argument('filename')
1412 @click.option('--overwrite', 'overwrite', default=None, # hidden=True,
1413 help='Deprecated. Use override')
1414 @click.option('--override', 'overwrite', default=None,
1415 help='overrides fields in descriptor, format: '
1416 '"key1.key2...=value[;key3...=value;...]"')
1417 @click.option('--skip-charm-build', default=False, is_flag=True,
1418 help='The charm will not be compiled, it is assumed to already exist')
1419 @click.pass_context
1420 def nsd_create1(ctx, filename, overwrite, skip_charm_build):
1421 """creates a new NSD/NSpkg
1422
1423 FILENAME: NSD yaml file or NSpkg tar.gz file
1424 """
1425 logger.debug("")
1426 nsd_create(ctx, filename, overwrite=overwrite, skip_charm_build=skip_charm_build)
1427
1428
1429 @cli_osm.command(name='nspkg-create', short_help='creates a new NSD/NSpkg')
1430 @click.argument('filename')
1431 @click.option('--overwrite', 'overwrite', default=None, # hidden=True,
1432 help='Deprecated. Use override')
1433 @click.option('--override', 'overwrite', default=None,
1434 help='overrides fields in descriptor, format: '
1435 '"key1.key2...=value[;key3...=value;...]"')
1436 @click.option('--skip-charm-build', default=False, is_flag=True,
1437 help='The charm will not be compiled, it is assumed to already exist')
1438 @click.pass_context
1439 def nsd_create2(ctx, filename, overwrite, skip_charm_build):
1440 """creates a new NSD/NSpkg
1441
1442 FILENAME: NSD folder, NSD yaml file or NSpkg tar.gz file
1443 """
1444 logger.debug("")
1445 nsd_create(ctx, filename, overwrite=overwrite, skip_charm_build=skip_charm_build)
1446
1447
1448 def vnfd_create(ctx, filename, overwrite, skip_charm_build):
1449 logger.debug("")
1450 # try:
1451 check_client_version(ctx.obj, ctx.command.name)
1452 ctx.obj.vnfd.create(filename, overwrite=overwrite, skip_charm_build=skip_charm_build)
1453 # except ClientException as e:
1454 # print(str(e))
1455 # exit(1)
1456
1457
1458 @cli_osm.command(name='vnfd-create', short_help='creates a new VNFD/VNFpkg')
1459 @click.argument('filename')
1460 @click.option('--overwrite', 'overwrite', default=None,
1461 help='overwrite deprecated, use override')
1462 @click.option('--override', 'overwrite', default=None,
1463 help='overrides fields in descriptor, format: '
1464 '"key1.key2...=value[;key3...=value;...]"')
1465 @click.option('--skip-charm-build', default=False, is_flag=True,
1466 help='The charm will not be compiled, it is assumed to already exist')
1467 @click.pass_context
1468 def vnfd_create1(ctx, filename, overwrite, skip_charm_build):
1469 """creates a new VNFD/VNFpkg
1470
1471 FILENAME: VNFD yaml file or VNFpkg tar.gz file
1472 """
1473 logger.debug("")
1474 vnfd_create(ctx, filename, overwrite=overwrite, skip_charm_build=skip_charm_build)
1475
1476
1477 @cli_osm.command(name='vnfpkg-create', short_help='creates a new VNFD/VNFpkg')
1478 @click.argument('filename')
1479 @click.option('--overwrite', 'overwrite', default=None, # hidden=True,
1480 help='Deprecated. Use override')
1481 @click.option('--override', 'overwrite', default=None,
1482 help='overrides fields in descriptor, format: '
1483 '"key1.key2...=value[;key3...=value;...]"')
1484 @click.option('--skip-charm-build', default=False, is_flag=True,
1485 help='The charm will not be compiled, it is assumed to already exist')
1486 @click.pass_context
1487 def vnfd_create2(ctx, filename, overwrite, skip_charm_build):
1488 """creates a new VNFD/VNFpkg
1489
1490 FILENAME: NF Package Folder, NF Descriptor yaml file or NFpkg tar.gz file
1491 """
1492 logger.debug("")
1493 vnfd_create(ctx, filename, overwrite=overwrite, skip_charm_build=skip_charm_build)
1494
1495
1496 @cli_osm.command(name='nfpkg-create', short_help='creates a new NFpkg')
1497 @click.argument('filename')
1498 @click.option('--overwrite', 'overwrite', default=None, # hidden=True,
1499 help='Deprecated. Use override')
1500 @click.option('--override', 'overwrite', default=None,
1501 help='overrides fields in descriptor, format: '
1502 '"key1.key2...=value[;key3...=value;...]"')
1503 @click.option('--skip-charm-build', default=False, is_flag=True,
1504 help='The charm will not be compiled, it is assumed to already exist')
1505 @click.pass_context
1506 def nfpkg_create(ctx, filename, overwrite, skip_charm_build):
1507 """creates a new NFpkg
1508
1509 FILENAME: NF Package Folder, NF Descriptor yaml file or NFpkg tar.gz filems to build
1510 """
1511 logger.debug("")
1512 vnfd_create(ctx, filename, overwrite=overwrite, skip_charm_build=skip_charm_build)
1513
1514
1515 @cli_osm.command(name='ns-create', short_help='creates a new Network Service instance')
1516 @click.option('--ns_name',
1517 prompt=True, help='name of the NS instance')
1518 @click.option('--nsd_name',
1519 prompt=True, help='name of the NS descriptor')
1520 @click.option('--vim_account',
1521 prompt=True, help='default VIM account id or name for the deployment')
1522 @click.option('--admin_status',
1523 default='ENABLED',
1524 help='administration status')
1525 @click.option('--ssh_keys',
1526 default=None,
1527 help='comma separated list of public key files to inject to vnfs')
1528 @click.option('--config',
1529 default=None,
1530 help='ns specific yaml configuration')
1531 @click.option('--config_file',
1532 default=None,
1533 help='ns specific yaml configuration file')
1534 @click.option('--wait',
1535 required=False,
1536 default=False,
1537 is_flag=True,
1538 help='do not return the control immediately, but keep it '
1539 'until the operation is completed, or timeout')
1540 @click.pass_context
1541 def ns_create(ctx,
1542 nsd_name,
1543 ns_name,
1544 vim_account,
1545 admin_status,
1546 ssh_keys,
1547 config,
1548 config_file,
1549 wait):
1550 """creates a new NS instance"""
1551 logger.debug("")
1552 # try:
1553 if config_file:
1554 check_client_version(ctx.obj, '--config_file')
1555 if config:
1556 raise ClientException('"--config" option is incompatible with "--config_file" option')
1557 with open(config_file, 'r') as cf:
1558 config=cf.read()
1559 ctx.obj.ns.create(
1560 nsd_name,
1561 ns_name,
1562 config=config,
1563 ssh_keys=ssh_keys,
1564 account=vim_account,
1565 wait=wait)
1566 # except ClientException as e:
1567 # print(str(e))
1568 # exit(1)
1569
1570
1571 def nst_create(ctx, filename, overwrite):
1572 logger.debug("")
1573 # try:
1574 check_client_version(ctx.obj, ctx.command.name)
1575 ctx.obj.nst.create(filename, overwrite)
1576 # except ClientException as e:
1577 # print(str(e))
1578 # exit(1)
1579
1580
1581 @cli_osm.command(name='nst-create', short_help='creates a new Network Slice Template (NST)')
1582 @click.argument('filename')
1583 @click.option('--overwrite', 'overwrite', default=None, # hidden=True,
1584 help='Deprecated. Use override')
1585 @click.option('--override', 'overwrite', default=None,
1586 help='overrides fields in descriptor, format: '
1587 '"key1.key2...=value[;key3...=value;...]"')
1588 @click.pass_context
1589 def nst_create1(ctx, charm_folder, overwrite):
1590 """creates a new Network Slice Template (NST)
1591
1592 FILENAME: NST package folder, NST yaml file or NSTpkg tar.gz file
1593 """
1594 logger.debug("")
1595 nst_create(ctx, charm_folder, overwrite)
1596
1597
1598 @cli_osm.command(name='netslice-template-create', short_help='creates a new Network Slice Template (NST)')
1599 @click.argument('filename')
1600 @click.option('--overwrite', 'overwrite', default=None, # hidden=True,
1601 help='Deprecated. Use override')
1602 @click.option('--override', 'overwrite', default=None,
1603 help='overrides fields in descriptor, format: '
1604 '"key1.key2...=value[;key3...=value;...]"')
1605 @click.pass_context
1606 def nst_create2(ctx, filename, overwrite):
1607 """creates a new Network Slice Template (NST)
1608
1609 FILENAME: NST yaml file or NSTpkg tar.gz file
1610 """
1611 logger.debug("")
1612 nst_create(ctx, filename, overwrite)
1613
1614
1615 def nsi_create(ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file, wait):
1616 """creates a new Network Slice Instance (NSI)"""
1617 logger.debug("")
1618 # try:
1619 check_client_version(ctx.obj, ctx.command.name)
1620 if config_file:
1621 if config:
1622 raise ClientException('"--config" option is incompatible with "--config_file" option')
1623 with open(config_file, 'r') as cf:
1624 config=cf.read()
1625 ctx.obj.nsi.create(nst_name, nsi_name, config=config, ssh_keys=ssh_keys,
1626 account=vim_account, wait=wait)
1627 # except ClientException as e:
1628 # print(str(e))
1629 # exit(1)
1630
1631
1632 @cli_osm.command(name='nsi-create', short_help='creates a new Network Slice Instance')
1633 @click.option('--nsi_name', prompt=True, help='name of the Network Slice Instance')
1634 @click.option('--nst_name', prompt=True, help='name of the Network Slice Template')
1635 @click.option('--vim_account', prompt=True, help='default VIM account id or name for the deployment')
1636 @click.option('--ssh_keys', default=None,
1637 help='comma separated list of keys to inject to vnfs')
1638 @click.option('--config', default=None,
1639 help='Netslice specific yaml configuration:\n'
1640 'netslice_subnet: [\n'
1641 'id: TEXT, vim_account: TEXT,\n'
1642 'vnf: [member-vnf-index: TEXT, vim_account: TEXT]\n'
1643 'vld: [name: TEXT, vim-network-name: TEXT or DICT with vim_account, vim_net entries]\n'
1644 'additionalParamsForNsi: {param: value, ...}\n'
1645 'additionalParamsForsubnet: [{id: SUBNET_ID, additionalParamsForNs: {}, additionalParamsForVnf: {}}]\n'
1646 '],\n'
1647 'netslice-vld: [name: TEXT, vim-network-name: TEXT or DICT with vim_account, vim_net entries]'
1648 )
1649 @click.option('--config_file',
1650 default=None,
1651 help='nsi specific yaml configuration file')
1652 @click.option('--wait',
1653 required=False,
1654 default=False,
1655 is_flag=True,
1656 help='do not return the control immediately, but keep it '
1657 'until the operation is completed, or timeout')
1658 @click.pass_context
1659 def nsi_create1(ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file, wait):
1660 """creates a new Network Slice Instance (NSI)"""
1661 logger.debug("")
1662 nsi_create(ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file, wait=wait)
1663
1664
1665 @cli_osm.command(name='netslice-instance-create', short_help='creates a new Network Slice Instance')
1666 @click.option('--nsi_name', prompt=True, help='name of the Network Slice Instance')
1667 @click.option('--nst_name', prompt=True, help='name of the Network Slice Template')
1668 @click.option('--vim_account', prompt=True, help='default VIM account id or name for the deployment')
1669 @click.option('--ssh_keys', default=None,
1670 help='comma separated list of keys to inject to vnfs')
1671 @click.option('--config', default=None,
1672 help='Netslice specific yaml configuration:\n'
1673 'netslice_subnet: [\n'
1674 'id: TEXT, vim_account: TEXT,\n'
1675 'vnf: [member-vnf-index: TEXT, vim_account: TEXT]\n'
1676 'vld: [name: TEXT, vim-network-name: TEXT or DICT with vim_account, vim_net entries]'
1677 '],\n'
1678 'netslice-vld: [name: TEXT, vim-network-name: TEXT or DICT with vim_account, vim_net entries]'
1679 )
1680 @click.option('--config_file',
1681 default=None,
1682 help='nsi specific yaml configuration file')
1683 @click.option('--wait',
1684 required=False,
1685 default=False,
1686 is_flag=True,
1687 help='do not return the control immediately, but keep it '
1688 'until the operation is completed, or timeout')
1689 @click.pass_context
1690 def nsi_create2(ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file, wait):
1691 """creates a new Network Slice Instance (NSI)"""
1692 logger.debug("")
1693 nsi_create(ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file, wait=wait)
1694
1695
1696 @cli_osm.command(name='pdu-create', short_help='adds a new Physical Deployment Unit to the catalog')
1697 @click.option('--name', help='name of the Physical Deployment Unit')
1698 @click.option('--pdu_type', help='type of PDU (e.g. router, firewall, FW001)')
1699 @click.option('--interface',
1700 help='interface(s) of the PDU: name=<NAME>,mgmt=<true|false>,ip-address=<IP_ADDRESS>'+
1701 '[,type=<overlay|underlay>][,mac-address=<MAC_ADDRESS>][,vim-network-name=<VIM_NET_NAME>]',
1702 multiple=True)
1703 @click.option('--description', help='human readable description')
1704 @click.option('--vim_account', help='list of VIM accounts (in the same VIM) that can reach this PDU', multiple=True)
1705 @click.option('--descriptor_file', default=None,
1706 help='PDU descriptor file (as an alternative to using the other arguments')
1707 @click.pass_context
1708 def pdu_create(ctx, name, pdu_type, interface, description, vim_account, descriptor_file):
1709 """creates a new Physical Deployment Unit (PDU)"""
1710 logger.debug("")
1711 # try:
1712 check_client_version(ctx.obj, ctx.command.name)
1713 pdu = {}
1714 if not descriptor_file:
1715 if not name:
1716 raise ClientException('in absence of descriptor file, option "--name" is mandatory')
1717 if not pdu_type:
1718 raise ClientException('in absence of descriptor file, option "--pdu_type" is mandatory')
1719 if not interface:
1720 raise ClientException('in absence of descriptor file, option "--interface" is mandatory (at least once)')
1721 if not vim_account:
1722 raise ClientException('in absence of descriptor file, option "--vim_account" is mandatory (at least once)')
1723 else:
1724 with open(descriptor_file, 'r') as df:
1725 pdu = yaml.safe_load(df.read())
1726 if name: pdu["name"] = name
1727 if pdu_type: pdu["type"] = pdu_type
1728 if description: pdu["description"] = description
1729 if vim_account: pdu["vim_accounts"] = vim_account
1730 if interface:
1731 ifaces_list = []
1732 for iface in interface:
1733 new_iface={k:v for k,v in [i.split('=') for i in iface.split(',')]}
1734 new_iface["mgmt"] = (new_iface.get("mgmt","false").lower() == "true")
1735 ifaces_list.append(new_iface)
1736 pdu["interfaces"] = ifaces_list
1737 ctx.obj.pdu.create(pdu)
1738 # except ClientException as e:
1739 # print(str(e))
1740 # exit(1)
1741
1742
1743 ####################
1744 # UPDATE operations
1745 ####################
1746
1747 def nsd_update(ctx, name, content):
1748 logger.debug("")
1749 # try:
1750 check_client_version(ctx.obj, ctx.command.name)
1751 ctx.obj.nsd.update(name, content)
1752 # except ClientException as e:
1753 # print(str(e))
1754 # exit(1)
1755
1756
1757 @cli_osm.command(name='nsd-update', short_help='updates a NSD/NSpkg')
1758 @click.argument('name')
1759 @click.option('--content', default=None,
1760 help='filename with the NSD/NSpkg replacing the current one')
1761 @click.pass_context
1762 def nsd_update1(ctx, name, content):
1763 """updates a NSD/NSpkg
1764
1765 NAME: name or ID of the NSD/NSpkg
1766 """
1767 logger.debug("")
1768 nsd_update(ctx, name, content)
1769
1770
1771 @cli_osm.command(name='nspkg-update', short_help='updates a NSD/NSpkg')
1772 @click.argument('name')
1773 @click.option('--content', default=None,
1774 help='filename with the NSD/NSpkg replacing the current one')
1775 @click.pass_context
1776 def nsd_update2(ctx, name, content):
1777 """updates a NSD/NSpkg
1778
1779 NAME: name or ID of the NSD/NSpkg
1780 """
1781 logger.debug("")
1782 nsd_update(ctx, name, content)
1783
1784
1785 def vnfd_update(ctx, name, content):
1786 logger.debug("")
1787 # try:
1788 check_client_version(ctx.obj, ctx.command.name)
1789 ctx.obj.vnfd.update(name, content)
1790 # except ClientException as e:
1791 # print(str(e))
1792 # exit(1)
1793
1794
1795 @cli_osm.command(name='vnfd-update', short_help='updates a new VNFD/VNFpkg')
1796 @click.argument('name')
1797 @click.option('--content', default=None,
1798 help='filename with the VNFD/VNFpkg replacing the current one')
1799 @click.pass_context
1800 def vnfd_update1(ctx, name, content):
1801 """updates a VNFD/VNFpkg
1802
1803 NAME: name or ID of the VNFD/VNFpkg
1804 """
1805 logger.debug("")
1806 vnfd_update(ctx, name, content)
1807
1808
1809 @cli_osm.command(name='vnfpkg-update', short_help='updates a VNFD/VNFpkg')
1810 @click.argument('name')
1811 @click.option('--content', default=None,
1812 help='filename with the VNFD/VNFpkg replacing the current one')
1813 @click.pass_context
1814 def vnfd_update2(ctx, name, content):
1815 """updates a VNFD/VNFpkg
1816
1817 NAME: VNFD yaml file or VNFpkg tar.gz file
1818 """
1819 logger.debug("")
1820 vnfd_update(ctx, name, content)
1821
1822
1823 @cli_osm.command(name='nfpkg-update', short_help='updates a NFpkg')
1824 @click.argument('name')
1825 @click.option('--content', default=None,
1826 help='filename with the NFpkg replacing the current one')
1827 @click.pass_context
1828 def nfpkg_update(ctx, name, content):
1829 """updates a NFpkg
1830
1831 NAME: NF Descriptor yaml file or NFpkg tar.gz file
1832 """
1833 logger.debug("")
1834 vnfd_update(ctx, name, content)
1835
1836
1837 def nst_update(ctx, name, content):
1838 logger.debug("")
1839 # try:
1840 check_client_version(ctx.obj, ctx.command.name)
1841 ctx.obj.nst.update(name, content)
1842 # except ClientException as e:
1843 # print(str(e))
1844 # exit(1)
1845
1846
1847 @cli_osm.command(name='nst-update', short_help='updates a Network Slice Template (NST)')
1848 @click.argument('name')
1849 @click.option('--content', default=None,
1850 help='filename with the NST/NSTpkg replacing the current one')
1851 @click.pass_context
1852 def nst_update1(ctx, name, content):
1853 """updates a Network Slice Template (NST)
1854
1855 NAME: name or ID of the NSD/NSpkg
1856 """
1857 logger.debug("")
1858 nst_update(ctx, name, content)
1859
1860
1861 @cli_osm.command(name='netslice-template-update', short_help='updates a Network Slice Template (NST)')
1862 @click.argument('name')
1863 @click.option('--content', default=None,
1864 help='filename with the NST/NSTpkg replacing the current one')
1865 @click.pass_context
1866 def nst_update2(ctx, name, content):
1867 """updates a Network Slice Template (NST)
1868
1869 NAME: name or ID of the NSD/NSpkg
1870 """
1871 logger.debug("")
1872 nst_update(ctx, name, content)
1873
1874
1875 ####################
1876 # DELETE operations
1877 ####################
1878
1879 def nsd_delete(ctx, name, force):
1880 logger.debug("")
1881 # try:
1882 if not force:
1883 ctx.obj.nsd.delete(name)
1884 else:
1885 check_client_version(ctx.obj, '--force')
1886 ctx.obj.nsd.delete(name, force)
1887 # except ClientException as e:
1888 # print(str(e))
1889 # exit(1)
1890
1891
1892 @cli_osm.command(name='nsd-delete', short_help='deletes a NSD/NSpkg')
1893 @click.argument('name')
1894 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
1895 @click.pass_context
1896 def nsd_delete1(ctx, name, force):
1897 """deletes a NSD/NSpkg
1898
1899 NAME: name or ID of the NSD/NSpkg to be deleted
1900 """
1901 logger.debug("")
1902 nsd_delete(ctx, name, force)
1903
1904
1905 @cli_osm.command(name='nspkg-delete', short_help='deletes a NSD/NSpkg')
1906 @click.argument('name')
1907 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
1908 @click.pass_context
1909 def nsd_delete2(ctx, name, force):
1910 """deletes a NSD/NSpkg
1911
1912 NAME: name or ID of the NSD/NSpkg to be deleted
1913 """
1914 logger.debug("")
1915 nsd_delete(ctx, name, force)
1916
1917
1918 def vnfd_delete(ctx, name, force):
1919 logger.debug("")
1920 # try:
1921 if not force:
1922 ctx.obj.vnfd.delete(name)
1923 else:
1924 check_client_version(ctx.obj, '--force')
1925 ctx.obj.vnfd.delete(name, force)
1926 # except ClientException as e:
1927 # print(str(e))
1928 # exit(1)
1929
1930
1931 @cli_osm.command(name='vnfd-delete', short_help='deletes a VNFD/VNFpkg')
1932 @click.argument('name')
1933 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
1934 @click.pass_context
1935 def vnfd_delete1(ctx, name, force):
1936 """deletes a VNFD/VNFpkg
1937
1938 NAME: name or ID of the VNFD/VNFpkg to be deleted
1939 """
1940 logger.debug("")
1941 vnfd_delete(ctx, name, force)
1942
1943
1944 @cli_osm.command(name='vnfpkg-delete', short_help='deletes a VNFD/VNFpkg')
1945 @click.argument('name')
1946 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
1947 @click.pass_context
1948 def vnfd_delete2(ctx, name, force):
1949 """deletes a VNFD/VNFpkg
1950
1951 NAME: name or ID of the VNFD/VNFpkg to be deleted
1952 """
1953 logger.debug("")
1954 vnfd_delete(ctx, name, force)
1955
1956
1957 @cli_osm.command(name='nfpkg-delete', short_help='deletes a NFpkg')
1958 @click.argument('name')
1959 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
1960 @click.pass_context
1961 def nfpkg_delete(ctx, name, force):
1962 """deletes a NFpkg
1963
1964 NAME: name or ID of the NFpkg to be deleted
1965 """
1966 logger.debug("")
1967 vnfd_delete(ctx, name, force)
1968
1969
1970 @cli_osm.command(name='ns-delete', short_help='deletes a NS instance')
1971 @click.argument('name')
1972 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
1973 @click.option('--wait',
1974 required=False,
1975 default=False,
1976 is_flag=True,
1977 help='do not return the control immediately, but keep it '
1978 'until the operation is completed, or timeout')
1979 @click.pass_context
1980 def ns_delete(ctx, name, force, wait):
1981 """deletes a NS instance
1982
1983 NAME: name or ID of the NS instance to be deleted
1984 """
1985 logger.debug("")
1986 # try:
1987 if not force:
1988 ctx.obj.ns.delete(name, wait=wait)
1989 else:
1990 check_client_version(ctx.obj, '--force')
1991 ctx.obj.ns.delete(name, force, wait=wait)
1992 # except ClientException as e:
1993 # print(str(e))
1994 # exit(1)
1995
1996
1997 def nst_delete(ctx, name, force):
1998 logger.debug("")
1999 # try:
2000 check_client_version(ctx.obj, ctx.command.name)
2001 ctx.obj.nst.delete(name, force)
2002 # except ClientException as e:
2003 # print(str(e))
2004 # exit(1)
2005
2006
2007 @cli_osm.command(name='nst-delete', short_help='deletes a Network Slice Template (NST)')
2008 @click.argument('name')
2009 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
2010 @click.pass_context
2011 def nst_delete1(ctx, name, force):
2012 """deletes a Network Slice Template (NST)
2013
2014 NAME: name or ID of the NST/NSTpkg to be deleted
2015 """
2016 logger.debug("")
2017 nst_delete(ctx, name, force)
2018
2019
2020 @cli_osm.command(name='netslice-template-delete', short_help='deletes a Network Slice Template (NST)')
2021 @click.argument('name')
2022 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
2023 @click.pass_context
2024 def nst_delete2(ctx, name, force):
2025 """deletes a Network Slice Template (NST)
2026
2027 NAME: name or ID of the NST/NSTpkg to be deleted
2028 """
2029 logger.debug("")
2030 nst_delete(ctx, name, force)
2031
2032
2033 def nsi_delete(ctx, name, force, wait):
2034 logger.debug("")
2035 # try:
2036 check_client_version(ctx.obj, ctx.command.name)
2037 ctx.obj.nsi.delete(name, force, wait=wait)
2038 # except ClientException as e:
2039 # print(str(e))
2040 # exit(1)
2041
2042
2043 @cli_osm.command(name='nsi-delete', short_help='deletes a Network Slice Instance (NSI)')
2044 @click.argument('name')
2045 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
2046 @click.option('--wait',
2047 required=False,
2048 default=False,
2049 is_flag=True,
2050 help='do not return the control immediately, but keep it '
2051 'until the operation is completed, or timeout')
2052 @click.pass_context
2053 def nsi_delete1(ctx, name, force, wait):
2054 """deletes a Network Slice Instance (NSI)
2055
2056 NAME: name or ID of the Network Slice instance to be deleted
2057 """
2058 logger.debug("")
2059 nsi_delete(ctx, name, force, wait=wait)
2060
2061
2062 @cli_osm.command(name='netslice-instance-delete', short_help='deletes a Network Slice Instance (NSI)')
2063 @click.argument('name')
2064 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
2065 @click.pass_context
2066 def nsi_delete2(ctx, name, force, wait):
2067 """deletes a Network Slice Instance (NSI)
2068
2069 NAME: name or ID of the Network Slice instance to be deleted
2070 """
2071 logger.debug("")
2072 nsi_delete(ctx, name, force, wait=wait)
2073
2074
2075 @cli_osm.command(name='pdu-delete', short_help='deletes a Physical Deployment Unit (PDU)')
2076 @click.argument('name')
2077 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
2078 @click.pass_context
2079 def pdu_delete(ctx, name, force):
2080 """deletes a Physical Deployment Unit (PDU)
2081
2082 NAME: name or ID of the PDU to be deleted
2083 """
2084 logger.debug("")
2085 # try:
2086 check_client_version(ctx.obj, ctx.command.name)
2087 ctx.obj.pdu.delete(name, force)
2088 # except ClientException as e:
2089 # print(str(e))
2090 # exit(1)
2091
2092
2093 #################
2094 # VIM operations
2095 #################
2096
2097 @cli_osm.command(name='vim-create', short_help='creates a new VIM account')
2098 @click.option('--name',
2099 prompt=True,
2100 help='Name to create datacenter')
2101 @click.option('--user',
2102 prompt=True,
2103 help='VIM username')
2104 @click.option('--password',
2105 prompt=True,
2106 hide_input=True,
2107 confirmation_prompt=True,
2108 help='VIM password')
2109 @click.option('--auth_url',
2110 prompt=True,
2111 help='VIM url')
2112 @click.option('--tenant',
2113 prompt=True,
2114 help='VIM tenant name')
2115 @click.option('--config',
2116 default=None,
2117 help='VIM specific config parameters')
2118 @click.option('--account_type',
2119 default='openstack',
2120 help='VIM type')
2121 @click.option('--description',
2122 default=None,
2123 help='human readable description')
2124 @click.option('--sdn_controller', default=None, help='Name or id of the SDN controller associated to this VIM account')
2125 @click.option('--sdn_port_mapping', default=None, help="File describing the port mapping between compute nodes' ports and switch ports")
2126 @click.option('--wait',
2127 required=False,
2128 default=False,
2129 is_flag=True,
2130 help='do not return the control immediately, but keep it '
2131 'until the operation is completed, or timeout')
2132 @click.pass_context
2133 def vim_create(ctx,
2134 name,
2135 user,
2136 password,
2137 auth_url,
2138 tenant,
2139 config,
2140 account_type,
2141 description,
2142 sdn_controller,
2143 sdn_port_mapping,
2144 wait):
2145 """creates a new VIM account"""
2146 logger.debug("")
2147 # try:
2148 if sdn_controller:
2149 check_client_version(ctx.obj, '--sdn_controller')
2150 if sdn_port_mapping:
2151 check_client_version(ctx.obj, '--sdn_port_mapping')
2152 vim = {}
2153 vim['vim-username'] = user
2154 vim['vim-password'] = password
2155 vim['vim-url'] = auth_url
2156 vim['vim-tenant-name'] = tenant
2157 vim['vim-type'] = account_type
2158 vim['description'] = description
2159 vim['config'] = config
2160 if sdn_controller or sdn_port_mapping:
2161 ctx.obj.vim.create(name, vim, sdn_controller, sdn_port_mapping, wait=wait)
2162 else:
2163 ctx.obj.vim.create(name, vim, wait=wait)
2164 # except ClientException as e:
2165 # print(str(e))
2166 # exit(1)
2167
2168
2169 @cli_osm.command(name='vim-update', short_help='updates a VIM account')
2170 @click.argument('name')
2171 @click.option('--newname', help='New name for the VIM account')
2172 @click.option('--user', help='VIM username')
2173 @click.option('--password', help='VIM password')
2174 @click.option('--auth_url', help='VIM url')
2175 @click.option('--tenant', help='VIM tenant name')
2176 @click.option('--config', help='VIM specific config parameters')
2177 @click.option('--account_type', help='VIM type')
2178 @click.option('--description', help='human readable description')
2179 @click.option('--sdn_controller', default=None, help='Name or id of the SDN controller associated to this VIM account')
2180 @click.option('--sdn_port_mapping', default=None, help="File describing the port mapping between compute nodes' ports and switch ports")
2181 @click.option('--wait',
2182 required=False,
2183 default=False,
2184 is_flag=True,
2185 help='do not return the control immediately, but keep it '
2186 'until the operation is completed, or timeout')
2187 @click.pass_context
2188 def vim_update(ctx,
2189 name,
2190 newname,
2191 user,
2192 password,
2193 auth_url,
2194 tenant,
2195 config,
2196 account_type,
2197 description,
2198 sdn_controller,
2199 sdn_port_mapping,
2200 wait):
2201 """updates a VIM account
2202
2203 NAME: name or ID of the VIM account
2204 """
2205 logger.debug("")
2206 # try:
2207 check_client_version(ctx.obj, ctx.command.name)
2208 vim = {}
2209 if newname: vim['name'] = newname
2210 if user: vim['vim_user'] = user
2211 if password: vim['vim_password'] = password
2212 if auth_url: vim['vim_url'] = auth_url
2213 if tenant: vim['vim-tenant-name'] = tenant
2214 if account_type: vim['vim_type'] = account_type
2215 if description: vim['description'] = description
2216 if config: vim['config'] = config
2217 ctx.obj.vim.update(name, vim, sdn_controller, sdn_port_mapping, wait=wait)
2218 # except ClientException as e:
2219 # print(str(e))
2220 # exit(1)
2221
2222
2223 @cli_osm.command(name='vim-delete', short_help='deletes a VIM account')
2224 @click.argument('name')
2225 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
2226 @click.option('--wait',
2227 required=False,
2228 default=False,
2229 is_flag=True,
2230 help='do not return the control immediately, but keep it '
2231 'until the operation is completed, or timeout')
2232 @click.pass_context
2233 def vim_delete(ctx, name, force, wait):
2234 """deletes a VIM account
2235
2236 NAME: name or ID of the VIM account to be deleted
2237 """
2238 logger.debug("")
2239 # try:
2240 if not force:
2241 ctx.obj.vim.delete(name, wait=wait)
2242 else:
2243 check_client_version(ctx.obj, '--force')
2244 ctx.obj.vim.delete(name, force, wait=wait)
2245 # except ClientException as e:
2246 # print(str(e))
2247 # exit(1)
2248
2249
2250 @cli_osm.command(name='vim-list', short_help='list all VIM accounts')
2251 #@click.option('--ro_update/--no_ro_update',
2252 # default=False,
2253 # help='update list from RO')
2254 @click.option('--filter', default=None,
2255 help='restricts the list to the VIM accounts matching the filter')
2256 @click.pass_context
2257 def vim_list(ctx, filter):
2258 """list all VIM accounts"""
2259 logger.debug("")
2260 if filter:
2261 check_client_version(ctx.obj, '--filter')
2262 # if ro_update:
2263 # check_client_version(ctx.obj, '--ro_update', 'v1')
2264 fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
2265 if fullclassname == 'osmclient.sol005.client.Client':
2266 resp = ctx.obj.vim.list(filter)
2267 # else:
2268 # resp = ctx.obj.vim.list(ro_update)
2269 table = PrettyTable(['vim name', 'uuid'])
2270 for vim in resp:
2271 table.add_row([vim['name'], vim['uuid']])
2272 table.align = 'l'
2273 print(table)
2274
2275
2276 @cli_osm.command(name='vim-show', short_help='shows the details of a VIM account')
2277 @click.argument('name')
2278 @click.pass_context
2279 def vim_show(ctx, name):
2280 """shows the details of a VIM account
2281
2282 NAME: name or ID of the VIM account
2283 """
2284 logger.debug("")
2285 # try:
2286 resp = ctx.obj.vim.get(name)
2287 if 'vim_password' in resp:
2288 resp['vim_password']='********'
2289 # except ClientException as e:
2290 # print(str(e))
2291 # exit(1)
2292
2293 table = PrettyTable(['key', 'attribute'])
2294 for k, v in list(resp.items()):
2295 table.add_row([k, wrap_text(text=json.dumps(v, indent=2),width=100)])
2296 table.align = 'l'
2297 print(table)
2298
2299
2300 ####################
2301 # WIM operations
2302 ####################
2303
2304 @cli_osm.command(name='wim-create', short_help='creates a new WIM account')
2305 @click.option('--name',
2306 prompt=True,
2307 help='Name for the WIM account')
2308 @click.option('--user',
2309 help='WIM username')
2310 @click.option('--password',
2311 help='WIM password')
2312 @click.option('--url',
2313 prompt=True,
2314 help='WIM url')
2315 # @click.option('--tenant',
2316 # help='wIM tenant name')
2317 @click.option('--config',
2318 default=None,
2319 help='WIM specific config parameters')
2320 @click.option('--wim_type',
2321 help='WIM type')
2322 @click.option('--description',
2323 default=None,
2324 help='human readable description')
2325 @click.option('--wim_port_mapping', default=None,
2326 help="File describing the port mapping between DC edge (datacenters, switches, ports) and WAN edge "
2327 "(WAN service endpoint id and info)")
2328 @click.option('--wait',
2329 required=False,
2330 default=False,
2331 is_flag=True,
2332 help='do not return the control immediately, but keep it '
2333 'until the operation is completed, or timeout')
2334 @click.pass_context
2335 def wim_create(ctx,
2336 name,
2337 user,
2338 password,
2339 url,
2340 # tenant,
2341 config,
2342 wim_type,
2343 description,
2344 wim_port_mapping,
2345 wait):
2346 """creates a new WIM account"""
2347 logger.debug("")
2348 # try:
2349 check_client_version(ctx.obj, ctx.command.name)
2350 # if sdn_controller:
2351 # check_client_version(ctx.obj, '--sdn_controller')
2352 # if sdn_port_mapping:
2353 # check_client_version(ctx.obj, '--sdn_port_mapping')
2354 wim = {}
2355 if user: wim['user'] = user
2356 if password: wim['password'] = password
2357 if url: wim['wim_url'] = url
2358 # if tenant: wim['tenant'] = tenant
2359 wim['wim_type'] = wim_type
2360 if description: wim['description'] = description
2361 if config: wim['config'] = config
2362 ctx.obj.wim.create(name, wim, wim_port_mapping, wait=wait)
2363 # except ClientException as e:
2364 # print(str(e))
2365 # exit(1)
2366
2367
2368 @cli_osm.command(name='wim-update', short_help='updates a WIM account')
2369 @click.argument('name')
2370 @click.option('--newname', help='New name for the WIM account')
2371 @click.option('--user', help='WIM username')
2372 @click.option('--password', help='WIM password')
2373 @click.option('--url', help='WIM url')
2374 @click.option('--config', help='WIM specific config parameters')
2375 @click.option('--wim_type', help='WIM type')
2376 @click.option('--description', help='human readable description')
2377 @click.option('--wim_port_mapping', default=None,
2378 help="File describing the port mapping between DC edge (datacenters, switches, ports) and WAN edge "
2379 "(WAN service endpoint id and info)")
2380 @click.option('--wait',
2381 required=False,
2382 default=False,
2383 is_flag=True,
2384 help='do not return the control immediately, but keep it until the operation is completed, or timeout')
2385 @click.pass_context
2386 def wim_update(ctx,
2387 name,
2388 newname,
2389 user,
2390 password,
2391 url,
2392 config,
2393 wim_type,
2394 description,
2395 wim_port_mapping,
2396 wait):
2397 """updates a WIM account
2398
2399 NAME: name or ID of the WIM account
2400 """
2401 logger.debug("")
2402 # try:
2403 check_client_version(ctx.obj, ctx.command.name)
2404 wim = {}
2405 if newname: wim['name'] = newname
2406 if user: wim['user'] = user
2407 if password: wim['password'] = password
2408 if url: wim['url'] = url
2409 # if tenant: wim['tenant'] = tenant
2410 if wim_type: wim['wim_type'] = wim_type
2411 if description: wim['description'] = description
2412 if config: wim['config'] = config
2413 ctx.obj.wim.update(name, wim, wim_port_mapping, wait=wait)
2414 # except ClientException as e:
2415 # print(str(e))
2416 # exit(1)
2417
2418
2419 @cli_osm.command(name='wim-delete', short_help='deletes a WIM account')
2420 @click.argument('name')
2421 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
2422 @click.option('--wait',
2423 required=False,
2424 default=False,
2425 is_flag=True,
2426 help='do not return the control immediately, but keep it until the operation is completed, or timeout')
2427 @click.pass_context
2428 def wim_delete(ctx, name, force, wait):
2429 """deletes a WIM account
2430
2431 NAME: name or ID of the WIM account to be deleted
2432 """
2433 logger.debug("")
2434 # try:
2435 check_client_version(ctx.obj, ctx.command.name)
2436 ctx.obj.wim.delete(name, force, wait=wait)
2437 # except ClientException as e:
2438 # print(str(e))
2439 # exit(1)
2440
2441
2442 @cli_osm.command(name='wim-list', short_help='list all WIM accounts')
2443 @click.option('--filter', default=None,
2444 help='restricts the list to the WIM accounts matching the filter')
2445 @click.pass_context
2446 def wim_list(ctx, filter):
2447 """list all WIM accounts"""
2448 logger.debug("")
2449 # try:
2450 check_client_version(ctx.obj, ctx.command.name)
2451 resp = ctx.obj.wim.list(filter)
2452 table = PrettyTable(['wim name', 'uuid'])
2453 for wim in resp:
2454 table.add_row([wim['name'], wim['uuid']])
2455 table.align = 'l'
2456 print(table)
2457 # except ClientException as e:
2458 # print(str(e))
2459 # exit(1)
2460
2461
2462 @cli_osm.command(name='wim-show', short_help='shows the details of a WIM account')
2463 @click.argument('name')
2464 @click.pass_context
2465 def wim_show(ctx, name):
2466 """shows the details of a WIM account
2467
2468 NAME: name or ID of the WIM account
2469 """
2470 logger.debug("")
2471 # try:
2472 check_client_version(ctx.obj, ctx.command.name)
2473 resp = ctx.obj.wim.get(name)
2474 if 'password' in resp:
2475 resp['wim_password']='********'
2476 # except ClientException as e:
2477 # print(str(e))
2478 # exit(1)
2479
2480 table = PrettyTable(['key', 'attribute'])
2481 for k, v in list(resp.items()):
2482 table.add_row([k, json.dumps(v, indent=2)])
2483 table.align = 'l'
2484 print(table)
2485
2486
2487 ####################
2488 # SDN controller operations
2489 ####################
2490
2491 @cli_osm.command(name='sdnc-create', short_help='creates a new SDN controller')
2492 @click.option('--name',
2493 prompt=True,
2494 help='Name to create sdn controller')
2495 @click.option('--type',
2496 prompt=True,
2497 help='SDN controller type')
2498 @click.option('--sdn_controller_version', # hidden=True,
2499 help='Deprecated. Use --config {version: sdn_controller_version}')
2500 @click.option('--url',
2501 help='URL in format http[s]://HOST:IP/')
2502 @click.option('--ip_address', # hidden=True,
2503 help='Deprecated. Use --url')
2504 @click.option('--port', # hidden=True,
2505 help='Deprecated. Use --url')
2506 @click.option('--switch_dpid', # hidden=True,
2507 help='Deprecated. Use --config {dpid: DPID}')
2508 @click.option('--config',
2509 help='Extra information for SDN in yaml format, as {dpid: (Openflow Datapath ID), version: version}')
2510 @click.option('--user',
2511 help='SDN controller username')
2512 @click.option('--password',
2513 hide_input=True,
2514 confirmation_prompt=True,
2515 help='SDN controller password')
2516 @click.option('--description', default=None, help='human readable description')
2517 @click.option('--wait',
2518 required=False,
2519 default=False,
2520 is_flag=True,
2521 help="do not return the control immediately, but keep it until the operation is completed, or timeout")
2522 @click.pass_context
2523 def sdnc_create(ctx, **kwargs):
2524 """creates a new SDN controller"""
2525 logger.debug("")
2526 sdncontroller = {x: kwargs[x] for x in kwargs if kwargs[x] and
2527 x not in ("wait", "ip_address", "port", "switch_dpid")}
2528 if kwargs.get("port"):
2529 print("option '--port' is deprecated, use '-url' instead")
2530 sdncontroller["port"] = int(kwargs["port"])
2531 if kwargs.get("ip_address"):
2532 print("option '--ip_address' is deprecated, use '-url' instead")
2533 sdncontroller["ip"] = kwargs["ip_address"]
2534 if kwargs.get("switch_dpid"):
2535 print("option '--switch_dpid' is deprecated, use '---config={dpid: DPID}' instead")
2536 sdncontroller["dpid"] = kwargs["switch_dpid"]
2537 if kwargs.get("sdn_controller_version"):
2538 print("option '--sdn_controller_version' is deprecated, use '---config={version: SDN_CONTROLLER_VERSION}'"
2539 " instead")
2540 # try:
2541 check_client_version(ctx.obj, ctx.command.name)
2542 ctx.obj.sdnc.create(kwargs["name"], sdncontroller, wait=kwargs["wait"])
2543 # except ClientException as e:
2544 # print(str(e))
2545 # exit(1)
2546
2547 @cli_osm.command(name='sdnc-update', short_help='updates an SDN controller')
2548 @click.argument('name')
2549 @click.option('--newname', help='New name for the SDN controller')
2550 @click.option('--description', default=None, help='human readable description')
2551 @click.option('--type', help='SDN controller type')
2552 @click.option('--url', help='URL in format http[s]://HOST:IP/')
2553 @click.option('--config', help='Extra information for SDN in yaml format, as '
2554 '{dpid: (Openflow Datapath ID), version: version}')
2555 @click.option('--user', help='SDN controller username')
2556 @click.option('--password', help='SDN controller password')
2557 @click.option('--ip_address', help='Deprecated. Use --url') # hidden=True
2558 @click.option('--port', help='Deprecated. Use --url') # hidden=True
2559 @click.option('--switch_dpid', help='Deprecated. Use --config {switch_dpid: DPID}') # hidden=True
2560 @click.option('--sdn_controller_version', help='Deprecated. Use --config {version: VERSION}') # hidden=True
2561 @click.option('--wait', required=False, default=False, is_flag=True,
2562 help='do not return the control immediately, but keep it until the operation is completed, or timeout')
2563 @click.pass_context
2564 def sdnc_update(ctx, **kwargs):
2565 """updates an SDN controller
2566
2567 NAME: name or ID of the SDN controller
2568 """
2569 logger.debug("")
2570 sdncontroller = {x: kwargs[x] for x in kwargs if kwargs[x] and
2571 x not in ("wait", "ip_address", "port", "switch_dpid", "new_name")}
2572 if kwargs.get("newname"):
2573 sdncontroller["name"] = kwargs["newname"]
2574 if kwargs.get("port"):
2575 print("option '--port' is deprecated, use '-url' instead")
2576 sdncontroller["port"] = int(kwargs["port"])
2577 if kwargs.get("ip_address"):
2578 print("option '--ip_address' is deprecated, use '-url' instead")
2579 sdncontroller["ip"] = kwargs["ip_address"]
2580 if kwargs.get("switch_dpid"):
2581 print("option '--switch_dpid' is deprecated, use '---config={dpid: DPID}' instead")
2582 sdncontroller["dpid"] = kwargs["switch_dpid"]
2583 if kwargs.get("sdn_controller_version"):
2584 print("option '--sdn_controller_version' is deprecated, use '---config={version: SDN_CONTROLLER_VERSION}'"
2585 " instead")
2586
2587 # try:
2588 check_client_version(ctx.obj, ctx.command.name)
2589 ctx.obj.sdnc.update(kwargs["name"], sdncontroller, wait=kwargs["wait"])
2590 # except ClientException as e:
2591 # print(str(e))
2592 # exit(1)
2593
2594
2595 @cli_osm.command(name='sdnc-delete', short_help='deletes an SDN controller')
2596 @click.argument('name')
2597 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
2598 @click.option('--wait', required=False, default=False, is_flag=True,
2599 help='do not return the control immediately, but keep it until the operation is completed, or timeout')
2600 @click.pass_context
2601 def sdnc_delete(ctx, name, force, wait):
2602 """deletes an SDN controller
2603
2604 NAME: name or ID of the SDN controller to be deleted
2605 """
2606 logger.debug("")
2607 # try:
2608 check_client_version(ctx.obj, ctx.command.name)
2609 ctx.obj.sdnc.delete(name, force, wait=wait)
2610 # except ClientException as e:
2611 # print(str(e))
2612 # exit(1)
2613
2614
2615 @cli_osm.command(name='sdnc-list', short_help='list all SDN controllers')
2616 @click.option('--filter', default=None,
2617 help="restricts the list to the SDN controllers matching the filter with format: 'k[.k..]=v[&k[.k]=v2]'")
2618 @click.pass_context
2619 def sdnc_list(ctx, filter):
2620 """list all SDN controllers"""
2621 logger.debug("")
2622 # try:
2623 check_client_version(ctx.obj, ctx.command.name)
2624 resp = ctx.obj.sdnc.list(filter)
2625 # except ClientException as e:
2626 # print(str(e))
2627 # exit(1)
2628 table = PrettyTable(['sdnc name', 'id'])
2629 for sdnc in resp:
2630 table.add_row([sdnc['name'], sdnc['_id']])
2631 table.align = 'l'
2632 print(table)
2633
2634
2635 @cli_osm.command(name='sdnc-show', short_help='shows the details of an SDN controller')
2636 @click.argument('name')
2637 @click.pass_context
2638 def sdnc_show(ctx, name):
2639 """shows the details of an SDN controller
2640
2641 NAME: name or ID of the SDN controller
2642 """
2643 logger.debug("")
2644 # try:
2645 check_client_version(ctx.obj, ctx.command.name)
2646 resp = ctx.obj.sdnc.get(name)
2647 # except ClientException as e:
2648 # print(str(e))
2649 # exit(1)
2650
2651 table = PrettyTable(['key', 'attribute'])
2652 for k, v in list(resp.items()):
2653 table.add_row([k, json.dumps(v, indent=2)])
2654 table.align = 'l'
2655 print(table)
2656
2657
2658 ###########################
2659 # K8s cluster operations
2660 ###########################
2661
2662 @cli_osm.command(name='k8scluster-add', short_help='adds a K8s cluster to OSM')
2663 @click.argument('name')
2664 @click.option('--creds',
2665 prompt=True,
2666 help='credentials file, i.e. a valid `.kube/config` file')
2667 @click.option('--version',
2668 prompt=True,
2669 help='Kubernetes version')
2670 @click.option('--vim',
2671 prompt=True,
2672 help='VIM target, the VIM where the cluster resides')
2673 @click.option('--k8s-nets',
2674 prompt=True,
2675 help='list of VIM networks, in JSON inline format, where the cluster is accessible via L3 routing, e.g. "{(k8s_net1:vim_network1) [,(k8s_net2:vim_network2) ...]}"')
2676 @click.option('--description',
2677 default=None,
2678 help='human readable description')
2679 @click.option('--namespace',
2680 default='kube-system',
2681 help='namespace to be used for its operation, defaults to `kube-system`')
2682 @click.option('--cni',
2683 default=None,
2684 help='list of CNI plugins, in JSON inline format, used in the cluster')
2685 #@click.option('--skip-init',
2686 # is_flag=True,
2687 # help='If set, K8s cluster is assumed to be ready for its use with OSM')
2688 #@click.option('--wait',
2689 # is_flag=True,
2690 # help='do not return the control immediately, but keep it until the operation is completed, or timeout')
2691 @click.pass_context
2692 def k8scluster_add(ctx,
2693 name,
2694 creds,
2695 version,
2696 vim,
2697 k8s_nets,
2698 description,
2699 namespace,
2700 cni):
2701 """adds a K8s cluster to OSM
2702
2703 NAME: name of the K8s cluster
2704 """
2705 # try:
2706 check_client_version(ctx.obj, ctx.command.name)
2707 cluster = {}
2708 cluster['name'] = name
2709 with open(creds, 'r') as cf:
2710 cluster['credentials'] = yaml.safe_load(cf.read())
2711 cluster['k8s_version'] = version
2712 cluster['vim_account'] = vim
2713 cluster['nets'] = yaml.safe_load(k8s_nets)
2714 cluster['description'] = description
2715 if namespace: cluster['namespace'] = namespace
2716 if cni: cluster['cni'] = yaml.safe_load(cni)
2717 ctx.obj.k8scluster.create(name, cluster)
2718 # except ClientException as e:
2719 # print(str(e))
2720 # exit(1)
2721
2722
2723 @cli_osm.command(name='k8scluster-update', short_help='updates a K8s cluster')
2724 @click.argument('name')
2725 @click.option('--newname', help='New name for the K8s cluster')
2726 @click.option('--creds', help='credentials file, i.e. a valid `.kube/config` file')
2727 @click.option('--version', help='Kubernetes version')
2728 @click.option('--vim', help='VIM target, the VIM where the cluster resides')
2729 @click.option('--k8s-nets', help='list of VIM networks, in JSON inline format, where the cluster is accessible via L3 routing, e.g. "{(k8s_net1:vim_network1) [,(k8s_net2:vim_network2) ...]}"')
2730 @click.option('--description', help='human readable description')
2731 @click.option('--namespace', help='namespace to be used for its operation, defaults to `kube-system`')
2732 @click.option('--cni', help='list of CNI plugins, in JSON inline format, used in the cluster')
2733 @click.pass_context
2734 def k8scluster_update(ctx,
2735 name,
2736 newname,
2737 creds,
2738 version,
2739 vim,
2740 k8s_nets,
2741 description,
2742 namespace,
2743 cni):
2744 """updates a K8s cluster
2745
2746 NAME: name or ID of the K8s cluster
2747 """
2748 # try:
2749 check_client_version(ctx.obj, ctx.command.name)
2750 cluster = {}
2751 if newname: cluster['name'] = newname
2752 if creds:
2753 with open(creds, 'r') as cf:
2754 cluster['credentials'] = yaml.safe_load(cf.read())
2755 if version: cluster['k8s_version'] = version
2756 if vim: cluster['vim_account'] = vim
2757 if k8s_nets: cluster['nets'] = yaml.safe_load(k8s_nets)
2758 if description: cluster['description'] = description
2759 if namespace: cluster['namespace'] = namespace
2760 if cni: cluster['cni'] = yaml.safe_load(cni)
2761 ctx.obj.k8scluster.update(name, cluster)
2762 # except ClientException as e:
2763 # print(str(e))
2764 # exit(1)
2765
2766
2767 @cli_osm.command(name='k8scluster-delete', short_help='deletes a K8s cluster')
2768 @click.argument('name')
2769 @click.option('--force', is_flag=True, help='forces the deletion from the DB (not recommended)')
2770 #@click.option('--wait',
2771 # is_flag=True,
2772 # help='do not return the control immediately, but keep it until the operation is completed, or timeout')
2773 @click.pass_context
2774 def k8scluster_delete(ctx, name, force):
2775 """deletes a K8s cluster
2776
2777 NAME: name or ID of the K8s cluster to be deleted
2778 """
2779 # try:
2780 check_client_version(ctx.obj, ctx.command.name)
2781 ctx.obj.k8scluster.delete(name, force=force)
2782 # except ClientException as e:
2783 # print(str(e))
2784 # exit(1)
2785
2786
2787 @cli_osm.command(name='k8scluster-list')
2788 @click.option('--filter', default=None,
2789 help='restricts the list to the K8s clusters matching the filter')
2790 @click.option('--literal', is_flag=True,
2791 help='print literally, no pretty table')
2792 @click.pass_context
2793 def k8scluster_list(ctx, filter, literal):
2794 """list all K8s clusters"""
2795 # try:
2796 check_client_version(ctx.obj, ctx.command.name)
2797 resp = ctx.obj.k8scluster.list(filter)
2798 if literal:
2799 print(yaml.safe_dump(resp))
2800 return
2801 table = PrettyTable(['Name', 'Id', 'Version', 'VIM', 'K8s-nets', 'Operational State', 'Description'])
2802 for cluster in resp:
2803 table.add_row([cluster['name'], cluster['_id'], cluster['k8s_version'], cluster['vim_account'],
2804 json.dumps(cluster['nets']), cluster["_admin"]["operationalState"],
2805 trunc_text(cluster.get('description',''),40)])
2806 table.align = 'l'
2807 print(table)
2808 # except ClientException as e:
2809 # print(str(e))
2810 # exit(1)
2811
2812
2813 @cli_osm.command(name='k8scluster-show', short_help='shows the details of a K8s cluster')
2814 @click.argument('name')
2815 @click.option('--literal', is_flag=True,
2816 help='print literally, no pretty table')
2817 @click.pass_context
2818 def k8scluster_show(ctx, name, literal):
2819 """shows the details of a K8s cluster
2820
2821 NAME: name or ID of the K8s cluster
2822 """
2823 # try:
2824 resp = ctx.obj.k8scluster.get(name)
2825 if literal:
2826 print(yaml.safe_dump(resp))
2827 return
2828 table = PrettyTable(['key', 'attribute'])
2829 for k, v in list(resp.items()):
2830 table.add_row([k, wrap_text(text=json.dumps(v, indent=2),width=100)])
2831 table.align = 'l'
2832 print(table)
2833 # except ClientException as e:
2834 # print(str(e))
2835 # exit(1)
2836
2837
2838
2839 ###########################
2840 # Repo operations
2841 ###########################
2842
2843 @cli_osm.command(name='repo-add', short_help='adds a repo to OSM')
2844 @click.argument('name')
2845 @click.argument('uri')
2846 @click.option('--type',
2847 type=click.Choice(['helm-chart', 'juju-bundle']),
2848 prompt=True,
2849 help='type of repo (helm-chart for Helm Charts, juju-bundle for Juju Bundles)')
2850 @click.option('--description',
2851 default=None,
2852 help='human readable description')
2853 #@click.option('--wait',
2854 # is_flag=True,
2855 # help='do not return the control immediately, but keep it until the operation is completed, or timeout')
2856 @click.pass_context
2857 def repo_add(ctx,
2858 name,
2859 uri,
2860 type,
2861 description):
2862 """adds a repo to OSM
2863
2864 NAME: name of the repo
2865 URI: URI of the repo
2866 """
2867 # try:
2868 check_client_version(ctx.obj, ctx.command.name)
2869 repo = {}
2870 repo['name'] = name
2871 repo['url'] = uri
2872 repo['type'] = type
2873 repo['description'] = description
2874 ctx.obj.repo.create(name, repo)
2875 # except ClientException as e:
2876 # print(str(e))
2877 # exit(1)
2878
2879
2880 @cli_osm.command(name='repo-update', short_help='updates a repo in OSM')
2881 @click.argument('name')
2882 @click.option('--newname', help='New name for the repo')
2883 @click.option('--uri', help='URI of the repo')
2884 @click.option('--type', type=click.Choice(['helm-chart', 'juju-bundle']),
2885 help='type of repo (helm-chart for Helm Charts, juju-bundle for Juju Bundles)')
2886 @click.option('--description', help='human readable description')
2887 #@click.option('--wait',
2888 # is_flag=True,
2889 # help='do not return the control immediately, but keep it until the operation is completed, or timeout')
2890 @click.pass_context
2891 def repo_update(ctx,
2892 name,
2893 newname,
2894 uri,
2895 type,
2896 description):
2897 """updates a repo in OSM
2898
2899 NAME: name of the repo
2900 """
2901 # try:
2902 check_client_version(ctx.obj, ctx.command.name)
2903 repo = {}
2904 if newname: repo['name'] = newname
2905 if uri: repo['uri'] = uri
2906 if type: repo['type'] = type
2907 if description: repo['description'] = description
2908 ctx.obj.repo.update(name, repo)
2909 # except ClientException as e:
2910 # print(str(e))
2911 # exit(1)
2912
2913
2914 @cli_osm.command(name='repo-delete', short_help='deletes a repo')
2915 @click.argument('name')
2916 @click.option('--force', is_flag=True, help='forces the deletion from the DB (not recommended)')
2917 #@click.option('--wait',
2918 # is_flag=True,
2919 # help='do not return the control immediately, but keep it until the operation is completed, or timeout')
2920 @click.pass_context
2921 def repo_delete(ctx, name, force):
2922 """deletes a repo
2923
2924 NAME: name or ID of the repo to be deleted
2925 """
2926 # try:
2927 check_client_version(ctx.obj, ctx.command.name)
2928 ctx.obj.repo.delete(name, force=force)
2929 # except ClientException as e:
2930 # print(str(e))
2931 # exit(1)
2932
2933
2934 @cli_osm.command(name='repo-list')
2935 @click.option('--filter', default=None,
2936 help='restricts the list to the repos matching the filter')
2937 @click.option('--literal', is_flag=True,
2938 help='print literally, no pretty table')
2939 @click.pass_context
2940 def repo_list(ctx, filter, literal):
2941 """list all repos"""
2942 # try:
2943 check_client_version(ctx.obj, ctx.command.name)
2944 resp = ctx.obj.repo.list(filter)
2945 if literal:
2946 print(yaml.safe_dump(resp))
2947 return
2948 table = PrettyTable(['Name', 'Id', 'Type', 'URI', 'Description'])
2949 for repo in resp:
2950 #cluster['k8s-nets'] = json.dumps(yaml.safe_load(cluster['k8s-nets']))
2951 table.add_row([repo['name'], repo['_id'], repo['type'], repo['url'], trunc_text(repo.get('description',''),40)])
2952 table.align = 'l'
2953 print(table)
2954 # except ClientException as e:
2955 # print(str(e))
2956 # exit(1)
2957
2958
2959 @cli_osm.command(name='repo-show', short_help='shows the details of a repo')
2960 @click.argument('name')
2961 @click.option('--literal', is_flag=True,
2962 help='print literally, no pretty table')
2963 @click.pass_context
2964 def repo_show(ctx, name, literal):
2965 """shows the details of a repo
2966
2967 NAME: name or ID of the repo
2968 """
2969 # try:
2970 resp = ctx.obj.repo.get(name)
2971 if literal:
2972 print(yaml.safe_dump(resp))
2973 return
2974 table = PrettyTable(['key', 'attribute'])
2975 for k, v in list(resp.items()):
2976 table.add_row([k, json.dumps(v, indent=2)])
2977 table.align = 'l'
2978 print(table)
2979 # except ClientException as e:
2980 # print(str(e))
2981 # exit(1)
2982
2983
2984
2985 ####################
2986 # Project mgmt operations
2987 ####################
2988
2989 @cli_osm.command(name='project-create', short_help='creates a new project')
2990 @click.argument('name')
2991 #@click.option('--description',
2992 # default='no description',
2993 # help='human readable description')
2994 @click.option('--domain-name', 'domain_name',
2995 default=None,
2996 help='assign to a domain')
2997 @click.pass_context
2998 def project_create(ctx, name, domain_name):
2999 """Creates a new project
3000
3001 NAME: name of the project
3002 DOMAIN_NAME: optional domain name for the project when keystone authentication is used
3003 """
3004 logger.debug("")
3005 project = {}
3006 project['name'] = name
3007 if domain_name:
3008 project['domain_name'] = domain_name
3009 # try:
3010 check_client_version(ctx.obj, ctx.command.name)
3011 ctx.obj.project.create(name, project)
3012 # except ClientException as e:
3013 # print(str(e))
3014 # exit(1)
3015
3016
3017 @cli_osm.command(name='project-delete', short_help='deletes a project')
3018 @click.argument('name')
3019 #@click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
3020 @click.pass_context
3021 def project_delete(ctx, name):
3022 """deletes a project
3023
3024 NAME: name or ID of the project to be deleted
3025 """
3026 logger.debug("")
3027 # try:
3028 check_client_version(ctx.obj, ctx.command.name)
3029 ctx.obj.project.delete(name)
3030 # except ClientException as e:
3031 # print(str(e))
3032 # exit(1)
3033
3034
3035 @cli_osm.command(name='project-list', short_help='list all projects')
3036 @click.option('--filter', default=None,
3037 help='restricts the list to the projects matching the filter')
3038 @click.pass_context
3039 def project_list(ctx, filter):
3040 """list all projects"""
3041 logger.debug("")
3042 # try:
3043 check_client_version(ctx.obj, ctx.command.name)
3044 resp = ctx.obj.project.list(filter)
3045 # except ClientException as e:
3046 # print(str(e))
3047 # exit(1)
3048 table = PrettyTable(['name', 'id'])
3049 for proj in resp:
3050 table.add_row([proj['name'], proj['_id']])
3051 table.align = 'l'
3052 print(table)
3053
3054
3055 @cli_osm.command(name='project-show', short_help='shows the details of a project')
3056 @click.argument('name')
3057 @click.pass_context
3058 def project_show(ctx, name):
3059 """shows the details of a project
3060
3061 NAME: name or ID of the project
3062 """
3063 logger.debug("")
3064 # try:
3065 check_client_version(ctx.obj, ctx.command.name)
3066 resp = ctx.obj.project.get(name)
3067 # except ClientException as e:
3068 # print(str(e))
3069 # exit(1)
3070
3071 table = PrettyTable(['key', 'attribute'])
3072 for k, v in resp.items():
3073 table.add_row([k, json.dumps(v, indent=2)])
3074 table.align = 'l'
3075 print(table)
3076
3077
3078 @cli_osm.command(name='project-update', short_help='updates a project (only the name can be updated)')
3079 @click.argument('project')
3080 @click.option('--name',
3081 prompt=True,
3082 help='new name for the project')
3083
3084 @click.pass_context
3085 def project_update(ctx, project, name):
3086 """
3087 Update a project name
3088
3089 :param ctx:
3090 :param project: id or name of the project to modify
3091 :param name: new name for the project
3092 :return:
3093 """
3094 logger.debug("")
3095 project_changes = {}
3096 project_changes['name'] = name
3097
3098 # try:
3099 check_client_version(ctx.obj, ctx.command.name)
3100 ctx.obj.project.update(project, project_changes)
3101 # except ClientException as e:
3102 # print(str(e))
3103
3104
3105 ####################
3106 # User mgmt operations
3107 ####################
3108
3109 @cli_osm.command(name='user-create', short_help='creates a new user')
3110 @click.argument('username')
3111 @click.option('--password',
3112 prompt=True,
3113 hide_input=True,
3114 confirmation_prompt=True,
3115 help='user password')
3116 @click.option('--projects',
3117 # prompt="Comma separate list of projects",
3118 multiple=True,
3119 callback=lambda ctx, param, value: ''.join(value).split(',') if all(len(x)==1 for x in value) else value,
3120 help='list of project ids that the user belongs to')
3121 @click.option('--project-role-mappings', 'project_role_mappings',
3122 default=None, multiple=True,
3123 help='creating user project/role(s) mapping')
3124 @click.option('--domain-name', 'domain_name',
3125 default=None,
3126 help='assign to a domain')
3127 @click.pass_context
3128 def user_create(ctx, username, password, projects, project_role_mappings, domain_name):
3129 """Creates a new user
3130
3131 \b
3132 USERNAME: name of the user
3133 PASSWORD: password of the user
3134 PROJECTS: projects assigned to user (internal only)
3135 PROJECT_ROLE_MAPPING: roles in projects assigned to user (keystone)
3136 DOMAIN_NAME: optional domain name for the user when keystone authentication is used
3137 """
3138 logger.debug("")
3139 user = {}
3140 user['username'] = username
3141 user['password'] = password
3142 user['projects'] = projects
3143 user['project_role_mappings'] = project_role_mappings
3144 if domain_name:
3145 user['domain_name'] = domain_name
3146
3147 # try:
3148 check_client_version(ctx.obj, ctx.command.name)
3149 ctx.obj.user.create(username, user)
3150 # except ClientException as e:
3151 # print(str(e))
3152 # exit(1)
3153
3154
3155 @cli_osm.command(name='user-update', short_help='updates user information')
3156 @click.argument('username')
3157 @click.option('--password',
3158 # prompt=True,
3159 # hide_input=True,
3160 # confirmation_prompt=True,
3161 help='user password')
3162 @click.option('--set-username', 'set_username',
3163 default=None,
3164 help='change username')
3165 @click.option('--set-project', 'set_project',
3166 default=None, multiple=True,
3167 help='create/replace the project,role(s) mapping for this project: \'project,role1,role2,...\'')
3168 @click.option('--remove-project', 'remove_project',
3169 default=None, multiple=True,
3170 help='removes project from user: \'project\'')
3171 @click.option('--add-project-role', 'add_project_role',
3172 default=None, multiple=True,
3173 help='adds project,role(s) mapping: \'project,role1,role2,...\'')
3174 @click.option('--remove-project-role', 'remove_project_role',
3175 default=None, multiple=True,
3176 help='removes project,role(s) mapping: \'project,role1,role2,...\'')
3177 @click.pass_context
3178 def user_update(ctx, username, password, set_username, set_project, remove_project,
3179 add_project_role, remove_project_role):
3180 """Update a user information
3181
3182 \b
3183 USERNAME: name of the user
3184 PASSWORD: new password
3185 SET_USERNAME: new username
3186 SET_PROJECT: creating mappings for project/role(s)
3187 REMOVE_PROJECT: deleting mappings for project/role(s)
3188 ADD_PROJECT_ROLE: adding mappings for project/role(s)
3189 REMOVE_PROJECT_ROLE: removing mappings for project/role(s)
3190 """
3191 logger.debug("")
3192 user = {}
3193 user['password'] = password
3194 user['username'] = set_username
3195 user['set-project'] = set_project
3196 user['remove-project'] = remove_project
3197 user['add-project-role'] = add_project_role
3198 user['remove-project-role'] = remove_project_role
3199
3200 # try:
3201 check_client_version(ctx.obj, ctx.command.name)
3202 ctx.obj.user.update(username, user)
3203 # except ClientException as e:
3204 # print(str(e))
3205 # exit(1)
3206
3207
3208 @cli_osm.command(name='user-delete', short_help='deletes a user')
3209 @click.argument('name')
3210 #@click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
3211 @click.pass_context
3212 def user_delete(ctx, name):
3213 """deletes a user
3214
3215 \b
3216 NAME: name or ID of the user to be deleted
3217 """
3218 logger.debug("")
3219 # try:
3220 check_client_version(ctx.obj, ctx.command.name)
3221 ctx.obj.user.delete(name)
3222 # except ClientException as e:
3223 # print(str(e))
3224 # exit(1)
3225
3226
3227 @cli_osm.command(name='user-list', short_help='list all users')
3228 @click.option('--filter', default=None,
3229 help='restricts the list to the users matching the filter')
3230 @click.pass_context
3231 def user_list(ctx, filter):
3232 """list all users"""
3233 # try:
3234 check_client_version(ctx.obj, ctx.command.name)
3235 resp = ctx.obj.user.list(filter)
3236 # except ClientException as e:
3237 # print(str(e))
3238 # exit(1)
3239 table = PrettyTable(['name', 'id'])
3240 for user in resp:
3241 table.add_row([user['username'], user['_id']])
3242 table.align = 'l'
3243 print(table)
3244
3245
3246 @cli_osm.command(name='user-show', short_help='shows the details of a user')
3247 @click.argument('name')
3248 @click.pass_context
3249 def user_show(ctx, name):
3250 """shows the details of a user
3251
3252 NAME: name or ID of the user
3253 """
3254 logger.debug("")
3255 # try:
3256 check_client_version(ctx.obj, ctx.command.name)
3257 resp = ctx.obj.user.get(name)
3258 if 'password' in resp:
3259 resp['password']='********'
3260 # except ClientException as e:
3261 # print(str(e))
3262 # exit(1)
3263
3264 table = PrettyTable(['key', 'attribute'])
3265 for k, v in resp.items():
3266 table.add_row([k, json.dumps(v, indent=2)])
3267 table.align = 'l'
3268 print(table)
3269
3270
3271 ####################
3272 # Fault Management operations
3273 ####################
3274
3275 @cli_osm.command(name='ns-alarm-create')
3276 @click.argument('name')
3277 @click.option('--ns', prompt=True, help='NS instance id or name')
3278 @click.option('--vnf', prompt=True,
3279 help='VNF name (VNF member index as declared in the NSD)')
3280 @click.option('--vdu', prompt=True,
3281 help='VDU name (VDU name as declared in the VNFD)')
3282 @click.option('--metric', prompt=True,
3283 help='Name of the metric (e.g. cpu_utilization)')
3284 @click.option('--severity', default='WARNING',
3285 help='severity of the alarm (WARNING, MINOR, MAJOR, CRITICAL, INDETERMINATE)')
3286 @click.option('--threshold_value', prompt=True,
3287 help='threshold value that, when crossed, an alarm is triggered')
3288 @click.option('--threshold_operator', prompt=True,
3289 help='threshold operator describing the comparison (GE, LE, GT, LT, EQ)')
3290 @click.option('--statistic', default='AVERAGE',
3291 help='statistic (AVERAGE, MINIMUM, MAXIMUM, COUNT, SUM)')
3292 @click.pass_context
3293 def ns_alarm_create(ctx, name, ns, vnf, vdu, metric, severity,
3294 threshold_value, threshold_operator, statistic):
3295 """creates a new alarm for a NS instance"""
3296 # TODO: Check how to validate threshold_value.
3297 # Should it be an integer (1-100), percentage, or decimal (0.01-1.00)?
3298 logger.debug("")
3299 # try:
3300 ns_instance = ctx.obj.ns.get(ns)
3301 alarm = {}
3302 alarm['alarm_name'] = name
3303 alarm['ns_id'] = ns_instance['_id']
3304 alarm['correlation_id'] = ns_instance['_id']
3305 alarm['vnf_member_index'] = vnf
3306 alarm['vdu_name'] = vdu
3307 alarm['metric_name'] = metric
3308 alarm['severity'] = severity
3309 alarm['threshold_value'] = int(threshold_value)
3310 alarm['operation'] = threshold_operator
3311 alarm['statistic'] = statistic
3312 check_client_version(ctx.obj, ctx.command.name)
3313 ctx.obj.ns.create_alarm(alarm)
3314 # except ClientException as e:
3315 # print(str(e))
3316 # exit(1)
3317
3318
3319 #@cli_osm.command(name='ns-alarm-delete')
3320 #@click.argument('name')
3321 #@click.pass_context
3322 #def ns_alarm_delete(ctx, name):
3323 # """deletes an alarm
3324 #
3325 # NAME: name of the alarm to be deleted
3326 # """
3327 # try:
3328 # check_client_version(ctx.obj, ctx.command.name)
3329 # ctx.obj.ns.delete_alarm(name)
3330 # except ClientException as e:
3331 # print(str(e))
3332 # exit(1)
3333
3334
3335 ####################
3336 # Performance Management operations
3337 ####################
3338
3339 @cli_osm.command(name='ns-metric-export', short_help='exports a metric to the internal OSM bus, which can be read by other apps')
3340 @click.option('--ns', prompt=True, help='NS instance id or name')
3341 @click.option('--vnf', prompt=True,
3342 help='VNF name (VNF member index as declared in the NSD)')
3343 @click.option('--vdu', prompt=True,
3344 help='VDU name (VDU name as declared in the VNFD)')
3345 @click.option('--metric', prompt=True,
3346 help='name of the metric (e.g. cpu_utilization)')
3347 #@click.option('--period', default='1w',
3348 # help='metric collection period (e.g. 20s, 30m, 2h, 3d, 1w)')
3349 @click.option('--interval', help='periodic interval (seconds) to export metrics continuously')
3350 @click.pass_context
3351 def ns_metric_export(ctx, ns, vnf, vdu, metric, interval):
3352 """exports a metric to the internal OSM bus, which can be read by other apps"""
3353 # TODO: Check how to validate interval.
3354 # Should it be an integer (seconds), or should a suffix (s,m,h,d,w) also be permitted?
3355 logger.debug("")
3356 # try:
3357 ns_instance = ctx.obj.ns.get(ns)
3358 metric_data = {}
3359 metric_data['ns_id'] = ns_instance['_id']
3360 metric_data['correlation_id'] = ns_instance['_id']
3361 metric_data['vnf_member_index'] = vnf
3362 metric_data['vdu_name'] = vdu
3363 metric_data['metric_name'] = metric
3364 metric_data['collection_unit'] = 'WEEK'
3365 metric_data['collection_period'] = 1
3366 check_client_version(ctx.obj, ctx.command.name)
3367 if not interval:
3368 print('{}'.format(ctx.obj.ns.export_metric(metric_data)))
3369 else:
3370 i = 1
3371 while True:
3372 print('{} {}'.format(ctx.obj.ns.export_metric(metric_data),i))
3373 time.sleep(int(interval))
3374 i+=1
3375 # except ClientException as e:
3376 # print(str(e))
3377 # exit(1)
3378
3379
3380 ####################
3381 # Other operations
3382 ####################
3383
3384 @cli_osm.command(name='version', short_help='shows client and server versions')
3385 @click.pass_context
3386 def get_version(ctx):
3387 """shows client and server versions"""
3388 # try:
3389 check_client_version(ctx.obj, "version")
3390 print ("Server version: {}".format(ctx.obj.get_version()))
3391 print ("Client version: {}".format(pkg_resources.get_distribution("osmclient").version))
3392 # except ClientException as e:
3393 # print(str(e))
3394 # exit(1)
3395
3396 @cli_osm.command(name='upload-package', short_help='uploads a VNF package or NS package')
3397 @click.argument('filename')
3398 @click.option('--skip-charm-build', default=False, is_flag=True,
3399 help='the charm will not be compiled, it is assumed to already exist')
3400 @click.pass_context
3401 def upload_package(ctx, filename, skip_charm_build):
3402 """uploads a vnf package or ns package
3403
3404 filename: vnf or ns package folder, or vnf or ns package file (tar.gz)
3405 """
3406 logger.debug("")
3407 # try:
3408 ctx.obj.package.upload(filename, skip_charm_build=skip_charm_build)
3409 fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
3410 if fullclassname != 'osmclient.sol005.client.Client':
3411 ctx.obj.package.wait_for_upload(filename)
3412 # except ClientException as e:
3413 # print(str(e))
3414 # exit(1)
3415
3416
3417 #@cli_osm.command(name='ns-scaling-show')
3418 #@click.argument('ns_name')
3419 #@click.pass_context
3420 #def show_ns_scaling(ctx, ns_name):
3421 # """shows the status of a NS scaling operation
3422 #
3423 # NS_NAME: name of the NS instance being scaled
3424 # """
3425 # try:
3426 # check_client_version(ctx.obj, ctx.command.name, 'v1')
3427 # resp = ctx.obj.ns.list()
3428 # except ClientException as e:
3429 # print(str(e))
3430 # exit(1)
3431 #
3432 # table = PrettyTable(
3433 # ['group-name',
3434 # 'instance-id',
3435 # 'operational status',
3436 # 'create-time',
3437 # 'vnfr ids'])
3438 #
3439 # for ns in resp:
3440 # if ns_name == ns['name']:
3441 # nsopdata = ctx.obj.ns.get_opdata(ns['id'])
3442 # scaling_records = nsopdata['nsr:nsr']['scaling-group-record']
3443 # for record in scaling_records:
3444 # if 'instance' in record:
3445 # instances = record['instance']
3446 # for inst in instances:
3447 # table.add_row(
3448 # [record['scaling-group-name-ref'],
3449 # inst['instance-id'],
3450 # inst['op-status'],
3451 # time.strftime('%Y-%m-%d %H:%M:%S',
3452 # time.localtime(
3453 # inst['create-time'])),
3454 # inst['vnfrs']])
3455 # table.align = 'l'
3456 # print(table)
3457
3458
3459 #@cli_osm.command(name='ns-scale')
3460 #@click.argument('ns_name')
3461 #@click.option('--ns_scale_group', prompt=True)
3462 #@click.option('--index', prompt=True)
3463 #@click.option('--wait',
3464 # required=False,
3465 # default=False,
3466 # is_flag=True,
3467 # help='do not return the control immediately, but keep it \
3468 # until the operation is completed, or timeout')
3469 #@click.pass_context
3470 #def ns_scale(ctx, ns_name, ns_scale_group, index, wait):
3471 # """scales NS
3472 #
3473 # NS_NAME: name of the NS instance to be scaled
3474 # """
3475 # try:
3476 # check_client_version(ctx.obj, ctx.command.name, 'v1')
3477 # ctx.obj.ns.scale(ns_name, ns_scale_group, index, wait=wait)
3478 # except ClientException as e:
3479 # print(str(e))
3480 # exit(1)
3481
3482
3483 #@cli_osm.command(name='config-agent-list')
3484 #@click.pass_context
3485 #def config_agent_list(ctx):
3486 # """list config agents"""
3487 # try:
3488 # check_client_version(ctx.obj, ctx.command.name, 'v1')
3489 # except ClientException as e:
3490 # print(str(e))
3491 # exit(1)
3492 # table = PrettyTable(['name', 'account-type', 'details'])
3493 # for account in ctx.obj.vca.list():
3494 # table.add_row(
3495 # [account['name'],
3496 # account['account-type'],
3497 # account['juju']])
3498 # table.align = 'l'
3499 # print(table)
3500
3501
3502 #@cli_osm.command(name='config-agent-delete')
3503 #@click.argument('name')
3504 #@click.pass_context
3505 #def config_agent_delete(ctx, name):
3506 # """deletes a config agent
3507 #
3508 # NAME: name of the config agent to be deleted
3509 # """
3510 # try:
3511 # check_client_version(ctx.obj, ctx.command.name, 'v1')
3512 # ctx.obj.vca.delete(name)
3513 # except ClientException as e:
3514 # print(str(e))
3515 # exit(1)
3516
3517
3518 #@cli_osm.command(name='config-agent-add')
3519 #@click.option('--name',
3520 # prompt=True)
3521 #@click.option('--account_type',
3522 # prompt=True)
3523 #@click.option('--server',
3524 # prompt=True)
3525 #@click.option('--user',
3526 # prompt=True)
3527 #@click.option('--secret',
3528 # prompt=True,
3529 # hide_input=True,
3530 # confirmation_prompt=True)
3531 #@click.pass_context
3532 #def config_agent_add(ctx, name, account_type, server, user, secret):
3533 # """adds a config agent"""
3534 # try:
3535 # check_client_version(ctx.obj, ctx.command.name, 'v1')
3536 # ctx.obj.vca.create(name, account_type, server, user, secret)
3537 # except ClientException as e:
3538 # print(str(e))
3539 # exit(1)
3540
3541
3542 #@cli_osm.command(name='ro-dump')
3543 #@click.pass_context
3544 #def ro_dump(ctx):
3545 # """shows RO agent information"""
3546 # check_client_version(ctx.obj, ctx.command.name, 'v1')
3547 # resp = ctx.obj.vim.get_resource_orchestrator()
3548 # table = PrettyTable(['key', 'attribute'])
3549 # for k, v in list(resp.items()):
3550 # table.add_row([k, json.dumps(v, indent=2)])
3551 # table.align = 'l'
3552 # print(table)
3553
3554
3555 #@cli_osm.command(name='vcs-list')
3556 #@click.pass_context
3557 #def vcs_list(ctx):
3558 # check_client_version(ctx.obj, ctx.command.name, 'v1')
3559 # resp = ctx.obj.utils.get_vcs_info()
3560 # table = PrettyTable(['component name', 'state'])
3561 # for component in resp:
3562 # table.add_row([component['component_name'], component['state']])
3563 # table.align = 'l'
3564 # print(table)
3565
3566
3567 @cli_osm.command(name='ns-action', short_help='executes an action/primitive over a NS instance')
3568 @click.argument('ns_name')
3569 @click.option('--vnf_name', default=None, help='member-vnf-index if the target is a vnf instead of a ns)')
3570 @click.option('--kdu_name', default=None, help='kdu-name if the target is a kdu)')
3571 @click.option('--vdu_id', default=None, help='vdu-id if the target is a vdu')
3572 @click.option('--vdu_count', default=None, help='number of vdu instance of this vdu_id')
3573 @click.option('--action_name', prompt=True, help='action name')
3574 @click.option('--params', default=None, help='action params in YAML/JSON inline string')
3575 @click.option('--params_file', default=None, help='YAML/JSON file with action params')
3576 @click.option('--wait',
3577 required=False,
3578 default=False,
3579 is_flag=True,
3580 help='do not return the control immediately, but keep it until the operation is completed, or timeout')
3581 @click.pass_context
3582 def ns_action(ctx,
3583 ns_name,
3584 vnf_name,
3585 kdu_name,
3586 vdu_id,
3587 vdu_count,
3588 action_name,
3589 params,
3590 params_file,
3591 wait):
3592 """executes an action/primitive over a NS instance
3593
3594 NS_NAME: name or ID of the NS instance
3595 """
3596 logger.debug("")
3597 # try:
3598 check_client_version(ctx.obj, ctx.command.name)
3599 op_data = {}
3600 if vnf_name:
3601 op_data['member_vnf_index'] = vnf_name
3602 if kdu_name:
3603 op_data['kdu_name'] = kdu_name
3604 if vdu_id:
3605 op_data['vdu_id'] = vdu_id
3606 if vdu_count:
3607 op_data['vdu_count_index'] = vdu_count
3608 op_data['primitive'] = action_name
3609 if params_file:
3610 with open(params_file, 'r') as pf:
3611 params = pf.read()
3612 if params:
3613 op_data['primitive_params'] = yaml.safe_load(params)
3614 else:
3615 op_data['primitive_params'] = {}
3616 print(ctx.obj.ns.exec_op(ns_name, op_name='action', op_data=op_data, wait=wait))
3617
3618 # except ClientException as e:
3619 # print(str(e))
3620 # exit(1)
3621
3622
3623 @cli_osm.command(name='vnf-scale', short_help='executes a VNF scale (adding/removing VDUs)')
3624 @click.argument('ns_name')
3625 @click.argument('vnf_name')
3626 @click.option('--scaling-group', prompt=True, help="scaling-group-descriptor name to use")
3627 @click.option('--scale-in', default=False, is_flag=True, help="performs a scale in operation")
3628 @click.option('--scale-out', default=False, is_flag=True, help="performs a scale out operation (by default)")
3629 @click.pass_context
3630 def vnf_scale(ctx,
3631 ns_name,
3632 vnf_name,
3633 scaling_group,
3634 scale_in,
3635 scale_out):
3636 """
3637 Executes a VNF scale (adding/removing VDUs)
3638
3639 \b
3640 NS_NAME: name or ID of the NS instance.
3641 VNF_NAME: member-vnf-index in the NS to be scaled.
3642 """
3643 logger.debug("")
3644 # try:
3645 check_client_version(ctx.obj, ctx.command.name)
3646 if not scale_in and not scale_out:
3647 scale_out = True
3648 ctx.obj.ns.scale_vnf(ns_name, vnf_name, scaling_group, scale_in, scale_out)
3649 # except ClientException as e:
3650 # print(str(e))
3651 # exit(1)
3652
3653
3654 ##############################
3655 # Role Management Operations #
3656 ##############################
3657
3658 @cli_osm.command(name='role-create', short_help='creates a new role')
3659 @click.argument('name')
3660 @click.option('--permissions',
3661 default=None,
3662 help='role permissions using a dictionary')
3663 @click.pass_context
3664 def role_create(ctx, name, permissions):
3665 """
3666 Creates a new role.
3667
3668 \b
3669 NAME: Name or ID of the role.
3670 DEFINITION: Definition of grant/denial of access to resources.
3671 """
3672 logger.debug("")
3673 # try:
3674 check_client_version(ctx.obj, ctx.command.name)
3675 ctx.obj.role.create(name, permissions)
3676 # except ClientException as e:
3677 # print(str(e))
3678 # exit(1)
3679
3680
3681 @cli_osm.command(name='role-update', short_help='updates a role')
3682 @click.argument('name')
3683 @click.option('--set-name',
3684 default=None,
3685 help='change name of rle')
3686 # @click.option('--permissions',
3687 # default=None,
3688 # help='provide a yaml format dictionary with incremental changes. Values can be bool or None to delete')
3689 @click.option('--add',
3690 default=None,
3691 help='yaml format dictionary with permission: True/False to access grant/denial')
3692 @click.option('--remove',
3693 default=None,
3694 help='yaml format list to remove a permission')
3695 @click.pass_context
3696 def role_update(ctx, name, set_name, add, remove):
3697 """
3698 Updates a role.
3699
3700 \b
3701 NAME: Name or ID of the role.
3702 DEFINITION: Definition overwrites the old definition.
3703 ADD: Grant/denial of access to resource to add.
3704 REMOVE: Grant/denial of access to resource to remove.
3705 """
3706 logger.debug("")
3707 # try:
3708 check_client_version(ctx.obj, ctx.command.name)
3709 ctx.obj.role.update(name, set_name, None, add, remove)
3710 # except ClientException as e:
3711 # print(str(e))
3712 # exit(1)
3713
3714
3715 @cli_osm.command(name='role-delete', short_help='deletes a role')
3716 @click.argument('name')
3717 # @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
3718 @click.pass_context
3719 def role_delete(ctx, name):
3720 """
3721 Deletes a role.
3722
3723 \b
3724 NAME: Name or ID of the role.
3725 """
3726 logger.debug("")
3727 # try:
3728 check_client_version(ctx.obj, ctx.command.name)
3729 ctx.obj.role.delete(name)
3730 # except ClientException as e:
3731 # print(str(e))
3732 # exit(1)
3733
3734
3735 @cli_osm.command(name='role-list', short_help='list all roles')
3736 @click.option('--filter', default=None,
3737 help='restricts the list to the projects matching the filter')
3738 @click.pass_context
3739 def role_list(ctx, filter):
3740 """
3741 List all roles.
3742 """
3743 logger.debug("")
3744 # try:
3745 check_client_version(ctx.obj, ctx.command.name)
3746 resp = ctx.obj.role.list(filter)
3747 # except ClientException as e:
3748 # print(str(e))
3749 # exit(1)
3750 table = PrettyTable(['name', 'id'])
3751 for role in resp:
3752 table.add_row([role['name'], role['_id']])
3753 table.align = 'l'
3754 print(table)
3755
3756
3757 @cli_osm.command(name='role-show', short_help='show specific role')
3758 @click.argument('name')
3759 @click.pass_context
3760 def role_show(ctx, name):
3761 """
3762 Shows the details of a role.
3763
3764 \b
3765 NAME: Name or ID of the role.
3766 """
3767 logger.debug("")
3768 # try:
3769 check_client_version(ctx.obj, ctx.command.name)
3770 resp = ctx.obj.role.get(name)
3771 # except ClientException as e:
3772 # print(str(e))
3773 # exit(1)
3774
3775 table = PrettyTable(['key', 'attribute'])
3776 for k, v in resp.items():
3777 table.add_row([k, json.dumps(v, indent=2)])
3778 table.align = 'l'
3779 print(table)
3780
3781
3782 @cli_osm.command(name='package-create',
3783 short_help='Create a package descriptor')
3784 @click.argument('package-type')
3785 @click.argument('package-name')
3786 @click.option('--base-directory',
3787 default='.',
3788 help=('(NS/VNF/NST) Set the location for package creation. Default: "."'))
3789 @click.option('--image',
3790 default="image-name",
3791 help='(VNF) Set the name of the vdu image. Default "image-name"')
3792 @click.option('--vdus',
3793 default=1,
3794 help='(VNF) Set the number of vdus in a VNF. Default 1')
3795 @click.option('--vcpu',
3796 default=1,
3797 help='(VNF) Set the number of virtual CPUs in a vdu. Default 1')
3798 @click.option('--memory',
3799 default=1024,
3800 help='(VNF) Set the memory size (MB) of the vdu. Default 1024')
3801 @click.option('--storage',
3802 default=10,
3803 help='(VNF) Set the disk size (GB) of the vdu. Default 10')
3804 @click.option('--interfaces',
3805 default=0,
3806 help='(VNF) Set the number of additional interfaces apart from the management interface. Default 0')
3807 @click.option('--vendor',
3808 default="OSM",
3809 help='(NS/VNF) Set the descriptor vendor. Default "OSM"')
3810 @click.option('--override',
3811 default=False,
3812 is_flag=True,
3813 help='(NS/VNF/NST) Flag for overriding the package if exists.')
3814 @click.option('--detailed',
3815 is_flag=True,
3816 default=False,
3817 help='(NS/VNF/NST) Flag for generating descriptor .yaml with all possible commented options')
3818 @click.option('--netslice-subnets',
3819 default=1,
3820 help='(NST) Number of netslice subnets. Default 1')
3821 @click.option('--netslice-vlds',
3822 default=1,
3823 help='(NST) Number of netslice vlds. Default 1')
3824 @click.pass_context
3825 def package_create(ctx,
3826 package_type,
3827 base_directory,
3828 package_name,
3829 override,
3830 image,
3831 vdus,
3832 vcpu,
3833 memory,
3834 storage,
3835 interfaces,
3836 vendor,
3837 detailed,
3838 netslice_subnets,
3839 netslice_vlds):
3840 """
3841 Creates an OSM NS, VNF, NST package
3842
3843 \b
3844 PACKAGE_TYPE: Package to be created: NS, VNF or NST.
3845 PACKAGE_NAME: Name of the package to create the folder with the content.
3846 """
3847
3848 # try:
3849 check_client_version(ctx.obj, ctx.command.name)
3850 print("Creating the {} structure: {}/{}".format(package_type.upper(), base_directory, package_name))
3851 resp = ctx.obj.package_tool.create(package_type,
3852 base_directory,
3853 package_name,
3854 override=override,
3855 image=image,
3856 vdus=vdus,
3857 vcpu=vcpu,
3858 memory=memory,
3859 storage=storage,
3860 interfaces=interfaces,
3861 vendor=vendor,
3862 detailed=detailed,
3863 netslice_subnets=netslice_subnets,
3864 netslice_vlds=netslice_vlds)
3865 print(resp)
3866 # except ClientException as inst:
3867 # print("ERROR: {}".format(inst))
3868 # exit(1)
3869
3870 @cli_osm.command(name='package-validate',
3871 short_help='Validate a package descriptor')
3872 @click.argument('base-directory',
3873 default=".",
3874 required=False)
3875 @click.option('--recursive/--no-recursive',
3876 default=True,
3877 help='The activated recursive option will validate the yaml files'
3878 ' within the indicated directory and in its subdirectories')
3879 @click.pass_context
3880 def package_validate(ctx,
3881 base_directory,
3882 recursive):
3883 """
3884 Validate descriptors given a base directory.
3885
3886 \b
3887 BASE_DIRECTORY: Stub folder for NS, VNF or NST package.
3888 """
3889 # try:
3890 check_client_version(ctx.obj, ctx.command.name)
3891 results = ctx.obj.package_tool.validate(base_directory, recursive)
3892 table = PrettyTable()
3893 table.field_names = ["TYPE", "PATH", "VALID", "ERROR"]
3894 # Print the dictionary generated by the validation function
3895 for result in results:
3896 table.add_row([result["type"], result["path"], result["valid"], result["error"]])
3897 table.sortby = "VALID"
3898 table.align["PATH"] = "l"
3899 table.align["TYPE"] = "l"
3900 table.align["ERROR"] = "l"
3901 print(table)
3902 # except ClientException as inst:
3903 # print("ERROR: {}".format(inst))
3904 # exit(1)
3905
3906 @cli_osm.command(name='package-build',
3907 short_help='Build the tar.gz of the package')
3908 @click.argument('package-folder')
3909 @click.option('--skip-validation',
3910 default=False,
3911 is_flag=True,
3912 help='skip package validation')
3913 @click.option('--skip-charm-build', default=False, is_flag=True,
3914 help='the charm will not be compiled, it is assumed to already exist')
3915 @click.pass_context
3916 def package_build(ctx,
3917 package_folder,
3918 skip_validation,
3919 skip_charm_build):
3920 """
3921 Build the package NS, VNF given the package_folder.
3922
3923 \b
3924 PACKAGE_FOLDER: Folder of the NS, VNF or NST to be packaged
3925 """
3926 # try:
3927 check_client_version(ctx.obj, ctx.command.name)
3928 results = ctx.obj.package_tool.build(package_folder,
3929 skip_validation=skip_validation,
3930 skip_charm_build=skip_charm_build)
3931 print(results)
3932 # except ClientException as inst:
3933 # print("ERROR: {}".format(inst))
3934 # exit(1)
3935
3936
3937 def cli():
3938 try:
3939 cli_osm()
3940 exit(0)
3941 except pycurl.error as exc:
3942 print(exc)
3943 print('Maybe "--hostname" option or OSM_HOSTNAME environment variable needs to be specified')
3944 except ClientException as exc:
3945 print("ERROR: {}".format(exc))
3946 except (FileNotFoundError, PermissionError) as exc:
3947 print("Cannot open file: {}".format(exc))
3948 except yaml.YAMLError as exc:
3949 print("Invalid YAML format: {}".format(exc))
3950 exit(1)
3951 # TODO capture other controlled exceptions here
3952 # TODO remove the ClientException captures from all places, unless they do something different
3953
3954
3955 if __name__ == '__main__':
3956 cli()
3957