Fix for Multi BaseHot VNF cannot be instantiated

This fix will allow Instantiation to be successful when
Multiple Base Hot is defined in the VNFD Package.

Closes-Bug: # 1895830
https://bugs.launchpad.net/tacker/+bug/1895830

Change-Id: I2d5e677820a4978d609ab492aa64cdc5269fd5c9
(cherry picked from commit 59e166b62a)
This commit is contained in:
Aldinson Esto 2020-09-18 22:54:10 +09:00 committed by Toshiaki Takahashi
parent e72a66bbba
commit 3795395a62
6 changed files with 455 additions and 61 deletions

View File

@ -134,8 +134,15 @@ class TestOpenStack(base.FixturedTestCase):
yaml_file_dict = yaml.safe_load(f)
return yaml_file_dict
@mock.patch('tacker.vnfm.infra_drivers.openstack.openstack'
'.OpenStack._format_base_hot')
@mock.patch('tacker.vnflcm.utils._get_vnflcm_interface')
@mock.patch('tacker.vnflcm.utils.get_base_nest_hot_dict')
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_normal(self, mock_OpenstackClients_heat):
def test_create_normal(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict,
mock_get_vnflcm_interface,
mock_format_base_hot):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file()
@ -148,14 +155,20 @@ class TestOpenStack(base.FixturedTestCase):
'instantiate_vnf_request_lcm_userdata.json')
inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None
inst_req_info_test.flavour_id = test_json['flavourId']
vnf_resource = type('', (), {})
vnf_resource.resource_identifier = constants.INVALID_UUID
grant_info_test = {'vdu_name': {vnf_resource}}
nested_hot_dict = {'parameters': {'vnf': 'test'}}
mock_get_base_hot_dict.return_value = \
self._read_file(), nested_hot_dict
vnf_instance = fd_utils.get_vnf_instance_object()
self.openstack.create(self.plugin, self.context, vnf,
self.auth_attr, inst_req_info=inst_req_info_test,
vnf_package_path=vnf_package_path_test,
base_hot_dict=base_hot_dict_test,
grant_info=grant_info_test)
grant_info=grant_info_test,
vnf_instance=vnf_instance)
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_heat_stack(self, mock_OpenstackClients_heat):
@ -218,8 +231,10 @@ class TestOpenStack(base.FixturedTestCase):
inst_req_info=inst_req_info_test,
grant_info=grant_info_test)
@mock.patch('tacker.vnflcm.utils.get_base_nest_hot_dict')
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_userdata_null(self, mock_OpenstackClients_heat):
def test_create_userdata_null(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file()
@ -235,17 +250,25 @@ class TestOpenStack(base.FixturedTestCase):
inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None
inst_req_info_test.flavour_id = test_json['flavourId']
grant_info_test = None
nested_hot_dict = {'test': 'test'}
mock_get_base_hot_dict.return_value = \
self._read_file(), nested_hot_dict
vnf_instance = fd_utils.get_vnf_instance_object()
self.assertRaises(vnfm.LCMUserDataFailed,
self.openstack.create,
self.plugin, self.context, vnf,
self.auth_attr, base_hot_dict_test,
vnf_package_path_test,
inst_req_info=inst_req_info_test,
grant_info=grant_info_test)
grant_info=grant_info_test,
vnf_instance=vnf_instance)
@mock.patch('tacker.vnflcm.utils.get_base_nest_hot_dict')
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_userdataclass_null(self, mock_OpenstackClients_heat):
def test_create_userdataclass_null(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file()
@ -261,17 +284,25 @@ class TestOpenStack(base.FixturedTestCase):
inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None
inst_req_info_test.flavour_id = test_json['flavourId']
grant_info_test = None
nested_hot_dict = {'test': 'test'}
mock_get_base_hot_dict.return_value = \
self._read_file(), nested_hot_dict
vnf_instance = fd_utils.get_vnf_instance_object()
self.assertRaises(vnfm.LCMUserDataFailed,
self.openstack.create,
self.plugin, self.context, vnf,
self.auth_attr, base_hot_dict_test,
vnf_package_path_test,
inst_req_info=inst_req_info_test,
grant_info=grant_info_test)
grant_info=grant_info_test,
vnf_instance=vnf_instance)
@mock.patch('tacker.vnflcm.utils.get_base_nest_hot_dict')
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_import_module_exception(self, mock_OpenstackClients_heat):
def test_create_import_module_exception(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file()
@ -284,7 +315,12 @@ class TestOpenStack(base.FixturedTestCase):
'instantiate_vnf_request_lcm_userdata.json')
inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None
inst_req_info_test.flavour_id = test_json['flavourId']
grant_info_test = None
nested_hot_dict = {'test': 'test'}
mock_get_base_hot_dict.return_value = \
self._read_file(), nested_hot_dict
vnf_instance = fd_utils.get_vnf_instance_object()
with mock.patch.object(importlib, 'import_module') as mock_importlib:
mock_importlib.side_effect = Exception('Test Exception')
self.assertRaises(vnfm.LCMUserDataFailed,
@ -293,10 +329,13 @@ class TestOpenStack(base.FixturedTestCase):
self.auth_attr, base_hot_dict_test,
vnf_package_path_test,
inst_req_info=inst_req_info_test,
grant_info=grant_info_test)
grant_info=grant_info_test,
vnf_instance=vnf_instance)
@mock.patch('tacker.vnflcm.utils.get_base_nest_hot_dict')
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_getattr_none(self, mock_OpenstackClients_heat):
def test_create_getattr_none(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file()
@ -309,7 +348,12 @@ class TestOpenStack(base.FixturedTestCase):
'instantiate_vnf_request_lcm_userdata.json')
inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None
inst_req_info_test.flavour_id = test_json['flavourId']
grant_info_test = None
nested_hot_dict = {'test': 'test'}
mock_get_base_hot_dict.return_value = \
self._read_file(), nested_hot_dict
vnf_instance = fd_utils.get_vnf_instance_object()
with mock.patch.object(importlib, 'import_module') as mock_importlib:
mock_importlib.return_value = None
self.assertRaises(vnfm.LCMUserDataFailed,
@ -318,10 +362,13 @@ class TestOpenStack(base.FixturedTestCase):
self.auth_attr, base_hot_dict_test,
vnf_package_path_test,
inst_req_info=inst_req_info_test,
grant_info=grant_info_test)
grant_info=grant_info_test,
vnf_instance=vnf_instance)
@mock.patch('tacker.vnflcm.utils.get_base_nest_hot_dict')
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_missing_file(self, mock_OpenstackClients_heat):
def test_create_missing_file(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file()
@ -334,19 +381,27 @@ class TestOpenStack(base.FixturedTestCase):
'instantiate_vnf_request_lcm_userdata.json')
inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None
inst_req_info_test.flavour_id = test_json['flavourId']
vnf_resource = type('', (), {})
vnf_resource.resource_identifier = constants.INVALID_UUID
grant_info_test = {'vdu_name': {vnf_resource}}
nested_hot_dict = {'test': 'test'}
mock_get_base_hot_dict.return_value = \
self._read_file(), nested_hot_dict
vnf_instance = fd_utils.get_vnf_instance_object()
self.assertRaises(vnfm.LCMUserDataFailed,
self.openstack.create,
self.plugin, self.context, vnf,
self.auth_attr, inst_req_info=inst_req_info_test,
vnf_package_path=vnf_package_path_test,
base_hot_dict=base_hot_dict_test,
grant_info=grant_info_test)
grant_info=grant_info_test,
vnf_instance=vnf_instance)
@mock.patch('tacker.vnflcm.utils.get_base_nest_hot_dict')
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_return_none_dict(self, mock_OpenstackClients_heat):
def test_create_return_none_dict(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file()
@ -361,38 +416,54 @@ class TestOpenStack(base.FixturedTestCase):
'UserData/lcm_user_data_non_dict.py'
inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None
inst_req_info_test.flavour_id = test_json['flavourId']
vnf_resource = type('', (), {})
vnf_resource.resource_identifier = constants.INVALID_UUID
grant_info_test = {'vdu_name': {vnf_resource}}
nested_hot_dict = {'test': 'test'}
mock_get_base_hot_dict.return_value = \
self._read_file(), nested_hot_dict
vnf_instance = fd_utils.get_vnf_instance_object()
self.assertRaises(vnfm.LCMUserDataFailed,
self.openstack.create,
self.plugin, self.context, vnf,
self.auth_attr, inst_req_info=inst_req_info_test,
vnf_package_path=vnf_package_path_test,
base_hot_dict=base_hot_dict_test,
grant_info=grant_info_test)
grant_info=grant_info_test,
vnf_instance=vnf_instance)
@mock.patch('tacker.vnflcm.utils.get_base_nest_hot_dict')
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_none_base_hot_dict(self, mock_OpenstackClients_heat):
def test_create_none_base_hot_dict(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
vnf['placement_attr'] = {'region_name': 'dummy_region'}
inst_req_info_test = type('', (), {})
test_json = self._json_load(
'instantiate_vnf_request_lcm_userdata.json')
inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.flavour_id = test_json['flavourId']
base_hot_dict_test = None
vnf_package_path_test = None
grant_info_test = None
nested_hot_dict = {}
mock_get_base_hot_dict.return_value = \
self._read_file(), nested_hot_dict
vnf_instance = fd_utils.get_vnf_instance_object()
self.assertRaises(vnfm.LCMUserDataFailed,
self.openstack.create,
self.plugin, self.context, vnf,
self.auth_attr, base_hot_dict_test,
vnf_package_path_test,
inst_req_info=inst_req_info_test,
grant_info=grant_info_test)
grant_info=grant_info_test,
vnf_instance=vnf_instance)
@mock.patch('tacker.vnflcm.utils.get_base_nest_hot_dict')
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_invalid_user_data(self, mock_OpenstackClients_heat):
def test_create_invalid_user_data(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file()
@ -409,18 +480,26 @@ class TestOpenStack(base.FixturedTestCase):
inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None
inst_req_info_test.flavour_id = test_json['flavourId']
grant_info_test = None
nested_hot_dict = {'test': 'test'}
mock_get_base_hot_dict.return_value = \
self._read_file(), nested_hot_dict
vnf_instance = fd_utils.get_vnf_instance_object()
self.assertRaises(vnfm.LCMUserDataFailed,
self.openstack.create,
self.plugin, self.context, vnf,
self.auth_attr, base_hot_dict_test,
vnf_package_path_test,
inst_req_info=inst_req_info_test,
grant_info=grant_info_test)
grant_info=grant_info_test,
vnf_instance=vnf_instance)
@mock.patch('tacker.vnflcm.utils.get_base_nest_hot_dict')
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_invalid_user_data_class(self,
mock_OpenstackClients_heat):
mock_OpenstackClients_heat,
mock_get_base_hot_dict):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file()
@ -437,18 +516,25 @@ class TestOpenStack(base.FixturedTestCase):
inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None
inst_req_info_test.flavour_id = test_json['flavourId']
grant_info_test = None
nested_hot_dict = {'test': 'test'}
mock_get_base_hot_dict.return_value = \
self._read_file(), nested_hot_dict
vnf_instance = fd_utils.get_vnf_instance_object()
self.assertRaises(vnfm.LCMUserDataFailed,
self.openstack.create,
self.plugin, self.context, vnf,
self.auth_attr, base_hot_dict_test,
vnf_package_path_test,
inst_req_info=inst_req_info_test,
grant_info=grant_info_test)
grant_info=grant_info_test,
vnf_instance=vnf_instance)
@mock.patch('tacker.vnflcm.utils.get_base_nest_hot_dict')
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_lcm_user_data_and_user_data_class_no_value(self,
mock_OpenstackClients_heat):
mock_OpenstackClients_heat, mock_get_base_hot_dict):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file()
@ -466,16 +552,22 @@ class TestOpenStack(base.FixturedTestCase):
inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = test_json['extVirtualLinks']
inst_req_info_test.flavour_id = test_json['flavourId']
vnf_resource = type('', (), {})
vnf_resource.resource_identifier = constants.INVALID_UUID
grant_info_test = {'vdu_name': {vnf_resource}}
nested_hot_dict = {'test': 'test'}
mock_get_base_hot_dict.return_value = \
self._read_file(), nested_hot_dict
vnf_instance = fd_utils.get_vnf_instance_object()
self.assertRaises(vnfm.LCMUserDataFailed,
self.openstack.create,
self.plugin, self.context, vnf,
self.auth_attr, inst_req_info=inst_req_info_test,
vnf_package_path=vnf_package_path_test,
base_hot_dict=base_hot_dict_test,
grant_info=grant_info_test)
grant_info=grant_info_test,
vnf_instance=vnf_instance)
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_lcm_user_data_and_user_data_class_none(self,
@ -523,8 +615,10 @@ class TestOpenStack(base.FixturedTestCase):
vnf_package_path=vnf_package_path_test,
inst_req_info=inst_req_info_test)
@mock.patch('tacker.vnflcm.utils.get_base_nest_hot_dict')
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_instance_exception(self, mock_OpenstackClients_heat):
def test_create_instance_exception(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file()
@ -539,16 +633,22 @@ class TestOpenStack(base.FixturedTestCase):
'UserData/lcm_user_data_invalid_script.py'
inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None
inst_req_info_test.flavour_id = test_json['flavourId']
vnf_resource = type('', (), {})
vnf_resource.resource_identifier = constants.INVALID_UUID
grant_info_test = {'vdu_name': {vnf_resource}}
nested_hot_dict = {'test': 'test'}
mock_get_base_hot_dict.return_value = \
self._read_file(), nested_hot_dict
vnf_instance = fd_utils.get_vnf_instance_object()
self.assertRaises(vnfm.LCMUserDataFailed,
self.openstack.create,
self.plugin, self.context, vnf,
self.auth_attr, inst_req_info=inst_req_info_test,
vnf_package_path=vnf_package_path_test,
base_hot_dict=base_hot_dict_test,
grant_info=grant_info_test)
grant_info=grant_info_test,
vnf_instance=vnf_instance)
def test_create_wait(self):
self._response_in_wait_until_stack_ready(["CREATE_IN_PROGRESS",
@ -1082,10 +1182,11 @@ class TestOpenStack(base.FixturedTestCase):
self.requests_mock.register_uri(
'POST', url, json={'stack': fd_utils.get_dummy_stack()},
headers=self.json_headers)
vnf_instance = fd_utils.get_vnf_instance_object()
instance_id = self.openstack.instantiate_vnf(
self.context, None, vnfd_dict, vim_connection_info,
inst_req_info, grant_response)
self.context, vnf_instance, vnfd_dict, vim_connection_info,
inst_req_info, grant_response, self.plugin)
self.assertEqual(uuidsentinel.instance_id, instance_id)

View File

@ -51,7 +51,8 @@ class TestUtils(testtools.TestCase):
return yaml_file_dict
def test_create_initial_param_dict(self):
base_hot_dict = self._read_file("hot_lcm_user_data.yaml")
base_hot_dict = {}
base_hot_dict['resources'] = self._read_file("hot_lcm_user_data.yaml")
initial_param_dict = utils.create_initial_param_dict(base_hot_dict)
self.assertEqual(example_initial_param_dict, initial_param_dict)
@ -153,7 +154,8 @@ class TestUtils(testtools.TestCase):
self.assertEqual({}, vdu_image_dict)
def test_create_cpd_vl_dict(self):
base_hot_dict = {'resources': {'dummy_cpd_id': "101010_d"}}
base_hot_dict = \
{'resources': {'resources': {'dummy_cpd_id': "101010_d"}}}
inst_req_info = instantiate_vnf_req.InstantiateVnfRequest()
ext_virtual_links_test_value = instantiate_vnf_req.ExtVirtualLinkData()
ext_virtual_links_test_value.resource_id = 'dummy_resource_id'
@ -169,7 +171,8 @@ class TestUtils(testtools.TestCase):
self.assertEqual({'dummy_cpd_id': 'dummy_resource_id'}, cpd_vl_dict)
def test_create_cpd_vl_dict_no_cp_resource(self):
base_hot_dict = {'resources': {'dummy_cpd_id': "101010_d"}}
base_hot_dict = \
{'resources': {'resources': {'dummy_cpd_id': "101010_d"}}}
inst_req_info = instantiate_vnf_req.InstantiateVnfRequest()
ext_virtual_links_test_value = instantiate_vnf_req.ExtVirtualLinkData()
ext_virtual_links_test_value.resource_id = 'dummy_resource_id'

View File

@ -76,6 +76,36 @@ def _get_vnfd_dict(context, vnfd_id, flavour_id):
return vnfd_dict
def _get_vnflcm_interface(context, interface, vnf_instance, flavour_id):
'''Gets the interface found in vnfd
...
node_templates:
VNF:
interfaces:
Vnflcm:
<interface>
'''
interface_value = None
vnfd_dict = _get_vnfd_dict(context, vnf_instance.vnfd_id, flavour_id)
if not isinstance(vnfd_dict, dict):
raise exceptions.InvalidContentType(msg="VNFD not valid")
if vnfd_dict.get('topology_template'):
topology_template = vnfd_dict.get('topology_template')
if topology_template.get('node_templates'):
for a_val in topology_template.get('node_templates').values():
if 'interfaces' in a_val.keys():
interfaces = a_val.get('interfaces')
if interfaces.get('Vnflcm'):
vnflcm = interfaces.get('Vnflcm')
if vnflcm:
interface_value = vnflcm.get(interface)
return interface_value
def _build_affected_resources(vnf_instance,
change_type=fields.ResourceChangeType.ADDED):
'''build affected resources from vnf_instance instantiated info '''
@ -1022,3 +1052,31 @@ def _get_base_hot_dict(context, vnfd_id):
base_hot_dict = yaml.safe_load(open(source_file_path))
LOG.debug("Loaded base hot: %s", base_hot_dict)
return base_hot_dict
def get_base_nest_hot_dict(context, flavour_id, vnfd_id):
vnf_package_id = _get_vnf_package_id(context, vnfd_id)
vnf_package_base_path = cfg.CONF.vnf_package.vnf_package_csar_path
vnf_package_csar_path = vnf_package_base_path + '/' + vnf_package_id
base_hot_dir = 'BaseHOT'
ext = [".yaml", ".yml"]
base_hot_path = vnf_package_csar_path + '/' + \
base_hot_dir + '/' + flavour_id
base_hot_dict = None
nested_hot_path = base_hot_path + '/nested'
nested_hot_dict = {}
if os.path.exists(base_hot_path):
for file in os.listdir(base_hot_path):
if file.endswith(tuple(ext)):
source_file_path = os.path.join(base_hot_path, file)
base_hot_dict = yaml.safe_load(open(source_file_path))
if os.path.exists(nested_hot_path):
for file in os.listdir(nested_hot_path):
if file.endswith(tuple(ext)):
source_file_path = os.path.join(nested_hot_path, file)
nested_hot = yaml.safe_load(open(source_file_path))
nested_hot_dict[file] = nested_hot
LOG.debug("Loaded base hot: %s", base_hot_dict)
LOG.debug("Loaded nested_hot_dict: %s", nested_hot_dict)
return base_hot_dict, nested_hot_dict

View File

@ -136,8 +136,10 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
vim_connection_info, instantiate_vnf_req):
vnfd_dict = vnflcm_utils._get_vnfd_dict(context, vnf_instance.vnfd_id,
instantiate_vnf_req.flavour_id)
base_hot_dict = vnflcm_utils._get_base_hot_dict(
context, vnf_instance.vnfd_id)
base_hot_dict, nested_hot_dict = vnflcm_utils. \
get_base_nest_hot_dict(context,
instantiate_vnf_req.flavour_id,
vnf_instance.vnfd_id)
vnf_package_path = None
if base_hot_dict is not None:
vnf_package_path = vnflcm_utils._get_vnf_package_path(

View File

@ -14,12 +14,13 @@
# License for the specific language governing permissions and limitations
# under the License.
import copy
import eventlet
import importlib
import os
import sys
import time
import yaml
from collections import OrderedDict
from oslo_config import cfg
@ -28,8 +29,6 @@ from oslo_serialization import jsonutils
from oslo_utils import encodeutils
from oslo_utils import excutils
from oslo_utils import uuidutils
import yaml
from tacker._i18n import _
from tacker.common import exceptions
from tacker.common import log
@ -39,6 +38,7 @@ from tacker.extensions import vnfm
from tacker import objects
from tacker.objects import fields
from tacker.tosca.utils import represent_odict
from tacker.vnflcm import utils as vnflcm_utils
from tacker.vnfm.infra_drivers import abstract_driver
from tacker.vnfm.infra_drivers.openstack import constants as infra_cnst
from tacker.vnfm.infra_drivers.openstack import glance_client as gc
@ -48,6 +48,7 @@ from tacker.vnfm.infra_drivers.openstack import vdu
from tacker.vnfm.infra_drivers import scale_driver
from tacker.vnfm.lcm_user_data.constants import USER_DATA_TIMEOUT
eventlet.monkey_patch(time=True)
LOG = logging.getLogger(__name__)
@ -89,6 +90,8 @@ OUTPUT_PREFIX = 'mgmt_ip-'
ALARMING_POLICY = 'tosca.policies.tacker.Alarming'
SCALING_POLICY = 'tosca.policies.tacker.Scaling'
NOVA_SERVER_RESOURCE = "OS::Nova::Server"
def get_scaling_policy_name(action, policy_name):
return '%s_scale_%s' % (policy_name, action)
@ -117,7 +120,8 @@ class OpenStack(abstract_driver.VnfAbstractDriver,
@log.log
def create(self, plugin, context, vnf, auth_attr,
base_hot_dict=None, vnf_package_path=None,
inst_req_info=None, grant_info=None):
inst_req_info=None, grant_info=None,
vnf_instance=None):
LOG.debug('vnf %s', vnf)
region_name = vnf.get('placement_attr', {}).get('region_name', None)
heatclient = hc.HeatClient(auth_attr, region_name)
@ -138,10 +142,20 @@ class OpenStack(abstract_driver.VnfAbstractDriver,
if user_data_path is not None and user_data_class is not None:
LOG.info('Execute user data and create heat-stack.')
base_hot_dict, nested_hot_dict = vnflcm_utils. \
get_base_nest_hot_dict(context,
inst_req_info.flavour_id,
vnf_instance.vnfd_id)
if base_hot_dict is None:
error_reason = _("failed to get Base HOT.")
raise vnfm.LCMUserDataFailed(reason=error_reason)
if base_hot_dict is None:
nested_hot_dict = {}
for name, hot in nested_hot_dict.items():
vnf['attributes'][name] = self._format_base_hot(hot)
vnfd_str = vnf['vnfd']['attributes']['vnfd']
vnfd_dict = yaml.safe_load(vnfd_str)
LOG.debug('VNFD: %s', vnfd_dict)
@ -175,10 +189,13 @@ class OpenStack(abstract_driver.VnfAbstractDriver,
# Set the timeout and execute the UserData script.
hot_param_dict = None
param_base_hot_dict = copy.deepcopy(nested_hot_dict)
param_base_hot_dict['heat_template'] = base_hot_dict
with eventlet.timeout.Timeout(USER_DATA_TIMEOUT, False):
try:
hot_param_dict = klass.instantiate(
base_hot_dict, vnfd_dict, inst_req_info, grant_info)
param_base_hot_dict, vnfd_dict,
inst_req_info, grant_info)
except Exception:
raise
finally:
@ -196,9 +213,13 @@ class OpenStack(abstract_driver.VnfAbstractDriver,
"is not in dict format.")
raise vnfm.LCMUserDataFailed(reason=error_reason)
# Add stack param to vnf_attributes
vnf['attributes'].update({'stack_param': str(hot_param_dict)})
# Create heat-stack with BaseHOT and parameters
stack = self._create_stack_with_user_data(
heatclient, vnf, base_hot_dict, hot_param_dict)
heatclient, vnf, base_hot_dict,
nested_hot_dict, hot_param_dict)
elif user_data_path is None and user_data_class is None:
LOG.info('Execute heat-translator and create heat-stack.')
@ -230,13 +251,18 @@ class OpenStack(abstract_driver.VnfAbstractDriver,
@log.log
def _create_stack_with_user_data(self, heatclient, vnf,
base_hot_dict, hot_param_dict):
base_hot_dict, nested_hot_dict,
hot_param_dict):
fields = {}
fields['stack_name'] = ("vnflcm_" + vnf["id"])
fields['template'] = self._format_base_hot(base_hot_dict)
fields['parameters'] = hot_param_dict
fields['timeout_mins'] = (
self.STACK_RETRIES * self.STACK_RETRY_WAIT // 60)
if nested_hot_dict:
fields['files'] = {}
for name, value in nested_hot_dict.items():
fields['files'][name] = self._format_base_hot(value)
LOG.debug('fields: %s', fields)
LOG.debug('template: %s', fields['template'])
@ -771,7 +797,7 @@ class OpenStack(abstract_driver.VnfAbstractDriver,
def instantiate_vnf(self, context, vnf_instance, vnfd_dict,
vim_connection_info, instantiate_vnf_req,
grant_response,
grant_response, plugin,
base_hot_dict=None, vnf_package_path=None):
access_info = vim_connection_info.access_info
region_name = access_info.get('region')
@ -779,10 +805,11 @@ class OpenStack(abstract_driver.VnfAbstractDriver,
placement_attr.update({'region_name': region_name})
vnfd_dict['placement_attr'] = placement_attr
instance_id = self.create(None, context, vnfd_dict,
instance_id = self.create(plugin, context, vnfd_dict,
access_info, base_hot_dict, vnf_package_path,
inst_req_info=instantiate_vnf_req,
grant_info=grant_response)
grant_info=grant_response,
vnf_instance=vnf_instance)
vnfd_dict['instance_id'] = instance_id
return instance_id

View File

@ -33,7 +33,11 @@ LOG = logging.getLogger(__name__)
HOT_NOVA_SERVER = 'OS::Nova::Server'
HOT_NOVA_FLAVOR = 'OS::Nova::Flavor'
HOT_NEUTRON_PORT = 'OS::Neutron::Port'
SUPPORTED_HOT_TYPE = [HOT_NOVA_SERVER, HOT_NOVA_FLAVOR, HOT_NEUTRON_PORT]
AUTO_SCALING_GROUP = 'OS::Heat::AutoScalingGroup'
HOT_CINDER_VOLUME = 'OS::Cinder::Volume'
SUPPORTED_HOT_TYPE = [HOT_NOVA_SERVER, HOT_NOVA_FLAVOR,
HOT_NEUTRON_PORT, HOT_CINDER_VOLUME]
PORT_SERVER_TYPE = [HOT_NOVA_SERVER, HOT_NEUTRON_PORT, HOT_CINDER_VOLUME]
def create_initial_param_dict(base_hot_dict):
@ -55,20 +59,123 @@ def create_initial_param_dict(base_hot_dict):
}
}
resources = base_hot_dict.get('resources', {})
for resource_name, resource_val in resources.items():
resource_type = resource_val.get('type')
if resource_type in SUPPORTED_HOT_TYPE:
resource_props = resource_val.get('properties', {})
for prop_key, prop_val in resource_props.items():
if isinstance(prop_val, dict) and 'get_param' in prop_val:
param_list = prop_val['get_param']
if len(param_list) == 4:
resource_info = initial_param_dict.get(
param_list[0], {}).get(
param_list[1], {})
if param_list[2] not in resource_info:
resource_info[param_list[2]] = {}
for hot_dict in base_hot_dict.values():
param_nfv = hot_dict.get('parameters', {}).get('nfv')
if not param_nfv:
continue
resources = hot_dict.get('resources', {})
for resource_name, resource_val in resources.items():
resource_type = resource_val.get('type')
if resource_type in SUPPORTED_HOT_TYPE:
resource_props = resource_val.get('properties', {})
for prop_key, prop_val in resource_props.items():
if isinstance(prop_val, dict) and 'get_param' in prop_val:
param_list = prop_val['get_param']
if len(param_list) == 4:
resource_info = initial_param_dict.get(
param_list[0], {}).get(
param_list[1], {})
if param_list[2] not in resource_info:
resource_info[param_list[2]] = {}
if resource_type == HOT_NOVA_SERVER:
cinder_boot = resource_props.get('block_device_mapping_v2',
{})
if cinder_boot:
for boot in cinder_boot:
b_param = boot.get('image')
if b_param and \
isinstance(b_param, dict) and \
'get_param' in b_param:
param_list = b_param['get_param']
if len(param_list) == 4:
resource_info = initial_param_dict.get(
param_list[0], {}).get(
param_list[1], {})
if param_list[2] not in resource_info:
resource_info[param_list[2]] = {}
elif resource_type == AUTO_SCALING_GROUP:
resource_nest = resource_val.get('properties').get('resource')
resource_props = resource_nest.get('properties', {})
for prop_key, prop_val in resource_props.items():
if isinstance(prop_val, dict) and 'get_param' in prop_val:
param_list = prop_val['get_param']
if len(param_list) == 4:
resource_info = initial_param_dict.get(
param_list[0], {}).get(
param_list[1], {})
if param_list[2] not in resource_info:
resource_info[param_list[2]] = {}
LOG.info('initial_param_dict: %s', initial_param_dict)
return initial_param_dict
def create_initial_param_server_port_dict(base_hot_dict):
"""Create initial dict containing information about get_param resources.
:param base_hot_dict: dict(Base HOT dict format)
:return: dict('nfv', Initial HOT resource dict)
NOTE: 'nfv' is a fixed value for 1st element.
'VDU' and 'CP' are supported for 2nd element.
3rd and 4th element are mandatory.
"""
initial_param_dict = {
'nfv': {
'VDU': {
},
'CP': {
}
}
}
for hot_dict in base_hot_dict.values():
LOG.debug("init hot_dict: %s", hot_dict)
param_nfv = hot_dict.get('parameters', {}).get('nfv')
if not param_nfv:
continue
resources = hot_dict.get('resources', {})
for resource_name, resource_val in resources.items():
resource_type = resource_val.get('type')
if resource_type in PORT_SERVER_TYPE:
resource_props = resource_val.get('properties', {})
for prop_key, prop_val in resource_props.items():
if isinstance(prop_val, dict) and 'get_param' in prop_val:
param_list = prop_val['get_param']
if len(param_list) == 4:
resource_info = initial_param_dict.get(
param_list[0], {}).get(
param_list[1], {})
if param_list[2] not in resource_info:
resource_info[param_list[2]] = {}
if resource_type == HOT_NOVA_SERVER:
cinder_boot = resource_props.get('block_device_mapping_v2',
{})
if cinder_boot:
for boot in cinder_boot:
b_param = boot.get('image')
if b_param and \
isinstance(b_param, dict) and \
'get_param' in b_param:
param_list = b_param['get_param']
if len(param_list) == 4:
resource_info = initial_param_dict.get(
param_list[0], {}).get(
param_list[1], {})
if param_list[2] not in resource_info:
resource_info[param_list[2]] = {}
elif resource_type == AUTO_SCALING_GROUP:
resource_nest = resource_val.get('properties').get('resource')
resource_props = resource_nest.get('properties', {})
for prop_key, prop_val in resource_props.items():
if isinstance(prop_val, dict) and 'get_param' in prop_val:
param_list = prop_val['get_param']
if len(param_list) == 4:
resource_info = initial_param_dict.get(
param_list[0], {}).get(
param_list[1], {})
if param_list[2] not in resource_info:
resource_info[param_list[2]] = {}
LOG.info('initial_param_dict: %s', initial_param_dict)
return initial_param_dict
@ -162,11 +269,107 @@ def create_cpd_vl_dict(base_hot_dict, inst_req_info):
ext_cps = ext_vl.ext_cps
vl_uuid = ext_vl.resource_id
for ext_cp in ext_cps:
cp_resource = base_hot_dict['resources'].get(
ext_cp.cpd_id)
if cp_resource is None:
continue
cpd_vl_dict[ext_cp.cpd_id] = vl_uuid
for hot_dict in base_hot_dict.values():
cp_resource = hot_dict['resources'].get(
ext_cp.cpd_id)
if cp_resource is None:
continue
cpd_vl_dict[ext_cp.cpd_id] = vl_uuid
break
LOG.info('cpd_vl_dict: %s', cpd_vl_dict)
return cpd_vl_dict
def get_diff_base_hot_param_from_api(base_hot_dict, inst_req_info):
"""Compare base hot param from API param.
:param base_hot_dict: dict(Base HOT dict format)
:param inst_req_info: dict(Instantiation request information format)
:return: dict(Parameters)
"""
param_value = {}
additional_param = inst_req_info.additional_params
if additional_param is None:
additional_param = {}
input_attributes = base_hot_dict['heat_template'].get('parameters')
for input_attr, value in input_attributes.items():
if additional_param.get(input_attr):
param_value.update({input_attr: additional_param.get(
input_attr)})
return param_value
def create_vdu_flavor_capability_name_dict(vnfd_dict):
"""Create a dict containing information about VDU's flavor.
:param vnfd_dict: dict(VNFD dict format)
:return: dict(VDU name, VDU Capability Name dict)
"""
vdu_flavor_dict = {}
node_templates = vnfd_dict.get(
'topology_template', {}).get(
'node_templates', {})
for vdu_name, val in node_templates.items():
vdu_flavor_props = val.get(
'capabilities', {}).get(
'virtual_compute', {}).get('properties', {})
if vdu_flavor_props is not {}:
for key, val in vdu_flavor_props.items():
if key == 'requested_additional_capabilities':
capability_props = val.get('properties', {})
if 'requested_additional_capability_name'\
in capability_props.keys():
vdu_flavor_dict[vdu_name] = \
capability_props[
"requested_additional"
"_capability_name"]
LOG.info('vdu_flavor_dict: %s', vdu_flavor_dict)
return vdu_flavor_dict
def create_sw_image_dict(vnfd_dict):
"""Create a dict containing information about VDU's flavor.
:param vnfd_dict: dict(VNFD dict format)
:return: dict(VDU name, VDU SW Image data dict)
"""
sw_image_data = {}
node_templates = vnfd_dict.get(
'topology_template', {}).get(
'node_templates', {})
for vdu_name, val in node_templates.items():
sw_image_data_props = val.get(
'properties', {}).get('sw_image_data', {})
if sw_image_data_props is not {}:
if 'name' in sw_image_data_props.keys():
sw_image_data[vdu_name] = sw_image_data_props['name']
LOG.info('sw_image_data: %s', sw_image_data)
return sw_image_data
def create_network_dict(inst_req_info, param_dict):
"""Create a dict containing information about VDU's network.
:param inst_req_info: dict(Instantiation request information format)
:param param_dict: dict('nfv', Initial HOT resource dict)
:return: dict(VDU name, VDU SW Image data dict)
"""
cp_data = {}
ext_vl_param = inst_req_info.ext_virtual_links
cp_param = param_dict.get('nfv', {}).get('CP')
for ext_vl in ext_vl_param:
for ext_cp in ext_vl.ext_cps:
if ext_cp.cpd_id in cp_param.keys():
cp_data[ext_cp.cpd_id] = ext_vl.resource_id
LOG.info('cp_data: %s', cp_data)
return cp_data