1 # Copyright (C) 2018 Verizon. All Rights Reserved
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
26 from lcm.nf import const
27 from lcm.pub.database.models import SubscriptionModel
28 from lcm.pub.database.models import VmInstModel
29 from lcm.pub.database.models import NetworkInstModel
30 from lcm.pub.database.models import PortInstModel
31 from lcm.pub.database.models import StorageInstModel
32 from lcm.pub.database.models import VNFCInstModel
33 from lcm.pub.database.models import NfInstModel
34 from lcm.pub.utils.timeutil import now_time
35 from lcm.pub.utils.enumutil import enum
37 logger = logging.getLogger(__name__)
39 rest_no_auth, rest_oneway_auth, rest_bothway_auth = 0, 1, 2
40 HTTP_200_OK, HTTP_201_CREATED, HTTP_204_NO_CONTENT, HTTP_202_ACCEPTED = '200', '201', '204', '202'
41 status_ok_list = [HTTP_200_OK, HTTP_201_CREATED, HTTP_204_NO_CONTENT, HTTP_202_ACCEPTED]
42 HTTP_404_NOTFOUND, HTTP_403_FORBIDDEN, HTTP_401_UNAUTHORIZED, HTTP_400_BADREQUEST = '404', '403', '401', '400'
44 lCM_OP_OCC="VnfLcmOperationOccurrenceNotification",
45 CREATION="VnfIdentifierCreationNotification",
46 DELETION="VnfIdentifierDeletionNotification"
50 class NotificationsUtil(object):
51 def send_notification(self, notification):
52 logger.info("Send Notifications to the callbackUri")
54 "operationState": "operation_states",
55 "operation": "operation_types"
57 subscriptions_filter = {v + "__contains": notification[k] for k, v in list(filters.items())}
59 subscriptions = SubscriptionModel.objects.filter(**subscriptions_filter)
60 if not subscriptions.exists():
61 logger.info("No subscriptions created for the filters %s" % notification)
63 logger.info("Start sending notifications")
64 for subscription in subscriptions:
66 notification["subscriptionId"] = subscription.subscription_id
67 notification['_links']['subscription'] = {
68 'href': '/api/vnflcm/v1/subscriptions/%s' % subscription.subscription_id
70 callbackUri = subscription.callback_uri
71 auth_info = json.loads(subscription.auth_info)
72 if const.BASIC in auth_info["authType"]:
74 self.post_notification(callbackUri, notification)
75 except Exception as e:
76 logger.error("Failed to post notification: %s", e.args[0])
78 def post_notification(self, callbackUri, notification):
79 logger.info("Sending notification to %s", callbackUri)
80 resp = self.call_req(callbackUri, "", "", "POST", json.dumps(notification))
82 logger.error('Status code is %s, detail is %s.', resp[2], resp[1])
84 def call_req(self, full_url, user, passwd, method, content=''):
85 callid = str(uuid.uuid1())
86 logger.debug("[%s]call_req('%s','%s','%s',%s,'%s','%s')" % (
87 callid, full_url, user, passwd, rest_no_auth, method, content))
92 headers = {'content-type': 'application/json', 'accept': 'application/json'}
94 headers['Authorization'] = 'Basic %s' % base64.b64encode(
95 bytes('%s:%s' % (user, passwd), "utf-8")).decode()
97 for retry_times in range(3):
98 http = httplib2.Http(ca_certs=ca_certs, disable_ssl_certificate_validation=True)
99 http.follow_all_redirects = True
101 resp, resp_content = http.request(full_url, method=method.upper(), body=content, headers=headers)
102 logger.info("resp=%s,resp_content=%s" % (resp, resp_content))
103 resp_status, resp_body = resp['status'], resp_content.decode('UTF-8')
104 resp_Location = resp.get('Location', "")
105 logger.debug("[%s][%d]status=%s,resp_body=%s)" % (callid, retry_times, resp_status, resp_body))
106 if resp_status in status_ok_list:
107 ret = [0, resp_body, resp_status, resp_Location]
109 ret = [1, resp_body, resp_status, resp_Location]
111 except Exception as ex:
112 if 'httplib.ResponseNotReady' in str(sys.exc_info()):
113 logger.debug("retry_times=%d", retry_times)
114 logger.error(traceback.format_exc())
115 ret = [1, "Unable to connect to %s" % full_url, resp_status, resp_Location]
118 except urllib.error.URLError as err:
119 ret = [2, str(err), resp_status, resp_Location]
120 except Exception as ex:
121 logger.error(traceback.format_exc())
122 logger.error("[%s]ret=%s" % (callid, str(sys.exc_info())))
123 res_info = str(sys.exc_info())
124 if 'httplib.ResponseNotReady' in res_info:
125 res_info = "The URL[%s] request failed or is not responding." % full_url
126 ret = [3, res_info, resp_status, resp_Location]
128 logger.error(traceback.format_exc())
129 ret = [4, str(sys.exc_info()), resp_status, resp_Location]
131 logger.debug("[%s]ret=%s" % (callid, str(ret)))
135 def set_affected_vnfcs(affected_vnfcs, nfinstid, changetype):
136 vnfcs = VNFCInstModel.objects.filter(instid=nfinstid)
140 vm = VmInstModel.objects.filter(vmid=vnfc.vmid)
143 'vimConnectionId': vm[0].vimid,
144 'resourceId': vm[0].resourceid,
145 'resourceProviderId': vm[0].vmname, # TODO: is resourceName mapped to resourceProviderId?
146 'vimLevelResourceType': 'vm'
148 affected_vnfcs.append({
149 'id': vnfc.vnfcinstanceid,
151 'changeType': changetype,
152 'computeResource': vm_resource
154 logger.debug("affected_vnfcs=%s", affected_vnfcs)
155 return affected_vnfcs
158 def set_affected_vls(affected_vls, nfinstid, changetype):
159 networks = NetworkInstModel.objects.filter(instid=nfinstid)
160 for network in networks:
162 'vimConnectionId': network.vimid,
163 'resourceId': network.resourceid,
164 'resourceProviderId': network.name, # TODO: is resourceName mapped to resourceProviderId?
165 'vimLevelResourceType': 'network'
167 affected_vls.append({
168 'id': network.networkid,
169 'virtualLinkDescId': network.nodeId,
170 'changeType': changetype,
171 'networkResource': network_resource
173 logger.debug("affected_vls=%s", affected_vls)
176 def set_ext_connectivity(ext_connectivity, nfinstid):
177 ext_connectivity_map = {}
178 ports = PortInstModel.objects.filter(instid=nfinstid)
180 if port.networkid not in ext_connectivity_map:
181 ext_connectivity_map[port.networkid] = []
182 ext_connectivity_map[port.networkid].append({
183 'id': port.portid, # TODO: port.portid or port.nodeid?
185 'vimConnectionId': port.vimid,
186 'resourceId': port.resourceid,
187 'resourceProviderId': port.name, # TODO: is resourceName mapped to resourceProviderId?
188 'vimLevelResourceType': 'port'
190 'cpInstanceId': port.portid # TODO: port.cpinstanceid is not initiated when create port resource.
192 for network_id, ext_link_ports in list(ext_connectivity_map.items()):
193 networks = NetworkInstModel.objects.filter(networkid=network_id)
194 net_name = networks[0].name if networks else network_id
196 'vimConnectionId': ext_link_ports[0]['resourceHandle']['vimConnectionId'],
197 'resourceId': network_id,
198 'resourceProviderId': net_name, # TODO: is resourceName mapped to resourceProviderId?
199 'vimLevelResourceType': 'network'
201 ext_connectivity.append({
203 'resourceHandle': network_resource,
204 'extLinkPorts': ext_link_ports
206 logger.debug("ext_connectivity=%s", ext_connectivity)
209 def set_affected_vss(affected_vss, nfinstid, changetype):
210 vss = StorageInstModel.objects.filter(instid=nfinstid)
212 affected_vss.append({
214 'virtualStorageDescId': vs.nodeId,
215 'changeType': changetype,
217 'vimConnectionId': vs.vimid,
218 'resourceId': vs.resourceid,
219 'resourceProviderId': vs.name, # TODO: is resourceName mapped to resourceProviderId?
220 'vimLevelResourceType': 'volume'
223 logger.debug("affected_vss=%s", affected_vss)
226 def get_notification_status(operation_state):
227 if operation_state == const.OPERATION_STATE_TYPE.STARTING:
228 return const.LCM_NOTIFICATION_STATUS.START
229 return const.LCM_NOTIFICATION_STATUS.RESULT
232 def prepare_notification(nfinstid, jobid, operation, operation_state):
233 logger.info('Start to prepare notification')
234 notification_content = {
235 'id': str(uuid.uuid4()), # shall be the same if sent multiple times due to multiple subscriptions.
236 'notificationType': NOTIFY_TYPE.lCM_OP_OCC,
237 # set 'subscriptionId' after filtering for subscribers
238 'timeStamp': now_time(),
239 'notificationStatus': get_notification_status(operation_state),
240 'operationState': operation_state,
241 'vnfInstanceId': nfinstid,
242 'operation': operation,
243 'isAutomaticInvocation': False,
244 'vnfLcmOpOccId': jobid,
246 'affectedVirtualLinks': [],
247 'affectedVirtualStorages': [],
248 'changedExtConnectivity': [],
252 'href': '%s/vnf_instances/%s' % (const.URL_PREFIX, nfinstid)
255 'href': '%s/vnf_lcm_op_occs/%s' % (const.URL_PREFIX, jobid)
259 nfInsts = NfInstModel.objects.filter(nfinstid=nfinstid)
261 notification_content['vnfmInstId'] = nfInsts[0].vnfminstid
263 notification_content['vnfmInstId'] = "1"
264 return notification_content
267 def prepare_notification_data(nfinstid, jobid, changetype, operation):
268 data = prepare_notification(
272 operation_state=const.OPERATION_STATE_TYPE.COMPLETED
275 set_affected_vnfcs(data['affectedVnfcs'], nfinstid, changetype)
276 set_affected_vls(data['affectedVirtualLinks'], nfinstid, changetype)
277 set_affected_vss(data['affectedVirtualStorages'], nfinstid, changetype)
278 set_ext_connectivity(data['changedExtConnectivity'], nfinstid)
280 logger.debug('Notification content: %s' % data)
284 def prepare_vnf_identifier_notification(notify_type, nfinstid):
286 "id": str(uuid.uuid4()), # shall be the same if sent multiple times due to multiple subscriptions.
287 "notificationType": notify_type,
288 "timeStamp": now_time(),
289 "vnfInstanceId": nfinstid,
292 'href': '%s/vnf_instances/%s' % (const.URL_PREFIX, nfinstid)
297 logger.debug('Vnf Identifier Notification: %s' % data)