Support updating VNF parameters in tacker

Implementation to update parameters of created VNF in tacker.

Change-Id: I7746644f7340ea8e25150f9fd8fbc59ec4e5c720
Blueprint: https://blueprints.launchpad.net/tacker/+spec/reservation-vnfm
This commit is contained in:
Hiroo Kitamura 2020-02-27 09:20:38 +00:00
parent 55319282f3
commit 3f3cee41c0
17 changed files with 425 additions and 7 deletions

View File

@ -19,8 +19,8 @@ VNF Manager User Guide
======================
Tacker VNF Manager (VNFM) component manages the life-cycle of a Virtual Network
Function (VNF). VNFM takes care of deployment, monitoring, scaling and removal
of VNFs on a Virtual Infrastructure Manager (VIM).
Function (VNF). VNFM takes care of deployment, monitoring, updating, scaling
and removal of VNFs on a Virtual Infrastructure Manager (VIM).
Onboarding VNF
@ -122,6 +122,19 @@ Status of various VNFM resources can be checked by following commands.
..
Updating VNF
============
VNFs can be updated as shown below.
--config, --config-file and --param-file can not be specified together.
.. code-block:: console
openstack vnf set --config <CONFIG-DATA> <VNF_ID/NAME>
openstack vnf set --config-file <CONFIG-FILE-NAME> <VNF_ID/NAME>
openstack vnf set --param-file <PARAMETER-FILE-NAME> <VNF_ID/NAME>
..
Deleting VNF and VNFD
=====================

View File

@ -64,6 +64,10 @@ class VNFCreateFailed(exceptions.TackerException):
message = _('creating VNF based on %(vnfd_id)s failed')
class VNFUpdateInvalidInput(exceptions.TackerException):
message = _('VNF Update Invalid Input %(reason)s')
class VNFUpdateWaitFailed(exceptions.TackerException):
message = _('VNF Update %(reason)s')

View File

@ -15,6 +15,7 @@ DEFAULT_ALARM_ACTIONS = ['respawn', 'log', 'log_and_kill', 'notify']
POLICY_RESERVATION = 'tosca.policies.tacker.Reservation'
VNF_CIRROS_CREATE_TIMEOUT = 300
VNFC_CREATE_TIMEOUT = 600
VNF_CIRROS_UPDATE_TIMEOUT = 300
VNF_CIRROS_DELETE_TIMEOUT = 300
VNF_CIRROS_DEAD_TIMEOUT = 500
ACTIVE_SLEEP_TIME = 3

View File

@ -0,0 +1,3 @@
{
flavor: 'm1.small'
}

View File

