e2f19312468b4c1f45e21657a7688841b633fdc9
[vfc/nfvo/lcm.git] / lcm / ns / ns_terminate.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
15 import json
16 import logging
17 import math
18 import threading
19 import time
20 import traceback
21
22 from lcm.ns.vnfs.wait_job import wait_job_finish
23 from lcm.pub.database.models import NSInstModel, VLInstModel, FPInstModel, NfInstModel
24 from lcm.pub.exceptions import NSLCMException
25 from lcm.pub.msapi.nslcm import call_from_ns_cancel_resource
26 from lcm.pub.utils.jobutil import JOB_MODEL_STATUS, JobUtil
27 from lcm.pub.utils.values import ignore_case_get
28
29 JOB_ERROR = 255
30 # [delete vnf try times]
31
32 logger = logging.getLogger(__name__)
33
34
35 class TerminateNsService(threading.Thread):
36     def __init__(self, ns_inst_id, terminate_type, terminate_timeout, job_id):
37         threading.Thread.__init__(self)
38         self.ns_inst_id = ns_inst_id
39         self.terminate_type = terminate_type
40         self.terminate_timeout = terminate_timeout
41         self.job_id = job_id
42         self.vnfm_inst_id = ''
43
44     def run(self):
45         try:
46             self.do_biz()
47         except NSLCMException as e:
48             JobUtil.add_job_status(self.job_id, JOB_ERROR, e.message)
49         except:
50             logger.error(traceback.format_exc())
51             JobUtil.add_job_status(self.job_id, JOB_ERROR, "ns terminate fail.", '')
52
53     def do_biz(self):
54         if not self.check_data():
55             JobUtil.add_job_status(self.job_id, 100, "Need not terminate.", '')
56             return
57
58         self.cancel_sfc_list()
59         self.cancel_vnf_list()
60         time.sleep(4)
61         self.cancel_vl_list()
62
63         self.finaldata()
64
65     def check_data(self):
66         JobUtil.add_job_status(self.job_id, 0, "TERMINATING...", '')
67         ns_inst = NSInstModel.objects.filter(id=self.ns_inst_id)
68         if not ns_inst.exists():
69             logger.warn('ns instance [%s] does not exist.' % self.ns_inst_id)
70             return False
71         JobUtil.add_job_status(self.job_id, 10, "Ns cancel: check ns_inst_id success", '')
72         return True
73
74     # delete VLINST
75     def cancel_vl_list(self):
76         array_vlinst = VLInstModel.objects.filter(ownertype='2', ownerid=self.ns_inst_id)
77         if not array_vlinst:
78             logger.error("[cancel_vl_list] no vlinst attatch to ns_inst_id:%s" % self.ns_inst_id)
79             return
80         step_progress = 20 / len(array_vlinst)
81         cur_progress = 70
82         for vlinst in array_vlinst:
83             tmp_msg = vlinst.vlinstanceid
84             try:
85                 ret = self.delete_vl(tmp_msg)
86                 if ret[0] == 0:
87                     cur_progress += step_progress
88                     result = json.JSONDecoder().decode(ret[1]).get("result", "")
89                     if str(result) == '0':
90                         JobUtil.add_job_status(self.job_id, cur_progress, "Delete vlinst:[%s] success." % tmp_msg, '')
91                     else:
92                         JobUtil.add_job_status(self.job_id, cur_progress, "Delete vlinst:[%s] failed." % tmp_msg, '')
93                         return 'false'
94                 else:
95                     NSInstModel.objects.filter(id=self.ns_inst_id).update(status='FAILED')
96                     return 'false'
97             except Exception as e:
98                 logger.error("[cancel_vl_list] error[%s]!" % e.message)
99                 logger.error(traceback.format_exc())
100                 JobUtil.add_job_status(self.job_id, cur_progress, "Delete vlinst:[%s] Failed." % tmp_msg, '')
101                 return 'false'
102         return 'true'
103
104     # delete SFC
105     def cancel_sfc_list(self):
106         array_sfcinst = FPInstModel.objects.filter(nsinstid=self.ns_inst_id)
107         if not array_sfcinst:
108             logger.error("[cancel_sfc_list] no sfcinst attatch to ns_inst_id:%s" % self.ns_inst_id)
109             return
110         step_progress = 20 / len(array_sfcinst)
111         cur_progress = 30
112         for sfcinst in array_sfcinst:
113             tmp_msg = sfcinst.sfcid
114             try:
115                 ret = self.delete_sfc(tmp_msg)
116                 if ret[0] == 0:
117                     cur_progress += step_progress
118                     result = json.JSONDecoder().decode(ret[1]).get("result", "")
119                     if str(result) == '0':
120                         JobUtil.add_job_status(self.job_id, cur_progress, "Delete sfcinst:[%s] success." % tmp_msg, '')
121                     else:
122                         JobUtil.add_job_status(self.job_id, cur_progress, "Delete sfcinst:[%s] failed." % tmp_msg, '')
123                         return 'false'
124                 else:
125                     NSInstModel.objects.filter(id=self.ns_inst_id).update(status='FAILED')
126                     return 'false'
127             except Exception as e:
128                 logger.error("[cancel_sfc_list] error[%s]!" % e.message)
129                 logger.error(traceback.format_exc())
130                 JobUtil.add_job_status(self.job_id, cur_progress, "Delete sfcinst:[%s] Failed." % tmp_msg, '')
131                 return 'false'
132         return 'true'
133
134     # delete Vnf
135     def cancel_vnf_list(self):
136         array_vnfinst = NfInstModel.objects.filter(ns_inst_id=self.ns_inst_id)
137         if not array_vnfinst:
138             logger.error("[cancel_vnf_list] no vnfinst attatch to ns_inst_id:%s" % self.ns_inst_id)
139             return
140         step_progress = 20 / len(array_vnfinst)
141         cur_progress = 50
142         for vnfinst in array_vnfinst:
143             tmp_msg = vnfinst.nfinstid
144             try:
145                 self.delete_vnf(tmp_msg)
146                 cur_progress += step_progress
147                 JobUtil.add_job_status(self.job_id, cur_progress, "Delete vnfinst:[%s] success." % tmp_msg, '')
148             except Exception as e:
149                 logger.error("[cancel_vnf_list] error[%s]!" % e.message)
150                 logger.error(traceback.format_exc())
151                 JobUtil.add_job_status(self.job_id, cur_progress, "Delete vnfinst:[%s] Failed." % tmp_msg, '')
152                 return 'false'
153         return 'true'
154
155     def delete_vnf(self, nf_instid):
156         ret = call_from_ns_cancel_resource('vnf', nf_instid)
157         self.delete_resource(ret)
158
159     def delete_sfc(self, sfc_instid):
160         ret = call_from_ns_cancel_resource('sfc', sfc_instid)
161         return ret
162
163     def delete_vl(self, vl_instid):
164         ret = call_from_ns_cancel_resource('vl', vl_instid)
165         return ret
166
167     def delete_resource(self, result):
168         logger.debug("terminate_type=%s, result=%s", self.terminate_type, result)
169         if result[0] == 0:
170             job_info = json.JSONDecoder().decode(result[1])
171             vnfm_job_id = ignore_case_get(job_info, "jobid")
172             self.add_progress(5, "SEND_TERMINATE_REQ_SUCCESS")
173             if self.terminate_type == 'forceful':
174                 ret = wait_job_finish(self.vnfm_inst_id, self.job_id, vnfm_job_id,
175                                       progress_range=[10, 50],
176                                       timeout=self.terminate_timeout,
177                                       job_callback=TerminateNsService.wait_job_mode_callback, mode='1')
178                 if ret != JOB_MODEL_STATUS.FINISHED:
179                     logger.error('[NS terminate] VNFM terminate ns failed')
180                     NSInstModel.objects.filter(id=self.ns_inst_id).update(status='FAILED')
181                     raise NSLCMException("DELETE_NS_RESOURCE_FAILED")
182         else:
183             logger.error('[NS terminate] VNFM terminate ns failed')
184             NSInstModel.objects.filter(id=self.ns_inst_id).update(status='FAILED')
185             raise NSLCMException("DELETE_NS_RESOURCE_FAILED")
186
187     def exception(self):
188         NSInstModel.objects.filter(id=self.ns_inst_id).update(status='FAILED')
189         raise NSLCMException("DELETE_NS_RESOURCE_FAILED")
190
191     def finaldata(self):
192         NSInstModel.objects.filter(id=self.ns_inst_id).update(status='null')
193         JobUtil.add_job_status(self.job_id, 100, "ns terminate ends.", '')
194
195     @staticmethod
196     def call_vnfm_to_cancel_resource(res_type, instid):
197         ret = call_from_ns_cancel_resource(res_type, instid)
198         return ret
199
200     def add_progress(self, progress, status_decs, error_code=""):
201         JobUtil.add_job_status(self.job_id, progress, status_decs, error_code)
202
203     @staticmethod
204     def wait_job_mode_callback(vnfo_job_id, vnfm_job_id, job_status, jobs, progress_range, **kwargs):
205         for job in jobs:
206             progress = TerminateNsService.calc_progress_over_100(job['progress'], progress_range)
207             if 255 == progress and '1' == kwargs['mode']:
208                 break
209             JobUtil.add_job_status(vnfo_job_id, progress, job.get('statusdescription', ''), job.get('errorcode', ''))
210
211         latest_progress = TerminateNsService.calc_progress_over_100(job_status['progress'], progress_range)
212         if 255 == latest_progress and '1' == kwargs['mode']:
213             JobUtil.add_job_status(vnfo_job_id, progress_range[1], job_status.get('statusdescription', ''),
214                                    job_status.get('errorcode', ''))
215         else:
216             JobUtil.add_job_status(vnfo_job_id, latest_progress, job_status.get('statusdescription', ''),
217                                    job_status.get('errorcode', ''))
218         if job_status['status'] in ('error', 'finished'):
219             return True, job_status['status']
220         return False, 'processing'
221
222     @staticmethod
223     def wait_job_finish_common_call_back(vnfo_job_id, vnfm_job_id, job_status, jobs, progress_range, **kwargs):
224         error_254 = False
225         for job in jobs:
226             progress = TerminateNsService.calc_progress_over_100(job['progress'], progress_range)
227             if 254 == progress:
228                 logger.debug("=========254==============")
229                 progress = 255
230                 error_254 = True
231             JobUtil.add_job_status(vnfo_job_id, progress, job.get('statusdescription', ""), job.get('errorcode', ""))
232         latest_progress = TerminateNsService.calc_progress_over_100(job_status['progress'], progress_range)
233         if 254 == latest_progress:
234             logger.debug("=========254==============")
235             latest_progress = 255
236             error_254 = True
237         JobUtil.add_job_status(vnfo_job_id, latest_progress, job_status.get('statusdescription', ""),
238                                job_status.get('errorcode', ""))
239         # return error_254
240         if error_254:
241             logger.debug("return 254")
242             return True, 'error_254'
243         if job_status['status'] in ('error', 'finished'):
244             return True, job_status['status']
245         return False, 'processing'
246
247     @staticmethod
248     def calc_progress_over_100(vnfm_progress, target_range=None):
249         if target_range is None:
250             target_range = [0, 100]
251         progress = int(vnfm_progress)
252         if progress > 100:
253             return progress
254         floor_progress = int(math.floor(float(target_range[1] - target_range[0]) / 100 * progress))
255         target_range = floor_progress + target_range[0]
256         return target_range