summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSirisha Gopigiri <sirishagopigiri@gmail.com>2019-03-11 13:58:22 +0530
committerSirisha Gopigiri <sirisha.gopigiri@verizon.com>2019-03-11 17:32:08 +0530
commit9ce8ab299e1d4c20e1ce2ad6f781baed32de51ba (patch)
tree4897930852f82da1d64903dc7c878c7df72aa1d1
parent68f9a9e97e345f7a3d3b37cb59a923cbf4f2046d (diff)
Add Create NsdManagementSubscription API
Add SOL 005 Create NsdManagementSubscription API Change-Id: I8154b0592454c4944ed2f17b179eed8e8b6112c4 Issue-ID: VFC-1217 Signed-off-by: Sirisha Gopigiri <sirisha.gopigiri@verizon.com>
-rw-r--r--catalog/packages/biz/nsdm_subscription.py177
-rwxr-xr-xcatalog/packages/const.py19
-rw-r--r--catalog/packages/serializers/nsdm_filter_data.py124
-rw-r--r--catalog/packages/serializers/nsdm_subscription.py75
-rw-r--r--catalog/packages/serializers/response.py3
-rw-r--r--catalog/packages/tests/test_nsdm_subscription.py340
-rwxr-xr-xcatalog/packages/urls.py6
-rw-r--r--catalog/packages/views/nsdm_subscription_views.py105
-rw-r--r--catalog/pub/database/models.py34
-rw-r--r--catalog/pub/exceptions.py8
10 files changed, 888 insertions, 3 deletions
diff --git a/catalog/packages/biz/nsdm_subscription.py b/catalog/packages/biz/nsdm_subscription.py
new file mode 100644
index 00000000..19df46e0
--- /dev/null
+++ b/catalog/packages/biz/nsdm_subscription.py
@@ -0,0 +1,177 @@
+# 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 requests
+import uuid
+
+from collections import Counter
+
+from rest_framework import status
+
+from catalog.packages import const
+from catalog.pub.database.models import NsdmSubscriptionModel
+from catalog.pub.exceptions import CatalogException, \
+ NsdmBadRequestException, NsdmDuplicateSubscriptionException
+from catalog.pub.utils.values import ignore_case_get
+
+logger = logging.getLogger(__name__)
+
+PARAMSBASICKEYS = ["userName", "password"]
+
+PARAMSOAUTH2CLIENTCREDENTIALSKEYS = ["clientId", "clientPassword",
+ "tokenEndpoint"]
+
+
+def is_filter_type_equal(new_filter, existing_filter):
+ return Counter(list(set(new_filter))) == Counter(existing_filter)
+
+
+class NsdmSubscription:
+
+ def __init__(self):
+ pass
+
+ def check_callbackuri_connection(self):
+ logger.debug("Create Subscription --> Test Callback URI --"
+ "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 CatalogException("callbackUri %s returns %s status "
+ "code." % (self.callback_uri,
+ response.status_code))
+ except Exception:
+ raise CatalogException("callbackUri %s didn't return 204 status"
+ "code." % self.callback_uri)
+
+ def fill_resp_data(self, subscription):
+ subscription_filter = dict()
+ for filter_type in const.NSDM_NOTIFICATION_FILTERS:
+ subscription_filter[filter_type] = \
+ ast.literal_eval(subscription.__dict__[filter_type])
+ resp_data = {
+ 'id': subscription.subscriptionid,
+ 'callbackUri': subscription.callback_uri,
+ 'filter': subscription_filter,
+ '_links': json.loads(subscription.links)
+ }
+ return resp_data
+
+ def create(self, data):
+ logger.debug("Start Create Subscription... ")
+ self.filter = ignore_case_get(data, "filter", {})
+ self.callback_uri = ignore_case_get(data, "callbackUri")
+ self.authentication = ignore_case_get(data, "authentication", {})
+ self.subscription_id = str(uuid.uuid4())
+ self.check_callbackuri_connection()
+ self.check_valid_auth_info()
+ self.check_filter_types()
+ self.check_valid()
+ self.save_db()
+ subscription = \
+ NsdmSubscriptionModel.objects.get(
+ subscriptionid=self.subscription_id)
+ return self.fill_resp_data(subscription)
+
+ def check_filter_types(self):
+ # Check if both nsdId and nsdInfoId
+ # or pnfdId and pnfdInfoId are present
+ logger.debug("Create Subscription --> Validating Filters... ")
+ if self.filter and \
+ self.filter.get("nsdId", "") and \
+ self.filter.get("nsdInfoId", ""):
+ raise NsdmBadRequestException("Notification Filter should contain"
+ " either nsdId or nsdInfoId")
+ if self.filter and \
+ self.filter.get("pnfdId", "") and \
+ self.filter.get("pnfdInfoIds", ""):
+ raise NsdmBadRequestException("Notification Filter should contain"
+ " either pnfdId or pnfdInfoIds")
+
+ def check_valid_auth_info(self):
+ logger.debug("Create Subscription --> Validating Auth "
+ "details if provided... ")
+ if self.authentication.get("paramsBasic", {}) and \
+ const.BASIC not in self.authentication.get("authType", ''):
+ raise NsdmBadRequestException('Auth type should be ' + const.BASIC)
+ if self.authentication.get("paramsOauth2ClientCredentials", {}) and \
+ const.OAUTH2_CLIENT_CREDENTIALS not in \
+ self.authentication.get("authType", ''):
+ raise NsdmBadRequestException('Auth type should '
+ 'be ' + const.OAUTH2_CLIENT_CREDENTIALS)
+ if const.BASIC in self.authentication.get("authType", '') and \
+ "paramsBasic" in self.authentication.keys() and \
+ not is_filter_type_equal(PARAMSBASICKEYS,
+ self.authentication.
+ get("paramsBasic").keys()):
+ raise NsdmBadRequestException('userName and password needed '
+ 'for ' + const.BASIC)
+ if const.OAUTH2_CLIENT_CREDENTIALS in \
+ self.authentication.get("authType", '') and \
+ "paramsOauth2ClientCredentials" in \
+ self.authentication.keys() and \
+ not is_filter_type_equal(PARAMSOAUTH2CLIENTCREDENTIALSKEYS,
+ self.authentication.
+ get("paramsOauth2ClientCredentials")
+ .keys()):
+ raise NsdmBadRequestException('clientId, clientPassword and '
+ 'tokenEndpoint required '
+ 'for ' + const.OAUTH2_CLIENT_CREDENTIALS)
+
+ def check_filter_exists(self, subscription):
+ for filter_type in const.NSDM_NOTIFICATION_FILTERS:
+ if not is_filter_type_equal(self.filter.get(filter_type, []),
+ ast.literal_eval(
+ getattr(subscription,
+ filter_type))):
+ return False
+ return True
+
+ def check_valid(self):
+ logger.debug("Create Subscription --> Checking DB if "
+ "same subscription exists already exists... ")
+ subscriptions = \
+ NsdmSubscriptionModel.objects.filter(
+ callback_uri=self.callback_uri)
+ if not subscriptions.exists():
+ return
+ for subscription in subscriptions:
+ if self.check_filter_exists(subscription):
+ raise NsdmDuplicateSubscriptionException(
+ "Already Subscription exists with the "
+ "same callbackUri and filter")
+
+ def save_db(self):
+ logger.debug("Create Subscription --> Saving the subscription "
+ "%s to the database" % self.subscription_id)
+ links = {
+ "self": {
+ "href":
+ const.NSDM_SUBSCRIPTION_ROOT_URI + self.subscription_id
+ }
+ }
+ subscription_save_db = {
+ "subscriptionid": self.subscription_id,
+ "callback_uri": self.callback_uri,
+ "auth_info": self.authentication,
+ "links": json.dumps(links)
+ }
+ for filter_type in const.NSDM_NOTIFICATION_FILTERS:
+ subscription_save_db[filter_type] = json.dumps(
+ list(set(self.filter.get(filter_type, []))))
+ NsdmSubscriptionModel.objects.create(**subscription_save_db)
+ logger.debug('Create Subscription[%s] success', self.subscription_id)
diff --git a/catalog/packages/const.py b/catalog/packages/const.py
index e942ffdf..522cb859 100755
--- a/catalog/packages/const.py
+++ b/catalog/packages/const.py
@@ -26,3 +26,22 @@ OAUTH2_CLIENT_CREDENTIALS = "OAUTH2_CLIENT_CREDENTIALS"
NOTIFICATION_TYPES = ["VnfPackageOnboardingNotification", "VnfPackageChangeNotification"]
VNFPKG_SUBSCRIPTION_ROOT_URI = "api/vnfpkgm/v1/subscriptions/"
+
+NSDM_SUBSCRIPTION_ROOT_URI = "api/nsd/v1/subscriptions/"
+
+NSDM_NOTIFICATION_FILTERS = ["notificationTypes", "nsdInfoId", "nsdName",
+ "nsdId", "nsdVersion", "nsdDesigner",
+ "nsdInvariantId", "vnfPkgIds", "pnfdInfoIds",
+ "nestedNsdInfoIds", "nsdOnboardingState",
+ "nsdOperationalState", "nsdUsageState",
+ "pnfdId", "pnfdName", "pnfdVersion",
+ "pnfdProvider", "pnfdInvariantId",
+ "pnfdOnboardingState", "pnfdUsageState"]
+
+NSDM_NOTIFICATION_TYPES = ["NsdOnBoardingNotification",
+ "NsdOnboardingFailureNotification",
+ "NsdChangeNotification",
+ "NsdDeletionNotification",
+ "PnfdOnBoardingNotification",
+ "PnfdOnBoardingFailureNotification",
+ "PnfdDeletionNotification"]
diff --git a/catalog/packages/serializers/nsdm_filter_data.py b/catalog/packages/serializers/nsdm_filter_data.py
new file mode 100644
index 00000000..d5a08910
--- /dev/null
+++ b/catalog/packages/serializers/nsdm_filter_data.py
@@ -0,0 +1,124 @@
+# 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 NSDM_NOTIFICATION_TYPES
+
+
+class NsdmNotificationsFilter(serializers.Serializer):
+ notificationTypes = serializers.ListField(
+ child=serializers.ChoiceField(
+ required=True,
+ choices=NSDM_NOTIFICATION_TYPES),
+ help_text="Match particular notification types",
+ allow_null=False,
+ required=False)
+ nsdInfoId = serializers.ListField(
+ child=serializers.UUIDField(),
+ help_text="Match NS packages with particular nsdInfoIds",
+ allow_null=False,
+ required=False)
+ nsdId = serializers.ListField(
+ child=serializers.UUIDField(),
+ help_text="Match NS Packages with particular nsdIds",
+ allow_null=False,
+ required=False)
+ nsdName = serializers.ListField(
+ child=serializers.CharField(max_length=255, required=True),
+ help_text="Match NS Packages with particular nsdNames",
+ allow_null=False,
+ required=False)
+ nsdVersion = serializers.ListField(
+ child=serializers.CharField(max_length=255, required=True),
+ help_text="match NS packages that belong to certain nsdversion",
+ required=False,
+ allow_null=False)
+ nsdInvariantId = serializers.ListField(
+ child=serializers.UUIDField(),
+ help_text="Match NS Packages with particular nsdInvariantIds",
+ allow_null=False,
+ required=False)
+ vnfPkgIds = serializers.ListField(
+ child=serializers.UUIDField(),
+ help_text="Match NS Packages that has VNF PackageIds",
+ allow_null=False,
+ required=False)
+ nestedNsdInfoIds = serializers.ListField(
+ child=serializers.UUIDField(),
+ help_text="Match NS Packages with particular nsdInvariantIds",
+ allow_null=False,
+ required=False)
+ nsdOnboardingState = serializers.ListField(
+ child=serializers.ChoiceField(required=True,
+ choices=['CREATED', 'UPLOADING',
+ 'PROCESSING', 'ONBOARDED']),
+ help_text="Match NS Packages with particular NS Onboarding State",
+ allow_null=False,
+ required=False)
+ nsdOperationalState = serializers.ListField(
+ child=serializers.ChoiceField(required=True,
+ choices=['ENABLED', 'DISABLED']),
+ help_text="Match NS Packages with particular NS Operational State",
+ allow_null=False,
+ required=False)
+ nsdUsageState = serializers.ListField(
+ child=serializers.ChoiceField(required=True,
+ choices=['IN_USE', 'NOT_IN_USE']),
+ help_text="Match NS Packages with particular NS Usage State",
+ allow_null=False,
+ required=False)
+ pnfdInfoIds = serializers.ListField(
+ child=serializers.UUIDField(),
+ help_text="Match PF packages with particular pnfdInfoIds",
+ allow_null=False,
+ required=False)
+ pnfdId = serializers.ListField(
+ child=serializers.UUIDField(),
+ help_text="Match PF packages with particular pnfdInfoIds",
+ allow_null=False,
+ required=False)
+ pnfdName = serializers.ListField(
+ child=serializers.CharField(max_length=255, required=True),
+ help_text="Match PF Packages with particular pnfdNames",
+ allow_null=False,
+ required=False)
+ pnfdVersion = serializers.ListField(
+ child=serializers.CharField(max_length=255, required=True),
+ help_text="match PF packages that belong to certain pnfd version",
+ required=False,
+ allow_null=False)
+ pnfdProvider = serializers.ListField(
+ child=serializers.CharField(max_length=255, required=True),
+ help_text="Match PF Packages with particular pnfdProvider",
+ allow_null=False,
+ required=False)
+ pnfdInvariantId = serializers.ListField(
+ child=serializers.UUIDField(),
+ help_text="Match PF Packages with particular pnfdInvariantIds",
+ allow_null=False,
+ required=False)
+ pnfdOnboardingState = serializers.ListField(
+ child=serializers.ChoiceField(required=True,
+ choices=['CREATED', 'UPLOADING',
+ 'PROCESSING', 'ONBOARDED']),
+ help_text="Match PF Packages with particular PNF Onboarding State ",
+ allow_null=False,
+ required=False)
+ pnfdUsageState = serializers.ListField(
+ child=serializers.ChoiceField(
+ required=True, choices=['IN_USE', 'NOT_IN_USE']),
+ help_text="Match PF Packages with particular PNF usage State",
+ allow_null=False,
+ required=False)
diff --git a/catalog/packages/serializers/nsdm_subscription.py b/catalog/packages/serializers/nsdm_subscription.py
new file mode 100644
index 00000000..c96c0296
--- /dev/null
+++ b/catalog/packages/serializers/nsdm_subscription.py
@@ -0,0 +1,75 @@
+# 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 link import LinkSerializer
+from subscription_auth_data import SubscriptionAuthenticationSerializer
+from nsdm_filter_data import NsdmNotificationsFilter
+
+
+class NsdmSubscriptionLinkSerializer(serializers.Serializer):
+ self = LinkSerializer(
+ help_text="Links to resources related to this resource.",
+ required=True)
+
+
+class NsdmSubscriptionSerializer(serializers.Serializer):
+ id = serializers.CharField(
+ help_text="Identifier of this subscription resource.",
+ max_length=255,
+ required=True,
+ allow_null=False)
+ callbackUri = serializers.CharField(
+ help_text="The URI of the endpoint to send the notification to.",
+ max_length=255,
+ required=True,
+ allow_null=False)
+ filter = NsdmNotificationsFilter(
+ help_text="Filter settings for this subscription, to define the "
+ "of all notifications this subscription relates to.",
+ required=False)
+ _links = NsdmSubscriptionLinkSerializer(
+ help_text="Links to resources related to this resource.",
+ required=True)
+
+
+class NsdmSubscriptionsSerializer(serializers.ListSerializer):
+ child = NsdmSubscriptionSerializer()
+
+
+class NsdmSubscriptionIdSerializer(serializers.Serializer):
+ subscription_id = serializers.UUIDField(
+ help_text="Identifier of this subscription resource.",
+ required=True,
+ allow_null=False)
+
+
+class NsdmSubscriptionRequestSerializer(serializers.Serializer):
+ callbackUri = serializers.CharField(
+ help_text="The URI of the endpoint to send the notification to.",
+ required=True,
+ allow_null=False)
+ filter = NsdmNotificationsFilter(
+ help_text="Filter settings for the subscription,"
+ " to define the subset of all "
+ "notifications this subscription relates to.",
+ required=False,
+ allow_null=True)
+ authentication = SubscriptionAuthenticationSerializer(
+ help_text="Authentication parameters to configure"
+ " the use of Authorization when sending "
+ "notifications corresponding to this subscription.",
+ required=False,
+ allow_null=True)
diff --git a/catalog/packages/serializers/response.py b/catalog/packages/serializers/response.py
index 64740780..449d0622 100644
--- a/catalog/packages/serializers/response.py
+++ b/catalog/packages/serializers/response.py
@@ -26,3 +26,6 @@ class ProblemDetailsSerializer(serializers.Serializer):
"specification or by an implementation.",
required=False,
allow_null=True)
+
+ class Meta:
+ ref_name = 'SUBSCRIPTION_ProblemDetailsSerializer'
diff --git a/catalog/packages/tests/test_nsdm_subscription.py b/catalog/packages/tests/test_nsdm_subscription.py
new file mode 100644
index 00000000..fb355552
--- /dev/null
+++ b/catalog/packages/tests/test_nsdm_subscription.py
@@ -0,0 +1,340 @@
+# 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 mock
+import uuid
+from django.test import TestCase
+from rest_framework.test import APIClient
+from rest_framework import status
+
+from catalog.packages.biz.nsdm_subscription import NsdmSubscription
+from catalog.pub.database.models import NsdmSubscriptionModel
+
+
+class TestNsdmSubscription(TestCase):
+
+ def setUp(self):
+ self.client = APIClient()
+ NsdmSubscriptionModel.objects.all().delete()
+ self.subscription_id = str(uuid.uuid4())
+ self.subscription = {
+ "callbackUri": "http://callbackuri.com",
+ "authentication": {
+ "authType": ["BASIC"],
+ "paramsBasic": {
+ "userName": "username",
+ "password": "password"
+ }
+ }
+ }
+ self.links = {
+ "self": {
+ "href": "/api/v1/subscriptions/" + self.subscription_id
+ }
+ }
+ self.test_subscription = {
+ "callbackUri": "http://callbackuri.com",
+ "id": self.subscription_id,
+ "filter": {
+ "notificationTypes": [
+ "NsdOnBoardingNotification"
+ ],
+ "nsdInfoId": [],
+ "nsdId": [],
+ "nsdName": [],
+ "nsdVersion": [],
+ "nsdInvariantId": [],
+ "vnfPkgIds": [],
+ "nestedNsdInfoIds": [],
+ "nsdOnboardingState": [],
+ "nsdOperationalState": [],
+ "nsdUsageState": [],
+ "pnfdInfoIds": [],
+ "pnfdId": [],
+ "pnfdName": [],
+ "pnfdVersion": [],
+ "pnfdProvider": [],
+ "pnfdInvariantId": [],
+ "pnfdOnboardingState": [],
+ "pnfdUsageState": []
+ },
+ "_links": self.links,
+ }
+
+ def tearDown(self):
+ pass
+
+ @mock.patch("requests.get")
+ @mock.patch.object(uuid, 'uuid4')
+ def test_nsdm_subscribe_notification(self, mock_uuid4, mock_requests):
+ temp_uuid = str(uuid.uuid4())
+ mock_requests.return_value.status_code = 204
+ mock_requests.get.return_value.status_code = 204
+ mock_uuid4.return_value = temp_uuid
+ response = self.client.post("/api/nsd/v1/subscriptions",
+ data=self.subscription, format='json')
+ self.assertEqual(201, response.status_code)
+ self.assertEqual(self.subscription["callbackUri"],
+ response.data["callbackUri"])
+ self.assertEqual(temp_uuid, response.data["id"])
+
+ @mock.patch("requests.get")
+ @mock.patch.object(uuid, 'uuid4')
+ def test_nsdm_subscribe_callbackFailure(self, mock_uuid4, mock_requests):
+ temp_uuid = str(uuid.uuid4())
+ mock_requests.return_value.status_code = 500
+ mock_requests.get.return_value.status_code = 500
+ mock_uuid4.return_value = temp_uuid
+ expected_data = {
+ 'status': 500,
+ 'detail': "callbackUri http://callbackuri.com didn't"
+ " return 204 statuscode.",
+ 'title': 'Creating Subscription Failed!'
+ }
+ response = self.client.post("/api/nsd/v1/subscriptions",
+ data=self.subscription, format='json')
+ self.assertEqual(500, response.status_code)
+ self.assertEqual(expected_data, response.data)
+
+ @mock.patch("requests.get")
+ def test_nsdm_second_subscription(self, mock_requests):
+ mock_requests.return_value.status_code = 204
+ mock_requests.get.return_value.status_code = 204
+ response = self.client.post("/api/nsd/v1/subscriptions",
+ data=self.subscription, format='json')
+ self.assertEqual(201, response.status_code)
+ self.assertEqual(self.subscription["callbackUri"],
+ response.data["callbackUri"])
+ dummy_subscription = {
+ "callbackUri": "http://callbackuri.com",
+ "authentication": {
+ "authType": ["BASIC"],
+ "paramsBasic": {
+ "userName": "username",
+ "password": "password"
+ }
+ },
+ "filter": {
+ "nsdId": ["b632bddc-bccd-4180-bd8d-4e8a9578eff7"],
+ }
+ }
+ response = self.client.post("/api/nsd/v1/subscriptions",
+ data=dummy_subscription, format='json')
+ self.assertEqual(201, response.status_code)
+ self.assertEqual(dummy_subscription["callbackUri"],
+ response.data["callbackUri"])
+
+ @mock.patch("requests.get")
+ def test_nsdm_duplicate_subscription(self, mock_requests):
+ mock_requests.return_value.status_code = 204
+ mock_requests.get.return_value.status_code = 204
+ response = self.client.post("/api/nsd/v1/subscriptions",
+ data=self.subscription, format='json')
+ self.assertEqual(201, response.status_code)
+ self.assertEqual(self.subscription["callbackUri"],
+ response.data["callbackUri"])
+ expected_data = {
+ 'status': 303,
+ 'detail': 'Already Subscription exists with'
+ ' the same callbackUri and filter',
+ 'title': 'Creating Subscription Failed!'
+ }
+ response = self.client.post("/api/nsd/v1/subscriptions",
+ data=self.subscription, format='json')
+ self.assertEqual(303, response.status_code)
+ self.assertEqual(expected_data, response.data)
+
+ @mock.patch("requests.get")
+ def test_nsdm_bad_request(self, mock_requests):
+ dummy_subscription = {
+ "callbackUri": "http://callbackuri.com",
+ "authentication": {
+ "authType": ["BASIC"],
+ "paramsBasic": {
+ "userName": "username",
+ "password": "password"
+ }
+ },
+ "filter": {
+ "nsdId": "b632bddc-bccd-4180-bd8d-4e8a9578eff7",
+ }
+ }
+ response = self.client.post("/api/nsd/v1/subscriptions",
+ data=dummy_subscription, format='json')
+ self.assertEqual(400, response.status_code)
+
+ @mock.patch("requests.get")
+ def test_nsdm_invalid_authtype_subscription(self, mock_requests):
+ dummy_subscription = {
+ "callbackUri": "http://callbackuri.com",
+ "authentication": {
+ "authType": ["OAUTH2_CLIENT_CREDENTIALS"],
+ "paramsBasic": {
+ "userName": "username",
+ "password": "password"
+ }
+ }
+ }
+ mock_requests.return_value.status_code = 204
+ mock_requests.get.return_value.status_code = 204
+ expected_data = {
+ 'status': 400,
+ 'detail': 'Auth type should be BASIC',
+ 'title': 'Creating Subscription Failed!'
+ }
+ response = self.client.post("/api/nsd/v1/subscriptions",
+ data=dummy_subscription, format='json')
+ self.assertEqual(400, response.status_code)
+ self.assertEqual(expected_data, response.data)
+
+ @mock.patch("requests.get")
+ def test_nsdm_invalid_authtype_oauthclient_subscription(
+ self, mock_requests):
+ dummy_subscription = {
+ "callbackUri": "http://callbackuri.com",
+ "authentication": {
+ "authType": ["BASIC"],
+ "paramsOauth2ClientCredentials": {
+ "clientId": "clientId",
+ "clientPassword": "password",
+ "tokenEndpoint": "http://tokenEndpoint"
+ }
+ }
+ }
+ mock_requests.return_value.status_code = 204
+ mock_requests.get.return_value.status_code = 204
+ expected_data = {
+ 'status': 400,
+ 'detail': 'Auth type should be OAUTH2_CLIENT_CREDENTIALS',
+ 'title': 'Creating Subscription Failed!'
+ }
+ response = self.client.post("/api/nsd/v1/subscriptions",
+ data=dummy_subscription, format='json')
+ self.assertEqual(400, response.status_code)
+ self.assertEqual(expected_data, response.data)
+
+ @mock.patch("requests.get")
+ def test_nsdm_invalid_authparams_subscription(self, mock_requests):
+ dummy_subscription = {
+ "callbackUri": "http://callbackuri.com",
+ "authentication": {
+ "authType": ["BASIC"],
+ "paramsBasic": {
+ "userName": "username"
+ }
+ }
+ }
+ mock_requests.return_value.status_code = 204
+ mock_requests.get.return_value.status_code = 204
+ expected_data = {
+ 'status': 400,
+ 'detail': 'userName and password needed for BASIC',
+ 'title': 'Creating Subscription Failed!'
+ }
+ response = self.client.post("/api/nsd/v1/subscriptions",
+ data=dummy_subscription, format='json')
+ self.assertEqual(400, response.status_code)
+ self.assertEqual(expected_data, response.data)
+
+ @mock.patch("requests.get")
+ def test_nsdm_invalid_authparams_oauthclient_subscription(
+ self, mock_requests):
+ dummy_subscription = {
+ "callbackUri": "http://callbackuri.com",
+ "authentication": {
+ "authType": ["OAUTH2_CLIENT_CREDENTIALS"],
+ "paramsOauth2ClientCredentials": {
+ "clientPassword": "password",
+ "tokenEndpoint": "http://tokenEndpoint"
+ }
+ }
+ }
+ mock_requests.return_value.status_code = 204
+ mock_requests.get.return_value.status_code = 204
+ expected_data = {
+ 'status': 400,
+ 'detail': 'clientId, clientPassword and tokenEndpoint'
+ ' required for OAUTH2_CLIENT_CREDENTIALS',
+ 'title': 'Creating Subscription Failed!'
+ }
+ response = self.client.post("/api/nsd/v1/subscriptions",
+ data=dummy_subscription, format='json')
+ self.assertEqual(400, response.status_code)
+ self.assertEqual(expected_data, response.data)
+
+ @mock.patch("requests.get")
+ def test_nsdm_invalid_filter_subscription(self, mock_requests):
+ dummy_subscription = {
+ "callbackUri": "http://callbackuri.com",
+ "authentication": {
+ "authType": ["BASIC"],
+ "paramsBasic": {
+ "userName": "username",
+ "password": "password"
+ }
+ },
+ "filter": {
+ "nsdId": ["b632bddc-bccd-4180-bd8d-4e8a9578eff7"],
+ "nsdInfoId": ["d0ea5ec3-0b98-438a-9bea-488230cff174"]
+ }
+ }
+ mock_requests.return_value.status_code = 204
+ mock_requests.get.return_value.status_code = 204
+ expected_data = {
+ 'status': 400,
+ 'detail': 'Notification Filter should contain'
+ ' either nsdId or nsdInfoId',
+ 'title': 'Creating Subscription Failed!'
+ }
+ response = self.client.post("/api/nsd/v1/subscriptions",
+ data=dummy_subscription, format='json')
+ self.assertEqual(400, response.status_code)
+ self.assertEqual(expected_data, response.data)
+
+ @mock.patch("requests.get")
+ def test_nsdm_invalid_filter_pnfd_subscription(self, mock_requests):
+ dummy_subscription = {
+ "callbackUri": "http://callbackuri.com",
+ "authentication": {
+ "authType": ["BASIC"],
+ "paramsBasic": {
+ "userName": "username",
+ "password": "password"
+ }
+ },
+ "filter": {
+ "pnfdId": ["b632bddc-bccd-4180-bd8d-4e8a9578eff7"],
+ "pnfdInfoIds": ["d0ea5ec3-0b98-438a-9bea-488230cff174"]
+ }
+ }
+ mock_requests.return_value.status_code = 204
+ mock_requests.get.return_value.status_code = 204
+ expected_data = {
+ 'status': 400,
+ 'detail': 'Notification Filter should contain'
+ ' either pnfdId or pnfdInfoIds',
+ 'title': 'Creating Subscription Failed!'
+ }
+ response = self.client.post("/api/nsd/v1/subscriptions",
+ data=dummy_subscription, format='json')
+ self.assertEqual(400, response.status_code)
+ self.assertEqual(expected_data, response.data)
+
+ @mock.patch.object(NsdmSubscription, 'create')
+ def test_nsdmsubscription_create_when_catch_exception(self, mock_create):
+ mock_create.side_effect = TypeError("Unicode type")
+ response = self.client.post('/api/nsd/v1/subscriptions',
+ data=self.subscription, format='json')
+ self.assertEqual(response.status_code,
+ status.HTTP_500_INTERNAL_SERVER_ERROR)
diff --git a/catalog/packages/urls.py b/catalog/packages/urls.py
index 1c20fc20..0add1d53 100755
--- a/catalog/packages/urls.py
+++ b/catalog/packages/urls.py
@@ -16,7 +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 SubscriptionsView
-from catalog.packages.views import catalog_views, ns_descriptor_views, pnf_descriptor_views
+from catalog.packages.views import catalog_views, ns_descriptor_views, pnf_descriptor_views, nsdm_subscription_views
urlpatterns = [
@@ -36,8 +36,8 @@ urlpatterns = [
url(r'^api/nsd/v1/ns_descriptors$', ns_descriptor_views.ns_descriptors_rc, name='ns_descriptors_rc'),
url(r'^api/nsd/v1/ns_descriptors/(?P<nsdInfoId>[0-9a-zA-Z\-\_]+)$', ns_descriptor_views.ns_info_rd, name='ns_info_rd'),
url(r'^api/nsd/v1/ns_descriptors/(?P<nsdInfoId>[0-9a-zA-Z\-\_]+)/nsd_content$', ns_descriptor_views.nsd_content_ru, name='nsd_content_ru'),
- # url(r'^api/nsd/v1/subscriptions', nsd_subscriptions.as_view(), name='subscriptions_rc'),
- # url(r'^api/nsd/v1/subscriptions/(?P<subscriptionId>[0-9a-zA-Z\-\_]+)$', nsd_subscription.as_view(), name='subscription_rd'),
+ url(r'^api/nsd/v1/subscriptions$', nsdm_subscription_views.nsd_subscription_rc, name='nsd_subscription_rc'),
+ # url(r'^api/nsd/v1/subscriptions/(?P<subscriptionId>[0-9a-zA-Z\-\_]+)$', nsdm_subscription_views.nsd_subscription_rd, name='nsd_subscription_rd'),
# ETSI SOL005 PNFD
url(r'^api/nsd/v1/pnf_descriptors$', pnf_descriptor_views.pnf_descriptors_rc, name='pnf_descriptors_rc'),
diff --git a/catalog/packages/views/nsdm_subscription_views.py b/catalog/packages/views/nsdm_subscription_views.py
new file mode 100644
index 00000000..6869d782
--- /dev/null
+++ b/catalog/packages/views/nsdm_subscription_views.py
@@ -0,0 +1,105 @@
+# 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
+import traceback
+
+from drf_yasg.utils import swagger_auto_schema
+from rest_framework import status
+from rest_framework.decorators import api_view
+from rest_framework.response import Response
+
+from catalog.packages.serializers.nsdm_subscription import \
+ NsdmSubscriptionSerializer, \
+ NsdmSubscriptionRequestSerializer
+from catalog.packages.serializers.response \
+ import ProblemDetailsSerializer
+from catalog.pub.exceptions import \
+ NsdmBadRequestException, NsdmDuplicateSubscriptionException
+from catalog.packages.biz.nsdm_subscription import NsdmSubscription
+
+
+logger = logging.getLogger(__name__)
+
+
+def validate_data(data, serializer):
+ serialized_data = serializer(data=data)
+ if not serialized_data.is_valid():
+ logger.error('Data validation failed.')
+ raise NsdmBadRequestException(serialized_data.errors)
+ return serialized_data
+
+
+def get_problem_details_serializer(title, status_code, error_message):
+ problem_details = {
+ "title": title,
+ "status": status_code,
+ "detail": error_message
+ }
+ problem_details_serializer = ProblemDetailsSerializer(data=problem_details)
+ problem_details_serializer.is_valid()
+ return problem_details_serializer
+
+
+@swagger_auto_schema(
+ method='POST',
+ operation_description="Create Subscription for NSD Management",
+ request_body=NsdmSubscriptionRequestSerializer(),
+ responses={
+ status.HTTP_201_CREATED: NsdmSubscriptionSerializer,
+ status.HTTP_303_SEE_OTHER: ProblemDetailsSerializer(),
+ status.HTTP_400_BAD_REQUEST: ProblemDetailsSerializer(),
+ status.HTTP_500_INTERNAL_SERVER_ERROR: ProblemDetailsSerializer()
+ }
+)
+@api_view(http_method_names=['POST'])
+def nsd_subscription_rc(request):
+ if request.method == 'POST':
+ logger.debug("SubscribeNotification--post::> %s" % request.data)
+ try:
+ title = 'Creating Subscription Failed!'
+ nsdm_subscription_request = \
+ validate_data(request.data,
+ NsdmSubscriptionRequestSerializer)
+ subscription = NsdmSubscription().create(
+ nsdm_subscription_request.data)
+ subscription_resp = validate_data(subscription,
+ NsdmSubscriptionSerializer)
+ return Response(data=subscription_resp.data,
+ status=status.HTTP_201_CREATED)
+ except NsdmDuplicateSubscriptionException as e:
+ logger.error(e.message)
+ problem_details_serializer = \
+ get_problem_details_serializer(title,
+ status.HTTP_303_SEE_OTHER,
+ e.message)
+ return Response(data=problem_details_serializer.data,
+ status=status.HTTP_303_SEE_OTHER)
+ except NsdmBadRequestException as e:
+ problem_details_serializer = \
+ get_problem_details_serializer(title,
+ status.HTTP_400_BAD_REQUEST,
+ e.message)
+ return Response(data=problem_details_serializer.data,
+ status=status.HTTP_400_BAD_REQUEST)
+ except Exception as e:
+ logger.error(e.message)
+ logger.error(traceback.format_exc())
+ problem_details_serializer = \
+ get_problem_details_serializer(
+ title,
+ status.HTTP_500_INTERNAL_SERVER_ERROR,
+ e.message)
+ 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 c1e2a8ae..ef95dea9 100644
--- a/catalog/pub/database/models.py
+++ b/catalog/pub/database/models.py
@@ -142,6 +142,40 @@ class JobStatusModel(models.Model):
return json.dumps(dict([(attr, getattr(self, attr)) for attr in [f.name for f in self._meta.fields]]))
+class NsdmSubscriptionModel(models.Model):
+ subscriptionid = models.CharField(db_column='SUBSCRIPTIONID', max_length=255, primary_key=True)
+ notificationTypes = models.TextField(db_column='NOTIFICATIONTYPES', null=True)
+ auth_info = models.TextField(db_column='AUTHINFO', null=True)
+ callback_uri = models.CharField(db_column='CALLBACKURI', max_length=255)
+ nsdInfoId = models.TextField(db_column='NSDINFOID', null=True)
+ nsdId = models.TextField(db_column='NSDID', null=True)
+ nsdName = models.TextField(db_column='NSDNAME', null=True)
+ nsdVersion = models.TextField(db_column='NSDVERSION', null=True)
+ nsdDesigner = models.TextField(db_column='NSDDESIGNER', null=True)
+ nsdInvariantId = models.TextField(db_column='NSDINVARIANTID', null=True)
+ vnfPkgIds = models.TextField(db_column='VNFPKGIDS', null=True)
+ pnfdInfoIds = models.TextField(db_column='PNFDINFOIDS', null=True)
+ nestedNsdInfoIds = models.TextField(db_column='NESTEDNSDINFOIDS', null=True)
+ nsdOnboardingState = models.TextField(db_column='NSDONBOARDINGSTATE', null=True)
+ nsdOperationalState = models.TextField(db_column='NSDOPERATIONALSTATE', null=True)
+ nsdUsageState = models.TextField(db_column='NSDUSAGESTATE', null=True)
+ pnfdId = models.TextField(db_column='PNFDID', null=True)
+ pnfdName = models.TextField(db_column='PNFDNAME', null=True)
+ pnfdVersion = models.TextField(db_column='PNFDVERSION', null=True)
+ pnfdProvider = models.TextField(db_column='PNFDPROVIDER', null=True)
+ pnfdInvariantId = models.TextField(db_column='PNFDINVARIANTID', null=True)
+ pnfdOnboardingState = models.TextField(db_column='PNFDONBOARDINGSTATE', null=True)
+ pnfdUsageState = models.TextField(db_column='PNFDUSAGESTATE', null=True)
+ links = models.TextField(db_column='LINKS')
+
+ class Meta:
+ db_table = 'CATALOG_NSDM_SUBSCRIPTION'
+
+ 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)
diff --git a/catalog/pub/exceptions.py b/catalog/pub/exceptions.py
index 7f348d65..81379d7b 100644
--- a/catalog/pub/exceptions.py
+++ b/catalog/pub/exceptions.py
@@ -27,3 +27,11 @@ class VnfPkgSubscriptionException(CatalogException):
class VnfPkgDuplicateSubscriptionException(CatalogException):
pass
+
+
+class NsdmBadRequestException(CatalogException):
+ pass
+
+
+class NsdmDuplicateSubscriptionException(CatalogException):
+ pass