Add heal vnf instance API

Implemented heal vnf instance API.
* POST /vnflcm/v1/vnf_instances/{vnf_instance_id}/heal}

Co-authored-By: Ajay Parja <ajay.parja@nttdata.com>
Co-authored-By: Nitin Uikey <nitin.uikey@nttdata.com>
Co-authored-By: Shubham Potale <shubham.potale@nttdata.com>

Change-Id: I38bdac64a8a3eb3f8880085b5a77db97e5a1a8f2
Blueprint: support-etsi-nfv-specs
This commit is contained in:
tpatil 2019-12-05 07:47:47 +00:00
parent fbb38266d6
commit b4f357f2b3
19 changed files with 1641 additions and 13 deletions

View File

@ -210,3 +210,19 @@ terminate = {
'required': ['terminationType'], 'required': ['terminationType'],
'additionalProperties': False, 'additionalProperties': False,
} }
heal = {
'type': 'object',
'properties': {
'cause': {'type': 'string', 'maxLength': 255},
'vnfcInstanceId': {
'type': 'array',
"items": {
"type": "string",
'format': 'uuid'
}
}
},
'additionalProperties': False,
}

View File

@ -269,8 +269,40 @@ class VnfLcmController(wsgi.Controller):
vnf_instance = self._get_vnf_instance(context, id) vnf_instance = self._get_vnf_instance(context, id)
self._terminate(context, vnf_instance, body) self._terminate(context, vnf_instance, body)
@check_vnf_state(action="heal",
instantiation_state=[fields.VnfInstanceState.INSTANTIATED],
task_state=[None])
def _heal(self, context, vnf_instance, request_body):
req_body = utils.convert_camelcase_to_snakecase(request_body)
heal_vnf_request = objects.HealVnfRequest(context=context, **req_body)
inst_vnf_info = vnf_instance.instantiated_vnf_info
vnfc_resource_info_ids = [vnfc_resource_info.id for
vnfc_resource_info in inst_vnf_info.vnfc_resource_info]
for vnfc_id in heal_vnf_request.vnfc_instance_id:
# check if vnfc_id exists in vnfc_resource_info
if vnfc_id not in vnfc_resource_info_ids:
msg = _("Vnfc id %(vnfc_id)s not present in vnf instance "
"%(id)s")
raise webob.exc.HTTPBadRequest(
explanation=msg % {"vnfc_id": vnfc_id,
"id": vnf_instance.id})
vnf_instance.task_state = fields.VnfInstanceTaskState.HEALING
vnf_instance.save()
self.rpc_api.heal(context, vnf_instance, heal_vnf_request)
@wsgi.response(http_client.ACCEPTED)
@wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN,
http_client.NOT_FOUND, http_client.CONFLICT))
@validation.schema(vnf_lcm.heal)
def heal(self, request, id, body): def heal(self, request, id, body):
raise webob.exc.HTTPNotImplemented() context = request.environ['tacker.context']
context.can(vnf_lcm_policies.VNFLCM % 'heal')
vnf_instance = self._get_vnf_instance(context, id)
self._heal(context, vnf_instance, body)
def create_resource(): def create_resource():

View File

@ -70,6 +70,13 @@ class VnflcmAPIRouter(wsgi.Router):
"/vnf_instances/{id}/instantiate", "/vnf_instances/{id}/instantiate",
methods, controller, default_resource) methods, controller, default_resource)
# Allowed methods on
# /vnflcm/v1/vnf_instances/{vnfInstanceId}/heal resource
methods = {"POST": "heal"}
self._setup_route(mapper,
"/vnf_instances/{id}/heal",
methods, controller, default_resource)
# Allowed methods on # Allowed methods on
# /vnflcm/v1/vnf_instances/{vnfInstanceId}/terminate resource # /vnflcm/v1/vnf_instances/{vnfInstanceId}/terminate resource
methods = {"POST": "terminate"} methods = {"POST": "terminate"}

View File

@ -266,6 +266,10 @@ class VnfPreInstantiationFailed(TackerException):
"%(error)s") "%(error)s")
class VnfHealFailed(TackerException):
message = _("Heal Vnf failed for vnf %(id)s, error: %(error)s")
class OrphanedObjectError(TackerException): class OrphanedObjectError(TackerException):
msg_fmt = _('Cannot call %(method)s on orphaned %(objtype)s object') msg_fmt = _('Cannot call %(method)s on orphaned %(objtype)s object')

View File

