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