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