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