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