9baabf9768fe073097dfececd3940d16fb6b1754
[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             try:
132                 cur_progress += step_progress
133                 if not vnfjobid:
134                     continue
135                 is_job_ok = self.wait_delete_vnf_job_finish(vnfjobid)
136                 msg = "%s to delete VNF(%s)" % ("Succeed" if is_job_ok else "Failed", vnfinstid)
137                 logger.debug(msg)
138                 JobUtil.add_job_status(self.job_id, cur_progress, msg)
139             except Exception as e:
140                 msg = "Exception occurs when delete VNF(%s)" % vnfinstid
141                 logger.debug(msg)
142                 JobUtil.add_job_status(self.job_id, cur_progress, msg)
143
144     def delete_vnf(self, nf_instid):
145         term_param = {
146             "terminationType": self.terminate_type
147         }
148         if self.terminate_timeout:
149             term_param["gracefulTerminationTimeout"] = int(self.terminate_timeout)
150         ret = call_from_ns_cancel_resource('vnf', nf_instid, term_param)
151         if ret[0] != 0:
152             logger.error("Terminate VNF(%s) failed: %s", nf_instid, ret[1])
153             return ''
154         job_info = json.JSONDecoder().decode(ret[1])
155         vnf_job_id = ignore_case_get(job_info, "jobId")
156         return vnf_job_id
157
158     def wait_delete_vnf_job_finish(self, vnf_job_id):
159         count = 0
160         retry_count = 400
161         interval_second = 2
162         response_id, new_response_id = 0, 0
163         job_end_normal, job_timeout = False, True
164         while count < retry_count:
165             count = count + 1
166             time.sleep(interval_second)
167             uri = "/api/nslcm/v1/jobs/%s?responseId=%s" % (vnf_job_id, response_id)
168             ret = restcall.req_by_msb(uri, "GET")
169             if ret[0] != 0:
170                 logger.error("Failed to query job: %s:%s", ret[2], ret[1])
171                 continue
172             job_result = json.JSONDecoder().decode(ret[1])
173             if "responseDescriptor" not in job_result:
174                 logger.debug("No new progress after response_id(%s) in job(%s)", response_id, vnf_job_id)
175                 continue
176             progress = job_result["responseDescriptor"]["progress"]
177             new_response_id = job_result["responseDescriptor"]["responseId"]
178             job_desc = job_result["responseDescriptor"]["statusDescription"]
179             if new_response_id != response_id:
180                 logger.debug("%s:%s:%s", progress, new_response_id, job_desc)
181                 response_id = new_response_id
182                 count = 0
183             if progress == JOB_ERROR:
184                 job_timeout = False
185                 logger.error("Job(%s) failed: %s", vnf_job_id, job_desc)
186                 break
187             elif progress == 100:
188                 job_end_normal, job_timeout = True, False
189                 logger.info("Job(%s) ended normally", vnf_job_id)
190                 break
191         if job_timeout:
192             logger.error("Job(%s) timeout", vnf_job_id)
193         return job_end_normal
194
195     def cancel_pnf_list(self):
196         pnfinst_list = PNFInstModel.objects.filter(nsInstances__contains=self.ns_inst_id)
197         if len(pnfinst_list) > 0:
198             cur_progress = 90
199             step_progress = 5 / len(pnfinst_list)
200             for pnfinst in pnfinst_list:
201                 delete_result = "fail"
202                 try:
203                     ret = call_from_ns_cancel_resource('pnf', pnfinst.pnfId)
204                     if ret[0] == 0:
205                             delete_result = "success"
206                 except Exception as e:
207                     logger.error("[cancel_pnf_list] error[%s]!" % e.message)
208                     logger.error(traceback.format_exc())
209                 job_msg = "Delete pnfinst:[%s] %s" % (pnfinst.pnfId, delete_result)
210                 cur_progress += step_progress
211                 JobUtil.add_job_status(self.job_id, cur_progress, job_msg)