@ -438,6 +438,9 @@ class Conductor(manager.Manager):
LOG.error("Failed to update usage_state of vnf package %s", LOG.error("Failed to update usage_state of vnf package %s",
vnf_package.id) vnf_package.id)
def heal(self, context, vnf_instance, heal_vnf_request):
self.vnflcm_driver.heal_vnf(context, vnf_instance, heal_vnf_request)
def init(args, **kwargs): def init(args, **kwargs):
CONF(args=args, project='tacker', CONF(args=args, project='tacker',

View File

@ -49,3 +49,14 @@ class VNFLcmRPCAPI(object):
return rpc_method(context, 'terminate', return rpc_method(context, 'terminate',
vnf_instance=vnf_instance, vnf_instance=vnf_instance,
terminate_vnf_req=terminate_vnf_req) terminate_vnf_req=terminate_vnf_req)
def heal(self, context, vnf_instance, heal_vnf_request, cast=True):
serializer = objects_base.TackerObjectSerializer()
client = rpc.get_client(self.target, version_cap=None,
serializer=serializer)
cctxt = client.prepare()
rpc_method = cctxt.cast if cast else cctxt.call
return rpc_method(context, 'heal',
vnf_instance=vnf_instance,
heal_vnf_request=heal_vnf_request)

View File

@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from oslo_utils import versionutils
from tacker.objects import base from tacker.objects import base
from tacker.objects import fields from tacker.objects import fields
@ -33,10 +35,20 @@ class HealVnfAdditionalParams(base.TackerObject):
class HealVnfRequest(base.TackerObject): class HealVnfRequest(base.TackerObject):
# Version 1.0: Initial version # Version 1.0: Initial version
VERSION = '1.0' # Version 1.1: Added vnf_instance_id
VERSION = '1.1'
fields = { fields = {
'cause': fields.StringField(), 'vnfc_instance_id': fields.ListOfStringsField(nullable=True,
default=[]),
'cause': fields.StringField(nullable=True, default=None),
'additional_params': fields.ListOfObjectsField( 'additional_params': fields.ListOfObjectsField(
'HealVnfAdditionalParams') 'HealVnfAdditionalParams', default=[])
} }
def obj_make_compatible(self, primitive, target_version):
super(HealVnfRequest, self).obj_make_compatible(primitive,
target_version)
target_version = versionutils.convert_version_to_tuple(target_version)
if target_version < (1, 1) and 'vnfc_instance_id' in primitive:
del primitive['vnfc_instance_id']

View File

@ -22,6 +22,176 @@ from tacker.objects import fields
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
def _get_vim_connection_info(vnf_instance):
vim_connection_infos = []
for vim_conn_info in vnf_instance.vim_connection_info:
new_vim_conn_info = vim_conn_info.obj_clone()
vim_connection_infos.append(new_vim_conn_info)
return vim_connection_infos
def _get_ext_managed_virtual_links(vnf_instantiated_info):
ext_managed_virtual_links = []
for ext_mng_vl_info in \
vnf_instantiated_info.ext_managed_virtual_link_info:
network_resource = ext_mng_vl_info.network_resource
ext_mng_vl_data = objects.ExtManagedVirtualLinkData(
id=ext_mng_vl_info.id,
vnf_virtual_link_desc_id=ext_mng_vl_info.vnf_virtual_link_desc_id,
resource_id=network_resource.resource_id)
ext_managed_virtual_links.append(ext_mng_vl_data)
return ext_managed_virtual_links
def _get_cp_instance_id(ext_virtual_link_info_id, vnf_instantiated_info):
cp_instances = []
for vnf_vl_res_info in \
vnf_instantiated_info.vnf_virtual_link_resource_info:
if ext_virtual_link_info_id == \
vnf_vl_res_info.vnf_virtual_link_desc_id:
for vnf_link_port in vnf_vl_res_info.vnf_link_ports:
cp_instances.append(vnf_link_port.cp_instance_id)
return cp_instances
def _get_cp_data_from_vnfc_resource_info(cp_instance_list,
vnf_instantiated_info):
vnfc_cp_infos = []
for vnfc_resource_info in vnf_instantiated_info.vnfc_resource_info:
for vnfc_cp_info in vnfc_resource_info.vnfc_cp_info:
if vnfc_cp_info.id in cp_instance_list:
vnfc_cp_infos.append(vnfc_cp_info)
return vnfc_cp_infos
def _get_link_ports(vnfc_cp_info_list, vnf_instantiated_info):
ext_link_ports = []
for vnfc_cp_info in vnfc_cp_info_list:
for ext_vl_info in vnf_instantiated_info.ext_virtual_link_info:
for ext_vl_port in ext_vl_info.ext_link_ports:
if ext_vl_port.id != vnfc_cp_info.vnf_ext_cp_id:
continue
resource_handle = ext_vl_port.resource_handle.obj_clone()
ext_link_port_data = objects.ExtLinkPortData(
id=ext_vl_port.id,
resource_handle=resource_handle)
ext_link_ports.append(ext_link_port_data)
break
return ext_link_ports
def _get_cp_protocol_data_list(ext_cp_info):
cp_protocol_data_list = []
def _get_ip_addresses(ip_addresses):
ip_addresses = []
for ip_address in ip_addresses:
# TODO(nitin-uikey): How to determine num_dynamic_addresses
# back from InstantiatedVnfInfo->IpAddress.
ip_address_data = IpAddress(
type=ip_address.type,
subnet_id=ip_address.subnet_id,
fixed_addresses=ip_address.addresses)
ip_addresses.append(ip_address_data)
return ip_addresses
for cp_protocol_info in ext_cp_info.cp_protocol_info:
if cp_protocol_info.ip_over_ethernet:
ip_addresses = _get_ip_addresses(cp_protocol_info.
ip_over_ethernet.ip_addresses)
ip_over_ethernet_data = objects.IpOverEthernetAddressData(
mac_address=cp_protocol_info.ip_over_ethernet.mac_address,
ip_addresses=ip_addresses)
else:
ip_over_ethernet_data = None
cp_protocol_data = objects.CpProtocolData(
layer_protocol=cp_protocol_info.layer_protocol,
ip_over_ethernet=ip_over_ethernet_data)
cp_protocol_data_list.append(cp_protocol_data)
return cp_protocol_data_list
def _get_cp_ids(vnfc_cp_info_list, vnf_instantiated_info):
ext_cps = []
for vnfc_cp_info in vnfc_cp_info_list:
for ext_cp_info in vnf_instantiated_info.ext_cp_info:
if ext_cp_info.cpd_id != vnfc_cp_info.cpd_id:
continue
ext_cp_configs = []
vnf_ext_cp_data = objects.VnfExtCpData()
vnf_ext_cp_data.cpd_id = ext_cp_info.cpd_id
cp_protocol_data_list = _get_cp_protocol_data_list(ext_cp_info)
# TODO(nitin-uikey) set cp_instance_id
# and prepare 1-N objects of VnfExtCpConfig
vnf_ext_cp_config = objects.VnfExtCpConfig(
link_port_id=vnfc_cp_info.vnf_ext_cp_id,
cp_protocol_data=cp_protocol_data_list)
ext_cp_configs.append(vnf_ext_cp_config)
vnf_ext_cp_data = objects.VnfExtCpData(
cpd_id=ext_cp_info.cpd_id,
cp_config=ext_cp_configs)
ext_cps.append(vnf_ext_cp_data)
break
return ext_cps
def _get_ext_virtual_link_data(vnf_instantiated_info):
ext_virtual_links = []
for ext_vl_info in \
vnf_instantiated_info.ext_virtual_link_info:
resource_handle = ext_vl_info.resource_handle
ext_vl_data = objects.ExtVirtualLinkData(
id=ext_vl_info.id,
resource_id=resource_handle.resource_id)
# call vnf virtual link resource info
cp_instances = _get_cp_instance_id(ext_vl_info.id,
vnf_instantiated_info)
# get cp info from vnfcresources
vnfc_cp_infos = _get_cp_data_from_vnfc_resource_info(cp_instances,
vnf_instantiated_info)
ext_vl_data.ext_link_ports = _get_link_ports(vnfc_cp_infos,
vnf_instantiated_info)
# assign the data to extcp info and link port
ext_vl_data.ext_cps = _get_cp_ids(vnfc_cp_infos,
vnf_instantiated_info)
ext_virtual_links.append(ext_vl_data)
return ext_virtual_links
@base.TackerObjectRegistry.register @base.TackerObjectRegistry.register
class InstantiateVnfRequest(base.TackerObject): class InstantiateVnfRequest(base.TackerObject):
# Version 1.0: Initial version # Version 1.0: Initial version
@ -87,6 +257,38 @@ class InstantiateVnfRequest(base.TackerObject):
ext_virtual_links=ext_virtual_links, ext_virtual_links=ext_virtual_links,
additional_params=additional_params) additional_params=additional_params)
@classmethod
def from_vnf_instance(cls, vnf_instance):
vnf_instantiated_info = vnf_instance.instantiated_vnf_info
# Vim connection info
vim_connection_info = _get_vim_connection_info(vnf_instance)
# Flavour id
flavour_id = vnf_instantiated_info.flavour_id
# Instantiation level
instantiation_level_id = vnf_instantiated_info.instantiation_level_id
# Externally managed virtual links
ext_managed_virtual_links = _get_ext_managed_virtual_links(
vnf_instantiated_info)
# External virtual links
ext_virtual_links = _get_ext_virtual_link_data(vnf_instantiated_info)
additional_params = vnf_instantiated_info.additional_params
instantiate_vnf_request = cls(flavour_id=flavour_id,
instantiation_level_id=instantiation_level_id,
ext_managed_virtual_links=ext_managed_virtual_links,
vim_connection_info=vim_connection_info,
ext_virtual_links=ext_virtual_links,
additional_params=additional_params)
return instantiate_vnf_request
@base.TackerObjectRegistry.register @base.TackerObjectRegistry.register
class ExtManagedVirtualLinkData(base.TackerObject): class ExtManagedVirtualLinkData(base.TackerObject):

View File

@ -65,6 +65,17 @@ rules = [
'path': '/vnflcm/v1/vnf_instances/{vnfInstanceId}/terminate' 'path': '/vnflcm/v1/vnf_instances/{vnfInstanceId}/terminate'
} }
] ]
),
policy.DocumentedRuleDefault(
name=VNFLCM % 'heal',
check_str=base.RULE_ADMIN_OR_OWNER,
description="Heal a VNF instance.",
operations=[
{
'method': 'POST',
'path': '/vnflcm/v1/vnf_instances/{vnfInstanceId}/heal'
}
]
) )
] ]

View File

@ -342,6 +342,21 @@ class TestConductor(SqlTestCase):
mock_log.error.assert_called_once_with(expected_msg, mock_log.error.assert_called_once_with(expected_msg,
vnf_package_vnfd.package_uuid) vnf_package_vnfd.package_uuid)
def test_heal_vnf_instance(self):
vnf_package_vnfd = self._create_and_upload_vnf_package()
vnf_instance_data = fake_obj.get_vnf_instance_data(
vnf_package_vnfd.vnfd_id)
vnf_instance = objects.VnfInstance(context=self.context,
**vnf_instance_data)
vnf_instance.create()
vnf_instance.instantiation_state = \
fields.VnfInstanceState.INSTANTIATED
vnf_instance.save()
heal_vnf_req = objects.HealVnfRequest(cause="healing request")
self.conductor.heal(self.context, vnf_instance, heal_vnf_req)
self.vnflcm_driver.heal_vnf.assert_called_once_with(
self.context, mock.ANY, heal_vnf_req)
@mock.patch.object(os, 'remove') @mock.patch.object(os, 'remove')
@mock.patch.object(shutil, 'rmtree') @mock.patch.object(shutil, 'rmtree')
@mock.patch.object(os.path, 'exists') @mock.patch.object(os.path, 'exists')

View File

