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