@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import time
import unittest
import yaml
@ -85,6 +86,48 @@ class VnfmTestParam(base.BaseTackerTest):
return vnf_instance, param_values_dict
def _test_vnf_update(self, vnf_instance, param_values):
# Update Vnf
vnf_id = vnf_instance['vnf']['id']
new_param_values = {'vnf': {'attributes': {
'param_values': param_values}}}
self.client.update_vnf(vnf_id, new_param_values)
self.wait_until_vnf_active(
vnf_id,
constants.VNF_CIRROS_UPDATE_TIMEOUT,
constants.ACTIVE_SLEEP_TIME)
# Wait until the update on the heat side is completed,
# because vnf deletion will cause a conflict without waiting for this.
stack_id = self.client.show_vnf(vnf_id)['vnf']['instance_id']
start_time = int(time.time())
while True:
vdu_resource = self.get_vdu_resource(stack_id, "VDU1")
vdu_resource_dict = vdu_resource.to_dict()
vdu_resource_status = vdu_resource_dict['resource_status']
if ((int(time.time()) - start_time >
constants.VNF_CIRROS_UPDATE_TIMEOUT) or
(vdu_resource_status == 'UPDATE_COMPLETE')):
break
time.sleep(constants.ACTIVE_SLEEP_TIME)
self.verify_vnf_crud_events(
vnf_id, evt_constants.RES_EVT_UPDATE, evt_constants.PENDING_UPDATE)
self.verify_vnf_crud_events(
vnf_id, evt_constants.RES_EVT_UPDATE, evt_constants.ACTIVE)
# Verify vnf_param_values_dict is same as param values from vnf_show
vnf_instance = self.client.show_vnf(vnf_id)
vnf_param_values = vnf_instance['vnf']['attributes']['param_values']
vnf_param_values_dict = yaml.safe_load(vnf_param_values)
# Verify stack_parameters is same as parameters from stack_show
instance_id = vnf_instance['vnf']['instance_id']
stack_values = self.h_client.stacks.get(instance_id)
stack_parameters = stack_values.parameters
return vnf_param_values_dict, stack_parameters
def _test_vnf_delete(self, vnf_instance):
# Delete Vnf
vnf_id = vnf_instance['vnf']['id']
@ -126,11 +169,32 @@ class VnfmTestParam(base.BaseTackerTest):
def _test_vnf_param_tosca_template(self, vnfd_file, vnfd_name,
param_file, vnf_name):
vnfd_instance = self._test_vnfd_create(vnfd_file, vnfd_name)
# Get vnfd_id
vnfd_id = vnfd_instance['vnfd']['id']
# Add vnfd delete to cleanup job so that if vnf_instance fails to
# create or update then it will be cleaned-up automatically
# in tearDown()
self.addCleanup(self.client.delete_vnfd, vnfd_id)
# Create vnf instance
values_str = read_file(param_file)
values_dict = yaml.safe_load(values_str)
vnf_instance, param_values_dict = self._test_vnf_create(
vnfd_instance, vnf_name, values_dict)
self.assertEqual(values_dict, param_values_dict)
new_values_str = read_file('sample-tosca-vnf-update-values.yaml')
new_values_dict = yaml.safe_load(new_values_str)
vnf_param_values_dict, stack_parameters = self._test_vnf_update(
vnf_instance, new_values_dict)
for key, value in new_values_dict.items():
if vnf_param_values_dict.get(key):
self.assertEqual(value, vnf_param_values_dict[key])
if stack_parameters.get(key):
self.assertEqual(value, stack_parameters[key])
self._test_vnf_delete(vnf_instance)
vnf_id = vnf_instance['vnf']['id']
self.verify_vnf_crud_events(
@ -142,4 +206,3 @@ class VnfmTestParam(base.BaseTackerTest):
constants.VNF_CIRROS_DELETE_TIMEOUT)
self.verify_vnf_crud_events(vnf_id, evt_constants.RES_EVT_DELETE,
evt_constants.PENDING_DELETE, cnt=2)
self.addCleanup(self.client.delete_vnfd, vnfd_instance['vnfd']['id'])

View File