@ -0,0 +1,282 @@
# Copyright (c) 2020 NTT DATA
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from tacker import context
from tacker import objects
from tacker.objects import fields
from tacker.tests.unit import base
from tacker.tests import uuidsentinel
def get_instantiated_info_dict_for_ext_links_and_flavour_id():
return {"instantiated_vnf_info": {
"additional_params": {'key1': 'value1'},
"vnfc_resource_info": [{
"vdu_id": "VDU1",
"vnfc_cp_info": [{
"cp_protocol_info": [{
"ip_over_ethernet": {
"mac_address": "fa:16:3e:0d:6f:71"},
"layer_protocol": "IP_OVER_ETHERNET"}],
"vnf_ext_cp_id": None,
"cpd_id": "CP1",
"vnf_link_port_id": "a41417d8-5cba-4256-aa4a-c68b79bb2dc2",
"id": "350ab5d7-7cf0-4d24-aa2f-ec7e51149d56"}, {
"cp_protocol_info": [{
"layer_protocol": "IP_OVER_ETHERNET"}],
"vnf_ext_cp_id": "413f4e46-21cf-41b1-be0f-de8d23f76cfe",
"cpd_id": "CP2",
"vnf_link_port_id": "b5f67448-87ee-4250-ad8b-514bc693f6c4",
"id": "2cab54a3-8ac5-4338-b586-b12e4ff1a89f"}, {
"vnf_link_port_id": "124f1923-9de1-4729-8d4e-bc0613066dc4",
"id": "b3341e7b-20bb-4ae5-8be9-b00cf79a9395",
"vnf_ext_cp_id": None,
"cpd_id": "CP3"}],
"compute_resource": {
"vim_level_resource_type": "OS::Nova::Server",
"resource_id": "c83ac219-376a-4016-b8b2-50c3707f71c4"},
"id": "0688be82-05a6-4b00-b869-d4719baf4f42",
"storage_resource_ids": [],
"metadata": {}}],
"ext_virtual_link_info": [{
"resource_handle": {
"vim_level_resource_type": None,
"resource_id": "7ec01a11-e584-404a-88bd-39a56b63e29c"},
"id": "net0"}, {
"resource_handle": {
"vim_level_resource_type": None,
"resource_id": "dc67ee99-e963-44e2-a152-f0fb492eae76"},
"id": "external_network",
"ext_link_ports": [{
"cp_instance_id": "f47a9e33-b31a-4290-828a-c7569c52bd0e",
"resource_handle": {
"vim_level_resource_type": "LINKPORT",
"resource_id": "67f7e772-0d31-4087-bf4c-2576fadcbdb7"},
"id": "413f4e46-21cf-41b1-be0f-de8d23f76cfe"}]}],
"vnf_virtual_link_resource_info": [{
"vnf_link_ports": [{
"cp_instance_id": "b3341e7b-20bb-4ae5-8be9-b00cf79a9395",
"resource_handle": {
"vim_level_resource_type": "OS::Neutron::Port",
"resource_id": "87b567a0-783a-4c06-b0d8-cdafd40c51e3"},
"id": "124f1923-9de1-4729-8d4e-bc0613066dc4"}],
"network_resource": {
"vim_level_resource_type": "OS::Neutron::Net",
"resource_id": "413f4e46-21cf-41b1-be0f-de8d23f76cfd"},
"vnf_virtual_link_desc_id": "VL3",
"id": "84ffa547-1164-4e60-863d-c7dac770f824"}, {
"vnf_link_ports": [{
"cp_instance_id": "2cab54a3-8ac5-4338-b586-b12e4ff1a89f",
"resource_handle": {
"vim_level_resource_type": "OS::Neutron::Port",
"resource_id": "413f4e46-21cf-41b1-be0f-de8d23f76cfe"},
"id": "b5f67448-87ee-4250-ad8b-514bc693f6c4"}],
"network_resource": {
"vim_level_resource_type": "OS::Neutron::Net",
"resource_id": "dc67ee99-e963-44e2-a152-f0fb492eae76"},
"vnf_virtual_link_desc_id": "external_network",
"id": "1050cce6-27ca-40df-b66e-160113fc4826"}, {
"vnf_link_ports": [{
"cp_instance_id": "350ab5d7-7cf0-4d24-aa2f-ec7e51149d56",
"resource_handle": {
"vim_level_resource_type": "OS::Neutron::Port",
"resource_id": "2bca5538-edd6-4590-ab1a-552ae7d0f81b"},
"id": "a41417d8-5cba-4256-aa4a-c68b79bb2dc2"}],
"network_resource": {
"vim_level_resource_type": "OS::Neutron::Net",
"resource_id": "7ec01a11-e584-404a-88bd-39a56b63e29c"},
"vnf_virtual_link_desc_id": "net0",
"id": "5ad98d4d-4b81-4b1b-a88b-1f64b66e151b"}],
"ext_managed_virtual_link_info": [{
"network_resource": {
"vim_level_resource_type": "OS::Neutron::Net",
"resource_id": "413f4e46-21cf-41b1-be0f-de8d23f76cfd"},
"id": "net1",
"vnf_link_ports": [{
"cp_instance_id": "b3341e7b-20bb-4ae5-8be9-b00cf79a9395",
"resource_handle": {
"vim_level_resource_type": "OS::Neutron::Port",
"resource_id": "87b567a0-783a-4c06-b0d8-cdafd40c51e3"},
"id": "124f1923-9de1-4729-8d4e-bc0613066dc4"}],
"vnf_virtual_link_desc_id": "VL3"}],
"flavour_id": "simple",
"vnf_state": "STARTED",
"ext_cp_info": [{
"cp_protocol_info": [{
"ip_over_ethernet": {
"mac_address": "fa:16:3e:0d:6f:71"},
"layer_protocol": "IP_OVER_ETHERNET"}],
"cpd_id": "CP1",
"id": "19f0aa71-9376-43dc-8e13-5e76e4bdc8bf",
"ext_link_port_id": None}, {
"cp_protocol_info": [{
"layer_protocol": "IP_OVER_ETHERNET"}],
"cpd_id": "CP2",
"id": "f47a9e33-b31a-4290-828a-c7569c52bd0e",
"ext_link_port_id": None}]
}
}
class InstantiateVnfRequestTestCase(base.TestCase):
def setUp(self):
super(InstantiateVnfRequestTestCase, self).setUp()
self.context = context.get_admin_context()
def _create_vim_connection_info(self, **kwargs):
vim_connection_info = objects.VimConnectionInfo(**kwargs)
return [vim_connection_info]
def _create_vnf_instance(self, vim_connection_info=None):
vnf_instance = objects.VnfInstance(
context=self.context,
vnf_instance_name="Sample vnf instance name",
vnf_instance_description="Sample vnf instance description",
vnfd_id=uuidsentinel.vnfd_id,
instantiation_state=fields.VnfInstanceState.INSTANTIATED,
vnf_provider='Company',
vnf_product_name='Sample VNF',
vnf_software_version='1.0',
vnfd_version='1.0',
tenant_id=uuidsentinel.tenant_id)
if vim_connection_info:
vnf_instance.vim_connection_info = vim_connection_info
return vnf_instance
def test_from_vnf_instance_with_flavour(self):
"""Map flavour id"""
inst_vnf_request = objects.InstantiateVnfRequest(flavour_id="simple")
instantiated_vnf_info = objects.InstantiatedVnfInfo(
flavour_id="simple")
vnf_instance = self._create_vnf_instance()
vnf_instance.instantiated_vnf_info = instantiated_vnf_info
inst_vnf_request_actual = objects.InstantiateVnfRequest.\
from_vnf_instance(vnf_instance)
self.compare_obj(inst_vnf_request, inst_vnf_request_actual)
def test_from_vnf_instance_with_flavour_and_instantiation_level(self):
"""Map flavour id and instantiation level"""
inst_vnf_request = objects.InstantiateVnfRequest(flavour_id="simple",
instantiation_level_id="instantiation_level_1")
instantiated_vnf_info = objects.InstantiatedVnfInfo(
flavour_id="simple",
instantiation_level_id="instantiation_level_1")
vnf_instance = self._create_vnf_instance()
vnf_instance.instantiated_vnf_info = instantiated_vnf_info
inst_vnf_request_actual = objects.InstantiateVnfRequest.\
from_vnf_instance(vnf_instance)
self.compare_obj(inst_vnf_request, inst_vnf_request_actual)
def test_from_vnf_instance_with_ext_vl_and_ext_managed_vl(self):
"""Map external and internal network information.
Map following information:
a) ext_virtual_link_info
b) ext_managed_virtual_link_info
"""
ext_vl_info = [{
"ext_cps": [{
"cp_config": [{
"cp_protocol_data": [{
"ip_over_ethernet": {
"mac_address": "fa:16:3e:0d:6f:71"},
"layer_protocol": "IP_OVER_ETHERNET"}]}],
"cpd_id": "CP1"}],
"id": "net0",
"resource_id": "7ec01a11-e584-404a-88bd-39a56b63e29c"}, {
"ext_cps": [{
"cp_config": [{
"cp_protocol_data": [{
"layer_protocol": "IP_OVER_ETHERNET"}],
"link_port_id": "413f4e46-21cf-41b1-be0f-de8d23f76cfe"}],
"cpd_id": "CP2"}],
"ext_link_ports": [{
"id": "413f4e46-21cf-41b1-be0f-de8d23f76cfe",
"resource_handle": {
"resource_id": "67f7e772-0d31-4087-bf4c-2576fadcbdb7",
"vim_level_resource_type": "LINKPORT"}}],
"id": "external_network",
"resource_id": "dc67ee99-e963-44e2-a152-f0fb492eae76"}]
ext_mg_vl = {"id": "net1",
"resource_id": "413f4e46-21cf-41b1-be0f-de8d23f76cfd",
"vnf_virtual_link_desc_id": "VL3"}
vim_connection_info = [{
"id": "6b0ff598-60d6-49b4-a907-a1111de52d92",
"vim_id": uuidsentinel.vim_id,
"vim_type": "ETSINFV.OPENSTACK_KEYSTONE.v_2",
"access_info": {}}]
vnf_info_data = {'ext_managed_virtual_links': [ext_mg_vl],
'ext_virtual_links': ext_vl_info,
'vim_connection_info': vim_connection_info,
'flavour_id': 'simple',
'additional_params': {'key1': 'value1'}}
inst_vnf_request = objects.InstantiateVnfRequest.obj_from_primitive(
vnf_info_data, self.context)
response = get_instantiated_info_dict_for_ext_links_and_flavour_id()
instantiated_vnf_info_dict = response.get('instantiated_vnf_info')
instantiated_vnf_info = objects.InstantiatedVnfInfo.obj_from_primitive(
instantiated_vnf_info_dict, self.context)
vnf_instance = self._create_vnf_instance(
self._create_vim_connection_info(**vim_connection_info[0]))
vnf_instance.instantiated_vnf_info = instantiated_vnf_info
inst_vnf_req_actual = objects.InstantiateVnfRequest.from_vnf_instance(
vnf_instance)
self.assertEqual(inst_vnf_request.flavour_id,
inst_vnf_req_actual.flavour_id)
self.assertEqual(inst_vnf_request.instantiation_level_id,
inst_vnf_req_actual.instantiation_level_id)
self.assertEqual(inst_vnf_request.additional_params,
inst_vnf_req_actual.additional_params)
for expected, actual in zip(inst_vnf_request.ext_managed_virtual_links,
inst_vnf_req_actual.ext_managed_virtual_links):
self.compare_obj(expected, actual)
for expected, actual in zip(inst_vnf_request.ext_virtual_links,
inst_vnf_req_actual.ext_virtual_links):
self.assertEqual(expected.id, actual.id)
self.assertEqual(expected.resource_id, actual.resource_id)
for expected, actual in zip(inst_vnf_request.vim_connection_info,
inst_vnf_req_actual.vim_connection_info):
self.assertEqual(expected.id, actual.id)
self.assertEqual(expected.vim_id, actual.vim_id)
self.assertEqual(expected.vim_type, actual.vim_type)

