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