summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lcm/lcm/nf/const.py29
-rw-r--r--lcm/lcm/nf/serializers/lccn_filter_data.py29
-rw-r--r--lcm/lcm/nf/serializers/notification_types.py128
-rw-r--r--lcm/lcm/nf/serializers/vnf_lcm_op_occ.py4
-rw-r--r--lcm/lcm/nf/tests/test_query_vnf_lcm_op.py22
-rw-r--r--lcm/lcm/pub/utils/notificationsutil.py63
-rw-r--r--lcm/lcm/pub/utils/tests.py79
7 files changed, 315 insertions, 39 deletions
diff --git a/lcm/lcm/nf/const.py b/lcm/lcm/nf/const.py
index 37205c5a..8be83896 100644
--- a/lcm/lcm/nf/const.py
+++ b/lcm/lcm/nf/const.py
@@ -35,6 +35,35 @@ OAUTH2_CLIENT_CREDENTIALS = "OAUTH2_CLIENT_CREDENTIALS"
LCCNNOTIFICATION = "VnfLcmOperationOccurrenceNotification"
+NOTIFICATION_TYPES = [
+ "VnfLcmOperationOccurrenceNotification",
+ "VnfIdentifierCreationNotification",
+ "VnfIdentifierDeletionNotification"
+]
+
+LCM_OPERATION_TYPES = [
+ "INSTANTIATE",
+ "SCALE",
+ "SCALE_TO_LEVEL",
+ "CHANGE_FLAVOUR",
+ "TERMINATE",
+ "HEAL",
+ "OPERATE",
+ "CHANGE_EXT_CONN",
+ "MODIFY_INFO"
+]
+
+LCM_OPERATION_STATE_TYPES = [
+ "STARTING",
+ "PROCESSING",
+ "COMPLETED",
+ "FAILED_TEMP",
+ "FAILED",
+ "ROLLING_BACK",
+ "ROLLED_BACK"
+]
+
+
inst_req_data = {
"flavourId": "flavour_1",
"instantiationLevelId": "instantiationLevel_1",
diff --git a/lcm/lcm/nf/serializers/lccn_filter_data.py b/lcm/lcm/nf/serializers/lccn_filter_data.py
index 547ba094..b016a4f0 100644
--- a/lcm/lcm/nf/serializers/lccn_filter_data.py
+++ b/lcm/lcm/nf/serializers/lccn_filter_data.py
@@ -15,34 +15,7 @@
from rest_framework import serializers
from vnf_instance_subscription_filter import VnfInstanceSubscriptionFilter
-
-
-NOTIFICATION_TYPES = [
- "VnfLcmOperationOccurrenceNotification",
- "VnfIdentifierCreationNotification",
- "VnfIdentifierDeletionNotification"]
-
-LCM_OPERATION_TYPES = [
- "INSTANTIATE",
- "SCALE",
- "SCALE_TO_LEVEL",
- "CHANGE_FLAVOUR",
- "TERMINATE",
- "HEAL",
- "OPERATE",
- "CHANGE_EXT_CONN",
- "MODIFY_INFO"
-]
-
-LCM_OPERATION_STATE_TYPES = [
- "STARTING",
- "PROCESSING",
- "COMPLETED",
- "FAILED_TEMP",
- "FAILED",
- "ROLLING_BACK",
- "ROLLED_BACK"
-]
+from lcm.nf.const import NOTIFICATION_TYPES, LCM_OPERATION_TYPES, LCM_OPERATION_STATE_TYPES
class LifeCycleChangeNotificationsFilter(serializers.Serializer):
diff --git a/lcm/lcm/nf/serializers/notification_types.py b/lcm/lcm/nf/serializers/notification_types.py
new file mode 100644
index 00000000..5ee5eb03
--- /dev/null
+++ b/lcm/lcm/nf/serializers/notification_types.py
@@ -0,0 +1,128 @@
+# Copyright (C) 2018 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 affected_vnfcs import AffectedVnfcsSerializer
+from affected_vls import AffectedVLsSerializer
+from affected_storages import AffectedStoragesSerializer
+from lcm.nf.const import LCM_OPERATION_TYPES, LCM_OPERATION_STATE_TYPES
+from link import LinkSerializer
+from response import ProblemDetailsSerializer
+from ext_virtual_link_info import ExtVirtualLinkInfoSerializer
+from vnf_info_modifications import VnfInfoModificationsSerializer
+
+
+class LinksSerializer(serializers.Serializer):
+ vnfInstance = LinkSerializer(
+ help_text="Link to the resource representing the VNF instance to "
+ "which the notified change applies.",
+ required=True,
+ allow_null=False)
+ subscription = LinkSerializer(
+ help_text="Link to the related subscription.",
+ required=True,
+ allow_null=False)
+ vnfLcmOpOcc = LinkSerializer(
+ help_text="Link to the VNF lifecycle management operation"
+ "occurrence that this notification is related to. Shall be"
+ "present if there is a related lifecycle operation occurance.",
+ required=False,
+ allow_null=False)
+
+
+class VnfLcmOperationOccurrenceNotification(serializers.Serializer):
+ id = serializers.CharField(
+ help_text="Identifier of this notification",
+ max_length=255,
+ required=True,
+ allow_null=False)
+ notificationType = serializers.CharField(
+ help_text="Type of the notification",
+ max_length=50,
+ required=True,
+ allow_null=False)
+ subscriptionId = serializers.CharField(
+ help_text="Identifier for the subscription",
+ required=False)
+ timeStamp = serializers.CharField(
+ help_text="Date-time of the generation of the notification.",
+ required=True)
+ notificationStatus = serializers.ChoiceField(
+ help_text="Indicates whether this notification reports about the start"
+ "of a lifecycle operation or the result of a lifecycle"
+ "operation",
+ choices=["START", "RESULT"],
+ required=True)
+ operationState = serializers.ChoiceField(
+ choices=LCM_OPERATION_STATE_TYPES,
+ help_text="The state of the VNF LCM operation occurrence. ",
+ required=True)
+ vnfInstanceId = serializers.CharField(
+ help_text="The identifier of the VNF instance affected. ",
+ required=True)
+ operation = serializers.ChoiceField(
+ help_text="The lifecycle management operation.",
+ required=True,
+ choices=LCM_OPERATION_TYPES)
+ isAutomaticInvocation = serializers.BooleanField(
+ help_text="Set to true if this VNF LCM operation occurrence has"
+ "been triggered by an automated procedure inside the"
+ "VNFM. Otherwise False",
+ required=True)
+ vnfLcmOpOccId = serializers.CharField(
+ help_text="The identifier of the VNF lifecycle management"
+ "operation occurrence associated to the notification.",
+ required=True)
+ affectedVnfcs = AffectedVnfcsSerializer(
+ help_text="Information about VNFC instances that were affected " +
+ "during the lifecycle operation.",
+ required=False,
+ many=True
+ )
+ affectedVirtualLinks = AffectedVLsSerializer(
+ help_text="Information about VL instances that were affected " +
+ "during the lifecycle operation. ",
+ required=False,
+ many=True
+ )
+ affectedVirtualStorages = AffectedStoragesSerializer(
+ help_text="Information about virtualised storage instances that " +
+ "were affected during the lifecycle operation",
+ required=False,
+ many=True
+ )
+ changedInfo = VnfInfoModificationsSerializer(
+ help_text="Information about the changed VNF instance information, " +
+ "including VNF configurable properties",
+ required=False,
+ allow_null=True)
+ changedExtConnectivity = ExtVirtualLinkInfoSerializer(
+ help_text="Information about changed external connectivity, if this " +
+ "notification represents the result of a lifecycle operation occurrence. " +
+ "Shall be present if the 'notificationStatus' is set to 'RESULT' and the " +
+ "'operation' is set to 'CHANGE_EXT_CONN'. Shall be absent otherwise.",
+ many=True,
+ required=False,
+ allow_null=True)
+ error = ProblemDetailsSerializer(
+ help_text="If 'operationState' is 'FAILED_TEMP' or 'FAILED' or " +
+ "'PROCESSING' or 'ROLLING_BACK' and previous value of 'operationState' " +
+ "was 'FAILED_TEMP' this attribute shall be present ",
+ allow_null=True,
+ required=False
+ )
+ _links = LinksSerializer(
+ help_text="Links to resources related to this resource.",
+ required=True)
diff --git a/lcm/lcm/nf/serializers/vnf_lcm_op_occ.py b/lcm/lcm/nf/serializers/vnf_lcm_op_occ.py
index f2ef664d..a2b50196 100644
--- a/lcm/lcm/nf/serializers/vnf_lcm_op_occ.py
+++ b/lcm/lcm/nf/serializers/vnf_lcm_op_occ.py
@@ -18,6 +18,7 @@ from rest_framework import serializers
from affected_vnfcs import AffectedVnfcsSerializer
from affected_vls import AffectedVLsSerializer
from affected_storages import AffectedStoragesSerializer
+from link import LinkSerializer
from response import ProblemDetailsSerializer
from ext_virtual_link_info import ExtVirtualLinkInfoSerializer
from vnf_info_modifications import VnfInfoModificationsSerializer
@@ -68,9 +69,8 @@ class ResourceChangesSerializer(serializers.Serializer):
class LcmOpLinkSerializer(serializers.Serializer):
- self = serializers.CharField(
+ self = LinkSerializer(
help_text="URI of this resource.",
- max_length=255,
required=True,
allow_null=False)
vnfInstance = serializers.CharField(
diff --git a/lcm/lcm/nf/tests/test_query_vnf_lcm_op.py b/lcm/lcm/nf/tests/test_query_vnf_lcm_op.py
index feabe534..b08d4b05 100644
--- a/lcm/lcm/nf/tests/test_query_vnf_lcm_op.py
+++ b/lcm/lcm/nf/tests/test_query_vnf_lcm_op.py
@@ -42,7 +42,9 @@ class TestVNFLcmOpOccs(TestCase):
"changedInfo": None,
"changedExtConnectivity": None,
"_links": {
- "self": "demo",
+ "self": {
+ "href": "demo"
+ },
"vnfInstance": "demo"
}
}
@@ -58,7 +60,9 @@ class TestVNFLcmOpOccs(TestCase):
"isCancelPending": False,
"cancelMode": None,
"_links": {
- "self": "demo",
+ "self": {
+ "href": "demo"
+ },
"vnfInstance": "demo"
}
}]
@@ -80,7 +84,9 @@ class TestVNFLcmOpOccs(TestCase):
"changedInfo": None,
"changedExtConnectivity": None,
"_links": {
- "self": "demo",
+ "self": {
+ "href": "demo"
+ },
"vnfInstance": "demo"
}
}]
@@ -99,7 +105,7 @@ class TestVNFLcmOpOccs(TestCase):
grant_id=None, operation="SCALE", is_automatic_invocation=False,
operation_params='{}', is_cancel_pending=False, cancel_mode=None,
error=None, resource_changes=None, changed_ext_connectivity=None,
- links=json.dumps({"self": "demo", "vnfInstance": "demo"})).save()
+ links=json.dumps({"self": {"href": "demo"}, "vnfInstance": "demo"})).save()
response = self.client.get("/api/vnflcm/v1/vnf_lcm_op_occs", format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual([self.test_single_vnf_lcm_op], response.data)
@@ -122,7 +128,7 @@ class TestVNFLcmOpOccs(TestCase):
grant_id=None, operation="INSTANTIATE", is_automatic_invocation=False,
operation_params='{}', is_cancel_pending=False, cancel_mode=None,
error=None, resource_changes=None, changed_ext_connectivity=None,
- links=json.dumps({"self": "demo", "vnfInstance": "demo"})).save()
+ links=json.dumps({"self": {"href": "demo"}, "vnfInstance": "demo"})).save()
lcm_op_id = "99442b18-a5c7-11e8-998c-bf1755941f16"
VNFLcmOpOccModel(id=lcm_op_id, operation_state="STARTING",
@@ -131,7 +137,7 @@ class TestVNFLcmOpOccs(TestCase):
grant_id=None, operation="SCALE", is_automatic_invocation=False,
operation_params='{}', is_cancel_pending=False, cancel_mode=None,
error=None, resource_changes=None, changed_ext_connectivity=None,
- links=json.dumps({"self": "demo", "vnfInstance": "demo"})).save()
+ links=json.dumps({"self": {"href": "demo"}, "vnfInstance": "demo"})).save()
response = self.client.get("/api/vnflcm/v1/vnf_lcm_op_occs", format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(self.test_multiple_vnf_lcm_op, response.data)
@@ -153,7 +159,7 @@ class TestVNFLcmOpOccs(TestCase):
grant_id=None, operation="SCALE", is_automatic_invocation=False,
operation_params='{}', is_cancel_pending=False, cancel_mode=None,
error=None, resource_changes=None, changed_ext_connectivity=None,
- links=json.dumps({"self": "demo", "vnfInstance": "demo"})).save()
+ links=json.dumps({"self": {"href": "demo"}, "vnfInstance": "demo"})).save()
response = self.client.get("/api/vnflcm/v1/vnf_lcm_op_occs?exclude_default", format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(self.test_vnflcmop_with_exclude_default, response.data)
@@ -167,7 +173,7 @@ class TestVNFLcmOpOccs(TestCase):
grant_id=None, operation="SCALE", is_automatic_invocation=False,
operation_params='{}', is_cancel_pending=False, cancel_mode=None,
error=None, resource_changes=None, changed_ext_connectivity=None,
- links=json.dumps({"self": "demo", "vnfInstance": "demo"})).save()
+ links=json.dumps({"self": {"href": "demo"}, "vnfInstance": "demo"})).save()
response = self.client.get("/api/vnflcm/v1/vnf_lcm_op_occs/" + lcm_op_id, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(self.test_single_vnf_lcm_op, response.data)
diff --git a/lcm/lcm/pub/utils/notificationsutil.py b/lcm/lcm/pub/utils/notificationsutil.py
new file mode 100644
index 00000000..3af8f22a
--- /dev/null
+++ b/lcm/lcm/pub/utils/notificationsutil.py
@@ -0,0 +1,63 @@
+# Copyright (C) 2018 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 json
+import logging
+import requests
+
+from rest_framework import status
+from requests.auth import HTTPBasicAuth
+
+from lcm.nf import const
+from lcm.pub.database.models import SubscriptionModel
+
+logger = logging.getLogger(__name__)
+
+
+class NotificationsUtil(object):
+ def __init__(self):
+ pass
+
+ def send_notification(self, notification):
+ logger.info("Send Notifications to the callbackUri")
+ filters = {
+ "vnfInstanceId": "vnf_instance_filter",
+ "operationState": "operation_states",
+ "operation": "operation_types"
+ }
+ subscriptions_filter = {v + "__contains": notification[k] for k, v in filters.iteritems()}
+
+ subscriptions = SubscriptionModel.objects.filter(**subscriptions_filter)
+ if not subscriptions.exists():
+ logger.info("No subscriptions created for the filters %s" % notification)
+ return
+ logger.info("Start sending notifications")
+ for subscription in subscriptions:
+ # set subscription id
+ notification["subscriptionId"] = subscription.subscription_id
+ callbackUri = subscription.callback_uri
+ auth_info = json.loads(subscription.auth_info)
+ if auth_info["authType"] == const.OAUTH2_CLIENT_CREDENTIALS:
+ pass
+ self.post_notification(callbackUri, auth_info, notification)
+
+ def post_notification(self, callbackUri, auth_info, notification):
+ params = auth_info.get("paramsBasic", {})
+ username = params.get("userName")
+ password = params.get("password")
+ logger.info("Sending notification to %s", callbackUri)
+ resp = requests.post(callbackUri, data=notification, auth=HTTPBasicAuth(username, password))
+ if resp.status_code != status.HTTP_204_NO_CONTENT:
+ raise Exception("Unable to send the notification to %s, due to %s" % (callbackUri, resp.text))
+ return
diff --git a/lcm/lcm/pub/utils/tests.py b/lcm/lcm/pub/utils/tests.py
index 87516f07..16210db6 100644
--- a/lcm/lcm/pub/utils/tests.py
+++ b/lcm/lcm/pub/utils/tests.py
@@ -16,14 +16,16 @@ import unittest
import mock
import enumutil
import fileutil
+import json
import urllib2
import syscomm
import timeutil
import values
import platform
-from lcm.pub.database.models import JobStatusModel, JobModel
+from lcm.pub.database.models import JobStatusModel, JobModel, SubscriptionModel
from lcm.pub.utils.jobutil import JobUtil
+from lcm.pub.utils.notificationsutil import NotificationsUtil
class MockReq():
@@ -225,3 +227,78 @@ class UtilsTest(unittest.TestCase):
self.assertEqual("def", values.ignore_case_get(data, 'abc'))
self.assertEqual("klm", values.ignore_case_get(data, 'hig'))
self.assertEqual("bbb", values.ignore_case_get(data, 'aaa', 'bbb'))
+
+
+class TestNotificationUtils(unittest.TestCase):
+ def setUp(self):
+ subscription_id = 1
+ auth_params = {
+ "authType": ["BASIC"],
+ "paramsBasic": {
+ "username": "username",
+ "password": "password"
+ }
+ }
+ notification_types = ["VnfLcmOperationOccurrenceNotification"]
+ operation_types = ["INSTANTIATE"]
+ operation_states = ["STARTING"]
+ vnf_instance_filter = {
+ 'vnfdIds': ['99442b18-a5c7-11e8-998c-bf1755941f13', '9fe4080c-b1a3-11e8-bb96-645106374fd3'],
+ 'vnfInstanceIds': ['99442b18-a5c7-11e8-998c-bf1755941f12'],
+ 'vnfInstanceNames': ['demo'],
+ 'vnfProductsFromProviders': {
+ 'vnfProvider': u'string',
+ 'vnfProducts': {
+ 'vnfProductName': 'string',
+ 'versions': {
+ 'vnfSoftwareVersion': u'string',
+ 'vnfdVersions': 'string'
+ }
+ }
+ }
+ }
+ links = {
+ "self": "demo"
+ }
+ SubscriptionModel(subscription_id=subscription_id, callback_uri="http://demo",
+ auth_info=json.dumps(auth_params),
+ notification_types=json.dumps(notification_types),
+ operation_types=json.dumps(operation_types),
+ operation_states=json.dumps(operation_states),
+ vnf_instance_filter=json.dumps(vnf_instance_filter),
+ links=json.dumps(links)).save()
+
+ def tearDown(self):
+ SubscriptionModel.objects.all().delete()
+
+ @mock.patch('requests.post')
+ def test_send_notification(self, mock_post):
+ dummy_notification = {
+ "vnfInstanceId": "99442b18-a5c7-11e8-998c-bf1755941f13",
+ "operationState": "STARTING",
+ "operation": "INSTANTIATE",
+ }
+ mock_post.return_value.status_code = 204
+ NotificationsUtil().send_notification(dummy_notification)
+ mock_post.assert_called_once()
+
+ @mock.patch('requests.post')
+ def test_send_notification_with_empty_filters(self, mock_post):
+ dummy_notification = {
+ "vnfInstanceId": "9fe4080c-b1a3-11e8-bb96-645106374fd3",
+ "operationState": "",
+ "operation": "",
+ }
+ mock_post.return_value.status_code = 204
+ NotificationsUtil().send_notification(dummy_notification)
+ mock_post.assert_called_once()
+
+ @mock.patch('requests.post')
+ def test_send_notification_unmatched_filters(self, mock_post):
+ dummy_notification = {
+ "vnfInstanceId": "9fe4080c-b1a3-11e8-bb96-xxxxx",
+ "operationState": "DUMMY",
+ "operation": "DUMMY",
+ }
+ NotificationsUtil().send_notification(dummy_notification)
+ mock_post.assert_not_called()