diff --git a/releasenotes/notes/bp-placement-constraints-e3256cfc2d1b2b9f.yaml b/releasenotes/notes/bp-placement-constraints-e3256cfc2d1b2b9f.yaml new file mode 100644 index 000000000..fc5e94206 --- /dev/null +++ b/releasenotes/notes/bp-placement-constraints-e3256cfc2d1b2b9f.yaml @@ -0,0 +1,5 @@ +--- +features: + - Add placement constraints support so that the VNFM may interoperate with 3rd + party NFVO, it enables to deploy each VM in the VNF after setting the zone + information based on the grant response from NFVO. diff --git a/tacker/conductor/conductor_server.py b/tacker/conductor/conductor_server.py index a84e75fd1..6d3e2f577 100644 --- a/tacker/conductor/conductor_server.py +++ b/tacker/conductor/conductor_server.py @@ -1084,12 +1084,15 @@ class Conductor(manager.Manager): placement_obj_list = [] topo_temp = vnfd_dict.get('topology_template', {}) for policy in topo_temp.get('policies', []): + affinity_type = { + 'tosca.policies.nfv.AntiAffinityRule': 'ANTI_AFFINITY', + 'tosca.policies.nfv.AffinityRule': 'AFFINITY'} for policy_name, policy_dict in policy.items(): - key_type = 'tosca.policies.nfv.AntiAffinityRule' - if policy_dict['type'] == key_type: + if policy_dict['type'] in affinity_type.keys(): placement_constraint = objects.PlacementConstraint() - placement_constraint.affinity_or_anti_affinity = \ - 'ANTI_AFFINITY' + key = policy_dict['type'] + placement_constraint.affinity_or_anti_affinity = ( + affinity_type[key]) placement_constraint.scope = 'ZONE' placement_constraint.resource = [] placement_constraint.fallback_best_effort = True diff --git a/tacker/tests/etc/samples/etsi/nfv/functional6/BaseHOT/simple/helloworld3.yaml b/tacker/tests/etc/samples/etsi/nfv/functional6/BaseHOT/simple/helloworld3.yaml index 6e5fb4992..f21a9b78a 100644 --- a/tacker/tests/etc/samples/etsi/nfv/functional6/BaseHOT/simple/helloworld3.yaml +++ b/tacker/tests/etc/samples/etsi/nfv/functional6/BaseHOT/simple/helloworld3.yaml @@ -23,6 +23,7 @@ resources: net3: { get_resource: extmanageNW_1 } net4: { get_resource: extmanageNW_2 } net5: { get_resource: internalNW_1 } + subnet: { get_param: [nfv, CP, VDU1_CP2, fixed_ips, 0, subnet]} VDU1_scale_out: type: OS::Heat::ScalingPolicy properties: @@ -41,20 +42,21 @@ resources: type: OS::Heat::AutoScalingGroup depends_on: VDU1 properties: - min_size: 2 - max_size: 2 - desired_capacity: 2 + min_size: 1 + max_size: 1 + desired_capacity: 1 resource: type: VDU2.yaml properties: flavor: { get_param: [ nfv, VDU, VDU2, flavor ] } image: { get_param: [ nfv, VDU, VDU2, image ] } - zone: { get_param: [ nfv, vdu, VDU2, zone ] } net1: { get_param: [ nfv, CP, VDU2_CP1, network ] } net2: { get_param: [ nfv, CP, VDU2_CP2, network ] } net3: { get_resource: extmanageNW_1 } net4: { get_resource: extmanageNW_2 } net5: { get_resource: internalNW_1 } + ip1: { get_param: [nfv, CP, VDU2_CP2, fixed_ips, 0, ip_address]} + subnet: { get_param: [nfv, CP, VDU2_CP2, fixed_ips, 0, subnet]} VDU2_scale_out: type: OS::Heat::ScalingPolicy properties: diff --git a/tacker/tests/etc/samples/etsi/nfv/functional6/BaseHOT/simple/nested/VDU1.yaml b/tacker/tests/etc/samples/etsi/nfv/functional6/BaseHOT/simple/nested/VDU1.yaml index b10f7375d..dbedc9fa4 100644 --- a/tacker/tests/etc/samples/etsi/nfv/functional6/BaseHOT/simple/nested/VDU1.yaml +++ b/tacker/tests/etc/samples/etsi/nfv/functional6/BaseHOT/simple/nested/VDU1.yaml @@ -18,6 +18,8 @@ parameters: type: string net5: type: string + subnet: + type: string resources: VDU1: @@ -47,6 +49,8 @@ resources: type: OS::Neutron::Port properties: network: { get_param: net2 } + fixed_ips: + - subnet: { get_param: subnet} VDU1_CP3: type: OS::Neutron::Port properties: diff --git a/tacker/tests/etc/samples/etsi/nfv/functional6/BaseHOT/simple/nested/VDU2.yaml b/tacker/tests/etc/samples/etsi/nfv/functional6/BaseHOT/simple/nested/VDU2.yaml index c4772dad6..c79d6737b 100644 --- a/tacker/tests/etc/samples/etsi/nfv/functional6/BaseHOT/simple/nested/VDU2.yaml +++ b/tacker/tests/etc/samples/etsi/nfv/functional6/BaseHOT/simple/nested/VDU2.yaml @@ -6,8 +6,6 @@ parameters: type: string image: type: string - zone: - type: string net1: type: string net2: @@ -18,6 +16,10 @@ parameters: type: string net5: type: string + ip1: + type: string + subnet: + type: string resources: VDU2: @@ -37,7 +39,6 @@ resources: get_resource: VDU2_CP4 - port: get_resource: VDU2_CP5 - availability_zone: { get_param: zone } VDU2_CP1: type: OS::Neutron::Port @@ -47,6 +48,9 @@ resources: type: OS::Neutron::Port properties: network: { get_param: net2 } + fixed_ips: + - ip_address: { get_param: ip1} + subnet: { get_param: subnet} VDU2_CP3: type: OS::Neutron::Port properties: diff --git a/tacker/tests/etc/samples/etsi/nfv/functional6/Definitions/helloworld3_df_simple.yaml b/tacker/tests/etc/samples/etsi/nfv/functional6/Definitions/helloworld3_df_simple.yaml index a53204c42..1ed638e22 100644 --- a/tacker/tests/etc/samples/etsi/nfv/functional6/Definitions/helloworld3_df_simple.yaml +++ b/tacker/tests/etc/samples/etsi/nfv/functional6/Definitions/helloworld3_df_simple.yaml @@ -96,8 +96,8 @@ topology_template: name: VDU2 description: VDU2 compute node vdu_profile: - min_number_of_instances: 2 - max_number_of_instances: 2 + min_number_of_instances: 1 + max_number_of_instances: 1 sw_image_data: name: cirros-0.4.0-x86_64-disk version: '0.4.0' @@ -291,7 +291,7 @@ topology_template: type: tosca.policies.nfv.VduInitialDelta properties: initial_delta: - number_of_instances: 2 + number_of_instances: 1 targets: [ VDU2 ] - VDU1_scaling_aspect_deltas: @@ -334,9 +334,9 @@ topology_template: properties: levels: instantiation_level_1: - number_of_instances: 2 + number_of_instances: 1 instantiation_level_2: - number_of_instances: 2 + number_of_instances: 1 targets: [ VDU2 ] - internalVL1_instantiation_levels: diff --git a/tacker/tests/functional/sol/vnflcm/base.py b/tacker/tests/functional/sol/vnflcm/base.py index 78dbc4260..d1ab0b6fb 100644 --- a/tacker/tests/functional/sol/vnflcm/base.py +++ b/tacker/tests/functional/sol/vnflcm/base.py @@ -626,6 +626,14 @@ class BaseVnfLcmTest(base.BaseTackerTest): return resource + def _get_heat_stack_template(self, stack_id, nested_depth=0): + try: + template = self.h_client.stacks.template(stack_id) + except Exception: + return None + + return template + def _get_image_id_from_resource_attributes(self, stack_resource_details): if stack_resource_details is None: return None diff --git a/tacker/tests/functional/sol_separated_nfvo/vnflcm/test_vnf_instance_with_user_data_nfvo_separate.py b/tacker/tests/functional/sol_separated_nfvo/vnflcm/test_vnf_instance_with_user_data_nfvo_separate.py index b639a7fc8..3fc1a896e 100644 --- a/tacker/tests/functional/sol_separated_nfvo/vnflcm/test_vnf_instance_with_user_data_nfvo_separate.py +++ b/tacker/tests/functional/sol_separated_nfvo/vnflcm/test_vnf_instance_with_user_data_nfvo_separate.py @@ -108,8 +108,7 @@ class VnfLcmWithNfvoSeparator(vnflcm_base.BaseVnfLcmTest): - Delete subscription. - Show subscription. """ - vnf_package_info = self._register_vnf_package_mock_response( - package_dir="functional5") + vnf_package_info = self._register_vnf_package_mock_response() glance_image = self._list_glance_image()[0] # Create subscription and register it. @@ -143,9 +142,10 @@ class VnfLcmWithNfvoSeparator(vnflcm_base.BaseVnfLcmTest): self.vim['tenant_id'], glance_image.id)) # Instantiate vnf instance - request_body = fake_vnflcm.VnfInstances.make_inst_request_body( - self.vim['tenant_id'], self.ext_networks, self.ext_mngd_networks, - self.ext_link_ports, self.ext_subnets) + request_body = fake_vnflcm.VnfInstances.\ + make_inst_request_body_include_num_dynamic( + self.vim['tenant_id'], self.ext_networks, + self.ext_mngd_networks, self.ext_link_ports, self.ext_subnets) resp, _ = self._instantiate_vnf_instance(vnf_instance_id, request_body) self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id) self._assert_instantiate_vnf(resp, vnf_instance_id) @@ -284,12 +284,14 @@ class VnfLcmWithNfvoSeparator(vnflcm_base.BaseVnfLcmTest): self.vim['tenant_id'], glance_image.id)) # Instantiate vnf instance - request_body = fake_vnflcm.VnfInstances.make_inst_request_body( - self.vim['tenant_id'], self.ext_networks, self.ext_mngd_networks, - self.ext_link_ports, self.ext_subnets) + request_body = fake_vnflcm.VnfInstances.\ + make_inst_request_body_include_num_dynamic( + self.vim['tenant_id'], self.ext_networks, + self.ext_mngd_networks, self.ext_link_ports, self.ext_subnets) resp, _ = self._instantiate_vnf_instance(vnf_instance_id, request_body) self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id) self._assert_instantiate_vnf(resp, vnf_instance_id) + self._assert_stack_template(vnf_instance_id) # Show vnf instance resp, vnf_instance = self._show_vnf_instance(vnf_instance_id) @@ -303,15 +305,19 @@ class VnfLcmWithNfvoSeparator(vnflcm_base.BaseVnfLcmTest): self.vim['tenant_id'], glance_image.id)) # Heal vnf (exists vnfc_instace_id) - vnfc_instance_id_list = [ - vnfc.get('id') for vnfc in vnf_instance.get( - 'instantiatedVnfInfo', {}).get( - 'vnfcResourceInfo', [])] + vnfc_instance_id_list = [] + + for vnfc in vnf_instance.get('instantiatedVnfInfo', {}).\ + get('vnfcResourceInfo', []): + if vnfc.get('vduId') == 'VDU1': + vnfc_instance_id_list.append(vnfc.get('id')) + request_body = fake_vnflcm.VnfInstances.make_heal_request_body( vnfc_instance_id_list) resp, _ = self._heal_vnf_instance(vnf_instance_id, request_body) self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id) self._assert_heal_vnf(resp, vnf_instance_id) + self._assert_stack_template(vnf_instance_id) # Set Fake server response for Grant-Req(Terminate) vnflcm_base.FAKE_SERVER_MANAGER.set_callback('POST', @@ -522,3 +528,14 @@ class VnfLcmWithNfvoSeparator(vnflcm_base.BaseVnfLcmTest): ans_list.append(detail.attributes['fixed_ips']) return ans_list + + def _assert_stack_template(self, vnf_instance_id): + stack = self._get_heat_stack(vnf_instance_id) + resources_list\ + = self._get_heat_resource_list(stack.id, nested_depth=2) + stack_name_wd = vnf_instance_id + "-VDU2" + physical_resource_id = [r.physical_resource_id for r + in resources_list if stack_name_wd in r.stack_name] + template = self._get_heat_stack_template(physical_resource_id[0]) + template_count = str(template).count("zone") + self.assertEqual(template_count, 3) diff --git a/tacker/tests/unit/vnfm/infra_drivers/openstack/test_openstack_driver.py b/tacker/tests/unit/vnfm/infra_drivers/openstack/test_openstack_driver.py index 6d710784d..3c90373bb 100644 --- a/tacker/tests/unit/vnfm/infra_drivers/openstack/test_openstack_driver.py +++ b/tacker/tests/unit/vnfm/infra_drivers/openstack/test_openstack_driver.py @@ -264,10 +264,18 @@ class TestOpenStack(base.FixturedTestCase): vnf_resource = type('', (), {}) vnf_resource.resource_identifier = constants.INVALID_UUID grant_info_test = {'vdu_name': {vnf_resource}} - nested_hot_dict = {'parameters': {'vnf': 'test'}} + nested_hot_dict = { + 'VDU1.yaml': {'parameters': {'vnf': 'test', + 'zone': {'type': 'string'}}, + 'resources': {'VDU1': {'properties': + {'availability_zone': {'get_param': 'zone'}}}}}} + base_hot_dict = self._read_file() + base_hot_dict['resources']['VDU1']['properties'].setdefault( + 'resource', {'properties': {'zone': + {'get_param': ['nfv', 'vdu', 'VDU1', 'zone']}}}) mock_get_base_hot_dict.return_value = \ - self._read_file(), nested_hot_dict - vimAssets = {'compute_resource_flavours': [ + base_hot_dict, nested_hot_dict + vim_assets = {'compute_resource_flavours': [ {'vim_connection_id': uuidsentinel.vim_id, 'vnfd_virtual_compute_desc_id': 'VDU1', 'vim_flavour_id': 'm1.tiny'}], @@ -275,27 +283,27 @@ class TestOpenStack(base.FixturedTestCase): {'vim_connection_id': uuidsentinel.vim_id, 'vnfd_software_image_id': 'VDU1', 'vim_software_image_id': 'cirros'}]} - resAddResource = [] + res_add_resource = [] resource = { 'resource_definition_id': '2c6e5cc7-240d-4458-a683-1fe648351280', 'vim_connection_id': uuidsentinel.vim_id, 'zone_id': '5e4da3c3-4a55-412a-b624-843921f8b51d'} - resAddResource.append(resource) + res_add_resource.append(resource) resource = { 'resource_definition_id': 'faf14707-da7c-4eec-be99-8099fa1e9fa9', 'vim_connection_id': uuidsentinel.vim_id, 'zone_id': '5e4da3c3-4a55-412a-b624-843921f8b51d'} - resAddResource.append(resource) + res_add_resource.append(resource) resource = { 'resource_definition_id': 'faf14707-da7c-4eec-be99-8099fa1e9fa0', 'vim_connection_id': uuidsentinel.vim_id, 'zone_id': '5e4da3c3-4a55-412a-b624-843921f8b51d'} - resAddResource.append(resource) + res_add_resource.append(resource) resource = { 'resource_definition_id': 'faf14707-da7c-4eec-be99-8099fa1e9fa1', 'vim_connection_id': uuidsentinel.vim_id, 'zone_id': '5e4da3c3-4a55-412a-b624-843921f8b51d'} - resAddResource.append(resource) + res_add_resource.append(resource) zone = { 'id': '5e4da3c3-4a55-412a-b624-843921f8b51d', 'zone_id': 'nova', @@ -313,8 +321,224 @@ class TestOpenStack(base.FixturedTestCase): grant_dict['vnf_instance_id'] = uuidsentinel.vnf_instance_id grant_dict['vnf_lcm_op_occ_id'] = uuidsentinel.vnf_lcm_op_occ_id grant_dict['add_resources'] = [] - grant_dict['add_resources'].extend(resAddResource) - grant_dict['vim_assets'] = vimAssets + grant_dict['add_resources'].extend(res_add_resource) + grant_dict['vim_assets'] = vim_assets + grant_dict['zones'] = [zone] + grant_dict['vim_connections'] = [vim_obj] + grant_obj = objects.Grant.obj_from_primitive( + grant_dict, context=self.context) + vnf['grant'] = grant_obj + vnf_instance = fd_utils.get_vnf_instance_object() + vnf_instance.instantiated_vnf_info.reinitialize() + vnfc_obj = objects.VnfcResourceInfo() + vnfc_obj.id = '2c6e5cc7-240d-4458-a683-1fe648351280' + vnfc_obj.vdu_id = 'VDU1' + vnfc_obj.storage_resource_ids = \ + ['faf14707-da7c-4eec-be99-8099fa1e9fa0'] + compute_resource = objects.ResourceHandle( + vim_connection_id=uuidsentinel.vim_id, + resource_id='6e1c286d-c023-4b34-8369-831c6e84cce2') + vnfc_obj.compute_resource = compute_resource + vnf_instance.instantiated_vnf_info.vnfc_resource_info = [vnfc_obj] + 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, + grant_info=grant_info_test, + vnf_instance=vnf_instance) + + @mock.patch('tacker.vnfm.vim_client.VimClient.get_vim') + @mock.patch('tacker.vnfm.infra_drivers.openstack.openstack' + '.OpenStack._format_base_hot') + @mock.patch('tacker.vnflcm.utils.get_base_nest_hot_dict') + @mock.patch('tacker.common.clients.OpenstackClients') + def test_create_grant_zone_add(self, mock_OpenstackClients_heat, + mock_get_base_hot_dict, + mock_format_base_hot, + mock_get_vim): + mock_get_vim.return_value = { + 'vim_id': uuidsentinel.vnfd_id, + 'vim_type': 'test', + 'vim_auth': {'username': 'test', 'password': 'test'}, + 'placement_attr': {'region': 'TestRegionOne'}, + 'tenant': 'test' + } + vnf = utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid, + flavour='simple') + vnf['placement_attr'] = {'region_name': 'dummy_region'} + vnf_package_path_test = os.path.abspath( + os.path.join(os.path.dirname(__file__), + "../../../../etc/samples/etsi/nfv", + "user_data_sample_normal")) + 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.ext_virtual_links = None + inst_req_info_test.flavour_id = 'simple' + vnf_resource = type('', (), {}) + vnf_resource.resource_identifier = constants.INVALID_UUID + grant_info_test = {'vdu_name': {vnf_resource}} + nested_hot_dict = { + 'VDU1.yaml': {'parameters': {'vnf': 'test'}, + 'resources': {'VDU1': {'properties': {}}}}} + mock_get_base_hot_dict.return_value = \ + self._read_file(), nested_hot_dict + vim_assets = {'compute_resource_flavours': [ + {'vim_connection_id': uuidsentinel.vim_id, + 'vnfd_virtual_compute_desc_id': 'VDU1', + 'vim_flavour_id': 'm1.tiny'}], + 'softwareImages': [ + {'vim_connection_id': uuidsentinel.vim_id, + 'vnfd_software_image_id': 'VDU1', + 'vim_software_image_id': 'cirros'}]} + res_add_resource = [] + resource = { + 'resource_definition_id': '2c6e5cc7-240d-4458-a683-1fe648351280', + 'vim_connection_id': uuidsentinel.vim_id, + 'zone_id': '5e4da3c3-4a55-412a-b624-843921f8b51d'} + res_add_resource.append(resource) + resource = { + 'resource_definition_id': 'faf14707-da7c-4eec-be99-8099fa1e9fa9', + 'vim_connection_id': uuidsentinel.vim_id, + 'zone_id': '5e4da3c3-4a55-412a-b624-843921f8b51d'} + res_add_resource.append(resource) + resource = { + 'resource_definition_id': 'faf14707-da7c-4eec-be99-8099fa1e9fa0', + 'vim_connection_id': uuidsentinel.vim_id, + 'zone_id': '5e4da3c3-4a55-412a-b624-843921f8b51d'} + res_add_resource.append(resource) + resource = { + 'resource_definition_id': 'faf14707-da7c-4eec-be99-8099fa1e9fa1', + 'vim_connection_id': uuidsentinel.vim_id, + 'zone_id': '5e4da3c3-4a55-412a-b624-843921f8b51d'} + res_add_resource.append(resource) + zone = { + 'id': '5e4da3c3-4a55-412a-b624-843921f8b51d', + 'zone_id': 'nova', + 'vim_connection_id': uuidsentinel.vim_id} + vim_obj = {'id': '0b9c66bb-9e1f-4bb2-92c3-913074e52e2b', + 'vim_id': uuidsentinel.vim_id, + 'vim_type': 'openstack', + 'access_info': { + 'password': 'test_pw', + 'username': 'test_user', + 'region': 'test_region', + 'tenant': uuidsentinel.tenant}} + grant_dict = {} + grant_dict['id'] = 'c213e465-8220-487e-9464-f79104e81e96' + grant_dict['vnf_instance_id'] = uuidsentinel.vnf_instance_id + grant_dict['vnf_lcm_op_occ_id'] = uuidsentinel.vnf_lcm_op_occ_id + grant_dict['add_resources'] = [] + grant_dict['add_resources'].extend(res_add_resource) + grant_dict['vim_assets'] = vim_assets + grant_dict['zones'] = [zone] + grant_dict['vim_connections'] = [vim_obj] + grant_obj = objects.Grant.obj_from_primitive( + grant_dict, context=self.context) + vnf['grant'] = grant_obj + vnf_instance = fd_utils.get_vnf_instance_object() + vnf_instance.instantiated_vnf_info.reinitialize() + vnfc_obj = objects.VnfcResourceInfo() + vnfc_obj.id = '2c6e5cc7-240d-4458-a683-1fe648351280' + vnfc_obj.vdu_id = 'VDU1' + vnfc_obj.storage_resource_ids = \ + ['faf14707-da7c-4eec-be99-8099fa1e9fa0'] + compute_resource = objects.ResourceHandle( + vim_connection_id=uuidsentinel.vim_id, + resource_id='6e1c286d-c023-4b34-8369-831c6e84cce2') + vnfc_obj.compute_resource = compute_resource + vnf_instance.instantiated_vnf_info.vnfc_resource_info = [vnfc_obj] + 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, + grant_info=grant_info_test, + vnf_instance=vnf_instance) + + @mock.patch('tacker.vnfm.vim_client.VimClient.get_vim') + @mock.patch('tacker.vnfm.infra_drivers.openstack.openstack' + '.OpenStack._format_base_hot') + @mock.patch('tacker.vnflcm.utils.get_base_nest_hot_dict') + @mock.patch('tacker.common.clients.OpenstackClients') + def test_create_grant_zone_id_none(self, mock_OpenstackClients_heat, + mock_get_base_hot_dict, + mock_format_base_hot, + mock_get_vim): + mock_get_vim.return_value = { + 'vim_id': uuidsentinel.vnfd_id, + 'vim_type': 'test', + 'vim_auth': {'username': 'test', 'password': 'test'}, + 'placement_attr': {'region': 'TestRegionOne'}, + 'tenant': 'test' + } + vnf = utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid, + flavour='simple') + vnf['placement_attr'] = {'region_name': 'dummy_region'} + vnf_package_path_test = os.path.abspath( + os.path.join(os.path.dirname(__file__), + "../../../../etc/samples/etsi/nfv", + "user_data_sample_normal")) + 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.ext_virtual_links = None + inst_req_info_test.flavour_id = 'simple' + vnf_resource = type('', (), {}) + vnf_resource.resource_identifier = constants.INVALID_UUID + grant_info_test = {'vdu_name': {vnf_resource}} + nested_hot_dict = { + 'VDU1.yaml': {'parameters': {'vnf': 'test'}, + 'resources': {'VDU1': {'properties': {}}}}} + mock_get_base_hot_dict.return_value = \ + self._read_file(), nested_hot_dict + vim_assets = {'compute_resource_flavours': [ + {'vim_connection_id': uuidsentinel.vim_id, + 'vnfd_virtual_compute_desc_id': 'VDU1', + 'vim_flavour_id': 'm1.tiny'}], + 'softwareImages': [ + {'vim_connection_id': uuidsentinel.vim_id, + 'vnfd_software_image_id': 'VDU1', + 'vim_software_image_id': 'cirros'}]} + res_add_resource = [] + resource = { + 'resource_definition_id': '2c6e5cc7-240d-4458-a683-1fe648351280', + 'vim_connection_id': uuidsentinel.vim_id, + 'zone_id': '5e4da3c3-4a55-412a-b624-843921f8b51d'} + res_add_resource.append(resource) + resource = { + 'resource_definition_id': 'faf14707-da7c-4eec-be99-8099fa1e9fa9', + 'vim_connection_id': uuidsentinel.vim_id, + 'zone_id': '5e4da3c3-4a55-412a-b624-843921f8b51d'} + res_add_resource.append(resource) + resource = { + 'resource_definition_id': 'faf14707-da7c-4eec-be99-8099fa1e9fa0', + 'vim_connection_id': uuidsentinel.vim_id, + 'zone_id': '5e4da3c3-4a55-412a-b624-843921f8b51d'} + res_add_resource.append(resource) + resource = { + 'resource_definition_id': 'faf14707-da7c-4eec-be99-8099fa1e9fa1', + 'vim_connection_id': uuidsentinel.vim_id, + 'zone_id': '5e4da3c3-4a55-412a-b624-843921f8b51d'} + res_add_resource.append(resource) + zone = { + 'id': '5e4da3c3-4a55-412a-b624-843921f8b51d', + 'zone_id': '', + 'vim_connection_id': uuidsentinel.vim_id} + vim_obj = {'id': '0b9c66bb-9e1f-4bb2-92c3-913074e52e2b', + 'vim_id': uuidsentinel.vim_id, + 'vim_type': 'openstack', + 'access_info': { + 'password': 'test_pw', + 'username': 'test_user', + 'region': 'test_region', + 'tenant': uuidsentinel.tenant}} + grant_dict = {} + grant_dict['id'] = 'c213e465-8220-487e-9464-f79104e81e96' + grant_dict['vnf_instance_id'] = uuidsentinel.vnf_instance_id + grant_dict['vnf_lcm_op_occ_id'] = uuidsentinel.vnf_lcm_op_occ_id + grant_dict['add_resources'] = [] + grant_dict['add_resources'].extend(res_add_resource) + grant_dict['vim_assets'] = vim_assets grant_dict['zones'] = [zone] grant_dict['vim_connections'] = [vim_obj] grant_obj = objects.Grant.obj_from_primitive( @@ -2085,6 +2309,199 @@ class TestOpenStack(base.FixturedTestCase): region_name=None ) + @mock.patch.object(hc.HeatClient, "update") + def test_scale_out_initial_zone_none(self, mock_update): + scale_vnf_request = objects.ScaleVnfRequest(type='SCALE_OUT', + aspect_id='SP1', + number_of_steps=1) + vnf_info = {} + add_resources = [] + resource = objects.ResourceDefinition( + id='2c6e5cc7-240d-4458-a683-1fe648351280', + type='COMPUTE', + vdu_id='VDU1', + resource_template_id='VDU1') + add_resources.append(resource) + resource = objects.ResourceDefinition( + id='faf14707-da7c-4eec-be99-8099fa1e9fa9', + type='LINKPORT', + vdu_id='VDU1', + resource_template_id='PORT1') + add_resources.append(resource) + resource = objects.ResourceDefinition( + id='faf14707-da7c-4eec-be99-8099fa1e9fa9', + type='STORAGE', + vdu_id='VDU1', + resource_template_id='ST1') + add_resources.append(resource) + vnf_info['addResources'] = add_resources + vnf_info['attributes'] = {} + vnf_info['attributes']['scale_group'] = '{\"scaleGroupDict\": ' + \ + '{ \"SP1\": { \"vdu\": [\"VDU1\"], \"num\": ' + \ + '1, \"maxLevel\": 3, \"initialNum\": 0, ' + \ + '\"initialLevel\": 0, \"default\": 0 }}}' + vnf_info['attributes']['heat_template'] = \ + utils.get_dummy_scale_initial_hot() + vnf_info['attributes']['SP1_res.yaml'] = \ + utils.get_dummy_scale_nest_initial_hot() + stack_param_dict = {} + stack_param_dict['nfv'] = {} + stack_param_dict['nfv']['VDU'] = {} + stack_param_dict['nfv']['VDU']['VDU1'] = {} + stack_param_dict['nfv']['VDU']['VDU1']['flavor'] = '' + stack_param_dict['nfv']['VDU']['VDU1']['image'] = '' + vnf_info['attributes'].update({'stack_param': str(stack_param_dict)}) + vnf_info['instance_id'] = uuidsentinel.stack_id + testjson = '{"id": "c213e465-8220-487e-9464-f79104e81e96", ' + \ + '"vnf_instance_id": ' + \ + '"47101fb6-bd18-4e04-b2b5-22370a023448", ' + \ + '"vnf_lcm_op_occ_id": ' + \ + '"f26f181d-7891-4720-b022-b074ec1733ef", ' + \ + '"zones": [{' + \ + '"id": ' + \ + '"5e4da3c3-4a55-412a-b624-843921f8b51d", ' + \ + '"zone_id": ' + \ + '"nova", ' + \ + '"vim_connection_id": ' + \ + '"b6eacd1b-5a9e-41ea-a33b-9d7196cd9187"}], ' + \ + '"add_resources": [{"resource_definition_id": ' + \ + '"2c6e5cc7-240d-4458-a683-1fe648351280", ' + \ + '"vim_connection_id": ' + \ + '"b6eacd1b-5a9e-41ea-a33b-9d7196cd9187", ' + \ + '"zone_id": "5e4da3c3-4a55-412a-b624-843921f8b51d"}' + \ + ', {"resource_definition_id": ' + \ + '"faf14707-da7c-4eec-be99-8099fa1e9fa9", ' + \ + '"vim_connection_id": ' + \ + '"b6eacd1b-5a9e-41ea-a33b-9d7196cd9187", ' + \ + '"zone_id": "5e4da3c3-4a55-412a-b624-843921f8b51d"}' + \ + ', {"resource_definition_id": ' + \ + '"faf14707-da7c-4eec-be99-8099fa1e9fa9", ' + \ + '"vim_connection_id": ' + \ + '"b6eacd1b-5a9e-41ea-a33b-9d7196cd9187", ' + \ + '"zone_id": ' + \ + '"5e4da3c3-4a55-412a-b624-843921f8b51d"}], ' + \ + '"vim_assets": {"compute_resource_flavours": ' + \ + '[{"vim_connection_id": ' + \ + '"b6eacd1b-5a9e-41ea-a33b-9d7196cd9187", ' + \ + '"vnfd_virtual_compute_desc_id": "VDU1", ' + \ + '"vim_flavour_id": "m1.tiny"}], "software_images": ' + \ + '[{"vim_connection_id": ' + \ + '"b6eacd1b-5a9e-41ea-a33b-9d7196cd9187", ' + \ + '"vnfd_software_image_id": "VDU1", ' + \ + '"vim_software_image_id": "cirros"}]}}' + res_body = jsonutils.loads(testjson) + res_dict = cutils.convert_camelcase_to_snakecase(res_body) + grant_obj = objects.Grant.obj_from_primitive( + res_dict, context=context) + vnf_info['grant'] = grant_obj + self.openstack.scale_out_initial(context=self.context, + plugin=self, + auth_attr=None, + vnf_info=vnf_info, + scale_vnf_request=scale_vnf_request, + region_name=None + ) + + @mock.patch.object(hc.HeatClient, "update") + def test_scale_out_initial_zone_id_none(self, mock_update): + scale_vnf_request = objects.ScaleVnfRequest(type='SCALE_OUT', + aspect_id='SP1', + number_of_steps=1) + vnf_info = {} + add_resources = [] + resource = objects.ResourceDefinition( + id='2c6e5cc7-240d-4458-a683-1fe648351280', + type='COMPUTE', + vdu_id='VDU1', + resource_template_id='VDU1') + add_resources.append(resource) + resource = objects.ResourceDefinition( + id='faf14707-da7c-4eec-be99-8099fa1e9fa9', + type='LINKPORT', + vdu_id='VDU1', + resource_template_id='PORT1') + add_resources.append(resource) + resource = objects.ResourceDefinition( + id='faf14707-da7c-4eec-be99-8099fa1e9fa9', + type='STORAGE', + vdu_id='VDU1', + resource_template_id='ST1') + add_resources.append(resource) + vnf_info['addResources'] = add_resources + vnf_info['attributes'] = {} + vnf_info['attributes']['scale_group'] = '{\"scaleGroupDict\": ' + \ + '{ \"SP1\": { \"vdu\": [\"VDU1\"], \"num\": ' + \ + '1, \"maxLevel\": 3, \"initialNum\": 0, ' + \ + '\"initialLevel\": 0, \"default\": 0 }}}' + vnf_info['attributes']['heat_template'] = \ + utils.get_dummy_scale_initial_hot() + vnf_info['attributes']['SP1_res.yaml'] = \ + utils.get_dummy_scale_nest_initial_hot() + stack_param_dict = {} + stack_param_dict['nfv'] = {} + stack_param_dict['nfv']['VDU'] = {} + stack_param_dict['nfv']['VDU']['VDU1'] = {} + stack_param_dict['nfv']['VDU']['VDU1']['zone'] = '' + stack_param_dict['nfv']['VDU']['VDU1']['flavor'] = '' + stack_param_dict['nfv']['VDU']['VDU1']['image'] = '' + vnf_info['attributes'].update({'stack_param': str(stack_param_dict)}) + vnf_info['instance_id'] = uuidsentinel.stack_id + testjson = '{"id": "c213e465-8220-487e-9464-f79104e81e96", ' + \ + '"vnf_instance_id": ' + \ + '"47101fb6-bd18-4e04-b2b5-22370a023448", ' + \ + '"vnf_lcm_op_occ_id": ' + \ + '"f26f181d-7891-4720-b022-b074ec1733ef", ' + \ + '"zones": [{' + \ + '"id": ' + \ + '"5e4da3c3-4a55-412a-b624-843921f8b51d", ' + \ + '"zone_id": ' + \ + '"", ' + \ + '"vim_connection_id": ' + \ + '"b6eacd1b-5a9e-41ea-a33b-9d7196cd9187"}], ' + \ + '"add_resources": [{"resource_definition_id": ' + \ + '"2c6e5cc7-240d-4458-a683-1fe648351280", ' + \ + '"id": ' + \ + '"2c6e5cc7-240d-4458-a683-1fe648351280", ' + \ + '"vim_connection_id": ' + \ + '"b6eacd1b-5a9e-41ea-a33b-9d7196cd9187", ' + \ + '"zone_id": "5e4da3c3-4a55-412a-b624-843921f8b51d"}' + \ + ', {"resource_definition_id": ' + \ + '"faf14707-da7c-4eec-be99-8099fa1e9fa9", ' + \ + '"id": ' + \ + '"faf14707-da7c-4eec-be99-8099fa1e9fa9", ' + \ + '"vim_connection_id": ' + \ + '"b6eacd1b-5a9e-41ea-a33b-9d7196cd9187", ' + \ + '"zone_id": "5e4da3c3-4a55-412a-b624-843921f8b51d"}' + \ + ', {"resource_definition_id": ' + \ + '"faf14707-da7c-4eec-be99-8099fa1e9fa9", ' + \ + '"id": ' + \ + '"faf14707-da7c-4eec-be99-8099fa1e9fa9", ' + \ + '"vim_connection_id": ' + \ + '"b6eacd1b-5a9e-41ea-a33b-9d7196cd9187", ' + \ + '"zone_id": ' + \ + '"5e4da3c3-4a55-412a-b624-843921f8b51d"}], ' + \ + '"vim_assets": {"compute_resource_flavours": ' + \ + '[{"vim_connection_id": ' + \ + '"b6eacd1b-5a9e-41ea-a33b-9d7196cd9187", ' + \ + '"vnfd_virtual_compute_desc_id": "VDU1", ' + \ + '"vim_flavour_id": "m1.tiny"}], "software_images": ' + \ + '[{"vim_connection_id": ' + \ + '"b6eacd1b-5a9e-41ea-a33b-9d7196cd9187", ' + \ + '"vnfd_software_image_id": "VDU1", ' + \ + '"vim_software_image_id": "cirros"}]}}' + res_body = jsonutils.loads(testjson) + res_dict = cutils.convert_camelcase_to_snakecase(res_body) + grant_obj = objects.Grant.obj_from_primitive( + res_dict, context=context) + vnf_info['grant'] = grant_obj + self.openstack.scale_out_initial(context=self.context, + plugin=self, + auth_attr=None, + vnf_info=vnf_info, + scale_vnf_request=scale_vnf_request, + region_name=None + ) + @mock.patch.object(hc.HeatClient, "resource_get") @mock.patch.object(hc.HeatClient, "resource_get_list") def test_get_rollback_ids(self, mock_list, mock_resource): diff --git a/tacker/vnfm/infra_drivers/openstack/openstack.py b/tacker/vnfm/infra_drivers/openstack/openstack.py index aaed19bd0..0261e812e 100644 --- a/tacker/vnfm/infra_drivers/openstack/openstack.py +++ b/tacker/vnfm/infra_drivers/openstack/openstack.py @@ -264,29 +264,9 @@ class OpenStack(abstract_driver.VnfAbstractDriver, scale_status_list.append(scale_status) vnf['scale_status'] = scale_status_list if vnf.get('grant'): - grant = vnf['grant'] - ins_inf = vnf_instance.instantiated_vnf_info.vnfc_resource_info - for addrsc in grant.add_resources: - for zone in grant.zones: - if zone.id == addrsc.zone_id: - vdu_name = None - for rsc in ins_inf: - if addrsc.resource_definition_id == rsc.id: - vdu_name = rsc.vdu_id - break - if not vdu_name: - continue - hot_param_dict['nfv']['VDU'][vdu_name]['zone'] = \ - zone.zone_id - if 'vim_assets' in grant and grant.vim_assets: - for flavour in grant.vim_assets.compute_resource_flavours: - vdu_name = flavour.vnfd_virtual_compute_desc_id - hot_param_dict['nfv']['VDU'][vdu_name]['flavor'] = \ - flavour.vim_flavour_id - for image in grant.vim_assets.software_images: - vdu_name = image.vnfd_software_image_id - hot_param_dict['nfv']['VDU'][vdu_name]['image'] = \ - image.vim_software_image_id + base_hot_dict, nested_hot_dict, hot_param_dict = \ + self._setup_hot_for_grant_resources(vnf, vnf_instance, + base_hot_dict, nested_hot_dict, hot_param_dict) # Add stack param to vnf_attributes vnf['attributes'].update({'stack_param': str(hot_param_dict)}) @@ -366,6 +346,79 @@ class OpenStack(abstract_driver.VnfAbstractDriver, return stack['stack']['id'] + @log.log + def _setup_hot_for_grant_resources(self, vnf, vnf_instance, + base_hot_dict, nested_hot_dict, hot_param_dict): + """Setup HOT related params for grant resources + + Update base_hot_dict, nested_hot_dict and hot_param_dict as HOT related + params for grant resources. + + :param vnf: + :param vnf_instance: + :param base_hot_dict: + :param nested_hot_dict: + :param hot_param_dict: + :returns: updated base_hot_dict, nested_hot_dict and hot_param_dict + """ + + # Rename for readability + grant = vnf['grant'] + bh = base_hot_dict + nh = nested_hot_dict + hparam = hot_param_dict + + ins_inf = vnf_instance.instantiated_vnf_info.vnfc_resource_info + for addrsc in grant.add_resources: + for zone in grant.zones: + if zone.id == addrsc.zone_id: + vdu_name = None + for rsc in ins_inf: + if addrsc.resource_definition_id == rsc.id: + vdu_name = rsc.vdu_id + break + if not vdu_name: + continue + + vdu_prop = bh['resources'][vdu_name]['properties'] + if not vdu_prop.get('resource'): + vdu_prop['resource'] = {'properties': {}} + + vdu_rsrc_prop = vdu_prop['resource']['properties'] + if not vdu_rsrc_prop.get('zone'): + vdu_rsrc_prop['zone'] = {'get_param': + ['nfv', 'vdu', vdu_name, 'zone']} + + if nh: + for yaml_name in nh: + if not (vdu_name in yaml_name): + continue + if not nh[yaml_name]['parameters'].get('zone'): + nh[yaml_name]['parameters']['zone'] = { + 'type': 'string'} + vdu_props = nh[yaml_name]['resources'][vdu_name][ + 'properties'] + if not (vdu_props.get('availability_zone')): + vdu_props['availability_zone'] = { + 'get_param': 'zone'} + + h_vdu = hparam['nfv']['VDU'][vdu_name] + if not h_vdu.get('zone') and zone.zone_id: + hparam['nfv']['VDU'][vdu_name]['zone'] = zone.zone_id + if h_vdu.get('zone') and not zone.zone_id: + del hparam['nfv']['VDU'][vdu_name]['zone'] + + if 'vim_assets' in grant and grant.vim_assets: + h_vdus = hparam['nfv']['VDU'] + for flv in grant.vim_assets.compute_resource_flavours: + vdu_name = flv.vnfd_virtual_compute_desc_id + h_vdus[vdu_name]['flavor'] = flv.vim_flavour_id + for img in grant.vim_assets.software_images: + vdu_name = img.vnfd_software_image_id + h_vdus[vdu_name]['image'] = img.vim_software_image_id + + return bh, nh, hparam + @log.log def _delete_user_data_module(self, user_data_module): # Delete module recursively. @@ -1674,10 +1727,18 @@ class OpenStack(abstract_driver.VnfAbstractDriver, for zone in grant.zones: if zone.id == addrsc.zone_id: for rsc in vnf_info['addResources']: - if addrsc.id == rsc.id: + if addrsc.resource_definition_id == rsc.id: vdu_name = rsc.vdu_id break - stack_param['nfv']['VDU'][vdu_name]['zone'] = zone.zone_id + if not (stack_param['nfv']['VDU'] + [vdu_name]).get('zone') and\ + zone.zone_id: + stack_param['nfv']['VDU'][vdu_name]['zone'] = \ + zone.zone_id + if (stack_param['nfv']['VDU'] + [vdu_name]).get('zone') and\ + not zone.zone_id: + del stack_param['nfv']['VDU'][vdu_name]['zone'] if 'vim_assets' in grant and grant.vim_assets: for flavour in grant.vim_assets.compute_resource_flavours: vdu_name = flavour.vnfd_virtual_compute_desc_id