Add log and comment
[modeling/etsicatalog.git] / catalog / packages / biz / notificationsutil.py
1 # Copyright 2019 ZTE Corporation.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #         http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 import json
16 import logging
17 import traceback
18 import uuid
19
20 import requests
21 from django.db.models import Q
22 from requests.auth import HTTPBasicAuth
23 from rest_framework import status
24
25 import catalog.pub.utils.timeutil
26 from catalog.packages import const
27 from catalog.packages.serializers.vnf_pkg_notifications import PkgChangeNotificationSerializer, \
28     PkgOnboardingNotificationSerializer
29 from catalog.pub.database.models import VnfPackageModel, VnfPkgSubscriptionModel, NsdmSubscriptionModel
30 from catalog.pub.utils.values import remove_none_key
31
32 logger = logging.getLogger(__name__)
33
34
35 class NotificationsUtil(object):
36     """
37     Util for notifications
38     """
39
40     def __init__(self, notification_type):
41         self.notification_type = notification_type
42         self.notifyserializer = None
43
44     def prepare_notification(self, **kwargs):
45         pass
46
47     def send_notification(self):
48         """
49         Send notification
50         :return:
51         """
52         notification = self.prepare_notification()
53
54         subscriptions_filter = {v + "__contains": notification[k] for k, v in self.filter.items()}
55         subscriptions_filter = remove_none_key(subscriptions_filter)
56         logger.debug('send_notification subscriptions_filter = %s' % subscriptions_filter)
57         q1 = Q()
58         q1.connector = 'OR'
59         for k, v in subscriptions_filter.items():
60             q1.children.append((k, v))
61
62         subscriptions = self.SubscriptionModel.objects.filter(q1)
63         if not subscriptions.exists():
64             logger.info("No subscriptions created for the filter %s" % notification)
65             return
66         logger.info("Start sending notifications")
67         for sub in subscriptions:
68             # set subscription id
69             notification["subscriptionId"] = sub.get_subscription_id()
70             notification['_links']['subscription'] = {
71                 'href': '/%s%s' % (self.subscription_root_uri, notification["subscriptionId"])
72             }
73             callbackuri = sub.callback_uri
74             """
75             auth_info = json.loads(sub.auth_info)
76             if auth_info["authType"] == const.OAUTH2_CLIENT_CREDENTIALS:
77                 pass
78             """
79             if self.notifyserializer:
80                 serialized_data = self.notifyserializer(data=notification)
81                 if not serialized_data.is_valid():
82                     logger.error('Notification Data is invalid:%s.' % serialized_data.errors)
83
84             if sub.auth_info:
85                 self.post_notification(callbackuri, notification, auth_info=json.loads(sub.auth_info))
86             else:
87                 self.post_notification(callbackuri, notification)
88
89     def post_notification(self, callbackuri, notification, auth_info=None):
90         """
91         Post notification
92         :param callbackuri:
93         :param notification:
94         :param auth_info:
95         :return:
96         """
97         try:
98             if auth_info:
99                 if const.BASIC in auth_info.get("authType", ''):
100                     params = auth_info.get("paramsBasic", {})
101                     username = params.get("userName")
102                     password = params.get("password")
103                     resp = requests.post(callbackuri,
104                                          data=json.dumps(notification),
105                                          headers={'Connection': 'close',
106                                                   'content-type': 'application/json',
107                                                   'accept': 'application/json'},
108                                          auth=HTTPBasicAuth(username, password),
109                                          verify=False)
110                 elif const.OAUTH2_CLIENT_CREDENTIALS in auth_info.get("authType", ''):
111                     # todo
112                     pass
113                 else:
114                     # todo
115                     pass
116             else:
117                 resp = requests.post(callbackuri,
118                                      data=json.dumps(notification),
119                                      headers={'Connection': 'close',
120                                               'content-type': 'application/json',
121                                               'accept': 'application/json'},
122                                      verify=False)
123
124             if resp.status_code == status.HTTP_204_NO_CONTENT:
125                 logger.info("Sending notification to %s successfully.", callbackuri)
126             else:
127                 logger.error("Sending notification to %s failed: %s" % (callbackuri, resp))
128         except:
129             logger.error("Post notification failed.")
130             logger.error(traceback.format_exc())
131
132
133 class PkgNotifications(NotificationsUtil):
134     """
135     Notification Utils for VNF pckages
136     """
137
138     def __init__(self, notification_type, vnf_pkg_id, change_type=None, operational_state=None):
139         super(PkgNotifications, self).__init__(notification_type)
140         self.filter = {
141             'vnfdId': 'vnfd_id',
142             'vnfPkgId': 'vnf_pkg_id'
143         }
144         self.vnf_pkg_id = vnf_pkg_id
145         self.change_type = change_type
146         self.operational_state = operational_state
147         self.SubscriptionModel = VnfPkgSubscriptionModel
148         self.subscription_root_uri = const.VNFPKG_SUBSCRIPTION_ROOT_URI
149         if self.notification_type == "VnfPackageChangeNotification":
150             self.notifyserializer = PkgChangeNotificationSerializer
151         else:
152             self.notifyserializer = PkgOnboardingNotificationSerializer
153
154     def prepare_notification(self):
155         """
156         Prepare notification
157         :return:
158         """
159         logger.info('Start to prepare Pkgnotification')
160
161         vnf_pkg = VnfPackageModel.objects.filter(vnfPackageId=self.vnf_pkg_id)
162         vnfd_id = None
163         if vnf_pkg:
164             vnfd_id = vnf_pkg[0].vnfdId
165         notification_content = {
166             'id': str(uuid.uuid4()),  # shall be the same if sent multiple times due to multiple subscriptions.
167             'notificationType': self.notification_type,
168             # set 'subscriptionId' after filtering for subscribers
169             'timeStamp': catalog.pub.utils.timeutil.now_time(),
170             'vnfPkgId': self.vnf_pkg_id,
171             'vnfdId': vnfd_id,
172             '_links': {
173                 'vnfPackage': {
174                     'href': '/%s/vnf_packages/%s' % (const.PKG_URL_PREFIX, self.vnf_pkg_id)
175                 }
176             }
177         }
178
179         if self.notification_type == "VnfPackageChangeNotification":
180             notification_content['changeType'] = self.change_type
181             notification_content['operationalState'] = self.operational_state
182
183         return notification_content
184
185
186 class NsdNotifications(NotificationsUtil):
187     """
188     Notification Util for NS packages
189     """
190
191     def __init__(self, notification_type, nsd_info_id, nsd_id, failure_details=None, operational_state=None):
192         super(NsdNotifications, self).__init__(notification_type)
193         self.filter = {
194             'nsdInfoId': 'nsdInfoId',
195             'nsdId': 'nsdId',
196         }
197         self.SubscriptionModel = NsdmSubscriptionModel
198         self.subscription_root_uri = const.NSDM_SUBSCRIPTION_ROOT_URI
199         self.nsd_info_id = nsd_info_id
200         self.nsd_id = nsd_id
201         self.failure_details = failure_details
202         self.operational_state = operational_state
203         # todo:
204         # if self.notification_type == "VnfPackageChangeNotification":
205         #     self.notifyserializer = PkgChangeNotificationSerializer
206         # else:
207         #     self.notifyserializer = PkgOnboardingNotificationSerializer
208
209     def prepare_notification(self):
210         """
211         Prepare notification
212         :return:
213         """
214         logger.info('Start to prepare Nsdnotification')
215
216         notification_content = {
217             'id': str(uuid.uuid4()),  # shall be the same if sent multiple times due to multiple subscriptions.
218             'notificationType': self.notification_type,
219             # set 'subscriptionId' after filtering for subscribers
220             'timeStamp': catalog.pub.utils.timeutil.now_time(),
221             'nsdInfoId': self.nsd_info_id,
222             'nsdId': self.nsd_id,
223             '_links': {
224                 'nsdInfo': {
225                     'href': '/%s/ns_descriptors/%s' % (
226                         const.NSD_URL_PREFIX, self.nsd_info_id)
227                 }
228             }
229         }
230         if self.notification_type == "NsdOnboardingFailureNotification":
231             notification_content['onboardingFailureDetails'] = self.failure_details
232         if self.notification_type == "NsdChangeNotification":
233             notification_content['nsdOperationalState'] = self.operational_state
234         return notification_content
235
236
237 class PnfNotifications(NotificationsUtil):
238     """
239     Notification util for PNF package
240     """
241     def __init__(self, notification_type, pnfd_info_id, pnfd_id, failure_details=None):
242         super(PnfNotifications, self).__init__(notification_type)
243         self.filter = {
244             'pnfdId': 'pnfdId',
245             'pnfdInfoIds': 'pnfdInfoIds',
246         }
247         self.SubscriptionModel = NsdmSubscriptionModel
248         self.subscription_root_uri = const.NSDM_SUBSCRIPTION_ROOT_URI
249         self.pnfd_info_id = pnfd_info_id
250         self.pnfd_id = pnfd_id
251         self.failure_details = failure_details
252         # todo
253         # if self.notification_type == "VnfPackageChangeNotification":
254         #     self.notifyserializer = PkgChangeNotificationSerializer
255         # else:
256         #     self.notifyserializer = PkgOnboardingNotificationSerializer
257
258     def prepare_notification(self, *args, **kwargs):
259         """
260         Prepare notification
261         :param args:
262         :param kwargs:
263         :return:
264         """
265         logger.info('Start to prepare Pnfnotification')
266         notification_content = {
267             'id': str(uuid.uuid4()),  # shall be the same if sent multiple times due to multiple subscriptions.
268             'notificationType': self.notification_type,
269             # set 'subscriptionId' after filtering for subscribers
270             'timeStamp': catalog.pub.utils.timeutil.now_time(),
271             'pnfdInfoIds': self.pnfd_info_id,
272             'pnfdId': self.pnfd_id,
273             '_links': {
274                 'pnfdInfo': {
275                     'href': '/%s/pnf_descriptors/%s' % (const.NSD_URL_PREFIX,
276                                                         self.pnfd_info_id)
277                 }
278             }
279         }
280         if self.notification_type == "PnfdOnboardingFailureNotification":
281             notification_content['onboardingFailureDetails'] = self.failure_details
282         return notification_content