View File

@ -835,3 +835,140 @@ class TestController(base.TestCase):
"terminate while the vnf instance is in this state.") "terminate while the vnf instance is in this state.")
self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id, self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
resp.json['conflictingRequest']['message']) resp.json['conflictingRequest']['message'])
@mock.patch.object(objects.VnfInstance, "get_by_id")
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch.object(VNFLcmRPCAPI, "heal")
@ddt.data({'cause': 'healing'}, {})
def test_heal(self, body, mock_rpc_heal, mock_save,
mock_vnf_by_id):
vnf_instance_obj = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED)
mock_vnf_by_id.return_value = vnf_instance_obj
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s/heal' % uuidsentinel.vnf_instance_id)
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'POST'
resp = req.get_response(self.app)
self.assertEqual(http_client.ACCEPTED, resp.status_code)
mock_rpc_heal.assert_called_once()
def test_heal_cause_max_length_exceeded(self):
body = {'cause': 'A' * 256}
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s/heal' % uuidsentinel.vnf_instance_id)
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'POST'
resp = req.get_response(self.app)
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
@mock.patch.object(objects.VnfInstance, "get_by_id")
def test_heal_incorrect_instantiated_state(self, mock_vnf_by_id):
vnf_instance_obj = fakes.return_vnf_instance(
fields.VnfInstanceState.NOT_INSTANTIATED)
mock_vnf_by_id.return_value = vnf_instance_obj
body = {}
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s/heal' % uuidsentinel.vnf_instance_id)
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'POST'
resp = req.get_response(self.app)
self.assertEqual(http_client.CONFLICT, resp.status_code)
expected_msg = ("Vnf instance %s in instantiation_state "
"NOT_INSTANTIATED. Cannot heal while the vnf instance "
"is in this state.")
self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
resp.json['conflictingRequest']['message'])
@mock.patch.object(objects.VnfInstance, "get_by_id")
def test_heal_incorrect_task_state(self, mock_vnf_by_id):
vnf_instance_obj = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED,
task_state=fields.VnfInstanceTaskState.HEALING)
mock_vnf_by_id.return_value = vnf_instance_obj
body = {}
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s/heal' % uuidsentinel.vnf_instance_id)
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'POST'
resp = req.get_response(self.app)
self.assertEqual(http_client.CONFLICT, resp.status_code)
expected_msg = ("Vnf instance %s in task_state "
"HEALING. Cannot heal while the vnf instance "
"is in this state.")
self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
resp.json['conflictingRequest']['message'])
@mock.patch.object(objects.VnfInstance, "get_by_id")
def test_heal_with_invalid_vnfc_id(self, mock_vnf_by_id):
vnf_instance_obj = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED)
mock_vnf_by_id.return_value = vnf_instance_obj
body = {'vnfcInstanceId': [uuidsentinel.vnfc_instance_id]}
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s/heal' % uuidsentinel.vnf_instance_id)
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'POST'
resp = req.get_response(self.app)
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
expected_msg = "Vnfc id %s not present in vnf instance %s"
self.assertEqual(expected_msg % (uuidsentinel.vnfc_instance_id,
uuidsentinel.vnf_instance_id), resp.json['badRequest']['message'])
@ddt.data('HEAD', 'PUT', 'DELETE', 'PATCH', 'GET')
def test_heal_invalid_http_method(self, method):
body = {}
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s/heal' % uuidsentinel.vnf_instance_id)
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = method
resp = req.get_response(self.app)
self.assertEqual(http_client.METHOD_NOT_ALLOWED, resp.status_code)
@ddt.data({'attribute': 'cause', 'value': 123,
'expected_type': 'string'},
{'attribute': 'cause', 'value': True,
'expected_type': 'string'},
{'attribute': 'vnfcInstanceId', 'value': 123,
'expected_type': 'array'},
{'attribute': 'vnfcInstanceId', 'value': True,
'expected_type': 'array'},
)
@ddt.unpack
def test_heal_with_invalid_request_body(
self, attribute, value, expected_type):
body = {}
req = fake_request.HTTPRequest.blank(
'/vnf_instances/29c770a3-02bc-4dfc-b4be-eb173ac00567/heal')
body.update({attribute: value})
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'POST'
exception = self.assertRaises(
exceptions.ValidationError, self.controller.heal,
req, body=body)
expected_message = \
("Invalid input for field/attribute {attribute}. Value: {value}. "
"{value} is not of type '{expected_type}'".
format(value=value, attribute=attribute,
expected_type=expected_type))
self.assertEqual(expected_message, exception.msg)

View File

