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