Add lcm op occ to heal vnf
[vfc/gvnfm/vnflcm.git] / lcm / lcm / nf / biz / heal_vnf.py
1 # Copyright (C) 2018 Verizon. All Rights Reserved.
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
15 import json
16 import logging
17 import traceback
18 import uuid
19 from threading import Thread
20
21 from lcm.pub.database.models import NfInstModel
22 from lcm.pub.database.models import VmInstModel
23 from lcm.pub.database.models import VNFCInstModel
24 from lcm.pub.exceptions import NFLCMException
25 from lcm.pub.exceptions import NFLCMExceptionConflict
26 from lcm.pub.utils.jobutil import JobUtil
27 from lcm.pub.utils.notificationsutil import NotificationsUtil
28 from lcm.pub.utils.timeutil import now_time
29 from lcm.pub.utils.values import ignore_case_get
30 from lcm.pub.vimapi import adaptor
31 from lcm.nf.biz.grant_vnf import grant_resource
32 from lcm.nf.const import VNF_STATUS, GRANT_TYPE
33 from lcm.nf.const import OPERATION_STATE_TYPE, LCM_NOTIFICATION_STATUS
34 from lcm.nf.const import CHANGE_TYPE, OPERATION_TYPE, HEAL_ACTION_TYPE
35 from lcm.nf.const import OPERATION_TASK
36 from lcm.nf.const import SUB_OPERATION_TASK
37 from lcm.nf.biz import common
38 from .operate_vnf_lcm_op_occ import VnfLcmOpOcc
39
40
41 logger = logging.getLogger(__name__)
42
43
44 class HealVnf(Thread):
45     def __init__(self, data, nf_inst_id, job_id):
46         super(HealVnf, self).__init__()
47         self.data = data
48         self.nf_inst_id = nf_inst_id
49         self.job_id = job_id
50         self.affectedvm = ignore_case_get(
51             ignore_case_get(
52                 self.data,
53                 "additionalParams"
54             ),
55             "affectedvm"
56         )
57         # TODO: Check if we could move the action param into the list of affectedvm structure
58         self.action = ignore_case_get(
59             ignore_case_get(
60                 self.data,
61                 "additionalParams"
62             ),
63             "action"
64         )
65         self.grant_type = ""
66         if self.action == HEAL_ACTION_TYPE.START:
67             self.grant_type = GRANT_TYPE.HEAL_CREATE
68         elif self.action == HEAL_ACTION_TYPE.RESTART:
69             self.grant_type = GRANT_TYPE.HEAL_RESTART
70         self.lcm_op_occ = VnfLcmOpOcc(
71             vnf_inst_id=nf_inst_id,
72             lcm_op_id=job_id,
73             operation=OPERATION_TYPE.HEAL,
74             task=OPERATION_TASK.HEAL
75         )
76         self.pre_deal()
77
78     def run(self):
79         try:
80             self.heal_pre()
81             self.lcm_op_occ.notify_lcm(OPERATION_STATE_TYPE.STARTING)
82             self.apply_grant()
83             self.lcm_op_occ.upd(
84                 sub_operation=SUB_OPERATION_TASK.GRANTED,
85                 operation_state=OPERATION_STATE_TYPE.PROCESSING
86             )
87             self.lcm_op_occ.notify_lcm(OPERATION_STATE_TYPE.PROCESSING)
88             self.heal_resource()
89             JobUtil.add_job_status(self.job_id, 100, "Heal Vnf success.")
90             NfInstModel.objects.filter(nfinstid=self.nf_inst_id).update(
91                 status='INSTANTIATED',
92                 lastuptime=now_time()
93             )
94             self.lcm_notify(
95                 LCM_NOTIFICATION_STATUS.RESULT,
96                 OPERATION_STATE_TYPE.COMPLETED
97             )
98             self.lcm_op_occ.upd(
99                 sub_operation=SUB_OPERATION_TASK.SUCCESS,
100                 operation_state=OPERATION_STATE_TYPE.COMPLETED
101             )
102         except NFLCMException as e:
103             logger.error(e.message)
104             self.vnf_heal_failed_handle(e.message)
105         except Exception as e:
106             logger.error(e.message)
107             logger.error(traceback.format_exc())
108             self.vnf_heal_failed_handle(e.message)
109
110     def pre_deal(self):
111         logger.debug("Start pre deal for VNF heal_vnf task")
112
113         vnf_is_in_processing, vnf_op = self.lcm_op_occ.is_in_processing()
114         if vnf_is_in_processing:
115             raise NFLCMExceptionConflict('VNF(%s) %s in processing.' % (
116                 self.nf_inst_id, vnf_op
117             ))
118         self.lcm_op_occ.add()
119
120     def heal_pre(self):
121         if self.action not in (HEAL_ACTION_TYPE.START, HEAL_ACTION_TYPE.RESTART):
122             raise NFLCMException("Action should be %s or %s" % (HEAL_ACTION_TYPE.START, HEAL_ACTION_TYPE.RESTART))
123
124         self.vm_id = ignore_case_get(self.affectedvm, "vmid")
125         self.vdu_id = ignore_case_get(self.affectedvm, "vduid")
126         self.vm_name = ignore_case_get(self.affectedvm, "vmname")
127         if not (self.vm_id and self.vdu_id and self.vm_name):
128             raise NFLCMException("VM identifiers is not present in request.")
129
130         self.vnf_insts = NfInstModel.objects.filter(nfinstid=self.nf_inst_id)
131         self.vnfd_info = json.loads(self.vnf_insts[0].vnfd_model)
132
133     def apply_grant(self):
134         if self.action == HEAL_ACTION_TYPE.RESTART:
135             self.vdu = VmInstModel.objects.filter(instid=self.nf_inst_id, resourceid=self.vm_id)
136             if not self.vdu:
137                 raise NFLCMException("VNF Vm(%s) does not exist." % self.vm_id)
138             self.vimid = self.vdu[0].vimid
139             self.tenant = self.vdu[0].tenant
140             logger.debug("Get heal vnf vm(%s,%s) info successfully.", self.vm_id, self.vm_name)
141             JobUtil.add_job_status(self.job_id, 20, 'Nf Healing get vnf vm info finish')
142             return
143
144         vdus = ignore_case_get(self.vnfd_info, "vdus")
145         self.vdu = [elem for elem in vdus if ignore_case_get(elem, "vdu_id") == self.vdu_id]
146         if not self.vdu:
147             raise NFLCMException("VNF Vdu(%s) does not exist." % self.vdu_id)
148         apply_result = grant_resource(data=self.data, nf_inst_id=self.nf_inst_id, job_id=self.job_id,
149                                       grant_type=self.grant_type, vdus=self.vdu)
150
151         self.vimid = ignore_case_get(apply_result, "vimid"),
152         self.tenant = ignore_case_get(apply_result, "tenant")
153         logger.info("Grant resource, response: %s" % apply_result)
154         JobUtil.add_job_status(self.job_id, 20, 'Nf Healing grant_resource finish')
155
156     def heal_resource(self):
157         logger.info('Heal resource begin')
158         data = {'action': self.action, 'vimid': self.vimid, 'tenant': self.tenant}
159         adaptor.heal_vim_res(self.vdu, self.vnfd_info, self.do_notify, data, json.loads(self.vnf_insts[0].vimInfo), json.loads(self.vnf_insts[0].resInfo))
160         logger.info('Heal resource complete')
161
162     def do_notify(self, res_type, ret):
163         logger.info('Creating [%s] resource' % res_type)
164         resource_save_method = getattr(common, res_type + '_save')
165         resource_save_method(self.job_id, self.nf_inst_id, ret)
166
167     def vnf_heal_failed_handle(self, error_msg):
168         logger.error('VNF Healing failed, detail message: %s' % error_msg)
169         NfInstModel.objects.filter(nfinstid=self.nf_inst_id).update(
170             status=VNF_STATUS.FAILED,
171             lastuptime=now_time()
172         )
173         self.lcm_op_occ.notify_lcm(OPERATION_STATE_TYPE.FAILED, error_msg)
174         JobUtil.add_job_status(self.job_id, 255, error_msg)
175         self.lcm_op_occ.upd(
176             sub_operation=SUB_OPERATION_TASK.ERROR,
177             operation_state=OPERATION_STATE_TYPE.FAILED,
178             error={
179                 "status": 500,
180                 "detail": error_msg
181             }
182         )
183
184     def lcm_notify(self, status, opState, err=None):
185         notification_content = self.prepareNotificationData(status, opState, err)
186         logger.info('Notify data = %s' % notification_content)
187         NotificationsUtil().send_notification(notification_content)
188         logger.info('Notify end')
189
190     def prepareNotificationData(self, status, opState, err=None):
191         affected_vnfcs = []
192         if status == LCM_NOTIFICATION_STATUS.RESULT and opState == OPERATION_STATE_TYPE.COMPLETED:
193             chtype = ""
194             if self.action == HEAL_ACTION_TYPE.START:
195                 chtype = CHANGE_TYPE.ADDED
196             else:
197                 chtype = CHANGE_TYPE.MODIFIED
198             vnfcs = VNFCInstModel.objects.filter(instid=self.nf_inst_id, vmid=self.vm_id)
199             vm_resource = {}
200             vm = VmInstModel.objects.filter(instid=self.nf_inst_id, resourceid=self.vm_id)
201             if vm:
202                 vm_resource = {
203                     'vimConnectionId': vm[0].vimid,
204                     'resourceId': vm[0].resourceid,
205                     'vimLevelResourceType': 'vm'
206                 }
207             if vnfcs:
208                 affected_vnfcs.append({
209                     'id': vnfcs[0].vnfcinstanceid,
210                     'vduId': vnfcs[0].vduid,
211                     'changeType': chtype,
212                     'computeResource': vm_resource
213                 })
214
215         notification_content = {
216             "id": str(uuid.uuid4()),
217             "notificationType": "VnfLcmOperationOccurrenceNotification",
218             "subscriptionId": "",
219             "timeStamp": now_time(),
220             "notificationStatus": status,
221             "operationState": opState,
222             "vnfInstanceId": self.nf_inst_id,
223             "operation": OPERATION_TYPE.HEAL,
224             "isAutomaticInvocation": "false",
225             "vnfLcmOpOccId": self.job_id,
226             "affectedVnfcs": affected_vnfcs,
227             "affectedVirtualLinks": [],
228             "affectedVirtualStorages": [],
229             "changedInfo": {},
230             "changedExtConnectivity": [],
231             "_links": {"vnfInstance": {"href": ""},
232                        "subscription": {"href": ""},
233                        "vnfLcmOpOcc": {"href": ""}}
234         }
235         if opState in (OPERATION_STATE_TYPE.FAILED, OPERATION_STATE_TYPE.FAILED_TEMP):
236             notification_content["error"] = {"status": 500, "detail": err}
237         notification_content["_links"]["vnfInstance"]["href"] = "/vnf_instances/%s" % self.nf_inst_id
238         notification_content["_links"]["vnfLcmOpOcc"]["href"] = "/vnf_lc_ops/%s" % self.job_id
239         return notification_content