Merge "Fix Healing so image is updated after Healing"

This commit is contained in:
Zuul 2021-08-01 15:47:53 +00:00 committed by Gerrit Code Review
commit 79714e44bf
8 changed files with 238 additions and 14 deletions

View File

@ -1953,6 +1953,21 @@ class Conductor(manager.Manager):
error_point=vnf_dict['current_error_point']
)
def _update_vnf_attributes_stack_param(self, context, vnf_dict, vnf_id,
heal_vnf_request, inst_vnf_info):
stack_param = vnflcm_utils.get_stack_param(
context, vnf_dict, heal_vnf_request, inst_vnf_info)
# update vnf_attribute in DB
with context.session.begin(subtransactions=True):
vnf_attr_model = (context.session.query(
vnfm_db.VNFAttribute).
filter_by(vnf_id=vnf_id).
filter_by(key='stack_param').first())
if vnf_attr_model:
vnf_attr_model.update({'value': str(stack_param)})
@coordination.synchronized('{vnf_instance[id]}')
def heal(self,
context,
@ -1998,6 +2013,11 @@ class Conductor(manager.Manager):
self._update_instantiated_vnf_info(context, vnf_instance,
heal_vnf_request)
# update stack_param in vnf_attribute table
self._update_vnf_attributes_stack_param(
context, vnf_dict, vnf_instance.id, heal_vnf_request,
vnf_instance.instantiated_vnf_info)
# update instance_in in vnf_table
self._add_additional_vnf_info(context, vnf_instance)

View File

@ -10,6 +10,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import json
import os
import tempfile
import time
@ -1253,3 +1254,14 @@ class BaseVnfLcmTest(base.BaseTackerTest):
return [
h for h in notify_histories
if h.request_body.get('vnfInstanceId') == vnf_instance_id]
def _get_heat_stack_show(self, vnf_instance_id, resource_name):
"""Retrieve image name of the resource from stack"""
try:
stack = self._get_heat_stack(vnf_instance_id)
stack_info = self.h_client.stacks.get(stack.id)
stack_dict = stack_info.to_dict()
resource_dict = json.loads(stack_dict['parameters']['nfv'])
except Exception:
return None
return resource_dict['VDU'][resource_name]['image']

View File

@ -374,6 +374,13 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
resp, vnf_instance = self._show_vnf_instance(vnf_instance_id)
self.assertEqual(200, resp.status_code)
# Get VNF image after instantiate VDU2
image_before_update = self._get_heat_stack_show(
vnf_instance_id, 'VDU2')
self.assertIsNotNone(
image_before_update,
"failed to retrieve image")
# Update vnf (vnfdId)
sample_name = 'functional2'
csar_package_path = os.path.abspath(
@ -409,6 +416,12 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id)
self.assert_heal_vnf(resp, vnf_instance_id, vnf_package_id)
# Check of Image changes after heal
image_after_heal = self._get_heat_stack_show(vnf_instance_id, 'VDU2')
self.assertIsNotNone(
image_after_heal, "failed to retrieve image")
self.assertNotEqual(image_before_update, image_after_heal)
# Terminate VNF
stack = self._get_heat_stack(vnf_instance_id)
resources_list = self._get_heat_resource_list(stack.id)

View File

@ -541,5 +541,5 @@ class VnfLcmWithNfvoSeparator(vnflcm_base.BaseVnfLcmTest):
physical_resource_id = [r.physical_resource_id for r
in resources_list if stack_name_wd in r.stack_name]
template = self._get_heat_stack_template(physical_resource_id[0])
template_count = str(template).count("zone")
template_count = str(template).count("flavor")
self.assertEqual(template_count, 3)

View File

@ -1344,6 +1344,8 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
mock_log.error.assert_called_once_with(expected_msg,
vnf_package_vnfd.package_uuid)
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_update_vnf_attributes_stack_param')
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_add_additional_vnf_info')
@mock.patch('tacker.conductor.conductor_server.Conductor.'
@ -1355,7 +1357,8 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
@mock.patch.object(objects.VnfLcmOpOcc, "get_by_id")
def test_heal_vnf_instance(self, mock_vnf_by_id,
mock_get_lock, mock_save, mock_change_vnf_status,
mock_update_insta_vnf_info, mock_add_additional_vnf_info):
mock_update_insta_vnf_info, mock_add_additional_vnf_info,
mock_update_vnf_attributes_stack_param):
lcm_op_occs_data = fakes.get_lcm_op_occs_data()
mock_vnf_by_id.return_value = \
objects.VnfLcmOpOcc(context=self.context,
@ -1380,7 +1383,12 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
assert_called_once_with(self.context, vnf_instance, heal_vnf_req)
mock_add_additional_vnf_info. \
assert_called_once_with(self.context, vnf_instance)
mock_update_vnf_attributes_stack_param.assert_called_once_with(
self.context, vnf_dict, vnf_instance.id, heal_vnf_req,
vnf_instance.instantiated_vnf_info)
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_update_vnf_attributes_stack_param')
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_add_additional_vnf_info')
@mock.patch('tacker.conductor.conductor_server.Conductor.'
@ -1393,7 +1401,8 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
def test_heal_vnf_instance_error_point_notify_processing(
self, mock_vnf_by_id, mock_get_lock, mock_save,
mock_change_vnf_status, mock_update_insta_vnf_info,
mock_add_additional_vnf_info):
mock_add_additional_vnf_info,
mock_update_vnf_attributes_stack_param):
lcm_op_occs_data = fakes.get_lcm_op_occs_data()
mock_vnf_by_id.return_value = \
objects.VnfLcmOpOcc(context=self.context,
@ -1418,7 +1427,12 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
assert_called_once_with(self.context, vnf_instance, heal_vnf_req)
mock_add_additional_vnf_info. \
assert_called_once_with(self.context, vnf_instance)
mock_update_vnf_attributes_stack_param.assert_called_once_with(
self.context, vnf_dict, vnf_instance.id, heal_vnf_req,
vnf_instance.instantiated_vnf_info)
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_update_vnf_attributes_stack_param')
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_add_additional_vnf_info')
@mock.patch('tacker.conductor.conductor_server.Conductor.'
@ -1431,7 +1445,8 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
def test_heal_vnf_instance_error_point_internal_processing(
self, mock_vnf_by_id, mock_get_lock, mock_save,
mock_change_vnf_status, mock_update_insta_vnf_info,
mock_add_additional_vnf_info):
mock_add_additional_vnf_info,
mock_update_vnf_attributes_stack_param):
lcm_op_occs_data = fakes.get_lcm_op_occs_data()
mock_vnf_by_id.return_value = \
objects.VnfLcmOpOcc(context=self.context,
@ -1456,6 +1471,9 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
assert_called_once_with(self.context, vnf_instance, heal_vnf_req)
mock_add_additional_vnf_info. \
assert_called_once_with(self.context, vnf_instance)
mock_update_vnf_attributes_stack_param.assert_called_once_with(
self.context, vnf_dict, vnf_instance.id, heal_vnf_req,
vnf_instance.instantiated_vnf_info)
@mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver'
'.heal_vnf')
@ -1496,6 +1514,8 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
self.assertEqual(mock_update_insta_vnf_info.call_count, 0)
self.assertEqual(mock_add_additional_vnf_info.call_count, 0)
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_update_vnf_attributes_stack_param')
@mock.patch('tacker.conductor.conductor_server.Conductor'
'._change_vnf_status')
@mock.patch('tacker.conductor.conductor_server.Conductor'
@ -1528,7 +1548,8 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
mock_save,
mock_add_vnf_info,
mock_update_vnf_info,
mock_change_status):
mock_change_status,
mock_update_vnf_attributes_stack_param):
vnf_package_vnfd = self._create_and_upload_vnf_package()
vnf_instance_data = fake_obj.get_vnf_instance_data(
vnf_package_vnfd.vnfd_id)
@ -1581,9 +1602,12 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
heal_vnf_req, vnf_lcm_op_occs_id)
mock_add_vnf_info.assert_called_once()
mock_update_vnf_info.assert_called_once()
mock_update_vnf_attributes_stack_param.assert_called_once()
self.vnflcm_driver.heal_vnf.assert_called_once_with(
self.context, mock.ANY, vnf_dict, heal_vnf_req)
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_update_vnf_attributes_stack_param')
@mock.patch('tacker.conductor.conductor_server.Conductor'
'._change_vnf_status')
@mock.patch('tacker.conductor.conductor_server.Conductor'
@ -1619,7 +1643,8 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
mock_save,
mock_add_vnf_info,
mock_update_vnf_info,
mock_change_status):
mock_change_status,
mock_update_vnf_attribute_stack_param):
vnf_package_vnfd = self._create_and_upload_vnf_package()
vnf_instance_data = fake_obj.get_vnf_instance_data(
vnf_package_vnfd.vnfd_id)
@ -1746,6 +1771,7 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
heal_vnf_req, vnf_lcm_op_occs_id)
mock_add_vnf_info.assert_called_once()
mock_update_vnf_info.assert_called_once()
mock_update_vnf_attribute_stack_param.assert_called_once()
self.vnflcm_driver.heal_vnf.assert_called_once_with(
self.context, mock.ANY, vnf_dict, heal_vnf_req)
@ -1916,6 +1942,8 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
mock_send.call_args[0][1].get('operationState'),
'ROLLED_BACK')
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_update_vnf_attributes_stack_param')
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_send_lcm_op_occ_notification')
@mock.patch('tacker.conductor.conductor_server.Conductor.'
@ -1929,7 +1957,7 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
def test_heal_vnf_instance_exception(self,
mock_log, mock_get_lock, mock_add_additional_vnf_info,
mock_change_vnf_status, mock_update_insta_vnf_info,
mock_send_notification):
mock_send_notification, mock_update_vnf_attributes_stack_param):
vnf_package_vnfd = self._create_and_upload_vnf_package()
vnf_instance_data = fake_obj.get_vnf_instance_data(
vnf_package_vnfd.vnfd_id)
@ -1951,6 +1979,9 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
vnf_instance.id, mock.ANY, constants.ERROR, "")
mock_update_insta_vnf_info.assert_called_with(self.context,
vnf_instance, heal_vnf_req)
mock_update_vnf_attributes_stack_param.assert_called_once_with(
self.context, vnf_dict, vnf_instance.id, heal_vnf_req,
vnf_instance.instantiated_vnf_info)
self.assertEqual(mock_send_notification.call_count, 2)
@unittest.skip("Such test is no longer feasible.")

