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