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:
parent
55319282f3
commit
3f3cee41c0
@ -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
|
||||
=====================
|
||||
|
||||
|
@ -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')
|
||||
|
||||
|
@ -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
|
||||
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
flavor: 'm1.small'
|
||||
}
|
@ -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'])
|
||||
|
@ -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',
|
||||
|
@ -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]
|
@ -0,0 +1,2 @@
|
||||
flavor: m1.tiny
|
||||
reservation_id: 891cd152-3925-4c9e-9074-239a902b68d7
|
@ -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
|
||||
|
@ -0,0 +1,2 @@
|
||||
flavor: m1.tiny
|
||||
reservation_id: 891cd152-3925-4c9e-9074-239a902b68d7
|
@ -0,0 +1,3 @@
|
||||
flavor: m1.tiny
|
||||
reservation_id: 99999999-3925-4c9e-9074-239a902b68d7
|
||||
new_param_key: new_param_value
|
@ -0,0 +1,2 @@
|
||||
flavor: m1.tiny
|
||||
reservation_id: 99999999-3925-4c9e-9074-239a902b68d7
|
@ -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',
|
||||
|
@ -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'] = {}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user