From 3443bf629945765b058253e714a90e72a04991f2 Mon Sep 17 00:00:00 2001 From: Koichi Edagawa Date: Mon, 11 Oct 2021 17:17:28 +0900 Subject: [PATCH] Allow VNF instantiation by VIM of the same tenant This patch adds a validation check to allow VNF instantiation when the target VNF and VIM belongs to the same tenants. Co-Author: Manpreet Kaur Implement: blueprint multi-tenant-policy Change-Id: Ia966280c6d565b0021b29f593cd064daec0492f4 --- tacker/common/exceptions.py | 5 + .../unit/conductor/test_conductor_server.py | 90 ++-------------- tacker/tests/unit/vnflcm/fakes.py | 24 +++++ .../unit/vnflcm/test_load_vnf_interfaces.py | 11 +- .../tests/unit/vnflcm/test_vnflcm_driver.py | 102 +++++++++++------- .../vnfm/infra_drivers/kubernetes/fakes.py | 1 + tacker/vnflcm/utils.py | 1 + tacker/vnflcm/vnflcm_driver.py | 8 ++ 8 files changed, 116 insertions(+), 126 deletions(-) diff --git a/tacker/common/exceptions.py b/tacker/common/exceptions.py index 19ad46f6e..d1c85ce43 100644 --- a/tacker/common/exceptions.py +++ b/tacker/common/exceptions.py @@ -431,3 +431,8 @@ class VnfConflictStateWithErrorPoint(Conflict): class InvalidIpAddr(TackerException): message = _('Invalid ip address value in resource %(id)s.') + + +class TenantMatchFailure(TackerException): + message = _('The target %(resource)s %(id)s cannot be %(action)s ' + 'from a VIM of a different tenant.') diff --git a/tacker/tests/unit/conductor/test_conductor_server.py b/tacker/tests/unit/conductor/test_conductor_server.py index 6bb59a4ab..547f880e8 100644 --- a/tacker/tests/unit/conductor/test_conductor_server.py +++ b/tacker/tests/unit/conductor/test_conductor_server.py @@ -1668,14 +1668,6 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase): vnfd_key = 'vnfd_' + vnf_instance.instantiated_vnf_info.flavour_id vnfd_yaml = vnf_dict['vnfd']['attributes'].get(vnfd_key, '') mock_vnfd_dict.return_value = yaml.safe_load(vnfd_yaml) - 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'}} vimAssets = {'computeResourceFlavours': [ {'vimConnectionId': uuidsentinel.vim_id, 'vnfdVirtualComputeDescId': 'CDU1', @@ -1695,7 +1687,7 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase): json_data = grant_dict mock_grants.return_value = MockResponse(json_data=json_data) - mock_vim.return_value = vim_obj + mock_vim.return_value = vnflcm_fakes.get_dummy_openstack_vim_obj() mock_d1.return_value = [] mock_placement.return_value = [] self.conductor.heal(self.context, vnf_instance, vnf_dict, @@ -1852,14 +1844,6 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase): vnf_lcm_op_occs_id = 'a9c36d21-21aa-4692-8922-7999bbcae08c' mock_exec.return_value = True mock_act.return_value = None - 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'}} vimAssets = {'computeResourceFlavours': [ {'vimConnectionId': uuidsentinel.vim_id, 'vnfdVirtualComputeDescId': 'CDU1', @@ -1904,7 +1888,7 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase): json_data = grant_dict mock_grants.return_value = MockResponse(json_data=json_data) - mock_vim.return_value = vim_obj + mock_vim.return_value = vnflcm_fakes.get_dummy_openstack_vim_obj() mock_d1.return_value = ['ST1'] res_str = '[{"id_type": "RES_MGMT", "resource_id": ' + \ '"2c6e5cc7-240d-4458-a683-1fe648351200", ' + \ @@ -1988,14 +1972,6 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase): vnf_lcm_op_occs_id = 'a9c36d21-21aa-4692-8922-7999bbcae08c' mock_exec.return_value = True mock_act.return_value = None - 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'}} vimAssets = {'computeResourceFlavours': [ {'vimConnectionId': uuidsentinel.vim_id, 'vnfdVirtualComputeDescId': 'CDU1', @@ -2020,7 +1996,7 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase): json_data = grant_dict mock_grants.return_value = MockResponse(json_data=json_data) - mock_vim.return_value = vim_obj + mock_vim.return_value = vnflcm_fakes.get_dummy_openstack_vim_obj() mock_d1.return_value = [] mock_placement.return_value = [] self.assertRaises(exceptions.ValidationError, @@ -2090,18 +2066,10 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase): vnf_lcm_op_occs_id = 'a9c36d21-21aa-4692-8922-7999bbcae08c' mock_exec.return_value = True mock_act.return_value = None - 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_grants.side_effect = \ requests.exceptions.HTTPError("MockException") - mock_vim.return_value = vim_obj + mock_vim.return_value = vnflcm_fakes.get_dummy_openstack_vim_obj() mock_d1.return_value = [] mock_placement.return_value = [] self.assertRaises(requests.exceptions.HTTPError, @@ -2282,16 +2250,8 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase): fields.VnfInstanceState.INSTANTIATED, scale_status="scale_status") scale_vnf_request = fakes.scale_request("SCALE_IN", 1) - 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 + mock_vim.return_value = vnflcm_fakes.get_dummy_openstack_vim_obj() mock_d1.return_value = ([], [], "", "") vnf_info['addResources'] = [] vnf_info['removeResources'] = [] @@ -2355,16 +2315,8 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase): fields.VnfInstanceState.INSTANTIATED, scale_status="scale_status") scale_vnf_request = fakes.scale_request("SCALE_IN", 1) - 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 + mock_vim.return_value = vnflcm_fakes.get_dummy_openstack_vim_obj() removeResources = [] resRemoveResource = [] resource = objects.ResourceDefinition( @@ -2485,16 +2437,8 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase): fields.VnfInstanceState.INSTANTIATED, scale_status="scale_status") scale_vnf_request = fakes.scale_request("SCALE_OUT", 1) - 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 + mock_vim.return_value = vnflcm_fakes.get_dummy_openstack_vim_obj() addResources = [] resAddResource = [] resource = objects.ResourceDefinition( @@ -2634,16 +2578,8 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase): fields.VnfInstanceState.INSTANTIATED, scale_status="scale_status") scale_vnf_request = fakes.scale_request("SCALE_OUT", 1) - 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 + mock_vim.return_value = vnflcm_fakes.get_dummy_openstack_vim_obj() addResources = [] resource = objects.ResourceDefinition( id='2c6e5cc7-240d-4458-a683-1fe648351280', @@ -2736,16 +2672,8 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase): fields.VnfInstanceState.INSTANTIATED, scale_status="scale_status") scale_vnf_request = fakes.scale_request("SCALE_OUT", 1) - 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 + mock_vim.return_value = vnflcm_fakes.get_dummy_openstack_vim_obj() addResources = [] resource = objects.ResourceDefinition( id='2c6e5cc7-240d-4458-a683-1fe648351280', diff --git a/tacker/tests/unit/vnflcm/fakes.py b/tacker/tests/unit/vnflcm/fakes.py index 3178aa8d4..a6bafd94d 100644 --- a/tacker/tests/unit/vnflcm/fakes.py +++ b/tacker/tests/unit/vnflcm/fakes.py @@ -739,6 +739,30 @@ def get_dummy_vim_connection_info(): 'vim_id': 'fake_vim_id', 'vim_type': 'openstack', 'extra': {}} +def get_dummy_openstack_vim_obj(): + return {'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'}, + 'tenant': uuidsentinel.tenant_id} + + +def get_dummy_k8s_vim_obj(): + return {'vim_id': uuidsentinel.vim_id, + 'vim_name': 'fake_vim', + 'vim_type': 'kubernetes', + 'vim_auth': { + 'auth_url': 'http://localhost/8443', + 'password': 'test_pw', + 'username': 'test_user', + 'project_name': 'test_project'}, + 'tenant': uuidsentinel.tenant_id} + + def get_dummy_instantiate_vnf_request(**updates): instantiate_vnf_request = { 'additional_params': None, 'created_at': '', 'deleted': '', diff --git a/tacker/tests/unit/vnflcm/test_load_vnf_interfaces.py b/tacker/tests/unit/vnflcm/test_load_vnf_interfaces.py index 47c177d96..337d2e0fd 100644 --- a/tacker/tests/unit/vnflcm/test_load_vnf_interfaces.py +++ b/tacker/tests/unit/vnflcm/test_load_vnf_interfaces.py @@ -130,10 +130,13 @@ class MgmtVnfLcmDriverTest(db_base.SqlTestCase): def _stub_get_vim(self): vim_obj = {'vim_id': '6261579e-d6f3-49ad-8bc3-a9cb974778ff', - 'vim_name': 'fake_vim', 'vim_auth': - {'auth_url': 'http://localhost/identity', 'password': - 'test_pw', 'username': 'test_user', 'project_name': - 'test_project'}, 'vim_type': 'openstack'} + 'vim_name': 'fake_vim', + 'vim_auth': + {'auth_url': 'http://localhost/identity', + 'password': 'test_pw', 'username': 'test_user', + 'project_name': 'test_project'}, + 'vim_type': 'openstack', + 'tenant': uuidsentinel.tenant_id} self.vim_client.get_vim.return_value = vim_obj @mock.patch('tacker.vnflcm.utils.get_default_scale_status') diff --git a/tacker/tests/unit/vnflcm/test_vnflcm_driver.py b/tacker/tests/unit/vnflcm/test_vnflcm_driver.py index d019465f1..2fe9884ad 100644 --- a/tacker/tests/unit/vnflcm/test_vnflcm_driver.py +++ b/tacker/tests/unit/vnflcm/test_vnflcm_driver.py @@ -184,10 +184,12 @@ class TestVnflcmDriver(db_base.SqlTestCase): def _stub_get_vim(self): vim_obj = {'vim_id': '6261579e-d6f3-49ad-8bc3-a9cb974778ff', - 'vim_name': 'fake_vim', 'vim_auth': - {'auth_url': 'http://localhost/identity', 'password': - 'test_pw', 'username': 'test_user', 'project_name': - 'test_project'}, 'vim_type': 'openstack'} + 'vim_name': 'fake_vim', + 'vim_auth': {'auth_url': 'http://localhost/identity', + 'password': 'test_pw', 'username': 'test_user', + 'project_name': 'test_project'}, + 'vim_type': 'openstack', + 'tenant': uuidsentinel.tenant_id} self.vim_client.get_vim.return_value = vim_obj @mock.patch('tacker.vnflcm.utils.get_default_scale_status') @@ -238,6 +240,57 @@ class TestVnflcmDriver(db_base.SqlTestCase): self.assertEqual(6, self._vnf_manager.invoke.call_count) shutil.rmtree(fake_csar) + @mock.patch('tacker.vnflcm.utils.get_default_scale_status') + @mock.patch('tacker.vnflcm.utils._make_final_vnf_dict') + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') + @mock.patch.object(TackerManager, 'get_service_plugins', + return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(objects.VnfResource, 'create') + @mock.patch.object(objects.VnfPackageVnfd, 'get_by_id') + @mock.patch.object(objects.VnfInstance, "save") + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') + def test_instantiate_vnf_when_vim_and_vnf_belong_to_different_tenant( + self, mock_vnf_interfaces, mock_vnfd_dict, + mock_vnf_instance_save, mock_vnf_package_vnfd, mock_create, + mock_get_service_plugins, mock_init_hash, mock_final_vnf_dict, + mock_default_status): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() + 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 + instantiate_vnf_req_dict = fakes.get_dummy_instantiate_vnf_request() + instantiate_vnf_req_obj = \ + objects.InstantiateVnfRequest.obj_from_primitive( + instantiate_vnf_req_dict, self.context) + vnf_instance_obj = fakes.return_vnf_instance() + vnf_instance_obj.tenant_id = uuidsentinel.uuid + mock_default_status.return_value = None + + 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') + test_utils.copy_csar_files(fake_csar, "vnflcm4") + self._mock_vnf_manager() + driver = vnflcm_driver.VnfLcmDriver() + vnf_dict = { + "vnfd": {"attributes": {}}, "attributes": {}, + "before_error_point": EP.INITIAL} + error = self.assertRaises(exceptions.TenantMatchFailure, + driver.instantiate_vnf, self.context, vnf_instance_obj, + vnf_dict, instantiate_vnf_req_obj) + expected_error = ("The target VNF %s cannot be instantiate " + "from a VIM of a different tenant.") + + self.assertEqual(expected_error % vnf_instance_obj.id, str(error)) + shutil.rmtree(fake_csar) + @mock.patch('tacker.vnflcm.utils.get_default_scale_status') @mock.patch('tacker.vnflcm.utils._make_final_vnf_dict') @mock.patch.object(VnfLcmDriver, @@ -1228,16 +1281,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): # 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 + mock_vim.return_value = fakes.get_dummy_openstack_vim_obj() vnf_instance = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED) @@ -1915,15 +1959,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): None] mock_vnf_package_vnfd.return_value = fakes.return_vnf_package_vnfd() driver = vnflcm_driver.VnfLcmDriver() - vim_obj = {'vim_id': uuidsentinel.vim_id, - 'vim_name': 'fake_vim', - 'vim_type': 'kubernetes', - 'vim_auth': { - 'auth_url': 'http://localhost:8443', - 'password': 'test_pw', - 'username': 'test_user', - 'project_name': 'test_project'}} - self.vim_client.get_vim.return_value = vim_obj + self.vim_client.get_vim.return_value = fakes.get_dummy_k8s_vim_obj() driver.scale_vnf(self.context, vnf_info, vnf_instance, scale_vnf_request) @@ -1959,15 +1995,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): mock_vnfd_dict.return_value = fakes.vnfd_dict_cnf() mock_yaml_safe_load.return_value = fakes.vnfd_dict_cnf() driver = vnflcm_driver.VnfLcmDriver() - vim_obj = {'vim_id': uuidsentinel.vim_id, - 'vim_name': 'fake_vim', - 'vim_type': 'kubernetes', - 'vim_auth': { - 'auth_url': 'http://localhost:8443', - 'password': 'test_pw', - 'username': 'test_user', - 'project_name': 'test_project'}} - self.vim_client.get_vim.return_value = vim_obj + self.vim_client.get_vim.return_value = fakes.get_dummy_k8s_vim_obj() driver.scale_vnf(self.context, vnf_info, vnf_instance, scale_vnf_request) @@ -3013,15 +3041,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): mock_vnfd_dict.return_value = fakes.vnfd_dict_cnf() operation_params = jsonutils.loads(vnf_lcm_op_occs.operation_params) mock_yaml_safe_load.return_value = fakes.vnfd_dict_cnf() - vim_obj = {'vim_id': uuidsentinel.vim_id, - 'vim_name': 'fake_vim', - 'vim_type': 'kubernetes', - 'vim_auth': { - 'auth_url': 'http://localhost:8443', - 'password': 'test_pw', - 'username': 'test_user', - 'project_name': 'test_project'}} - self.vim_client.get_vim.return_value = vim_obj + self.vim_client.get_vim.return_value = fakes.get_dummy_k8s_vim_obj() self._mock_vnf_manager() driver = vnflcm_driver.VnfLcmDriver() diff --git a/tacker/tests/unit/vnfm/infra_drivers/kubernetes/fakes.py b/tacker/tests/unit/vnfm/infra_drivers/kubernetes/fakes.py index da1c38f61..e92b994b6 100644 --- a/tacker/tests/unit/vnfm/infra_drivers/kubernetes/fakes.py +++ b/tacker/tests/unit/vnfm/infra_drivers/kubernetes/fakes.py @@ -1255,5 +1255,6 @@ def fake_k8s_vim_obj(): 'username': 'test_user', 'project_name': 'test_project'}, 'vim_type': 'kubernetes', + 'tenant': uuidsentinel.tenant_id, 'extra': {}} return vim_obj diff --git a/tacker/vnflcm/utils.py b/tacker/vnflcm/utils.py index 019442bde..65ee9de73 100644 --- a/tacker/vnflcm/utils.py +++ b/tacker/vnflcm/utils.py @@ -64,6 +64,7 @@ def _get_vim(context, vim_connection_info): vim_info = {'id': vim_res['vim_id'], 'vim_id': vim_res['vim_id'], 'vim_type': vim_res['vim_type'], 'access_info': vim_res['vim_auth'], + 'tenant_id': vim_res['tenant'], 'extra': vim_res.get('extra', {})} return vim_info diff --git a/tacker/vnflcm/vnflcm_driver.py b/tacker/vnflcm/vnflcm_driver.py index f1bc382aa..97f1895b2 100644 --- a/tacker/vnflcm/vnflcm_driver.py +++ b/tacker/vnflcm/vnflcm_driver.py @@ -514,6 +514,14 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver): vim_info = vnflcm_utils._get_vim(context, instantiate_vnf_req.vim_connection_info) + if vim_info['tenant_id'] != vnf_instance.tenant_id: + LOG.error('The target VNF %(id)s cannot be instantiate ' + 'from a VIM of a different tenant.', + {"id": vnf_instance.id}) + raise exceptions.TenantMatchFailure(resource='VNF', + id=vnf_instance.id, + action='instantiate') + vim_connection_info = objects.VimConnectionInfo.obj_from_primitive( vim_info, context)