From 6215857a5a88907c2ba522b862992815e47f4960 Mon Sep 17 00:00:00 2001 From: hongyuzhao Date: Tue, 18 Feb 2020 16:11:50 +0800 Subject: [PATCH] additionalArtifacts is not implemented in the response of the Query VNF API Change-Id: I62e3338b038eec1a318841521c3f2dcd1c05ee5e Issue-ID: MODELING-312 Signed-off-by: hongyuzhao --- catalog/packages/biz/vnf_package.py | 52 ++++++++++++++++++++++++++++- catalog/packages/tests/test_vnf_package.py | 36 ++++++++++++++++++++ catalog/pub/utils/fileutil.py | 35 +++++++++++++++++++ static/catalog/vgw.csar | Bin 0 -> 24116 bytes 4 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 static/catalog/vgw.csar diff --git a/catalog/packages/biz/vnf_package.py b/catalog/packages/biz/vnf_package.py index daf2fb2..6dec204 100644 --- a/catalog/packages/biz/vnf_package.py +++ b/catalog/packages/biz/vnf_package.py @@ -229,6 +229,56 @@ class VnfPkgUploadThread(threading.Thread): logger.info('VNF packge(%s) has been uploaded.' % self.vnf_pkg_id) +def get_mfile_data(path): + logger.debug('get_mfile_data path %s' % path) + files = fileutil.filter_files(path, '.mf') + if files: + src_file = os.path.join(path, files[0]) + src_dict_list = [] + with open(src_file, 'r') as f: + data = f.readlines() + for line in data: + if line.strip() == "": + continue + src_dict = {} + k, v = line.split(':', maxsplit=1) + if k.strip() in ["Source", "Algorithm", "Hash"]: + if k.strip() == "Source" and src_dict: + src_dict_list.extend(src_dict) + src_dict = {} + src_dict[k.strip()] = v.strip() + print("src_dict:%s" % src_dict) + if src_dict: + src_dict_list.append(src_dict) + + logger.debug('get_mfile_data: %s' % src_dict_list) + return src_dict_list + + +def fill_artifacts_data(vnf_pkg_id): + vnf_pkg_path = os.path.join(CATALOG_ROOT_PATH, vnf_pkg_id) + if os.path.exists(vnf_pkg_path) is False: + return None + files = fileutil.filter_files(vnf_pkg_path, '.csar') + for filename in files: + logger.info('fill_artifacts_data filename (%s)...' % filename) + dst_file_path = os.path.join(vnf_pkg_path, "tmp") + src_file = os.path.join(vnf_pkg_path, filename) + dst_file = os.path.join(dst_file_path, filename) + fileutil.recreate_dir(dst_file_path) + fileutil.copy(src_file, vnf_pkg_path, dst_file) + artifact_vnf_file = fileutil.unzip_file(dst_file, dst_file_path, "") + artifacts = get_mfile_data(artifact_vnf_file) + if artifacts: + return [{ + "artifactPath": artifact.get("Source", ""), + "checksum": { + "algorithm": artifact.get("Hash", "Null"), + "hash": artifact.get("Algorithm", "Null") + } + } for artifact in artifacts] + + def fill_response_data(nf_pkg): pkg_info = {} pkg_info["id"] = nf_pkg.vnfPackageId @@ -239,7 +289,7 @@ def fill_response_data(nf_pkg): if nf_pkg.checksum: pkg_info["checksum"] = json.JSONDecoder().decode(nf_pkg.checksum) pkg_info["softwareImages"] = None # TODO - pkg_info["additionalArtifacts"] = None # TODO + pkg_info["additionalArtifacts"] = fill_artifacts_data(nf_pkg.vnfPackageId) pkg_info["onboardingState"] = nf_pkg.onboardingState pkg_info["operationalState"] = nf_pkg.operationalState pkg_info["usageState"] = nf_pkg.usageState diff --git a/catalog/packages/tests/test_vnf_package.py b/catalog/packages/tests/test_vnf_package.py index 5422361..8deb9ec 100644 --- a/catalog/packages/tests/test_vnf_package.py +++ b/catalog/packages/tests/test_vnf_package.py @@ -423,3 +423,39 @@ class TestVnfPackage(TestCase): self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) response = self.client.get(VNF_BASE_URL + "/222/artifacts/image1") self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_upload_vnf_pkg_with_artifacts(self): + data = {'file': open(os.path.join(CATALOG_ROOT_PATH, "vgw.csar"), "rb")} + VnfPackageModel.objects.create( + vnfPackageId="222", + onboardingState="CREATED" + ) + response = self.client.put("%s/222/package_content" % VNF_BASE_URL, data=data) + vnf_pkg = VnfPackageModel.objects.filter(vnfPackageId="222") + self.assertEqual(PKG_STATUS.ONBOARDED, vnf_pkg[0].onboardingState) + self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) + response = self.client.get("%s/222" % VNF_BASE_URL) + print(response.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + expact_response_data = { + "id": "222", + "vnfdId": "b1bb0ce7-2222-4fa7-95ed-4840d70a1177", + "vnfProductName": "vcpe_vgw", + "vnfSoftwareVersion": "1.0", + "vnfdVersion": "1.0", + "softwareImages": None, + "additionalArtifacts": [ + { + "artifactPath": "MainServiceTemplate.yaml", + "checksum": { + "algorithm": "Null", + "hash": "Null" + } + } + ], + "onboardingState": "ONBOARDED", + "operationalState": "ENABLED", + "usageState": "NOT_IN_USE", + "_links": None + } + self.assertEqual(response.data, expact_response_data) diff --git a/catalog/pub/utils/fileutil.py b/catalog/pub/utils/fileutil.py index 9344f72..e6eb6f2 100644 --- a/catalog/pub/utils/fileutil.py +++ b/catalog/pub/utils/fileutil.py @@ -54,7 +54,9 @@ def download_file_from_http(url, local_dir, file_name): def unzip_file(zip_src, dst_dir, csar_path): + logger.debug("unzip_file %s to %s.", zip_src, dst_dir) if os.path.exists(zip_src): + logger.debug("unzip_file %s.", zip_src) fz = zipfile.ZipFile(zip_src, 'r') for file in fz.namelist(): fz.extract(file, dst_dir) @@ -86,3 +88,36 @@ def get_artifact_path(vnf_path, artifact_file): if artifact_file in files: return os.path.join(root, artifact_file) return None + + +def end_with(_s_in, *suffix): + array = map(_s_in.endswith, suffix) + if True in array: + return True + return False + + +def filter_files(search_path, suffix): + f_find = [] + file_list = os.listdir(search_path) + for file_item in file_list: + if end_with(file_item, suffix): + f_find.append(file_item) + return f_find + + +def recreate_dir(path): + if os.path.exists(path): + shutil.rmtree(path) + os.makedirs(path, mode=0o777) + + +def copy(src_file, dest_dir, new_file_name=None): + if not os.path.exists(dest_dir): + os.makedirs(dest_dir) + if new_file_name is None: + dst = os.path.join(dest_dir, os.path.basename(src_file)) + else: + dst = os.path.join(dest_dir, new_file_name) + shutil.copyfile(src_file, dst) + shutil.copymode(src_file, dst) diff --git a/static/catalog/vgw.csar b/static/catalog/vgw.csar new file mode 100644 index 0000000000000000000000000000000000000000..927812a7635c919a8cdf280adeb7eab846635967 GIT binary patch literal 24116 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 H2lww^dkU52 literal 0 HcmV?d00001 -- 2.16.6