Add thread in waiting delete vnf
[vfc/nfvo/lcm.git] / lcm / ns / biz / 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 import json
15 import logging
16 import threading
17 import time
18 import traceback
19
20 from lcm.jobs.const import JOB_INSTANCE_RESPONSE_ID_URI
21 from lcm.pub.database.models import NSInstModel, VLInstModel, FPInstModel, NfInstModel
22 from lcm.pub.exceptions import NSLCMException
23 from lcm.pub.msapi.nslcm import call_from_ns_cancel_resource
24 from lcm.pub.utils.jobutil import JobUtil
25 from lcm.pub.utils.values import ignore_case_get
26 from lcm.pub.utils import restcall
27 from lcm.ns.enum import OWNER_TYPE
28 from lcm.pub.database.models import PNFInstModel
29 from lcm.ns.biz.ns_lcm_op_occ import NsLcmOpOcc
30 from lcm.jobs.enum import JOB_PROGRESS
31
32 logger = logging.getLogger(__name__)
33
34
35 class TerminateNsService(threading.Thread):
36     def __init__(self, ns_inst_id, job_id, request_data):
37         threading.Thread.__init__(self)
38         self.terminate_type = request_data.get('terminationType', 'GRACEFUL')
39         self.terminate_timeout = request_data.get('gracefulTerminationTimeout', 600)
40         self.job_id = job_id
41         self.ns_inst_id = ns_inst_id
42         self.occ_id = NsLcmOpOcc.create(ns_inst_id, "TERMINATE", "PROCESSING", False, request_data)
43
44     def run(self):
45         try:
46             if not NSInstModel.objects.filter(id=self.ns_inst_id):
47                 JobUtil.add_job_status(self.job_id, JOB_PROGRESS.FINISHED, "Need not terminate.", '')
48                 NsLcmOpOcc.update(self.occ_id, "COMPLETED")
49                 return
50             JobUtil.add_job_status(self.job_id, 10, "Starting terminate...", '')
51
52             self.cancel_sfc_list()
53             self.cancel_vnf_list()
54             self.cancel_vl_list()
55             self.cancel_pnf_list()
56
57             NSInstModel.objects.filter(id=self.ns_inst_id).update(status='NOT_INSTANTIATED')
58             JobUtil.add_job_status(self.job_id, JOB_PROGRESS.FINISHED, "ns terminate ends.", '')
59             NsLcmOpOcc.update(self.occ_id, "COMPLETED")
60         except NSLCMException as e:
61             JobUtil.add_job_status(self.job_id, JOB_PROGRESS.ERROR, e.args[0])
62             NsLcmOpOcc.update(self.occ_id, operationState="FAILED", error=e.args[0])
63         except Exception as e:
64             logger.error(e.args[0])
65             logger.error(traceback.format_exc())
66             JobUtil.add_job_status(self.job_id, JOB_PROGRESS.ERROR, "ns terminate fail.")
67             NsLcmOpOcc.update(self.occ_id, operationState="FAILED", error=e.args[0])
68
69     def cancel_vl_list(self):
70         array_vlinst = VLInstModel.objects.filter(ownertype=OWNER_TYPE.NS, ownerid=self.ns_inst_id)
71         if not array_vlinst:
72             logger.info("[cancel_vl_list] no vlinst attatch to ns_inst_id: %s" % self.ns_inst_id)
73             return
74         step_progress = 20 / len(array_vlinst)
75         cur_progress = 70
76         for vlinst in array_vlinst:
77             delete_result = "failed"
78             cur_progress += step_progress
79             try:
80                 ret = call_from_ns_cancel_resource('vl', vlinst.vlinstanceid)
81                 if ret[0] == 0:
82                     result = json.JSONDecoder().decode(ret[1]).get("result", "")
83                     if str(result) == '0':
84                         delete_result = "success"
85             except Exception as e:
86                 logger.error("[cancel_vl_list] error[%s]!" % e.args[0])
87                 logger.error(traceback.format_exc())
88             job_msg = "Delete vlinst:[%s] %s." % (vlinst.vlinstanceid, delete_result)
89             JobUtil.add_job_status(self.job_id, cur_progress, job_msg)
90
91     def cancel_sfc_list(self):
92         array_sfcinst = FPInstModel.objects.filter(nsinstid=self.ns_inst_id)
93         if not array_sfcinst:
94             logger.info("[cancel_sfc_list] no sfcinst attatch to ns_inst_id: %s" % self.ns_inst_id)
95             return
96         step_progress = 20 / len(array_sfcinst)
97         cur_progress = 30
98         for sfcinst in array_sfcinst:
99             cur_progress += step_progress
100             delete_result = "failed"
101             try:
102                 ret = call_from_ns_cancel_resource('sfc', sfcinst.sfcid)
103                 if ret[0] == 0:
104                     result = json.JSONDecoder().decode(ret[1]).get("result", "")
105                     if str(result) == '0':
106                         delete_result = "success"
107             except Exception as e:
108                 logger.error("[cancel_sfc_list] error[%s]!" % e.args[0])
109                 logger.error(traceback.format_exc())
110             job_msg = "Delete sfcinst:[%s] %s." % (sfcinst.sfcid, delete_result)
111             JobUtil.add_job_status(self.job_id, cur_progress, job_msg)
112
113     def cancel_vnf_list(self):
114         array_vnfinst = NfInstModel.objects.filter(ns_inst_id=self.ns_inst_id)
115         if not array_vnfinst:
116             logger.info("[cancel_vnf_list] no vnfinst attatch to ns_inst_id: %s" % self.ns_inst_id)
117             return
118         step_progress = 10 / len(array_vnfinst)
119         cur_progress = 50
120         vnf_jobs = []
121         for vnfinst in array_vnfinst:
122             cur_progress += step_progress
123             delete_result = "failed"
124             vnf_job_id = ''
125             try:
126                 vnf_job_id = self.delete_vnf(vnfinst.nfinstid)
127                 if vnf_job_id:
128                     delete_result = "deleting"
129             except Exception as e:
130                 logger.error("[cancel_vnf_list] error[%s]!" % e.args[0])
131                 logger.error(traceback.format_exc())
132             job_msg = "Delete vnfinst:[%s] %s." % (vnfinst.nfinstid, delete_result)
133             JobUtil.add_job_status(self.job_id, cur_progress, job_msg)
134             vnf_jobs.append((vnfinst.nfinstid, vnf_job_id))
135
136         thread = threading.Thread(
137             target=self.wait_delete_vnfs,
138             args=(vnf_jobs, cur_progress, step_progress,))
139         thread.start()
140
141     def wait_delete_vnfs(self, vnf_jobs, cur_progress, step_progress):
142         for vnfinstid, vnfjobid in vnf_jobs:
143             try:
144                 cur_progress += step_progress
145                 if not vnfjobid:
146                     continue
147                 is_job_ok = self.wait_delete_vnf_job_finish(vnfjobid)
148                 msg = "%s to delete VNF(%s)" %\
149                       ("Succeed" if is_job_ok else "Failed", vnfinstid)
150                 logger.debug(msg)
151                 JobUtil.add_job_status(self.job_id, cur_progress, msg)
152             except Exception as e:
153                 msg = "Exception occurs(%s) when delete VNF(%s)" % (e, vnfinstid)
154                 logger.debug(msg)
155                 JobUtil.add_job_status(self.job_id, cur_progress, msg)
156
157     def delete_vnf(self, nf_instid):
158         term_param = {
159             "terminationType": self.terminate_type
160         }
161         if self.terminate_timeout:
162             term_param["gracefulTerminationTimeout"] = int(self.terminate_timeout)
163         ret = call_from_ns_cancel_resource('vnf', nf_instid, term_param)
164         if ret[0] != 0:
165             logger.error("Terminate VNF(%s) failed: %s", nf_instid, ret[1])
166             return ''
167         job_info = json.JSONDecoder().decode(ret[1])
168         vnf_job_id = ignore_case_get(job_info, "jobId")
169         return vnf_job_id
170
171     def wait_delete_vnf_job_finish(self, vnf_job_id):
172         count = 0
173         retry_count = 400
174         interval_second = 2
175         response_id, new_response_id = 0, 0
176         job_end_normal, job_timeout = False, True
177         while count < retry_count:
178             count = count + 1
179             time.sleep(interval_second)
180             uri = JOB_INSTANCE_RESPONSE_ID_URI % (vnf_job_id, response_id)
181             ret = restcall.req_by_msb(uri, "GET")
182             if ret[0] != 0:
183                 logger.error("Failed to query job: %s:%s", ret[2], ret[1])
184                 continue
185             job_result = json.JSONDecoder().decode(ret[1])
186             if "responseDescriptor" not in job_result:
187                 logger.debug("No new progress after response_id(%s) in job(%s)", response_id, vnf_job_id)
188                 continue
189             progress = job_result["responseDescriptor"]["progress"]
190             new_response_id = job_result["responseDescriptor"]["responseId"]
191             job_desc = job_result["responseDescriptor"]["statusDescription"]
192             if new_response_id != response_id:
193                 logger.debug("%s:%s:%s", progress, new_response_id, job_desc)
194                 response_id = new_response_id
195                 count = 0
196             if progress == JOB_PROGRESS.ERROR:
197                 job_timeout = False
198                 logger.error("Job(%s) failed: %s", vnf_job_id, job_desc)
199                 break
200             elif progress == JOB_PROGRESS.FINISHED:
201                 job_end_normal, job_timeout = True, False
202                 logger.info("Job(%s) ended normally", vnf_job_id)
203                 break
204         if job_timeout:
205             logger.error("Job(%s) timeout", vnf_job_id)
206         return job_end_normal
207
208     def cancel_pnf_list(self):
209         pnfinst_list = PNFInstModel.objects.filter(nsInstances__contains=self.ns_inst_id)
210         if len(pnfinst_list) > 0:
211             cur_progress = 90
212             step_progress = 5 / len(pnfinst_list)
213             for pnfinst in pnfinst_list:
214                 delete_result = "fail"
215                 try:
216                     ret = call_from_ns_cancel_resource('pnf', pnfinst.pnfId)
217                     if ret[0] == 0:
218                         delete_result = "success"
219                 except Exception as e:
220                     logger.error("[cancel_pnf_list] error[%s]!" % e.args[0])
221                     logger.error(traceback.format_exc())
222                 job_msg = "Delete pnfinst:[%s] %s" % (pnfinst.pnfId, delete_result)
223                 cur_progress += step_progress
224                 JobUtil.add_job_status(self.job_id, cur_progress, job_msg)