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