From 68f9a9e97e345f7a3d3b37cb59a923cbf4f2046d Mon Sep 17 00:00:00 2001 From: Bharath Thiruveedula Date: Fri, 1 Mar 2019 18:06:39 +0530 Subject: Add Create VnfPkgmSubscription API Add SOL 005 Create VnfPkgmSubscription API Issue-ID: VFC-1221 Change-Id: Id0b7d8c454a8c77cf14b9d299b5afb3e9ab29df5 Signed-off-by: Bharath Thiruveedula --- .gitignore | 4 +- catalog/packages/biz/vnf_pkg_subscription.py | 138 +++++++++++++++++++++ catalog/packages/const.py | 10 ++ catalog/packages/serializers/nsd_info.py | 3 + catalog/packages/serializers/response.py | 28 +++++ .../packages/serializers/subscription_auth_data.py | 69 +++++++++++ catalog/packages/serializers/vnf_pkg_info.py | 3 + .../packages/serializers/vnf_pkg_notifications.py | 99 +++++++++++++++ .../packages/serializers/vnf_pkg_subscription.py | 87 +++++++++++++ .../packages/tests/test_vnf_pkg_subscription.py | 100 +++++++++++++++ catalog/packages/urls.py | 5 +- .../views/vnf_package_subscription_views.py | 72 +++++++++++ catalog/pub/database/models.py | 35 ++++++ catalog/pub/exceptions.py | 8 ++ 14 files changed, 659 insertions(+), 2 deletions(-) create mode 100755 catalog/packages/biz/vnf_pkg_subscription.py mode change 100644 => 100755 catalog/packages/const.py create mode 100644 catalog/packages/serializers/response.py create mode 100755 catalog/packages/serializers/subscription_auth_data.py create mode 100755 catalog/packages/serializers/vnf_pkg_notifications.py create mode 100755 catalog/packages/serializers/vnf_pkg_subscription.py create mode 100644 catalog/packages/tests/test_vnf_pkg_subscription.py mode change 100644 => 100755 catalog/packages/urls.py create mode 100755 catalog/packages/views/vnf_package_subscription_views.py diff --git a/.gitignore b/.gitignore index f5ecb0a5..54a7acb3 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ logs/*.log .tox target htmlcov -.coverage \ No newline at end of file +.coverage +static/*/* +test-reports/* diff --git a/catalog/packages/biz/vnf_pkg_subscription.py b/catalog/packages/biz/vnf_pkg_subscription.py new file mode 100755 index 00000000..20bd68ed --- /dev/null +++ b/catalog/packages/biz/vnf_pkg_subscription.py @@ -0,0 +1,138 @@ +# 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 ast +import json +import logging +import os +import requests +import uuid + +from collections import Counter +from rest_framework import status + +from catalog.packages import const +from catalog.pub.database.models import VnfPkgSubscriptionModel +from catalog.pub.exceptions import VnfPkgSubscriptionException, VnfPkgDuplicateSubscriptionException +from catalog.pub.utils.values import ignore_case_get + + +logger = logging.getLogger(__name__) + +ROOT_FILTERS = { + "notificationTypes": "notification_types", + "vnfdId": "vnfd_id", + "vnfPkgId": "vnf_pkg_id", + "operationalState": "operation_states", + "usageState": "usage_states" +} + + +def is_filter_type_equal(new_filter, existing_filter): + return Counter(new_filter) == Counter(existing_filter) + + +class CreateSubscription(object): + + def __init__(self, data): + self.data = data + self.filter = ignore_case_get(self.data, "filters", {}) + self.callback_uri = ignore_case_get(self.data, "callbackUri") + self.authentication = ignore_case_get(self.data, "authentication", {}) + self.notification_types = ignore_case_get(self.filter, "notificationTypes", []) + self.operation_states = ignore_case_get(self.filter, "operationalState", []) + self.usage_states = ignore_case_get(self.filter, "usageState", []) + self.vnfd_id = ignore_case_get(self.filter, "vnfdId", []) + self.vnf_pkg_id = ignore_case_get(self.filter, "vnfPkgId", []) + self.vnf_products_from_provider = \ + ignore_case_get(self.filter, "vnfProductsFromProviders", {}) + + def check_callbackuri_connection(self): + logger.debug("SubscribeNotification-post::> Sending GET request " + "to %s" % self.callback_uri) + try: + response = requests.get(self.callback_uri, timeout=2) + if response.status_code != status.HTTP_204_NO_CONTENT: + raise VnfPkgSubscriptionException("callbackUri %s returns %s status " + "code." % (self.callback_uri, response.status_code)) + except Exception: + raise VnfPkgSubscriptionException("callbackUri %s didn't return 204 status" + "code." % self.callback_uri) + + def do_biz(self): + self.subscription_id = str(uuid.uuid4()) + self.check_callbackuri_connection() + self.check_valid_auth_info() + self.check_valid() + self.save_db() + subscription = VnfPkgSubscriptionModel.objects.get(subscription_id=self.subscription_id) + if subscription: + return subscription.toDict() + + def check_valid_auth_info(self): + logger.debug("SubscribeNotification--post::> Validating Auth " + "details if provided") + if self.authentication.get("paramsBasic", {}) and \ + const.BASIC not in self.authentication.get("authType"): + raise VnfPkgSubscriptionException('Auth type should be ' + const.BASIC) + if self.authentication.get("paramsOauth2ClientCredentials", {}) and \ + const.OAUTH2_CLIENT_CREDENTIALS not in self.authentication.get("authType"): + raise VnfPkgSubscriptionException('Auth type should be ' + const.OAUTH2_CLIENT_CREDENTIALS) + + def check_filter_exists(self, sub): + # Check the usage states, operationStates + for filter_type in ["operation_states", "usage_states"]: + if not is_filter_type_equal(getattr(self, filter_type), + ast.literal_eval(getattr(sub, filter_type))): + return False + # If all the above types are same then check id filters + for id_filter in ["vnfd_id", "vnf_pkg_id"]: + if not is_filter_type_equal(getattr(self, id_filter), + ast.literal_eval(getattr(sub, id_filter))): + return False + return True + + def check_valid(self): + logger.debug("SubscribeNotification--post::> Checking DB if " + "callbackUri already exists") + subscriptions = VnfPkgSubscriptionModel.objects.filter(callback_uri=self.callback_uri) + if not subscriptions.exists(): + return True + for subscription in subscriptions: + if self.check_filter_exists(subscription): + raise VnfPkgDuplicateSubscriptionException( + "Already Subscription (%s) exists with the " + "same callbackUri and filter" % subscription.subscription_id) + return True + + def save_db(self): + logger.debug("SubscribeNotification--post::> Saving the subscription " + "%s to the database" % self.subscription_id) + links = { + "self": { + "href": os.path.join(const.VNFPKG_SUBSCRIPTION_ROOT_URI, self.subscription_id) + } + } + VnfPkgSubscriptionModel.objects.create( + subscription_id=self.subscription_id, + callback_uri=self.callback_uri, + notification_types=json.dumps(self.notification_types), + auth_info=json.dumps(self.authentication), + usage_states=json.dumps(self.usage_states), + operation_states=json.dumps(self.operation_states), + vnf_products_from_provider=json.dumps(self.vnf_products_from_provider), + vnfd_id=json.dumps(self.vnfd_id), + vnf_pkg_id=json.dumps(self.vnf_pkg_id), + links=json.dumps(links)) + logger.debug('Create Subscription[%s] success', self.subscription_id) diff --git a/catalog/packages/const.py b/catalog/packages/const.py old mode 100644 new mode 100755 index 9d3f6ebd..e942ffdf --- a/catalog/packages/const.py +++ b/catalog/packages/const.py @@ -16,3 +16,13 @@ from catalog.pub.utils.jobutil import enum PKG_STATUS = enum(CREATED="CREATED", UPLOADING="UPLOADING", PROCESSING="PROCESSING", ONBOARDED="ONBOARDED", IN_USE="IN_USE", NOT_IN_USE="NOT_IN_USE", ENABLED="ENABLED", DISABLED="DISABLED") + +AUTH_TYPES = ["BASIC", "OAUTH2_CLIENT_CREDENTIALS", "TLS_CERT"] + +BASIC = "BASIC" + +OAUTH2_CLIENT_CREDENTIALS = "OAUTH2_CLIENT_CREDENTIALS" + +NOTIFICATION_TYPES = ["VnfPackageOnboardingNotification", "VnfPackageChangeNotification"] + +VNFPKG_SUBSCRIPTION_ROOT_URI = "api/vnfpkgm/v1/subscriptions/" diff --git a/catalog/packages/serializers/nsd_info.py b/catalog/packages/serializers/nsd_info.py index e934a1f1..59df1b65 100644 --- a/catalog/packages/serializers/nsd_info.py +++ b/catalog/packages/serializers/nsd_info.py @@ -29,6 +29,9 @@ class _LinkSerializer(serializers.Serializer): allow_null=False ) + class Meta: + ref_name = 'NSD_LinkSerializer' + class NsdInfoSerializer(serializers.Serializer): id = serializers.CharField( diff --git a/catalog/packages/serializers/response.py b/catalog/packages/serializers/response.py new file mode 100644 index 00000000..64740780 --- /dev/null +++ b/catalog/packages/serializers/response.py @@ -0,0 +1,28 @@ +# 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. + +from rest_framework import serializers + + +class ProblemDetailsSerializer(serializers.Serializer): + type = serializers.CharField(help_text="Type", required=False, allow_null=True) + title = serializers.CharField(help_text="Title", required=False, allow_null=True) + status = serializers.IntegerField(help_text="Status", required=True) + detail = serializers.CharField(help_text="Detail", required=True, allow_null=True) + instance = serializers.CharField(help_text="Instance", required=False, allow_null=True) + additional_details = serializers.ListField( + help_text="Any number of additional attributes, as defined in a " + "specification or by an implementation.", + required=False, + allow_null=True) diff --git a/catalog/packages/serializers/subscription_auth_data.py b/catalog/packages/serializers/subscription_auth_data.py new file mode 100755 index 00000000..5d40e4d3 --- /dev/null +++ b/catalog/packages/serializers/subscription_auth_data.py @@ -0,0 +1,69 @@ +# 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. + +from rest_framework import serializers + +from catalog.packages import const + + +class OAuthCredentialsSerializer(serializers.Serializer): + clientId = serializers.CharField( + help_text="Client identifier to be used in the access token " + "request of the OAuth 2.0 client credentials grant type.", + required=False, + max_length=255, + allow_null=False) + clientPassword = serializers.CharField( + help_text="Client password to be used in the access token " + "request of the OAuth 2.0 client credentials grant type.", + required=False, + max_length=255, + allow_null=False) + tokenEndpoint = serializers.CharField( + help_text="The token endpoint from which the access token can " + "be obtained.", + required=False, + max_length=255, + allow_null=False) + + +class BasicAuthSerializer(serializers.Serializer): + userName = serializers.CharField( + help_text="Username to be used in HTTP Basic authentication.", + max_length=255, + required=False, + allow_null=False) + password = serializers.CharField( + help_text="Password to be used in HTTP Basic authentication.", + max_length=255, + required=False, + allow_null=False) + + +class SubscriptionAuthenticationSerializer(serializers.Serializer): + authType = serializers.ListField( + child=serializers.ChoiceField(required=True, choices=const.AUTH_TYPES), + help_text="Defines the types of Authentication / Authorization " + "which the API consumer is willing to accept when " + "receiving a notification.", + required=True) + paramsBasic = BasicAuthSerializer( + help_text="Parameters for authentication/authorization using BASIC.", + required=False, + allow_null=False) + paramsOauth2ClientCredentials = OAuthCredentialsSerializer( + help_text="Parameters for authentication/authorization using " + "OAUTH2_CLIENT_CREDENTIALS.", + required=False, + allow_null=False) diff --git a/catalog/packages/serializers/vnf_pkg_info.py b/catalog/packages/serializers/vnf_pkg_info.py index 4c63a463..844937fb 100644 --- a/catalog/packages/serializers/vnf_pkg_info.py +++ b/catalog/packages/serializers/vnf_pkg_info.py @@ -33,6 +33,9 @@ class _LinkSerializer(serializers.Serializer): required=True, allow_null=False) + class Meta: + ref_name = 'VNF_PKGM_Link_Serializer' + class VnfPkgInfoSerializer(serializers.Serializer): id = serializers.CharField( diff --git a/catalog/packages/serializers/vnf_pkg_notifications.py b/catalog/packages/serializers/vnf_pkg_notifications.py new file mode 100755 index 00000000..deb0ea03 --- /dev/null +++ b/catalog/packages/serializers/vnf_pkg_notifications.py @@ -0,0 +1,99 @@ +# 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. + +from rest_framework import serializers + +from catalog.packages.const import NOTIFICATION_TYPES + +PackageOperationalStateType = ["ENABLED", "DISABLED"] +PackageUsageStateType = ["IN_USE", "NOT_IN_USE"] + + +class VersionSerializer(serializers.Serializer): + vnfSoftwareVersion = serializers.CharField( + help_text="VNF software version to match.", + max_length=255, + required=True, + allow_null=False) + vnfdVersions = serializers.ListField( + child=serializers.CharField(), + help_text="Match VNF packages that contain " + "VNF products with certain VNFD versions", + required=False, + allow_null=False) + + +class vnfProductsSerializer(serializers.Serializer): + vnfProductName = serializers.CharField( + help_text="Name of the VNF product to match.", + max_length=255, + required=True, + allow_null=False) + versions = VersionSerializer( + help_text="match VNF packages that contain " + "VNF products with certain versions", + required=False, + allow_null=False + ) + + +class vnfProductsProvidersSerializer(serializers.Serializer): + vnfProvider = serializers.CharField( + help_text="Name of the VNFprovider to match.", + max_length=255, + required=True, + allow_null=False) + vnfProducts = vnfProductsSerializer( + help_text="match VNF packages that contain " + "VNF products with certain product names, " + "from one particular provider", + required=False, + allow_null=False + ) + + +class PkgmNotificationsFilter(serializers.Serializer): + notificationTypes = serializers.ListField( + child=serializers.ChoiceField(required=True, choices=NOTIFICATION_TYPES), + help_text="Match particular notification types", + allow_null=False, + required=False) + vnfProductsFromProviders = vnfProductsProvidersSerializer( + help_text="Match VNF packages that contain " + "VNF products from certain providers.", + allow_null=False, + required=False + ) + vnfdId = serializers.ListField( + child=serializers.UUIDField(), + help_text="Match VNF packages with a VNFD identifier" + "listed in the attribute", + required=False, + allow_null=False) + vnfPkgId = serializers.ListField( + child=serializers.UUIDField(), + help_text="Match VNF packages with a VNFD identifier" + "listed in the attribute", + required=False, + allow_null=False) + operationalState = serializers.ListField( + child=serializers.ChoiceField(required=True, choices=PackageOperationalStateType), + help_text="Operational state of the VNF package.", + allow_null=False, + required=False) + usageState = serializers.ListField( + child=serializers.ChoiceField(required=True, choices=PackageUsageStateType), + help_text="Operational state of the VNF package.", + allow_null=False, + required=False) diff --git a/catalog/packages/serializers/vnf_pkg_subscription.py b/catalog/packages/serializers/vnf_pkg_subscription.py new file mode 100755 index 00000000..8cbac758 --- /dev/null +++ b/catalog/packages/serializers/vnf_pkg_subscription.py @@ -0,0 +1,87 @@ +# 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. + +from rest_framework import serializers + +from catalog.packages.serializers import subscription_auth_data +from catalog.packages.serializers import vnf_pkg_notifications + + +class LinkSerializer(serializers.Serializer): + href = serializers.CharField( + help_text="URI of the referenced resource.", + required=True, + allow_null=False, + allow_blank=False) + + class Meta: + ref_name = 'VNF_SUBSCRIPTION_LINKSERIALIZER' + + +class LinkSelfSerializer(serializers.Serializer): + self = LinkSerializer( + help_text="URI of this resource.", + required=True, + allow_null=False) + + +class PkgmSubscriptionRequestSerializer(serializers.Serializer): + filters = vnf_pkg_notifications.PkgmNotificationsFilter( + help_text="Filter settings for this subscription, " + "to define the subset of all notifications" + " this subscription relates to", + required=False, + allow_null=False + ) + callbackUri = serializers.URLField( + help_text="Callback URI to send" + "the notification", + required=True, + allow_null=False) + authentication = subscription_auth_data.SubscriptionAuthenticationSerializer( + help_text="Authentication parameters to configure the use of " + "authorization when sending notifications corresponding to" + "this subscription", + required=False, + allow_null=False + ) + + +class PkgmSubscriptionSerializer(serializers.Serializer): + id = serializers.UUIDField( + help_text="Identifier of this subscription resource.", + required=True, + allow_null=False) + callbackUri = serializers.URLField( + help_text="The URI of the endpoint to send the notification to.", + required=True, + allow_null=False) + + _links = LinkSelfSerializer( + help_text="Links to resources related to this resource.", + required=True, + allow_null=False) + + filter = vnf_pkg_notifications.PkgmNotificationsFilter( + help_text="Filter settings for this subscription, " + "to define the subset of all notifications" + " this subscription relates to", + required=False, + allow_null=False + ) + + +class PkgmSubscriptionsSerializer(serializers.ListSerializer): + child = PkgmSubscriptionSerializer() + allow_empty = True diff --git a/catalog/packages/tests/test_vnf_pkg_subscription.py b/catalog/packages/tests/test_vnf_pkg_subscription.py new file mode 100644 index 00000000..7c8b9f13 --- /dev/null +++ b/catalog/packages/tests/test_vnf_pkg_subscription.py @@ -0,0 +1,100 @@ +# 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 uuid +import mock +from rest_framework.test import APIClient +from django.test import TestCase +from catalog.pub.database.models import VnfPkgSubscriptionModel + + +class TestNfPackageSubscription(TestCase): + def setUp(self): + self.client = APIClient() + VnfPkgSubscriptionModel.objects.filter().delete() + self.vnf_subscription_data = { + "filters": { + "notificationTypes": [ + "VnfPackageOnboardingNotification" + ], + "vnfProductsFromProviders": { + "vnfProvider": "string", + "vnfProducts": { + "vnfProductName": "string", + "versions": { + "vnfSoftwareVersion": "string", + "vnfdVersions": [ + "string" + ] + } + } + }, + "vnfdId": [ + "3fa85f64-5717-4562-b3fc-2c963f66afa6" + ], + "vnfPkgId": [ + "3fa85f64-5717-4562-b3fc-2c963f66afa6" + ], + "operationalState": [ + "ENABLED" + ], + "usageState": [ + "IN_USE" + ] + }, + "callbackUri": "http://www.vnf1.com/notification", + "authentication": { + "authType": [ + "BASIC" + ], + "paramsBasic": { + "userName": "string", + "password": "string" + } + } + } + + def tearDown(self): + pass + + @mock.patch("requests.get") + @mock.patch.object(uuid, 'uuid4') + def test_create_vnf_subscription(self, mock_uuid4, mock_requests): + temp_uuid = "99442b18-a5c7-11e8-998c-bf1755941f13" + mock_requests.return_value.status_code = 204 + mock_requests.get.status_code = 204 + mock_uuid4.return_value = temp_uuid + response = self.client.post("/api/vnfpkgm/v1/subscriptions", data=self.vnf_subscription_data, format='json') + self.assertEqual(201, response.status_code) + self.assertEqual(self.vnf_subscription_data["callbackUri"], response.data["callbackUri"]) + self.assertEqual(temp_uuid, response.data["id"]) + + @mock.patch("requests.get") + @mock.patch.object(uuid, 'uuid4') + def test_create_duplicate_subscriptions(self, mock_uuid4, mock_requests): + temp_uuid = "99442b18-a5c7-11e8-998c-bf1755941f13" + temp1_uuid = "00342b18-a5c7-11e8-998c-bf1755941f12" + mock_requests.return_value.status_code = 204 + mock_requests.get.status_code = 204 + mock_uuid4.side_effect = [temp_uuid, temp1_uuid] + response = self.client.post("/api/vnfpkgm/v1/subscriptions", data=self.vnf_subscription_data, format='json') + self.assertEqual(201, response.status_code) + self.assertEqual(self.vnf_subscription_data["callbackUri"], response.data["callbackUri"]) + self.assertEqual(temp_uuid, response.data["id"]) + temp_uuid = "00442b18-a5c7-11e8-998c-bf1755941f12" + mock_requests.return_value.status_code = 204 + mock_requests.get.status_code = 204 + mock_uuid4.return_value = temp_uuid + response = self.client.post("/api/vnfpkgm/v1/subscriptions", data=self.vnf_subscription_data, format='json') + self.assertEqual(303, response.status_code) diff --git a/catalog/packages/urls.py b/catalog/packages/urls.py old mode 100644 new mode 100755 index e62bc76a..1c20fc20 --- a/catalog/packages/urls.py +++ b/catalog/packages/urls.py @@ -15,6 +15,7 @@ from django.conf.urls import url from catalog.packages.views import vnf_package_views +from catalog.packages.views.vnf_package_subscription_views import SubscriptionsView from catalog.packages.views import catalog_views, ns_descriptor_views, pnf_descriptor_views @@ -48,7 +49,9 @@ urlpatterns = [ 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\-\_]+)/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'), + url(r'^api/vnfpkgm/v1/subscriptions$', SubscriptionsView.as_view(), name='subscriptions_create_query'), + # url(r'^api/vnfpkgm/v1/subscriptions/(?P[0-9a-zA-Z\-\_]+)$', vnf_package_subscription_views.vnf_package_subscriptions_rc, name='subscriptions_rc'), # url(r'^api/vnfpkgm/v1/vnf_packages/(?P[0-9a-zA-Z\-\_]+)/vnfd$', vnfd.as_view(), name='vnfd_r'),# url(r'^api/vnfpkgm/v1/vnf_packages/(?P[0-9a-zA-Z\-\_]+)/artifacts/artifactPath$', artifacts.as_view(), name='artifacts_r'), - # url(r'^api/vnfpkgm/v1/subscriptions', vnfpkg_subscriptions.as_view(), name='subscriptions_rc'), + # url(r'^api/vnfpkgm/v1/subscriptions/(?P[0-9a-zA-Z\-\_]+)$', vnfpkg_subscription.as_view(), name='subscription_rd'), ] diff --git a/catalog/packages/views/vnf_package_subscription_views.py b/catalog/packages/views/vnf_package_subscription_views.py new file mode 100755 index 00000000..d7449a89 --- /dev/null +++ b/catalog/packages/views/vnf_package_subscription_views.py @@ -0,0 +1,72 @@ +# 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 catalog.packages.serializers.vnf_pkg_subscription import PkgmSubscriptionRequestSerializer, \ + PkgmSubscriptionSerializer +from catalog.packages.serializers.response import ProblemDetailsSerializer +from catalog.packages.biz.vnf_pkg_subscription import CreateSubscription +from catalog.packages.views.common import validate_data +from catalog.pub.exceptions import VnfPkgDuplicateSubscriptionException + +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 SubscriptionsView(APIView): + + @swagger_auto_schema( + request_body=PkgmSubscriptionRequestSerializer, + responses={ + status.HTTP_201_CREATED: PkgmSubscriptionSerializer(), + status.HTTP_500_INTERNAL_SERVER_ERROR: "Internal error" + } + ) + def post(self, request): + logger.debug("Create VNF package Subscription> %s" % request.data) + try: + vnf_pkg_subscription_request = validate_data(request.data, PkgmSubscriptionRequestSerializer) + data = CreateSubscription(vnf_pkg_subscription_request.data).do_biz() + subscription_info = validate_data(data, PkgmSubscriptionSerializer) + return Response(data=subscription_info.data, status=status.HTTP_201_CREATED) + except VnfPkgDuplicateSubscriptionException as e: + logger.error(e.message) + logger.error(traceback.format_exc()) + problem_details_serializer = get_problem_details_serializer(status.HTTP_303_SEE_OTHER, + traceback.format_exc()) + return Response(data=problem_details_serializer.data, status=status.HTTP_303_SEE_OTHER) + except Exception as e: + logger.error(e.message) + logger.error(traceback.format_exc()) + problem_details_serializer = get_problem_details_serializer(status.HTTP_500_INTERNAL_SERVER_ERROR, + traceback.format_exc()) + return Response(data=problem_details_serializer.data, status=status.HTTP_500_INTERNAL_SERVER_ERROR) diff --git a/catalog/pub/database/models.py b/catalog/pub/database/models.py index 04e39c29..c1e2a8ae 100644 --- a/catalog/pub/database/models.py +++ b/catalog/pub/database/models.py @@ -140,3 +140,38 @@ class JobStatusModel(models.Model): def toJSON(self): import json return json.dumps(dict([(attr, getattr(self, attr)) for attr in [f.name for f in self._meta.fields]])) + + +class VnfPkgSubscriptionModel(models.Model): + subscription_id = models.CharField(max_length=255, primary_key=True, db_column='SUBSCRIPTION_ID') + callback_uri = models.URLField(db_column="CALLBACK_URI", max_length=255) + auth_info = models.TextField(db_column="AUTH_INFO") + usage_states = models.TextField(db_column="USAGE_STATES") + notification_types = models.TextField(db_column="NOTIFICATION_TYPES") + vnfd_id = models.TextField(db_column="VNFD_ID") + vnf_pkg_id = models.TextField(db_column="VNF_PKG_ID") + operation_states = models.TextField(db_column="OPERATION_STATES") + vnf_products_from_provider = \ + models.TextField(db_column="VNF_PRODUCTS_FROM_PROVIDER") + links = models.TextField(db_column="LINKS") + + class Meta: + db_table = 'VNF_PKG_SUBSCRIPTION' + + def toDict(self): + import json + subscription_obj = { + "id": self.subscription_id, + "callbackUri": self.callback_uri, + "_links": json.loads(self.links) + } + filter_obj = { + "notificationTypes": json.loads(self.notification_types), + "vnfdId": json.loads(self.vnfd_id), + "vnfPkgId": json.loads(self.vnf_pkg_id), + "operationalState": json.loads(self.operation_states), + "usageState": json.loads(self.usage_states), + "vnfProductsFromProviders": json.loads(self.vnf_products_from_provider) + } + subscription_obj["filter"] = filter_obj + return subscription_obj diff --git a/catalog/pub/exceptions.py b/catalog/pub/exceptions.py index a86775ea..7f348d65 100644 --- a/catalog/pub/exceptions.py +++ b/catalog/pub/exceptions.py @@ -19,3 +19,11 @@ class CatalogException(Exception): class ResourceNotFoundException(CatalogException): pass + + +class VnfPkgSubscriptionException(CatalogException): + pass + + +class VnfPkgDuplicateSubscriptionException(CatalogException): + pass -- cgit 1.2.3-korg