Adding role management
[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
30
31 def check_client_version(obj, what, version='sol005'):
32 """
33 Checks the version of the client object and raises error if it not the expected.
34
35 :param obj: the client object
36 :what: the function or command under evaluation (used when an error is raised)
37 :return: -
38 :raises ClientError: if the specified version does not match the client version
39 """
40 fullclassname = obj.__module__ + "." + obj.__class__.__name__
41 message = 'The following commands or options are only supported with the option "--sol005": {}'.format(what)
42 if version == 'v1':
43 message = 'The following commands or options are not supported when using option "--sol005": {}'.format(what)
44 if fullclassname != 'osmclient.{}.client.Client'.format(version):
45 raise ClientException(message)
46 return
47
48
49 @click.group()
50 @click.option('--hostname',
51 default="127.0.0.1",
52 envvar='OSM_HOSTNAME',
53 help='hostname of server. ' +
54 'Also can set OSM_HOSTNAME in environment')
55 @click.option('--sol005/--no-sol005',
56 default=True,
57 envvar='OSM_SOL005',
58 help='Use ETSI NFV SOL005 API (default) or the previous SO API. ' +
59 'Also can set OSM_SOL005 in environment')
60 @click.option('--user',
61 default=None,
62 envvar='OSM_USER',
63 help='user (only from Release FOUR, defaults to admin). ' +
64 'Also can set OSM_USER in environment')
65 @click.option('--password',
66 default=None,
67 envvar='OSM_PASSWORD',
68 help='password (only from Release FOUR, defaults to admin). ' +
69 'Also can set OSM_PASSWORD in environment')
70 @click.option('--project',
71 default=None,
72 envvar='OSM_PROJECT',
73 help='project (only from Release FOUR, defaults to admin). ' +
74 'Also can set OSM_PROJECT in environment')
75 @click.option('--so-port',
76 default=None,
77 envvar='OSM_SO_PORT',
78 help='hostname of server. ' +
79 'Also can set OSM_SO_PORT in environment')
80 @click.option('--so-project',
81 default=None,
82 envvar='OSM_SO_PROJECT',
83 help='Project Name in SO. ' +
84 'Also can set OSM_SO_PROJECT in environment')
85 @click.option('--ro-hostname',
86 default=None,
87 envvar='OSM_RO_HOSTNAME',
88 help='hostname of RO server. ' +
89 'Also can set OSM_RO_HOSTNAME in environment')
90 @click.option('--ro-port',
91 default=None,
92 envvar='OSM_RO_PORT',
93 help='hostname of RO server. ' +
94 'Also can set OSM_RO_PORT in environment')
95 @click.pass_context
96 def cli(ctx, hostname, sol005, user, password, project, so_port, so_project, ro_hostname, ro_port):
97 if hostname is None:
98 print((
99 "either hostname option or OSM_HOSTNAME " +
100 "environment variable needs to be specified"))
101 exit(1)
102 kwargs={}
103 if so_port is not None:
104 kwargs['so_port']=so_port
105 if so_project is not None:
106 kwargs['so_project']=so_project
107 if ro_hostname is not None:
108 kwargs['ro_host']=ro_hostname
109 if ro_port is not None:
110 kwargs['ro_port']=ro_port
111 if user is not None:
112 kwargs['user']=user
113 if password is not None:
114 kwargs['password']=password
115 if project is not None:
116 kwargs['project']=project
117
118 ctx.obj = client.Client(host=hostname, sol005=sol005, **kwargs)
119
120
121 ####################
122 # LIST operations
123 ####################
124
125 @cli.command(name='ns-list')
126 @click.option('--filter', default=None,
127 help='restricts the list to the NS instances matching the filter.')
128 @click.pass_context
129 def ns_list(ctx, filter):
130 """list all NS instances
131
132 \b
133 Options:
134 --filter filterExpr Restricts the list to the NS instances matching the filter
135
136 \b
137 filterExpr consists of one or more strings formatted according to "simpleFilterExpr",
138 concatenated using the "&" character:
139
140 \b
141 filterExpr := <simpleFilterExpr>["&"<simpleFilterExpr>]*
142 simpleFilterExpr := <attrName>["."<attrName>]*["."<op>]"="<value>[","<value>]*
143 op := "eq" | "neq" | "gt" | "lt" | "gte" | "lte" | "cont" | "ncont"
144 attrName := string
145 value := scalar value
146
147 \b
148 where:
149 * zero or more occurrences
150 ? zero or one occurrence
151 [] grouping of expressions to be used with ? and *
152 "" quotation marks for marking string constants
153 <> name separator
154
155 \b
156 "AttrName" is the name of one attribute in the data type that defines the representation
157 of the resource. The dot (".") character in "simpleFilterExpr" allows concatenation of
158 <attrName> entries to filter by attributes deeper in the hierarchy of a structured document.
159 "Op" stands for the comparison operator. If the expression has concatenated <attrName>
160 entries, it means that the operator "op" is applied to the attribute addressed by the last
161 <attrName> entry included in the concatenation. All simple filter expressions are combined
162 by the "AND" logical operator. In a concatenation of <attrName> entries in a <simpleFilterExpr>,
163 the rightmost "attrName" entry in a "simpleFilterExpr" is called "leaf attribute". The
164 concatenation of all "attrName" entries except the leaf attribute is called the "attribute
165 prefix". If an attribute referenced in an expression is an array, an object that contains a
166 corresponding array shall be considered to match the expression if any of the elements in the
167 array matches all expressions that have the same attribute prefix.
168
169 \b
170 Filter examples:
171 --filter admin-status=ENABLED
172 --filter nsd-ref=<NSD_NAME>
173 --filter nsd.vendor=<VENDOR>
174 --filter nsd.vendor=<VENDOR>&nsd-ref=<NSD_NAME>
175 --filter nsd.constituent-vnfd.vnfd-id-ref=<VNFD_NAME>
176 """
177 if filter:
178 check_client_version(ctx.obj, '--filter')
179 resp = ctx.obj.ns.list(filter)
180 else:
181 resp = ctx.obj.ns.list()
182 table = PrettyTable(
183 ['ns instance name',
184 'id',
185 'operational status',
186 'config status',
187 'detailed status'])
188 for ns in resp:
189 fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
190 if fullclassname == 'osmclient.sol005.client.Client':
191 nsr = ns
192 nsr_name = nsr['name']
193 nsr_id = nsr['_id']
194 else:
195 nsopdata = ctx.obj.ns.get_opdata(ns['id'])
196 nsr = nsopdata['nsr:nsr']
197 nsr_name = nsr['name-ref']
198 nsr_id = nsr['ns-instance-config-ref']
199 opstatus = nsr['operational-status'] if 'operational-status' in nsr else 'Not found'
200 configstatus = nsr['config-status'] if 'config-status' in nsr else 'Not found'
201 detailed_status = nsr['detailed-status'] if 'detailed-status' in nsr else 'Not found'
202 if configstatus == "config_not_needed":
203 configstatus = "configured (no charms)"
204 table.add_row(
205 [nsr_name,
206 nsr_id,
207 opstatus,
208 configstatus,
209 detailed_status])
210 table.align = 'l'
211 print(table)
212
213
214 def nsd_list(ctx, filter):
215 if filter:
216 check_client_version(ctx.obj, '--filter')
217 resp = ctx.obj.nsd.list(filter)
218 else:
219 resp = ctx.obj.nsd.list()
220 # print yaml.safe_dump(resp)
221 table = PrettyTable(['nsd name', 'id'])
222 fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
223 if fullclassname == 'osmclient.sol005.client.Client':
224 for ns in resp:
225 name = ns['name'] if 'name' in ns else '-'
226 table.add_row([name, ns['_id']])
227 else:
228 for ns in resp:
229 table.add_row([ns['name'], ns['id']])
230 table.align = 'l'
231 print(table)
232
233
234 @cli.command(name='nsd-list')
235 @click.option('--filter', default=None,
236 help='restricts the list to the NSD/NSpkg matching the filter')
237 @click.pass_context
238 def nsd_list1(ctx, filter):
239 """list all NSD/NS pkg in the system"""
240 nsd_list(ctx, filter)
241
242
243 @cli.command(name='nspkg-list')
244 @click.option('--filter', default=None,
245 help='restricts the list to the NSD/NSpkg matching the filter')
246 @click.pass_context
247 def nsd_list2(ctx, filter):
248 """list all NSD/NS pkg in the system"""
249 nsd_list(ctx, filter)
250
251
252 def vnfd_list(ctx, nf_type, filter):
253 if nf_type:
254 check_client_version(ctx.obj, '--nf_type')
255 elif filter:
256 check_client_version(ctx.obj, '--filter')
257 if nf_type:
258 if nf_type == "vnf":
259 nf_filter = "_admin.type=vnfd"
260 elif nf_type == "pnf":
261 nf_filter = "_admin.type=pnfd"
262 elif nf_type == "hnf":
263 nf_filter = "_admin.type=hnfd"
264 else:
265 raise ClientException('wrong value for "--nf_type" option, allowed values: vnf, pnf, hnf')
266 if filter:
267 filter = '{}&{}'.format(nf_filter, filter)
268 else:
269 filter = nf_filter
270 if filter:
271 resp = ctx.obj.vnfd.list(filter)
272 else:
273 resp = ctx.obj.vnfd.list()
274 # print yaml.safe_dump(resp)
275 table = PrettyTable(['nfpkg name', 'id'])
276 fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
277 if fullclassname == 'osmclient.sol005.client.Client':
278 for vnfd in resp:
279 name = vnfd['name'] if 'name' in vnfd else '-'
280 table.add_row([name, vnfd['_id']])
281 else:
282 for vnfd in resp:
283 table.add_row([vnfd['name'], vnfd['id']])
284 table.align = 'l'
285 print(table)
286
287
288 @cli.command(name='vnfd-list')
289 @click.option('--nf_type', help='type of NF (vnf, pnf, hnf)')
290 @click.option('--filter', default=None,
291 help='restricts the list to the NF pkg matching the filter')
292 @click.pass_context
293 def vnfd_list1(ctx, nf_type, filter):
294 """list all VNFD/VNF pkg in the system"""
295 vnfd_list(ctx, nf_type, filter)
296
297
298 @cli.command(name='vnfpkg-list')
299 @click.option('--nf_type', help='type of NF (vnf, pnf, hnf)')
300 @click.option('--filter', default=None,
301 help='restricts the list to the NFpkg matching the filter')
302 @click.pass_context
303 def vnfd_list2(ctx, nf_type, filter):
304 """list all VNFD/VNF pkg in the system"""
305 vnfd_list(ctx, nf_type, filter)
306
307
308 @cli.command(name='nfpkg-list')
309 @click.option('--nf_type', help='type of NF (vnf, pnf, hnf)')
310 @click.option('--filter', default=None,
311 help='restricts the list to the NFpkg matching the filter')
312 @click.pass_context
313 def nfpkg_list(ctx, nf_type, filter):
314 """list all NF pkg (VNF pkg, PNF pkg, HNF pkg) in the system"""
315 try:
316 check_client_version(ctx.obj, ctx.command.name)
317 vnfd_list(ctx, nf_type, filter)
318 except ClientException as inst:
319 print((inst.message))
320 exit(1)
321
322
323 def vnf_list(ctx, ns, filter):
324 try:
325 if ns or filter:
326 if ns:
327 check_client_version(ctx.obj, '--ns')
328 if filter:
329 check_client_version(ctx.obj, '--filter')
330 resp = ctx.obj.vnf.list(ns, filter)
331 else:
332 resp = ctx.obj.vnf.list()
333 except ClientException as inst:
334 print((inst.message))
335 exit(1)
336 fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
337 if fullclassname == 'osmclient.sol005.client.Client':
338 table = PrettyTable(
339 ['vnf id',
340 'name',
341 'ns id',
342 'vnf member index',
343 'vnfd name',
344 'vim account id',
345 'ip address'])
346 for vnfr in resp:
347 name = vnfr['name'] if 'name' in vnfr else '-'
348 table.add_row(
349 [vnfr['_id'],
350 name,
351 vnfr['nsr-id-ref'],
352 vnfr['member-vnf-index-ref'],
353 vnfr['vnfd-ref'],
354 vnfr['vim-account-id'],
355 vnfr['ip-address']])
356 else:
357 table = PrettyTable(
358 ['vnf name',
359 'id',
360 'operational status',
361 'config status'])
362 for vnfr in resp:
363 if 'mgmt-interface' not in vnfr:
364 vnfr['mgmt-interface'] = {}
365 vnfr['mgmt-interface']['ip-address'] = None
366 table.add_row(
367 [vnfr['name'],
368 vnfr['id'],
369 vnfr['operational-status'],
370 vnfr['config-status']])
371 table.align = 'l'
372 print(table)
373
374
375 @cli.command(name='vnf-list')
376 @click.option('--ns', default=None, help='NS instance id or name to restrict the NF list')
377 @click.option('--filter', default=None,
378 help='restricts the list to the NF instances matching the filter.')
379 @click.pass_context
380 def vnf_list1(ctx, ns, filter):
381 """list all NF instances"""
382 vnf_list(ctx, ns, filter)
383
384
385 @cli.command(name='nf-list')
386 @click.option('--ns', default=None, help='NS instance id or name to restrict the NF list')
387 @click.option('--filter', default=None,
388 help='restricts the list to the NF instances matching the filter.')
389 @click.pass_context
390 def nf_list(ctx, ns, filter):
391 """list all NF instances
392
393 \b
394 Options:
395 --ns TEXT NS instance id or name to restrict the VNF list
396 --filter filterExpr Restricts the list to the VNF instances matching the filter
397
398 \b
399 filterExpr consists of one or more strings formatted according to "simpleFilterExpr",
400 concatenated using the "&" character:
401
402 \b
403 filterExpr := <simpleFilterExpr>["&"<simpleFilterExpr>]*
404 simpleFilterExpr := <attrName>["."<attrName>]*["."<op>]"="<value>[","<value>]*
405 op := "eq" | "neq" | "gt" | "lt" | "gte" | "lte" | "cont" | "ncont"
406 attrName := string
407 value := scalar value
408
409 \b
410 where:
411 * zero or more occurrences
412 ? zero or one occurrence
413 [] grouping of expressions to be used with ? and *
414 "" quotation marks for marking string constants
415 <> name separator
416
417 \b
418 "AttrName" is the name of one attribute in the data type that defines the representation
419 of the resource. The dot (".") character in "simpleFilterExpr" allows concatenation of
420 <attrName> entries to filter by attributes deeper in the hierarchy of a structured document.
421 "Op" stands for the comparison operator. If the expression has concatenated <attrName>
422 entries, it means that the operator "op" is applied to the attribute addressed by the last
423 <attrName> entry included in the concatenation. All simple filter expressions are combined
424 by the "AND" logical operator. In a concatenation of <attrName> entries in a <simpleFilterExpr>,
425 the rightmost "attrName" entry in a "simpleFilterExpr" is called "leaf attribute". The
426 concatenation of all "attrName" entries except the leaf attribute is called the "attribute
427 prefix". If an attribute referenced in an expression is an array, an object that contains a
428 corresponding array shall be considered to match the expression if any of the elements in the
429 array matches all expressions that have the same attribute prefix.
430
431 \b
432 Filter examples:
433 --filter vim-account-id=<VIM_ACCOUNT_ID>
434 --filter vnfd-ref=<VNFD_NAME>
435 --filter vdur.ip-address=<IP_ADDRESS>
436 --filter vnfd-ref=<VNFD_NAME>,vdur.ip-address=<IP_ADDRESS>
437 """
438 vnf_list(ctx, ns, filter)
439
440
441 @cli.command(name='ns-op-list')
442 @click.argument('name')
443 @click.pass_context
444 def ns_op_list(ctx, name):
445 """shows the history of operations over a NS instance
446
447 NAME: name or ID of the NS instance
448 """
449 try:
450 check_client_version(ctx.obj, ctx.command.name)
451 resp = ctx.obj.ns.list_op(name)
452 except ClientException as inst:
453 print((inst.message))
454 exit(1)
455
456 table = PrettyTable(['id', 'operation', 'status'])
457 for op in resp:
458 table.add_row([op['id'], op['lcmOperationType'],
459 op['operationState']])
460 table.align = 'l'
461 print(table)
462
463
464 def nsi_list(ctx, filter):
465 """list all Network Slice Instances"""
466 try:
467 check_client_version(ctx.obj, ctx.command.name)
468 resp = ctx.obj.nsi.list(filter)
469 except ClientException as inst:
470 print((inst.message))
471 exit(1)
472 table = PrettyTable(
473 ['netslice instance name',
474 'id',
475 'operational status',
476 'config status',
477 'detailed status'])
478 for nsi in resp:
479 nsi_name = nsi['name']
480 nsi_id = nsi['_id']
481 opstatus = nsi['operational-status'] if 'operational-status' in nsi else 'Not found'
482 configstatus = nsi['config-status'] if 'config-status' in nsi else 'Not found'
483 detailed_status = nsi['detailed-status'] if 'detailed-status' in nsi else 'Not found'
484 if configstatus == "config_not_needed":
485 configstatus = "configured (no charms)"
486 table.add_row(
487 [nsi_name,
488 nsi_id,
489 opstatus,
490 configstatus,
491 detailed_status])
492 table.align = 'l'
493 print(table)
494
495
496 @cli.command(name='nsi-list')
497 @click.option('--filter', default=None,
498 help='restricts the list to the Network Slice Instances matching the filter')
499 @click.pass_context
500 def nsi_list1(ctx, filter):
501 """list all Network Slice Instances (NSI)"""
502 nsi_list(ctx, filter)
503
504
505 @cli.command(name='netslice-instance-list')
506 @click.option('--filter', default=None,
507 help='restricts the list to the Network Slice Instances matching the filter')
508 @click.pass_context
509 def nsi_list2(ctx, filter):
510 """list all Network Slice Instances (NSI)"""
511 nsi_list(ctx, filter)
512
513
514 def nst_list(ctx, filter):
515 try:
516 check_client_version(ctx.obj, ctx.command.name)
517 resp = ctx.obj.nst.list(filter)
518 except ClientException as inst:
519 print((inst.message))
520 exit(1)
521 # print yaml.safe_dump(resp)
522 table = PrettyTable(['nst name', 'id'])
523 for nst in resp:
524 name = nst['name'] if 'name' in nst else '-'
525 table.add_row([name, nst['_id']])
526 table.align = 'l'
527 print(table)
528
529
530 @cli.command(name='nst-list')
531 @click.option('--filter', default=None,
532 help='restricts the list to the NST matching the filter')
533 @click.pass_context
534 def nst_list1(ctx, filter):
535 """list all Network Slice Templates (NST) in the system"""
536 nst_list(ctx, filter)
537
538
539 @cli.command(name='netslice-template-list')
540 @click.option('--filter', default=None,
541 help='restricts the list to the NST matching the filter')
542 @click.pass_context
543 def nst_list2(ctx, filter):
544 """list all Network Slice Templates (NST) in the system"""
545 nst_list(ctx, filter)
546
547
548 def nsi_op_list(ctx, name):
549 try:
550 check_client_version(ctx.obj, ctx.command.name)
551 resp = ctx.obj.nsi.list_op(name)
552 except ClientException as inst:
553 print((inst.message))
554 exit(1)
555 table = PrettyTable(['id', 'operation', 'status'])
556 for op in resp:
557 table.add_row([op['id'], op['lcmOperationType'],
558 op['operationState']])
559 table.align = 'l'
560 print(table)
561
562
563 @cli.command(name='nsi-op-list')
564 @click.argument('name')
565 @click.pass_context
566 def nsi_op_list1(ctx, name):
567 """shows the history of operations over a Network Slice Instance (NSI)
568
569 NAME: name or ID of the Network Slice Instance
570 """
571 nsi_op_list(ctx, name)
572
573
574 @cli.command(name='netslice-instance-op-list')
575 @click.argument('name')
576 @click.pass_context
577 def nsi_op_list2(ctx, name):
578 """shows the history of operations over a Network Slice Instance (NSI)
579
580 NAME: name or ID of the Network Slice Instance
581 """
582 nsi_op_list(ctx, name)
583
584
585 @cli.command(name='pdu-list')
586 @click.option('--filter', default=None,
587 help='restricts the list to the Physical Deployment Units matching the filter')
588 @click.pass_context
589 def pdu_list(ctx, filter):
590 """list all Physical Deployment Units (PDU)"""
591 try:
592 check_client_version(ctx.obj, ctx.command.name)
593 resp = ctx.obj.pdu.list(filter)
594 except ClientException as inst:
595 print((inst.message))
596 exit(1)
597 table = PrettyTable(
598 ['pdu name',
599 'id',
600 'type',
601 'mgmt ip address'])
602 for pdu in resp:
603 pdu_name = pdu['name']
604 pdu_id = pdu['_id']
605 pdu_type = pdu['type']
606 pdu_ipaddress = "None"
607 for iface in pdu['interfaces']:
608 if iface['mgmt']:
609 pdu_ipaddress = iface['ip-address']
610 break
611 table.add_row(
612 [pdu_name,
613 pdu_id,
614 pdu_type,
615 pdu_ipaddress])
616 table.align = 'l'
617 print(table)
618
619
620 ####################
621 # SHOW operations
622 ####################
623
624 def nsd_show(ctx, name, literal):
625 try:
626 resp = ctx.obj.nsd.get(name)
627 # resp = ctx.obj.nsd.get_individual(name)
628 except ClientException as inst:
629 print((inst.message))
630 exit(1)
631
632 if literal:
633 print(yaml.safe_dump(resp))
634 return
635
636 table = PrettyTable(['field', 'value'])
637 for k, v in list(resp.items()):
638 table.add_row([k, json.dumps(v, indent=2)])
639 table.align = 'l'
640 print(table)
641
642
643 @cli.command(name='nsd-show', short_help='shows the content of a NSD')
644 @click.option('--literal', is_flag=True,
645 help='print literally, no pretty table')
646 @click.argument('name')
647 @click.pass_context
648 def nsd_show1(ctx, name, literal):
649 """shows the content of a NSD
650
651 NAME: name or ID of the NSD/NSpkg
652 """
653 nsd_show(ctx, name, literal)
654
655
656 @cli.command(name='nspkg-show', short_help='shows the content of a NSD')
657 @click.option('--literal', is_flag=True,
658 help='print literally, no pretty table')
659 @click.argument('name')
660 @click.pass_context
661 def nsd_show2(ctx, name, literal):
662 """shows the content of a NSD
663
664 NAME: name or ID of the NSD/NSpkg
665 """
666 nsd_show(ctx, name, literal)
667
668
669 def vnfd_show(ctx, name, literal):
670 try:
671 resp = ctx.obj.vnfd.get(name)
672 # resp = ctx.obj.vnfd.get_individual(name)
673 except ClientException as inst:
674 print((inst.message))
675 exit(1)
676
677 if literal:
678 print(yaml.safe_dump(resp))
679 return
680
681 table = PrettyTable(['field', 'value'])
682 for k, v in list(resp.items()):
683 table.add_row([k, json.dumps(v, indent=2)])
684 table.align = 'l'
685 print(table)
686
687
688 @cli.command(name='vnfd-show', short_help='shows the content of a VNFD')
689 @click.option('--literal', is_flag=True,
690 help='print literally, no pretty table')
691 @click.argument('name')
692 @click.pass_context
693 def vnfd_show1(ctx, name, literal):
694 """shows the content of a VNFD
695
696 NAME: name or ID of the VNFD/VNFpkg
697 """
698 vnfd_show(ctx, name, literal)
699
700
701 @cli.command(name='vnfpkg-show', short_help='shows the content of a VNFD')
702 @click.option('--literal', is_flag=True,
703 help='print literally, no pretty table')
704 @click.argument('name')
705 @click.pass_context
706 def vnfd_show2(ctx, name, literal):
707 """shows the content of a VNFD
708
709 NAME: name or ID of the VNFD/VNFpkg
710 """
711 vnfd_show(ctx, name, literal)
712
713
714 @cli.command(name='nfpkg-show', short_help='shows the content of a NF Descriptor')
715 @click.option('--literal', is_flag=True,
716 help='print literally, no pretty table')
717 @click.argument('name')
718 @click.pass_context
719 def nfpkg_show(ctx, name, literal):
720 """shows the content of a NF Descriptor
721
722 NAME: name or ID of the NFpkg
723 """
724 vnfd_show(ctx, name, literal)
725
726
727 @cli.command(name='ns-show', short_help='shows the info of a NS instance')
728 @click.argument('name')
729 @click.option('--literal', is_flag=True,
730 help='print literally, no pretty table')
731 @click.option('--filter', default=None)
732 @click.pass_context
733 def ns_show(ctx, name, literal, filter):
734 """shows the info of a NS instance
735
736 NAME: name or ID of the NS instance
737 """
738 try:
739 ns = ctx.obj.ns.get(name)
740 except ClientException as inst:
741 print((inst.message))
742 exit(1)
743
744 if literal:
745 print(yaml.safe_dump(ns))
746 return
747
748 table = PrettyTable(['field', 'value'])
749
750 for k, v in list(ns.items()):
751 if filter is None or filter in k:
752 table.add_row([k, json.dumps(v, indent=2)])
753
754 fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
755 if fullclassname != 'osmclient.sol005.client.Client':
756 nsopdata = ctx.obj.ns.get_opdata(ns['id'])
757 nsr_optdata = nsopdata['nsr:nsr']
758 for k, v in list(nsr_optdata.items()):
759 if filter is None or filter in k:
760 table.add_row([k, json.dumps(v, indent=2)])
761 table.align = 'l'
762 print(table)
763
764
765 @cli.command(name='vnf-show', short_help='shows the info of a VNF instance')
766 @click.argument('name')
767 @click.option('--literal', is_flag=True,
768 help='print literally, no pretty table')
769 @click.option('--filter', default=None)
770 @click.pass_context
771 def vnf_show(ctx, name, literal, filter):
772 """shows the info of a VNF instance
773
774 NAME: name or ID of the VNF instance
775 """
776 try:
777 check_client_version(ctx.obj, ctx.command.name)
778 resp = ctx.obj.vnf.get(name)
779 except ClientException as inst:
780 print((inst.message))
781 exit(1)
782
783 if literal:
784 print(yaml.safe_dump(resp))
785 return
786
787 table = PrettyTable(['field', 'value'])
788 for k, v in list(resp.items()):
789 if filter is None or filter in k:
790 table.add_row([k, json.dumps(v, indent=2)])
791 table.align = 'l'
792 print(table)
793
794
795 @cli.command(name='vnf-monitoring-show')
796 @click.argument('vnf_name')
797 @click.pass_context
798 def vnf_monitoring_show(ctx, vnf_name):
799 try:
800 check_client_version(ctx.obj, ctx.command.name, 'v1')
801 resp = ctx.obj.vnf.get_monitoring(vnf_name)
802 except ClientException as inst:
803 print((inst.message))
804 exit(1)
805
806 table = PrettyTable(['vnf name', 'monitoring name', 'value', 'units'])
807 if resp is not None:
808 for monitor in resp:
809 table.add_row(
810 [vnf_name,
811 monitor['name'],
812 monitor['value-integer'],
813 monitor['units']])
814 table.align = 'l'
815 print(table)
816
817
818 @cli.command(name='ns-monitoring-show')
819 @click.argument('ns_name')
820 @click.pass_context
821 def ns_monitoring_show(ctx, ns_name):
822 try:
823 check_client_version(ctx.obj, ctx.command.name, 'v1')
824 resp = ctx.obj.ns.get_monitoring(ns_name)
825 except ClientException as inst:
826 print((inst.message))
827 exit(1)
828
829 table = PrettyTable(['vnf name', 'monitoring name', 'value', 'units'])
830 for key, val in list(resp.items()):
831 for monitor in val:
832 table.add_row(
833 [key,
834 monitor['name'],
835 monitor['value-integer'],
836 monitor['units']])
837 table.align = 'l'
838 print(table)
839
840
841 @cli.command(name='ns-op-show', short_help='shows the info of an operation')
842 @click.argument('id')
843 @click.option('--filter', default=None)
844 @click.pass_context
845 def ns_op_show(ctx, id, filter):
846 """shows the detailed info of an operation
847
848 ID: operation identifier
849 """
850 try:
851 check_client_version(ctx.obj, ctx.command.name)
852 op_info = ctx.obj.ns.get_op(id)
853 except ClientException as inst:
854 print((inst.message))
855 exit(1)
856
857 table = PrettyTable(['field', 'value'])
858 for k, v in list(op_info.items()):
859 if filter is None or filter in k:
860 table.add_row([k, json.dumps(v, indent=2)])
861 table.align = 'l'
862 print(table)
863
864
865 def nst_show(ctx, name, literal):
866 try:
867 check_client_version(ctx.obj, ctx.command.name)
868 resp = ctx.obj.nst.get(name)
869 #resp = ctx.obj.nst.get_individual(name)
870 except ClientException as inst:
871 print((inst.message))
872 exit(1)
873
874 if literal:
875 print(yaml.safe_dump(resp))
876 return
877
878 table = PrettyTable(['field', 'value'])
879 for k, v in list(resp.items()):
880 table.add_row([k, json.dumps(v, indent=2)])
881 table.align = 'l'
882 print(table)
883
884
885 @cli.command(name='nst-show', short_help='shows the content of a Network Slice Template (NST)')
886 @click.option('--literal', is_flag=True,
887 help='print literally, no pretty table')
888 @click.argument('name')
889 @click.pass_context
890 def nst_show1(ctx, name, literal):
891 """shows the content of a Network Slice Template (NST)
892
893 NAME: name or ID of the NST
894 """
895 nst_show(ctx, name, literal)
896
897
898 @cli.command(name='netslice-template-show', short_help='shows the content of a Network Slice Template (NST)')
899 @click.option('--literal', is_flag=True,
900 help='print literally, no pretty table')
901 @click.argument('name')
902 @click.pass_context
903 def nst_show2(ctx, name, literal):
904 """shows the content of a Network Slice Template (NST)
905
906 NAME: name or ID of the NST
907 """
908 nst_show(ctx, name, literal)
909
910
911 def nsi_show(ctx, name, literal, filter):
912 try:
913 check_client_version(ctx.obj, ctx.command.name)
914 nsi = ctx.obj.nsi.get(name)
915 except ClientException as inst:
916 print((inst.message))
917 exit(1)
918
919 if literal:
920 print(yaml.safe_dump(nsi))
921 return
922
923 table = PrettyTable(['field', 'value'])
924
925 for k, v in list(nsi.items()):
926 if filter is None or filter in k:
927 table.add_row([k, json.dumps(v, indent=2)])
928
929 table.align = 'l'
930 print(table)
931
932
933 @cli.command(name='nsi-show', short_help='shows the content of a Network Slice Instance (NSI)')
934 @click.argument('name')
935 @click.option('--literal', is_flag=True,
936 help='print literally, no pretty table')
937 @click.option('--filter', default=None)
938 @click.pass_context
939 def nsi_show1(ctx, name, literal, filter):
940 """shows the content of a Network Slice Instance (NSI)
941
942 NAME: name or ID of the Network Slice Instance
943 """
944 nsi_show(ctx, name, literal, filter)
945
946
947 @cli.command(name='netslice-instance-show', short_help='shows the content of a Network Slice Instance (NSI)')
948 @click.argument('name')
949 @click.option('--literal', is_flag=True,
950 help='print literally, no pretty table')
951 @click.option('--filter', default=None)
952 @click.pass_context
953 def nsi_show2(ctx, name, literal, filter):
954 """shows the content of a Network Slice Instance (NSI)
955
956 NAME: name or ID of the Network Slice Instance
957 """
958 nsi_show(ctx, name, literal, filter)
959
960
961 def nsi_op_show(ctx, id, filter):
962 try:
963 check_client_version(ctx.obj, ctx.command.name)
964 op_info = ctx.obj.nsi.get_op(id)
965 except ClientException as inst:
966 print((inst.message))
967 exit(1)
968
969 table = PrettyTable(['field', 'value'])
970 for k, v in list(op_info.items()):
971 if filter is None or filter in k:
972 table.add_row([k, json.dumps(v, indent=2)])
973 table.align = 'l'
974 print(table)
975
976
977 @cli.command(name='nsi-op-show', short_help='shows the info of an operation over a Network Slice Instance(NSI)')
978 @click.argument('id')
979 @click.option('--filter', default=None)
980 @click.pass_context
981 def nsi_op_show1(ctx, id, filter):
982 """shows the info of an operation over a Network Slice Instance(NSI)
983
984 ID: operation identifier
985 """
986 nsi_op_show(ctx, id, filter)
987
988
989 @cli.command(name='netslice-instance-op-show', short_help='shows the info of an operation over a Network Slice Instance(NSI)')
990 @click.argument('id')
991 @click.option('--filter', default=None)
992 @click.pass_context
993 def nsi_op_show2(ctx, id, filter):
994 """shows the info of an operation over a Network Slice Instance(NSI)
995
996 ID: operation identifier
997 """
998 nsi_op_show(ctx, id, filter)
999
1000
1001 @cli.command(name='pdu-show', short_help='shows the content of a Physical Deployment Unit (PDU)')
1002 @click.argument('name')
1003 @click.option('--literal', is_flag=True,
1004 help='print literally, no pretty table')
1005 @click.option('--filter', default=None)
1006 @click.pass_context
1007 def pdu_show(ctx, name, literal, filter):
1008 """shows the content of a Physical Deployment Unit (PDU)
1009
1010 NAME: name or ID of the PDU
1011 """
1012 try:
1013 check_client_version(ctx.obj, ctx.command.name)
1014 pdu = ctx.obj.pdu.get(name)
1015 except ClientException as inst:
1016 print((inst.message))
1017 exit(1)
1018
1019 if literal:
1020 print(yaml.safe_dump(pdu))
1021 return
1022
1023 table = PrettyTable(['field', 'value'])
1024
1025 for k, v in list(pdu.items()):
1026 if filter is None or filter in k:
1027 table.add_row([k, json.dumps(v, indent=2)])
1028
1029 table.align = 'l'
1030 print(table)
1031
1032
1033 ####################
1034 # CREATE operations
1035 ####################
1036
1037 def nsd_create(ctx, filename, overwrite):
1038 try:
1039 check_client_version(ctx.obj, ctx.command.name)
1040 ctx.obj.nsd.create(filename, overwrite)
1041 except ClientException as inst:
1042 print((inst.message))
1043 exit(1)
1044
1045
1046 @cli.command(name='nsd-create', short_help='creates a new NSD/NSpkg')
1047 @click.argument('filename')
1048 @click.option('--overwrite', default=None,
1049 help='overwrites some fields in NSD')
1050 @click.pass_context
1051 def nsd_create1(ctx, filename, overwrite):
1052 """creates a new NSD/NSpkg
1053
1054 FILENAME: NSD yaml file or NSpkg tar.gz file
1055 """
1056 nsd_create(ctx, filename, overwrite)
1057
1058
1059 @cli.command(name='nspkg-create', short_help='creates a new NSD/NSpkg')
1060 @click.argument('filename')
1061 @click.option('--overwrite', default=None,
1062 help='overwrites some fields in NSD')
1063 @click.pass_context
1064 def nsd_create2(ctx, filename, overwrite):
1065 """creates a new NSD/NSpkg
1066
1067 FILENAME: NSD yaml file or NSpkg tar.gz file
1068 """
1069 nsd_create(ctx, filename, overwrite)
1070
1071
1072 def vnfd_create(ctx, filename, overwrite):
1073 try:
1074 check_client_version(ctx.obj, ctx.command.name)
1075 ctx.obj.vnfd.create(filename, overwrite)
1076 except ClientException as inst:
1077 print((inst.message))
1078 exit(1)
1079
1080
1081 @cli.command(name='vnfd-create', short_help='creates a new VNFD/VNFpkg')
1082 @click.argument('filename')
1083 @click.option('--overwrite', default=None,
1084 help='overwrites some fields in VNFD')
1085 @click.pass_context
1086 def vnfd_create1(ctx, filename, overwrite):
1087 """creates a new VNFD/VNFpkg
1088
1089 FILENAME: VNFD yaml file or VNFpkg tar.gz file
1090 """
1091 vnfd_create(ctx, filename, overwrite)
1092
1093
1094 @cli.command(name='vnfpkg-create', short_help='creates a new VNFD/VNFpkg')
1095 @click.argument('filename')
1096 @click.option('--overwrite', default=None,
1097 help='overwrites some fields in VNFD')
1098 @click.pass_context
1099 def vnfd_create2(ctx, filename, overwrite):
1100 """creates a new VNFD/VNFpkg
1101
1102 FILENAME: VNFD yaml file or VNFpkg tar.gz file
1103 """
1104 vnfd_create(ctx, filename, overwrite)
1105
1106
1107 @cli.command(name='nfpkg-create', short_help='creates a new NFpkg')
1108 @click.argument('filename')
1109 @click.option('--overwrite', default=None,
1110 help='overwrites some fields in NFD')
1111 @click.pass_context
1112 def nfpkg_create(ctx, filename, overwrite):
1113 """creates a new NFpkg
1114
1115 FILENAME: NF Descriptor yaml file or NFpkg tar.gz file
1116 """
1117 vnfd_create(ctx, filename, overwrite)
1118
1119
1120 @cli.command(name='ns-create', short_help='creates a new Network Service instance')
1121 @click.option('--ns_name',
1122 prompt=True, help='name of the NS instance')
1123 @click.option('--nsd_name',
1124 prompt=True, help='name of the NS descriptor')
1125 @click.option('--vim_account',
1126 prompt=True, help='default VIM account id or name for the deployment')
1127 @click.option('--admin_status',
1128 default='ENABLED',
1129 help='administration status')
1130 @click.option('--ssh_keys',
1131 default=None,
1132 help='comma separated list of public key files to inject to vnfs')
1133 @click.option('--config',
1134 default=None,
1135 help='ns specific yaml configuration')
1136 @click.option('--config_file',
1137 default=None,
1138 help='ns specific yaml configuration file')
1139 @click.pass_context
1140 def ns_create(ctx,
1141 nsd_name,
1142 ns_name,
1143 vim_account,
1144 admin_status,
1145 ssh_keys,
1146 config,
1147 config_file):
1148 """creates a new NS instance"""
1149 try:
1150 if config_file:
1151 check_client_version(ctx.obj, '--config_file')
1152 if config:
1153 raise ClientException('"--config" option is incompatible with "--config_file" option')
1154 with open(config_file, 'r') as cf:
1155 config=cf.read()
1156 ctx.obj.ns.create(
1157 nsd_name,
1158 ns_name,
1159 config=config,
1160 ssh_keys=ssh_keys,
1161 account=vim_account)
1162 except ClientException as inst:
1163 print(inst.message)
1164 exit(1)
1165
1166
1167 def nst_create(ctx, filename, overwrite):
1168 try:
1169 check_client_version(ctx.obj, ctx.command.name)
1170 ctx.obj.nst.create(filename, overwrite)
1171 except ClientException as inst:
1172 print((inst.message))
1173 exit(1)
1174
1175
1176 @cli.command(name='nst-create', short_help='creates a new Network Slice Template (NST)')
1177 @click.argument('filename')
1178 @click.option('--overwrite', default=None,
1179 help='overwrites some fields in NST')
1180 @click.pass_context
1181 def nst_create1(ctx, filename, overwrite):
1182 """creates a new Network Slice Template (NST)
1183
1184 FILENAME: NST yaml file or NSTpkg tar.gz file
1185 """
1186 nst_create(ctx, filename, overwrite)
1187
1188
1189 @cli.command(name='netslice-template-create', short_help='creates a new Network Slice Template (NST)')
1190 @click.argument('filename')
1191 @click.option('--overwrite', default=None,
1192 help='overwrites some fields in NST')
1193 @click.pass_context
1194 def nst_create2(ctx, filename, overwrite):
1195 """creates a new Network Slice Template (NST)
1196
1197 FILENAME: NST yaml file or NSTpkg tar.gz file
1198 """
1199 nst_create(ctx, filename, overwrite)
1200
1201
1202 def nsi_create(ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file):
1203 """creates a new Network Slice Instance (NSI)"""
1204 try:
1205 check_client_version(ctx.obj, ctx.command.name)
1206 if config_file:
1207 if config:
1208 raise ClientException('"--config" option is incompatible with "--config_file" option')
1209 with open(config_file, 'r') as cf:
1210 config=cf.read()
1211 ctx.obj.nsi.create(nst_name, nsi_name, config=config, ssh_keys=ssh_keys,
1212 account=vim_account)
1213 except ClientException as inst:
1214 print(inst.message)
1215 exit(1)
1216
1217
1218 @cli.command(name='nsi-create', short_help='creates a new Network Slice Instance')
1219 @click.option('--nsi_name', prompt=True, help='name of the Network Slice Instance')
1220 @click.option('--nst_name', prompt=True, help='name of the Network Slice Template')
1221 @click.option('--vim_account', prompt=True, help='default VIM account id or name for the deployment')
1222 @click.option('--ssh_keys', default=None,
1223 help='comma separated list of keys to inject to vnfs')
1224 @click.option('--config', default=None,
1225 help='Netslice specific yaml configuration:\n'
1226 'netslice_subnet: [\n'
1227 'id: TEXT, vim_account: TEXT,\n'
1228 'vnf: [member-vnf-index: TEXT, vim_account: TEXT]\n'
1229 'vld: [name: TEXT, vim-network-name: TEXT or DICT with vim_account, vim_net entries]\n'
1230 'additionalParamsForNsi: {param: value, ...}\n'
1231 'additionalParamsForsubnet: [{id: SUBNET_ID, additionalParamsForNs: {}, additionalParamsForVnf: {}}]\n'
1232 '],\n'
1233 'netslice-vld: [name: TEXT, vim-network-name: TEXT or DICT with vim_account, vim_net entries]'
1234 )
1235 @click.option('--config_file',
1236 default=None,
1237 help='nsi specific yaml configuration file')
1238 @click.pass_context
1239 def nsi_create1(ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file):
1240 """creates a new Network Slice Instance (NSI)"""
1241 nsi_create(ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file)
1242
1243
1244 @cli.command(name='netslice-instance-create', short_help='creates a new Network Slice Instance')
1245 @click.option('--nsi_name', prompt=True, help='name of the Network Slice Instance')
1246 @click.option('--nst_name', prompt=True, help='name of the Network Slice Template')
1247 @click.option('--vim_account', prompt=True, help='default VIM account id or name for the deployment')
1248 @click.option('--ssh_keys', default=None,
1249 help='comma separated list of keys to inject to vnfs')
1250 @click.option('--config', default=None,
1251 help='Netslice specific yaml configuration:\n'
1252 'netslice_subnet: [\n'
1253 'id: TEXT, vim_account: TEXT,\n'
1254 'vnf: [member-vnf-index: TEXT, vim_account: TEXT]\n'
1255 'vld: [name: TEXT, vim-network-name: TEXT or DICT with vim_account, vim_net entries]'
1256 '],\n'
1257 'netslice-vld: [name: TEXT, vim-network-name: TEXT or DICT with vim_account, vim_net entries]'
1258 )
1259 @click.option('--config_file',
1260 default=None,
1261 help='nsi specific yaml configuration file')
1262 @click.pass_context
1263 def nsi_create2(ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file):
1264 """creates a new Network Slice Instance (NSI)"""
1265 nsi_create(ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file)
1266
1267
1268 @cli.command(name='pdu-create', short_help='adds a new Physical Deployment Unit to the catalog')
1269 @click.option('--name', help='name of the Physical Deployment Unit')
1270 @click.option('--pdu_type', help='type of PDU (e.g. router, firewall, FW001)')
1271 @click.option('--interface',
1272 help='interface(s) of the PDU: name=<NAME>,mgmt=<true|false>,ip-address=<IP_ADDRESS>'+
1273 '[,type=<overlay|underlay>][,mac-address=<MAC_ADDRESS>][,vim-network-name=<VIM_NET_NAME>]',
1274 multiple=True)
1275 @click.option('--description', help='human readable description')
1276 @click.option('--vim_account', help='list of VIM accounts (in the same VIM) that can reach this PDU', multiple=True)
1277 @click.option('--descriptor_file', default=None, help='PDU descriptor file (as an alternative to using the other arguments')
1278 @click.pass_context
1279 def pdu_create(ctx, name, pdu_type, interface, description, vim_account, descriptor_file):
1280 """creates a new Physical Deployment Unit (PDU)"""
1281 try:
1282 check_client_version(ctx.obj, ctx.command.name)
1283 pdu = {}
1284 if not descriptor_file:
1285 if not name:
1286 raise ClientException('in absence of descriptor file, option "--name" is mandatory')
1287 if not pdu_type:
1288 raise ClientException('in absence of descriptor file, option "--pdu_type" is mandatory')
1289 if not interface:
1290 raise ClientException('in absence of descriptor file, option "--interface" is mandatory (at least once)')
1291 if not vim_account:
1292 raise ClientException('in absence of descriptor file, option "--vim_account" is mandatory (at least once)')
1293 else:
1294 with open(descriptor_file, 'r') as df:
1295 pdu = yaml.load(df.read())
1296 if name: pdu["name"] = name
1297 if pdu_type: pdu["type"] = pdu_type
1298 if description: pdu["description"] = description
1299 if vim_account: pdu["vim_accounts"] = vim_account
1300 if interface:
1301 ifaces_list = []
1302 for iface in interface:
1303 new_iface={k:v for k,v in [i.split('=') for i in iface.split(',')]}
1304 new_iface["mgmt"] = (new_iface.get("mgmt","false").lower() == "true")
1305 ifaces_list.append(new_iface)
1306 pdu["interfaces"] = ifaces_list
1307 ctx.obj.pdu.create(pdu)
1308 except ClientException as inst:
1309 print((inst.message))
1310 exit(1)
1311
1312
1313 ####################
1314 # UPDATE operations
1315 ####################
1316
1317 def nsd_update(ctx, name, content):
1318 try:
1319 check_client_version(ctx.obj, ctx.command.name)
1320 ctx.obj.nsd.update(name, content)
1321 except ClientException as inst:
1322 print((inst.message))
1323 exit(1)
1324
1325
1326 @cli.command(name='nsd-update', short_help='updates a NSD/NSpkg')
1327 @click.argument('name')
1328 @click.option('--content', default=None,
1329 help='filename with the NSD/NSpkg replacing the current one')
1330 @click.pass_context
1331 def nsd_update1(ctx, name, content):
1332 """updates a NSD/NSpkg
1333
1334 NAME: name or ID of the NSD/NSpkg
1335 """
1336 nsd_update(ctx, name, content)
1337
1338
1339 @cli.command(name='nspkg-update', short_help='updates a NSD/NSpkg')
1340 @click.argument('name')
1341 @click.option('--content', default=None,
1342 help='filename with the NSD/NSpkg replacing the current one')
1343 @click.pass_context
1344 def nsd_update2(ctx, name, content):
1345 """updates a NSD/NSpkg
1346
1347 NAME: name or ID of the NSD/NSpkg
1348 """
1349 nsd_update(ctx, name, content)
1350
1351
1352 def vnfd_update(ctx, name, content):
1353 try:
1354 check_client_version(ctx.obj, ctx.command.name)
1355 ctx.obj.vnfd.update(name, content)
1356 except ClientException as inst:
1357 print((inst.message))
1358 exit(1)
1359
1360
1361 @cli.command(name='vnfd-update', short_help='updates a new VNFD/VNFpkg')
1362 @click.argument('name')
1363 @click.option('--content', default=None,
1364 help='filename with the VNFD/VNFpkg replacing the current one')
1365 @click.pass_context
1366 def vnfd_update1(ctx, name, content):
1367 """updates a VNFD/VNFpkg
1368
1369 NAME: name or ID of the VNFD/VNFpkg
1370 """
1371 vnfd_update(ctx, name, content)
1372
1373
1374 @cli.command(name='vnfpkg-update', short_help='updates a VNFD/VNFpkg')
1375 @click.argument('name')
1376 @click.option('--content', default=None,
1377 help='filename with the VNFD/VNFpkg replacing the current one')
1378 @click.pass_context
1379 def vnfd_update2(ctx, name, content):
1380 """updates a VNFD/VNFpkg
1381
1382 NAME: VNFD yaml file or VNFpkg tar.gz file
1383 """
1384 vnfd_update(ctx, name, content)
1385
1386
1387 @cli.command(name='nfpkg-update', short_help='updates a NFpkg')
1388 @click.argument('name')
1389 @click.option('--content', default=None,
1390 help='filename with the NFpkg replacing the current one')
1391 @click.pass_context
1392 def nfpkg_update(ctx, name, content):
1393 """updates a NFpkg
1394
1395 NAME: NF Descriptor yaml file or NFpkg tar.gz file
1396 """
1397 vnfd_update(ctx, name, content)
1398
1399
1400 def nst_update(ctx, name, content):
1401 try:
1402 check_client_version(ctx.obj, ctx.command.name)
1403 ctx.obj.nst.update(name, content)
1404 except ClientException as inst:
1405 print((inst.message))
1406 exit(1)
1407
1408
1409 @cli.command(name='nst-update', short_help='updates a Network Slice Template (NST)')
1410 @click.argument('name')
1411 @click.option('--content', default=None,
1412 help='filename with the NST/NSTpkg replacing the current one')
1413 @click.pass_context
1414 def nst_update1(ctx, name, content):
1415 """updates a Network Slice Template (NST)
1416
1417 NAME: name or ID of the NSD/NSpkg
1418 """
1419 nst_update(ctx, name, content)
1420
1421
1422 @cli.command(name='netslice-template-update', short_help='updates a Network Slice Template (NST)')
1423 @click.argument('name')
1424 @click.option('--content', default=None,
1425 help='filename with the NST/NSTpkg replacing the current one')
1426 @click.pass_context
1427 def nst_update2(ctx, name, content):
1428 """updates a Network Slice Template (NST)
1429
1430 NAME: name or ID of the NSD/NSpkg
1431 """
1432 nst_update(ctx, name, content)
1433
1434
1435 ####################
1436 # DELETE operations
1437 ####################
1438
1439 def nsd_delete(ctx, name, force):
1440 try:
1441 if not force:
1442 ctx.obj.nsd.delete(name)
1443 else:
1444 check_client_version(ctx.obj, '--force')
1445 ctx.obj.nsd.delete(name, force)
1446 except ClientException as inst:
1447 print((inst.message))
1448 exit(1)
1449
1450
1451 @cli.command(name='nsd-delete', short_help='deletes a NSD/NSpkg')
1452 @click.argument('name')
1453 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
1454 @click.pass_context
1455 def nsd_delete1(ctx, name, force):
1456 """deletes a NSD/NSpkg
1457
1458 NAME: name or ID of the NSD/NSpkg to be deleted
1459 """
1460 nsd_delete(ctx, name, force)
1461
1462
1463 @cli.command(name='nspkg-delete', short_help='deletes a NSD/NSpkg')
1464 @click.argument('name')
1465 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
1466 @click.pass_context
1467 def nsd_delete2(ctx, name, force):
1468 """deletes a NSD/NSpkg
1469
1470 NAME: name or ID of the NSD/NSpkg to be deleted
1471 """
1472 nsd_delete(ctx, name, force)
1473
1474
1475 def vnfd_delete(ctx, name, force):
1476 try:
1477 if not force:
1478 ctx.obj.vnfd.delete(name)
1479 else:
1480 check_client_version(ctx.obj, '--force')
1481 ctx.obj.vnfd.delete(name, force)
1482 except ClientException as inst:
1483 print((inst.message))
1484 exit(1)
1485
1486
1487 @cli.command(name='vnfd-delete', short_help='deletes a VNFD/VNFpkg')
1488 @click.argument('name')
1489 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
1490 @click.pass_context
1491 def vnfd_delete1(ctx, name, force):
1492 """deletes a VNFD/VNFpkg
1493
1494 NAME: name or ID of the VNFD/VNFpkg to be deleted
1495 """
1496 vnfd_delete(ctx, name, force)
1497
1498
1499 @cli.command(name='vnfpkg-delete', short_help='deletes a VNFD/VNFpkg')
1500 @click.argument('name')
1501 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
1502 @click.pass_context
1503 def vnfd_delete2(ctx, name, force):
1504 """deletes a VNFD/VNFpkg
1505
1506 NAME: name or ID of the VNFD/VNFpkg to be deleted
1507 """
1508 vnfd_delete(ctx, name, force)
1509
1510
1511 @cli.command(name='nfpkg-delete', short_help='deletes a NFpkg')
1512 @click.argument('name')
1513 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
1514 @click.pass_context
1515 def nfpkg_delete(ctx, name, force):
1516 """deletes a NFpkg
1517
1518 NAME: name or ID of the NFpkg to be deleted
1519 """
1520 vnfd_delete(ctx, name, force)
1521
1522
1523 @cli.command(name='ns-delete', short_help='deletes a NS instance')
1524 @click.argument('name')
1525 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
1526 @click.pass_context
1527 def ns_delete(ctx, name, force):
1528 """deletes a NS instance
1529
1530 NAME: name or ID of the NS instance to be deleted
1531 """
1532 try:
1533 if not force:
1534 ctx.obj.ns.delete(name)
1535 else:
1536 check_client_version(ctx.obj, '--force')
1537 ctx.obj.ns.delete(name, force)
1538 except ClientException as inst:
1539 print((inst.message))
1540 exit(1)
1541
1542
1543 def nst_delete(ctx, name, force):
1544 try:
1545 check_client_version(ctx.obj, ctx.command.name)
1546 ctx.obj.nst.delete(name, force)
1547 except ClientException as inst:
1548 print((inst.message))
1549 exit(1)
1550
1551
1552 @cli.command(name='nst-delete', short_help='deletes a Network Slice Template (NST)')
1553 @click.argument('name')
1554 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
1555 @click.pass_context
1556 def nst_delete1(ctx, name, force):
1557 """deletes a Network Slice Template (NST)
1558
1559 NAME: name or ID of the NST/NSTpkg to be deleted
1560 """
1561 nst_delete(ctx, name, force)
1562
1563
1564 @cli.command(name='netslice-template-delete', short_help='deletes a Network Slice Template (NST)')
1565 @click.argument('name')
1566 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
1567 @click.pass_context
1568 def nst_delete2(ctx, name, force):
1569 """deletes a Network Slice Template (NST)
1570
1571 NAME: name or ID of the NST/NSTpkg to be deleted
1572 """
1573 nst_delete(ctx, name, force)
1574
1575
1576 def nsi_delete(ctx, name, force):
1577 try:
1578 check_client_version(ctx.obj, ctx.command.name)
1579 ctx.obj.nsi.delete(name, force)
1580 except ClientException as inst:
1581 print((inst.message))
1582 exit(1)
1583
1584
1585 @cli.command(name='nsi-delete', short_help='deletes a Network Slice Instance (NSI)')
1586 @click.argument('name')
1587 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
1588 @click.pass_context
1589 def nsi_delete1(ctx, name, force):
1590 """deletes a Network Slice Instance (NSI)
1591
1592 NAME: name or ID of the Network Slice instance to be deleted
1593 """
1594 nsi_delete(ctx, name, force)
1595
1596
1597 @cli.command(name='netslice-instance-delete', short_help='deletes a Network Slice Instance (NSI)')
1598 @click.argument('name')
1599 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
1600 @click.pass_context
1601 def nsi_delete2(ctx, name, force):
1602 """deletes a Network Slice Instance (NSI)
1603
1604 NAME: name or ID of the Network Slice instance to be deleted
1605 """
1606 nsi_delete(ctx, name, force)
1607
1608
1609 @cli.command(name='pdu-delete', short_help='deletes a Physical Deployment Unit (PDU)')
1610 @click.argument('name')
1611 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
1612 @click.pass_context
1613 def pdu_delete(ctx, name, force):
1614 """deletes a Physical Deployment Unit (PDU)
1615
1616 NAME: name or ID of the PDU to be deleted
1617 """
1618 try:
1619 check_client_version(ctx.obj, ctx.command.name)
1620 ctx.obj.pdu.delete(name, force)
1621 except ClientException as inst:
1622 print((inst.message))
1623 exit(1)
1624
1625
1626 ####################
1627 # VIM operations
1628 ####################
1629
1630 @cli.command(name='vim-create')
1631 @click.option('--name',
1632 prompt=True,
1633 help='Name to create datacenter')
1634 @click.option('--user',
1635 prompt=True,
1636 help='VIM username')
1637 @click.option('--password',
1638 prompt=True,
1639 hide_input=True,
1640 confirmation_prompt=True,
1641 help='VIM password')
1642 @click.option('--auth_url',
1643 prompt=True,
1644 help='VIM url')
1645 @click.option('--tenant',
1646 prompt=True,
1647 help='VIM tenant name')
1648 @click.option('--config',
1649 default=None,
1650 help='VIM specific config parameters')
1651 @click.option('--account_type',
1652 default='openstack',
1653 help='VIM type')
1654 @click.option('--description',
1655 default='no description',
1656 help='human readable description')
1657 @click.option('--sdn_controller', default=None, help='Name or id of the SDN controller associated to this VIM account')
1658 @click.option('--sdn_port_mapping', default=None, help="File describing the port mapping between compute nodes' ports and switch ports")
1659 @click.pass_context
1660 def vim_create(ctx,
1661 name,
1662 user,
1663 password,
1664 auth_url,
1665 tenant,
1666 config,
1667 account_type,
1668 description,
1669 sdn_controller,
1670 sdn_port_mapping):
1671 """creates a new VIM account"""
1672 try:
1673 if sdn_controller:
1674 check_client_version(ctx.obj, '--sdn_controller')
1675 if sdn_port_mapping:
1676 check_client_version(ctx.obj, '--sdn_port_mapping')
1677 vim = {}
1678 vim['vim-username'] = user
1679 vim['vim-password'] = password
1680 vim['vim-url'] = auth_url
1681 vim['vim-tenant-name'] = tenant
1682 vim['vim-type'] = account_type
1683 vim['description'] = description
1684 vim['config'] = config
1685 if sdn_controller or sdn_port_mapping:
1686 ctx.obj.vim.create(name, vim, sdn_controller, sdn_port_mapping)
1687 else:
1688 ctx.obj.vim.create(name, vim)
1689 except ClientException as inst:
1690 print((inst.message))
1691 exit(1)
1692
1693
1694 @cli.command(name='vim-update', short_help='updates a VIM account')
1695 @click.argument('name')
1696 @click.option('--newname', help='New name for the VIM account')
1697 @click.option('--user', help='VIM username')
1698 @click.option('--password', help='VIM password')
1699 @click.option('--auth_url', help='VIM url')
1700 @click.option('--tenant', help='VIM tenant name')
1701 @click.option('--config', help='VIM specific config parameters')
1702 @click.option('--account_type', help='VIM type')
1703 @click.option('--description', help='human readable description')
1704 @click.option('--sdn_controller', default=None, help='Name or id of the SDN controller associated to this VIM account')
1705 @click.option('--sdn_port_mapping', default=None, help="File describing the port mapping between compute nodes' ports and switch ports")
1706 @click.pass_context
1707 def vim_update(ctx,
1708 name,
1709 newname,
1710 user,
1711 password,
1712 auth_url,
1713 tenant,
1714 config,
1715 account_type,
1716 description,
1717 sdn_controller,
1718 sdn_port_mapping):
1719 """updates a VIM account
1720
1721 NAME: name or ID of the VIM account
1722 """
1723 try:
1724 check_client_version(ctx.obj, ctx.command.name)
1725 vim = {}
1726 if newname: vim['name'] = newname
1727 if user: vim['vim_user'] = user
1728 if password: vim['vim_password'] = password
1729 if auth_url: vim['vim_url'] = auth_url
1730 if tenant: vim['vim-tenant-name'] = tenant
1731 if account_type: vim['vim_type'] = account_type
1732 if description: vim['description'] = description
1733 if config: vim['config'] = config
1734 ctx.obj.vim.update(name, vim, sdn_controller, sdn_port_mapping)
1735 except ClientException as inst:
1736 print((inst.message))
1737 exit(1)
1738
1739
1740 @cli.command(name='vim-delete')
1741 @click.argument('name')
1742 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
1743 @click.pass_context
1744 def vim_delete(ctx, name, force):
1745 """deletes a VIM account
1746
1747 NAME: name or ID of the VIM account to be deleted
1748 """
1749 try:
1750 if not force:
1751 ctx.obj.vim.delete(name)
1752 else:
1753 check_client_version(ctx.obj, '--force')
1754 ctx.obj.vim.delete(name, force)
1755 except ClientException as inst:
1756 print((inst.message))
1757 exit(1)
1758
1759
1760 @cli.command(name='vim-list')
1761 @click.option('--ro_update/--no_ro_update',
1762 default=False,
1763 help='update list from RO')
1764 @click.option('--filter', default=None,
1765 help='restricts the list to the VIM accounts matching the filter')
1766 @click.pass_context
1767 def vim_list(ctx, ro_update, filter):
1768 """list all VIM accounts"""
1769 if filter:
1770 check_client_version(ctx.obj, '--filter')
1771 if ro_update:
1772 check_client_version(ctx.obj, '--ro_update', 'v1')
1773 fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
1774 if fullclassname == 'osmclient.sol005.client.Client':
1775 resp = ctx.obj.vim.list(filter)
1776 else:
1777 resp = ctx.obj.vim.list(ro_update)
1778 table = PrettyTable(['vim name', 'uuid'])
1779 for vim in resp:
1780 table.add_row([vim['name'], vim['uuid']])
1781 table.align = 'l'
1782 print(table)
1783
1784
1785 @cli.command(name='vim-show')
1786 @click.argument('name')
1787 @click.pass_context
1788 def vim_show(ctx, name):
1789 """shows the details of a VIM account
1790
1791 NAME: name or ID of the VIM account
1792 """
1793 try:
1794 resp = ctx.obj.vim.get(name)
1795 if 'vim_password' in resp:
1796 resp['vim_password']='********'
1797 except ClientException as inst:
1798 print((inst.message))
1799 exit(1)
1800
1801 table = PrettyTable(['key', 'attribute'])
1802 for k, v in list(resp.items()):
1803 table.add_row([k, json.dumps(v, indent=2)])
1804 table.align = 'l'
1805 print(table)
1806
1807
1808 ####################
1809 # WIM operations
1810 ####################
1811
1812 @cli.command(name='wim-create')
1813 @click.option('--name',
1814 prompt=True,
1815 help='Name for the WIM account')
1816 @click.option('--user',
1817 help='WIM username')
1818 @click.option('--password',
1819 help='WIM password')
1820 @click.option('--url',
1821 prompt=True,
1822 help='WIM url')
1823 # @click.option('--tenant',
1824 # help='wIM tenant name')
1825 @click.option('--config',
1826 default=None,
1827 help='WIM specific config parameters')
1828 @click.option('--wim_type',
1829 help='WIM type')
1830 @click.option('--description',
1831 default='no description',
1832 help='human readable description')
1833 @click.option('--wim_port_mapping', default=None, help="File describing the port mapping between DC edge (datacenters, switches, ports) and WAN edge (WAN service endpoint id and info)")
1834 @click.pass_context
1835 def wim_create(ctx,
1836 name,
1837 user,
1838 password,
1839 url,
1840 # tenant,
1841 config,
1842 wim_type,
1843 description,
1844 wim_port_mapping):
1845 """creates a new WIM account"""
1846 try:
1847 check_client_version(ctx.obj, ctx.command.name)
1848 # if sdn_controller:
1849 # check_client_version(ctx.obj, '--sdn_controller')
1850 # if sdn_port_mapping:
1851 # check_client_version(ctx.obj, '--sdn_port_mapping')
1852 wim = {}
1853 if user: wim['user'] = user
1854 if password: wim['password'] = password
1855 if url: wim['wim_url'] = url
1856 # if tenant: wim['tenant'] = tenant
1857 wim['wim_type'] = wim_type
1858 if description: wim['description'] = description
1859 if config: wim['config'] = config
1860 ctx.obj.wim.create(name, wim, wim_port_mapping)
1861 except ClientException as inst:
1862 print((inst.message))
1863 exit(1)
1864
1865
1866 @cli.command(name='wim-update', short_help='updates a WIM account')
1867 @click.argument('name')
1868 @click.option('--newname', help='New name for the WIM account')
1869 @click.option('--user', help='WIM username')
1870 @click.option('--password', help='WIM password')
1871 @click.option('--url', help='WIM url')
1872 @click.option('--config', help='WIM specific config parameters')
1873 @click.option('--wim_type', help='WIM type')
1874 @click.option('--description', help='human readable description')
1875 @click.option('--wim_port_mapping', default=None, help="File describing the port mapping between DC edge (datacenters, switches, ports) and WAN edge (WAN service endpoint id and info)")
1876 @click.pass_context
1877 def wim_update(ctx,
1878 name,
1879 newname,
1880 user,
1881 password,
1882 url,
1883 config,
1884 wim_type,
1885 description,
1886 wim_port_mapping):
1887 """updates a WIM account
1888
1889 NAME: name or ID of the WIM account
1890 """
1891 try:
1892 check_client_version(ctx.obj, ctx.command.name)
1893 wim = {}
1894 if newname: wim['name'] = newname
1895 if user: wim['user'] = user
1896 if password: wim['password'] = password
1897 if url: wim['url'] = url
1898 # if tenant: wim['tenant'] = tenant
1899 if wim_type: wim['wim_type'] = wim_type
1900 if description: wim['description'] = description
1901 if config: wim['config'] = config
1902 ctx.obj.wim.update(name, wim, wim_port_mapping)
1903 except ClientException as inst:
1904 print((inst.message))
1905 exit(1)
1906
1907
1908 @cli.command(name='wim-delete')
1909 @click.argument('name')
1910 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
1911 @click.pass_context
1912 def wim_delete(ctx, name, force):
1913 """deletes a WIM account
1914
1915 NAME: name or ID of the WIM account to be deleted
1916 """
1917 try:
1918 check_client_version(ctx.obj, ctx.command.name)
1919 ctx.obj.wim.delete(name, force)
1920 except ClientException as inst:
1921 print((inst.message))
1922 exit(1)
1923
1924
1925 @cli.command(name='wim-list')
1926 @click.option('--filter', default=None,
1927 help='restricts the list to the WIM accounts matching the filter')
1928 @click.pass_context
1929 def wim_list(ctx, filter):
1930 """list all WIM accounts"""
1931 try:
1932 check_client_version(ctx.obj, ctx.command.name)
1933 resp = ctx.obj.wim.list(filter)
1934 table = PrettyTable(['wim name', 'uuid'])
1935 for wim in resp:
1936 table.add_row([wim['name'], wim['uuid']])
1937 table.align = 'l'
1938 print(table)
1939 except ClientException as inst:
1940 print((inst.message))
1941 exit(1)
1942
1943
1944 @cli.command(name='wim-show')
1945 @click.argument('name')
1946 @click.pass_context
1947 def wim_show(ctx, name):
1948 """shows the details of a WIM account
1949
1950 NAME: name or ID of the WIM account
1951 """
1952 try:
1953 check_client_version(ctx.obj, ctx.command.name)
1954 resp = ctx.obj.wim.get(name)
1955 if 'password' in resp:
1956 resp['wim_password']='********'
1957 except ClientException as inst:
1958 print((inst.message))
1959 exit(1)
1960
1961 table = PrettyTable(['key', 'attribute'])
1962 for k, v in list(resp.items()):
1963 table.add_row([k, json.dumps(v, indent=2)])
1964 table.align = 'l'
1965 print(table)
1966
1967
1968 ####################
1969 # SDN controller operations
1970 ####################
1971
1972 @cli.command(name='sdnc-create')
1973 @click.option('--name',
1974 prompt=True,
1975 help='Name to create sdn controller')
1976 @click.option('--type',
1977 prompt=True,
1978 help='SDN controller type')
1979 @click.option('--sdn_controller_version',
1980 help='SDN controller username')
1981 @click.option('--ip_address',
1982 prompt=True,
1983 help='SDN controller IP address')
1984 @click.option('--port',
1985 prompt=True,
1986 help='SDN controller port')
1987 @click.option('--switch_dpid',
1988 prompt=True,
1989 help='Switch DPID (Openflow Datapath ID)')
1990 @click.option('--user',
1991 help='SDN controller username')
1992 @click.option('--password',
1993 hide_input=True,
1994 confirmation_prompt=True,
1995 help='SDN controller password')
1996 #@click.option('--description',
1997 # default='no description',
1998 # help='human readable description')
1999 @click.pass_context
2000 def sdnc_create(ctx,
2001 name,
2002 type,
2003 sdn_controller_version,
2004 ip_address,
2005 port,
2006 switch_dpid,
2007 user,
2008 password):
2009 """creates a new SDN controller"""
2010 sdncontroller = {}
2011 sdncontroller['name'] = name
2012 sdncontroller['type'] = type
2013 sdncontroller['ip'] = ip_address
2014 sdncontroller['port'] = int(port)
2015 sdncontroller['dpid'] = switch_dpid
2016 if sdn_controller_version:
2017 sdncontroller['version'] = sdn_controller_version
2018 if user:
2019 sdncontroller['user'] = user
2020 if password:
2021 sdncontroller['password'] = password
2022 # sdncontroller['description'] = description
2023 try:
2024 check_client_version(ctx.obj, ctx.command.name)
2025 ctx.obj.sdnc.create(name, sdncontroller)
2026 except ClientException as inst:
2027 print((inst.message))
2028
2029
2030 @cli.command(name='sdnc-update', short_help='updates an SDN controller')
2031 @click.argument('name')
2032 @click.option('--newname', help='New name for the SDN controller')
2033 @click.option('--type', help='SDN controller type')
2034 @click.option('--sdn_controller_version', help='SDN controller username')
2035 @click.option('--ip_address', help='SDN controller IP address')
2036 @click.option('--port', help='SDN controller port')
2037 @click.option('--switch_dpid', help='Switch DPID (Openflow Datapath ID)')
2038 @click.option('--user', help='SDN controller username')
2039 @click.option('--password', help='SDN controller password')
2040 #@click.option('--description', default=None, help='human readable description')
2041 @click.pass_context
2042 def sdnc_update(ctx,
2043 name,
2044 newname,
2045 type,
2046 sdn_controller_version,
2047 ip_address,
2048 port,
2049 switch_dpid,
2050 user,
2051 password):
2052 """updates an SDN controller
2053
2054 NAME: name or ID of the SDN controller
2055 """
2056 sdncontroller = {}
2057 if newname: sdncontroller['name'] = newname
2058 if type: sdncontroller['type'] = type
2059 if ip_address: sdncontroller['ip'] = ip_address
2060 if port: sdncontroller['port'] = int(port)
2061 if switch_dpid: sdncontroller['dpid'] = switch_dpid
2062 # sdncontroller['description'] = description
2063 if sdn_controller_version is not None:
2064 if sdn_controller_version=="":
2065 sdncontroller['version'] = None
2066 else:
2067 sdncontroller['version'] = sdn_controller_version
2068 if user is not None:
2069 if user=="":
2070 sdncontroller['user'] = None
2071 else:
2072 sdncontroller['user'] = user
2073 if password is not None:
2074 if password=="":
2075 sdncontroller['password'] = None
2076 else:
2077 sdncontroller['password'] = user
2078 try:
2079 check_client_version(ctx.obj, ctx.command.name)
2080 ctx.obj.sdnc.update(name, sdncontroller)
2081 except ClientException as inst:
2082 print((inst.message))
2083 exit(1)
2084
2085
2086 @cli.command(name='sdnc-delete')
2087 @click.argument('name')
2088 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
2089 @click.pass_context
2090 def sdnc_delete(ctx, name, force):
2091 """deletes an SDN controller
2092
2093 NAME: name or ID of the SDN controller to be deleted
2094 """
2095 try:
2096 check_client_version(ctx.obj, ctx.command.name)
2097 ctx.obj.sdnc.delete(name, force)
2098 except ClientException as inst:
2099 print((inst.message))
2100 exit(1)
2101
2102
2103 @cli.command(name='sdnc-list')
2104 @click.option('--filter', default=None,
2105 help='restricts the list to the SDN controllers matching the filter')
2106 @click.pass_context
2107 def sdnc_list(ctx, filter):
2108 """list all SDN controllers"""
2109 try:
2110 check_client_version(ctx.obj, ctx.command.name)
2111 resp = ctx.obj.sdnc.list(filter)
2112 except ClientException as inst:
2113 print((inst.message))
2114 exit(1)
2115 table = PrettyTable(['name', 'id'])
2116 for sdnc in resp:
2117 table.add_row([sdnc['name'], sdnc['_id']])
2118 table.align = 'l'
2119 print(table)
2120
2121
2122 @cli.command(name='sdnc-show')
2123 @click.argument('name')
2124 @click.pass_context
2125 def sdnc_show(ctx, name):
2126 """shows the details of an SDN controller
2127
2128 NAME: name or ID of the SDN controller
2129 """
2130 try:
2131 check_client_version(ctx.obj, ctx.command.name)
2132 resp = ctx.obj.sdnc.get(name)
2133 except ClientException as inst:
2134 print((inst.message))
2135 exit(1)
2136
2137 table = PrettyTable(['key', 'attribute'])
2138 for k, v in list(resp.items()):
2139 table.add_row([k, json.dumps(v, indent=2)])
2140 table.align = 'l'
2141 print(table)
2142
2143
2144 ####################
2145 # Project mgmt operations
2146 ####################
2147
2148 @cli.command(name='project-create')
2149 @click.argument('name')
2150 #@click.option('--description',
2151 # default='no description',
2152 # help='human readable description')
2153 @click.pass_context
2154 def project_create(ctx, name):
2155 """Creates a new project
2156
2157 NAME: name of the project
2158 """
2159 project = {}
2160 project['name'] = name
2161 try:
2162 check_client_version(ctx.obj, ctx.command.name)
2163 ctx.obj.project.create(name, project)
2164 except ClientException as inst:
2165 print(inst.message)
2166
2167
2168 @cli.command(name='project-delete')
2169 @click.argument('name')
2170 #@click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
2171 @click.pass_context
2172 def project_delete(ctx, name):
2173 """deletes a project
2174
2175 NAME: name or ID of the project to be deleted
2176 """
2177 try:
2178 check_client_version(ctx.obj, ctx.command.name)
2179 ctx.obj.project.delete(name)
2180 except ClientException as inst:
2181 print(inst.message)
2182 exit(1)
2183
2184
2185 @cli.command(name='project-list')
2186 @click.option('--filter', default=None,
2187 help='restricts the list to the projects matching the filter')
2188 @click.pass_context
2189 def project_list(ctx, filter):
2190 """list all projects"""
2191 try:
2192 check_client_version(ctx.obj, ctx.command.name)
2193 resp = ctx.obj.project.list(filter)
2194 except ClientException as inst:
2195 print(inst.message)
2196 exit(1)
2197 table = PrettyTable(['name', 'id'])
2198 for proj in resp:
2199 table.add_row([proj['name'], proj['_id']])
2200 table.align = 'l'
2201 print(table)
2202
2203
2204 @cli.command(name='project-show')
2205 @click.argument('name')
2206 @click.pass_context
2207 def project_show(ctx, name):
2208 """shows the details of a project
2209
2210 NAME: name or ID of the project
2211 """
2212 try:
2213 check_client_version(ctx.obj, ctx.command.name)
2214 resp = ctx.obj.project.get(name)
2215 except ClientException as inst:
2216 print(inst.message)
2217 exit(1)
2218
2219 table = PrettyTable(['key', 'attribute'])
2220 for k, v in resp.items():
2221 table.add_row([k, json.dumps(v, indent=2)])
2222 table.align = 'l'
2223 print(table)
2224
2225
2226 ####################
2227 # User mgmt operations
2228 ####################
2229
2230 @cli.command(name='user-create')
2231 @click.argument('username')
2232 @click.option('--password',
2233 prompt=True,
2234 hide_input=True,
2235 confirmation_prompt=True,
2236 help='user password')
2237 @click.option('--projects',
2238 prompt="Comma separate list of projects",
2239 multiple=True,
2240 callback=lambda ctx, param, value: ''.join(value).split(',') if all(len(x)==1 for x in value) else value,
2241 help='list of project ids that the user belongs to')
2242 @click.option('--project-role-mapping', 'project_role_mappings',
2243 default=None, multiple=True,
2244 help='creating user project/role(s) mapping')
2245 @click.pass_context
2246 def user_create(ctx, username, password, projects, project_role_mappings):
2247 """Creates a new user
2248
2249 \b
2250 USERNAME: name of the user
2251 PASSWORD: password of the user
2252 PROJECTS: projects assigned to user (internal only)
2253 PROJECT_ROLE_MAPPING: roles in projects assigned to user (keystone)
2254 """
2255 user = {}
2256 user['username'] = username
2257 user['password'] = password
2258 user['projects'] = projects
2259 user['project-role-mappings'] = project_role_mappings
2260
2261 try:
2262 check_client_version(ctx.obj, ctx.command.name)
2263 ctx.obj.user.create(username, user)
2264 except ClientException as inst:
2265 print(inst.message)
2266
2267
2268 @cli.command(name='user-update')
2269 @click.argument('username')
2270 @click.option('--password',
2271 prompt=True,
2272 hide_input=True,
2273 confirmation_prompt=True,
2274 help='user password')
2275 @click.option('--set-project', 'set_project',
2276 default=None, multiple=True,
2277 help='create/replace the project,role(s) mapping for this project: \'project,role1,role2,...\'')
2278 @click.option('--remove-project', 'remove_project',
2279 default=None, multiple=True,
2280 help='removes project from user: \'project\'')
2281 @click.option('--add-project-role', 'add_project_role',
2282 default=None, multiple=True,
2283 help='adds project,role(s) mapping: \'project,role1,role2,...\'')
2284 @click.option('--remove-project-role', 'remove_project_role',
2285 default=None, multiple=True,
2286 help='removes project,role(s) mapping: \'project,role1,role2,...\'')
2287 @click.pass_context
2288 def user_update(ctx, username, password, set_project, remove_project,
2289 add_project_role, remove_project_role):
2290 """Update a user information
2291
2292 \b
2293 USERNAME: name of the user
2294 PASSWORD: new password
2295 SET_PROJECT: creating mappings for project/role(s)
2296 REMOVE_PROJECT: deleting mappings for project/role(s)
2297 ADD_PROJECT_ROLE: adding mappings for project/role(s)
2298 REMOVE_PROJECT_ROLE: removing mappings for project/role(s)
2299 """
2300 user = {}
2301 user['username'] = username
2302 user['password'] = password
2303 user['set-project'] = set_project
2304 user['remove-project'] = remove_project
2305 user['add-project-role'] = add_project_role
2306 user['remove-project-role'] = remove_project_role
2307
2308 try:
2309 check_client_version(ctx.obj, ctx.command.name)
2310 ctx.obj.user.update(username, user)
2311 except ClientException as inst:
2312 print(inst.message)
2313
2314
2315 @cli.command(name='user-delete')
2316 @click.argument('name')
2317 #@click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
2318 @click.pass_context
2319 def user_delete(ctx, name):
2320 """deletes a user
2321
2322 \b
2323 NAME: name or ID of the user to be deleted
2324 """
2325 try:
2326 check_client_version(ctx.obj, ctx.command.name)
2327 ctx.obj.user.delete(name)
2328 except ClientException as inst:
2329 print(inst.message)
2330 exit(1)
2331
2332
2333 @cli.command(name='user-list')
2334 @click.option('--filter', default=None,
2335 help='restricts the list to the users matching the filter')
2336 @click.pass_context
2337 def user_list(ctx, filter):
2338 """list all users"""
2339 try:
2340 check_client_version(ctx.obj, ctx.command.name)
2341 resp = ctx.obj.user.list(filter)
2342 except ClientException as inst:
2343 print(inst.message)
2344 exit(1)
2345 table = PrettyTable(['name', 'id'])
2346 for user in resp:
2347 table.add_row([user['username'], user['_id']])
2348 table.align = 'l'
2349 print(table)
2350
2351
2352 @cli.command(name='user-show')
2353 @click.argument('name')
2354 @click.pass_context
2355 def user_show(ctx, name):
2356 """shows the details of a user
2357
2358 NAME: name or ID of the user
2359 """
2360 try:
2361 check_client_version(ctx.obj, ctx.command.name)
2362 resp = ctx.obj.user.get(name)
2363 if 'password' in resp:
2364 resp['password']='********'
2365 except ClientException as inst:
2366 print(inst.message)
2367 exit(1)
2368
2369 table = PrettyTable(['key', 'attribute'])
2370 for k, v in resp.items():
2371 table.add_row([k, json.dumps(v, indent=2)])
2372 table.align = 'l'
2373 print(table)
2374
2375
2376 ####################
2377 # Fault Management operations
2378 ####################
2379
2380 @cli.command(name='ns-alarm-create')
2381 @click.argument('name')
2382 @click.option('--ns', prompt=True, help='NS instance id or name')
2383 @click.option('--vnf', prompt=True,
2384 help='VNF name (VNF member index as declared in the NSD)')
2385 @click.option('--vdu', prompt=True,
2386 help='VDU name (VDU name as declared in the VNFD)')
2387 @click.option('--metric', prompt=True,
2388 help='Name of the metric (e.g. cpu_utilization)')
2389 @click.option('--severity', default='WARNING',
2390 help='severity of the alarm (WARNING, MINOR, MAJOR, CRITICAL, INDETERMINATE)')
2391 @click.option('--threshold_value', prompt=True,
2392 help='threshold value that, when crossed, an alarm is triggered')
2393 @click.option('--threshold_operator', prompt=True,
2394 help='threshold operator describing the comparison (GE, LE, GT, LT, EQ)')
2395 @click.option('--statistic', default='AVERAGE',
2396 help='statistic (AVERAGE, MINIMUM, MAXIMUM, COUNT, SUM)')
2397 @click.pass_context
2398 def ns_alarm_create(ctx, name, ns, vnf, vdu, metric, severity,
2399 threshold_value, threshold_operator, statistic):
2400 """creates a new alarm for a NS instance"""
2401 ns_instance = ctx.obj.ns.get(ns)
2402 alarm = {}
2403 alarm['alarm_name'] = name
2404 alarm['ns_id'] = ns_instance['_id']
2405 alarm['correlation_id'] = ns_instance['_id']
2406 alarm['vnf_member_index'] = vnf
2407 alarm['vdu_name'] = vdu
2408 alarm['metric_name'] = metric
2409 alarm['severity'] = severity
2410 alarm['threshold_value'] = int(threshold_value)
2411 alarm['operation'] = threshold_operator
2412 alarm['statistic'] = statistic
2413 try:
2414 check_client_version(ctx.obj, ctx.command.name)
2415 ctx.obj.ns.create_alarm(alarm)
2416 except ClientException as inst:
2417 print((inst.message))
2418 exit(1)
2419
2420
2421 #@cli.command(name='ns-alarm-delete')
2422 #@click.argument('name')
2423 #@click.pass_context
2424 #def ns_alarm_delete(ctx, name):
2425 # '''deletes an alarm
2426 #
2427 # NAME: name of the alarm to be deleted
2428 # '''
2429 # try:
2430 # check_client_version(ctx.obj, ctx.command.name)
2431 # ctx.obj.ns.delete_alarm(name)
2432 # except ClientException as inst:
2433 # print(inst.message)
2434 # exit(1)
2435
2436
2437 ####################
2438 # Performance Management operations
2439 ####################
2440
2441 @cli.command(name='ns-metric-export')
2442 @click.option('--ns', prompt=True, help='NS instance id or name')
2443 @click.option('--vnf', prompt=True,
2444 help='VNF name (VNF member index as declared in the NSD)')
2445 @click.option('--vdu', prompt=True,
2446 help='VDU name (VDU name as declared in the VNFD)')
2447 @click.option('--metric', prompt=True,
2448 help='name of the metric (e.g. cpu_utilization)')
2449 #@click.option('--period', default='1w',
2450 # help='metric collection period (e.g. 20s, 30m, 2h, 3d, 1w)')
2451 @click.option('--interval', help='periodic interval (seconds) to export metrics continuously')
2452 @click.pass_context
2453 def ns_metric_export(ctx, ns, vnf, vdu, metric, interval):
2454 """exports a metric to the internal OSM bus, which can be read by other apps"""
2455 ns_instance = ctx.obj.ns.get(ns)
2456 metric_data = {}
2457 metric_data['ns_id'] = ns_instance['_id']
2458 metric_data['correlation_id'] = ns_instance['_id']
2459 metric_data['vnf_member_index'] = vnf
2460 metric_data['vdu_name'] = vdu
2461 metric_data['metric_name'] = metric
2462 metric_data['collection_unit'] = 'WEEK'
2463 metric_data['collection_period'] = 1
2464 try:
2465 check_client_version(ctx.obj, ctx.command.name)
2466 if not interval:
2467 print('{}'.format(ctx.obj.ns.export_metric(metric_data)))
2468 else:
2469 i = 1
2470 while True:
2471 print('{} {}'.format(ctx.obj.ns.export_metric(metric_data),i))
2472 time.sleep(int(interval))
2473 i+=1
2474 except ClientException as inst:
2475 print((inst.message))
2476 exit(1)
2477
2478
2479 ####################
2480 # Other operations
2481 ####################
2482
2483 @cli.command(name='upload-package')
2484 @click.argument('filename')
2485 @click.pass_context
2486 def upload_package(ctx, filename):
2487 """uploads a VNF package or NS package
2488
2489 FILENAME: VNF or NS package file (tar.gz)
2490 """
2491 try:
2492 ctx.obj.package.upload(filename)
2493 fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
2494 if fullclassname != 'osmclient.sol005.client.Client':
2495 ctx.obj.package.wait_for_upload(filename)
2496 except ClientException as inst:
2497 print((inst.message))
2498 exit(1)
2499
2500
2501 @cli.command(name='ns-scaling-show')
2502 @click.argument('ns_name')
2503 @click.pass_context
2504 def show_ns_scaling(ctx, ns_name):
2505 """shows the status of a NS scaling operation
2506
2507 NS_NAME: name of the NS instance being scaled
2508 """
2509 try:
2510 check_client_version(ctx.obj, ctx.command.name, 'v1')
2511 resp = ctx.obj.ns.list()
2512 except ClientException as inst:
2513 print((inst.message))
2514 exit(1)
2515
2516 table = PrettyTable(
2517 ['group-name',
2518 'instance-id',
2519 'operational status',
2520 'create-time',
2521 'vnfr ids'])
2522
2523 for ns in resp:
2524 if ns_name == ns['name']:
2525 nsopdata = ctx.obj.ns.get_opdata(ns['id'])
2526 scaling_records = nsopdata['nsr:nsr']['scaling-group-record']
2527 for record in scaling_records:
2528 if 'instance' in record:
2529 instances = record['instance']
2530 for inst in instances:
2531 table.add_row(
2532 [record['scaling-group-name-ref'],
2533 inst['instance-id'],
2534 inst['op-status'],
2535 time.strftime('%Y-%m-%d %H:%M:%S',
2536 time.localtime(
2537 inst['create-time'])),
2538 inst['vnfrs']])
2539 table.align = 'l'
2540 print(table)
2541
2542
2543 @cli.command(name='ns-scale')
2544 @click.argument('ns_name')
2545 @click.option('--ns_scale_group', prompt=True)
2546 @click.option('--index', prompt=True)
2547 @click.pass_context
2548 def ns_scale(ctx, ns_name, ns_scale_group, index):
2549 """scales NS
2550
2551 NS_NAME: name of the NS instance to be scaled
2552 """
2553 try:
2554 check_client_version(ctx.obj, ctx.command.name, 'v1')
2555 ctx.obj.ns.scale(ns_name, ns_scale_group, index)
2556 except ClientException as inst:
2557 print((inst.message))
2558 exit(1)
2559
2560
2561 @cli.command(name='config-agent-list')
2562 @click.pass_context
2563 def config_agent_list(ctx):
2564 """list config agents"""
2565 try:
2566 check_client_version(ctx.obj, ctx.command.name, 'v1')
2567 except ClientException as inst:
2568 print((inst.message))
2569 exit(1)
2570 table = PrettyTable(['name', 'account-type', 'details'])
2571 for account in ctx.obj.vca.list():
2572 table.add_row(
2573 [account['name'],
2574 account['account-type'],
2575 account['juju']])
2576 table.align = 'l'
2577 print(table)
2578
2579
2580 @cli.command(name='config-agent-delete')
2581 @click.argument('name')
2582 @click.pass_context
2583 def config_agent_delete(ctx, name):
2584 """deletes a config agent
2585
2586 NAME: name of the config agent to be deleted
2587 """
2588 try:
2589 check_client_version(ctx.obj, ctx.command.name, 'v1')
2590 ctx.obj.vca.delete(name)
2591 except ClientException as inst:
2592 print((inst.message))
2593 exit(1)
2594
2595
2596 @cli.command(name='config-agent-add')
2597 @click.option('--name',
2598 prompt=True)
2599 @click.option('--account_type',
2600 prompt=True)
2601 @click.option('--server',
2602 prompt=True)
2603 @click.option('--user',
2604 prompt=True)
2605 @click.option('--secret',
2606 prompt=True,
2607 hide_input=True,
2608 confirmation_prompt=True)
2609 @click.pass_context
2610 def config_agent_add(ctx, name, account_type, server, user, secret):
2611 """adds a config agent"""
2612 try:
2613 check_client_version(ctx.obj, ctx.command.name, 'v1')
2614 ctx.obj.vca.create(name, account_type, server, user, secret)
2615 except ClientException as inst:
2616 print((inst.message))
2617 exit(1)
2618
2619
2620 @cli.command(name='ro-dump')
2621 @click.pass_context
2622 def ro_dump(ctx):
2623 """shows RO agent information"""
2624 check_client_version(ctx.obj, ctx.command.name, 'v1')
2625 resp = ctx.obj.vim.get_resource_orchestrator()
2626 table = PrettyTable(['key', 'attribute'])
2627 for k, v in list(resp.items()):
2628 table.add_row([k, json.dumps(v, indent=2)])
2629 table.align = 'l'
2630 print(table)
2631
2632
2633 @cli.command(name='vcs-list')
2634 @click.pass_context
2635 def vcs_list(ctx):
2636 check_client_version(ctx.obj, ctx.command.name, 'v1')
2637 resp = ctx.obj.utils.get_vcs_info()
2638 table = PrettyTable(['component name', 'state'])
2639 for component in resp:
2640 table.add_row([component['component_name'], component['state']])
2641 table.align = 'l'
2642 print(table)
2643
2644
2645 @cli.command(name='ns-action')
2646 @click.argument('ns_name')
2647 @click.option('--vnf_name', default=None)
2648 @click.option('--action_name', prompt=True)
2649 @click.option('--params', prompt=True)
2650 @click.pass_context
2651 def ns_action(ctx,
2652 ns_name,
2653 vnf_name,
2654 action_name,
2655 params):
2656 """executes an action/primitive over a NS instance
2657
2658 NS_NAME: name or ID of the NS instance
2659 """
2660 try:
2661 check_client_version(ctx.obj, ctx.command.name)
2662 op_data={}
2663 if vnf_name:
2664 op_data['vnf_member_index'] = vnf_name
2665 op_data['primitive'] = action_name
2666 op_data['primitive_params'] = yaml.load(params)
2667 ctx.obj.ns.exec_op(ns_name, op_name='action', op_data=op_data)
2668
2669 except ClientException as inst:
2670 print((inst.message))
2671 exit(1)
2672
2673
2674 @cli.command(name='vnf-scale')
2675 @click.argument('ns_name')
2676 @click.argument('vnf_name')
2677 @click.option('--scaling-group', prompt=True, help="scaling-group-descriptor name to use")
2678 @click.option('--scale-in', default=False, is_flag=True, help="performs a scale in operation")
2679 @click.option('--scale-out', default=False, is_flag=True, help="performs a scale out operation (by default)")
2680 @click.pass_context
2681 def vnf_scale(ctx,
2682 ns_name,
2683 vnf_name,
2684 scaling_group,
2685 scale_in,
2686 scale_out):
2687 """
2688 Executes a VNF scale (adding/removing VDUs)
2689
2690 \b
2691 NS_NAME: name or ID of the NS instance.
2692 VNF_NAME: member-vnf-index in the NS to be scaled.
2693 """
2694 try:
2695 check_client_version(ctx.obj, ctx.command.name)
2696 if not scale_in and not scale_out:
2697 scale_out = True
2698 ctx.obj.ns.scale_vnf(ns_name, vnf_name, scaling_group, scale_in, scale_out)
2699 except ClientException as inst:
2700 print((inst.message))
2701 exit(1)
2702
2703
2704 ##############################
2705 # Role Management Operations #
2706 ##############################
2707
2708 @cli.command(name='role-create', short_help='creates a role')
2709 @click.argument('name')
2710 @click.option('--definition',
2711 default=None,
2712 help='role definition using a dictionary')
2713 @click.pass_context
2714 def role_create(ctx, name, definition):
2715 """
2716 Creates a new role.
2717
2718 \b
2719 NAME: Name or ID of the role.
2720 DEFINITION: Definition of grant/denial of access to resources.
2721 """
2722 try:
2723 check_client_version(ctx.obj, ctx.command.name)
2724 ctx.obj.role.create(name, definition)
2725 except ClientException as inst:
2726 print(inst.message)
2727
2728
2729 @cli.command(name='role-update', short_help='updates a role')
2730 @click.argument('name')
2731 @click.option('--definition',
2732 default=None,
2733 help='add a new definition to the role')
2734 @click.option('--add',
2735 default=None,
2736 help='add a resource access grant/denial')
2737 @click.option('--remove',
2738 default=None,
2739 help='remove a resource access grant/denial')
2740 @click.pass_context
2741 def role_update(ctx, name, definition, add, remove):
2742 """
2743 Updates a role.
2744
2745 \b
2746 NAME: Name or ID of the role.
2747 DEFINITION: Definition overwrites the old definition.
2748 ADD: Grant/denial of access to resource to add.
2749 REMOVE: Grant/denial of access to resource to remove.
2750 """
2751 try:
2752 check_client_version(ctx.obj, ctx.command.name)
2753 ctx.obj.role.update(name, definition, add, remove)
2754 except ClientException as inst:
2755 print(inst.message)
2756 exit(1)
2757
2758
2759 @cli.command(name='role-delete', short_help='deletes a role')
2760 @click.argument('name')
2761 # @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
2762 @click.pass_context
2763 def role_delete(ctx, name):
2764 """
2765 Deletes a role.
2766
2767 \b
2768 NAME: Name or ID of the role.
2769 """
2770 try:
2771 check_client_version(ctx.obj, ctx.command.name)
2772 ctx.obj.role.delete(name)
2773 except ClientException as inst:
2774 print(inst.message)
2775 exit(1)
2776
2777
2778 @cli.command(name='role-list', short_help='list all roles')
2779 @click.option('--filter', default=None,
2780 help='restricts the list to the projects matching the filter')
2781 @click.pass_context
2782 def role_list(ctx, filter):
2783 """
2784 List all roles.
2785 """
2786 try:
2787 check_client_version(ctx.obj, ctx.command.name)
2788 resp = ctx.obj.role.list(filter)
2789 except ClientException as inst:
2790 print(inst.message)
2791 exit(1)
2792 table = PrettyTable(['name', 'id'])
2793 for role in resp:
2794 table.add_row([role['name'], role['_id']])
2795 table.align = 'l'
2796 print(table)
2797
2798
2799 @cli.command(name='role-show', short_help='show specific role')
2800 @click.argument('name')
2801 @click.pass_context
2802 def role_show(ctx, name):
2803 """
2804 Shows the details of a role.
2805
2806 \b
2807 NAME: Name or ID of the role.
2808 """
2809 try:
2810 check_client_version(ctx.obj, ctx.command.name)
2811 resp = ctx.obj.role.get(name)
2812 except ClientException as inst:
2813 print(inst.message)
2814 exit(1)
2815
2816 table = PrettyTable(['key', 'attribute'])
2817 for k, v in resp.items():
2818 table.add_row([k, json.dumps(v, indent=2)])
2819 table.align = 'l'
2820 print(table)
2821
2822
2823 if __name__ == '__main__':
2824 try:
2825 cli()
2826 except pycurl.error as e:
2827 print(e)
2828 print('Maybe "--hostname" option or OSM_HOSTNAME' +
2829 'environment variable needs to be specified')
2830 exit(1)