@ -30,9 +30,15 @@ def _get_template(name):
return f.read()
tosca_vnfd_openwrt = _get_template('test_tosca_openwrt.yaml')
tosca_vnfd_openwrt_param = _get_template('test_tosca_openwrt_param.yaml')
tosca_invalid_vnfd = _get_template('test_tosca_parser_failure.yaml')
config_data = _get_template('config_data.yaml')
update_config_data = _get_template('update_config_data.yaml')
hot_data = _get_template('hot_data.yaml')
param_data = _get_template('param_data.yaml')
update_param_data = _get_template('update_param_data.yaml')
update_invalid_param_data = _get_template('update_invalid_param_data.yaml')
update_new_param_data = _get_template('update_new_param_data.yaml')
vnffg_params = _get_template('vnffg_params.yaml')
vnffg_multi_params = _get_template('vnffg_multi_params.yaml')
vnffgd_template = yaml.safe_load(_get_template('vnffgd_template.yaml'))
@ -129,6 +135,24 @@ def get_dummy_vnf_config_obj():
'config': {'firewall': 'dummy_firewall_values'}}}}}}}
def get_dummy_vnf_invalid_config_type_obj():
return {'vnf': {u'attributes': {u'config': 'dummy_config'}}}
def get_dummy_vnf_invalid_param_content():
return {'vnf': {u'attributes': {u'param_values': {}}}}
def get_dummy_vnf_param_obj():
return {'vnf': {u'attributes': {u'param_values':
{'flavor': 'm1.tiny',
'reservation_id': '99999999-3925-4c9e-9074-239a902b68d7'}}}}
def get_dummy_vnf_invalid_param_type_obj():
return {'vnf': {u'attributes': {u'param_values': 'dummy_param'}}}
def get_dummy_vnf(status='PENDING_CREATE', scaling_group=False,
instance_id=None):
dummy_vnf = {'status': status, 'instance_id': instance_id, 'name':
@ -172,10 +196,45 @@ def get_dummy_vnf_config_attr():
'description': u'OpenWRT with services'}
def get_dummy_vnf_param_attr():
return {'status': 'PENDING_CREATE', 'instance_id': None, 'name':
u'test_openwrt', 'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
'vnfd_id': u'eb094833-995e-49f0-a047-dfb56aaf7c4e',
'vnfd': {'service_types': [{'service_type': u'vnfd',
'id': u'4a4c2d44-8a52-4895-9a75-9d1c76c3e738'}],
'description': u'OpenWRT with services',
'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
'mgmt_driver': u'openwrt',
'attributes': {u'vnfd': tosca_vnfd_openwrt_param},
'id': u'fb048660-dc1b-4f0f-bd89-b023666650ec',
'name': u'openwrt_services'},
'mgmt_url': None, 'service_context': [],
'attributes': {'heat_template': hot_data,
'param_values': param_data},
'id': 'eb84260e-5ff7-4332-b032-50a14d6c1123',
'description': u'OpenWRT with services'}
def get_dummy_vnf_update_config():
return {'vnf': {'attributes': {'config': update_config_data}}}
def get_dummy_vnf_update_param():
return {'vnf': {'attributes': {'param_values': update_param_data}}}
def get_dummy_vnf_update_new_param():
return {'vnf': {'attributes': {'param_values': update_new_param_data}}}
def get_dummy_vnf_update_invalid_param():
return {'vnf': {'attributes': {'param_values': update_invalid_param_data}}}
def get_dummy_vnf_update_empty_param():
return {'vnf': {'attributes': {'param_values': {}}}}
def get_vim_obj():
return {'vim': {'type': 'openstack',
'auth_url': 'http://localhost/identity',

View File

@ -0,0 +1,27 @@
heat_template_version: 2013-05-23
description: 'OpenWRT with services
'
parameters:
flavor: {type: string, default: m1.tiny}
reservation_id: {type: string, default: 891cd152-3925-4c9e-9074-239a902b68d7}
resources:
VDU1:
type: OS::Nova::Server
properties:
networks:
- port: {get_resource: CP1}
image: OpenWRT
user_data_format: SOFTWARE_CONFIG
config_drive: false
flavor: {get_param: flavor}
scheduler_hints:
reservation: {get_param: reservation_id}
CP1:
type: OS::Neutron::Port
properties: {network: existing_network_1, port_security_enabled: false}
outputs:
mgmt_ip-VDU1:
value:
get_attr: [CP1, fixed_ips, 0, ip_address]

View File

@ -0,0 +1,2 @@
flavor: m1.tiny
reservation_id: 891cd152-3925-4c9e-9074-239a902b68d7

View File

@ -0,0 +1,44 @@
tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0
description: OpenWRT with services
metadata:
template_name: OpenWRT
topology_template:
inputs:
flavor:
type: string
reservation_id:
type: string
node_templates:
VDU1:
type: tosca.nodes.nfv.VDU.Tacker
properties:
image: OpenWRT
flavor: {get_input: flavor}
reservation: {get_input: reservation_id}
config: |
param0: key1
param1: key2
mgmt_driver: openwrt
CP1:
type: tosca.nodes.nfv.CP.Tacker
properties:
management: true
anti_spoofing_protection: false
requirements:
- virtualLink:
node: VL1
- virtualBinding:
node: VDU1
VL1:
type: tosca.nodes.nfv.VL
properties:
network_name: existing_network_1
vendor: Tacker

View File

@ -0,0 +1,2 @@
flavor: m1.tiny
reservation_id: 891cd152-3925-4c9e-9074-239a902b68d7

View File

@ -0,0 +1,3 @@
flavor: m1.tiny
reservation_id: 99999999-3925-4c9e-9074-239a902b68d7
new_param_key: new_param_value

View File

@ -0,0 +1,2 @@
flavor: m1.tiny
reservation_id: 99999999-3925-4c9e-9074-239a902b68d7

View File

@ -105,6 +105,7 @@ class TestOpenStack(base.TestCase):
hot_param_template = _get_template('hot_openwrt_params.yaml')
hot_ipparam_template = _get_template('hot_openwrt_ipparams.yaml')
tosca_vnfd_openwrt = _get_template('test_tosca_openwrt.yaml')
tosca_vnfd_openwrt_param = _get_template('test_tosca_openwrt_param.yaml')
config_data = _get_template('config_data.yaml')
def setUp(self):
@ -196,6 +197,40 @@ class TestOpenStack(base.TestCase):
'id': 'eb84260e-5ff7-4332-b032-50a14d6c1123', 'description':
u'OpenWRT with services'}
def _get_expected_vnf_update_param_obj(self):
return {'status': 'PENDING_CREATE', 'instance_id': None, 'name':
u'test_openwrt', 'tenant_id':
u'ad7ebc56538745a08ef7c5e97f8bd437', 'vnfd_id':
u'eb094833-995e-49f0-a047-dfb56aaf7c4e', 'vnfd': {
'service_types': [{'service_type': u'vnfd', 'id':
u'4a4c2d44-8a52-4895-9a75-9d1c76c3e738'}], 'description':
u'OpenWRT with services', 'tenant_id':
u'ad7ebc56538745a08ef7c5e97f8bd437', 'mgmt_driver': u'openwrt',
'attributes': {u'vnfd': self.tosca_vnfd_openwrt_param},
'id': u'fb048660-dc1b-4f0f-bd89-b023666650ec', 'name':
u'openwrt_services'}, 'mgmt_url': None, 'service_context': [],
'attributes': {'heat_template': utils.hot_data,
'param_values': utils.update_param_data},
'id': 'eb84260e-5ff7-4332-b032-50a14d6c1123', 'description':
u'OpenWRT with services'}
def _get_expected_vnf_update_new_param_obj(self):
return {'status': 'PENDING_CREATE', 'instance_id': None, 'name':
u'test_openwrt', 'tenant_id':
u'ad7ebc56538745a08ef7c5e97f8bd437', 'vnfd_id':
u'eb094833-995e-49f0-a047-dfb56aaf7c4e', 'vnfd': {
'service_types': [{'service_type': u'vnfd', 'id':
u'4a4c2d44-8a52-4895-9a75-9d1c76c3e738'}], 'description':
u'OpenWRT with services', 'tenant_id':
u'ad7ebc56538745a08ef7c5e97f8bd437', 'mgmt_driver': u'openwrt',
'attributes': {u'vnfd': self.tosca_vnfd_openwrt_param},
'id': u'fb048660-dc1b-4f0f-bd89-b023666650ec', 'name':
u'openwrt_services'}, 'mgmt_url': None, 'service_context': [],
'attributes': {'heat_template': utils.hot_data,
'param_values': utils.update_new_param_data},
'id': 'eb84260e-5ff7-4332-b032-50a14d6c1123', 'description':
u'OpenWRT with services'}
def _get_expected_active_vnf(self):
return {'status': 'ACTIVE',
'instance_id': None,
@ -259,6 +294,51 @@ class TestOpenStack(base.TestCase):
mock_log.error.assert_called_with(
"VNF '%s' failed to heal", vnf_dict['id'])
def test_update_new_param(self):
vnf_obj = utils.get_dummy_vnf_param_attr()
vnf_param_obj = utils.get_dummy_vnf_update_new_param()
expected_vnf_update = self._get_expected_vnf_update_new_param_obj()
vnf_id = '4a4c2d44-8a52-4895-9a75-9d1c76c3e738'
self.infra_driver.update(plugin=None, context=self.context,
vnf_id=vnf_id, vnf_dict=vnf_obj,
vnf=vnf_param_obj,
auth_attr=utils.get_vim_auth_obj())
expected_vnf_update['attributes']['param_values'] = yaml.safe_load(
expected_vnf_update['attributes']['param_values'])
vnf_obj['attributes']['param_values'] = yaml.safe_load(
vnf_obj['attributes']['param_values'])
self.assertEqual(expected_vnf_update, vnf_obj)
@mock.patch('tacker.vnfm.infra_drivers.openstack.openstack.LOG')
def test_update_invalid_param(self, mock_log):
vnf_obj = utils.get_dummy_vnf_param_attr()
vnf_param_obj = utils.get_dummy_vnf_update_invalid_param()
vnf_id = '4a4c2d44-8a52-4895-9a75-9d1c76c3e738'
self.assertRaises(vnfm.VNFUpdateInvalidInput,
self.infra_driver.update,
plugin=None, context=self.context,
vnf_id=vnf_id, vnf_dict=vnf_obj,
vnf=vnf_param_obj,
auth_attr=utils.get_vim_auth_obj())
log_msg = "at vnf_id {} because all parameters "\
"match the existing one.".format(vnf_id)
mock_log.warning.assert_called_with(log_msg)
@mock.patch('tacker.vnfm.infra_drivers.openstack.openstack.LOG')
def test_update_empty_param(self, mock_log):
vnf_obj = utils.get_dummy_vnf_param_attr()
vnf_param_obj = utils.get_dummy_vnf_update_empty_param()
vnf_id = '4a4c2d44-8a52-4895-9a75-9d1c76c3e738'
self.assertRaises(vnfm.VNFUpdateInvalidInput,
self.infra_driver.update,
plugin=None, context=self.context,
vnf_id=vnf_id, vnf_dict=vnf_obj,
vnf=vnf_param_obj,
auth_attr=utils.get_vim_auth_obj())
log_msg = "at vnf_id {} because the target "\
"yaml is empty.".format(vnf_id)
mock_log.warning.assert_called_with(log_msg)
def _get_expected_fields_tosca(self, template):
return {'stack_name':
'test_openwrt_eb84260e-5ff7-4332-b032-50a14d6c1123',

View File

@ -824,6 +824,57 @@ class TestVNFMPlugin(db_base.SqlTestCase):
dummy_vnf_obj['id'],
'VNF Update failed')
def test_update_vnf_param(self):
self._insert_dummy_vnf_template()
dummy_device_obj = self._insert_dummy_vnf()
vnf_param_obj = utils.get_dummy_vnf_param_obj()
result = self.vnfm_plugin.update_vnf(self.context,
dummy_device_obj['id'],
vnf_param_obj)
self.assertIsNotNone(result)
self.assertEqual(dummy_device_obj['id'], result['id'])
self.assertIn('instance_id', result)
self.assertIn('status', result)
self.assertIn('attributes', result)
self.assertIn('mgmt_ip_address', result)
self.assertIn('updated_at', result)
self._cos_db_plugin.create_event.assert_called_with(
self.context, evt_type=constants.RES_EVT_UPDATE, res_id=mock.ANY,
res_state=mock.ANY, res_type=constants.RES_TYPE_VNF,
tstamp=mock.ANY)
def test_update_vnf_invalid_config_type(self):
self._insert_dummy_vnf_template()
dummy_device_obj = self._insert_dummy_vnf()
vnf_param_obj = utils.get_dummy_vnf_invalid_config_type_obj()
self.assertRaises(vnfm.InvalidAPIAttributeType,
self.vnfm_plugin.update_vnf,
self.context,
dummy_device_obj['id'],
vnf_param_obj)
def test_update_vnf_invalid_param_type(self):
self._insert_dummy_vnf_template()
dummy_device_obj = self._insert_dummy_vnf()
vnf_param_obj = utils.get_dummy_vnf_invalid_param_type_obj()
self.assertRaises(vnfm.InvalidAPIAttributeType,
self.vnfm_plugin.update_vnf,
self.context,
dummy_device_obj['id'],
vnf_param_obj)
def test_update_vnf_invalid_param_content(self):
self.update.side_effect = vnfm.VNFUpdateInvalidInput(
reason='failed')
self._insert_dummy_vnf_template()
dummy_device_obj = self._insert_dummy_vnf()
vnf_param_obj = utils.get_dummy_vnf_invalid_param_content()
self.assertRaises(vnfm.VNFUpdateInvalidInput,
self.vnfm_plugin.update_vnf,
self.context,
dummy_device_obj['id'],
vnf_param_obj)
def _get_dummy_scaling_policy(self, type):
vnf_scale = {}
vnf_scale['scale'] = {}

View File

@ -54,7 +54,11 @@ class HeatClient(object):
return self.stacks.get(stack_id)
def update(self, stack_id, **kwargs):
try:
return self.stacks.update(stack_id, **kwargs)
except heatException.HTTPException:
type_, value, tb = sys.exc_info()
raise vnfm.HeatClientException(msg=value)
def resource_attr_support(self, resource_name, property_name):
resource = self.resource_types.get(resource_name)

View File

@ -202,11 +202,52 @@ class OpenStack(abstract_driver.VnfAbstractDriver,
heatclient = hc.HeatClient(auth_attr, region_name)
heatclient.get(vnf_id)
update_param_yaml = vnf['vnf'].get('attributes', {}).get(
'param_values', '')
update_config_yaml = vnf['vnf'].get('attributes', {}).get(
'config', '')
if update_param_yaml:
# conversion param_values
param_yaml = vnf_dict.get('attributes', {}).get('param_values', '')
param_dict = yaml.safe_load(param_yaml)
update_param_dict = yaml.safe_load(update_param_yaml)
# check update values
update_values = {}
for key, value in update_param_dict.items():
if key not in param_dict or\
update_param_dict[key] != param_dict[key]:
update_values[key] = value
if not update_values:
error_reason = _("at vnf_id {} because all parameters "
"match the existing one.").format(vnf_id)
LOG.warning(error_reason)
raise vnfm.VNFUpdateInvalidInput(reason=error_reason)
# update vnf_dict
utils.deep_update(param_dict, update_param_dict)
new_param_yaml = yaml.safe_dump(param_dict)
vnf_dict.setdefault(
'attributes', {})['param_values'] = new_param_yaml
# run stack update
stack_update_param = {
'parameters': update_values,
'existing': True}
heatclient.update(vnf_id, **stack_update_param)
elif not update_param_yaml and not update_config_yaml:
error_reason = _("at vnf_id {} because the target "
"yaml is empty.").format(vnf_id)
LOG.warning(error_reason)
raise vnfm.VNFUpdateInvalidInput(reason=error_reason)
# update config attribute
config_yaml = vnf_dict.get('attributes', {}).get('config', '')
update_yaml = vnf['vnf'].get('attributes', {}).get('config', '')
LOG.debug('yaml orig %(orig)s update %(update)s',
{'orig': config_yaml, 'update': update_yaml})
{'orig': config_yaml, 'update': update_config_yaml})
# If config_yaml is None, yaml.safe_load() will raise Attribute Error.
# So set config_yaml to {}, if it is None.
@ -214,7 +255,7 @@ class OpenStack(abstract_driver.VnfAbstractDriver,
config_dict = {}
else:
config_dict = yaml.safe_load(config_yaml) or {}
update_dict = yaml.safe_load(update_yaml)
update_dict = yaml.safe_load(update_config_yaml)
if not update_dict:
return

View File

@ -115,6 +115,7 @@ class VNFMPlugin(vnfm_db.VNFMPluginDb, VNFMMgmtMixin):
Plugin which supports Tacker framework
"""
OPTS_INFRA_DRIVER = [
cfg.ListOpt(
'infra_driver', default=['noop', 'openstack', 'kubernetes'],
@ -532,6 +533,17 @@ class VNFMPlugin(vnfm_db.VNFMPluginDb, VNFMMgmtMixin):
vnf_attributes['config'] = yaml.safe_dump(config)
else:
raise vnfm.InvalidAPIAttributeType(atype=type(config))
if vnf_attributes.get('param_values'):
param = vnf_attributes['param_values']
if isinstance(param, dict):
# TODO(sripriya) remove this yaml dump once db supports storing
# json format of yaml files in a separate column instead of
# key value string pairs in vnf attributes table
vnf_attributes['param_values'] = yaml.safe_dump(param)
else:
raise vnfm.InvalidAPIAttributeType(atype=type(param))
vnf_dict = self._update_vnf_pre(context, vnf_id,
constants.PENDING_UPDATE)
driver_name, vim_auth = self._get_infra_driver(context, vnf_dict)
@ -543,6 +555,13 @@ class VNFMPlugin(vnfm_db.VNFMPluginDb, VNFMMgmtMixin):
driver_name, 'update', plugin=self, context=context,
vnf_id=instance_id, vnf_dict=vnf_dict,
vnf=vnf, auth_attr=vim_auth)
except vnfm.VNFUpdateInvalidInput:
with excutils.save_and_reraise_exception():
vnf_dict['status'] = constants.ACTIVE
self._update_vnf_post(context, vnf_id,
constants.ACTIVE,
vnf_dict, constants.PENDING_UPDATE,
constants.RES_EVT_UPDATE)
except Exception as e:
with excutils.save_and_reraise_exception():
vnf_dict['status'] = constants.ERROR