@ -59,7 +59,7 @@ class FakeDriverManager(mock.Mock):
if self.fail_method_name and \ if self.fail_method_name and \
self.fail_method_name == 'create_wait': self.fail_method_name == 'create_wait':
raise InfraDriverException("create_wait failed") raise InfraDriverException("create_wait failed")
if 'post_vnf_instantiation' in args: elif 'post_vnf_instantiation' in args:
pass pass
if 'delete' in args: if 'delete' in args:
if self.fail_method_name and \ if self.fail_method_name and \
@ -73,6 +73,18 @@ class FakeDriverManager(mock.Mock):
if self.fail_method_name and \ if self.fail_method_name and \
self.fail_method_name == 'delete_vnf_resource': self.fail_method_name == 'delete_vnf_resource':
raise InfraDriverException("delete_vnf_resource failed") raise InfraDriverException("delete_vnf_resource failed")
elif 'heal_vnf' in args:
if self.fail_method_name and \
self.fail_method_name == 'heal_vnf':
raise InfraDriverException("heal_vnf failed")
elif 'heal_vnf_wait' in args:
if self.fail_method_name and \
self.fail_method_name == 'heal_vnf_wait':
raise InfraDriverException("heal_vnf_wait failed")
elif 'post_heal_vnf' in args:
if self.fail_method_name and \
self.fail_method_name == 'post_heal_vnf':
raise InfraDriverException("post_heal_vnf failed")
class FakeVimClient(mock.Mock): class FakeVimClient(mock.Mock):
@ -439,3 +451,259 @@ class TestVnflcmDriver(db_base.SqlTestCase):
self.assertEqual("delete_vnf_resource failed", str(error)) self.assertEqual("delete_vnf_resource failed", str(error))
self.assertEqual(2, mock_vnf_instance_save.call_count) self.assertEqual(2, mock_vnf_instance_save.call_count)
self.assertEqual(3, self._vnf_manager.invoke.call_count) self.assertEqual(3, self._vnf_manager.invoke.call_count)
@mock.patch.object(objects.VnfPackageVnfd, 'get_by_id')
@mock.patch.object(vim_client.VimClient, "get_vim")
@mock.patch.object(objects.VnfResource, "create")
@mock.patch.object(objects.VnfResource, "destroy")
@mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch('tacker.vnflcm.vnflcm_driver.LOG')
def test_heal_vnf_without_vnfc_instance(self, mock_log, mock_save,
mock_vnf_resource_list, mock_resource_destroy,
mock_resource_create, mock_vim, mock_vnf_package_vnfd):
vnf_package_vnfd = fakes.return_vnf_package_vnfd()
vnf_package_id = vnf_package_vnfd.package_uuid
mock_vnf_package_vnfd.return_value = vnf_package_vnfd
fake_csar = os.path.join(self.temp_dir, vnf_package_id)
cfg.CONF.set_override('vnf_package_csar_path', self.temp_dir,
group='vnf_package')
base_path = os.path.dirname(os.path.abspath(__file__))
sample_vnf_package_zip = os.path.join(
base_path, "../../etc/samples/sample_vnf_package_csar.zip")
extracted_zip_path = fake_csar
zipfile.ZipFile(sample_vnf_package_zip, 'r').extractall(
extracted_zip_path)
mock_vnf_resource_list.return_value = [fakes.return_vnf_resource()]
# Heal as per SOL003 i.e. without vnfcInstanceId
heal_vnf_req = objects.HealVnfRequest()
vim_obj = {'vim_id': uuidsentinel.vim_id,
'vim_name': 'fake_vim',
'vim_type': 'openstack',
'vim_auth': {
'auth_url': 'http://localhost/identity',
'password': 'test_pw',
'username': 'test_user',
'project_name': 'test_project'}}
mock_vim.return_value = vim_obj
vnf_instance = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED)
vnf_instance.instantiated_vnf_info.instance_id =\
uuidsentinel.instance_id
self._mock_vnf_manager()
driver = vnflcm_driver.VnfLcmDriver()
driver.heal_vnf(self.context, vnf_instance, heal_vnf_req)
self.assertEqual(1, mock_save.call_count)
# vnf resource software images will be deleted during
# deleting vnf instance.
self.assertEqual(1, mock_resource_destroy.call_count)
# Vnf resource software images will be created during
# instantiation.
self.assertEqual(1, mock_resource_create.call_count)
# Invoke will be called 7 times, 3 for deleting the vnf
# resources and 4 during instantiation.
self.assertEqual(7, self._vnf_manager.invoke.call_count)
expected_msg = ("Request received for healing vnf '%s' "
"is completed successfully")
mock_log.info.assert_called_with(expected_msg,
vnf_instance.id)
shutil.rmtree(fake_csar)
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch('tacker.vnflcm.vnflcm_driver.LOG')
def test_heal_vnf_without_vnfc_instance_infra_delete_fail(self, mock_log,
mock_save):
# Heal as per SOL003 i.e. without vnfcInstanceId
heal_vnf_req = objects.HealVnfRequest()
vnf_instance = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED)
vnf_instance.instantiated_vnf_info.instance_id =\
uuidsentinel.instance_id
self._mock_vnf_manager(fail_method_name='delete')
driver = vnflcm_driver.VnfLcmDriver()
self.assertRaises(exceptions.VnfHealFailed,
driver.heal_vnf, self.context, vnf_instance, heal_vnf_req)
self.assertEqual(1, mock_save.call_count)
self.assertEqual(1, self._vnf_manager.invoke.call_count)
self.assertEqual(fields.VnfInstanceTaskState.ERROR,
vnf_instance.task_state)
expected_msg = ('Failed to delete vnf resources for vnf instance %s '
'before respawning. The vnf is in inconsistent '
'state. Error: delete failed')
mock_log.error.assert_called_with(expected_msg % vnf_instance.id)
@mock.patch.object(objects.VnfPackageVnfd, 'get_by_id')
@mock.patch.object(vim_client.VimClient, "get_vim")
@mock.patch.object(objects.VnfResource, "create")
@mock.patch.object(objects.VnfResource, "destroy")
@mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch('tacker.vnflcm.vnflcm_driver.LOG')
def test_heal_vnf_without_vnfc_instance_infra_instantiate_vnf_fail(self,
mock_log, mock_save, mock_vnf_resource_list,
mock_resource_destroy, mock_resource_create, mock_vim,
mock_vnf_package_vnfd):
vnf_package_vnfd = fakes.return_vnf_package_vnfd()
vnf_package_id = vnf_package_vnfd.package_uuid
mock_vnf_package_vnfd.return_value = vnf_package_vnfd
fake_csar = os.path.join(self.temp_dir, vnf_package_id)
cfg.CONF.set_override('vnf_package_csar_path', self.temp_dir,
group='vnf_package')
base_path = os.path.dirname(os.path.abspath(__file__))
sample_vnf_package_zip = os.path.join(
base_path, "../../etc/samples/sample_vnf_package_csar.zip")
extracted_zip_path = fake_csar
zipfile.ZipFile(sample_vnf_package_zip, 'r').extractall(
extracted_zip_path)
mock_vnf_resource_list.return_value = [fakes.return_vnf_resource()]
# Heal as per SOL003 i.e. without vnfcInstanceId
heal_vnf_req = objects.HealVnfRequest()
vnf_instance = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED)
vnf_instance.instantiated_vnf_info.instance_id =\
uuidsentinel.instance_id
self._mock_vnf_manager(fail_method_name='instantiate_vnf')
driver = vnflcm_driver.VnfLcmDriver()
self.assertRaises(exceptions.VnfHealFailed,
driver.heal_vnf, self.context, vnf_instance, heal_vnf_req)
self.assertEqual(1, mock_save.call_count)
# vnf resource software images will be deleted during
# deleting vnf instance.
self.assertEqual(1, mock_resource_destroy.call_count)
# Vnf resource software images will be created during
# instantiation.
self.assertEqual(1, mock_resource_create.call_count)
self.assertEqual(5, self._vnf_manager.invoke.call_count)
self.assertEqual(fields.VnfInstanceTaskState.ERROR,
vnf_instance.task_state)
expected_msg = ('Failed to instantiate vnf instance %s '
'after termination. The vnf is in inconsistent '
'state. Error: Vnf instantiation failed for vnf %s, '
'error: instantiate_vnf failed')
mock_log.error.assert_called_with(expected_msg % (vnf_instance.id,
vnf_instance.id))
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch('tacker.vnflcm.vnflcm_driver.LOG')
def test_heal_vnf_with_vnfc_instance(self, mock_log, mock_save):
heal_vnf_req = objects.HealVnfRequest(vnfc_instance_id=[
uuidsentinel.vnfc_instance_id_1])
vnf_instance = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED,
task_state=fields.VnfInstanceTaskState.HEALING)
self._mock_vnf_manager()
driver = vnflcm_driver.VnfLcmDriver()
driver.heal_vnf(self.context, vnf_instance, heal_vnf_req)
self.assertEqual(1, mock_save.call_count)
self.assertEqual(3, self._vnf_manager.invoke.call_count)
self.assertEqual(None, vnf_instance.task_state)
expected_msg = ("Request received for healing vnf '%s' "
"is completed successfully")
mock_log.info.assert_called_with(expected_msg,
vnf_instance.id)
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch('tacker.vnflcm.vnflcm_driver.LOG')
def test_heal_vnf_with_infra_heal_vnf_fail(self, mock_log, mock_save):
heal_vnf_req = objects.HealVnfRequest(vnfc_instance_id=[
uuidsentinel.vnfc_instance_id_1])
vnf_instance = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED,
task_state=fields.VnfInstanceTaskState.HEALING)
self._mock_vnf_manager(fail_method_name='heal_vnf')
driver = vnflcm_driver.VnfLcmDriver()
self.assertRaises(exceptions.VnfHealFailed,
driver.heal_vnf, self.context, vnf_instance,
heal_vnf_req)
self.assertEqual(1, mock_save.call_count)
self.assertEqual(1, self._vnf_manager.invoke.call_count)
self.assertEqual(fields.VnfInstanceTaskState.ERROR,
vnf_instance.task_state)
expected_msg = ("Failed to heal vnf %(id)s in infra driver. "
"Error: %(error)s")
mock_log.error.assert_called_with(expected_msg,
{'id': vnf_instance.id, 'error': 'heal_vnf failed'})
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch('tacker.vnflcm.vnflcm_driver.LOG')
def test_heal_vnf_with_infra_heal_vnf_wait_fail(self, mock_log,
mock_save):
heal_vnf_req = objects.HealVnfRequest(vnfc_instance_id=[
uuidsentinel.vnfc_instance_id_1])
vnf_instance = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED,
task_state=fields.VnfInstanceTaskState.HEALING)
vnf_instance.instantiated_vnf_info.instance_id =\
uuidsentinel.instance_id
self._mock_vnf_manager(fail_method_name='heal_vnf_wait')
driver = vnflcm_driver.VnfLcmDriver()
# It won't raise any exception if infra driver raises
# heal_vnf_wait because there is a possibility the vnfc
# resources could go into inconsistent state so it would
# proceed further and call post_heal_vnf with a hope
# it will work and vnflcm can update the vnfc resources
# properly and hence the _vnf_manager.invoke.call_count
# should be 3 instead of 2.
driver.heal_vnf(self.context, vnf_instance, heal_vnf_req)
self.assertEqual(1, mock_save.call_count)
self.assertEqual(3, self._vnf_manager.invoke.call_count)
self.assertEqual(None, vnf_instance.task_state)
expected_msg = ('Failed to update vnf %(id)s resources for '
'instance%(instance)s. Error: %(error)s')
mock_log.error.assert_called_with(expected_msg,
{'id': vnf_instance.id,
'instance': vnf_instance.instantiated_vnf_info.instance_id,
'error': 'heal_vnf_wait failed'})
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch('tacker.vnflcm.vnflcm_driver.LOG')
def test_heal_vnf_with_infra_post_heal_vnf_fail(self, mock_log,
mock_save):
heal_vnf_req = objects.HealVnfRequest(vnfc_instance_id=[
uuidsentinel.vnfc_instance_id_1])
vnf_instance = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED,
task_state=fields.VnfInstanceTaskState.HEALING)
vnf_instance.instantiated_vnf_info.instance_id =\
uuidsentinel.instance_id
self._mock_vnf_manager(fail_method_name='post_heal_vnf')
driver = vnflcm_driver.VnfLcmDriver()
self.assertRaises(exceptions.VnfHealFailed,
driver.heal_vnf, self.context, vnf_instance, heal_vnf_req)
self.assertEqual(1, mock_save.call_count)
self.assertEqual(3, self._vnf_manager.invoke.call_count)
self.assertEqual(fields.VnfInstanceTaskState.ERROR,
vnf_instance.task_state)
expected_msg = ('Failed to store updated resources information for '
'instance %(instance)s for vnf %(id)s. '
'Error: %(error)s')
mock_log.error.assert_called_with(expected_msg,
{'instance': vnf_instance.instantiated_vnf_info.instance_id,
'id': vnf_instance.id,
'error': 'post_heal_vnf failed'})

View File

