Refactor codes for notify util
[vfc/gvnfm/vnflcm.git] / lcm / lcm / pub / utils / notificationsutil.py
1 # Copyright (C) 2018 Verizon. All Rights Reserved
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 uuid
18
19 import requests
20 from requests.auth import HTTPBasicAuth
21 from rest_framework import status
22
23 from lcm.nf import const
24 from lcm.pub.database.models import SubscriptionModel
25 from lcm.pub.database.models import VmInstModel
26 from lcm.pub.database.models import NetworkInstModel
27 from lcm.pub.database.models import PortInstModel
28 from lcm.pub.database.models import StorageInstModel
29 from lcm.pub.database.models import VNFCInstModel
30 from lcm.pub.utils.timeutil import now_time
31 from lcm.pub.utils.enumutil import enum
32
33 logger = logging.getLogger(__name__)
34
35 NOTIFY_TYPE = enum(
36     lCM_OP_OCC="VnfLcmOperationOccurrenceNotification",
37     CREATION="VnfIdentifierCreationNotification",
38     DELETION="VnfIdentifierDeletionNotification"
39 )
40
41
42 class NotificationsUtil(object):
43     def send_notification(self, notification):
44         logger.info("Send Notifications to the callbackUri")
45         filters = {
46             "operationState": "operation_states",
47             "operation": "operation_types"
48         }
49         subscriptions_filter = {v + "__contains": notification[k] for k, v in list(filters.items())}
50
51         subscriptions = SubscriptionModel.objects.filter(**subscriptions_filter)
52         if not subscriptions.exists():
53             logger.info("No subscriptions created for the filters %s" % notification)
54             return
55         logger.info("Start sending notifications")
56         for subscription in subscriptions:
57             # set subscription id
58             notification["subscriptionId"] = subscription.subscription_id
59             notification['_links']['subscription'] = {
60                 'href': '/api/vnflcm/v1/subscriptions/%s' % subscription.subscription_id
61             }
62             callbackUri = subscription.callback_uri
63             auth_info = json.loads(subscription.auth_info)
64             if auth_info["authType"] != const.OAUTH2_CLIENT_CREDENTIALS:
65                 try:
66                     self.post_notification(callbackUri, auth_info, notification)
67                 except Exception as e:
68                     logger.error("Failed to post notification: %s", e.args[0])
69
70     def post_notification(self, callbackUri, auth_info, notification):
71         params = auth_info.get("paramsBasic", {})
72         username = params.get("userName")
73         password = params.get("password")
74         logger.info("Sending notification to %s", callbackUri)
75         resp = requests.post(
76             callbackUri,
77             data=notification,
78             auth=HTTPBasicAuth(username, password)
79         )
80         if resp.status_code != status.HTTP_204_NO_CONTENT:
81             logger.error("Notify %s failed: %s", callbackUri, resp.text)
82
83
84 def set_affected_vnfcs(affected_vnfcs, nfinstid, changetype):
85     vnfcs = VNFCInstModel.objects.filter(instid=nfinstid)
86     for vnfc in vnfcs:
87         vm_resource = {}
88         if vnfc.vmid:
89             vm = VmInstModel.objects.filter(vmid=vnfc.vmid)
90             if vm:
91                 vm_resource = {
92                     'vimConnectionId': vm[0].vimid,
93                     'resourceId': vm[0].resourceid,
94                     'resourceProviderId': vm[0].vmname,  # TODO: is resourceName mapped to resourceProviderId?
95                     'vimLevelResourceType': 'vm'
96                 }
97         affected_vnfcs.append({
98             'id': vnfc.vnfcinstanceid,
99             'vduId': vnfc.vduid,
100             'changeType': changetype,
101             'computeResource': vm_resource
102         })
103     logger.debug("affected_vnfcs=%s", affected_vnfcs)
104     return affected_vnfcs
105
106
107 def set_affected_vls(affected_vls, nfinstid, changetype):
108     networks = NetworkInstModel.objects.filter(instid=nfinstid)
109     for network in networks:
110         network_resource = {
111             'vimConnectionId': network.vimid,
112             'resourceId': network.resourceid,
113             'resourceProviderId': network.name,  # TODO: is resourceName mapped to resourceProviderId?
114             'vimLevelResourceType': 'network'
115         }
116         affected_vls.append({
117             'id': network.networkid,
118             'virtualLinkDescId': network.nodeId,
119             'changeType': changetype,
120             'networkResource': network_resource
121         })
122     logger.debug("affected_vls=%s", affected_vls)
123
124
125 def set_ext_connectivity(ext_connectivity, nfinstid):
126     ext_connectivity_map = {}
127     ports = PortInstModel.objects.filter(instid=nfinstid)
128     for port in ports:
129         if port.networkid not in ext_connectivity_map:
130             ext_connectivity_map[port.networkid] = []
131         ext_connectivity_map[port.networkid].append({
132             'id': port.portid,  # TODO: port.portid or port.nodeid?
133             'resourceHandle': {
134                 'vimConnectionId': port.vimid,
135                 'resourceId': port.resourceid,
136                 'resourceProviderId': port.name,  # TODO: is resourceName mapped to resourceProviderId?
137                 'vimLevelResourceType': 'port'
138             },
139             'cpInstanceId': port.portid  # TODO: port.cpinstanceid is not initiated when create port resource.
140         })
141     for network_id, ext_link_ports in list(ext_connectivity_map.items()):
142         networks = NetworkInstModel.objects.filter(networkid=network_id)
143         net_name = networks[0].name if networks else network_id
144         network_resource = {
145             'vimConnectionId': ext_link_ports[0]['resourceHandle']['vimConnectionId'],
146             'resourceId': network_id,
147             'resourceProviderId': net_name,  # TODO: is resourceName mapped to resourceProviderId?
148             'vimLevelResourceType': 'network'
149         }
150         ext_connectivity.append({
151             'id': network_id,
152             'resourceHandle': network_resource,
153             'extLinkPorts': ext_link_ports
154         })
155     logger.debug("ext_connectivity=%s", ext_connectivity)
156
157
158 def set_affected_vss(affected_vss, nfinstid, changetype):
159     vss = StorageInstModel.objects.filter(instid=nfinstid)
160     for vs in vss:
161         affected_vss.append({
162             'id': vs.storageid,
163             'virtualStorageDescId': vs.nodeId,
164             'changeType': changetype,
165             'storageResource': {
166                 'vimConnectionId': vs.vimid,
167                 'resourceId': vs.resourceid,
168                 'resourceProviderId': vs.name,  # TODO: is resourceName mapped to resourceProviderId?
169                 'vimLevelResourceType': 'volume'
170             }
171         })
172     logger.debug("affected_vss=%s", affected_vss)
173
174
175 def get_notification_status(operation_state):
176     if operation_state == const.OPERATION_STATE_TYPE.STARTING:
177         return const.LCM_NOTIFICATION_STATUS.START
178     return const.LCM_NOTIFICATION_STATUS.RESULT
179
180
181 def prepare_notification(nfinstid, jobid, operation, operation_state):
182     logger.info('Start to prepare notification')
183     notification_content = {
184         'id': str(uuid.uuid4()),  # shall be the same if sent multiple times due to multiple subscriptions.
185         'notificationType': NOTIFY_TYPE.lCM_OP_OCC,
186         # set 'subscriptionId' after filtering for subscribers
187         'timeStamp': now_time(),
188         'notificationStatus': get_notification_status(operation_state),
189         'operationState': operation_state,
190         'vnfInstanceId': nfinstid,
191         'operation': operation,
192         'isAutomaticInvocation': False,
193         'vnfLcmOpOccId': jobid,
194         'affectedVnfcs': [],
195         'affectedVirtualLinks': [],
196         'affectedVirtualStorages': [],
197         'changedExtConnectivity': [],
198         'error': '',
199         '_links': {
200             'vnfInstance': {
201                 'href': '%s/vnf_instances/%s' % (const.URL_PREFIX, nfinstid)
202             },
203             'vnfLcmOpOcc': {
204                 'href': '%s/vnf_lcm_op_occs/%s' % (const.URL_PREFIX, jobid)
205             }
206         }
207     }
208     return notification_content
209
210
211 def prepare_notification_data(nfinstid, jobid, changetype, operation):
212     data = prepare_notification(
213         nfinstid=nfinstid,
214         jobid=jobid,
215         operation=operation,
216         operation_state=const.OPERATION_STATE_TYPE.COMPLETED
217     )
218
219     set_affected_vnfcs(data['affectedVnfcs'], nfinstid, changetype)
220     set_affected_vls(data['affectedVirtualLinks'], nfinstid, changetype)
221     set_affected_vss(data['affectedVirtualStorages'], nfinstid, changetype)
222     set_ext_connectivity(data['changedExtConnectivity'], nfinstid)
223
224     logger.debug('Notification content: %s' % data)
225     return data
226
227
228 def prepare_vnf_identifier_notification(notify_type, nfinstid):
229     data = {
230         "id": str(uuid.uuid4()),  # shall be the same if sent multiple times due to multiple subscriptions.
231         "notificationType": notify_type,
232         "timeStamp": now_time(),
233         "vnfInstanceId": nfinstid,
234         "_links": {
235             "vnfInstance": {
236                 'href': '%s/vnf_instances/%s' % (const.URL_PREFIX, nfinstid)
237             }
238         }
239     }
240
241     logger.debug('Vnf Identifier Notification: %s' % data)
242     return data