6989805f8c7732e3562578df6871390e81f2e297
[vfc/nfvo/lcm.git] / lcm / ns_vnfs / biz / create_vnfs.py
1 # Copyright 2016-2018 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 from threading import Thread
18 import traceback
19 import uuid
20
21 from lcm.ns.enum import OWNER_TYPE
22 from lcm.ns_vnfs.const import NFVO_VNF_INST_TIMEOUT_SECOND
23 from lcm.ns_vnfs.biz.subscribe import SubscriptionCreation
24 from lcm.ns_vnfs.biz.wait_job import wait_job_finish
25 from lcm.ns_vnfs.enum import VNF_STATUS
26 from lcm.pub.config.config import REPORT_TO_AAI
27 from lcm.pub.config.config import REG_TO_MSB_REG_PARAM, OOF_BASE_URL, OOF_PASSWD, OOF_USER
28 from lcm.pub.config.config import CUST_NAME, CUST_LAT, CUST_LONG
29 from lcm.pub.database.models import NfInstModel, NSInstModel, VNFFGInstModel, VLInstModel, OOFDataModel
30 from lcm.jobs.enum import JOB_MODEL_STATUS, JOB_ACTION, JOB_PROGRESS, JOB_ERROR_CODE, JOB_TYPE
31 from lcm.pub.exceptions import NSLCMException
32 from lcm.pub.msapi.aai import create_vnf_aai
33 from lcm.pub.msapi.extsys import get_vnfm_by_id
34 from lcm.pub.msapi.sdc_run_catalog import query_vnfpackage_by_id
35 from lcm.pub.msapi.vnfmdriver import send_nf_init_request
36 from lcm.pub.utils import restcall
37 from lcm.pub.utils.jobutil import JobUtil
38 # from lcm.pub.utils.share_lock import do_biz_with_share_lock
39 from lcm.pub.utils.timeutil import now_time
40 from lcm.pub.utils.values import ignore_case_get
41
42
43 logger = logging.getLogger(__name__)
44
45
46 def prepare_create_params():
47     nf_inst_id = str(uuid.uuid4())
48     NfInstModel(
49         nfinstid=nf_inst_id,
50         status=VNF_STATUS.INSTANTIATING,
51         create_time=now_time(),
52         lastuptime=now_time()
53     ).save()
54     job_id = JobUtil.create_job(JOB_TYPE.VNF, JOB_ACTION.CREATE, nf_inst_id)
55     JobUtil.add_job_status(job_id, JOB_PROGRESS.STARTED, 'create vnf record in database.', JOB_ERROR_CODE.NO_ERROR)
56     return nf_inst_id, job_id
57
58
59 class CreateVnfs(Thread):
60     def __init__(self, data, nf_inst_id, job_id):
61         super(CreateVnfs, self).__init__()
62         self.data = data
63         self.nf_inst_id = nf_inst_id
64         self.job_id = job_id
65         self.ns_inst_id = ''
66         self.vnf_id = ''
67         self.vnfd_id = ''
68         self.ns_inst_name = ''
69         self.nsd_model = ''
70         self.vnfd_model = ''
71         self.vnf_inst_name = ''
72         self.vnfm_inst_id = ''
73         self.inputs = ''
74         self.nf_package_info = ''
75         self.vnfm_nf_inst_id = ''
76         self.vnfm_job_id = ''
77         self.vnfm_inst_name = ''
78         self.vim_id = ''
79
80     def run(self):
81         try:
82             self.get_params()
83             self.check_nf_name_exist()
84             self.get_vnfd_id()
85             if REPORT_TO_AAI:
86                 self.create_vnf_in_aai()
87             self.check_nf_package_valid()
88             self.send_nf_init_request_to_vnfm()
89             self.send_homing_request_to_OOF()
90             self.send_get_vnfm_request_to_extsys()
91             self.wait_vnfm_job_finish()
92             self.subscribe()
93             self.save_info_to_db()
94             JobUtil.add_job_status(self.job_id, JOB_PROGRESS.FINISHED, 'vnf instantiation success', JOB_ERROR_CODE.NO_ERROR)
95         except NSLCMException as e:
96             self.vnf_inst_failed_handle(e.args[0])
97         except Exception:
98             logger.error(traceback.format_exc())
99             self.vnf_inst_failed_handle('unexpected exception')
100
101     def get_params(self):
102         self.ns_inst_id = self.data['ns_instance_id']
103         vnf_index = int(float(self.data['vnf_index'])) - 1
104         additional_vnf_info = self.data['additional_param_for_vnf'][vnf_index]
105         self.vnf_id = ignore_case_get(additional_vnf_info, 'vnfProfileId')
106         additional_param = ignore_case_get(additional_vnf_info, 'additionalParam')
107         self.properties = ignore_case_get(additional_param, 'properties')
108         self.vnfm_inst_id = ignore_case_get(additional_param, 'vnfmInstanceId')
109         para = ignore_case_get(additional_param, 'inputs')
110         self.inputs = json.loads(para) if isinstance(para, str) else para
111         self.vim_id = ignore_case_get(additional_param, 'vimId')
112         self.vnfd_id = ignore_case_get(additional_param, 'vnfdId')
113
114     def check_nf_name_exist(self):
115         is_exist = NfInstModel.objects.filter(nf_name=self.vnf_inst_name).exists()
116         if is_exist:
117             raise NSLCMException('The name of VNF instance already exists.')
118
119     def get_vnfd_id(self):
120         if self.vnfd_id:
121             logger.debug("need not get vnfd_id")
122             self.nsd_model = {'vnfs': [], 'vls': [], 'vnffgs': []}
123             self.vnf_inst_name = self.vnfd_id + str(uuid.uuid4())
124             self.vnf_inst_name = self.vnf_inst_name[:30]
125             return
126         ns_inst_info = NSInstModel.objects.get(id=self.ns_inst_id)
127         self.ns_inst_name = ns_inst_info.name
128         self.nsd_model = json.loads(ns_inst_info.nsd_model)
129         for vnf_info in self.nsd_model['vnfs']:
130             if self.vnf_id == vnf_info['vnf_id']:
131                 self.vnfd_id = vnf_info['properties']['id']
132                 if 'name' not in vnf_info['properties']:
133                     # HW vnf instance name must start with alphabet
134                     self.vnf_inst_name = 'vnf' + self.vnfd_id[:10] + str(uuid.uuid4())
135                 else:
136                     self.vnf_inst_name = vnf_info['properties']['name'] + str(uuid.uuid4())
137                 self.vnf_inst_name = self.vnf_inst_name[:30]
138                 self.vnf_inst_name = self.vnf_inst_name.replace("-", "_")
139                 return
140         raise NSLCMException('Can not found vnf in nsd model')
141
142     def check_nf_package_valid(self):
143         nfpackage_info = query_vnfpackage_by_id(self.vnfd_id)
144         self.nf_package_info = nfpackage_info["packageInfo"]
145         self.vnfd_model = ignore_case_get(self.nf_package_info, "vnfdModel")
146         self.vnfd_model = json.loads(self.vnfd_model)
147
148     def get_virtual_link_info(self, vnf_id):
149         virtual_link_list, ext_virtual_link = [], []
150         for vnf_info in self.nsd_model['vnfs']:
151             if vnf_info['vnf_id'] != vnf_id:
152                 continue
153             for network_info in vnf_info['networks']:
154                 vl_instance = VLInstModel.objects.get(
155                     vldid=network_info['vl_id'],
156                     ownertype=OWNER_TYPE.NS,
157                     ownerid=self.ns_inst_id)
158                 vl_instance_id = vl_instance.vlinstanceid
159                 network_name, subnet_name = self.get_network_info_of_vl(network_info['vl_id'])
160                 virtual_link_list.append({
161                     'network_name': network_name,
162                     'key_name': network_info['key_name'],
163                     'subnetwork_name': subnet_name,
164                     'vl_instance_id': vl_instance_id
165                 })
166                 vim_id = json.JSONDecoder().decode(vl_instance.vimid) if isinstance(vl_instance.vimid, str) \
167                     else vl_instance.vimid
168                 ext_virtual_link.append({
169                     "vlInstanceId": vl_instance_id,
170                     "resourceId": vl_instance.relatednetworkid,
171                     "resourceSubnetId": vl_instance.relatedsubnetworkid,
172                     "cpdId": self.get_cpd_id_of_vl(network_info['key_name']),
173                     "vim": {
174                         "vimid": vim_id
175                     },
176                     # SOL 003 align
177                     "id": vl_instance_id,
178                     "vimConnectionId": vl_instance.vimid,
179                     "extCps": self.get_cpds_of_vl(network_info['key_name'])
180                 })
181         return virtual_link_list, ext_virtual_link
182
183     def get_cpds_of_vl(self, vl_key):
184         extCps = []
185         logger.debug("vl_keya; %s" % vl_key)
186         for cpd in self.vnfd_model["vnf_exposed"]["external_cps"]:
187             logger.debug("exposed_cpd; %s" % cpd)
188             if vl_key == cpd["key_name"]:
189                 cp = {"cpdId": cpd["cpd_id"], "cpConfig": []}
190                 extCps.append(cp)
191         return extCps
192
193     def get_cpd_id_of_vl(self, vl_key):
194         for cpd in self.vnfd_model["vnf_exposed"]["external_cps"]:
195             if vl_key == cpd["key_name"]:
196                 return cpd["cpd_id"]
197         return ""
198
199     def get_network_info_of_vl(self, vl_id):
200         for vnf_info in self.nsd_model['vls']:
201             if vnf_info['vl_id'] == vl_id:
202                 return vnf_info['properties']['vl_profile']['networkName'], vnf_info['properties']['vl_profile']['networkName']  # ['initiationParameters']['name']
203         return '', ''
204
205     def send_nf_init_request_to_vnfm(self):
206         virtual_link_list, ext_virtual_link = self.get_virtual_link_info(self.vnf_id)
207         req_param = json.JSONEncoder().encode({
208             'vnfInstanceName': self.vnf_inst_name,
209             'vnfPackageId': ignore_case_get(self.nf_package_info, "vnfPackageId"),
210             'vnfDescriptorId': self.vnfd_id,
211             'flavourId': "default",
212             'extVirtualLink': ext_virtual_link,
213             'additionalParam': {
214                 "properties": self.properties,
215                 "inputs": self.inputs,
216                 "vimId": self.vim_id,
217                 "extVirtualLinks": virtual_link_list
218             }
219         })
220         rsp = send_nf_init_request(self.vnfm_inst_id, req_param)
221         self.vnfm_job_id = ignore_case_get(rsp, 'jobId')
222         self.vnfm_nf_inst_id = ignore_case_get(rsp, 'vnfInstanceId')
223
224         NfInstModel.objects.filter(nfinstid=self.nf_inst_id).update(
225             mnfinstid=self.vnfm_nf_inst_id,
226             nf_name=self.vnf_inst_name,
227             vnf_id=self.vnf_id,
228             package_id=ignore_case_get(self.nf_package_info, "vnfPackageId"),
229             vnfm_inst_id=self.vnfm_inst_id,
230             ns_inst_id=self.ns_inst_id,
231             version=ignore_case_get(self.nf_package_info, "vnfdVersion"),
232             vendor=ignore_case_get(self.nf_package_info, "vnfdProvider"),
233             vnfd_model=self.vnfd_model,
234             input_params=json.JSONEncoder().encode(self.inputs),
235             lastuptime=now_time())
236
237     def build_homing_request(self):
238         id = str(uuid.uuid4())
239         callback_uri = "http://{vfcBaseUrl}/api/nslcm/v1/ns/placevnf"
240         IP = REG_TO_MSB_REG_PARAM["nodes"][0]["ip"]
241         PORT = REG_TO_MSB_REG_PARAM["nodes"][0]["port"]
242         vfcBaseUrl = IP + ':' + PORT
243         callback_uri = callback_uri.format(vfcBaseUrl=vfcBaseUrl)
244         modelInvariantId = "no-resourceModelInvariantId"
245         modelVersionId = "no-resourceModelVersionId"
246         nsInfo = NSInstModel.objects.filter(id=self.ns_inst_id)
247         placementDemand = {
248             "resourceModuleName": self.vnf_id,
249             "serviceResourceId": self.vnfm_nf_inst_id,
250             "resourceModelInfo": {
251                 "modelInvariantId": modelInvariantId,
252                 "modelVersionId": modelVersionId
253             }
254         }
255
256         if self.vim_id:
257             # vim_info = self.vim_id.split("_")
258             # identifiers = list()
259             # identifiers.append(vim_info[1])
260             # cloudOwner = vim_info[0]
261             identifiers = list()
262             if type(self.vim_id) == str:
263                 self.vim_id = json.loads(self.vim_id)
264             identifiers.append(self.vim_id['cloud_regionid'])
265             cloudOwner = self.vim_id['cloud_owner']
266             required_candidate = [
267                 {
268                     "identifierType": "vimId",
269                     "cloudOwner": cloudOwner,
270                     "identifiers": identifiers
271                 }
272             ]
273             placementDemand["requiredCandidates"] = required_candidate
274
275         req_body = {
276             "requestInfo": {
277                 "transactionId": id,
278                 "requestId": id,
279                 "callbackUrl": callback_uri,
280                 "sourceId": "vfc",
281                 "requestType": "create",
282                 "numSolutions": 1,
283                 "optimizers": ["placement"],
284                 "timeout": 600
285             },
286             "placementInfo": {
287                 "requestParameters": {
288                     "customerLatitude": CUST_LAT,
289                     "customerLongitude": CUST_LONG,
290                     "customerName": CUST_NAME
291                 },
292                 "subscriberInfo": {
293                     "globalSubscriberId": "",
294                     "subscriberName": "",
295                     "subscriberCommonSiteId": "",
296                 },
297                 "placementDemands": []
298             },
299             "serviceInfo": {
300                 "serviceInstanceId": self.ns_inst_id,
301                 "serviceName": self.ns_inst_name,
302                 "modelInfo": {
303                     "modelInvariantId": nsInfo[0].nsd_invariant_id,
304                     "modelVersionId": nsInfo[0].nsd_id
305                 }
306             }
307         }
308         req_body["placementInfo"]["placementDemands"].append(placementDemand)
309         # Stored the init request info inside DB
310         OOFDataModel.objects.create(
311             request_id=id,
312             transaction_id=id,
313             request_status="init",
314             request_module_name=self.vnf_id,
315             service_resource_id=self.vnfm_nf_inst_id,
316             vim_id="",
317             cloud_owner="",
318             cloud_region_id="",
319             vdu_info="",
320         )
321         return req_body
322
323     def send_homing_request_to_OOF(self):
324         req_body = self.build_homing_request()
325         base_url = OOF_BASE_URL
326         resources = "/api/oof/v1/placement"
327         resp = restcall.call_req(base_url, OOF_USER, OOF_PASSWD, restcall.rest_no_auth, resources, "POST",
328                                  json.dumps(req_body), "")
329         # resp = restcall.call_req(
330         #     base_url=base_url,
331         #     user=OOF_USER,
332         #     passwd=OOF_PASSWD,
333         #     auth_type=restcall.rest_no_auth,
334         #     resource=resources,
335         #     method="POST",
336         #     content=json.dumps(req_body),
337         #     additional_headers="")
338         resp_body = resp[-2]
339         resp_status = resp[-1]
340         if resp_body:
341             logger.debug("Got OOF sync response")
342         else:
343             logger.warn("Missing OOF sync response")
344         logger.debug(("OOF sync response code is %s") % resp_status)
345         if str(resp_status) != '202' or resp[0] != 0:
346             OOFDataModel.objects.filter(
347                 request_id=req_body["requestInfo"]["requestId"],
348                 transaction_id=req_body["requestInfo"]["transactionId"]
349             ).update(
350                 request_status="failed",
351                 vim_id="none",
352                 cloud_owner="none",
353                 cloud_region_id="none",
354                 vdu_info="none"
355             )
356             logger.error("Received a Bad Sync from OOF with response code %s" % resp_status)
357         logger.info("Completed Homing request to OOF")
358
359     def send_get_vnfm_request_to_extsys(self):
360         resp_body = get_vnfm_by_id(self.vnfm_inst_id)
361         self.vnfm_inst_name = ignore_case_get(resp_body, 'name')
362
363     def wait_vnfm_job_finish(self):
364         ret = wait_job_finish(
365             vnfm_id=self.vnfm_inst_id,
366             vnfo_job_id=self.job_id,
367             vnfm_job_id=self.vnfm_job_id,
368             progress_range=[10, 90],
369             timeout=NFVO_VNF_INST_TIMEOUT_SECOND)
370
371         if ret != JOB_MODEL_STATUS.FINISHED:
372             raise NSLCMException('VNF instantiation failed from VNFM. The job status is %s' % ret)
373
374     def subscribe(self):
375         data = {
376             'vnfInstanceId': self.vnfm_nf_inst_id,
377             'vnfmId': self.vnfm_inst_id
378         }
379         try:
380             SubscriptionCreation(data).do_biz()
381         except NSLCMException as e:
382             logger.error("subscribe failed: %s", e.args[0])
383         except Exception as e:
384             logger.error("subscribe failed: %s", e.args[0])
385
386     def save_info_to_db(self):
387         logger.debug("save_info_to_db start")
388         # do_biz_with_share_lock("set-vnflist-in-vnffginst-%s" % self.ns_inst_id, self.save_vnf_inst_id_in_vnffg)
389         NfInstModel.objects.filter(nfinstid=self.nf_inst_id).update(status=VNF_STATUS.ACTIVE, lastuptime=now_time())
390         logger.debug("save_info_to_db end")
391
392     def vnf_inst_failed_handle(self, error_msg):
393         logger.error('VNF instantiation failed, detail message: %s' % error_msg)
394         NfInstModel.objects.filter(nfinstid=self.nf_inst_id).update(status=VNF_STATUS.FAILED, lastuptime=now_time())
395         JobUtil.add_job_status(self.job_id, 255, 'VNF instantiation failed, detail message: %s' % error_msg, 0)
396
397     def save_vnf_inst_id_in_vnffg(self):
398         vnffgs = self.nsd_model['vnffgs']
399         for vnffg in vnffgs:
400             if self.vnf_id not in vnffg['members']:
401                 continue
402             vnffg_inst_infos = VNFFGInstModel.objects.filter(vnffgdid=vnffg['vnffg_Id'], nsinstid=self.ns_inst_id)
403             if not vnffg_inst_infos:
404                 raise NSLCMException('Vnffg instance not exist.')
405             vnf_list = vnffg_inst_infos[0].vnflist
406             vnffg_inst_infos.update(vnf_list=vnf_list + ',' + self.nf_inst_id if vnf_list else self.nf_inst_id)
407
408     def create_vnf_in_aai(self):
409         logger.debug("CreateVnfs::create_vnf_in_aai::report vnf instance[%s] to aai." % self.nf_inst_id)
410         try:
411             ns_insts = NSInstModel.objects.filter(id=self.ns_inst_id)
412             self.global_customer_id = ns_insts[0].global_customer_id
413             self.service_type = ns_insts[0].service_type
414             data = {
415                 "vnf-id": self.nf_inst_id,
416                 "vnf-name": self.vnf_inst_name,
417                 "vnf-type": "vnf-type-test111",
418                 "service-id": self.ns_inst_id,
419                 "in-maint": True,
420                 "is-closed-loop-disabled": False,
421                 "relationship-list": {
422                     "relationship": [
423                         {
424                             "related-to": "service-instance",
425                             "relationship-data": [
426                                 {
427                                     "relationship-key": "customer.global-customer-id",
428                                     "relationship-value": self.global_customer_id
429                                 },
430                                 {
431                                     "relationship-key": "service-subscription.service-type",
432                                     "relationship-value": self.service_type
433                                 },
434                                 {
435                                     "relationship-key": "service-instance.service-instance-id",
436                                     "relationship-value": self.ns_inst_id
437                                 }
438                             ]
439                         }
440                     ]
441                 }
442             }
443             resp_data, resp_status = create_vnf_aai(self.nf_inst_id, data)
444             logger.debug("Success to create vnf[%s] to aai, ns instance=[%s], resp_status: [%s]."
445                          % (self.nf_inst_id, self.ns_inst_id, resp_status))
446         except NSLCMException as e:
447             logger.debug("Fail to create vnf[%s] to aai, ns instance=[%s], detail message: %s"
448                          % (self.nf_inst_id, self.ns_inst_id, e.args[0]))
449         except:
450             logger.error(traceback.format_exc())