@ -22,6 +22,7 @@ import tempfile
from tacker.common import exceptions from tacker.common import exceptions
from tacker import context from tacker import context
from tacker.extensions import vnfm from tacker.extensions import vnfm
from tacker import objects
from tacker.tests.common import helpers from tacker.tests.common import helpers
from tacker.tests.unit import base from tacker.tests.unit import base
from tacker.tests.unit.db import utils from tacker.tests.unit.db import utils
@ -77,6 +78,39 @@ class TestOpenStack(base.FixturedTestCase):
self.requests_mock.register_uri('GET', url, json=json, self.requests_mock.register_uri('GET', url, json=json,
headers=self.json_headers) headers=self.json_headers)
def _response_in_stack_get(self, id, stack_status='CREATE_COMPLETE'):
# response for heat_client's stack_get()
url = self.heat_url + '/stacks/' + id
json = {'stack': fd_utils.get_dummy_stack(status=stack_status)}
self.requests_mock.register_uri('GET', url, json=json,
headers=self.json_headers)
def _response_in_stack_update(self, id):
# response for heat_client's stack_patch()
url = self.heat_url + '/stacks/' + id
self.requests_mock.register_uri('PATCH', url,
headers=self.json_headers)
def _response_resource_mark_unhealthy(self, id, resources,
raise_exception=False):
# response for heat_client's heatclient.resource_mark_unhealthy
if not resources:
return
class MyException(Exception):
pass
for resource in resources:
url = os.path.join(self.heat_url, 'stacks', id,
'myStack/60f83b5e/resources', resource['resource_name'])
if raise_exception:
self.requests_mock.register_uri('PATCH', url,
exc=MyException("Any stuff"))
else:
self.requests_mock.register_uri('PATCH', url)
def test_create_wait(self): def test_create_wait(self):
self._response_in_wait_until_stack_ready(["CREATE_IN_PROGRESS", self._response_in_wait_until_stack_ready(["CREATE_IN_PROGRESS",
"CREATE_COMPLETE"]) "CREATE_COMPLETE"])
@ -235,10 +269,16 @@ class TestOpenStack(base.FixturedTestCase):
region_name=None) region_name=None)
self.assertEqual(dummy_event['id'], event_id) self.assertEqual(dummy_event['id'], event_id)
def _response_in_resource_get_list(self): def _response_in_resource_get_list(self, stack_id=None,
resources=None):
# response for heat_client's resource_get_list() # response for heat_client's resource_get_list()
if stack_id:
url = self.heat_url + '/stacks/' + stack_id + '/resources'
else:
url = self.heat_url + '/stacks/' + self.stack_id + '/resources' url = self.heat_url + '/stacks/' + self.stack_id + '/resources'
json = {'resources': [fd_utils.get_dummy_resource()]} resources = resources or [fd_utils.get_dummy_resource()]
json = {'resources': resources}
self.requests_mock.register_uri('GET', url, json=json, self.requests_mock.register_uri('GET', url, json=json,
headers=self.json_headers) headers=self.json_headers)
@ -763,3 +803,269 @@ class TestOpenStack(base.FixturedTestCase):
vnf_instance.instantiated_vnf_info. vnf_instance.instantiated_vnf_info.
ext_managed_virtual_link_info[0].vnf_link_ports[0]. ext_managed_virtual_link_info[0].vnf_link_ports[0].
resource_handle.resource_id) resource_handle.resource_id)
def test_heal_vnf_instance(self):
v_s_resource_info = fd_utils.get_virtual_storage_resource_info(
desc_id="storage1")
storage_resource_ids = [v_s_resource_info.id]
vnfc_resource_info = fd_utils.get_vnfc_resource_info(vdu_id="VDU_VNF",
storage_resource_ids=storage_resource_ids)
inst_vnf_info = fd_utils.get_vnf_instantiated_info(
virtual_storage_resource_info=[v_s_resource_info],
vnfc_resource_info=[vnfc_resource_info])
vnf_instance = fd_utils.get_vnf_instance_object(
instantiated_vnf_info=inst_vnf_info)
vim_connection_info = fd_utils.get_vim_connection_info_object()
heal_vnf_request = objects.HealVnfRequest(
vnfc_instance_id=[vnfc_resource_info.id],
cause="healing request")
# Mock various heat APIs that will be called by heatclient
# during the process of heal_vnf.
resources = [{
'resource_name': vnfc_resource_info.vdu_id,
'resource_type': vnfc_resource_info.compute_resource.
vim_level_resource_type,
'physical_resource_id': vnfc_resource_info.compute_resource.
resource_id}, {
'resource_name': v_s_resource_info.virtual_storage_desc_id,
'resource_type': v_s_resource_info.storage_resource.
vim_level_resource_type,
'physical_resource_id': v_s_resource_info.storage_resource.
resource_id}]
self._response_in_stack_get(inst_vnf_info.instance_id)
self._response_in_resource_get_list(inst_vnf_info.instance_id,
resources=resources)
self._responses_in_stack_list(inst_vnf_info.instance_id,
resources=resources)
self._response_in_stack_update(inst_vnf_info.instance_id)
self._response_resource_mark_unhealthy(inst_vnf_info.instance_id,
resources=resources)
self.openstack.heal_vnf(
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 3 times patch should be called, 2 for marking resources
# as unhealthy, and 1 for updating stack
self.assertEqual(3, len(patch_req))
def test_heal_vnf_instance_resource_mark_unhealthy_error(self):
vnfc_resource_info = fd_utils.get_vnfc_resource_info(vdu_id="VDU_VNF")
inst_vnf_info = fd_utils.get_vnf_instantiated_info(
vnfc_resource_info=[vnfc_resource_info])
vnf_instance = fd_utils.get_vnf_instance_object(
instantiated_vnf_info=inst_vnf_info)
vim_connection_info = fd_utils.get_vim_connection_info_object()
heal_vnf_request = objects.HealVnfRequest(
vnfc_instance_id=[vnfc_resource_info.id],
cause="healing request")
# Mock various heat APIs that will be called by heatclient
# during the process of heal_vnf.
resources = [{
'resource_name': vnfc_resource_info.vdu_id,
'resource_type': vnfc_resource_info.compute_resource.
vim_level_resource_type,
'physical_resource_id': vnfc_resource_info.compute_resource.
resource_id}]
self._response_in_stack_get(inst_vnf_info.instance_id)
self._response_in_resource_get_list(inst_vnf_info.instance_id,
resources=resources)
self._responses_in_stack_list(inst_vnf_info.instance_id,
resources=resources)
self._response_resource_mark_unhealthy(inst_vnf_info.instance_id,
resources=resources, raise_exception=True)
result = self.assertRaises(exceptions.VnfHealFailed,
self.openstack.heal_vnf, self.context, vnf_instance,
vim_connection_info, heal_vnf_request)
expected_msg = ("Failed to mark stack '%(id)s' resource as unhealthy "
"for resource '%(resource_name)s'") % {
'id': inst_vnf_info.instance_id,
'resource_name': resources[0]['resource_name']}
self.assertIn(expected_msg, str(result))
history = self.requests_mock.request_history
patch_req = [req.url for req in history if req.method == 'PATCH']
# only one time patch method be called for marking vdu resource
# as unhealthy
self.assertEqual(1, len(patch_req))
def test_heal_vnf_instance_incorrect_stack_status(self):
inst_vnf_info = fd_utils.get_vnf_instantiated_info()
vnf_instance = fd_utils.get_vnf_instance_object(
instantiated_vnf_info=inst_vnf_info)
vim_connection_info = fd_utils.get_vim_connection_info_object()
heal_vnf_request = objects.HealVnfRequest(
vnfc_instance_id=[uuidsentinel.vnfc_resource_id],
cause="healing request")
# 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,
stack_status='UPDATE_IN_PROGRESS')
result = self.assertRaises(exceptions.VnfHealFailed,
self.openstack.heal_vnf, self.context, vnf_instance,
vim_connection_info, heal_vnf_request)
expected_msg = ("Healing of vnf instance %s is possible only when "
"stack %s status is CREATE_COMPLETE,UPDATE_COMPLETE, "
"current stack status is UPDATE_IN_PROGRESS")
self.assertIn(expected_msg % (vnf_instance.id,
inst_vnf_info.instance_id), str(result))
def test_heal_vnf_wait(self):
inst_vnf_info = fd_utils.get_vnf_instantiated_info()
vnf_instance = fd_utils.get_vnf_instance_object(
instantiated_vnf_info=inst_vnf_info)
vim_connection_info = fd_utils.get_vim_connection_info_object()
# Mock various heat APIs that will be called by heatclient
# during the process of heal_vnf_wait.
self._response_in_wait_until_stack_ready(["UPDATE_IN_PROGRESS",
"UPDATE_COMPLETE"])
stack = self.openstack.heal_vnf_wait(
self.context, vnf_instance, vim_connection_info)
self.assertEqual('UPDATE_COMPLETE', stack.stack_status)
def test_heal_vnf_wait_fail(self):
inst_vnf_info = fd_utils.get_vnf_instantiated_info()
vnf_instance = fd_utils.get_vnf_instance_object(
instantiated_vnf_info=inst_vnf_info)
vim_connection_info = fd_utils.get_vim_connection_info_object()
# Mock various heat APIs that will be called by heatclient
# during the process of heal_vnf_wait.
self._response_in_wait_until_stack_ready(["UPDATE_IN_PROGRESS"])
self.openstack.STACK_RETRIES = 1
result = self.assertRaises(vnfm.VNFHealWaitFailed,
self.openstack.heal_vnf_wait, self.context, vnf_instance,
vim_connection_info)
expected_msg = ("VNF Heal action is not completed within 10 seconds "
"on stack %s") % inst_vnf_info.instance_id
self.assertIn(expected_msg, str(result))
def test_post_heal_vnf(self):
v_s_resource_info = fd_utils.get_virtual_storage_resource_info(
desc_id="storage1")
storage_resource_ids = [v_s_resource_info.id]
vnfc_resource_info = fd_utils.get_vnfc_resource_info(vdu_id="VDU_VNF",
storage_resource_ids=storage_resource_ids)
inst_vnf_info = fd_utils.get_vnf_instantiated_info(
virtual_storage_resource_info=[v_s_resource_info],
vnfc_resource_info=[vnfc_resource_info])
vnfc_resource_info.metadata['stack_id'] = inst_vnf_info.instance_id
vnf_instance = fd_utils.get_vnf_instance_object(
instantiated_vnf_info=inst_vnf_info)
vim_connection_info = fd_utils.get_vim_connection_info_object()
heal_vnf_request = objects.HealVnfRequest(
vnfc_instance_id=[vnfc_resource_info.id],
cause="healing request")
# Change the physical_resource_id of both the resources, so
# that we can check it's updated in vnf instance after
# post_heal_vnf call.
resources = [{
'resource_name': vnfc_resource_info.vdu_id,
'resource_type': vnfc_resource_info.compute_resource.
vim_level_resource_type,
'physical_resource_id': uuidsentinel.compute_resource_id_new}, {
'resource_name': v_s_resource_info.virtual_storage_desc_id,
'resource_type': v_s_resource_info.storage_resource.
vim_level_resource_type,
'physical_resource_id': uuidsentinel.storage_resource_id_new}]
# Mock various heat APIs that will be called by heatclient
# during the process of heal_vnf.
self._responses_in_stack_list(inst_vnf_info.instance_id,
resources=resources)
v_s_resource_info_old = v_s_resource_info.obj_clone()
vnfc_resource_info_old = vnfc_resource_info.obj_clone()
self.openstack.post_heal_vnf(self.context, vnf_instance,
vim_connection_info, heal_vnf_request)
vnfc_resource_info_new = vnf_instance.instantiated_vnf_info.\
vnfc_resource_info[0]
v_s_resource_info_new = vnf_instance.instantiated_vnf_info.\
virtual_storage_resource_info[0]
# Compare the resource_id, it should be updated with the new ones.
self.assertNotEqual(vnfc_resource_info_old.compute_resource.
resource_id, vnfc_resource_info_new.compute_resource.resource_id)
self.assertEqual(uuidsentinel.compute_resource_id_new,
vnfc_resource_info_new.compute_resource.resource_id)
self.assertNotEqual(v_s_resource_info_old.storage_resource.resource_id,
v_s_resource_info_new.storage_resource.resource_id)
self.assertEqual(uuidsentinel.storage_resource_id_new,
v_s_resource_info_new.storage_resource.resource_id)
def test_post_heal_vnf_fail(self):
vnfc_resource_info = fd_utils.get_vnfc_resource_info()
inst_vnf_info = fd_utils.get_vnf_instantiated_info(
vnfc_resource_info=[vnfc_resource_info])
vnfc_resource_info.metadata['stack_id'] = uuidsentinel.stack_id
vnf_instance = fd_utils.get_vnf_instance_object(
instantiated_vnf_info=inst_vnf_info)
vim_connection_info = fd_utils.get_vim_connection_info_object()
heal_vnf_request = objects.HealVnfRequest(
vnfc_instance_id=[vnfc_resource_info.id],
cause="healing request")
# Change the physical_resource_id of both the resources, so
# that we can check it's updated in vnf instance after
# post_heal_vnf call.
resources = [{
'resource_name': vnfc_resource_info.vdu_id,
'resource_type': vnfc_resource_info.compute_resource.
vim_level_resource_type,
'physical_resource_id': uuidsentinel.compute_resource_id_new}]
# Mock various heat APIs that will be called by heatclient
# during the process of heal_vnf.
self._responses_in_stack_list(inst_vnf_info.instance_id,
resources=resources)
result = self.assertRaises(exceptions.VnfHealFailed,
self.openstack.post_heal_vnf, self.context, vnf_instance,
vim_connection_info, heal_vnf_request)
expected_msg = ("Heal Vnf failed for vnf %s, error: Failed to find "
"stack_id %s") % (vnf_instance.id,
uuidsentinel.stack_id)
self.assertEqual(expected_msg, str(result))