View File

@ -49,6 +49,29 @@ vnf_dict = {
}
class FakeVNFMPlugin(mock.Mock):
def __init__(self):
super(FakeVNFMPlugin, self).__init__()
def get_vnf(self, context, vnf_id):
return {
'attributes': {},
'status': 'ACTIVE',
'vnfd_id': 'e889e4fe-52fe-437d-b1e1-a690dc95e3f8',
'tenant_id': '13d2ca8de70d48b2a2e0dbac2c327c0b',
'vim_id': '3f41faa7-5630-47d2-9d4a-1216953c8887',
'instance_id': 'd1121d3c-368b-4ac2-b39d-835aa3e4ccd8',
'placement_attr': {'vim_name': 'kubernetes-vim'},
'id': '436aaa6e-2db6-4d6e-a3fc-e728b2f0ac56',
'name': 'cnf_create_1',
'vnfd': {
'attributes': {
'vnfd_simple': 'dummy'
}
}
}
class FakeAlarmPlugin():
def add_alarm_url_to_vnf(self, context, vnf):
return
@ -66,6 +89,10 @@ class TestOpenStack(base.FixturedTestCase):
def setUp(self):
super(TestOpenStack, self).setUp()
self.patcher = mock.patch(
'tacker.manager.TackerManager.get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
self.mock_manager = self.patcher.start()
self.openstack = openstack.OpenStack()
self.context = context.get_admin_context()
self.heat_url = client.HEAT_URL
@ -1787,8 +1814,16 @@ class TestOpenStack(base.FixturedTestCase):
ext_managed_virtual_link_info[0].vnf_link_ports[0].
resource_handle.resource_id)
@mock.patch('tacker.vnflcm.utils.get_base_nest_hot_dict')
@mock.patch('tacker.vnflcm.utils._get_vnf_package_path')
@mock.patch.object(objects.VnfLcmOpOcc, "get_by_vnf_instance_id")
def test_heal_vnf_instance(self, mock_get_vnflcm_op_occs):
def test_heal_vnf_instance(self, mock_get_vnflcm_op_occs,
mock_get_vnf_package_path,
mock_get_base_hot_dict):
nested_hot_dict = {'parameters': {'vnf': 'test'}}
mock_get_base_hot_dict.return_value = \
self._read_file(), nested_hot_dict
v_s_resource_info = fd_utils.get_virtual_storage_resource_info(
desc_id="storage1")
@ -1836,7 +1871,8 @@ class TestOpenStack(base.FixturedTestCase):
error_point=fields.ErrorPoint.PRE_VIM_CONTROL)
mock_get_vnflcm_op_occs.return_value = vnf_lcm_op_occs
self.openstack.heal_vnf(
self.context, vnf_instance, vim_connection_info, heal_vnf_request)
self.context, vnf_instance, vim_connection_info,
heal_vnf_request)
history = self.requests_mock.request_history
patch_req = [req.url for req in history if req.method == 'PATCH']
@ -1844,9 +1880,16 @@ class TestOpenStack(base.FixturedTestCase):
# as unhealthy, and 1 for updating stack
self.assertEqual(3, len(patch_req))
@mock.patch('tacker.vnflcm.utils.get_base_nest_hot_dict')
@mock.patch('tacker.vnflcm.utils._get_vnf_package_path')
@mock.patch.object(objects.VnfLcmOpOcc, "get_by_vnf_instance_id")
def test_heal_vnf_instance_error_point_post_vim_control(
self, mock_get_vnflcm_op_occs):
self, mock_get_vnflcm_op_occs, mock_get_vnf_package_path,
mock_get_base_hot_dict):
nested_hot_dict = {'parameters': {'vnf': 'test'}}
mock_get_base_hot_dict.return_value = \
self._read_file(), nested_hot_dict
v_s_resource_info = fd_utils.get_virtual_storage_resource_info(
desc_id="storage1")
@ -1892,16 +1935,18 @@ class TestOpenStack(base.FixturedTestCase):
error_point=fields.ErrorPoint.POST_VIM_CONTROL)
mock_get_vnflcm_op_occs.return_value = vnf_lcm_op_occs
self.openstack.heal_vnf(
self.context, vnf_instance, vim_connection_info, heal_vnf_request)
self.context, vnf_instance, vim_connection_info,
heal_vnf_request)
history = self.requests_mock.request_history
patch_req = [req.url for req in history if req.method == 'PATCH']
# Total of 1 times for updating stack
self.assertEqual(1, len(patch_req))
@mock.patch('tacker.vnflcm.utils.get_base_nest_hot_dict')
@mock.patch.object(objects.VnfLcmOpOcc, "get_by_vnf_instance_id")
def test_heal_vnf_instance_resource_mark_unhealthy_error(
self, mock_get_vnflcm_op_occs):
self, mock_get_vnflcm_op_occs, mock_get_base_hot_dict):
vnfc_resource_info = fd_utils.get_vnfc_resource_info(vdu_id="VDU_VNF")
inst_vnf_info = fd_utils.get_vnf_instantiated_info(
@ -1916,6 +1961,10 @@ class TestOpenStack(base.FixturedTestCase):
vnfc_instance_id=[vnfc_resource_info.id],
cause="healing request")
nested_hot_dict = {'parameters': {'vnf': 'test'}}
mock_get_base_hot_dict.return_value = (
self._read_file(), nested_hot_dict)
# Mock various heat APIs that will be called by heatclient
# during the process of heal_vnf.
resources = [{
@ -1952,9 +2001,10 @@ class TestOpenStack(base.FixturedTestCase):
# as unhealthy
self.assertEqual(1, len(patch_req))
@mock.patch('tacker.vnflcm.utils.get_base_nest_hot_dict')
@mock.patch.object(objects.VnfLcmOpOcc, "get_by_vnf_instance_id")
def test_heal_vnf_instance_incorrect_stack_status(
self, mock_get_vnflcm_op_occs):
self, mock_get_vnflcm_op_occs, mock_get_base_hot_dict):
inst_vnf_info = fd_utils.get_vnf_instantiated_info()
vnf_instance = fd_utils.get_vnf_instance_object(
@ -1966,6 +2016,10 @@ class TestOpenStack(base.FixturedTestCase):
vnfc_instance_id=[uuidsentinel.vnfc_resource_id],
cause="healing request")
nested_hot_dict = {'parameters': {'vnf': 'test'}}
mock_get_base_hot_dict.return_value = (
self._read_file(), nested_hot_dict)
# Mock various heat APIs that will be called by heatclient
# during the process of heal_vnf.
self._response_in_stack_get(inst_vnf_info.instance_id,

View File

@ -28,6 +28,7 @@ from tacker.extensions import nfvo
from tacker import objects
from tacker.objects import fields
from tacker.tosca import utils as toscautils
from tacker.vnfm.lcm_user_data import utils as userdata_utils
from tacker.vnfm import vim_client
LOG = logging.getLogger(__name__)
@ -1314,3 +1315,63 @@ def _get_changed_ext_connectivity(
LOG.debug('changed_ext_connectivities: {}'.format(
changed_ext_connectivities))
return changed_ext_connectivities
def get_stack_param(context, vnf_dict, heal_vnf_request, inst_vnf_info):
stack_param = {}
vnfc_resources = []
# get vnfc resources
if not heal_vnf_request.vnfc_instance_id:
# include all vnfc resources
vnfc_resources = [
resource for resource in inst_vnf_info.vnfc_resource_info]
else:
for vnfc_resource in inst_vnf_info.vnfc_resource_info:
if vnfc_resource.id in heal_vnf_request.vnfc_instance_id:
vnfc_resources.append(vnfc_resource)
def _update_stack_params(
context, base_hot_dict, nested_hot_dict, vnfd_dict):
param_base_hot_dict = copy.deepcopy(nested_hot_dict)
param_base_hot_dict['heat_template'] = base_hot_dict
initial_param_dict = (
userdata_utils.create_initial_param_server_port_dict(
param_base_hot_dict)
)
del initial_param_dict['nfv']['CP']
vdu_flavor_dict = (
userdata_utils.create_vdu_flavor_capability_name_dict(vnfd_dict)
)
vdu_image_dict = userdata_utils.create_sw_image_dict(vnfd_dict)
final_param_dict = userdata_utils.create_final_param_dict(
initial_param_dict, vdu_flavor_dict, vdu_image_dict, {})
return final_param_dict['nfv']['VDU']
# get HOT dict
base_hot_dict, nested_hot_dict = get_base_nest_hot_dict(
context, inst_vnf_info.flavour_id, vnf_dict['vnfd_id'])
vnfd_dict = yaml.safe_load(
vnf_dict['vnfd']['attributes']['vnfd_' + inst_vnf_info.flavour_id])
if 'stack_param' in vnf_dict['attributes'].keys():
stack_param = yaml.safe_load(
vnf_dict['attributes']['stack_param'])
updated_vdu_params = _update_stack_params(
context, base_hot_dict, nested_hot_dict, vnfd_dict)
for vnfc_resource in vnfc_resources:
vdu_id = vnfc_resource.vdu_id
if (updated_vdu_params.get(vdu_id) and
stack_param['nfv']['VDU'].get(vdu_id)):
stack_param['nfv']['VDU'].update({
vdu_id: updated_vdu_params.get(vdu_id)
})
return stack_param

View File

@ -37,6 +37,7 @@ from tacker.common import utils
from tacker.db.common_services import common_services_db_plugin
from tacker.extensions import vnflcm
from tacker.extensions import vnfm
from tacker import manager
from tacker import objects
from tacker.objects import fields
from tacker.plugins.common import constants
@ -1264,6 +1265,10 @@ class OpenStack(abstract_driver.VnfAbstractDriver,
heatclient = hc.HeatClient(access_info, region_name=region_name)
vnf_lcm_op_occs = objects.VnfLcmOpOcc.get_by_vnf_instance_id(
context, vnf_instance.id)
stack_param = {}
vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM']
vnf_dict = vnfm_plugin.get_vnf(context, vnf_instance.id)
def _get_storage_resources(vnfc_resource):
# Prepare list of storage resources to be marked unhealthy
@ -1350,10 +1355,38 @@ class OpenStack(abstract_driver.VnfAbstractDriver,
_get_stack_status()
_resource_mark_unhealthy()
# get HOT dict
base_hot_dict, nested_hot_dict = \
vnflcm_utils.get_base_nest_hot_dict(
context,
inst_vnf_info.flavour_id,
vnf_dict['vnfd_id'])
stack_param = vnflcm_utils.get_stack_param(
context, vnf_dict, heal_vnf_request, inst_vnf_info)
# update stack with latest HOT
stack_update_param = {
'existing': True}
if base_hot_dict:
stack_update_param['template'] = \
self._format_base_hot(base_hot_dict)
if nested_hot_dict:
files_dict = {}
for name, value in nested_hot_dict.items():
files_dict[name] = self._format_base_hot(value)
stack_update_param['files'] = files_dict
if stack_param:
stack_update_param['parameters'] = stack_param
LOG.info("Updating stack %(stack)s for vnf instance %(id)s",
{"stack": inst_vnf_info.instance_id, "id": vnf_instance.id})
heatclient.update(stack_id=inst_vnf_info.instance_id, existing=True)
heatclient.update(
stack_id=inst_vnf_info.instance_id, **stack_update_param)
@log.log
def heal_vnf_wait(self, context, vnf_instance, vim_connection_info,