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