View File

@ -264,8 +264,8 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
encodeutils.exception_to_unicode(exp)) encodeutils.exception_to_unicode(exp))
def _delete_vnf_instance_resources(self, context, vnf_instance, def _delete_vnf_instance_resources(self, context, vnf_instance,
vim_connection_info, vim_connection_info, terminate_vnf_req=None,
terminate_vnf_req=None): update_instantiated_state=True):
if vnf_instance.instantiated_vnf_info and \ if vnf_instance.instantiated_vnf_info and \
vnf_instance.instantiated_vnf_info.instance_id: vnf_instance.instantiated_vnf_info.instance_id:
@ -285,6 +285,7 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
'delete', plugin=self, context=context, 'delete', plugin=self, context=context,
vnf_id=instance_id, auth_attr=access_info) vnf_id=instance_id, auth_attr=access_info)
if update_instantiated_state:
vnf_instance.instantiation_state = \ vnf_instance.instantiation_state = \
fields.VnfInstanceState.NOT_INSTANTIATED fields.VnfInstanceState.NOT_INSTANTIATED
vnf_instance.save() vnf_instance.save()
@ -304,3 +305,114 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
vnf_resource=vnf_resource) vnf_resource=vnf_resource)
vnf_resource.destroy(context) vnf_resource.destroy(context)
def _heal_vnf(self, context, vnf_instance, vim_connection_info,
heal_vnf_request):
inst_vnf_info = vnf_instance.instantiated_vnf_info
try:
self._vnf_manager.invoke(
vim_connection_info.vim_type, 'heal_vnf',
context=context, vnf_instance=vnf_instance,
vim_connection_info=vim_connection_info,
heal_vnf_request=heal_vnf_request)
except Exception as exp:
with excutils.save_and_reraise_exception() as exc_ctxt:
exc_ctxt.reraise = False
LOG.error("Failed to heal vnf %(id)s in infra driver. "
"Error: %(error)s", {"id": vnf_instance.id, "error":
encodeutils.exception_to_unicode(exp)})
raise exceptions.VnfHealFailed(id=vnf_instance.id,
error=encodeutils.exception_to_unicode(exp))
try:
self._vnf_manager.invoke(
vim_connection_info.vim_type, 'heal_vnf_wait',
context=context, vnf_instance=vnf_instance,
vim_connection_info=vim_connection_info)
except Exception as exp:
LOG.error("Failed to update vnf %(id)s resources for instance"
"%(instance)s. Error: %(error)s",
{'id': vnf_instance.id, 'instance':
inst_vnf_info.instance_id, 'error':
encodeutils.exception_to_unicode(exp)})
try:
self._vnf_manager.invoke(
vim_connection_info.vim_type, 'post_heal_vnf',
context=context, vnf_instance=vnf_instance,
vim_connection_info=vim_connection_info,
heal_vnf_request=heal_vnf_request)
self._vnf_instance_update(context, vnf_instance, task_state=None)
except Exception as exp:
with excutils.save_and_reraise_exception() as exc_ctxt:
exc_ctxt.reraise = False
LOG.error("Failed to store updated resources information for "
"instance %(instance)s for vnf %(id)s. "
"Error: %(error)s",
{'id': vnf_instance.id, 'instance':
inst_vnf_info.instance_id, 'error':
encodeutils.exception_to_unicode(exp)})
raise exceptions.VnfHealFailed(id=vnf_instance.id,
error=encodeutils.exception_to_unicode(exp))
def _respawn_vnf(self, context, vnf_instance, vim_connection_info,
heal_vnf_request):
try:
self._delete_vnf_instance_resources(context, vnf_instance,
vim_connection_info, update_instantiated_state=False)
except Exception as exc:
with excutils.save_and_reraise_exception() as exc_ctxt:
exc_ctxt.reraise = False
err_msg = ("Failed to delete vnf resources for vnf instance "
"%(id)s before respawning. The vnf is in "
"inconsistent state. Error: %(error)s")
LOG.error(err_msg % {"id": vnf_instance.id,
"error": six.text_type(exc)})
raise exceptions.VnfHealFailed(id=vnf_instance.id,
error=encodeutils.exception_to_unicode(exc))
# InstantiateVnfRequest is not stored in the db as it's mapped
# to InstantiatedVnfInfo version object. Convert InstantiatedVnfInfo
# version object to InstantiateVnfRequest so that vnf can be
# instantiated.
instantiate_vnf_request = objects.InstantiateVnfRequest.\
from_vnf_instance(vnf_instance)
try:
self._instantiate_vnf(context, vnf_instance, vim_connection_info,
instantiate_vnf_request)
except Exception as exc:
with excutils.save_and_reraise_exception() as exc_ctxt:
exc_ctxt.reraise = False
err_msg = ("Failed to instantiate vnf instance "
"%(id)s after termination. The vnf is in "
"inconsistent state. Error: %(error)s")
LOG.error(err_msg % {"id": vnf_instance.id,
"error": six.text_type(exc)})
raise exceptions.VnfHealFailed(id=vnf_instance.id,
error=encodeutils.exception_to_unicode(exc))
self._vnf_instance_update(context, vnf_instance,
instantiation_state=fields.VnfInstanceState.INSTANTIATED,
task_state=None)
@log.log
@revert_to_error_task_state
def heal_vnf(self, context, vnf_instance, heal_vnf_request):
LOG.info("Request received for healing vnf '%s'", vnf_instance.id)
vim_info = vnflcm_utils._get_vim(context,
vnf_instance.vim_connection_info)
vim_connection_info = objects.VimConnectionInfo.obj_from_primitive(
vim_info, context)
if not heal_vnf_request.vnfc_instance_id:
self._respawn_vnf(context, vnf_instance, vim_connection_info,
heal_vnf_request)
else:
self._heal_vnf(context, vnf_instance, vim_connection_info,
heal_vnf_request)
LOG.info("Request received for healing vnf '%s' is completed "
"successfully", vnf_instance.id)

