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