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