Merge "move subscription test json"
[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.pub.database.models import NSInstModel, VLInstModel, FPInstModel, NfInstModel
21 from lcm.pub.exceptions import NSLCMException
22 from lcm.pub.msapi.nslcm import call_from_ns_cancel_resource
23 from lcm.pub.utils.jobutil import JobUtil
24 from lcm.pub.utils.values import ignore_case_get
25 from lcm.pub.utils import restcall
26 from lcm.ns.enum import OWNER_TYPE
27 from lcm.pub.database.models import PNFInstModel
28 from lcm.ns.biz.ns_lcm_op_occ import NsLcmOpOcc
29
30 JOB_ERROR = 255
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, 100, "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='null')
58             JobUtil.add_job_status(self.job_id, 100, "ns terminate ends.", '')
59             NsLcmOpOcc.update(self.occ_id, "COMPLETED")
60         except NSLCMException as e:
61             JobUtil.add_job_status(self.job_id, JOB_ERROR, e.message)
62             NsLcmOpOcc.update(self.occ_id, operationState="FAILED", error=e.message)
63         except Exception as e:
64             logger.error(e.message)
65             logger.error(traceback.format_exc())
66             JobUtil.add_job_status(self.job_id, JOB_ERROR, "ns terminate fail.")
67             NsLcmOpOcc.update(self.occ_id, operationState="FAILED", error=e.message)
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.message)
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.message)
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.message)
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         for vnfinstid, vnfjobid in vnf_jobs:
137             try:
138                 cur_progress += step_progress
139                 if not vnfjobid:
140                     continue
141                 is_job_ok = self.wait_delete_vnf_job_finish(vnfjobid)
142                 msg = "%s to delete VNF(%s)" % ("Succeed" if is_job_ok else "Failed", vnfinstid)
143                 logger.debug(msg)
144                 JobUtil.add_job_status(self.job_id, cur_progress, msg)
145             except Exception as e:
146                 msg = "Exception occurs when delete VNF(%s)" % vnfinstid
147                 logger.debug(msg)
148                 JobUtil.add_job_status(self.job_id, cur_progress, msg)
149
150     def delete_vnf(self, nf_instid):
151         term_param = {
152             "terminationType": self.terminate_type
153         }
154         if self.terminate_timeout:
155             term_param["gracefulTerminationTimeout"] = int(self.terminate_timeout)
156         ret = call_from_ns_cancel_resource('vnf', nf_instid, term_param)
157         if ret[0] != 0:
158             logger.error("Terminate VNF(%s) failed: %s", nf_instid, ret[1])
159             return ''
160         job_info = json.JSONDecoder().decode(ret[1])
161         vnf_job_id = ignore_case_get(job_info, "jobId")
162         return vnf_job_id
163
164     def wait_delete_vnf_job_finish(self, vnf_job_id):
165         count = 0
166         retry_count = 400
167         interval_second = 2
168         response_id, new_response_id = 0, 0
169         job_end_normal, job_timeout = False, True
170         while count < retry_count:
171             count = count + 1
172             time.sleep(interval_second)
173             uri = "/api/nslcm/v1/jobs/%s?responseId=%s" % (vnf_job_id, response_id)
174             ret = restcall.req_by_msb(uri, "GET")
175             if ret[0] != 0:
176                 logger.error("Failed to query job: %s:%s", ret[2], ret[1])
177                 continue
178             job_result = json.JSONDecoder().decode(ret[1])
179             if "responseDescriptor" not in job_result:
180                 logger.debug("No new progress after response_id(%s) in job(%s)", response_id, vnf_job_id)
181                 continue
182             progress = job_result["responseDescriptor"]["progress"]
183             new_response_id = job_result["responseDescriptor"]["responseId"]
184             job_desc = job_result["responseDescriptor"]["statusDescription"]
185             if new_response_id != response_id:
186                 logger.debug("%s:%s:%s", progress, new_response_id, job_desc)
187                 response_id = new_response_id
188                 count = 0
189             if progress == JOB_ERROR:
190                 job_timeout = False
191                 logger.error("Job(%s) failed: %s", vnf_job_id, job_desc)
192                 break
193             elif progress == 100:
194                 job_end_normal, job_timeout = True, False
195                 logger.info("Job(%s) ended normally", vnf_job_id)
196                 break
197         if job_timeout:
198             logger.error("Job(%s) timeout", vnf_job_id)
199         return job_end_normal
200
201     def cancel_pnf_list(self):
202         pnfinst_list = PNFInstModel.objects.filter(nsInstances__contains=self.ns_inst_id)
203         if len(pnfinst_list) > 0:
204             cur_progress = 90
205             step_progress = 5 / len(pnfinst_list)
206             for pnfinst in pnfinst_list:
207                 delete_result = "fail"
208                 try:
209                     ret = call_from_ns_cancel_resource('pnf', pnfinst.pnfId)
210                     if ret[0] == 0:
211                         delete_result = "success"
212                 except Exception as e:
213                     logger.error("[cancel_pnf_list] error[%s]!" % e.message)
214                     logger.error(traceback.format_exc())
215                 job_msg = "Delete pnfinst:[%s] %s" % (pnfinst.pnfId, delete_result)
216                 cur_progress += step_progress
217                 JobUtil.add_job_status(self.job_id, cur_progress, job_msg)