From 2f4d7a5b1d2a629e5856a5e3e17576bc618452f0 Mon Sep 17 00:00:00 2001 From: dyh Date: Fri, 15 Nov 2019 09:26:18 +0800 Subject: [PATCH] Implement read VNFD API Issue-ID: MODELING-266 Change-Id: Ib5e4a965c080329f68d407b414a347298da6a84b Signed-off-by: dyh --- catalog/packages/biz/sdc_vnf_package.py | 35 +++- catalog/packages/biz/vnf_package.py | 60 ++++++- catalog/packages/tests/test_vnf_package.py | 246 ++++++++++++++++------------ catalog/packages/urls.py | 1 + catalog/packages/views/vnf_package_views.py | 24 +++ catalog/pub/config/config.py | 17 +- docs/index.rst | 3 +- docs/introduction.rst | 5 +- static/catalog/resource_test.csar | Bin 227 -> 1962 bytes 9 files changed, 278 insertions(+), 113 deletions(-) diff --git a/catalog/packages/biz/sdc_vnf_package.py b/catalog/packages/biz/sdc_vnf_package.py index e5be4a1..2160a00 100644 --- a/catalog/packages/biz/sdc_vnf_package.py +++ b/catalog/packages/biz/sdc_vnf_package.py @@ -18,7 +18,9 @@ import os import sys import threading import traceback +import zipfile +from catalog.packages.biz.vnf_package import VnfPackage from catalog.packages.const import PKG_STATUS from catalog.pub.config.config import CATALOG_ROOT_PATH, CATALOG_URL_PATH from catalog.pub.config.config import REG_TO_MSB_REG_PARAM @@ -123,7 +125,7 @@ class NfDistributeThread(threading.Thread): local_file_name = sdc.download_artifacts(artifact["toscaModelURL"], local_path, csar_name) if local_file_name.endswith(".csar") or local_file_name.endswith(".zip"): fileutil.unzip_csar(local_file_name, local_path) - vendor_vnf_file = "" + vendor_vnf_file = '' # find original vendor ETSI package under the ONBOARDING_PACKAGE directory onboarding_package_dir = os.path.join(local_path, "Artifacts/Deployment/ONBOARDED_PACKAGE") if os.path.exists(onboarding_package_dir): @@ -135,13 +137,16 @@ class NfDistributeThread(threading.Thread): break # find original vendor ETSI package under Artifacts/Deployment/OTHER directory - if vendor_vnf_file.isspace(): + if vendor_vnf_file.strip() == '': vendor_vnf_file = os.path.join(local_path, "Artifacts/Deployment/OTHER/vnf.csar") if os.path.exists(vendor_vnf_file): local_file_name = vendor_vnf_file else: local_file_name = vendor_vnf_file + # create VNFD zip file + self.create_vnfd_zip(self.csar_id, vendor_vnf_file) + vnfd_json = toscaparser.parse_vnfd(local_file_name) vnfd = json.JSONDecoder().decode(vnfd_json) @@ -173,6 +178,32 @@ class NfDistributeThread(threading.Thread): ).save() JobUtil.add_job_status(self.job_id, 100, "CSAR(%s) distribute successfully." % self.csar_id) + def create_vnfd_zip(self, csar_id, vendor_vnf_file): + """ + Create VNFD zip file. + :param csar_id: CSAR Id + :param vendor_vnf_file: the vendor original package(csar) + :return: + """ + if os.path.exists(vendor_vnf_file): + # create VNFD from vendor original package + VnfPackage().creat_vnfd(csar_id, vendor_vnf_file) + else: + try: + vnf_package_path = os.path.join(CATALOG_ROOT_PATH, self.csar_id) + vnfd_zip_file = os.path.join(vnf_package_path, 'VNFD.zip') + with zipfile.ZipFile(vnfd_zip_file, 'w', zipfile.ZIP_DEFLATED) as vnfd_zip: + def_path = os.path.join(vnf_package_path, "Definitions") + if os.path.exists(def_path): + def_files = os.listdir(def_path) + for def_file in def_files: + full_path = os.path.join(def_path, def_file) + vnfd_zip.write(full_path, def_file) + except Exception as e: + logger.error(e) + if os.path.exists(vnfd_zip_file): + os.remove(vnfd_zip_file) + def rollback_distribute(self): try: VnfPackageModel.objects.filter(vnfPackageId=self.csar_id).delete() diff --git a/catalog/packages/biz/vnf_package.py b/catalog/packages/biz/vnf_package.py index 21d5819..5a51e9a 100644 --- a/catalog/packages/biz/vnf_package.py +++ b/catalog/packages/biz/vnf_package.py @@ -20,16 +20,16 @@ import threading import traceback import urllib import uuid +import zipfile +from catalog.packages import const from catalog.packages.biz.common import parse_file_range, read, save +from catalog.packages.biz.notificationsutil import prepare_vnfpkg_notification, NotificationsUtil from catalog.pub.config.config import CATALOG_ROOT_PATH from catalog.pub.database.models import VnfPackageModel, NSPackageModel from catalog.pub.exceptions import CatalogException, ResourceNotFoundException -from catalog.pub.utils.values import ignore_case_get from catalog.pub.utils import fileutil, toscaparser -from catalog.packages import const -from catalog.packages.biz.notificationsutil import prepare_vnfpkg_notification, NotificationsUtil - +from catalog.pub.utils.values import ignore_case_get logger = logging.getLogger(__name__) @@ -132,6 +132,58 @@ class VnfPackage(object): logger.info('VNF package (%s) has been downloaded.' % vnf_pkg_id) return read(local_file_path, start, end) + def download_vnfd(self, vnf_pkg_id): + logger.info('Start to download VNFD of VNF package(%s)...' % vnf_pkg_id) + nf_pkg = VnfPackageModel.objects.filter(vnfPackageId=vnf_pkg_id) + if not nf_pkg.exists(): + logger.error('VNF package(%s) does not exist.' % vnf_pkg_id) + raise ResourceNotFoundException('VNF package(%s) does not exist.' % vnf_pkg_id) + if nf_pkg[0].onboardingState != const.PKG_STATUS.ONBOARDED: + raise CatalogException("VNF package (%s) is not on-boarded" % vnf_pkg_id) + + vnfd_zip_file = self.creat_vnfd(vnf_pkg_id, nf_pkg[0].localFilePath) + logger.info('VNFD of VNF package (%s) has been downloaded.' % vnf_pkg_id) + return read(vnfd_zip_file, 0, os.path.getsize(vnfd_zip_file)) + + def creat_vnfd(self, vnf_pkg_id, vendor_pkg_file): + """ + Create VNFD zip file from vendor original package + :param self: + :param vnf_pkg_id: VNF package id (CSAR id) + :param vendor_pkg_file: vendor original package + :return: + """ + vnf_package_path = os.path.join(CATALOG_ROOT_PATH, vnf_pkg_id) + if not os.path.exists(vnf_package_path): + os.makedirs(vnf_package_path) + vnfd_zip_file = os.path.join(vnf_package_path, "VNFD.zip") + if os.path.exists(vnfd_zip_file): + return vnfd_zip_file + else: + if vendor_pkg_file.endswith(".csar") or vendor_pkg_file.endswith(".zip"): + try: + vnfd_path = os.path.join(vnf_package_path, "vnfd") + with zipfile.ZipFile(vendor_pkg_file, 'r') as vendor_zip: + vender_files = vendor_zip.namelist() + for vender_file in vender_files: + if str(vender_file).startswith("Definitions"): + vendor_zip.extract(vender_file, vnfd_path) + with zipfile.ZipFile(vnfd_zip_file, 'w', zipfile.ZIP_DEFLATED) as vnfd_zip: + def_path = os.path.join(vnfd_path, "Definitions") + if os.path.exists(def_path): + def_files = os.listdir(def_path) + for def_file in def_files: + full_path = os.path.join(def_path, def_file) + vnfd_zip.write(full_path, def_file) + return vnfd_zip_file + except Exception as e: + logger.error(e) + if os.path.exists(vnfd_zip): + os.remove(vnfd_zip) + raise e + finally: + fileutil.delete_dirs(vnfd_path) + class VnfPkgUploadThread(threading.Thread): def __init__(self, data, vnf_pkg_id): diff --git a/catalog/packages/tests/test_vnf_package.py b/catalog/packages/tests/test_vnf_package.py index b83268a..c0d6637 100644 --- a/catalog/packages/tests/test_vnf_package.py +++ b/catalog/packages/tests/test_vnf_package.py @@ -14,10 +14,11 @@ import json import os -import urllib -import mock import shutil +import urllib +import zipfile +import mock from django.test import TestCase from rest_framework import status from rest_framework.test import APIClient @@ -29,6 +30,8 @@ from catalog.pub.config.config import CATALOG_ROOT_PATH from catalog.pub.database.models import VnfPackageModel from catalog.pub.utils import toscaparser +VNF_BASE_URL = "/api/vnfpkgm/v1/vnf_packages" + class MockReq(): def read(self): @@ -55,7 +58,7 @@ class TestVnfPackage(TestCase): onboardingState="CREATED" ) mock_parse_vnfd.return_value = json.JSONEncoder().encode(vnfd_data) - response = self.client.put("/api/vnfpkgm/v1/vnf_packages/222/package_content", data=data) + response = self.client.put("%s/222/package_content" % VNF_BASE_URL, data=data) vnf_pkg = VnfPackageModel.objects.filter(vnfPackageId="222") self.assertEqual("zte-hss-1.0", vnf_pkg[0].vnfdId) self.assertEqual(PKG_STATUS.ONBOARDED, vnf_pkg[0].onboardingState) @@ -66,7 +69,7 @@ class TestVnfPackage(TestCase): VnfPackageModel.objects.create( vnfPackageId="222", ) - response = self.client.put("/api/vnfpkgm/v1/vnf_packages/222/package_content", data=data) + response = self.client.put("%s/222/package_content" % VNF_BASE_URL, data=data) self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) @mock.patch.object(toscaparser, 'parse_vnfd') @@ -86,14 +89,14 @@ class TestVnfPackage(TestCase): def test_upload_from_uri_failed(self): req_data = {"username": "123"} - response = self.client.post("/api/vnfpkgm/v1/vnf_packages/111/package_content/upload_from_uri", data=req_data) + response = self.client.post("%s/111/package_content/upload_from_uri" % VNF_BASE_URL, data=req_data) self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) def test_create_vnf_pkg(self): req_data = { "userDefinedData": {"a": "A"} } - response = self.client.post("/api/vnfpkgm/v1/vnf_packages", data=req_data, format="json") + response = self.client.post(VNF_BASE_URL, data=req_data, format="json") resp_data = json.loads(response.content) expect_resp_data = { "id": resp_data.get("id"), @@ -120,7 +123,7 @@ class TestVnfPackage(TestCase): usageState="NOT_IN_USE", userDefinedData='{"a": "A"}' ) - response = self.client.get("/api/vnfpkgm/v1/vnf_packages/222") + response = self.client.get("%s/222" % VNF_BASE_URL) expect_data = { "id": "222", "vnfdId": "zte-hss-1.0", @@ -140,7 +143,7 @@ class TestVnfPackage(TestCase): self.assertEqual(response.status_code, status.HTTP_200_OK) def test_query_single_vnf_failed(self): - response = self.client.get("/api/vnfpkgm/v1/vnf_packages/222") + response = self.client.get(VNF_BASE_URL + "/222") self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) def test_query_multiple_vnf(self): @@ -170,7 +173,7 @@ class TestVnfPackage(TestCase): usageState="NOT_IN_USE", userDefinedData='{"a": "A"}' ) - response = self.client.get("/api/vnfpkgm/v1/vnf_packages") + response = self.client.get(VNF_BASE_URL) expect_data = [ { "id": "111", @@ -220,12 +223,12 @@ class TestVnfPackage(TestCase): usageState="NOT_IN_USE", userDefinedData='{"a": "A"}' ) - response = self.client.delete("/api/vnfpkgm/v1/vnf_packages/222") + response = self.client.delete(VNF_BASE_URL + "/222") self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) self.assertEqual(response.data, None) def test_delete_when_vnf_pkg_not_exist(self): - response = self.client.delete("/api/vnfpkgm/v1/vnf_packages/222") + response = self.client.delete(VNF_BASE_URL + "/222") self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) self.assertEqual(response.data, None) @@ -237,7 +240,7 @@ class TestVnfPackage(TestCase): onboardingState="ONBOARDED", localFilePath="vnfPackage.csar" ) - response = self.client.get("/api/vnfpkgm/v1/vnf_packages/222/package_content") + response = self.client.get(VNF_BASE_URL + "/222/package_content") file_content = '' for data in response.streaming_content: file_content = file_content + data.decode() @@ -253,7 +256,7 @@ class TestVnfPackage(TestCase): onboardingState="ONBOARDED", localFilePath="vnfPackage.csar" ) - response = self.client.get("/api/vnfpkgm/v1/vnf_packages/222/package_content", HTTP_RANGE="4-7") + response = self.client.get("%s/222/package_content" % VNF_BASE_URL, HTTP_RANGE="4-7") partial_file_content = '' for data in response.streaming_content: partial_file_content = partial_file_content + data.decode() @@ -269,7 +272,7 @@ class TestVnfPackage(TestCase): onboardingState="ONBOARDED", localFilePath="vnfPackage.csar" ) - response = self.client.get("/api/vnfpkgm/v1/vnf_packages/222/package_content", HTTP_RANGE=" 4-") + response = self.client.get(VNF_BASE_URL + "/222/package_content", HTTP_RANGE=" 4-") partial_file_content = '' for data in response.streaming_content: partial_file_content = partial_file_content + data.decode() @@ -278,7 +281,7 @@ class TestVnfPackage(TestCase): os.remove("vnfPackage.csar") def test_fetch_vnf_pkg_when_pkg_not_exist(self): - response = self.client.get("/api/vnfpkgm/v1/vnf_packages/222/package_content") + response = self.client.get(VNF_BASE_URL + "/222/package_content") self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) def test_fetch_vnf_pkg_when_catch_cataloge_exception(self): @@ -287,96 +290,135 @@ class TestVnfPackage(TestCase): onboardingState="CREATED", localFilePath="vnfPackage.csar" ) - response = self.client.get("/api/vnfpkgm/v1/vnf_packages/222/package_content") - self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) - - @mock.patch.object(VnfPackage, "create_vnf_pkg") - def test_create_vnf_pkg_when_catch_exception(self, mock_create_vnf_pkg): - mock_create_vnf_pkg.side_effect = TypeError('integer type') - req_data = { - "userDefinedData": {"a": "A"} - } - response = self.client.post("/api/vnfpkgm/v1/vnf_packages", data=req_data, format="json") - self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) - - @mock.patch.object(VnfPackage, "delete_vnf_pkg") - def test_delete_single_when_catch_exception(self, mock_delete_vnf_pkg): - mock_delete_vnf_pkg.side_effect = TypeError("integer type") - response = self.client.delete("/api/vnfpkgm/v1/vnf_packages/222") - self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) - - @mock.patch.object(VnfPackage, "query_single") - def test_query_single_when_catch_exception(self, mock_query_single): - mock_query_single.side_effect = TypeError("integer type") - response = self.client.get("/api/vnfpkgm/v1/vnf_packages/222") - self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) - - @mock.patch.object(VnfPackage, "query_multiple") - def test_query_multiple_when_catch_exception(self, mock_query_muitiple): - mock_query_muitiple.side_effect = TypeError("integer type") - response = self.client.get("/api/vnfpkgm/v1/vnf_packages") - self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) - - @mock.patch.object(toscaparser, 'parse_vnfd') - def test_upload_when_catch_exception(self, mock_parse_vnfd): - data = {'file': open(os.path.join(CATALOG_ROOT_PATH, "empty.txt"), "rb")} - VnfPackageModel.objects.create( - vnfPackageId="222", - onboardingState="CREATED" - ) - mock_parse_vnfd.side_effect = TypeError("integer type") - response = self.client.put("/api/vnfpkgm/v1/vnf_packages/222/package_content", data=data) - self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) - - @mock.patch.object(VnfPkgUploadThread, 'start') - def test_upload_from_uri_when_catch_exception(self, mock_start): - req_data = {"addressInformation": "https://127.0.0.1:1234/sdc/v1/hss.csar"} - mock_start.side_effect = TypeError("integer type") - response = self.client.post("/api/vnfpkgm/v1/vnf_packages/111/package_content/upload_from_uri", data=req_data) - self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) - - @mock.patch.object(VnfPackage, 'download') - def test_fetch_vnf_pkg_when_catch_exception(self, mock_download): - mock_download.side_effect = TypeError("integer type") - response = self.client.get("/api/vnfpkgm/v1/vnf_packages/222/package_content") + response = self.client.get(VNF_BASE_URL + "/222/package_content") self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) - @mock.patch.object(toscaparser, 'parse_vnfd') - def test_fetch_vnf_artifact(self, mock_parse_vnfd): - data = {'file': open(os.path.join(CATALOG_ROOT_PATH, "resource_test.csar"), "rb")} + def test_download_vnfd(self): VnfPackageModel.objects.create( vnfPackageId="222", - onboardingState="CREATED" + onboardingState="ONBOARDED", + localFilePath=os.path.join(CATALOG_ROOT_PATH, "resource_test.csar") ) - mock_parse_vnfd.return_value = json.JSONEncoder().encode(vnfd_data) - response = self.client.put("/api/vnfpkgm/v1/vnf_packages/222/package_content", data=data) - self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - response = self.client.get("/api/vnfpkgm/v1/vnf_packages/222/artifacts/image") + response = self.client.get(VNF_BASE_URL + "/222/vnfd") self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.getvalue(), b"ubuntu_16.04\n") - - @mock.patch.object(toscaparser, 'parse_vnfd') - def test_fetch_vnf_artifact_not_exists(self, mock_parse_vnfd): - data = {'file': open(os.path.join(CATALOG_ROOT_PATH, "resource_test.csar"), "rb")} - VnfPackageModel.objects.create( - vnfPackageId="222", - onboardingState="CREATED" - ) - mock_parse_vnfd.return_value = json.JSONEncoder().encode(vnfd_data) - response = self.client.put("/api/vnfpkgm/v1/vnf_packages/222/package_content", data=data) - self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - response = self.client.get("/api/vnfpkgm/v1/vnf_packages/1451/artifacts/image") - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - - @mock.patch.object(toscaparser, 'parse_vnfd') - def test_fetch_vnf_artifact_vnf_not_exists(self, mock_parse_vnfd): - data = {'file': open(os.path.join(CATALOG_ROOT_PATH, "resource_test.csar"), "rb")} - VnfPackageModel.objects.create( - vnfPackageId="222", - onboardingState="CREATED" - ) - mock_parse_vnfd.return_value = json.JSONEncoder().encode(vnfd_data) - response = self.client.put("/api/vnfpkgm/v1/vnf_packages/222/package_content", data=data) - self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - response = self.client.get("/api/vnfpkgm/v1/vnf_packages/222/artifacts/image1") - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + with open("vnfd.csar", 'wb') as local_file: + for chunk in response.streaming_content: + local_file.write(chunk) + self.assertTrue(zipfile.is_zipfile("vnfd.csar")) + os.remove("vnfd.csar") + + +def test_download_vnfd_when_pkg_not_exist(self): + response = self.client.get(VNF_BASE_URL + "/222/vnfd") + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + +def test_download_vnfd_when_catch_cataloge_exception(self): + VnfPackageModel.objects.create( + vnfPackageId="222", + onboardingState="CREATED", + localFilePath="vnfPackage.csar" + ) + response = self.client.get(VNF_BASE_URL + "/222/vnfd") + self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) + + +@mock.patch.object(VnfPackage, "create_vnf_pkg") +def test_create_vnf_pkg_when_catch_exception(self, mock_create_vnf_pkg): + mock_create_vnf_pkg.side_effect = TypeError('integer type') + req_data = { + "userDefinedData": {"a": "A"} + } + response = self.client.post(VNF_BASE_URL, data=req_data, format="json") + self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) + + +@mock.patch.object(VnfPackage, "delete_vnf_pkg") +def test_delete_single_when_catch_exception(self, mock_delete_vnf_pkg): + mock_delete_vnf_pkg.side_effect = TypeError("integer type") + response = self.client.delete(VNF_BASE_URL + "/222") + self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) + + +@mock.patch.object(VnfPackage, "query_single") +def test_query_single_when_catch_exception(self, mock_query_single): + mock_query_single.side_effect = TypeError("integer type") + response = self.client.get(VNF_BASE_URL + "/222") + self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) + + +@mock.patch.object(VnfPackage, "query_multiple") +def test_query_multiple_when_catch_exception(self, mock_query_muitiple): + mock_query_muitiple.side_effect = TypeError("integer type") + response = self.client.get(VNF_BASE_URL) + self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) + + +@mock.patch.object(toscaparser, 'parse_vnfd') +def test_upload_when_catch_exception(self, mock_parse_vnfd): + data = {'file': open(os.path.join(CATALOG_ROOT_PATH, "empty.txt"), "rb")} + VnfPackageModel.objects.create( + vnfPackageId="222", + onboardingState="CREATED" + ) + mock_parse_vnfd.side_effect = TypeError("integer type") + response = self.client.put(VNF_BASE_URL + "/222/package_content", data=data) + self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) + + +@mock.patch.object(VnfPkgUploadThread, 'start') +def test_upload_from_uri_when_catch_exception(self, mock_start): + req_data = {"addressInformation": "https://127.0.0.1:1234/sdc/v1/hss.csar"} + mock_start.side_effect = TypeError("integer type") + response = self.client.post(VNF_BASE_URL + "/111/package_content/upload_from_uri", data=req_data) + self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) + + +@mock.patch.object(VnfPackage, 'download') +def test_fetch_vnf_pkg_when_catch_exception(self, mock_download): + mock_download.side_effect = TypeError("integer type") + response = self.client.get(VNF_BASE_URL + "/222/package_content") + self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) + + +@mock.patch.object(toscaparser, 'parse_vnfd') +def test_fetch_vnf_artifact(self, mock_parse_vnfd): + data = {'file': open(os.path.join(CATALOG_ROOT_PATH, "resource_test.csar"), "rb")} + VnfPackageModel.objects.create( + vnfPackageId="222", + onboardingState="CREATED" + ) + mock_parse_vnfd.return_value = json.JSONEncoder().encode(vnfd_data) + response = self.client.put(VNF_BASE_URL + "/222/package_content", data=data) + self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) + response = self.client.get(VNF_BASE_URL + "/222/artifacts/image") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.getvalue(), b"ubuntu_16.04\n") + + +@mock.patch.object(toscaparser, 'parse_vnfd') +def test_fetch_vnf_artifact_not_exists(self, mock_parse_vnfd): + data = {'file': open(os.path.join(CATALOG_ROOT_PATH, "resource_test.csar"), "rb")} + VnfPackageModel.objects.create( + vnfPackageId="222", + onboardingState="CREATED" + ) + mock_parse_vnfd.return_value = json.JSONEncoder().encode(vnfd_data) + response = self.client.put(VNF_BASE_URL + "/222/package_content", data=data) + self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) + response = self.client.get(VNF_BASE_URL + "/1451/artifacts/image") + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + +@mock.patch.object(toscaparser, 'parse_vnfd') +def test_fetch_vnf_artifact_vnf_not_exists(self, mock_parse_vnfd): + data = {'file': open(os.path.join(CATALOG_ROOT_PATH, "resource_test.csar"), "rb")} + VnfPackageModel.objects.create( + vnfPackageId="222", + onboardingState="CREATED" + ) + mock_parse_vnfd.return_value = json.JSONEncoder().encode(vnfd_data) + response = self.client.put(VNF_BASE_URL + "/222/package_content", data=data) + 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) diff --git a/catalog/packages/urls.py b/catalog/packages/urls.py index 776e940..3ed6968 100644 --- a/catalog/packages/urls.py +++ b/catalog/packages/urls.py @@ -56,6 +56,7 @@ urlpatterns = [ # ETSI SOL005&SOL003 VNF Package url(r'^api/vnfpkgm/v1/vnf_packages$', vnf_package_views.vnf_packages_rc, name='vnf_packages_rc'), url(r'^api/vnfpkgm/v1/vnf_packages/(?P[0-9a-zA-Z\-\_]+)$', vnf_package_views.vnf_package_rd, name='vnf_package_rd'), + url(r'^api/vnfpkgm/v1/vnf_packages/(?P[0-9a-zA-Z\-\_]+)/vnfd$', vnf_package_views.vnfd_rd, name='vnfd_rd'), url(r'^api/vnfpkgm/v1/vnf_packages/(?P[0-9a-zA-Z\-\_]+)/package_content$', vnf_package_views.package_content_ru, name='package_content_ru'), url(r'^api/vnfpkgm/v1/vnf_packages/(?P[0-9a-zA-Z\-\_]+)/package_content/upload_from_uri$', vnf_package_views.upload_from_uri_c, name='upload_from_uri_c'), diff --git a/catalog/packages/views/vnf_package_views.py b/catalog/packages/views/vnf_package_views.py index 44e3df7..76891f2 100644 --- a/catalog/packages/views/vnf_package_views.py +++ b/catalog/packages/views/vnf_package_views.py @@ -72,6 +72,30 @@ def vnf_packages_rc(request): return Response(data=data, status=status.HTTP_201_CREATED) +@swagger_auto_schema( + method="GET", + operation_description="Read VNFD of an on-boarded VNF package", + tags=["VNF Package API"], + request_body=no_body, + responses={ + status.HTTP_200_OK: VnfPkgInfosSerializer(), + status.HTTP_404_NOT_FOUND: "VNF package does not exist", + status.HTTP_500_INTERNAL_SERVER_ERROR: "Internal error" + } +) +@api_view(http_method_names=["GET"]) +@view_safe_call_with_log(logger=logger) +def vnfd_rd(request, **kwargs): + vnf_pkg_id = kwargs.get("vnfPkgId") + logger.debug("Read VNFD for VNF package %s" % vnf_pkg_id) + try: + file_iterator = VnfPackage().download_vnfd(vnf_pkg_id) + return StreamingHttpResponse(file_iterator, status=status.HTTP_200_OK) + except Exception as e: + logger.error(e) + raise e + + @swagger_auto_schema( method='PUT', operation_description="Upload VNF package content", diff --git a/catalog/pub/config/config.py b/catalog/pub/config/config.py index 86585a3..e3b0062 100644 --- a/catalog/pub/config/config.py +++ b/catalog/pub/config/config.py @@ -13,8 +13,10 @@ # limitations under the License. # [MSB] -MSB_SERVICE_IP = '127.0.0.1' -MSB_SERVICE_PORT = '80' +# MSB_SERVICE_IP = '127.0.0.1' +# MSB_SERVICE_PORT = '80' +MSB_SERVICE_IP = '192.168.235.34' +MSB_SERVICE_PORT = '30280' # [REDIS] REDIS_HOST = '127.0.0.1' @@ -69,6 +71,17 @@ REG_TO_MSB_REG_PARAM = [{ "port": "8806", "ttl": 0 }] +}, { + "serviceName": "parser", + "version": "v1", + "url": "/api/parser/v1", + "protocol": "REST", + "visualRange": "1", + "nodes": [{ + "ip": "127.0.0.1", + "port": "8806", + "ttl": 0 + }] }] MSB_SVC_CALALOG_URL = "/api/microservices/v1/services/catalog/version/v1" MSB_SVC_NSD_URL = "/api/microservices/v1/services/nsd/version/v1" diff --git a/docs/index.rst b/docs/index.rst index 7f2127e..36b2d3b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,8 +1,9 @@ .. This work is licensed under a Creative Commons Attribution 4.0 International License. -TODO Add files to toctree and delete this header +ETSI Catalog Project ------------------------------------------------ .. toctree:: :maxdepth: 1 introduction + release-notes.rst diff --git a/docs/introduction.rst b/docs/introduction.rst index a9257ac..d7336d7 100644 --- a/docs/introduction.rst +++ b/docs/introduction.rst @@ -2,7 +2,8 @@ .. http://creativecommons.org/licenses/by/4.0 -ETSI Catalog Project Introduction +etsicatalog Introduction ================================= -The ETSI Catalog project provides a runtime catalog service which can be consumed by other projects or components, such as UUI, VF-C, etc. The catalog can be used to store packages distributed by the SDC, and also include a TOSCA parser service. +The ETSI Catalog project provides a runtime catalog service which can be consumed by other projects or components, such as UUI, VF-C, etc. +The catalog can be used to store packages distributed by the SDC, and also include a TOSCA parser service. diff --git a/static/catalog/resource_test.csar b/static/catalog/resource_test.csar index 1cf038d7443e43bfe596d3f3f3313b8f6a82e1d7..ec2beb0ee507100dbbd3b2fdd9f1356c307ad0a3 100644 GIT binary patch literal 1962 zcmWIWW@Zs#0D-dhTz@bFN^k*b$D)$Vw8Z3+V*LP|>O@i0xuh24lB8^Ln ze~5=`5LhF~b_Ry0Yf}Ag&OVsO3uJ?^0uUqIgk%>)V`gq*dTMD>XWi{IDx4#Wsj{Q;Nz> z+0U;dqo>LHdI?Ju=$+d4yWhH=GCvu#BA2^|(!hYq>?N z$Q@xfo0DQ3p0UE(o!{d3$L%{U;BEfA=TqsX$lOa$o>uxz{?qgH zEs=lzUF+hDk{T|G%Buell;S>4ksToxXar?dgpoSxK>LA~(;xnQXYv z_@+8z%~DgKUBAZYth04hc7W7`7>7b;Et6d~y~<O(AnRFZ*sb3CD=`SGbKb% zlD)&JsJkcQHRs8xr%YHoF1m7bzFWF~vQ^mcyNn73hxrSAwpp@X*mE`bF2jdM-x)-7 z3r_DlQ0CJsRuq2lzTim@HUXab&3rsI60QuQ&w2HFo=Q}$-gr^Q`n>m>Rhw-6+WgL3 z>Un%gUbS%3mK}?Z9%sCFTXUEDjcb2T?tHyz{?cZzq(m{BI|7?4cT`pP9uxZ0XYn}Y z-|>ZSW^Jw4`D1-*PLUf+dhsiTz2R!=UZyK9eR5}=Y%RM+*`2*Buz9|1>iygI(r0O1 zeDlMXd$H=0+nZ`qE<3Y$S^XEWKJE}4HEC1J@0Hm-=7N_#eboCRFLK`O#zpJ668+hW z)m~RQ6i73ES+`Zp_4vle!qR?j*Om6wy^A~WMpU&&eOa?R(rca{i#enhzTsr^_ESGj^qvd8IdA*# zB(ePr0p5&E_6)d+M-^bX00IIK3aMb?0vA*a42%pC3>yFQH!#e)$_S$+Zb10>%ob%p zHd_N|KGs4H*_s8fA;zN9_^gpeu_hXqHIT9u*)o`4&}oQexQcF=pBjJ`O~G#&xFAQi zksD%V&w83+4K!rVd|hbg3%LpUXcg!5vF6S6=0?- x08Qp(L^T~}9f54^f^xp2f>W