fe0b6403623ef7f426a522e4b206ff1713570089
[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 const.BASIC in auth_info["authType"]:
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             headers={'content-type': 'application/json'},
79             auth=HTTPBasicAuth(username, password)
80         )
81         if resp.status_code != status.HTTP_204_NO_CONTENT:
82             logger.error("Notify %s failed: %s", callbackUri, resp.text)
83
84
85 def set_affected_vnfcs(affected_vnfcs, nfinstid, changetype):
86     vnfcs = VNFCInstModel.objects.filter(instid=nfinstid)
87     for vnfc in vnfcs:
88         vm_resource = {}
89         if vnfc.vmid:
90             vm = VmInstModel.objects.filter(vmid=vnfc.vmid)
91             if vm:
92                 vm_resource = {
93                     'vimConnectionId': vm[0].vimid,
94                     'resourceId': vm[0].resourceid,
95                     'resourceProviderId': vm[0].vmname,  # TODO: is resourceName mapped to resourceProviderId?
96                     'vimLevelResourceType': 'vm'
97                 }
98         affected_vnfcs.append({
99             'id': vnfc.vnfcinstanceid,
100             'vduId': vnfc.vduid,
101             'changeType': changetype,
102             'computeResource': vm_resource
103         })
104     logger.debug("affected_vnfcs=%s", affected_vnfcs)
105     return affected_vnfcs
106
107
108 def set_affected_vls(affected_vls, nfinstid, changetype):
109     networks = NetworkInstModel.objects.filter(instid=nfinstid)
110     for network in networks:
111         network_resource = {
112             'vimConnectionId': network.vimid,
113             'resourceId': network.resourceid,
114             'resourceProviderId': network.name,  # TODO: is resourceName mapped to resourceProviderId?
115             'vimLevelResourceType': 'network'
116         }
117         affected_vls.append({
118             'id': network.networkid,
119             'virtualLinkDescId': network.nodeId,
120             'changeType': changetype,
121             'networkResource': network_resource
122         })
123     logger.debug("affected_vls=%s", affected_vls)
124
125
126 def set_ext_connectivity(ext_connectivity, nfinstid):
127     ext_connectivity_map = {}
128     ports = PortInstModel.objects.filter(instid=nfinstid)
129     for port in ports:
130         if port.networkid not in ext_connectivity_map:
131             ext_connectivity_map[port.networkid] = []
132         ext_connectivity_map[port.networkid].append({
133             'id': port.portid,  # TODO: port.portid or port.nodeid?
134             'resourceHandle': {
135                 'vimConnectionId': port.vimid,
136                 'resourceId': port.resourceid,
137                 'resourceProviderId': port.name,  # TODO: is resourceName mapped to resourceProviderId?
138                 'vimLevelResourceType': 'port'
139             },
140             'cpInstanceId': port.portid  # TODO: port.cpinstanceid is not initiated when create port resource.
141         })
142     for network_id, ext_link_ports in list(ext_connectivity_map.items()):
143         networks = NetworkInstModel.objects.filter(networkid=network_id)
144         net_name = networks[0].name if networks else network_id
145         network_resource = {
146             'vimConnectionId': ext_link_ports[0]['resourceHandle']['vimConnectionId'],
147             'resourceId': network_id,
148             'resourceProviderId': net_name,  # TODO: is resourceName mapped to resourceProviderId?
149             'vimLevelResourceType': 'network'
150         }
151         ext_connectivity.append({
152             'id': network_id,
153             'resourceHandle': network_resource,
154             'extLinkPorts': ext_link_ports
155         })
156     logger.debug("ext_connectivity=%s", ext_connectivity)
157
158
159 def set_affected_vss(affected_vss, nfinstid, changetype):
160     vss = StorageInstModel.objects.filter(instid=nfinstid)
161     for vs in vss:
162         affected_vss.append({
163             'id': vs.storageid,
164             'virtualStorageDescId': vs.nodeId,
165             'changeType': changetype,
166             'storageResource': {
167                 'vimConnectionId': vs.vimid,
168                 'resourceId': vs.resourceid,
169                 'resourceProviderId': vs.name,  # TODO: is resourceName mapped to resourceProviderId?
170                 'vimLevelResourceType': 'volume'
171             }
172         })
173     logger.debug("affected_vss=%s", affected_vss)
174
175
176 def get_notification_status(operation_state):
177     if operation_state == const.OPERATION_STATE_TYPE.STARTING:
178         return const.LCM_NOTIFICATION_STATUS.START
179     return const.LCM_NOTIFICATION_STATUS.RESULT
180
181
182 def prepare_notification(nfinstid, jobid, operation, operation_state):
183     logger.info('Start to prepare notification')
184     notification_content = {
185         'id': str(uuid.uuid4()),  # shall be the same if sent multiple times due to multiple subscriptions.
186         'notificationType': NOTIFY_TYPE.lCM_OP_OCC,
187         # set 'subscriptionId' after filtering for subscribers
188         'timeStamp': now_time(),
189         'notificationStatus': get_notification_status(operation_state),
190         'operationState': operation_state,
191         'vnfInstanceId': nfinstid,
192         'operation': operation,
193         'isAutomaticInvocation': False,
194         'vnfLcmOpOccId': jobid,
195         'affectedVnfcs': [],
196         'affectedVirtualLinks': [],
197         'affectedVirtualStorages': [],
198         'changedExtConnectivity': [],
199         'error': '',
200         '_links': {
201             'vnfInstance': {
202                 'href': '%s/vnf_instances/%s' % (const.URL_PREFIX, nfinstid)
203             },
204             'vnfLcmOpOcc': {
205                 'href': '%s/vnf_lcm_op_occs/%s' % (const.URL_PREFIX, jobid)
206             }
207         }
208     }
209     return notification_content
210
211
212 def prepare_notification_data(nfinstid, jobid, changetype, operation):
213     data = prepare_notification(
214         nfinstid=nfinstid,
215         jobid=jobid,
216         operation=operation,
217         operation_state=const.OPERATION_STATE_TYPE.COMPLETED
218     )
219
220     set_affected_vnfcs(data['affectedVnfcs'], nfinstid, changetype)
221     set_affected_vls(data['affectedVirtualLinks'], nfinstid, changetype)
222     set_affected_vss(data['affectedVirtualStorages'], nfinstid, changetype)
223     set_ext_connectivity(data['changedExtConnectivity'], nfinstid)
224
225     logger.debug('Notification content: %s' % data)
226     return data
227
228
229 def prepare_vnf_identifier_notification(notify_type, nfinstid):
230     data = {
231         "id": str(uuid.uuid4()),  # shall be the same if sent multiple times due to multiple subscriptions.
232         "notificationType": notify_type,
233         "timeStamp": now_time(),
234         "vnfInstanceId": nfinstid,
235         "_links": {
236             "vnfInstance": {
237                 'href': '%s/vnf_instances/%s' % (const.URL_PREFIX, nfinstid)
238             }
239         }
240     }
241
242     logger.debug('Vnf Identifier Notification: %s' % data)
243     return data