X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=osmclient%2Fscripts%2Fosm.py;h=406a9b9a67fc7590f1e7e0d2feea0f8d379a1dfb;hb=0a2a4d456c86923ef8a8712b746311561260646a;hp=57c2a2716f2de47a630f69b1de8fbe6f24341faf;hpb=14f8fbd75c6106b6b0601e0b4ac685cc011e073b;p=osm%2Fosmclient.git diff --git a/osmclient/scripts/osm.py b/osmclient/scripts/osm.py index 57c2a27..406a9b9 100755 --- a/osmclient/scripts/osm.py +++ b/osmclient/scripts/osm.py @@ -152,8 +152,10 @@ def cli_osm(ctx, hostname, user, password, project, verbose): @cli_osm.command(name='ns-list', short_help='list all NS instances') @click.option('--filter', default=None, help='restricts the list to the NS instances matching the filter.') +@click.option('--long', is_flag=True, + help='get more details of current operation in the NS.') @click.pass_context -def ns_list(ctx, filter): +def ns_list(ctx, filter, long): """list all NS instances \b @@ -201,45 +203,181 @@ def ns_list(ctx, filter): --filter nsd.vendor=&nsd-ref= --filter nsd.constituent-vnfd.vnfd-id-ref= """ + def summarize_deployment_status(status_dict): + #Nets + summary = "" + n_nets = 0 + status_nets = {} + net_list = status_dict['nets'] + for net in net_list: + n_nets += 1 + if net['status'] not in status_nets: + status_nets[net['status']] = 1 + else: + status_nets[net['status']] +=1 + message = "Nets: " + for k,v in status_nets.items(): + message += "{}:{},".format(k,v) + message += "TOTAL:{}".format(n_nets) + summary += "{}".format(message) + #VMs and VNFs + n_vms = 0 + status_vms = {} + status_vnfs = {} + vnf_list = status_dict['vnfs'] + for vnf in vnf_list: + member_vnf_index = vnf['member_vnf_index'] + if member_vnf_index not in status_vnfs: + status_vnfs[member_vnf_index] = {} + for vm in vnf['vms']: + n_vms += 1 + if vm['status'] not in status_vms: + status_vms[vm['status']] = 1 + else: + status_vms[vm['status']] +=1 + if vm['status'] not in status_vnfs[member_vnf_index]: + status_vnfs[member_vnf_index][vm['status']] = 1 + else: + status_vnfs[member_vnf_index][vm['status']] += 1 + message = "VMs: " + for k,v in status_vms.items(): + message += "{}:{},".format(k,v) + message += "TOTAL:{}".format(n_vms) + summary += "\n{}".format(message) + summary += "\nNFs:" + for k,v in status_vnfs.items(): + total = 0 + message = "\n {} VMs: ".format(k) + for k2,v2 in v.items(): + message += "{}:{},".format(k2,v2) + total += v2 + message += "TOTAL:{}".format(total) + summary += message + return summary + + def summarize_config_status(ee_list): + n_ee = 0 + status_ee = {} + for ee in ee_list: + n_ee += 1 + if ee['elementType'] not in status_ee: + status_ee[ee['elementType']] = {} + status_ee[ee['elementType']][ee['status']] = 1 + continue; + if ee['status'] in status_ee[ee['elementType']]: + status_ee[ee['elementType']][ee['status']] += 1 + else: + status_ee[ee['elementType']][ee['status']] = 1 + summary = "" + for elementType in ["KDU", "VDU", "PDU", "VNF", "NS"]: + if elementType in status_ee: + message = "" + total = 0 + for k,v in status_ee[elementType].items(): + message += "{}:{},".format(k,v) + total += v + message += "TOTAL:{}\n".format(total) + summary += "{}: {}".format(elementType, message) + summary += "TOTAL Exec. Env.: {}".format(n_ee) + return summary logger.debug("") if filter: check_client_version(ctx.obj, '--filter') resp = ctx.obj.ns.list(filter) else: resp = ctx.obj.ns.list() - table = PrettyTable( + if long: + table = PrettyTable( ['ns instance name', 'id', - 'operational status', - 'config status', - 'detailed status']) + 'date', + 'ns state', + 'current operation', + 'error details', + 'project', + 'vim (inst param)', + 'deployment status', + 'configuration status']) + project_list = ctx.obj.project.list() + vim_list = ctx.obj.vim.list() + else: + table = PrettyTable( + ['ns instance name', + 'id', + 'date', + 'ns state', + 'current operation', + 'error details']) for ns in resp: fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__ if fullclassname == 'osmclient.sol005.client.Client': nsr = ns nsr_name = nsr['name'] nsr_id = nsr['_id'] + date = datetime.fromtimestamp(nsr['create-time']).strftime("%Y-%m-%dT%H:%M:%S") + ns_state = nsr['nsState'] + if long: + deployment_status = summarize_deployment_status(nsr['deploymentStatus']) + config_status = summarize_config_status(nsr['configurationStatus']) + project_id = nsr.get('_admin').get('projects_read')[0] + project_name = '-' + for p in project_list: + if p['_id'] == project_id: + project_name = p['name'] + break + #project = '{} ({})'.format(project_name, project_id) + project = project_name + vim_id = nsr.get('datacenter') + vim_name = '-' + for v in vim_list: + if v['uuid'] == vim_id: + vim_name = v['name'] + break + #vim = '{} ({})'.format(vim_name, vim_id) + vim = vim_name + current_operation = "{} ({})".format(nsr['currentOperation'],nsr['currentOperationID']) + error_details = "N/A" + if ns_state == "BROKEN" or ns_state == "DEGRADED": + error_details = "{}\nDetail: {}".format(nsr['errorDescription'],nsr['errorDetail']) else: nsopdata = ctx.obj.ns.get_opdata(ns['id']) nsr = nsopdata['nsr:nsr'] nsr_name = nsr['name-ref'] nsr_id = nsr['ns-instance-config-ref'] - opstatus = nsr['operational-status'] if 'operational-status' in nsr else 'Not found' - configstatus = nsr['config-status'] if 'config-status' in nsr else 'Not found' - detailed_status = nsr['detailed-status'] if 'detailed-status' in nsr else 'Not found' - detailed_status = wrap_text(text=detailed_status,width=50) - if configstatus == "config_not_needed": - configstatus = "configured (no charms)" - - table.add_row( - [nsr_name, - nsr_id, - opstatus, - configstatus, - detailed_status]) + date = '-' + project = '-' + deployment_status = nsr['operational-status'] if 'operational-status' in nsr else 'Not found' + ns_state = deployment_status + config_status = nsr['config-status'] if 'config-status' in nsr else 'Not found' + current_operation = "Unknown" + error_details = nsr['detailed-status'] if 'detailed-status' in nsr else 'Not found' + if config_status == "config_not_needed": + config_status = "configured (no charms)" + + if long: + table.add_row( + [nsr_name, + nsr_id, + date, + ns_state, + current_operation, + wrap_text(text=error_details,width=40), + project, + vim, + deployment_status, + config_status]) + else: + table.add_row( + [nsr_name, + nsr_id, + date, + ns_state, + current_operation, + wrap_text(text=error_details,width=40)]) table.align = 'l' print(table) - + print('To get the history of all operations over a NS, run "osm ns-op-list NS_ID"') + print('For more details on the current operation, run "osm ns-op-show OPERATION_ID"') def nsd_list(ctx, filter): logger.debug("") @@ -485,6 +623,18 @@ def ns_op_list(ctx, name): NAME: name or ID of the NS instance """ + def formatParams(params): + if params['lcmOperationType']=='instantiate': + params.pop('nsDescription') + params.pop('nsName') + params.pop('nsdId') + params.pop('nsr_id') + elif params['lcmOperationType']=='action': + params.pop('primitive') + params.pop('lcmOperationType') + params.pop('nsInstanceId') + return params + logger.debug("") # try: check_client_version(ctx.obj, ctx.command.name) @@ -493,14 +643,27 @@ def ns_op_list(ctx, name): # print(str(e)) # exit(1) - table = PrettyTable(['id', 'operation', 'action_name', 'status']) + table = PrettyTable(['id', 'operation', 'action_name', 'operation_params', 'status', 'detail']) #print(yaml.safe_dump(resp)) for op in resp: action_name = "N/A" if op['lcmOperationType']=='action': action_name = op['operationParams']['primitive'] - table.add_row([op['id'], op['lcmOperationType'], action_name, - op['operationState']]) + detail = "-" + if op['operationState']=='PROCESSING': + if op['lcmOperationType']=='instantiate': + if op['stage']: + detail = op['stage'] + else: + detail = "In queue. Current position: {}".format(op['queuePosition']) + elif op['operationState']=='FAILED' or op['operationState']=='FAILED_TEMP': + detail = op['errorMessage'] + table.add_row([op['id'], + op['lcmOperationType'], + action_name, + wrap_text(text=json.dumps(formatParams(op['operationParams']),indent=2),width=70), + op['operationState'], + wrap_text(text=detail,width=50)]) table.align = 'l' print(table) @@ -2418,7 +2581,7 @@ def sdnc_show(ctx, name): # K8s cluster operations ########################### -@cli_osm.command(name='k8scluster-add') +@cli_osm.command(name='k8scluster-add', short_help='adds a K8s cluster to OSM') @click.argument('name') @click.option('--creds', prompt=True, @@ -2523,7 +2686,7 @@ def k8scluster_update(ctx, # exit(1) -@cli_osm.command(name='k8scluster-delete') +@cli_osm.command(name='k8scluster-delete', short_help='deletes a K8s cluster') @click.argument('name') @click.option('--force', is_flag=True, help='forces the deletion from the DB (not recommended)') #@click.option('--wait', @@ -2569,7 +2732,7 @@ def k8scluster_list(ctx, filter, literal): # exit(1) -@cli_osm.command(name='k8scluster-show') +@cli_osm.command(name='k8scluster-show', short_help='shows the details of a K8s cluster') @click.argument('name') @click.option('--literal', is_flag=True, help='print literally, no pretty table') @@ -2599,7 +2762,7 @@ def k8scluster_show(ctx, name, literal): # Repo operations ########################### -@cli_osm.command(name='repo-add') +@cli_osm.command(name='repo-add', short_help='adds a repo to OSM') @click.argument('name') @click.argument('uri') @click.option('--type', @@ -2636,7 +2799,7 @@ def repo_add(ctx, # exit(1) -@cli_osm.command(name='repo-update') +@cli_osm.command(name='repo-update', short_help='updates a repo in OSM') @click.argument('name') @click.option('--newname', help='New name for the repo') @click.option('--uri', help='URI of the repo') @@ -2670,7 +2833,7 @@ def repo_update(ctx, # exit(1) -@cli_osm.command(name='repo-delete') +@cli_osm.command(name='repo-delete', short_help='deletes a repo') @click.argument('name') @click.option('--force', is_flag=True, help='forces the deletion from the DB (not recommended)') #@click.option('--wait', @@ -2715,7 +2878,7 @@ def repo_list(ctx, filter, literal): # exit(1) -@cli_osm.command(name='repo-show') +@cli_osm.command(name='repo-show', short_help='shows the details of a repo') @click.argument('name') @click.option('--literal', is_flag=True, help='print literally, no pretty table') @@ -3128,9 +3291,10 @@ def ns_metric_export(ctx, ns, vnf, vdu, metric, interval): # Other operations #################### -@cli_osm.command(name='version') +@cli_osm.command(name='version', short_help='shows client and server versions') @click.pass_context def get_version(ctx): + """shows client and server versions""" # try: check_client_version(ctx.obj, "version") print ("Server version: {}".format(ctx.obj.get_version())) @@ -3671,13 +3835,17 @@ def package_build(ctx, def cli(): try: cli_osm() + exit(0) except pycurl.error as exc: print(exc) print('Maybe "--hostname" option or OSM_HOSTNAME environment variable needs to be specified') - exit(1) except ClientException as exc: print("ERROR: {}".format(exc)) - exit(1) + except (FileNotFoundError, PermissionError) as exc: + print("Cannot open file: {}".format(exc)) + except yaml.YAMLError as exc: + print("Invalid YAML format: {}".format(exc)) + exit(1) # TODO capture other controlled exceptions here # TODO remove the ClientException captures from all places, unless they do something different