From f6a4f77b98e5f05b74938b44bcbb2ed148e4941a Mon Sep 17 00:00:00 2001 From: Felipe Vicens Date: Thu, 14 May 2020 09:36:21 +0200 Subject: [PATCH 01/16] Fix #1063 flake tests osmclient is failing in jenkins merge stage due flake check Change-Id: I25f8940e000949176798c14207b7801434c7ff35 Signed-off-by: Felipe Vicens --- osmclient/common/package_tool.py | 4 ++-- osmclient/sol005/k8scluster.py | 2 +- osmclient/sol005/ns.py | 2 +- osmclient/sol005/nsi.py | 2 +- osmclient/sol005/pdud.py | 2 +- osmclient/sol005/repo.py | 2 +- osmclient/sol005/user.py | 2 +- osmclient/sol005/vnfd.py | 2 +- osmclient/sol005/wim.py | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/osmclient/common/package_tool.py b/osmclient/common/package_tool.py index 652dbc2..d60c1d0 100644 --- a/osmclient/common/package_tool.py +++ b/osmclient/common/package_tool.py @@ -290,7 +290,7 @@ class PackageTool(object): if os.path.isdir('{}/charms/layers/{}'.format(package_folder,charmName)): print('Building charm {}/charms/layers/{}'.format(package_folder, charmName)) self.charm_build(package_folder, charmName) - print('Charm built'.format(charmName)) + print('Charm built {}'.format(charmName)) else: if not os.path.isdir('{}/charms/{}'.format(package_folder,charmName)): raise ClientException ('The charm: {} referenced in the descriptor file ' @@ -392,7 +392,7 @@ class PackageTool(object): ignore_patterns = ('.gitignore') ignore = shutil.ignore_patterns(ignore_patterns) directory_name = os.path.abspath("{}/tmp".format(package_folder)) - os.makedirs("{}/{}".format(directory_name, os.path.basename(package_folder),exist_ok=True)) + os.makedirs("{}/{}".format(directory_name, os.path.basename(package_folder)),exist_ok=True) self._logger.debug("Makedirs DONE: {}/{}".format(directory_name, os.path.basename(package_folder))) for item in os.listdir(package_folder): self._logger.debug("Item: {}".format(item)) diff --git a/osmclient/sol005/k8scluster.py b/osmclient/sol005/k8scluster.py index 0cdb089..4ac2e48 100644 --- a/osmclient/sol005/k8scluster.py +++ b/osmclient/sol005/k8scluster.py @@ -135,7 +135,7 @@ class K8scluster(object): if resp: resp = json.loads(resp) if not resp or '_id' not in resp: - raise ClientException('failed to get K8s cluster info: '.format(resp)) + raise ClientException('failed to get K8s cluster info: {}'.format(resp)) return resp except NotFound: raise NotFound("K8s cluster {} not found".format(name)) diff --git a/osmclient/sol005/ns.py b/osmclient/sol005/ns.py index 4798bf3..b551868 100644 --- a/osmclient/sol005/ns.py +++ b/osmclient/sol005/ns.py @@ -294,7 +294,7 @@ class Ns(object): filter_string = '' if filter: filter_string = '&{}'.format(filter) - http_code, resp = self._http.get2_cmd('{}?nsInstanceId={}'.format( + http_code, resp = self._http.get2_cmd('{}?nsInstanceId={}{}'.format( self._apiBase, ns['_id'], filter_string) ) #print('HTTP CODE: {}'.format(http_code)) diff --git a/osmclient/sol005/nsi.py b/osmclient/sol005/nsi.py index 4671441..be1deea 100644 --- a/osmclient/sol005/nsi.py +++ b/osmclient/sol005/nsi.py @@ -271,7 +271,7 @@ class Nsi(object): filter_string = '' if filter: filter_string = '&{}'.format(filter) - http_code, resp = self._http.get2_cmd('{}?netsliceInstanceId={}'.format( + http_code, resp = self._http.get2_cmd('{}?netsliceInstanceId={}{}'.format( self._apiBase, nsi['_id'], filter_string) ) #print('HTTP CODE: {}'.format(http_code)) diff --git a/osmclient/sol005/pdud.py b/osmclient/sol005/pdud.py index 3abb78c..aa4bf69 100644 --- a/osmclient/sol005/pdud.py +++ b/osmclient/sol005/pdud.py @@ -118,7 +118,7 @@ class Pdu(object): if resp: resp = json.loads(resp) if not resp or 'id' not in resp: - raise ClientException('unexpected response from server: '.format( + raise ClientException('unexpected response from server: {}'.format( resp)) print(resp['id']) #else: diff --git a/osmclient/sol005/repo.py b/osmclient/sol005/repo.py index 7a31397..cc82402 100644 --- a/osmclient/sol005/repo.py +++ b/osmclient/sol005/repo.py @@ -129,7 +129,7 @@ class Repo(object): if resp: resp = json.loads(resp) if not resp or '_id' not in resp: - raise ClientException('failed to get repo info: '.format(resp)) + raise ClientException('failed to get repo info: {}'.format(resp)) return resp except NotFound: raise NotFound("Repo {} not found".format(name)) diff --git a/osmclient/sol005/user.py b/osmclient/sol005/user.py index 1584878..d28514e 100644 --- a/osmclient/sol005/user.py +++ b/osmclient/sol005/user.py @@ -201,7 +201,7 @@ class User(object): filter_string = '' if filter: filter_string = '?{}'.format(filter) - _, resp = self._http.get2_cmd('{}{}'.format(self._apiBase,filter_string,skip_query_admin=True)) + _, resp = self._http.get2_cmd('{}{}'.format(self._apiBase,filter_string), skip_query_admin=True) #print('RESP: {}'.format(resp)) if resp: return json.loads(resp) diff --git a/osmclient/sol005/vnfd.py b/osmclient/sol005/vnfd.py index 8307088..48fbdb7 100644 --- a/osmclient/sol005/vnfd.py +++ b/osmclient/sol005/vnfd.py @@ -253,7 +253,7 @@ class Vnfd(object): if resp: resp = json.loads(resp) if not resp or 'id' not in resp: - raise ClientException('unexpected response from server: '.format(resp)) + raise ClientException('unexpected response from server: {}'.format(resp)) print(resp['id']) elif http_code == 204: print('Updated') diff --git a/osmclient/sol005/wim.py b/osmclient/sol005/wim.py index 0ba8ab4..5da3941 100644 --- a/osmclient/sol005/wim.py +++ b/osmclient/sol005/wim.py @@ -236,7 +236,7 @@ class Wim(object): if resp: resp = json.loads(resp) if not resp or '_id' not in resp: - raise ClientException('failed to get wim info: '.format(resp)) + raise ClientException('failed to get wim info: {}'.format(resp)) return resp except NotFound: raise NotFound("wim '{}' not found".format(name)) -- 2.25.1 From b9c74d9f6c0c2ebc0b53064bffad625d3fa42d0f Mon Sep 17 00:00:00 2001 From: tierno Date: Mon, 18 May 2020 12:04:52 +0000 Subject: [PATCH 02/16] fix 1066: package creation over a path Change-Id: I980aeee9236ba666a861f13b70fbbfc19db731ef Signed-off-by: tierno --- osmclient/common/package_tool.py | 63 +++++++++++++++++--------------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/osmclient/common/package_tool.py b/osmclient/common/package_tool.py index d60c1d0..203806e 100644 --- a/osmclient/common/package_tool.py +++ b/osmclient/common/package_tool.py @@ -151,18 +151,17 @@ class PackageTool(object): """ self._logger.debug("") files = [f for f in glob.glob(package_folder + "/**/*.*", recursive=True)] - checksum = open("{}/checksums.txt".format(package_folder), "w+") - for file_item in files: - if "checksums.txt" in file_item: - continue - # from https://www.quickprogrammingtips.com/python/how-to-calculate-md5-hash-of-a-file-in-python.html - md5_hash = hashlib.md5() - with open(file_item, "rb") as f: - # Read and update hash in chunks of 4K - for byte_block in iter(lambda: f.read(4096), b""): - md5_hash.update(byte_block) - checksum.write("{}\t{}\n".format(md5_hash.hexdigest(), file_item)) - checksum.close() + with open("{}/checksums.txt".format(package_folder), "w+") as checksum: + for file_item in files: + if "checksums.txt" in file_item: + continue + # from https://www.quickprogrammingtips.com/python/how-to-calculate-md5-hash-of-a-file-in-python.html + md5_hash = hashlib.md5() + with open(file_item, "rb") as f: + # Read and update hash in chunks of 4K + for byte_block in iter(lambda: f.read(4096), b""): + md5_hash.update(byte_block) + checksum.write("{}\t{}\n".format(md5_hash.hexdigest(), file_item)) def create_folders(self, folders, package_type): """ @@ -362,27 +361,31 @@ class PackageTool(object): returns: .tar.gz name """ self._logger.debug("") + cwd = None try: - directory_name = self.create_temp_dir(package_folder, charm_list) + directory_name, package_name = self.create_temp_dir(package_folder, charm_list) cwd = os.getcwd() os.chdir(directory_name) - self.calculate_checksum(package_folder) - with tarfile.open("{}.tar.gz".format(package_folder), mode='w:gz') as archive: - print("Adding File: {}".format(package_folder)) - archive.add('{}'.format(package_folder), recursive=True) + self.calculate_checksum(package_name) + with tarfile.open("{}.tar.gz".format(package_name), mode='w:gz') as archive: + print("Adding File: {}".format(package_name)) + archive.add('{}'.format(package_name), recursive=True) #return "Created {}.tar.gz".format(package_folder) #self.build("{}".format(os.path.basename(package_folder))) os.chdir(cwd) except Exception as exc: + if cwd: + os.chdir(cwd) shutil.rmtree(os.path.join(package_folder, "tmp")) raise ClientException('failure during build of targz file (create temp dir, calculate checksum, tar.gz file): {}'.format(exc)) - os.rename("{}/{}.tar.gz".format(directory_name, os.path.basename(package_folder)), - "{}.tar.gz".format(os.path.basename(package_folder))) - os.rename("{}/{}/checksums.txt".format(directory_name, os.path.basename(package_folder)), + created_package = "{}/{}.tar.gz".format(package_folder, package_name) + os.rename("{}/{}.tar.gz".format(directory_name, package_name), + created_package) + os.rename("{}/{}/checksums.txt".format(directory_name, package_name), "{}/checksums.txt".format(package_folder)) shutil.rmtree(os.path.join(package_folder, "tmp")) - print("Package created: {}.tar.gz".format(os.path.basename(package_folder))) - return "{}.tar.gz".format(package_folder) + print("Package created: {}".format(created_package)) + return created_package def create_temp_dir(self, package_folder, charm_list=None): """ @@ -391,14 +394,16 @@ class PackageTool(object): self._logger.debug("") ignore_patterns = ('.gitignore') ignore = shutil.ignore_patterns(ignore_patterns) - directory_name = os.path.abspath("{}/tmp".format(package_folder)) - os.makedirs("{}/{}".format(directory_name, os.path.basename(package_folder)),exist_ok=True) - self._logger.debug("Makedirs DONE: {}/{}".format(directory_name, os.path.basename(package_folder))) + directory_name = os.path.abspath(package_folder) + package_name = os.path.basename(directory_name) + directory_name += "/tmp" + os.makedirs("{}/{}".format(directory_name, package_name), exist_ok=True) + self._logger.debug("Makedirs DONE: {}/{}".format(directory_name, package_name)) for item in os.listdir(package_folder): self._logger.debug("Item: {}".format(item)) if item != "tmp": s = os.path.join(package_folder, item) - d = os.path.join(os.path.join(directory_name, os.path.basename(package_folder)), item) + d = os.path.join(os.path.join(directory_name, package_name), item) if os.path.isdir(s): if item == "charms": os.makedirs(d, exist_ok=True) @@ -415,11 +420,11 @@ class PackageTool(object): format(charm, package_folder, package_folder)) d_temp = os.path.join(d, charm) self._logger.debug("Copying tree: {} -> {}".format(s_charm, d_temp)) - shutil.copytree(s_charm, d_temp, symlinks = True, ignore = ignore) + shutil.copytree(s_charm, d_temp, symlinks=True, ignore=ignore) self._logger.debug("DONE") else: self._logger.debug("Copying tree: {} -> {}".format(s,d)) - shutil.copytree(s, d, symlinks = True, ignore = ignore) + shutil.copytree(s, d, symlinks=True, ignore=ignore) self._logger.debug("DONE") else: if item in ignore_patterns: @@ -427,7 +432,7 @@ class PackageTool(object): self._logger.debug("Copying file: {} -> {}".format(s,d)) shutil.copy2(s, d) self._logger.debug("DONE") - return directory_name + return directory_name, package_name def charms_search(self, descriptor_file, desc_type): self._logger.debug("") -- 2.25.1 From 5f8a12367e3fba2a6f2ee7025e801dbff6da37a7 Mon Sep 17 00:00:00 2001 From: garciadeblas Date: Fri, 22 May 2020 14:33:35 +0000 Subject: [PATCH 03/16] package_tool: fix calculate checksum to consider only files Change-Id: I7676fbb51fb3d4f3f4ad174241c0e93418a8c49f Signed-off-by: garciadeblas --- osmclient/common/package_tool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osmclient/common/package_tool.py b/osmclient/common/package_tool.py index 203806e..3da96d5 100644 --- a/osmclient/common/package_tool.py +++ b/osmclient/common/package_tool.py @@ -150,7 +150,7 @@ class PackageTool(object): :returns: None """ self._logger.debug("") - files = [f for f in glob.glob(package_folder + "/**/*.*", recursive=True)] + files = [f for f in glob.glob(package_folder + "/**/*.*", recursive=True) if os.path.isfile(f)] with open("{}/checksums.txt".format(package_folder), "w+") as checksum: for file_item in files: if "checksums.txt" in file_item: -- 2.25.1 From bcb78331f0e8a321585fc7b84e9b7857a90ecb6c Mon Sep 17 00:00:00 2001 From: gomezl Date: Wed, 6 May 2020 09:44:54 +0200 Subject: [PATCH 04/16] Feature 8178 VNF Repositories Change-Id: I040da8dd9d5696f9029cf7ecf82aa1eff26bb22a Signed-off-by: gomezl --- .gitignore | 15 ++ osmclient/common/package_tool.py | 2 +- osmclient/scripts/osm.py | 348 ++++++++++++++++++++++----- osmclient/sol005/client.py | 2 + osmclient/sol005/osmrepo.py | 390 +++++++++++++++++++++++++++++++ requirements.txt | 3 + setup.py | 3 +- 7 files changed, 707 insertions(+), 56 deletions(-) create mode 100644 osmclient/sol005/osmrepo.py diff --git a/.gitignore b/.gitignore index afae2d8..b4c9ff5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,17 @@ +# +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. *.pyc .cache deb_dist/ @@ -17,3 +31,4 @@ stage/ .tox/ snap/.snapcraft/ .vscode +build/ diff --git a/osmclient/common/package_tool.py b/osmclient/common/package_tool.py index 3da96d5..c2e1995 100644 --- a/osmclient/common/package_tool.py +++ b/osmclient/common/package_tool.py @@ -289,7 +289,7 @@ class PackageTool(object): if os.path.isdir('{}/charms/layers/{}'.format(package_folder,charmName)): print('Building charm {}/charms/layers/{}'.format(package_folder, charmName)) self.charm_build(package_folder, charmName) - print('Charm built {}'.format(charmName)) + print('Charm built: {}'.format(charmName)) else: if not os.path.isdir('{}/charms/{}'.format(package_folder,charmName)): raise ClientException ('The charm: {} referenced in the descriptor file ' diff --git a/osmclient/scripts/osm.py b/osmclient/scripts/osm.py index 46ceb91..402a09b 100755 --- a/osmclient/scripts/osm.py +++ b/osmclient/scripts/osm.py @@ -20,7 +20,7 @@ OSM shell/cli import click from osmclient import client -from osmclient.common.exceptions import ClientException +from osmclient.common.exceptions import ClientException, NotFound from prettytable import PrettyTable import yaml import json @@ -466,6 +466,26 @@ def nsd_list2(ctx, filter, long): nsd_list(ctx, filter, long) +def pkg_repo_list(ctx, pkgtype, filter, repo, long): + resp = ctx.obj.osmrepo.pkg_list(pkgtype, filter, repo) + if long: + table = PrettyTable(['nfpkg name', 'vendor', 'version', 'latest', 'description', 'repository']) + else: + table = PrettyTable(['nfpkg name', 'repository']) + for vnfd in resp: + name = vnfd.get('name', '-') + repository = vnfd.get('repository') + if long: + vendor = vnfd.get('vendor') + version = vnfd.get('version') + description = vnfd.get('description') + latest = vnfd.get('latest') + table.add_row([name, vendor, version, latest, description, repository]) + else: + table.add_row([name, repository]) + table.align = 'l' + print(table) + def vnfd_list(ctx, nf_type, filter, long): logger.debug("") if nf_type: @@ -493,7 +513,7 @@ def vnfd_list(ctx, nf_type, filter, long): fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__ if fullclassname == 'osmclient.sol005.client.Client': if long: - table = PrettyTable(['nfpkg name', 'id', 'onboarding state', 'operational state', + table = PrettyTable(['nfpkg name', 'id', 'vendor', 'version', 'onboarding state', 'operational state', 'usage state', 'date', 'last update']) else: table = PrettyTable(['nfpkg name', 'id']) @@ -502,10 +522,12 @@ def vnfd_list(ctx, nf_type, filter, long): if long: onb_state = vnfd['_admin'].get('onboardingState','-') op_state = vnfd['_admin'].get('operationalState','-') + vendor = vnfd.get('vendor') + version = vnfd.get('version') usage_state = vnfd['_admin'].get('usageState','-') date = datetime.fromtimestamp(vnfd['_admin']['created']).strftime("%Y-%m-%dT%H:%M:%S") last_update = datetime.fromtimestamp(vnfd['_admin']['modified']).strftime("%Y-%m-%dT%H:%M:%S") - table.add_row([name, vnfd['_id'], onb_state, op_state, usage_state, date, last_update]) + table.add_row([name, vnfd['_id'], vendor, version, onb_state, op_state, usage_state, date, last_update]) else: table.add_row([name, vnfd['_id']]) else: @@ -539,6 +561,17 @@ def vnfd_list2(ctx, nf_type, filter, long): logger.debug("") vnfd_list(ctx, nf_type, filter, long) +@cli_osm.command(name='vnfpkg-repo-list', short_help='list all xNF from OSM repositories') +@click.option('--filter', default=None, + help='restricts the list to the NFpkg matching the filter') +@click.option('--repo', default=None, + help='restricts the list to a particular OSM repository') +@click.option('--long', is_flag=True, help='get more details') +@click.pass_context +def vnfd_list3(ctx, filter, repo, long): + """list xNF packages from OSM repositories""" + pkgtype = 'vnf' + pkg_repo_list(ctx, pkgtype, filter, repo, long) @cli_osm.command(name='nfpkg-list', short_help='list all xNF packages (VNF, HNF, PNF)') @click.option('--nf_type', help='type of NF (vnf, pnf, hnf)') @@ -556,6 +589,17 @@ def nfpkg_list(ctx, nf_type, filter, long): # print(str(e)) # exit(1) +@cli_osm.command(name='nfpkg-repo-list', short_help='list all xNF from OSM repositories') +@click.option('--filter', default=None, + help='restricts the list to the NFpkg matching the filter') +@click.option('--repo', default=None, + help='restricts the list to a particular OSM repository') +@click.option('--long', is_flag=True, help='get more details') +@click.pass_context +def vnfd_list4(ctx, filter, repo, long): + """list xNF packages from OSM repositories""" + pkgtype = 'vnf' + pkg_repo_list(ctx, pkgtype, filter, repo, long) def vnf_list(ctx, ns, filter, long): # try: @@ -619,6 +663,29 @@ def vnf_list1(ctx, ns, filter, long): logger.debug("") vnf_list(ctx, ns, filter, long) +@cli_osm.command(name='nsd-repo-list', short_help='list all NS from OSM repositories') +@click.option('--filter', default=None, + help='restricts the list to the NS matching the filter') +@click.option('--repo', default=None, + help='restricts the list to a particular OSM repository') +@click.option('--long', is_flag=True, help='get more details') +@click.pass_context +def nsd_list3(ctx, filter, repo, long): + """list xNF packages from OSM repositories""" + pkgtype = 'ns' + pkg_repo_list(ctx, pkgtype, filter, repo, long) + +@cli_osm.command(name='nspkg-repo-list', short_help='list all NS from OSM repositories') +@click.option('--filter', default=None, + help='restricts the list to the NS matching the filter') +@click.option('--repo', default=None, + help='restricts the list to a particular OSM repository') +@click.option('--long', is_flag=True, help='get more details') +@click.pass_context +def nspkg_list(ctx, filter, repo, long): + """list xNF packages from OSM repositories""" + pkgtype = 'ns' + pkg_repo_list(ctx, pkgtype, filter, repo, long) @cli_osm.command(name='nf-list', short_help='list all NF instances') @click.option('--ns', default=None, help='NS instance id or name to restrict the NF list') @@ -675,7 +742,7 @@ def nf_list(ctx, ns, filter, long): --filter vnfd-ref=,vdur.ip-address= """ logger.debug("") - vnf_list(ctx, ns, filter) + vnf_list(ctx, ns, filter, long) @cli_osm.command(name='ns-op-list', short_help='shows the history of operations over a NS instance') @@ -983,6 +1050,28 @@ def vnfd_show(ctx, name, literal): print(table) +def pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal): + logger.debug("") + # try: + resp = ctx.obj.osmrepo.pkg_get(pkgtype, name, repo, version, filter) + + if literal: + print(yaml.safe_dump(resp)) + return + pkgtype += 'd' + catalog = pkgtype + '-catalog' + full_catalog = pkgtype + ':' + catalog + if resp.get(catalog): + resp = resp.pop(catalog)[pkgtype][0] + elif resp.get(full_catalog): + resp = resp.pop(full_catalog)[pkgtype][0] + + table = PrettyTable(['field', 'value']) + for k, v in list(resp.items()): + table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)]) + table.align = 'l' + print(table) + @cli_osm.command(name='vnfd-show', short_help='shows the content of a VNFD') @click.option('--literal', is_flag=True, help='print literally, no pretty table') @@ -1010,6 +1099,69 @@ def vnfd_show2(ctx, name, literal): logger.debug("") vnfd_show(ctx, name, literal) +@cli_osm.command(name='vnfpkg-repo-show', short_help='shows the content of a VNFD') +@click.option('--literal', is_flag=True, + help='print literally, no pretty table') +@click.option('--repo', + required=True, + help='Repository name') +@click.argument('name') +@click.option('--filter', + help='filter by fields') +@click.option('--version', + default='latest', + help='package version') +@click.pass_context +def vnfd_show3(ctx, name, repo, version, literal=None, filter=None): + """shows the content of a VNFD in a repository + + NAME: name or ID of the VNFD/VNFpkg + """ + pkgtype = 'vnf' + pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal) + + +@cli_osm.command(name='nsd-repo-show', short_help='shows the content of a NSD') +@click.option('--literal', is_flag=True, + help='print literally, no pretty table') +@click.option('--repo', + required=True, + help='Repository name') +@click.argument('name') +@click.option('--filter', + help='filter by fields') +@click.option('--version', + default='latest', + help='package version') +@click.pass_context +def nsd_repo_show(ctx, name, repo, version, literal=None, filter=None): + """shows the content of a VNFD in a repository + + NAME: name or ID of the VNFD/VNFpkg + """ + pkgtype = 'ns' + pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal) + +@cli_osm.command(name='nspkg-repo-show', short_help='shows the content of a NSD') +@click.option('--literal', is_flag=True, + help='print literally, no pretty table') +@click.option('--repo', + required=True, + help='Repository name') +@click.argument('name') +@click.option('--filter', + help='filter by fields') +@click.option('--version', + default='latest', + help='package version') +@click.pass_context +def nsd_repo_show2(ctx, name, repo, version, literal=None, filter=None): + """shows the content of a VNFD in a repository + + NAME: name or ID of the VNFD/VNFpkg + """ + pkgtype = 'ns' + pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal) @cli_osm.command(name='nfpkg-show', short_help='shows the content of a NF Descriptor') @click.option('--literal', is_flag=True, @@ -1025,6 +1177,28 @@ def nfpkg_show(ctx, name, literal): vnfd_show(ctx, name, literal) +@cli_osm.command(name='nfpkg-repo-show', short_help='shows the content of a VNFD') +@click.option('--literal', is_flag=True, + help='print literally, no pretty table') +@click.option('--repo', + required=True, + help='Repository name') +@click.argument('name') +@click.option('--filter', + help='filter by fields') +@click.option('--version', + default='latest', + help='package version') +@click.pass_context +def vnfd_show4(ctx, name, repo, version, literal=None, filter=None): + """shows the content of a VNFD in a repository + + NAME: name or ID of the VNFD/VNFpkg + """ + pkgtype = 'vnf' + pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal) + + @cli_osm.command(name='ns-show', short_help='shows the info of a NS instance') @click.argument('name') @click.option('--literal', is_flag=True, @@ -1407,10 +1581,12 @@ def pdu_show(ctx, name, literal, filter): # CREATE operations #################### -def nsd_create(ctx, filename, overwrite, skip_charm_build): +def nsd_create(ctx, filename, overwrite, skip_charm_build, repo, vendor, version): logger.debug("") # try: check_client_version(ctx.obj, ctx.command.name) + if repo: + filename = ctx.obj.osmrepo.get_pkg('ns', filename, repo, vendor, version) ctx.obj.nsd.create(filename, overwrite=overwrite, skip_charm_build=skip_charm_build) # except ClientException as e: # print(str(e)) @@ -1426,8 +1602,14 @@ def nsd_create(ctx, filename, overwrite, skip_charm_build): '"key1.key2...=value[;key3...=value;...]"') @click.option('--skip-charm-build', default=False, is_flag=True, help='The charm will not be compiled, it is assumed to already exist') +@click.option('--repo', default=None, + help='[repository]: Repository name') +@click.option('--vendor', default=None, + help='[repository]: filter by vendor]') +@click.option('--version', default='latest', + help='[repository]: filter by version. Default: latest') @click.pass_context -def nsd_create1(ctx, filename, overwrite, skip_charm_build): +def nsd_create1(ctx, filename, overwrite, skip_charm_build, repo, vendor, version): """onboards a new NSpkg (alias of nspkg-create) (TO BE DEPRECATED) \b @@ -1436,7 +1618,8 @@ def nsd_create1(ctx, filename, overwrite, skip_charm_build): If FILENAME is an NF Package folder, it is built and then onboarded. """ logger.debug("") - nsd_create(ctx, filename, overwrite=overwrite, skip_charm_build=skip_charm_build) + nsd_create(ctx, filename, overwrite=overwrite, skip_charm_build=skip_charm_build, repo=repo, vendor=vendor, + version=version) @cli_osm.command(name='nspkg-create', short_help='creates a new NSD/NSpkg') @@ -1448,23 +1631,32 @@ def nsd_create1(ctx, filename, overwrite, skip_charm_build): '"key1.key2...=value[;key3...=value;...]"') @click.option('--skip-charm-build', default=False, is_flag=True, help='The charm will not be compiled, it is assumed to already exist') +@click.option('--repo', default=None, + help='[repository]: Repository name') +@click.option('--vendor', default=None, + help='[repository]: filter by vendor]') +@click.option('--version', default='latest', + help='[repository]: filter by version. Default: latest') @click.pass_context -def nsd_create2(ctx, filename, overwrite, skip_charm_build): +def nsd_pkg_create(ctx, filename, overwrite, skip_charm_build, repo, vendor, version): """onboards a new NSpkg - \b FILENAME: NF Package tar.gz file, NF Descriptor YAML file or NF Package folder If FILENAME is a file (NF Package tar.gz or NF Descriptor YAML), it is onboarded. If FILENAME is an NF Package folder, it is built and then onboarded. """ logger.debug("") - nsd_create(ctx, filename, overwrite=overwrite, skip_charm_build=skip_charm_build) + nsd_create(ctx, filename, overwrite=overwrite, skip_charm_build=skip_charm_build, repo=repo, vendor=vendor, + version=version) -def vnfd_create(ctx, filename, overwrite, skip_charm_build, override_epa, override_nonepa, override_paravirt): +def vnfd_create(ctx, filename, overwrite, skip_charm_build, override_epa, override_nonepa, override_paravirt, + repo, vendor, version): logger.debug("") # try: check_client_version(ctx.obj, ctx.command.name) + if repo: + filename = ctx.obj.osmrepo.get_pkg('vnf', filename, repo, vendor, version) ctx.obj.vnfd.create(filename, overwrite=overwrite, skip_charm_build=skip_charm_build, override_epa=override_epa, override_nonepa=override_nonepa, override_paravirt=override_paravirt) @@ -1488,10 +1680,16 @@ def vnfd_create(ctx, filename, overwrite, skip_charm_build, override_epa, overri help='removes all guest-epa parameters from all VDU') @click.option('--override-paravirt', required=False, default=False, is_flag=True, help='overrides all VDU interfaces to PARAVIRT') +@click.option('--repo', default=None, + help='[repository]: Repository name') +@click.option('--vendor', default=None, + help='[repository]: filter by vendor]') +@click.option('--version', default='latest', + help='[repository]: filter by version. Default: latest') @click.pass_context -def vnfd_create1(ctx, filename, overwrite, skip_charm_build, override_epa, override_nonepa, override_paravirt): - """onboards a new NFpkg (alias of nfpkg-create) (TO BE DEPRECATED) - +def vnfd_create1(ctx, filename, overwrite, skip_charm_build, override_epa, override_nonepa, override_paravirt, + repo,vendor, version): + """creates a new VNFD/VNFpkg \b FILENAME: NF Package tar.gz file, NF Descriptor YAML file or NF Package folder If FILENAME is a file (NF Package tar.gz or NF Descriptor YAML), it is onboarded. @@ -1499,7 +1697,8 @@ def vnfd_create1(ctx, filename, overwrite, skip_charm_build, override_epa, overr """ logger.debug("") vnfd_create(ctx, filename, overwrite=overwrite, skip_charm_build=skip_charm_build, - override_epa=override_epa, override_nonepa=override_nonepa, override_paravirt=override_paravirt) + override_epa=override_epa, override_nonepa=override_nonepa, override_paravirt=override_paravirt, + repo=repo, vendor=vendor, version=version) @cli_osm.command(name='vnfpkg-create', short_help='creates a new VNFD/VNFpkg') @@ -1517,10 +1716,16 @@ def vnfd_create1(ctx, filename, overwrite, skip_charm_build, override_epa, overr help='removes all guest-epa parameters from all VDU') @click.option('--override-paravirt', required=False, default=False, is_flag=True, help='overrides all VDU interfaces to PARAVIRT') +@click.option('--repo', default=None, + help='[repository]: Repository name') +@click.option('--vendor', default=None, + help='[repository]: filter by vendor]') +@click.option('--version', default='latest', + help='[repository]: filter by version. Default: latest') @click.pass_context -def vnfd_create2(ctx, filename, overwrite, skip_charm_build, override_epa, override_nonepa, override_paravirt): - """onboards a new NFpkg (alias of nfpkg-create) - +def vnfd_create2(ctx, filename, overwrite, skip_charm_build, override_epa, override_nonepa, override_paravirt, + repo, vendor, version): + """creates a new VNFD/VNFpkg \b FILENAME: NF Package tar.gz file, NF Descriptor YAML file or NF Package folder If FILENAME is a file (NF Package tar.gz or NF Descriptor YAML), it is onboarded. @@ -1528,8 +1733,8 @@ def vnfd_create2(ctx, filename, overwrite, skip_charm_build, override_epa, overr """ logger.debug("") vnfd_create(ctx, filename, overwrite=overwrite, skip_charm_build=skip_charm_build, - override_epa=override_epa, override_nonepa=override_nonepa, override_paravirt=override_paravirt) - + override_epa=override_epa, override_nonepa=override_nonepa, override_paravirt=override_paravirt, + repo=repo, vendor=vendor, version=version) @cli_osm.command(name='nfpkg-create', short_help='creates a new NFpkg') @click.argument('filename') @@ -1546,9 +1751,16 @@ def vnfd_create2(ctx, filename, overwrite, skip_charm_build, override_epa, overr help='removes all guest-epa parameters from all VDU') @click.option('--override-paravirt', required=False, default=False, is_flag=True, help='overrides all VDU interfaces to PARAVIRT') +@click.option('--repo', default=None, + help='[repository]: Repository name') +@click.option('--vendor', default=None, + help='[repository]: filter by vendor]') +@click.option('--version', default='latest', + help='[repository]: filter by version. Default: latest') @click.pass_context -def nfpkg_create(ctx, filename, overwrite, skip_charm_build, override_epa, override_nonepa, override_paravirt): - """onboards a new NFpkg (alias of nfpkg-create) +def nfpkg_create(ctx, filename, overwrite, skip_charm_build, override_epa, override_nonepa, override_paravirt, + repo, vendor, version): + """creates a new NFpkg \b FILENAME: NF Package tar.gz file, NF Descriptor YAML file or NF Package folder @@ -1557,7 +1769,8 @@ def nfpkg_create(ctx, filename, overwrite, skip_charm_build, override_epa, overr """ logger.debug("") vnfd_create(ctx, filename, overwrite=overwrite, skip_charm_build=skip_charm_build, - override_epa=override_epa, override_nonepa=override_nonepa, override_paravirt=override_paravirt) + override_epa=override_epa, override_nonepa=override_nonepa, override_paravirt=override_paravirt, + repo=repo, vendor=vendor, version=version) @cli_osm.command(name='ns-create', short_help='creates a new Network Service instance') @@ -2926,35 +3139,36 @@ def k8scluster_show(ctx, name, literal): @click.argument('name') @click.argument('uri') @click.option('--type', - type=click.Choice(['helm-chart', 'juju-bundle']), - prompt=True, - help='type of repo (helm-chart for Helm Charts, juju-bundle for Juju Bundles)') + type=click.Choice(['helm-chart', 'juju-bundle', 'osm']), + default='osm', + help='type of repo (helm-chart for Helm Charts, juju-bundle for Juju Bundles, osm for OSM Repositories)') @click.option('--description', default=None, help='human readable description') +@click.option('--user', + default=None, + help='OSM repository: The username of the OSM repository') +@click.option('--password', + default=None, + help='OSM repository: The password of the OSM repository') #@click.option('--wait', # is_flag=True, # help='do not return the control immediately, but keep it until the operation is completed, or timeout') @click.pass_context -def repo_add(ctx, - name, - uri, - type, - description): +def repo_add(ctx, **kwargs): """adds a repo to OSM NAME: name of the repo URI: URI of the repo """ # try: - check_client_version(ctx.obj, ctx.command.name) - repo = {} - repo['name'] = name - repo['url'] = uri - repo['type'] = type - if description: - repo['description'] = description - ctx.obj.repo.create(name, repo) + kwargs = {k: v for k, v in kwargs.items() if v is not None} + repo = kwargs + repo["url"] = repo.pop("uri") + if repo["type"] in ['helm-chart', 'juju-bundle']: + ctx.obj.repo.create(repo['name'], repo) + else: + ctx.obj.osmrepo.create(repo['name'], repo) # except ClientException as e: # print(str(e)) # exit(1) @@ -2964,8 +3178,6 @@ def repo_add(ctx, @click.argument('name') @click.option('--newname', help='New name for the repo') @click.option('--uri', help='URI of the repo') -@click.option('--type', type=click.Choice(['helm-chart', 'juju-bundle']), - help='type of repo (helm-chart for Helm Charts, juju-bundle for Juju Bundles)') @click.option('--description', help='human readable description') #@click.option('--wait', # is_flag=True, @@ -2975,7 +3187,6 @@ def repo_update(ctx, name, newname, uri, - type, description): """updates a repo in OSM @@ -2984,16 +3195,34 @@ def repo_update(ctx, # try: check_client_version(ctx.obj, ctx.command.name) repo = {} - if newname: repo['name'] = newname - if uri: repo['uri'] = uri - if type: repo['type'] = type + if newname: + repo['name'] = newname + if uri: + repo['uri'] = uri if description: repo['description'] = description - ctx.obj.repo.update(name, repo) + try: + ctx.obj.repo.update(name, repo) + except NotFound: + ctx.obj.osmrepo.update(name, repo) + # except ClientException as e: # print(str(e)) # exit(1) +@cli_osm.command(name='repo-index', short_help='Index a repository from a folder with artifacts') +@click.option('--origin', default='.', help='origin path where the artifacts are located') +@click.option('--destination', default='.', help='destination path where the index is deployed') +@click.pass_context +def repo_index(ctx, origin, destination): + """Index a repository + + NAME: name or ID of the repo to be deleted + """ + check_client_version(ctx.obj, ctx.command.name) + ctx.obj.osmrepo.repo_index(origin, destination) + + @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)') @@ -3006,9 +3235,11 @@ def repo_delete(ctx, name, force): NAME: name or ID of the repo to be deleted """ - # try: - check_client_version(ctx.obj, ctx.command.name) - ctx.obj.repo.delete(name, force=force) + logger.debug("") + try: + ctx.obj.repo.delete(name, force=force) + except NotFound: + ctx.obj.osmrepo.delete(name, force=force) # except ClientException as e: # print(str(e)) # exit(1) @@ -3023,8 +3254,10 @@ def repo_delete(ctx, name, force): def repo_list(ctx, filter, literal): """list all repos""" # try: + # K8s Repositories check_client_version(ctx.obj, ctx.command.name) resp = ctx.obj.repo.list(filter) + resp += ctx.obj.osmrepo.list(filter) if literal: print(yaml.safe_dump(resp)) return @@ -3034,6 +3267,7 @@ def repo_list(ctx, filter, literal): table.add_row([repo['name'], repo['_id'], repo['type'], repo['url'], trunc_text(repo.get('description',''),40)]) table.align = 'l' print(table) + # except ClientException as e: # print(str(e)) # exit(1) @@ -3049,14 +3283,20 @@ def repo_show(ctx, name, literal): NAME: name or ID of the repo """ - # try: - resp = ctx.obj.repo.get(name) + try: + resp = ctx.obj.repo.get(name) + except NotFound: + resp = ctx.obj.osmrepo.get(name) + if literal: - print(yaml.safe_dump(resp)) + if resp: + print(yaml.safe_dump(resp)) return table = PrettyTable(['key', 'attribute']) - for k, v in list(resp.items()): - table.add_row([k, json.dumps(v, indent=2)]) + if resp: + for k, v in list(resp.items()): + table.add_row([k, json.dumps(v, indent=2)]) + table.align = 'l' print(table) # except ClientException as e: diff --git a/osmclient/sol005/client.py b/osmclient/sol005/client.py index 5e28797..00c50cb 100644 --- a/osmclient/sol005/client.py +++ b/osmclient/sol005/client.py @@ -36,6 +36,7 @@ from osmclient.sol005 import role from osmclient.sol005 import pdud from osmclient.sol005 import k8scluster from osmclient.sol005 import repo +from osmclient.sol005 import osmrepo from osmclient.common import package_tool import json import logging @@ -93,6 +94,7 @@ class Client(object): self.pdu = pdud.Pdu(self._http_client, client=self) self.k8scluster = k8scluster.K8scluster(self._http_client, client=self) self.repo = repo.Repo(self._http_client, client=self) + self.osmrepo = osmrepo.OSMRepo(self._http_client, client=self) self.package_tool = package_tool.PackageTool(client=self) ''' self.vca = vca.Vca(http_client, client=self, **kwargs) diff --git a/osmclient/sol005/osmrepo.py b/osmclient/sol005/osmrepo.py new file mode 100644 index 0000000..4e37603 --- /dev/null +++ b/osmclient/sol005/osmrepo.py @@ -0,0 +1,390 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +""" +OSM Repo API handling +""" +from osmclient.common.exceptions import ClientException +from osmclient.sol005.repo import Repo +import requests +import logging +import tempfile +from shutil import copyfile, rmtree +import yaml +import tarfile +import glob +from packaging import version as versioning +import time +from os import listdir, mkdir, getcwd, remove +from os.path import isfile, isdir, join, abspath +import hashlib +from osm_im.validation import Validation as validation_im +import ruamel.yaml + + +class OSMRepo(Repo): + def __init__(self, http=None, client=None): + self._http = http + self._client = client + self._apiName = '/admin' + self._apiVersion = '/v1' + self._apiResource = '/osmrepos' + self._logger = logging.getLogger('osmclient') + self._apiBase = '{}{}{}'.format(self._apiName, + self._apiVersion, self._apiResource) + + def pkg_list(self, pkgtype, filter=None, repo=None): + """ + Returns a repo based on name or id + """ + self._logger.debug("") + self._client.get_token() + # Get OSM registered repository list + repositories = self.list() + if repo: + repositories = [r for r in repositories if r["name"] == repo] + if not repositories: + raise ClientException('Not repository found') + + vnf_repos = [] + for repository in repositories: + try: + r = requests.get('{}/index.yaml'.format(repository.get('url'))) + + if r.status_code == 200: + repo_list = yaml.safe_load(r.text) + vnf_packages = repo_list.get('{}_packages'.format(pkgtype)) + for repo in vnf_packages: + versions = vnf_packages.get(repo) + latest = versions.get('latest') + del versions['latest'] + for version in versions: + latest_version = False + if version == latest: + latest_version = True + vnf_repos.append({'vendor': versions[version].get("vendor"), + 'name': versions[version].get("name"), + 'version': version, + 'description': versions[version].get("description"), + 'location': versions[version].get("path"), + 'repository': repository.get('name'), + 'repourl': repository.get('url'), + 'latest': latest_version + }) + else: + raise Exception('repository in url {} unreachable'.format(repository.get('url'))) + except Exception as e: + logging.error("Error cannot read from repository {} '{}': {}".format(repository['name'], repository['url'], e)) + continue + + vnf_repos_filtered = [] + if filter: + for vnf_repo in vnf_repos: + for k, v in vnf_repo.items(): + if v: + kf, vf = filter.split('=') + if k == kf and vf in v: + vnf_repos_filtered.append(vnf_repo) + break + vnf_repos = vnf_repos_filtered + return vnf_repos + + def get_pkg(self, pkgtype, name, repo, filter, version): + """ + Returns the filename of the PKG downloaded to disk + """ + self._logger.debug("") + self._client.get_token() + f = None + f_name = None + # Get OSM registered repository list + pkgs = self.pkg_list(pkgtype, filter, repo) + for pkg in pkgs: + if pkg.get('repository') == repo and pkg.get('name') == name: + if 'latest' in version: + if not pkg.get('latest'): + continue + else: + version = pkg.get('version') + if pkg.get('version') == version: + r = requests.get('{}{}'.format(pkg.get('repourl'), pkg.get('location')), stream=True) + if r.status_code != 200: + raise ClientException("Package not found") + + with tempfile.NamedTemporaryFile(delete=False) as f: + f.write(r.raw.read()) + f_name = f.name + if not f_name: + raise ClientException("{} {} not found at repo {}".format(pkgtype,name, repo)) + return f_name + + def pkg_get(self, pkgtype, name, repo, version, filter): + + pkg_name = self.get_pkg(pkgtype, name, repo, filter, version) + if not pkg_name: + raise ClientException('Package not found') + folder, descriptor = self.zip_extraction(pkg_name) + with open(descriptor) as pkg: + pkg_descriptor = yaml.safe_load(pkg) + rmtree(folder, ignore_errors=False) + if ((pkgtype == 'vnf' and (pkg_descriptor.get('vnfd') or pkg_descriptor.get('vnfd:vnfd_catalog'))) or + (pkgtype == 'ns' and (pkg_descriptor.get('nsd') or pkg_descriptor.get('nsd:nsd_catalog')))): + raise ClientException('Wrong Package type') + return pkg_descriptor + + def repo_index(self, origin=".", destination='.'): + """ + Repo Index main function + :param origin: origin directory for getting all the artifacts + :param destination: destination folder for create and index the valid artifacts + """ + if destination == '.': + if origin == destination: + destination = 'repository' + + destination = abspath(destination) + origin = abspath(origin) + + if origin[0] != '/': + origin = join(getcwd(), origin) + if destination[0] != '/': + destination = join(getcwd(), destination) + + self.init_directory(destination) + artifacts = [f for f in listdir(origin) if isfile(join(origin, f))] + directories = [f for f in listdir(origin) if isdir(join(origin, f))] + for artifact in artifacts: + self.register_artifact_in_repository(join(origin, artifact), destination, source='file') + for artifact in directories: + self.register_artifact_in_repository(join(origin, artifact), destination, source='directory') + print("\nFinal Results: ") + print("VNF Packages Indexed: " + str(len(glob.glob(destination + "/vnf/*/*/metadata.yaml")))) + print("NS Packages Indexed: " + str(len(glob.glob(destination + "/ns/*/*/metadata.yaml")))) + + def md5(self, fname): + """ + Checksum generator + :param fname: file path + :return: checksum string + """ + hash_md5 = hashlib.md5() + with open(fname, "rb") as f: + for chunk in iter(lambda: f.read(4096), b""): + hash_md5.update(chunk) + return hash_md5.hexdigest() + + def fields_building(self, descriptor_json, file, package_type): + """ + From an artifact descriptor, obtain the fields required for indexing + :param descriptor_json: artifact description + :param file: artifact package + :param package_type: type of artifact (vnf or ns) + :return: fields + """ + fields = {} + base_path = '/' + package_type + '/' + if package_type == "vnf": + if descriptor_json.get('vnfd-catalog', False): + aux_dict = descriptor_json.get('vnfd-catalog', {}).get('vnfd', [{}])[0] + else: + aux_dict = descriptor_json.get('vnfd:vnfd-catalog', {}).get('vnfd', [{}])[0] + + images = [] + for vdu in aux_dict.get('vdu', ()): + images.append(vdu.get('image')) + fields['images'] = images + if package_type == "ns": + if descriptor_json.get('nsd-catalog', False): + aux_dict = descriptor_json.get('nsd-catalog', {}).get('nsd', [{}])[0] + else: + aux_dict = descriptor_json.get('nsd:nsd-catalog', {}).get('nsd', [{}])[0] + + vnfs = [] + + for vnf in aux_dict.get('constituent-vnfd', ()): + vnfs.append(vnf.get('vnfd-id-ref')) + self._logger.debug('Used VNFS in the NSD: ' + str(vnfs)) + fields['vnfd-id-ref'] = vnfs + + fields['name'] = aux_dict.get('name') + fields['id'] = aux_dict.get('id') + fields['description'] = aux_dict.get('description') + fields['vendor'] = aux_dict.get('vendor') + fields['version'] = aux_dict.get('version', '1.0') + fields['path'] = base_path + fields['id'] + '/' + fields['version'] + '/' + fields.get('id') + "-" + \ + fields.get('version') + '.tar.gz' + return fields + + def zip_extraction(self, file): + """ + Validation of artifact. + :param file: file path + :return: status details, status, fields, package_type + """ + self._logger.debug("Decompressing package file") + temp_file = '/tmp/' + file.split('/')[-1] + if file != temp_file: + copyfile(file, temp_file) + with tarfile.open(temp_file, "r:gz") as tar: + folder = tar.getnames()[0].split('/')[0] + tar.extractall() + + remove(temp_file) + descriptor_file = glob.glob(folder + "/*.y*ml")[0] + return folder, descriptor_file + + def validate_artifact(self, path, source): + """ + Validation of artifact. + :param path: file path + :return: status details, status, fields, package_type + """ + try: + package_type = '' + folder = '' + if source == 'directory': + descriptor_file = glob.glob(path + "/*.y*ml")[0] + else: + folder, descriptor_file = self.zip_extraction(path) + + self._logger.debug("Opening descriptor file: {}".format(descriptor_file)) + + with open(descriptor_file, 'r') as f: + descriptor_data = f.read() + validation = validation_im() + desc_type, descriptor_data = validation.yaml_validation(descriptor_data) + validation_im.pyangbind_validation(self, desc_type, descriptor_data) + if 'vnf' in list(descriptor_data.keys())[0]: + package_type = 'vnf' + else: + # raise ClientException("Not VNF package") + package_type = 'ns' + + self._logger.debug("Descriptor: {}".format(descriptor_data)) + fields = self.fields_building(descriptor_data, path, package_type) + self._logger.debug("Descriptor sucessfully validated") + return {"detail": "{}D successfully validated".format(package_type.upper()), + "code": "OK"}, True, fields, package_type + except Exception as e: + # Delete the folder we just created + return {"detail": str(e)}, False, {}, package_type + finally: + if folder: + rmtree(folder, ignore_errors=True) + + def compress_artifact(self, path): + """ + Compress a directory for building an artifact + :param path: path of the directory + :return: file path + """ + if path[-1] == '/': + path = path[:-1] + file = path + '.tar.gz' + with tarfile.open(file, "w:gz") as tar: + tar.add(path) + + return file + + def register_artifact_in_repository(self, path, destination, source): + """ + Registration of one artifact in a repository + file: VNF or NS + destination: path for index creation + """ + try: + compresed = False + fields = {} + res, valid, fields, package_type = self.validate_artifact(path, source) + if not valid: + raise Exception('{} {} Not well configured.'.format(package_type.upper(), str(path))) + else: + if source == 'directory': + path = self.compress_artifact(path) + compresed = True + fields['checksum'] = self.md5(path) + self.indexation(destination, path, package_type, fields) + + except Exception as e: + self._logger.debug(str(e)) + + finally: + if source == 'directory' and compresed: + remove(path) + + def indexation(self, destination, path, package_type, fields): + """ + Process for index packages + :param destination: index repository path + :param path: path of the package + :param package_type: package type (vnf, ns) + :param fields: dict with the required values + """ + data_ind = {'name': fields.get('name'), 'description': fields.get('description'), + 'vendor': fields.get('vendor'), 'path': fields.get('path')} + + final_path = join(destination, package_type, fields.get('id'), fields.get('version')) + if isdir(join(destination, package_type, fields.get('id'))): + if isdir(final_path): + self._logger.warning('{} {} already exists'.format(package_type.upper(), str(path))) + else: + mkdir(final_path) + copyfile(path, + final_path + '/' + fields.get('id') + "-" + fields.get('version') + '.tar.gz') + yaml.dump(fields, open(final_path + '/' + 'metadata.yaml', 'w'), + Dumper=ruamel.yaml.RoundTripDumper) + index = yaml.load(open(destination + '/index.yaml')) + + index['{}_packages'.format(package_type)][fields.get('id')][fields.get('version')] = data_ind + if versioning.parse(index['{}_packages'.format(package_type)][fields.get('id')][ + 'latest']) < versioning.parse(fields.get('version')): + index['{}_packages'.format(package_type)][fields.get('id')]['latest'] = fields.get( + 'version') + yaml.dump(index, open(destination + '/index.yaml', 'w'), Dumper=ruamel.yaml.RoundTripDumper) + self._logger.info('{} {} added in the repository'.format(package_type.upper(), str(path))) + else: + mkdir(destination + '/{}/'.format(package_type) + fields.get('id')) + mkdir(final_path) + copyfile(path, + final_path + '/' + fields.get('id') + "-" + fields.get('version') + '.tar.gz') + yaml.dump(fields, open(join(final_path, 'metadata.yaml'), 'w'), Dumper=ruamel.yaml.RoundTripDumper) + index = yaml.load(open(destination + '/index.yaml')) + + index['{}_packages'.format(package_type)][fields.get('id')] = {fields.get('version'): data_ind} + index['{}_packages'.format(package_type)][fields.get('id')]['latest'] = fields.get('version') + yaml.dump(index, open(join(destination, 'index.yaml'), 'w'), Dumper=ruamel.yaml.RoundTripDumper) + self._logger.info('{} {} added in the repository'.format(package_type.upper(), str(path))) + + def current_datatime(self): + """ + Datetime Generator + :return: Datetime as string with the following structure "2020-04-29T08:41:07.681653Z" + """ + return time.strftime('%Y-%m-%dT%H:%M:%S.%sZ') + + def init_directory(self, destination): + """ + Initialize the index directory. Creation of index.yaml, and the directories for vnf and ns + :param destination: + :return: + """ + if not isdir(destination): + mkdir(destination) + if not isfile(join(destination, 'index.yaml')): + mkdir(join(destination, 'vnf')) + mkdir(join(destination, 'ns')) + index_data = {'apiVersion': 'v1', 'generated': self.current_datatime(), 'vnf_packages': {}, + 'ns_packages': {}} + with open(join(destination, 'index.yaml'), 'w') as outfile: + yaml.dump(index_data, outfile, default_flow_style=False) diff --git a/requirements.txt b/requirements.txt index 96b6e69..a2a4e5c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,4 +21,7 @@ pycurl python-magic jinja2 verboselogs +packaging +ruamel.yaml +requests git+https://osm.etsi.org/gerrit/osm/IM.git#egg=osm-im diff --git a/setup.py b/setup.py index 1a60771..f5b9e15 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,8 @@ setup( license='Apache 2', install_requires=[ 'Click', 'prettytable', 'pyyaml', 'pycurl', 'python-magic', - 'jinja2', 'osm-im', 'verboselogs' + 'jinja2', 'osm-im', 'verboselogs', 'packaging', 'ruamel.yaml', + 'requests' ], dependency_links=[ 'git+https://osm.etsi.org/gerrit/osm/IM.git#egg=osm-im', -- 2.25.1 From 91f93e9042e27846474213b231f4319d7c08bb40 Mon Sep 17 00:00:00 2001 From: Felipe Vicens Date: Wed, 27 May 2020 20:09:22 +0200 Subject: [PATCH 05/16] Fix netslice-subnet vnf instantiation parameter Change-Id: If9f1a66cf505270c50d73aebf47ea718c7a6da05 Signed-off-by: Felipe Vicens --- osmclient/sol005/nsi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osmclient/sol005/nsi.py b/osmclient/sol005/nsi.py index be1deea..20065b7 100644 --- a/osmclient/sol005/nsi.py +++ b/osmclient/sol005/nsi.py @@ -195,7 +195,7 @@ class Nsi(object): vim_network_name_dict[get_vim_account_id(vim_account)] = vim_net vld["vim-network-name"] = vim_network_name_dict if "vnf" in nssubnet: - for vnf in nsi_config["vnf"]: + for vnf in nssubnet["vnf"]: if vnf.get("vim_account"): vnf["vimAccountId"] = get_vim_account_id(vnf.pop("vim_account")) nsi["netslice-subnet"] = nsi_config["netslice-subnet"] -- 2.25.1 From eb1f0721fc65cedd7771cd5f2960c6faa227d597 Mon Sep 17 00:00:00 2001 From: Felipe Vicens Date: Sun, 31 May 2020 20:13:31 +0200 Subject: [PATCH 06/16] Fix Bug #1088 Zipping packages using osm repo-index Change-Id: I229fb33437c43dcbbe9ba6b7930ffbcf1cb67185 Signed-off-by: Felipe Vicens --- osmclient/sol005/osmrepo.py | 47 ++++++++++++++----------------------- 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/osmclient/sol005/osmrepo.py b/osmclient/sol005/osmrepo.py index 4e37603..b4d49cf 100644 --- a/osmclient/sol005/osmrepo.py +++ b/osmclient/sol005/osmrepo.py @@ -17,6 +17,7 @@ OSM Repo API handling """ from osmclient.common.exceptions import ClientException from osmclient.sol005.repo import Repo +from osmclient.common.package_tool import PackageTool import requests import logging import tempfile @@ -193,7 +194,8 @@ class OSMRepo(Repo): :return: fields """ fields = {} - base_path = '/' + package_type + '/' + base_path = '/{}/'.format(package_type) + aux_dict = {} if package_type == "vnf": if descriptor_json.get('vnfd-catalog', False): aux_dict = descriptor_json.get('vnfd-catalog', {}).get('vnfd', [{}])[0] @@ -222,26 +224,26 @@ class OSMRepo(Repo): fields['description'] = aux_dict.get('description') fields['vendor'] = aux_dict.get('vendor') fields['version'] = aux_dict.get('version', '1.0') - fields['path'] = base_path + fields['id'] + '/' + fields['version'] + '/' + fields.get('id') + "-" + \ - fields.get('version') + '.tar.gz' + fields['path'] = "{}{}/{}/{}-{}.tar.gz".format(base_path, fields['id'], fields['version'], fields.get('id'), \ + fields.get('version')) return fields - def zip_extraction(self, file): + def zip_extraction(self, file_name): """ Validation of artifact. :param file: file path :return: status details, status, fields, package_type """ self._logger.debug("Decompressing package file") - temp_file = '/tmp/' + file.split('/')[-1] - if file != temp_file: - copyfile(file, temp_file) + temp_file = '/tmp/{}'.format(file_name.split('/')[-1]) + if file_name != temp_file: + copyfile(file_name, temp_file) with tarfile.open(temp_file, "r:gz") as tar: folder = tar.getnames()[0].split('/')[0] tar.extractall() remove(temp_file) - descriptor_file = glob.glob(folder + "/*.y*ml")[0] + descriptor_file = glob.glob('{}/*.y*ml'.format(folder))[0] return folder, descriptor_file def validate_artifact(self, path, source): @@ -250,11 +252,11 @@ class OSMRepo(Repo): :param path: file path :return: status details, status, fields, package_type """ + package_type = '' + folder = '' try: - package_type = '' - folder = '' if source == 'directory': - descriptor_file = glob.glob(path + "/*.y*ml")[0] + descriptor_file = glob.glob('{}/*.y*ml'.format(path))[0] else: folder, descriptor_file = self.zip_extraction(path) @@ -283,41 +285,28 @@ class OSMRepo(Repo): if folder: rmtree(folder, ignore_errors=True) - def compress_artifact(self, path): - """ - Compress a directory for building an artifact - :param path: path of the directory - :return: file path - """ - if path[-1] == '/': - path = path[:-1] - file = path + '.tar.gz' - with tarfile.open(file, "w:gz") as tar: - tar.add(path) - - return file - def register_artifact_in_repository(self, path, destination, source): """ Registration of one artifact in a repository file: VNF or NS destination: path for index creation """ + pt = PackageTool() + compresed = False try: - compresed = False fields = {} - res, valid, fields, package_type = self.validate_artifact(path, source) + _, valid, fields, package_type = self.validate_artifact(path, source) if not valid: raise Exception('{} {} Not well configured.'.format(package_type.upper(), str(path))) else: if source == 'directory': - path = self.compress_artifact(path) + path = pt.build(path) compresed = True fields['checksum'] = self.md5(path) self.indexation(destination, path, package_type, fields) except Exception as e: - self._logger.debug(str(e)) + self._logger.debug("Error registering artifact in Repository: {}".format(e)) finally: if source == 'directory' and compresed: -- 2.25.1 From 7340927388ae7e4faad31eaf61e0b7f9f7ed06ce Mon Sep 17 00:00:00 2001 From: tierno Date: Mon, 20 Apr 2020 16:25:41 +0000 Subject: [PATCH 07/16] bug 1085: generate package at package parent folder Change-Id: I9adf7a43107af939b4a9dccef7e47c3061daf49e Signed-off-by: tierno --- osmclient/common/package_tool.py | 24 +++++++++++++----------- osmclient/scripts/osm.py | 4 ++-- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/osmclient/common/package_tool.py b/osmclient/common/package_tool.py index c2e1995..19b1386 100644 --- a/osmclient/common/package_tool.py +++ b/osmclient/common/package_tool.py @@ -370,22 +370,24 @@ class PackageTool(object): with tarfile.open("{}.tar.gz".format(package_name), mode='w:gz') as archive: print("Adding File: {}".format(package_name)) archive.add('{}'.format(package_name), recursive=True) - #return "Created {}.tar.gz".format(package_folder) - #self.build("{}".format(os.path.basename(package_folder))) + # return "Created {}.tar.gz".format(package_folder) + # self.build("{}".format(os.path.basename(package_folder))) os.chdir(cwd) + cwd = None + created_package = "{}/{}.tar.gz".format(os.path.dirname(package_folder) or '.', package_name) + os.rename("{}/{}.tar.gz".format(directory_name, package_name), + created_package) + os.rename("{}/{}/checksums.txt".format(directory_name, package_name), + "{}/checksums.txt".format(package_folder)) + print("Package created: {}".format(created_package)) + return created_package except Exception as exc: + raise ClientException('failure during build of targz file (create temp dir, calculate checksum, ' + 'tar.gz file): {}'.format(exc)) + finally: if cwd: os.chdir(cwd) shutil.rmtree(os.path.join(package_folder, "tmp")) - raise ClientException('failure during build of targz file (create temp dir, calculate checksum, tar.gz file): {}'.format(exc)) - created_package = "{}/{}.tar.gz".format(package_folder, package_name) - os.rename("{}/{}.tar.gz".format(directory_name, package_name), - created_package) - os.rename("{}/{}/checksums.txt".format(directory_name, package_name), - "{}/checksums.txt".format(package_folder)) - shutil.rmtree(os.path.join(package_folder, "tmp")) - print("Package created: {}".format(created_package)) - return created_package def create_temp_dir(self, package_folder, charm_list=None): """ diff --git a/osmclient/scripts/osm.py b/osmclient/scripts/osm.py index 402a09b..0523207 100755 --- a/osmclient/scripts/osm.py +++ b/osmclient/scripts/osm.py @@ -3097,7 +3097,7 @@ def k8scluster_list(ctx, filter, literal): for cluster in resp: table.add_row([cluster['name'], cluster['_id'], cluster['k8s_version'], cluster['vim_account'], json.dumps(cluster['nets']), cluster["_admin"]["operationalState"], - trunc_text(cluster.get('description',''),40)]) + trunc_text(cluster.get('description') or '', 40)]) table.align = 'l' print(table) # except ClientException as e: @@ -3264,7 +3264,7 @@ def repo_list(ctx, filter, literal): table = PrettyTable(['Name', 'Id', 'Type', 'URI', 'Description']) for repo in resp: #cluster['k8s-nets'] = json.dumps(yaml.safe_load(cluster['k8s-nets'])) - table.add_row([repo['name'], repo['_id'], repo['type'], repo['url'], trunc_text(repo.get('description',''),40)]) + table.add_row([repo['name'], repo['_id'], repo['type'], repo['url'], trunc_text(repo.get('description') or '',40)]) table.align = 'l' print(table) -- 2.25.1 From 880fc142421ab5f24f5a297388c3a57f762ffce7 Mon Sep 17 00:00:00 2001 From: tierno Date: Thu, 28 May 2020 10:58:36 +0000 Subject: [PATCH 08/16] adding quotas to project create update Change-Id: I638e0a97bb68fbc87b7608bf38197769cc4bfb8d Signed-off-by: tierno --- osmclient/scripts/osm.py | 43 ++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/osmclient/scripts/osm.py b/osmclient/scripts/osm.py index 0523207..57fb6b3 100755 --- a/osmclient/scripts/osm.py +++ b/osmclient/scripts/osm.py @@ -3317,18 +3317,25 @@ def repo_show(ctx, name, literal): @click.option('--domain-name', 'domain_name', default=None, help='assign to a domain') +@click.option('--quotas', 'quotas', multiple=True, default=None, + help="provide quotas. Can be used several times: 'quota1=number[,quota2=number,...]'. Quotas can be one " + "of vnfds, nsds, nsts, pdus, nsrs, nsis, vim_accounts, wim_accounts, sdns, k8sclusters, k8srepos") @click.pass_context -def project_create(ctx, name, domain_name): +def project_create(ctx, name, domain_name, quotas): """Creates a new project NAME: name of the project DOMAIN_NAME: optional domain name for the project when keystone authentication is used + QUOTAS: set quotas for the project """ logger.debug("") - project = {} - project['name'] = name + project = {'name': name} if domain_name: project['domain_name'] = domain_name + quotas_dict = _process_project_quotas(quotas) + if quotas_dict: + project['quotas'] = quotas_dict + # try: check_client_version(ctx.obj, ctx.command.name) ctx.obj.project.create(name, project) @@ -3337,6 +3344,20 @@ def project_create(ctx, name, domain_name): # exit(1) +def _process_project_quotas(quota_list): + quotas_dict = {} + if not quota_list: + return quotas_dict + try: + for quota in quota_list: + for single_quota in quota.split(","): + k, v = single_quota.split("=") + quotas_dict[k] = None if v in ('None', 'null', '') else int(v) + except (ValueError, TypeError): + raise ClientException("invalid format for 'quotas'. Use 'k1=v1,v1=v2'. v must be a integer or null") + return quotas_dict + + @cli_osm.command(name='project-delete', short_help='deletes a project') @click.argument('name') #@click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions') @@ -3400,23 +3421,29 @@ def project_show(ctx, name): @cli_osm.command(name='project-update', short_help='updates a project (only the name can be updated)') @click.argument('project') -@click.option('--name', - prompt=True, +@click.option('--name', default=None, help='new name for the project') - +@click.option('--quotas', 'quotas', multiple=True, default=None, + help="change quotas. Can be used several times: 'quota1=number|empty[,quota2=...]' " + "(use empty to reset quota to default") @click.pass_context -def project_update(ctx, project, name): +def project_update(ctx, project, name, quotas): """ Update a project name :param ctx: :param project: id or name of the project to modify :param name: new name for the project + :param quotas: change quotas of the project :return: """ logger.debug("") project_changes = {} - project_changes['name'] = name + if name: + project_changes['name'] = name + quotas_dict = _process_project_quotas(quotas) + if quotas_dict: + project_changes['quotas'] = quotas_dict # try: check_client_version(ctx.obj, ctx.command.name) -- 2.25.1 From 4fad38a5dcd272da5e5381dd9e7c49cfd8bd9be7 Mon Sep 17 00:00:00 2001 From: garciadeblas Date: Fri, 29 May 2020 10:21:04 +0000 Subject: [PATCH 09/16] osm.py: safe_dump with indent 4 and default_flow_style False Change-Id: Ia8b28c47d507f07fa1cdf8ba7306640a37845b87 Signed-off-by: garciadeblas --- README.md | 4 ++-- osmclient/scripts/osm.py | 28 ++++++++++++++-------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 1791594..be8b0d2 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ from osmclient.common.exceptions import ClientException hostname = "127.0.0.1" myclient = client.Client(host=hostname, sol005=True) resp = myclient.nsd.list() -print yaml.safe_dump(resp) +print yaml.safe_dump(resp, indent=4, default_flow_style=False) ``` ### Simple Python code to get the list of VNF packages from a specific user and project @@ -151,7 +151,7 @@ if project is not None: kwargs['project']=project myclient = client.Client(host=hostname, sol005=True, **kwargs) resp = myclient.vnfd.list() -print yaml.safe_dump(resp) +print yaml.safe_dump(resp, indent=4, default_flow_style=False) ``` ## Enable autocompletion diff --git a/osmclient/scripts/osm.py b/osmclient/scripts/osm.py index 57fb6b3..8dbf2b6 100755 --- a/osmclient/scripts/osm.py +++ b/osmclient/scripts/osm.py @@ -290,7 +290,7 @@ def ns_list(ctx, filter, long): if ee['elementType'] not in status_ee: status_ee[ee['elementType']] = {} status_ee[ee['elementType']][ee['status']] = 1 - continue; + continue if ee['status'] in status_ee[ee['elementType']]: status_ee[ee['elementType']][ee['status']] += 1 else: @@ -992,7 +992,7 @@ def nsd_show(ctx, name, literal): # exit(1) if literal: - print(yaml.safe_dump(resp)) + print(yaml.safe_dump(resp, indent=4, default_flow_style=False)) return table = PrettyTable(['field', 'value']) @@ -1040,7 +1040,7 @@ def vnfd_show(ctx, name, literal): # exit(1) if literal: - print(yaml.safe_dump(resp)) + print(yaml.safe_dump(resp, indent=4, default_flow_style=False)) return table = PrettyTable(['field', 'value']) @@ -1056,7 +1056,7 @@ def pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal): resp = ctx.obj.osmrepo.pkg_get(pkgtype, name, repo, version, filter) if literal: - print(yaml.safe_dump(resp)) + print(yaml.safe_dump(resp, indent=4, default_flow_style=False)) return pkgtype += 'd' catalog = pkgtype + '-catalog' @@ -1218,7 +1218,7 @@ def ns_show(ctx, name, literal, filter): # exit(1) if literal: - print(yaml.safe_dump(ns)) + print(yaml.safe_dump(ns, indent=4, default_flow_style=False)) return table = PrettyTable(['field', 'value']) @@ -1307,7 +1307,7 @@ def vnf_show(ctx, name, literal, filter, kdu): print ("Could not determine KDU status") if literal: - print(yaml.safe_dump(resp)) + print(yaml.safe_dump(resp, indent=4, default_flow_style=False)) return table = PrettyTable(['field', 'value']) @@ -1388,7 +1388,7 @@ def ns_op_show(ctx, id, filter, literal): # exit(1) if literal: - print(yaml.safe_dump(op_info)) + print(yaml.safe_dump(op_info, indent=4, default_flow_style=False)) return table = PrettyTable(['field', 'value']) @@ -1410,7 +1410,7 @@ def nst_show(ctx, name, literal): # exit(1) if literal: - print(yaml.safe_dump(resp)) + print(yaml.safe_dump(resp, indent=4, default_flow_style=False)) return table = PrettyTable(['field', 'value']) @@ -1458,7 +1458,7 @@ def nsi_show(ctx, name, literal, filter): # exit(1) if literal: - print(yaml.safe_dump(nsi)) + print(yaml.safe_dump(nsi, indent=4, default_flow_style=False)) return table = PrettyTable(['field', 'value']) @@ -1564,7 +1564,7 @@ def pdu_show(ctx, name, literal, filter): # exit(1) if literal: - print(yaml.safe_dump(pdu)) + print(yaml.safe_dump(pdu, indent=4, default_flow_style=False)) return table = PrettyTable(['field', 'value']) @@ -3091,7 +3091,7 @@ def k8scluster_list(ctx, filter, literal): check_client_version(ctx.obj, ctx.command.name) resp = ctx.obj.k8scluster.list(filter) if literal: - print(yaml.safe_dump(resp)) + print(yaml.safe_dump(resp, indent=4, default_flow_style=False)) return table = PrettyTable(['Name', 'Id', 'Version', 'VIM', 'K8s-nets', 'Operational State', 'Description']) for cluster in resp: @@ -3118,7 +3118,7 @@ def k8scluster_show(ctx, name, literal): # try: resp = ctx.obj.k8scluster.get(name) if literal: - print(yaml.safe_dump(resp)) + print(yaml.safe_dump(resp, indent=4, default_flow_style=False)) return table = PrettyTable(['key', 'attribute']) for k, v in list(resp.items()): @@ -3259,7 +3259,7 @@ def repo_list(ctx, filter, literal): resp = ctx.obj.repo.list(filter) resp += ctx.obj.osmrepo.list(filter) if literal: - print(yaml.safe_dump(resp)) + print(yaml.safe_dump(resp, indent=4, default_flow_style=False)) return table = PrettyTable(['Name', 'Id', 'Type', 'URI', 'Description']) for repo in resp: @@ -3290,7 +3290,7 @@ def repo_show(ctx, name, literal): if literal: if resp: - print(yaml.safe_dump(resp)) + print(yaml.safe_dump(resp, indent=4, default_flow_style=False)) return table = PrettyTable(['key', 'attribute']) if resp: -- 2.25.1 From 39b4a9c0594537a190fbdbb5c00bbc82e387bff8 Mon Sep 17 00:00:00 2001 From: garciadeblas Date: Fri, 5 Jun 2020 09:17:50 +0000 Subject: [PATCH 11/16] Updated short help for some commands Change-Id: Iedcfb93e879996372258be4bbd6302cc79fc4dd6 Signed-off-by: garciadeblas --- osmclient/scripts/osm.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osmclient/scripts/osm.py b/osmclient/scripts/osm.py index 8dbf2b6..78f7896 100755 --- a/osmclient/scripts/osm.py +++ b/osmclient/scripts/osm.py @@ -1002,7 +1002,7 @@ def nsd_show(ctx, name, literal): print(table) -@cli_osm.command(name='nsd-show', short_help='shows the content of a NSD') +@cli_osm.command(name='nsd-show', short_help='shows the details of a NS package') @click.option('--literal', is_flag=True, help='print literally, no pretty table') @click.argument('name') @@ -1016,7 +1016,7 @@ def nsd_show1(ctx, name, literal): nsd_show(ctx, name, literal) -@cli_osm.command(name='nspkg-show', short_help='shows the content of a NSD') +@cli_osm.command(name='nspkg-show', short_help='shows the details of a NS package') @click.option('--literal', is_flag=True, help='print literally, no pretty table') @click.argument('name') @@ -1072,7 +1072,7 @@ def pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal): table.align = 'l' print(table) -@cli_osm.command(name='vnfd-show', short_help='shows the content of a VNFD') +@cli_osm.command(name='vnfd-show', short_help='shows the details of a NF package') @click.option('--literal', is_flag=True, help='print literally, no pretty table') @click.argument('name') @@ -1086,7 +1086,7 @@ def vnfd_show1(ctx, name, literal): vnfd_show(ctx, name, literal) -@cli_osm.command(name='vnfpkg-show', short_help='shows the content of a VNFD') +@cli_osm.command(name='vnfpkg-show', short_help='shows the details of a NF package') @click.option('--literal', is_flag=True, help='print literally, no pretty table') @click.argument('name') @@ -1099,7 +1099,7 @@ def vnfd_show2(ctx, name, literal): logger.debug("") vnfd_show(ctx, name, literal) -@cli_osm.command(name='vnfpkg-repo-show', short_help='shows the content of a VNFD') +@cli_osm.command(name='vnfpkg-repo-show', short_help='shows the details of a NF package in an OSM repository') @click.option('--literal', is_flag=True, help='print literally, no pretty table') @click.option('--repo', @@ -1121,7 +1121,7 @@ def vnfd_show3(ctx, name, repo, version, literal=None, filter=None): pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal) -@cli_osm.command(name='nsd-repo-show', short_help='shows the content of a NSD') +@cli_osm.command(name='nsd-repo-show', short_help='shows the details of a NS package in an OSM repository') @click.option('--literal', is_flag=True, help='print literally, no pretty table') @click.option('--repo', @@ -1142,7 +1142,7 @@ def nsd_repo_show(ctx, name, repo, version, literal=None, filter=None): pkgtype = 'ns' pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal) -@cli_osm.command(name='nspkg-repo-show', short_help='shows the content of a NSD') +@cli_osm.command(name='nspkg-repo-show', short_help='shows the details of a NS package in an OSM repository') @click.option('--literal', is_flag=True, help='print literally, no pretty table') @click.option('--repo', @@ -1163,7 +1163,7 @@ def nsd_repo_show2(ctx, name, repo, version, literal=None, filter=None): pkgtype = 'ns' pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal) -@cli_osm.command(name='nfpkg-show', short_help='shows the content of a NF Descriptor') +@cli_osm.command(name='nfpkg-show', short_help='shows the details of a NF package') @click.option('--literal', is_flag=True, help='print literally, no pretty table') @click.argument('name') @@ -1177,7 +1177,7 @@ def nfpkg_show(ctx, name, literal): vnfd_show(ctx, name, literal) -@cli_osm.command(name='nfpkg-repo-show', short_help='shows the content of a VNFD') +@cli_osm.command(name='nfpkg-repo-show', short_help='shows the details of a NF package in an OSM repository') @click.option('--literal', is_flag=True, help='print literally, no pretty table') @click.option('--repo', -- 2.25.1 From c706f69a072bef2b6e68d82fa1917d79ee2ee867 Mon Sep 17 00:00:00 2001 From: garciadeblas Date: Fri, 29 May 2020 15:00:49 +0000 Subject: [PATCH 12/16] Minor fix in error_details, only shown for BROKEN or DEGRADED or old client versions Change-Id: I49b382204b41a7b7dc36753c351deaaf520d2a05 Signed-off-by: garciadeblas --- osmclient/scripts/osm.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osmclient/scripts/osm.py b/osmclient/scripts/osm.py index 78f7896..a369c48 100755 --- a/osmclient/scripts/osm.py +++ b/osmclient/scripts/osm.py @@ -368,7 +368,8 @@ def ns_list(ctx, filter, long): else: current_operation = "{} ({})".format(nsr['_admin'].get('current-operation','-'), nsr['_admin']['nslcmop']) error_details = "N/A" - if ns_state == "BROKEN" or ns_state == "DEGRADED" or nsr.get('errorDescription'): + if ns_state == "BROKEN" or ns_state == "DEGRADED" or \ + ('currentOperation' not in nsr and nsr.get('errorDescription')): error_details = "{}\nDetail: {}".format(nsr['errorDescription'], nsr['errorDetail']) else: nsopdata = ctx.obj.ns.get_opdata(ns['id']) -- 2.25.1 From 4c5a703b30e32b97f739ed2bbb33993ddc61c5d0 Mon Sep 17 00:00:00 2001 From: garciadeblas Date: Mon, 1 Jun 2020 13:53:55 +0000 Subject: [PATCH 13/16] osm.py: renamed internal functions for pkg_repo_list Change-Id: I82c64f44db6356b2aafa4dd17bf87dbeae919448 Signed-off-by: garciadeblas --- osmclient/scripts/osm.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/osmclient/scripts/osm.py b/osmclient/scripts/osm.py index a369c48..f31d5f6 100755 --- a/osmclient/scripts/osm.py +++ b/osmclient/scripts/osm.py @@ -562,18 +562,6 @@ def vnfd_list2(ctx, nf_type, filter, long): logger.debug("") vnfd_list(ctx, nf_type, filter, long) -@cli_osm.command(name='vnfpkg-repo-list', short_help='list all xNF from OSM repositories') -@click.option('--filter', default=None, - help='restricts the list to the NFpkg matching the filter') -@click.option('--repo', default=None, - help='restricts the list to a particular OSM repository') -@click.option('--long', is_flag=True, help='get more details') -@click.pass_context -def vnfd_list3(ctx, filter, repo, long): - """list xNF packages from OSM repositories""" - pkgtype = 'vnf' - pkg_repo_list(ctx, pkgtype, filter, repo, long) - @cli_osm.command(name='nfpkg-list', short_help='list all xNF packages (VNF, HNF, PNF)') @click.option('--nf_type', help='type of NF (vnf, pnf, hnf)') @click.option('--filter', default=None, @@ -590,6 +578,18 @@ def nfpkg_list(ctx, nf_type, filter, long): # print(str(e)) # exit(1) +@cli_osm.command(name='vnfpkg-repo-list', short_help='list all xNF from OSM repositories') +@click.option('--filter', default=None, + help='restricts the list to the NFpkg matching the filter') +@click.option('--repo', default=None, + help='restricts the list to a particular OSM repository') +@click.option('--long', is_flag=True, help='get more details') +@click.pass_context +def nfpkg_repo_list1(ctx, filter, repo, long): + """list xNF packages from OSM repositories""" + pkgtype = 'vnf' + pkg_repo_list(ctx, pkgtype, filter, repo, long) + @cli_osm.command(name='nfpkg-repo-list', short_help='list all xNF from OSM repositories') @click.option('--filter', default=None, help='restricts the list to the NFpkg matching the filter') @@ -597,7 +597,7 @@ def nfpkg_list(ctx, nf_type, filter, long): help='restricts the list to a particular OSM repository') @click.option('--long', is_flag=True, help='get more details') @click.pass_context -def vnfd_list4(ctx, filter, repo, long): +def nfpkg_repo_list2(ctx, filter, repo, long): """list xNF packages from OSM repositories""" pkgtype = 'vnf' pkg_repo_list(ctx, pkgtype, filter, repo, long) @@ -671,7 +671,7 @@ def vnf_list1(ctx, ns, filter, long): help='restricts the list to a particular OSM repository') @click.option('--long', is_flag=True, help='get more details') @click.pass_context -def nsd_list3(ctx, filter, repo, long): +def nspkg_repo_list(ctx, filter, repo, long): """list xNF packages from OSM repositories""" pkgtype = 'ns' pkg_repo_list(ctx, pkgtype, filter, repo, long) @@ -683,7 +683,7 @@ def nsd_list3(ctx, filter, repo, long): help='restricts the list to a particular OSM repository') @click.option('--long', is_flag=True, help='get more details') @click.pass_context -def nspkg_list(ctx, filter, repo, long): +def nspkg_repo_list2(ctx, filter, repo, long): """list xNF packages from OSM repositories""" pkgtype = 'ns' pkg_repo_list(ctx, pkgtype, filter, repo, long) -- 2.25.1 From 7b7b5cdd7856dd11f1c5fb8051d90d684ae9dfbe Mon Sep 17 00:00:00 2001 From: tierno Date: Wed, 17 Jun 2020 12:56:12 +0000 Subject: [PATCH 14/16] show queue status for ns-op-list Change-Id: I659f78e3ffaff515c6b719ae9cdfcb8d44640fdc Signed-off-by: tierno --- osmclient/scripts/osm.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osmclient/scripts/osm.py b/osmclient/scripts/osm.py index f31d5f6..e3b5ac6 100755 --- a/osmclient/scripts/osm.py +++ b/osmclient/scripts/osm.py @@ -787,12 +787,12 @@ def ns_op_list(ctx, name, long): if op['lcmOperationType']=='action': action_name = op['operationParams']['primitive'] detail = "-" - if op['operationState']=='PROCESSING': - if op['lcmOperationType'] in ('instantiate', 'terminate'): + if op['operationState'] == 'PROCESSING': + if op['queuePosition'] is not None and op['queuePosition'] > 0: + detail = "In queue. Current position: {}".format(op['queuePosition']) + elif op['lcmOperationType'] in ('instantiate', 'terminate'): if op['stage']: detail = op['stage'] - else: - detail = "In queue. Current position: {}".format(op['queuePosition']) elif op['operationState'] in ('FAILED', 'FAILED_TEMP'): detail = op.get('errorMessage','-') date = datetime.fromtimestamp(op['startTime']).strftime("%Y-%m-%dT%H:%M:%S") -- 2.25.1 From 9342c58dce81a00546a3045d9069b15f9abd74c1 Mon Sep 17 00:00:00 2001 From: garciadeblas Date: Mon, 20 Jul 2020 11:27:17 +0000 Subject: [PATCH 15/16] Fix bug 1162: return without printing VNF record when KDU status could not be determined Change-Id: I448b7bed88a36c2df2085780054b73210bccfaab Signed-off-by: garciadeblas --- osmclient/scripts/osm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/osmclient/scripts/osm.py b/osmclient/scripts/osm.py index e3b5ac6..af20edf 100755 --- a/osmclient/scripts/osm.py +++ b/osmclient/scripts/osm.py @@ -1306,6 +1306,7 @@ def vnf_show(ctx, name, literal, filter, kdu): time.sleep(5) t += 5 print ("Could not determine KDU status") + return if literal: print(yaml.safe_dump(resp, indent=4, default_flow_style=False)) -- 2.25.1 From d27284799a0a3f7ecd899faf9a6896bddba2a99f Mon Sep 17 00:00:00 2001 From: garciadeblas Date: Mon, 20 Jul 2020 07:23:53 +0000 Subject: [PATCH 16/16] Fix bug 1159: vdu can be empty in a KNF, override option must take it into account Change-Id: I1947d65ab55af825f7aaa90b4fbf2f6e8f5a7a4b Signed-off-by: garciadeblas --- osmclient/sol005/vnfd.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osmclient/sol005/vnfd.py b/osmclient/sol005/vnfd.py index 48fbdb7..ac44495 100644 --- a/osmclient/sol005/vnfd.py +++ b/osmclient/sol005/vnfd.py @@ -204,7 +204,8 @@ class Vnfd(object): for k in vnfd: # Get only the first descriptor in case there are many in the yaml file # k can be vnfd:vnfd-catalog or vnfd-catalog. This check is skipped - vdu_list = vnfd[k]['vnfd'][0]['vdu'] + first_vnfd = vnfd[k]['vnfd'][0] + vdu_list = first_vnfd.get('vdu',[]) break; for vdu_number, vdu in enumerate(vdu_list): if override_epa: -- 2.25.1