Add fetch artifacts API 42/82142/2
authorBharath Thiruveedula <bharath.thiruveedula@verizon.com>
Wed, 13 Mar 2019 15:07:34 +0000 (20:37 +0530)
committerBharath Thiruveedula <bharath.thiruveedula@verizon.com>
Wed, 13 Mar 2019 16:59:55 +0000 (22:29 +0530)
Add SOL 005 Fetch Artifacts API

Issue-ID: VFC-1220
Change-Id: I25752ad11ea7179ca14ef6ce3d5042874e443330
Signed-off-by: Bharath Thiruveedula <bharath.thiruveedula@verizon.com>
.gitignore
catalog/packages/biz/vnf_pkg_artifacts.py [new file with mode: 0644]
catalog/packages/tests/test_vnf_package.py
catalog/packages/urls.py
catalog/packages/views/vnf_package_artifact_views.py [new file with mode: 0644]
catalog/pub/exceptions.py
catalog/pub/utils/fileutil.py
static/catalog/resource_test.csar [new file with mode: 0644]

index 54a7acb..e6ae545 100644 (file)
@@ -5,5 +5,4 @@ logs/*.log
 target
 htmlcov
 .coverage
-static/*/*
 test-reports/*
diff --git a/catalog/packages/biz/vnf_pkg_artifacts.py b/catalog/packages/biz/vnf_pkg_artifacts.py
new file mode 100644 (file)
index 0000000..1077228
--- /dev/null
@@ -0,0 +1,39 @@
+# Copyright (C) 2019 Verizon. 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.
+
+import logging
+
+from catalog.pub.database.models import VnfPackageModel
+from catalog.pub.exceptions import ResourceNotFoundException, ArtifactNotFoundException
+from catalog.pub.utils import fileutil
+
+logger = logging.getLogger(__name__)
+
+
+class FetchVnfPkgArtifact(object):
+    def fetch(self, vnfPkgId, artifactPath):
+        logger.debug("FetchVnfPkgArtifact--get--single--artifact--biz::>"
+                     "ID: %s path: %s" % (vnfPkgId, artifactPath))
+        vnf_pkg = VnfPackageModel.objects.filter(vnfPackageId=vnfPkgId).get()
+        if not vnf_pkg:
+            err_msg = "NF Package (%s) doesn't exists." % vnfPkgId
+            return ResourceNotFoundException(err_msg)
+        local_path = vnf_pkg.localFilePath
+        if local_path.endswith(".csar") or local_path.endswith(".zip"):
+            vnf_extract_path = fileutil.unzip_csar_to_tmp(local_path)
+            artifact_path = fileutil.get_artifact_path(vnf_extract_path, artifactPath)
+            if not artifact_path:
+                raise ArtifactNotFoundException("Couldn't artifact %s" % artifactPath)
+        file_content = open(artifact_path, 'rb').read()
+        return file_content
index 701a22e..135c983 100644 (file)
@@ -321,3 +321,43 @@ class TestVnfPackage(TestCase):
         mock_download.side_effect = TypeError("integer type")
         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(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("/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")
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+        self.assertEqual(response.getvalue(), "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/2224/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)
index a6fb600..dfd1326 100755 (executable)
@@ -16,6 +16,7 @@ from django.conf.urls import url
 
 from catalog.packages.views import vnf_package_views
 from catalog.packages.views.vnf_package_subscription_views import CreateQuerySubscriptionView, QueryTerminateSubscriptionView
+from catalog.packages.views.vnf_package_artifact_views import FetchVnfPkgmArtifactsView
 from catalog.packages.views import catalog_views, ns_descriptor_views, pnf_descriptor_views, nsdm_subscription_views
 
 
@@ -53,6 +54,7 @@ urlpatterns = [
     # ETSI SOL 005 VNF Package Management Subscription APIs
     url(r'^api/vnfpkgm/v1/subscriptions$', CreateQuerySubscriptionView.as_view(), name='subscriptions_create_query'),
     url(r'^api/vnfpkgm/v1/subscriptions/(?P<subscriptionId>[0-9a-zA-Z\-\_]+)$', QueryTerminateSubscriptionView.as_view(), name='subscriptions_query_terminate'),
+    url(r'^api/vnfpkgm/v1/vnf_packages/(?P<vnfPkgId>[0-9a-zA-Z\-\_]+)/artifacts/(?P<artifactPath>[0-9a-zA-Z\-\_]+)$', FetchVnfPkgmArtifactsView.as_view(), name="fetch_vnf_artifacts"),
     # url(r'^api/vnfpkgm/v1/subscriptions/(?P<vnfPkgId>[0-9a-zA-Z\-\_]+)$', vnf_package_subscription_views.vnf_package_subscriptions_rc, name='subscriptions_rc'),
     # url(r'^api/vnfpkgm/v1/vnf_packages/(?P<vnfPkgId>[0-9a-zA-Z\-\_]+)/vnfd$', vnfd.as_view(), name='vnfd_r'),# url(r'^api/vnfpkgm/v1/vnf_packages/(?P<vnfPkgId>[0-9a-zA-Z\-\_]+)/artifacts/artifactPath$', artifacts.as_view(), name='artifacts_r'),
 
diff --git a/catalog/packages/views/vnf_package_artifact_views.py b/catalog/packages/views/vnf_package_artifact_views.py
new file mode 100644 (file)
index 0000000..c321cca
--- /dev/null
@@ -0,0 +1,77 @@
+# Copyright (C) 2019 Verizon. 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.
+
+
+import traceback
+import logging
+
+from drf_yasg.utils import swagger_auto_schema
+from rest_framework import status
+from rest_framework.views import APIView
+from rest_framework.response import Response
+from django.http import FileResponse
+
+from catalog.packages.serializers.response import ProblemDetailsSerializer
+from catalog.packages.biz.vnf_pkg_artifacts import FetchVnfPkgArtifact
+from catalog.pub.exceptions import ResourceNotFoundException, ArtifactNotFoundException
+
+logger = logging.getLogger(__name__)
+VALID_FILTERS = ["callbackUri", "notificationTypes", "vnfdId", "vnfPkgId", "operationalState", "usageState"]
+
+
+def get_problem_details_serializer(status_code, error_message):
+    problem_details = {
+        "status": status_code,
+        "detail": error_message
+    }
+    problem_details_serializer = ProblemDetailsSerializer(data=problem_details)
+    problem_details_serializer.is_valid()
+    return problem_details_serializer
+
+
+class FetchVnfPkgmArtifactsView(APIView):
+
+    @swagger_auto_schema(
+        responses={
+            status.HTTP_200_OK: None,
+            status.HTTP_404_NOT_FOUND: ProblemDetailsSerializer(),
+            status.HTTP_500_INTERNAL_SERVER_ERROR: ProblemDetailsSerializer()
+        }
+    )
+    def get(self, request, vnfPkgId, artifactPath):
+        logger.debug("FetchVnfPkgmArtifactsView--get::> ")
+        try:
+
+            resp_data = FetchVnfPkgArtifact().fetch(vnfPkgId, artifactPath)
+            response = FileResponse(resp_data)
+
+            return response
+        except ResourceNotFoundException as e:
+            logger.error(e.message)
+            logger.error(traceback.format_exc())
+            problem_details_serializer = get_problem_details_serializer(status.HTTP_404_NOT_FOUND,
+                                                                        traceback.format_exc())
+            return Response(data=problem_details_serializer.data, status=status.HTTP_404_NOT_FOUND)
+        except ArtifactNotFoundException as e:
+            logger.error(e.message)
+            logger.error(traceback.format_exc())
+            problem_details_serializer = get_problem_details_serializer(status.HTTP_404_NOT_FOUND,
+                                                                        traceback.format_exc())
+            return Response(data=problem_details_serializer.data, status=status.HTTP_404_NOT_FOUND)
+        except Exception as e:
+            logger.error(e.message)
+            logger.error(traceback.format_exc())
+            problem_details_serializer = get_problem_details_serializer(status.HTTP_404_NOT_FOUND,
+                                                                        traceback.format_exc())
+            return Response(data=problem_details_serializer.data, status=status.HTTP_404_NOT_FOUND)
index 96c2fd9..afe45d1 100644 (file)
@@ -39,3 +39,7 @@ class NsdmBadRequestException(CatalogException):
 
 class NsdmDuplicateSubscriptionException(CatalogException):
     pass
+
+
+class ArtifactNotFoundException(CatalogException):
+    pass
index 48b64cb..d7811b8 100644 (file)
@@ -14,6 +14,7 @@
 import os
 import shutil
 import logging
+import tempfile
 import traceback
 import urllib2
 import zipfile
@@ -62,3 +63,17 @@ def unzip_file(zip_src, dst_dir, csar_path):
         return os.path.join(dst_dir, csar_path)
     else:
         return ""
+
+
+def unzip_csar_to_tmp(zip_src):
+    dirpath = tempfile.mkdtemp()
+    zip_ref = zipfile.ZipFile(zip_src, 'r')
+    zip_ref.extractall(dirpath)
+    return dirpath
+
+
+def get_artifact_path(vnf_path, artifact_file):
+    for root, dirs, files in os.walk(vnf_path):
+        if artifact_file in files:
+            return os.path.join(root, artifact_file)
+    return None
diff --git a/static/catalog/resource_test.csar b/static/catalog/resource_test.csar
new file mode 100644 (file)
index 0000000..1cf038d
Binary files /dev/null and b/static/catalog/resource_test.csar differ