View File

@ -102,3 +102,34 @@ class VnfAbstractDriver(extensions.PluginInterface):
def post_vnf_instantiation(self, context, vnf_instance, def post_vnf_instantiation(self, context, vnf_instance,
vim_connection_info): vim_connection_info):
pass pass
@abc.abstractmethod
def heal_vnf(self, context, vnf_instance, vim_connection_info,
heal_vnf_request):
"""Heal vnf
:param context: A RequestContext
:param vnf_instance: tacker.objects.VnfInstance to be healed
:vim_info: Credentials to initialize Vim connection
:heal_vnf_request: tacker.objects.HealVnfRequest object containing
parameters passed in the heal request
"""
pass
@abc.abstractmethod
def heal_vnf_wait(self, context, vnf_instance, vim_connection_info):
"""Check vnf is healed successfully"""
pass
@abc.abstractmethod
def post_heal_vnf(self, context, vnf_instance, vim_connection_info,
heal_vnf_request):
"""Update resource_id for each vnfc resources
:param context: A RequestContext
:param vnf_instance: tacker.objects.VnfInstance to be healed
:vim_info: Credentials to initialize Vim connection
:heal_vnf_request: tacker.objects.HealVnfRequest object containing
parameters passed in the heal request
"""
pass

View File

@ -567,3 +567,14 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
def post_vnf_instantiation(self, context, vnf_instance, def post_vnf_instantiation(self, context, vnf_instance,
vim_connection_info): vim_connection_info):
raise NotImplementedError() raise NotImplementedError()
def heal_vnf(self, context, vnf_instance, vim_connection_info,
heal_vnf_request):
raise NotImplementedError()
def heal_vnf_wait(self, context, vnf_instance, vim_connection_info):
raise NotImplementedError()
def post_heal_vnf(self, context, vnf_instance, vim_connection_info,
heal_vnf_request):
raise NotImplementedError()

View File

@ -93,3 +93,14 @@ class VnfNoop(abstract_driver.VnfAbstractDriver):
def post_vnf_instantiation(self, context, vnf_instance, def post_vnf_instantiation(self, context, vnf_instance,
vim_connection_info): vim_connection_info):
pass pass
def heal_vnf(self, context, vnf_instance, vim_connection_info,
heal_vnf_request):
pass
def heal_vnf_wait(self, context, vnf_instance, vim_connection_info):
pass
def post_heal_vnf(self, context, vnf_instance, vim_connection_info,
heal_vnf_request):
pass

View File

@ -796,3 +796,160 @@ class OpenStack(abstract_driver.VnfAbstractDriver,
resource_details[id].update({'child_stack': child_stack}) resource_details[id].update({'child_stack': child_stack})
return resource_details return resource_details
def _get_vnfc_resources_from_heal_request(self, inst_vnf_info,
heal_vnf_request):
if not heal_vnf_request.vnfc_instance_id:
# include all vnfc resources
return [resource for resource in inst_vnf_info.vnfc_resource_info]
vnfc_resources = []
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)
return vnfc_resources
@log.log
def heal_vnf(self, context, vnf_instance, vim_connection_info,
heal_vnf_request):
access_info = vim_connection_info.access_info
region_name = access_info.get('region')
inst_vnf_info = vnf_instance.instantiated_vnf_info
heatclient = hc.HeatClient(access_info, region_name=region_name)
def _get_storage_resources(vnfc_resource):
# Prepare list of storage resources to be marked unhealthy
for storage_id in vnfc_resource.storage_resource_ids:
for storage_res_info in inst_vnf_info. \
virtual_storage_resource_info:
if storage_res_info.id == storage_id:
yield storage_res_info.virtual_storage_desc_id, \
storage_res_info.storage_resource.resource_id
def _get_vdu_resources(vnfc_resources):
# Prepare list of vdu resources to be marked unhealthy
vdu_resources = []
for vnfc_resource in vnfc_resources:
resource_details = {"resource_name": vnfc_resource.vdu_id,
"physical_resource_id":
vnfc_resource.compute_resource.resource_id}
vdu_resources.append(resource_details)
# Get storage resources
for resource_name, resource_id in \
_get_storage_resources(vnfc_resource):
resource_details = {"resource_name": resource_name,
"physical_resource_id": resource_id}
vdu_resources.append(resource_details)
return vdu_resources
def _prepare_stack_resources_for_updation(vdu_resources,
stack_resources):
for resource in vdu_resources:
for stack_uuid, resources in stack_resources.items():
res_details = resources.get(resource['resource_name'])
if res_details and res_details['physical_resource_id'] == \
resource['physical_resource_id']:
yield stack_uuid, resource['resource_name']
def _resource_mark_unhealthy():
vnfc_resources = self._get_vnfc_resources_from_heal_request(
inst_vnf_info, heal_vnf_request)
vdu_resources = _get_vdu_resources(vnfc_resources)
stack_resources = self._get_stack_resources(
inst_vnf_info.instance_id, heatclient)
cause = heal_vnf_request.cause or "Healing"
for stack_uuid, resource_name in \
_prepare_stack_resources_for_updation(
vdu_resources, stack_resources):
try:
LOG.info("Marking resource %(resource)s as unhealthy for "
"stack %(stack)s for vnf instance %(id)s",
{"resource": resource_name,
"stack": stack_uuid,
"id": vnf_instance.id})
heatclient.resource_mark_unhealthy(
stack_id=stack_uuid,
resource_name=resource_name, mark_unhealthy=True,
resource_status_reason=cause)
except Exception as exp:
msg = ("Failed to mark stack '%(stack_id)s' resource as "
"unhealthy for resource '%(resource)s', "
"Error: %(error)s")
raise exceptions.VnfHealFailed(id=vnf_instance.id,
error=msg % {"stack_id": inst_vnf_info.instance_id,
"resource": resource_name,
"error": str(exp)})
def _get_stack_status():
stack_statuses = ["CREATE_COMPLETE", "UPDATE_COMPLETE"]
stack = heatclient.get(inst_vnf_info.instance_id)
if stack.stack_status not in stack_statuses:
error = ("Healing of vnf instance %(id)s is possible only "
"when stack %(stack_id)s status is %(statuses)s, "
"current stack status is %(status)s")
raise exceptions.VnfHealFailed(id=vnf_instance.id,
error=error % {"id": vnf_instance.id,
"stack_id": inst_vnf_info.instance_id,
"statuses": ",".join(stack_statuses),
"status": stack.stack_status})
_get_stack_status()
_resource_mark_unhealthy()
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)
@log.log
def heal_vnf_wait(self, context, vnf_instance, vim_connection_info):
"""Check vnf is healed successfully"""
access_info = vim_connection_info.access_info
region_name = access_info.get('region')
inst_vnf_info = vnf_instance.instantiated_vnf_info
stack = self._wait_until_stack_ready(inst_vnf_info.instance_id,
access_info, infra_cnst.STACK_UPDATE_IN_PROGRESS,
infra_cnst.STACK_UPDATE_COMPLETE, vnfm.VNFHealWaitFailed,
region_name=region_name)
return stack
def post_heal_vnf(self, context, vnf_instance, vim_connection_info,
heal_vnf_request):
"""Update resource_id for each vnfc resources
:param context: A RequestContext
:param vnf_instance: tacker.objects.VnfInstance to be healed
:vim_info: Credentials to initialize Vim connection
:heal_vnf_request: tacker.objects.HealVnfRequest object containing
parameters passed in the heal request
"""
access_info = vim_connection_info.access_info
region_name = access_info.get('region')
heatclient = hc.HeatClient(access_info, region_name)
inst_vnf_info = vnf_instance.instantiated_vnf_info
stack_resources = self._get_stack_resources(inst_vnf_info.instance_id,
heatclient)
vnfc_resources = self._get_vnfc_resources_from_heal_request(
inst_vnf_info, heal_vnf_request)
for vnfc_res_info in vnfc_resources:
stack_id = vnfc_res_info.metadata.get("stack_id")
resources = stack_resources.get(stack_id)
if not resources:
# NOTE(tpatil): This could happen when heat child stacks
# and the stack_id stored in metadata of vnfc_res_info are
# not in sync. There is no point in syncing inconsistent
# resources information so exit with an error,
error = "Failed to find stack_id %s" % stack_id
raise exceptions.VnfHealFailed(id=vnf_instance.id,
error=error)
self._update_vnfc_resource_info(vnf_instance, vnfc_res_info,
{stack_id: resources}, update_network_resource=False)