From 9e13a55aba31c47702d5aa835735aca6706e5a09 Mon Sep 17 00:00:00 2001 From: Yi Feng Date: Mon, 7 Jun 2021 12:31:17 +0900 Subject: [PATCH] Modify processing of _heal_grant and _scale_grant In NFV-SOL003 V2.6.1, the add_resources'id in grant should be same as the vnfc_resource_info'id. However, at present, if you execute heal_grant and scale_grant request, the add_resources's resource_definition_id in grantResponse cannot match the vnfc_resource_info.id. In _scale_grant, to fix this bug, we changed the processing of _scale_grant in openstack._get_grant_resource_scale_out method. Firstly initialize the data of vnfc_resource_info according to the input parameters and the vnfd file, then initialize the add_resources data of grant according to the vnfc_resource_info data. After scaling out, store the data in the stack into vnfc_resource_info in the openstack.scale_resource_update method. In addition, in openstack._get_grant_resource_scale_out, there is also a problem with the placement_constraint rule setting, which is also corrected in this patch. In _heal_grant, if heal entire vnf_instance, we initialize remove_resources used old vnf_instance's info, and then reinitialize vnf_instance's info and use them to initialize add_resources. If heal partial vnf_instance, we just use update_resources in grant. For instantiate, if you need to use the data obtained by instantiate_grant in instantiate_end, the process should execute the post_instantiate_vnf method before instantiate_end, so the vnflcm_driver._instantiate_vnf method has also been modified. Closes-Bug: #1930782 Change-Id: I1008472f5a7104324b61a413052dc44bc84c7ade --- tacker/conductor/conductor_server.py | 297 +++++++++---- .../BaseHOT/simple/helloworld3.yaml | 100 +++++ .../BaseHOT/simple/nested/VDU1.yaml | 65 +++ .../BaseHOT/simple/nested/VDU2.yaml | 65 +++ .../Definitions/helloworld3_df_simple.yaml | 413 ++++++++++++++++++ .../Definitions/helloworld3_top.vnfd.yaml | 31 ++ .../Definitions/helloworld3_types.yaml | 55 +++ .../nfv/functional7/TOSCA-Metadata/TOSCA.meta | 4 + .../etsi/nfv/functional7/UserData/__init__.py | 0 .../nfv/functional7/UserData/lcm_user_data.py | 35 ++ .../helloworld3_types.yaml | 59 +++ .../nfv/test_heal_grant_unit/sample_vnfd.yaml | 144 ++++++ .../sol_separated_nfvo/vnflcm/fake_grant.py | 9 +- ...f_instance_with_user_data_nfvo_separate.py | 166 +++++++ .../unit/conductor/test_conductor_server.py | 82 +++- tacker/tests/unit/vnflcm/fakes.py | 17 + tacker/tests/unit/vnflcm/test_controller.py | 2 +- .../unit/vnflcm/test_load_vnf_interfaces.py | 6 +- .../tests/unit/vnflcm/test_vnflcm_driver.py | 72 ++- .../kubernetes/test_kubernetes_driver.py | 8 +- .../openstack/data/hot_scale_grant.yaml | 4 +- .../fixture_data/fixture_data_utils.py | 47 ++ .../openstack/test_openstack_driver.py | 220 ++++++++++ tacker/vnflcm/vnflcm_driver.py | 18 +- .../kubernetes/kubernetes_driver.py | 3 +- .../vnfm/infra_drivers/openstack/openstack.py | 144 ++++-- tacker/vnfm/infra_drivers/scale_driver.py | 3 +- 27 files changed, 1919 insertions(+), 150 deletions(-) create mode 100644 tacker/tests/etc/samples/etsi/nfv/functional7/BaseHOT/simple/helloworld3.yaml create mode 100644 tacker/tests/etc/samples/etsi/nfv/functional7/BaseHOT/simple/nested/VDU1.yaml create mode 100644 tacker/tests/etc/samples/etsi/nfv/functional7/BaseHOT/simple/nested/VDU2.yaml create mode 100644 tacker/tests/etc/samples/etsi/nfv/functional7/Definitions/helloworld3_df_simple.yaml create mode 100644 tacker/tests/etc/samples/etsi/nfv/functional7/Definitions/helloworld3_top.vnfd.yaml create mode 100644 tacker/tests/etc/samples/etsi/nfv/functional7/Definitions/helloworld3_types.yaml create mode 100644 tacker/tests/etc/samples/etsi/nfv/functional7/TOSCA-Metadata/TOSCA.meta create mode 100644 tacker/tests/etc/samples/etsi/nfv/functional7/UserData/__init__.py create mode 100644 tacker/tests/etc/samples/etsi/nfv/functional7/UserData/lcm_user_data.py create mode 100644 tacker/tests/etc/samples/etsi/nfv/test_heal_grant_unit/helloworld3_types.yaml create mode 100644 tacker/tests/etc/samples/etsi/nfv/test_heal_grant_unit/sample_vnfd.yaml diff --git a/tacker/conductor/conductor_server.py b/tacker/conductor/conductor_server.py index a4020f5ae..4016fc791 100644 --- a/tacker/conductor/conductor_server.py +++ b/tacker/conductor/conductor_server.py @@ -1226,6 +1226,159 @@ class Conductor(manager.Manager): vnf_dict['grant'] = self._grant(context, grant_request) + def _init_remove_resources(self, vnf_inf, rm_resources): + for vnfc_resource in vnf_inf.vnfc_resource_info: + resource = objects.ResourceDefinition() + resource.id = vnfc_resource.id + resource.type = constants.TYPE_COMPUTE + resource.vdu_id = vnfc_resource.vdu_id + resource.resource_template_id = vnfc_resource.vdu_id + vim_id = vnfc_resource.compute_resource.vim_connection_id + rsc_id = vnfc_resource.compute_resource.resource_id + vnfc_rh = objects.ResourceHandle( + vim_connection_id=vim_id, + resource_id=rsc_id) + resource.resource = vnfc_rh + rm_resources.append(resource) + + for vl_resource in vnf_inf.vnf_virtual_link_resource_info: + resource = objects.ResourceDefinition() + resource.id = vl_resource.id + resource.type = constants.TYPE_VL + resource.resource_template_id = \ + vl_resource.vnf_virtual_link_desc_id + vim_id = vl_resource.network_resource.vim_connection_id + rsc_id = vl_resource.network_resource.resource_id + vl_rh = objects.ResourceHandle( + vim_connection_id=vim_id, + resource_id=rsc_id) + resource.resource = vl_rh + rm_resources.append(resource) + for cp_resource in vl_resource.vnf_link_ports: + for vnfc_resource in vnf_inf.vnfc_resource_info: + for vnfc_cp_resource in vnfc_resource.vnfc_cp_info: + if cp_resource.cp_instance_id == \ + vnfc_cp_resource.id: + resource = objects.ResourceDefinition() + resource.id = cp_resource.id + resource.type = constants.TYPE_LINKPORT + resource.vdu_id = vnfc_resource.vdu_id + resource.resource_template_id = \ + vnfc_cp_resource.cpd_id + vim_id = \ + cp_resource.resource_handle. \ + vim_connection_id + rsc_id = cp_resource.resource_handle. \ + resource_id + cp_rh = objects.ResourceHandle( + vim_connection_id=vim_id, + resource_id=rsc_id) + resource.resource = cp_rh + rm_resources.append(resource) + + for storage_resource in vnf_inf.virtual_storage_resource_info: + for vnfc_resource in vnf_inf.vnfc_resource_info: + if storage_resource.id in \ + vnfc_resource.storage_resource_ids: + resource = objects.ResourceDefinition() + resource.id = storage_resource.id + resource.type = constants.TYPE_STORAGE + resource.vdu_id = vnfc_resource.vdu_id + resource.resource_template_id = \ + storage_resource.virtual_storage_desc_id + vim_id = \ + storage_resource.storage_resource. \ + vim_connection_id + rsc_id = storage_resource.storage_resource.resource_id + st_rh = objects.ResourceHandle( + vim_connection_id=vim_id, + resource_id=rsc_id) + resource.resource = st_rh + rm_resources.append(resource) + + def _init_add_resources(self, context, vnf_instance, vim_connection_info, + add_resources, vnf_inf, placement_obj_list, + affinity_list, vnf_dict): + instantiate_vnf_request = objects.InstantiateVnfRequest. \ + from_vnf_instance(vnf_instance) + vnfd_dict = vnflcm_utils._get_vnfd_dict( + context, vnf_instance.vnfd_id, + instantiate_vnf_request.flavour_id) + vnf_instance_after = copy.deepcopy(vnf_instance) + vnf_instance_after.instantiated_vnf_info.reinitialize() + vnflcm_utils._build_instantiated_vnf_info( + vnfd_dict, instantiate_vnf_request, + vnf_instance_after, + vim_connection_info.vim_id) + vnf_inf_after = vnf_instance_after.instantiated_vnf_info + vnfc_rs_ids_list_before = [] + + for vnfc_resource in vnf_inf_after.vnfc_resource_info: + resource = objects.ResourceDefinition() + resource.id = vnfc_resource.id + resource.type = constants.TYPE_COMPUTE + resource.vdu_id = vnfc_resource.vdu_id + resource.resource_template_id = vnfc_resource.vdu_id + add_resources.append(resource) + + for vnfc_resource_before in vnf_inf.vnfc_resource_info: + if vnfc_resource_before.vdu_id == \ + vnfc_resource.vdu_id and vnfc_resource_before.id \ + not in vnfc_rs_ids_list_before: + key_id = vnfc_resource_before.id + vnfc_rs_ids_list_before.append(key_id) + break + + for placement_obj in placement_obj_list: + resource_dict = jsonutils.loads(placement_obj.resource) + set_flg = False + for resource in resource_dict: + if resource.get('resource_id') == key_id: + resource['id_type'] = 'GRANT' + resource['resource_id'] = vnfc_resource.id + g_name = placement_obj.server_group_name + affinity_list.append(g_name) + set_flg = True + res_json = jsonutils.dump_as_bytes(resource_dict) + placement_obj.resource = res_json + break + if set_flg: + break + + for vl_resource in vnf_inf_after.vnf_virtual_link_resource_info: + resource = objects.ResourceDefinition() + resource.id = vl_resource.id + resource.type = constants.TYPE_VL + resource.resource_template_id = \ + vl_resource.vnf_virtual_link_desc_id + add_resources.append(resource) + for cp_resource in vl_resource.vnf_link_ports: + for vnfc_resource in vnf_inf_after.vnfc_resource_info: + for vnfc_cp_resource in vnfc_resource.vnfc_cp_info: + if cp_resource.cp_instance_id == \ + vnfc_cp_resource.id: + resource = objects.ResourceDefinition() + resource.id = cp_resource.id + resource.type = constants.TYPE_LINKPORT + resource.vdu_id = vnfc_resource.vdu_id + resource.resource_template_id = \ + vnfc_cp_resource.cpd_id + add_resources.append(resource) + + for storage_resource in vnf_inf_after. \ + virtual_storage_resource_info: + for vnfc_resource in vnf_inf_after.vnfc_resource_info: + if storage_resource.id in \ + vnfc_resource.storage_resource_ids: + resource = objects.ResourceDefinition() + resource.id = storage_resource.id + resource.type = constants.TYPE_STORAGE + resource.vdu_id = vnfc_resource.vdu_id + resource.resource_template_id = \ + storage_resource.virtual_storage_desc_id + add_resources.append(resource) + vnf_dict['vnf_instance_after'] = vnf_instance_after + @grant_error_common def _heal_grant(self, context, @@ -1247,90 +1400,72 @@ class Conductor(manager.Manager): 'get_cinder_list', vnf_info=vnf_dict) - vnf_instantiated_info_after = copy.deepcopy(vnf_inf) - del_cre_vdu_list = [] - add_resources = [] - rm_resources = [] affinity_list = [] - for vnfc_resource in vnf_instantiated_info_after.vnfc_resource_info: - vnfc_key = vnfc_resource.compute_resource.resource_id - if not heal_vnf_request.vnfc_instance_id or \ - vnfc_key in heal_vnf_request.vnfc_instance_id: - if vnfc_resource.vdu_id not in del_cre_vdu_list: - del_cre_vdu_list.append(vnfc_resource.vdu_id) - for vnfc_resource in vnf_instantiated_info_after.vnfc_resource_info: - if vnfc_resource.vdu_id in del_cre_vdu_list: - resource = objects.ResourceDefinition() - resource.id = vnfc_resource.id - resource.type = constants.TYPE_COMPUTE - resource.vdu_id = vnfc_resource.vdu_id - resource.resource_template_id = vnfc_resource.vdu_id - vim_id = vnfc_resource.compute_resource.vim_connection_id - rsc_id = vnfc_resource.compute_resource.resource_id - vnfc_rh = objects.ResourceHandle( - vim_connection_id=vim_id, - resource_id=rsc_id) - resource.resource = vnfc_rh - rm_resources.append(resource) - add_uuid = uuidutils.generate_uuid() - resource = objects.ResourceDefinition() - resource.id = add_uuid - resource.type = constants.TYPE_COMPUTE - resource.vdu_id = vnfc_resource.vdu_id - resource.resource_template_id = vnfc_resource.vdu_id - add_resources.append(resource) + rm_resources = [] + add_resources = [] + update_resources = [] + if not heal_vnf_request.vnfc_instance_id: + # init remove_resources + self._init_remove_resources(vnf_inf, rm_resources) + # init add_resources + self._init_add_resources( + context, vnf_instance, vim_connection_info, add_resources, + vnf_inf, placement_obj_list, affinity_list, vnf_dict) + else: + for vnfc_resource in vnf_inf.vnfc_resource_info: + vnfc_key = vnfc_resource.id + if vnfc_key in heal_vnf_request.vnfc_instance_id: + resource = objects.ResourceDefinition() + resource.id = vnfc_resource.id + resource.type = constants.TYPE_COMPUTE + resource.vdu_id = vnfc_resource.vdu_id + resource.resource_template_id = vnfc_resource.vdu_id + vim_id = vnfc_resource.compute_resource.vim_connection_id + rsc_id = vnfc_resource.compute_resource.resource_id + vnfc_rh = objects.ResourceHandle( + vim_connection_id=vim_id, + resource_id=rsc_id) + resource.resource = vnfc_rh + update_resources.append(resource) - key_id = vnfc_resource.compute_resource.resource_id - for placement_obj in placement_obj_list: - resource_dict = jsonutils.loads(placement_obj.resource) - set_flg = False - for resource in resource_dict: - if resource.get('resource_id') == key_id: - resource['id_type'] = 'GRANT' - resource['resource_id'] = add_uuid - g_name = placement_obj.server_group_name - affinity_list.append(g_name) - set_flg = True - res_json = jsonutils.dump_as_bytes(resource_dict) - placement_obj.resource = res_json + key_id = vnfc_resource.compute_resource.resource_id + for placement_obj in placement_obj_list: + resource_dict = jsonutils.loads(placement_obj.resource) + set_flg = False + for resource in resource_dict: + if resource.get('resource_id') == key_id: + resource['id_type'] = 'GRANT' + resource['resource_id'] = vnfc_resource.id + g_name = placement_obj.server_group_name + affinity_list.append(g_name) + set_flg = True + res_json = \ + jsonutils.dump_as_bytes(resource_dict) + placement_obj.resource = res_json + break + if set_flg: break - if set_flg: - break - vnfc_resource.id = add_uuid - vnfc_resource.compute_resource = objects.ResourceHandle() - st_info = vnf_instantiated_info_after.virtual_storage_resource_info - for storage_resource in st_info: - if storage_resource.virtual_storage_desc_id in cinder_list: - for vnfc_resource in vnf_inf.vnfc_resource_info: - id_list = vnfc_resource.storage_resource_ids - if storage_resource.id in id_list: - resource = objects.ResourceDefinition() - resource.id = storage_resource.id - resource.type = constants.TYPE_STORAGE - resource.vdu_id = vnfc_resource.vdu_id - resource.resource_template_id = \ - storage_resource.virtual_storage_desc_id - st_rh = objects.ResourceHandle() - st_rh.vim_connection_id = \ - storage_resource.storage_resource.vim_connection_id - st_rh.resource_id = \ - storage_resource.storage_resource.resource_id - resource.resource = st_rh - rm_resources.append(resource) - - add_uuid = uuidutils.generate_uuid() - resource = objects.ResourceDefinition() - resource = objects.ResourceDefinition() - resource.id = add_uuid - resource.type = constants.TYPE_STORAGE - resource.vdu_id = vnfc_resource.vdu_id - resource.resource_template_id = \ - storage_resource.virtual_storage_desc_id - add_resources.append(resource) - storage_resource.id = add_uuid - storage_resource.storage_resource = \ - objects.ResourceHandle() + st_info = vnf_inf.virtual_storage_resource_info + for storage_resource in st_info: + if storage_resource.virtual_storage_desc_id in cinder_list: + for vnfc_resource in vnf_inf.vnfc_resource_info: + id_list = vnfc_resource.storage_resource_ids + if storage_resource.id in id_list: + resource = objects.ResourceDefinition() + resource.id = storage_resource.id + resource.type = constants.TYPE_STORAGE + resource.vdu_id = vnfc_resource.vdu_id + resource.resource_template_id = \ + storage_resource.virtual_storage_desc_id + st_rh = objects.ResourceHandle() + st_rh.vim_connection_id = \ + storage_resource.storage_resource.\ + vim_connection_id + st_rh.resource_id = \ + storage_resource.storage_resource.resource_id + resource.resource = st_rh + update_resources.append(resource) p_c_list = [] for placement_obj in placement_obj_list: @@ -1355,11 +1490,11 @@ class Conductor(manager.Manager): False, add_resources=add_resources, remove_resources=rm_resources, + update_resources=update_resources, placement_constraints=p_c_list) vnf_dict['placement_obj_list'] = placement_obj_list vnf_dict['grant'] = self._grant(context, g_request) - vnf_dict['vnf_instantiated_info_after'] = vnf_instantiated_info_after @grant_error_common def _terminate_grant(self, context, vnf_instance, vnf_lcm_op_occ_id): diff --git a/tacker/tests/etc/samples/etsi/nfv/functional7/BaseHOT/simple/helloworld3.yaml b/tacker/tests/etc/samples/etsi/nfv/functional7/BaseHOT/simple/helloworld3.yaml new file mode 100644 index 000000000..dda1386b2 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/functional7/BaseHOT/simple/helloworld3.yaml @@ -0,0 +1,100 @@ +heat_template_version: 2013-05-23 +description: 'Simple Base HOT for Sample VNF' + +parameters: + nfv: + type: json + +resources: + VDU1_scale: + type: OS::Heat::AutoScalingGroup + properties: + min_size: 1 + max_size: 3 + desired_capacity: 1 + resource: + type: VDU1.yaml + properties: + flavor: { get_param: [ nfv, VDU, VDU1, flavor ] } + image: { get_param: [ nfv, VDU, VDU1, image ] } + zone: { get_param: [ nfv, vdu, VDU1, zone ] } + net1: { get_param: [ nfv, CP, VDU1_CP1, network ] } + net2: { get_param: [ nfv, CP, VDU1_CP2, network ] } + 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_scale_out: + type: OS::Heat::ScalingPolicy + properties: + scaling_adjustment: 1 + auto_scaling_group_id: + get_resource: VDU1_scale + adjustment_type: change_in_capacity + VDU1_scale_scale_in: + type: OS::Heat::ScalingPolicy + properties: + scaling_adjustment: -1 + auto_scaling_group_id: + get_resource: VDU1_scale + adjustment_type: change_in_capacity + VDU2_scale: + type: OS::Heat::AutoScalingGroup + properties: + 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 ] } + 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_scale_out: + type: OS::Heat::ScalingPolicy + properties: + scaling_adjustment: 1 + auto_scaling_group_id: + get_resource: VDU2_scale + adjustment_type: change_in_capacity + VDU2_scale_scale_in: + type: OS::Heat::ScalingPolicy + properties: + scaling_adjustment: -1 + auto_scaling_group_id: + get_resource: VDU2_scale + adjustment_type: change_in_capacity + extmanageNW_1: + type: OS::Neutron::Net + extmanageNW_2: + type: OS::Neutron::Net + internalNW_1: + type: OS::Neutron::Net + extmanageNW_1_subnet: + type: OS::Neutron::Subnet + properties: + ip_version: 4 + network: + get_resource: extmanageNW_1 + cidr: 192.168.3.0/24 + extmanageNW_2_subnet: + type: OS::Neutron::Subnet + properties: + ip_version: 4 + network: + get_resource: extmanageNW_2 + cidr: 192.168.4.0/24 + internalNW_1_subnet: + type: OS::Neutron::Subnet + properties: + ip_version: 4 + network: + get_resource: internalNW_1 + cidr: 192.168.5.0/24 +outputs: {} diff --git a/tacker/tests/etc/samples/etsi/nfv/functional7/BaseHOT/simple/nested/VDU1.yaml b/tacker/tests/etc/samples/etsi/nfv/functional7/BaseHOT/simple/nested/VDU1.yaml new file mode 100644 index 000000000..e1976f391 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/functional7/BaseHOT/simple/nested/VDU1.yaml @@ -0,0 +1,65 @@ +heat_template_version: 2013-05-23 +description: 'VDU1 HOT for Sample VNF' + +parameters: + flavor: + type: string + image: + type: string + zone: + type: string + net1: + type: string + net2: + type: string + net3: + type: string + net4: + type: string + net5: + type: string + subnet: + type: string + +resources: + VDU1: + type: OS::Nova::Server + properties: + flavor: { get_param: flavor } + name: VDU1 + image: { get_param: image } + networks: + - port: + get_resource: VDU1_CP1 + - port: + get_resource: VDU1_CP2 + - port: + get_resource: VDU1_CP3 + - port: + get_resource: VDU1_CP4 + - port: + get_resource: VDU1_CP5 + availability_zone: { get_param: zone } + + VDU1_CP1: + type: OS::Neutron::Port + properties: + network: { get_param: net1 } + VDU1_CP2: + type: OS::Neutron::Port + properties: + network: { get_param: net2 } + fixed_ips: + - subnet: { get_param: subnet} + VDU1_CP3: + type: OS::Neutron::Port + properties: + network: { get_param: net3 } + VDU1_CP4: + type: OS::Neutron::Port + properties: + network: { get_param: net4 } + VDU1_CP5: + type: OS::Neutron::Port + properties: + network: { get_param: net5 } diff --git a/tacker/tests/etc/samples/etsi/nfv/functional7/BaseHOT/simple/nested/VDU2.yaml b/tacker/tests/etc/samples/etsi/nfv/functional7/BaseHOT/simple/nested/VDU2.yaml new file mode 100644 index 000000000..c79d6737b --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/functional7/BaseHOT/simple/nested/VDU2.yaml @@ -0,0 +1,65 @@ +heat_template_version: 2013-05-23 +description: 'VDU2 HOT for Sample VNF' + +parameters: + flavor: + type: string + image: + type: string + net1: + type: string + net2: + type: string + net3: + type: string + net4: + type: string + net5: + type: string + ip1: + type: string + subnet: + type: string + +resources: + VDU2: + type: OS::Nova::Server + properties: + flavor: { get_param: flavor } + name: VDU2 + image: { get_param: image } + networks: + - port: + get_resource: VDU2_CP1 + - port: + get_resource: VDU2_CP2 + - port: + get_resource: VDU2_CP3 + - port: + get_resource: VDU2_CP4 + - port: + get_resource: VDU2_CP5 + + VDU2_CP1: + type: OS::Neutron::Port + properties: + network: { get_param: net1 } + VDU2_CP2: + 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: + network: { get_param: net3 } + VDU2_CP4: + type: OS::Neutron::Port + properties: + network: { get_param: net4 } + VDU2_CP5: + type: OS::Neutron::Port + properties: + network: { get_param: net5 } diff --git a/tacker/tests/etc/samples/etsi/nfv/functional7/Definitions/helloworld3_df_simple.yaml b/tacker/tests/etc/samples/etsi/nfv/functional7/Definitions/helloworld3_df_simple.yaml new file mode 100644 index 000000000..d37a04345 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/functional7/Definitions/helloworld3_df_simple.yaml @@ -0,0 +1,413 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 + +description: Simple deployment flavour for Sample VNF + +imports: + - etsi_nfv_sol001_common_types.yaml + - etsi_nfv_sol001_vnfd_types.yaml + - helloworld3_types.yaml + +topology_template: + inputs: + descriptor_id: + type: string + descriptor_version: + type: string + provider: + type: string + product_name: + type: string + software_version: + type: string + vnfm_info: + type: list + entry_schema: + type: string + flavour_id: + type: string + flavour_description: + type: string + + substitution_mappings: + node_type: company.provider.VNF + properties: + flavour_id: simple + requirements: + virtual_link_external1_1: [ VDU1_CP1, virtual_link ] + virtual_link_external1_2: [ VDU2_CP1, virtual_link ] + virtual_link_external2_1: [ VDU1_CP2, virtual_link ] + virtual_link_external2_2: [ VDU2_CP2, virtual_link ] + + node_templates: + VNF: + type: company.provider.VNF + properties: + flavour_description: A simple flavour + interfaces: + Vnflcm: + instantiate: [] + instantiate_start: [] + instantiate_end: [] + terminate: [] + terminate_start: [] + terminate_end: [] + modify_information: [] + modify_information_start: [] + modify_information_end: [] + + VDU1: + type: tosca.nodes.nfv.Vdu.Compute + properties: + name: VDU1 + description: VDU1 compute node + vdu_profile: + min_number_of_instances: 1 + max_number_of_instances: 3 + sw_image_data: + name: cirros-0.5.2-x86_64-disk + version: '0.5.2' + checksum: + algorithm: sha-256 + hash: 932fcae93574e242dc3d772d5235061747dfe537668443a1f0567d893614b464 + container_format: bare + disk_format: qcow2 + min_disk: 0 GB + min_ram: 256 MB + size: 12 GB + capabilities: + virtual_compute: + properties: + requested_additional_capabilities: + properties: + requested_additional_capability_name: m1.tiny + support_mandatory: true + target_performance_parameters: + entry_schema: test + virtual_memory: + virtual_mem_size: 512 MB + virtual_cpu: + num_virtual_cpu: 1 + virtual_local_storage: + - size_of_storage: 3 GB + + VDU2: + type: tosca.nodes.nfv.Vdu.Compute + properties: + name: VDU2 + description: VDU2 compute node + vdu_profile: + min_number_of_instances: 1 + max_number_of_instances: 1 + sw_image_data: + name: cirros-0.5.2-x86_64-disk + version: '0.5.2' + checksum: + algorithm: sha-256 + hash: 932fcae93574e242dc3d772d5235061747dfe537668443a1f0567d893614b464 + container_format: bare + disk_format: qcow2 + min_disk: 0 GB + min_ram: 256 MB + size: 12 GB + capabilities: + virtual_compute: + properties: + requested_additional_capabilities: + properties: + requested_additional_capability_name: m1.tiny + support_mandatory: true + target_performance_parameters: + entry_schema: test + virtual_memory: + virtual_mem_size: 512 MB + virtual_cpu: + num_virtual_cpu: 1 + virtual_local_storage: + - size_of_storage: 3 GB + + VDU1_CP1: + type: tosca.nodes.nfv.VduCp + properties: + layer_protocols: [ ipv4 ] + order: 0 + requirements: + - virtual_binding: VDU1 + + VDU1_CP2: + type: tosca.nodes.nfv.VduCp + properties: + layer_protocols: [ ipv4 ] + order: 1 + requirements: + - virtual_binding: VDU1 + + VDU1_CP3: + type: tosca.nodes.nfv.VduCp + properties: + layer_protocols: [ ipv4 ] + order: 2 + requirements: + - virtual_binding: VDU1 + - virtual_link: internalVL1 + + VDU1_CP4: + type: tosca.nodes.nfv.VduCp + properties: + layer_protocols: [ ipv4 ] + order: 3 + requirements: + - virtual_binding: VDU1 + - virtual_link: internalVL2 + + VDU1_CP5: + type: tosca.nodes.nfv.VduCp + properties: + layer_protocols: [ ipv4 ] + order: 4 + requirements: + - virtual_binding: VDU1 + - virtual_link: internalVL3 + + VDU2_CP1: + type: tosca.nodes.nfv.VduCp + properties: + layer_protocols: [ ipv4 ] + order: 0 + requirements: + - virtual_binding: VDU2 + + VDU2_CP2: + type: tosca.nodes.nfv.VduCp + properties: + layer_protocols: [ ipv4 ] + order: 1 + requirements: + - virtual_binding: VDU2 + + VDU2_CP3: + type: tosca.nodes.nfv.VduCp + properties: + layer_protocols: [ ipv4 ] + order: 2 + requirements: + - virtual_binding: VDU2 + - virtual_link: internalVL1 + + VDU2_CP4: + type: tosca.nodes.nfv.VduCp + properties: + layer_protocols: [ ipv4 ] + order: 3 + requirements: + - virtual_binding: VDU2 + - virtual_link: internalVL2 + + VDU2_CP5: + type: tosca.nodes.nfv.VduCp + properties: + layer_protocols: [ ipv4 ] + order: 4 + requirements: + - virtual_binding: VDU2 + - virtual_link: internalVL3 + + internalVL1: + type: tosca.nodes.nfv.VnfVirtualLink + properties: + connectivity_type: + layer_protocols: [ ipv4 ] + description: External Managed Virtual link in the VNF + vl_profile: + max_bitrate_requirements: + root: 1048576 + leaf: 1048576 + min_bitrate_requirements: + root: 1048576 + leaf: 1048576 + virtual_link_protocol_data: + - associated_layer_protocol: ipv4 + l3_protocol_data: + ip_version: ipv4 + cidr: 33.33.0.0/24 + + internalVL2: + type: tosca.nodes.nfv.VnfVirtualLink + properties: + connectivity_type: + layer_protocols: [ ipv4 ] + description: External Managed Virtual link in the VNF + vl_profile: + max_bitrate_requirements: + root: 1048576 + leaf: 1048576 + min_bitrate_requirements: + root: 1048576 + leaf: 1048576 + virtual_link_protocol_data: + - associated_layer_protocol: ipv4 + l3_protocol_data: + ip_version: ipv4 + cidr: 33.34.0.0/24 + + internalVL3: + type: tosca.nodes.nfv.VnfVirtualLink + properties: + connectivity_type: + layer_protocols: [ ipv4 ] + description: Internal Virtual link in the VNF + vl_profile: + max_bitrate_requirements: + root: 1048576 + leaf: 1048576 + min_bitrate_requirements: + root: 1048576 + leaf: 1048576 + virtual_link_protocol_data: + - associated_layer_protocol: ipv4 + l3_protocol_data: + ip_version: ipv4 + cidr: 33.35.0.0/24 + + policies: + - scaling_aspects: + type: tosca.policies.nfv.ScalingAspects + properties: + aspects: + VDU1_scale: + name: VDU1_scale + description: VDU1 scaling aspect + max_scale_level: 2 + step_deltas: + - delta_1 + VDU2_scale: + name: VDU2_scale + description: VDU2 scaling aspect + max_scale_level: 2 + step_deltas: + - delta_1 + + - VDU1_initial_delta: + type: tosca.policies.nfv.VduInitialDelta + properties: + initial_delta: + number_of_instances: 1 + targets: [ VDU1 ] + + - VDU2_initial_delta: + type: tosca.policies.nfv.VduInitialDelta + properties: + initial_delta: + number_of_instances: 1 + targets: [ VDU2 ] + + - VDU1_scaling_aspect_deltas: + type: tosca.policies.nfv.VduScalingAspectDeltas + properties: + aspect: VDU1_scale + deltas: + delta_1: + number_of_instances: 1 + targets: [ VDU1 ] + + - VDU2_scaling_aspect_deltas: + type: tosca.policies.nfv.VduScalingAspectDeltas + properties: + aspect: VDU2_scale + deltas: + delta_1: + number_of_instances: 1 + targets: [ VDU2 ] + + - instantiation_levels: + type: tosca.policies.nfv.InstantiationLevels + properties: + levels: + instantiation_level_1: + description: Smallest size + scale_info: + VDU1_scale: + scale_level: 0 + VDU2_scale: + scale_level: 0 + instantiation_level_2: + description: Largest size + scale_info: + VDU1_scale: + scale_level: 2 + VDU2_scale: + scale_level: 2 + default_level: instantiation_level_1 + + - VDU1_instantiation_levels: + type: tosca.policies.nfv.VduInstantiationLevels + properties: + levels: + instantiation_level_1: + number_of_instances: 1 + instantiation_level_2: + number_of_instances: 3 + targets: [ VDU1 ] + + - VDU2_instantiation_levels: + type: tosca.policies.nfv.VduInstantiationLevels + properties: + levels: + instantiation_level_1: + number_of_instances: 1 + instantiation_level_2: + number_of_instances: 1 + targets: [ VDU2 ] + + - internalVL1_instantiation_levels: + type: tosca.policies.nfv.VirtualLinkInstantiationLevels + properties: + levels: + instantiation_level_1: + bitrate_requirements: + root: 1048576 + leaf: 1048576 + instantiation_level_2: + bitrate_requirements: + root: 1048576 + leaf: 1048576 + targets: [ internalVL1 ] + + - internalVL2_instantiation_levels: + type: tosca.policies.nfv.VirtualLinkInstantiationLevels + properties: + levels: + instantiation_level_1: + bitrate_requirements: + root: 1048576 + leaf: 1048576 + instantiation_level_2: + bitrate_requirements: + root: 1048576 + leaf: 1048576 + targets: [ internalVL2 ] + + - internalVL3_instantiation_levels: + type: tosca.policies.nfv.VirtualLinkInstantiationLevels + properties: + levels: + instantiation_level_1: + bitrate_requirements: + root: 1048576 + leaf: 1048576 + instantiation_level_2: + bitrate_requirements: + root: 1048576 + leaf: 1048576 + targets: [ internalVL3 ] + + - policy_antiaffinity_vdu1: + type: tosca.policies.nfv.AntiAffinityRule + targets: [ VDU1 ] + properties: + scope: zone + + - policy_antiaffinity_vdu2: + type: tosca.policies.nfv.AntiAffinityRule + targets: [ VDU2 ] + properties: + scope: zone diff --git a/tacker/tests/etc/samples/etsi/nfv/functional7/Definitions/helloworld3_top.vnfd.yaml b/tacker/tests/etc/samples/etsi/nfv/functional7/Definitions/helloworld3_top.vnfd.yaml new file mode 100644 index 000000000..0be18659b --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/functional7/Definitions/helloworld3_top.vnfd.yaml @@ -0,0 +1,31 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 + +description: Sample VNF + +imports: + - etsi_nfv_sol001_common_types.yaml + - etsi_nfv_sol001_vnfd_types.yaml + - helloworld3_types.yaml + - helloworld3_df_simple.yaml + +topology_template: + inputs: + selected_flavour: + type: string + description: VNF deployment flavour selected by the consumer. It is provided in the API + + node_templates: + VNF: + type: company.provider.VNF + properties: + flavour_id: { get_input: selected_flavour } + descriptor_id: b1bb0ce7-ebca-4fa7-95ed-4840d7000000 + provider: Company + product_name: Sample VNF + software_version: '1.0' + descriptor_version: '1.0' + vnfm_info: + - Tacker + requirements: + #- virtual_link_external # mapped in lower-level templates + #- virtual_link_internal # mapped in lower-level templates \ No newline at end of file diff --git a/tacker/tests/etc/samples/etsi/nfv/functional7/Definitions/helloworld3_types.yaml b/tacker/tests/etc/samples/etsi/nfv/functional7/Definitions/helloworld3_types.yaml new file mode 100644 index 000000000..5c026d6a2 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/functional7/Definitions/helloworld3_types.yaml @@ -0,0 +1,55 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 + +description: VNF type definition + +imports: + - etsi_nfv_sol001_common_types.yaml + - etsi_nfv_sol001_vnfd_types.yaml + +node_types: + company.provider.VNF: + derived_from: tosca.nodes.nfv.VNF + properties: + descriptor_id: + type: string + constraints: [ valid_values: [ b1bb0ce7-ebca-4fa7-95ed-4840d7000000 ] ] + default: b1bb0ce7-ebca-4fa7-95ed-4840d7000000 + descriptor_version: + type: string + constraints: [ valid_values: [ '1.0' ] ] + default: '1.0' + provider: + type: string + constraints: [ valid_values: [ 'Company' ] ] + default: 'Company' + product_name: + type: string + constraints: [ valid_values: [ 'Sample VNF' ] ] + default: 'Sample VNF' + software_version: + type: string + constraints: [ valid_values: [ '1.0' ] ] + default: '1.0' + vnfm_info: + type: list + entry_schema: + type: string + constraints: [ valid_values: [ Tacker ] ] + default: [ Tacker ] + flavour_id: + type: string + constraints: [ valid_values: [ simple ] ] + default: simple + flavour_description: + type: string + default: "falvour" + requirements: + - virtual_link_external1: + capability: tosca.capabilities.nfv.VirtualLinkable + - virtual_link_external2: + capability: tosca.capabilities.nfv.VirtualLinkable + - virtual_link_internal: + capability: tosca.capabilities.nfv.VirtualLinkable + interfaces: + Vnflcm: + type: tosca.interfaces.nfv.Vnflcm \ No newline at end of file diff --git a/tacker/tests/etc/samples/etsi/nfv/functional7/TOSCA-Metadata/TOSCA.meta b/tacker/tests/etc/samples/etsi/nfv/functional7/TOSCA-Metadata/TOSCA.meta new file mode 100644 index 000000000..cfd7444ac --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/functional7/TOSCA-Metadata/TOSCA.meta @@ -0,0 +1,4 @@ +TOSCA-Meta-File-Version: 1.0 +CSAR-Version: 1.1 +Created-by: Onboarding portal +Entry-Definitions: Definitions/helloworld3_top.vnfd.yaml diff --git a/tacker/tests/etc/samples/etsi/nfv/functional7/UserData/__init__.py b/tacker/tests/etc/samples/etsi/nfv/functional7/UserData/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tacker/tests/etc/samples/etsi/nfv/functional7/UserData/lcm_user_data.py b/tacker/tests/etc/samples/etsi/nfv/functional7/UserData/lcm_user_data.py new file mode 100644 index 000000000..725c5430a --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/functional7/UserData/lcm_user_data.py @@ -0,0 +1,35 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from tacker.vnfm.lcm_user_data.abstract_user_data import AbstractUserData +import tacker.vnfm.lcm_user_data.utils as UserDataUtil + + +class SampleUserData(AbstractUserData): + @staticmethod + def instantiate(base_hot_dict=None, + vnfd_dict=None, + inst_req_info=None, + grant_info=None): + api_param = UserDataUtil.get_diff_base_hot_param_from_api( + base_hot_dict, inst_req_info) + initial_param_dict = \ + UserDataUtil.create_initial_param_server_port_dict( + base_hot_dict) + vdu_flavor_dict = \ + UserDataUtil.create_vdu_flavor_capability_name_dict(vnfd_dict) + vdu_image_dict = UserDataUtil.create_sw_image_dict(vnfd_dict) + cpd_vl_dict = UserDataUtil.create_network_dict( + inst_req_info, initial_param_dict) + final_param_dict = UserDataUtil.create_final_param_dict( + initial_param_dict, vdu_flavor_dict, vdu_image_dict, cpd_vl_dict) + return {**final_param_dict, **api_param} diff --git a/tacker/tests/etc/samples/etsi/nfv/test_heal_grant_unit/helloworld3_types.yaml b/tacker/tests/etc/samples/etsi/nfv/test_heal_grant_unit/helloworld3_types.yaml new file mode 100644 index 000000000..c388b2f28 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_heal_grant_unit/helloworld3_types.yaml @@ -0,0 +1,59 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 + +description: VNF type definition + +node_types: + company.provider.VNF: + derived_from: tosca.nodes.nfv.VNF + properties: + id: + type: string + description: ID of this VNF + default: vnf_id + vendor: + type: string + description: name of the vendor who generate this VNF + default: vendor + version: + type: version + description: version of the software for this VNF + default: 1.0 + descriptor_id: + type: string + constraints: [ valid_values: [ b1db0ce7-ebca-1fb7-95ed-4840d70a1163 ] ] + default: b1db0ce7-ebca-1fb7-95ed-4840d70a1163 + descriptor_version: + type: string + constraints: [ valid_values: [ '1.0' ] ] + default: '1.0' + provider: + type: string + constraints: [ valid_values: [ 'Company' ] ] + default: 'Company' + product_name: + type: string + constraints: [ valid_values: [ 'Sample VNF' ] ] + default: 'Sample VNF' + software_version: + type: string + constraints: [ valid_values: [ '1.0' ] ] + default: '1.0' + vnfm_info: + type: list + entry_schema: + type: string + constraints: [ valid_values: [ Tacker ] ] + default: [ Tacker ] + flavour_id: + type: string + constraints: [ valid_values: [ simple ] ] + default: simple + flavour_description: + type: string + default: "This is the default flavour description" + requirements: + - virtual_link_internal: + capability: tosca.capabilities.nfv.VirtualLinkable + interfaces: + Vnflcm: + type: tosca.interfaces.nfv.Vnflcm diff --git a/tacker/tests/etc/samples/etsi/nfv/test_heal_grant_unit/sample_vnfd.yaml b/tacker/tests/etc/samples/etsi/nfv/test_heal_grant_unit/sample_vnfd.yaml new file mode 100644 index 000000000..1fdd78d37 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_heal_grant_unit/sample_vnfd.yaml @@ -0,0 +1,144 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 + +topology_template: + inputs: + id: + type: string + vendor: + type: string + version: + type: version + descriptor_id: + type: string + descriptor_version: + type: string + provider: + type: string + product_name: + type: string + software_version: + type: string + vnfm_info: + type: list + entry_schema: + type: string + flavour_id: + type: string + flavour_description: + type: string + + substitution_mappings: + node_type: company.provider.VNF + properties: + flavour_id: simple + requirements: + virtual_link_external1_1: [ VDU1_CP1, virtual_link ] + + node_templates: + VNF: + type: company.provider.VNF + properties: + flavour_description: A simple flavour + interfaces: + Vnflcm: + instantiate_end: [] + + VDU1: + type: tosca.nodes.nfv.Vdu.Compute + properties: + name: VDU1 + description: VDU1 compute node + vdu_profile: + min_number_of_instances: 2 + max_number_of_instances: 4 + sw_image_data: + name: ubuntu-20.04-server-cloudimg-amd64 + version: '20.04' + checksum: + algorithm: sha-512 + hash: fb1a1e50f9af2df6ab18a69b6bc5df07ebe8ef962b37e556ce95350ffc8f4a1118617d486e2018d1b3586aceaeda799e6cc073f330a7ad8f0ec0416cbd825452 + container_format: bare + disk_format: qcow2 + min_disk: 0 GB + size: 2 GB + + + capabilities: + virtual_compute: + properties: + requested_additional_capabilities: + properties: + requested_additional_capability_name: m1.medium + support_mandatory: true + target_performance_parameters: + entry_schema: test + virtual_memory: + virtual_mem_size: 4 GB + virtual_cpu: + num_virtual_cpu: 2 + virtual_local_storage: + - size_of_storage: 45 GB + + PORT1: + type: tosca.nodes.nfv.VduCp + properties: + layer_protocols: [ ipv4 ] + order: 0 + requirements: + - virtual_binding: VDU1 + + policies: + - scaling_aspects: + type: tosca.policies.nfv.ScalingAspects + properties: + aspects: + master_instance: + name: master_instance + description: master_instance scaling aspect + max_scale_level: 2 + step_deltas: + - delta_1 + + - VDU1_initial_delta: + type: tosca.policies.nfv.VduInitialDelta + properties: + initial_delta: + number_of_instances: 2 + targets: [ VDU1 ] + + + - VDU1_scaling_deltas: + type: tosca.policies.nfv.VduScalingAspectDeltas + properties: + aspect: master_instance + deltas: + delta_1: + number_of_instances: 1 + targets: [ VDU1 ] + + + - instantiation_levels: + type: tosca.policies.nfv.InstantiationLevels + properties: + levels: + instantiation_level_1: + description: Smallest size + scale_info: + master_instance: + scale_level: 0 + instantiation_level_2: + description: Largest size + scale_info: + master_instance: + scale_level: 2 + default_level: instantiation_level_1 + + - VDU1_instantiation_levels: + type: tosca.policies.nfv.VduInstantiationLevels + properties: + levels: + instantiation_level_1: + number_of_instances: 2 + instantiation_level_2: + number_of_instances: 4 + targets: [ VDU1 ] diff --git a/tacker/tests/functional/sol_separated_nfvo/vnflcm/fake_grant.py b/tacker/tests/functional/sol_separated_nfvo/vnflcm/fake_grant.py index 2f812c760..b58f7fdb1 100644 --- a/tacker/tests/functional/sol_separated_nfvo/vnflcm/fake_grant.py +++ b/tacker/tests/functional/sol_separated_nfvo/vnflcm/fake_grant.py @@ -81,8 +81,12 @@ class Grant: res_update_resources = [] for req_update_resource in req_update_resources: res_update_resource = { - "resourceDefinitionId": req_update_resource['id'] + "resourceDefinitionId": req_update_resource['id'], + "vimConnectionId": uuidsentinel.vim_connection_id } + if req_update_resource['type'] == 'COMPUTE': + res_update_resource["zoneId"] = uuidsentinel.zone_id + res_update_resources.append(res_update_resource) return res_update_resources @@ -177,6 +181,9 @@ class Grant: if 'removeResources' in request_body.keys(): res["removeResources"] = Grant._make_remove_resources( request_body['removeResources']) + if 'updateResources' in request_body.keys(): + res["updateResources"] = Grant._make_update_resources( + request_body['updateResources']) res["vimAssets"] = Grant._make_vim_assets(image_id) return res 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 afbcf5487..ab510942b 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 @@ -353,6 +353,140 @@ class VnfLcmWithNfvoSeparator(vnflcm_base.BaseVnfLcmTest): resp, response_body = self._delete_subscription(subscription_id) self.assertEqual(204, resp.status_code) + def test_inst_scale_term(self): + """Test basic life cycle operations with sample VNFD with UserData. + + In this test case, we do following steps. + - Create subscription. + - Create VNF instance. + - Instantiate VNF. + - Get VNF informations. + - Scale-Out VNF + - Scale-In VNF + - Terminate VNF + - Delete VNF + - Delete subscription + """ + vnf_package_info = self._register_vnf_package_mock_response( + package_dir='functional7') + glance_image = self._list_glance_image()[0] + + # Create subscription and register it. + callback_url = os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL, + self._testMethodName) + request_body = fake_vnflcm.Subscription.make_create_request_body( + 'http://localhost:{}{}'.format( + vnflcm_base.FAKE_SERVER_MANAGER.SERVER_PORT, + callback_url)) + resp, response_body = self._register_subscription(request_body) + self.assertEqual(201, resp.status_code) + self.assert_http_header_location_for_subscription(resp.headers) + self.assert_notification_get(callback_url) + subscription_id = response_body.get('id') + self.addCleanup(self._delete_subscription, subscription_id) + + # Create vnf instance + resp, vnf_instance = self._create_vnf_instance_from_body( + fake_vnflcm.VnfInstances.make_create_request_body( + vnf_package_info['vnfdId'])) + vnf_instance_id = vnf_instance.get('id') + self._wait_lcm_done(vnf_instance_id=vnf_instance_id) + self._assert_create_vnf(resp, vnf_instance) + self.addCleanup(self._delete_vnf_instance, vnf_instance_id) + + # Set Fake server response for Grant-Req(Instantiate) + vnflcm_base.FAKE_SERVER_MANAGER.set_callback('POST', + fake_grant.Grant.GRANT_REQ_PATH, status_code=201, + callback=lambda req_headers, + req_body: fake_grant.Grant.make_inst_response_body(req_body, + self.vim['tenant_id'], glance_image.id)) + + # Instantiate vnf instance + 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_scale(vnf_instance_id) + + # Show vnf instance + resp, vnf_instance = self._show_vnf_instance(vnf_instance_id) + self.assertEqual(200, resp.status_code) + + # Set Fake server response for Grant-Req(Scale-out) + vnflcm_base.FAKE_SERVER_MANAGER.set_callback('POST', + fake_grant.Grant.GRANT_REQ_PATH, status_code=201, + callback=lambda req_headers, + req_body: fake_grant.Grant.make_scaleout_response_body(req_body, + self.vim['tenant_id'], glance_image.id)) + + # Scale-out vnf instance + stack = self._get_heat_stack(vnf_instance_id) + pre_stack_resource_list = self._get_heat_resource_list(stack.id, 2) + request_body = fake_vnflcm.VnfInstances.\ + make_scale_request_body('SCALE_OUT') + resp, _ = self._scale_vnf_instance(vnf_instance_id, request_body) + self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id) + + post_stack_resource_list = self._get_heat_resource_list(stack.id, 2) + self._assert_scale_vnf(resp, vnf_instance_id, + pre_stack_resource_list, + post_stack_resource_list, + scale_type='SCALE_OUT') + + # Set Fake server response for Grant-Req(Scale-in) + vnflcm_base.FAKE_SERVER_MANAGER.set_callback('POST', + fake_grant.Grant.GRANT_REQ_PATH, status_code=201, + callback=lambda req_headers, + req_body: fake_grant.Grant.make_scalein_response_body(req_body)) + + # Scale-in vnf instance + stack = self._get_heat_stack(vnf_instance_id) + pre_stack_resource_list = self._get_heat_resource_list(stack.id, 2) + + request_body = fake_vnflcm.VnfInstances.make_scale_request_body( + 'SCALE_IN') + resp, _ = self._scale_vnf_instance(vnf_instance_id, request_body) + self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id) + + post_stack_resource_list = self._get_heat_resource_list(stack.id, 2) + self._assert_scale_vnf(resp, vnf_instance_id, + pre_stack_resource_list, + post_stack_resource_list, + scale_type='SCALE_IN') + + # Set Fake server response for Grant-Req(Terminate) + vnflcm_base.FAKE_SERVER_MANAGER.set_callback('POST', + fake_grant.Grant.GRANT_REQ_PATH, status_code=201, + callback=lambda req_headers, + req_body: fake_grant.Grant.make_term_response_body(req_body)) + + # Get stack informations to terminate. + stack = self._get_heat_stack(vnf_instance_id) + resources_list = self._get_heat_resource_list(stack.id) + resource_name_list = [r.resource_name for r in resources_list] + glance_image_id_list = self._get_glance_image_list_from_stack_resource( + stack.id, resource_name_list) + + # Terminate VNF + terminate_req_body = fake_vnflcm.VnfInstances.make_term_request_body() + resp, _ = self._terminate_vnf_instance(vnf_instance_id, + terminate_req_body) + self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id) + self._assert_terminate_vnf(resp, vnf_instance_id, stack.id, + resource_name_list, glance_image_id_list) + + # Delete VNF + resp, _ = self._delete_vnf_instance(vnf_instance_id) + self._wait_lcm_done(vnf_instance_id=vnf_instance_id) + self.assert_delete_vnf(resp, vnf_instance_id) + + # Delete Subscription + resp, response_body = self._delete_subscription(subscription_id) + self.assertEqual(204, resp.status_code) + def _assert_create_vnf(self, resp, vnf_instance): """Assert that VNF was created via fake server. @@ -421,6 +555,27 @@ class VnfLcmWithNfvoSeparator(vnflcm_base.BaseVnfLcmTest): self.assertEqual(1, len(grant_mock_responses)) self._assert_grant_mock_response(grant_mock_responses[0]) + def _assert_scale_vnf(self, + resp, + vnf_instance_id, + pre_stack_resource_list, + post_stack_resource_list, + scale_type): + super().assert_scale_vnf( + resp, + vnf_instance_id, + pre_stack_resource_list, + post_stack_resource_list, + scale_type=scale_type) + + # FT-checkpoint: Grant Response + grant_mock_responses = vnflcm_base.FAKE_SERVER_MANAGER.get_history( + fake_grant.Grant.GRANT_REQ_PATH) + vnflcm_base.FAKE_SERVER_MANAGER.clear_history( + fake_grant.Grant.GRANT_REQ_PATH) + self.assertEqual(1, len(grant_mock_responses)) + self._assert_grant_mock_response(grant_mock_responses[0]) + def _assert_terminate_vnf(self, resp, vnf_instance_id, stack_id, resource_name_list, glance_image_id_list): """Assert that VNF was terminated. @@ -543,3 +698,14 @@ class VnfLcmWithNfvoSeparator(vnflcm_base.BaseVnfLcmTest): template = self._get_heat_stack_template(physical_resource_id[0]) template_count = str(template).count("flavor") self.assertEqual(template_count, 3) + + def _assert_stack_template_scale(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 + "-VDU1" + 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/conductor/test_conductor_server.py b/tacker/tests/unit/conductor/test_conductor_server.py index cd80dac5f..6bb59a4ab 100644 --- a/tacker/tests/unit/conductor/test_conductor_server.py +++ b/tacker/tests/unit/conductor/test_conductor_server.py @@ -1608,6 +1608,9 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase): @mock.patch('tacker.conductor.conductor_server.Conductor.' '_update_vnf_attributes_stack_param') + @mock.patch('tacker.vnflcm.utils' + '._build_instantiated_vnf_info') + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') @mock.patch('tacker.conductor.conductor_server.Conductor' '._change_vnf_status') @mock.patch('tacker.conductor.conductor_server.Conductor' @@ -1641,6 +1644,8 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase): mock_add_vnf_info, mock_update_vnf_info, mock_change_status, + mock_vnfd_dict, + mock_build_info, mock_update_vnf_attributes_stack_param): vnf_package_vnfd = self._create_and_upload_vnf_package() vnf_instance_data = fake_obj.get_vnf_instance_data( @@ -1660,6 +1665,9 @@ 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 + 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', @@ -1700,6 +1708,7 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase): @mock.patch('tacker.conductor.conductor_server.Conductor.' '_update_vnf_attributes_stack_param') + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') @mock.patch('tacker.conductor.conductor_server.Conductor' '._change_vnf_status') @mock.patch('tacker.conductor.conductor_server.Conductor' @@ -1736,6 +1745,7 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase): mock_add_vnf_info, mock_update_vnf_info, mock_change_status, + mock_vnfd_dict, mock_update_vnf_attribute_stack_param): vnf_package_vnfd = self._create_and_upload_vnf_package() vnf_instance_data = fake_obj.get_vnf_instance_data( @@ -1795,6 +1805,50 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase): vnf_dict = db_utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid, flavour='simple') vnf_dict['before_error_point'] = fields.ErrorPoint.INITIAL + vnfd_yaml = os.path.abspath(os.path.join(os.path.dirname(__file__), + '../../etc/samples/etsi/nfv/' + 'test_heal_grant_unit/' + 'sample_vnfd.yaml')) + with open(vnfd_yaml) as f: + mock_vnfd_dict.return_value = yaml.safe_load(f) + mock_vnfd_dict.return_value['imports'] = [] + etsi_common_file_path = os.path.abspath( + os.path.join(os.path.dirname(__file__), + '../../etc/samples/etsi/nfv/common/Definitions/' + 'etsi_nfv_sol001_common_types.yaml')) + etsi_vnfd_file_path = os.path.abspath( + os.path.join(os.path.dirname(__file__), + '../../etc/samples/etsi/nfv/common/Definitions/' + 'etsi_nfv_sol001_vnfd_types.yaml')) + etsi_vnfd_file_path_tmp = os.path.abspath( + os.path.join(os.path.dirname(__file__), + '../../etc/samples/etsi/nfv/common/Definitions/' + 'etsi_nfv_sol001_vnfd_types_tmp.yaml')) + with open(etsi_vnfd_file_path) as f: + data = yaml.safe_load(f) + del data['imports'] + data['imports'] = [] + data['imports'].append(etsi_common_file_path) + with open(etsi_vnfd_file_path_tmp, "w", encoding="utf-8") as f: + yaml.dump(data, f) + types_file_path = os.path.abspath( + os.path.join(os.path.dirname(__file__), + '../../etc/samples/etsi/nfv/test_heal_grant_unit/' + 'helloworld3_types.yaml')) + types_file_tmp_path = os.path.abspath( + os.path.join(os.path.dirname(__file__), + '../../etc/samples/etsi/nfv/test_heal_grant_unit/' + 'helloworld3_types_tmp.yaml')) + with open(types_file_path) as f: + data = yaml.safe_load(f) + data['imports'] = [] + data['imports'].append(etsi_common_file_path) + data['imports'].append(etsi_vnfd_file_path_tmp) + with open(types_file_tmp_path, "w", encoding="utf-8") as f: + yaml.dump(data, f) + mock_vnfd_dict.return_value['imports'].append(etsi_common_file_path) + mock_vnfd_dict.return_value['imports'].append(etsi_vnfd_file_path_tmp) + mock_vnfd_dict.return_value['imports'].append(types_file_tmp_path) vnf_lcm_op_occs_id = 'a9c36d21-21aa-4692-8922-7999bbcae08c' mock_exec.return_value = True mock_act.return_value = None @@ -1822,6 +1876,12 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase): resource = { 'resourceDefinitionId': 'faf14707-da7c-4eec-be99-8099fa1e9fa0'} resRemResource.append(resource) + resource = { + 'resourceDefinitionId': 'c91e0e86-e555-71fc-0f6b-e3d2945f2fd6'} + resRemResource.append(resource) + resource = { + 'resourceDefinitionId': '5d84a03c-07e9-4978-b0cd-6450482189b7'} + resRemResource.append(resource) resource = { 'resourceDefinitionId': '2c6e5cc7-240d-4458-a683-1fe648351281', 'vimConnectionId': uuidsentinel.vim_id, @@ -1866,7 +1926,12 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase): mock_update_vnf_attribute_stack_param.assert_called_once() self.vnflcm_driver.heal_vnf.assert_called_once_with( self.context, mock.ANY, vnf_dict, heal_vnf_req) + os.remove(types_file_tmp_path) + os.remove(etsi_vnfd_file_path_tmp) + @mock.patch('tacker.vnflcm.utils' + '._build_instantiated_vnf_info') + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') @mock.patch('tacker.conductor.conductor_server.Conductor' '.send_notification') @mock.patch('tacker.conductor.conductor_server.Conductor' @@ -1899,7 +1964,9 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase): mock_save, mock_add_vnf_info, mock_update_vnf_info, - mock_send): + mock_send, + mock_vnfd_dict, + mock_build_info): vnf_package_vnfd = self._create_and_upload_vnf_package() vnf_instance_data = fake_obj.get_vnf_instance_data( vnf_package_vnfd.vnfd_id) @@ -1915,6 +1982,9 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase): vnf_dict = db_utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid, flavour='simple') vnf_dict['before_error_point'] = fields.ErrorPoint.INITIAL + 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) vnf_lcm_op_occs_id = 'a9c36d21-21aa-4692-8922-7999bbcae08c' mock_exec.return_value = True mock_act.return_value = None @@ -1961,6 +2031,9 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase): mock_send.call_args[0][1].get('operationState'), 'ROLLED_BACK') + @mock.patch('tacker.vnflcm.utils' + '._build_instantiated_vnf_info') + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') @mock.patch('tacker.conductor.conductor_server.Conductor' '.send_notification') @mock.patch('tacker.conductor.conductor_server.Conductor' @@ -1993,7 +2066,9 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase): mock_save, mock_add_vnf_info, mock_update_vnf_info, - mock_send): + mock_send, + mock_vnfd_dict, + mock_build_info): vnf_package_vnfd = self._create_and_upload_vnf_package() vnf_instance_data = fake_obj.get_vnf_instance_data( vnf_package_vnfd.vnfd_id) @@ -2009,6 +2084,9 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase): vnf_dict = db_utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid, flavour='simple') vnf_dict['before_error_point'] = fields.ErrorPoint.INITIAL + 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) vnf_lcm_op_occs_id = 'a9c36d21-21aa-4692-8922-7999bbcae08c' mock_exec.return_value = True mock_act.return_value = None diff --git a/tacker/tests/unit/vnflcm/fakes.py b/tacker/tests/unit/vnflcm/fakes.py index 6a1810578..49580580a 100644 --- a/tacker/tests/unit/vnflcm/fakes.py +++ b/tacker/tests/unit/vnflcm/fakes.py @@ -165,6 +165,15 @@ def return_vnf_instance_model( return model_obj +def return_vnf_instance_delete( + instantiated_state=fields.VnfInstanceState.NOT_INSTANTIATED, + **updates): + data = _model_non_instantiated_vnf_instance(**updates) + data['instantiation_state'] = instantiated_state + vnf_instance_obj = objects.VnfInstance(**data) + return vnf_instance_obj + + def return_vnf_instance( instantiated_state=fields.VnfInstanceState.NOT_INSTANTIATED, scale_status=None, @@ -173,7 +182,15 @@ def return_vnf_instance( if instantiated_state == fields.VnfInstanceState.NOT_INSTANTIATED: data = _model_non_instantiated_vnf_instance(**updates) data['instantiation_state'] = instantiated_state + get_instantiated_vnf_info = { + 'flavour_id': uuidsentinel.flavour_id, + 'vnf_state': 'STARTED', + 'instance_id': '' + } + instantiated_vnf_info = get_instantiated_vnf_info + info_data = objects.InstantiatedVnfInfo(**instantiated_vnf_info) vnf_instance_obj = objects.VnfInstance(**data) + vnf_instance_obj.instantiated_vnf_info = info_data elif scale_status: data = _model_non_instantiated_vnf_instance(**updates) diff --git a/tacker/tests/unit/vnflcm/test_controller.py b/tacker/tests/unit/vnflcm/test_controller.py index 6b2621165..737ced86f 100644 --- a/tacker/tests/unit/vnflcm/test_controller.py +++ b/tacker/tests/unit/vnflcm/test_controller.py @@ -1729,7 +1729,7 @@ class TestController(base.TestCase): req = fake_request.HTTPRequest.blank( '/vnf_instances/%s' % uuidsentinel.vnf_instance_id) req.method = 'DELETE' - mock_vnf_by_id.return_value = fakes.return_vnf_instance() + mock_vnf_by_id.return_value = fakes.return_vnf_instance_delete() req.headers['Content-Type'] = 'application/json' # Call delete API diff --git a/tacker/tests/unit/vnflcm/test_load_vnf_interfaces.py b/tacker/tests/unit/vnflcm/test_load_vnf_interfaces.py index 6fe81047c..47c177d96 100644 --- a/tacker/tests/unit/vnflcm/test_load_vnf_interfaces.py +++ b/tacker/tests/unit/vnflcm/test_load_vnf_interfaces.py @@ -136,6 +136,7 @@ class MgmtVnfLcmDriverTest(db_base.SqlTestCase): 'test_project'}, 'vim_type': 'openstack'} self.vim_client.get_vim.return_value = vim_obj + @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', @@ -146,7 +147,7 @@ class MgmtVnfLcmDriverTest(db_base.SqlTestCase): def test_instantiate_vnf(self, mock_vnf_instance_save, mock_vnf_package_vnfd, mock_create, mock_get_service_plugins, mock_init_hash, - mock_final_vnf_dict): + mock_final_vnf_dict, mock_default_status): mock_init_hash.return_value = { "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" "b18d663b127100eb72b19eecd7ed51" @@ -159,6 +160,7 @@ class MgmtVnfLcmDriverTest(db_base.SqlTestCase): objects.InstantiateVnfRequest.obj_from_primitive( instantiate_vnf_req_dict, self.context) vnf_instance_obj = fakes.return_vnf_instance() + 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, @@ -173,7 +175,7 @@ class MgmtVnfLcmDriverTest(db_base.SqlTestCase): instantiate_vnf_req_obj) self.assertEqual(1, mock_vnf_instance_save.call_count) - self.assertEqual(5, self._vnf_manager.invoke.call_count) + self.assertEqual(6, self._vnf_manager.invoke.call_count) shutil.rmtree(fake_csar) @mock.patch('tacker.vnflcm.utils._make_final_vnf_dict') diff --git a/tacker/tests/unit/vnflcm/test_vnflcm_driver.py b/tacker/tests/unit/vnflcm/test_vnflcm_driver.py index 801523296..1ef961208 100644 --- a/tacker/tests/unit/vnflcm/test_vnflcm_driver.py +++ b/tacker/tests/unit/vnflcm/test_vnflcm_driver.py @@ -190,6 +190,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): 'test_project'}, 'vim_type': 'openstack'} self.vim_client.get_vim.return_value = vim_obj + @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') @@ -204,7 +205,8 @@ class TestVnflcmDriver(db_base.SqlTestCase): def test_instantiate_vnf( 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_get_service_plugins, mock_init_hash, mock_final_vnf_dict, + mock_default_status): mock_init_hash.return_value = { "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" "b18d663b127100eb72b19eecd7ed51" @@ -218,6 +220,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): objects.InstantiateVnfRequest.obj_from_primitive( instantiate_vnf_req_dict, self.context) vnf_instance_obj = fakes.return_vnf_instance() + 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, @@ -232,9 +235,10 @@ class TestVnflcmDriver(db_base.SqlTestCase): instantiate_vnf_req_obj) self.assertEqual(1, mock_vnf_instance_save.call_count) - self.assertEqual(5, self._vnf_manager.invoke.call_count) + 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') @@ -249,7 +253,8 @@ class TestVnflcmDriver(db_base.SqlTestCase): def test_instantiate_vnf_with_error_point_vnf_config_start( 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_get_service_plugins, mock_init_hash, mock_final_vnf_dict, + mock_default_status): mock_init_hash.return_value = { "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" "b18d663b127100eb72b19eecd7ed51" @@ -263,6 +268,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): objects.InstantiateVnfRequest.obj_from_primitive( instantiate_vnf_req_dict, self.context) vnf_instance_obj = fakes.return_vnf_instance() + 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, @@ -277,9 +283,10 @@ class TestVnflcmDriver(db_base.SqlTestCase): instantiate_vnf_req_obj) self.assertEqual(1, mock_vnf_instance_save.call_count) - self.assertEqual(5, self._vnf_manager.invoke.call_count) + 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') @@ -294,7 +301,8 @@ class TestVnflcmDriver(db_base.SqlTestCase): def test_instantiate_vnf_with_error_point_pre_vim_control( 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_get_service_plugins, mock_init_hash, mock_final_vnf_dict, + mock_default_status): mock_init_hash.return_value = { "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" "b18d663b127100eb72b19eecd7ed51" @@ -308,6 +316,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): objects.InstantiateVnfRequest.obj_from_primitive( instantiate_vnf_req_dict, self.context) vnf_instance_obj = fakes.return_vnf_instance() + 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, @@ -322,9 +331,10 @@ class TestVnflcmDriver(db_base.SqlTestCase): instantiate_vnf_req_obj) self.assertEqual(1, mock_vnf_instance_save.call_count) - self.assertEqual(4, self._vnf_manager.invoke.call_count) + self.assertEqual(5, 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') @@ -339,7 +349,8 @@ class TestVnflcmDriver(db_base.SqlTestCase): def test_instantiate_vnf_with_error_point_post_vim_control( 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_get_service_plugins, mock_init_hash, mock_final_vnf_dict, + mock_default_status): mock_init_hash.return_value = { "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" "b18d663b127100eb72b19eecd7ed51" @@ -353,6 +364,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): objects.InstantiateVnfRequest.obj_from_primitive( instantiate_vnf_req_dict, self.context) vnf_instance_obj = fakes.return_vnf_instance() + 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, @@ -367,9 +379,10 @@ class TestVnflcmDriver(db_base.SqlTestCase): instantiate_vnf_req_obj) self.assertEqual(1, mock_vnf_instance_save.call_count) - self.assertEqual(4, self._vnf_manager.invoke.call_count) + self.assertEqual(5, 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') @@ -384,7 +397,8 @@ class TestVnflcmDriver(db_base.SqlTestCase): def test_instantiate_vnf_with_error_point_internal_processing( 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_get_service_plugins, mock_init_hash, mock_final_vnf_dict, + mock_default_status): mock_init_hash.return_value = { "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" "b18d663b127100eb72b19eecd7ed51" @@ -398,6 +412,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): objects.InstantiateVnfRequest.obj_from_primitive( instantiate_vnf_req_dict, self.context) vnf_instance_obj = fakes.return_vnf_instance() + 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, @@ -415,6 +430,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): self.assertEqual(1, 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') @@ -429,7 +445,8 @@ class TestVnflcmDriver(db_base.SqlTestCase): def test_instantiate_vnf_with_error_point_vnf_config_end( 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_get_service_plugins, mock_init_hash, mock_final_vnf_dict, + mock_default_status): mock_init_hash.return_value = { "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" "b18d663b127100eb72b19eecd7ed51" @@ -443,6 +460,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): objects.InstantiateVnfRequest.obj_from_primitive( instantiate_vnf_req_dict, self.context) vnf_instance_obj = fakes.return_vnf_instance() + 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, @@ -460,6 +478,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): self.assertEqual(1, 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') @@ -474,7 +493,8 @@ class TestVnflcmDriver(db_base.SqlTestCase): def test_instantiate_vnf_with_ext_virtual_links( 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_get_service_plugins, mock_init_hash, mock_final_vnf_dict, + mock_default_status): mock_init_hash.return_value = { "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" "b18d663b127100eb72b19eecd7ed51" @@ -490,6 +510,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): objects.InstantiateVnfRequest.obj_from_primitive( instantiate_vnf_req_dict, self.context) vnf_instance_obj = fakes.return_vnf_instance() + 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, @@ -504,9 +525,10 @@ class TestVnflcmDriver(db_base.SqlTestCase): instantiate_vnf_req_obj) self.assertEqual(1, mock_vnf_instance_save.call_count) - self.assertEqual(5, self._vnf_manager.invoke.call_count) + 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') @@ -521,7 +543,8 @@ class TestVnflcmDriver(db_base.SqlTestCase): def test_instantiate_vnf_vim_connection_info( 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_get_service_plugins, mock_init_hash, mock_final_vnf_dict, + mock_default_status): mock_init_hash.return_value = { "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" "b18d663b127100eb72b19eecd7ed51" @@ -537,6 +560,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): objects.InstantiateVnfRequest.obj_from_primitive( instantiate_vnf_req_dict, self.context) vnf_instance_obj = fakes.return_vnf_instance() + 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, @@ -551,9 +575,10 @@ class TestVnflcmDriver(db_base.SqlTestCase): instantiate_vnf_req_obj) self.assertEqual(1, mock_vnf_instance_save.call_count) - self.assertEqual(5, self._vnf_manager.invoke.call_count) + 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') @@ -568,7 +593,8 @@ class TestVnflcmDriver(db_base.SqlTestCase): def test_instantiate_vnf_infra_fails_to_instantiate( 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_get_service_plugins, mock_init_hash, mock_final_vnf_dict, + mock_default_status): mock_init_hash.return_value = { "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" "b18d663b127100eb72b19eecd7ed51" @@ -584,6 +610,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): objects.InstantiateVnfRequest.obj_from_primitive( instantiate_vnf_req_dict, self.context) vnf_instance_obj = fakes.return_vnf_instance() + 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, @@ -610,6 +637,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): 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') @@ -624,7 +652,8 @@ class TestVnflcmDriver(db_base.SqlTestCase): def test_instantiate_vnf_infra_fails_to_wait_after_instantiate( 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_get_service_plugins, mock_init_hash, mock_final_vnf_dict, + mock_default_status): mock_init_hash.return_value = { "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" "b18d663b127100eb72b19eecd7ed51" @@ -640,6 +669,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): objects.InstantiateVnfRequest.obj_from_primitive( instantiate_vnf_req_dict, self.context) vnf_instance_obj = fakes.return_vnf_instance() + mock_default_status.return_value = None level = instantiate_vnf_req_obj.instantiation_level_id vnf_instance_obj.instantiated_vnf_info = objects.InstantiatedVnfInfo( flavour_id=instantiate_vnf_req_obj.flavour_id, @@ -674,6 +704,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): 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') @@ -689,7 +720,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): 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_final_vnf_dict, mock_default_status): mock_init_hash.return_value = { "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" "b18d663b127100eb72b19eecd7ed51" @@ -703,6 +734,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): objects.InstantiateVnfRequest.obj_from_primitive( instantiate_vnf_req_dict, self.context) vnf_instance_obj = fakes.return_vnf_instance() + 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, @@ -722,6 +754,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): mock_final_vnf_dict.assert_called_once() 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') @@ -737,7 +770,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): 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_final_vnf_dict, mock_default_status): mock_init_hash.return_value = { "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" "b18d663b127100eb72b19eecd7ed51" @@ -751,6 +784,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): objects.InstantiateVnfRequest.obj_from_primitive( instantiate_vnf_req_dict, self.context) vnf_instance_obj = fakes.return_vnf_instance() + 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, @@ -1227,7 +1261,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): self.assertEqual(1, mock_resource_create.call_count) # Invoke will be called 7 times, 3 for deleting the vnf # resources and 4 during instantiation. - self.assertEqual(8, self._vnf_manager.invoke.call_count) + self.assertEqual(9, self._vnf_manager.invoke.call_count) expected_msg = ("Request received for healing vnf '%s' " "is completed successfully") mock_log.info.assert_called_with(expected_msg, diff --git a/tacker/tests/unit/vnfm/infra_drivers/kubernetes/test_kubernetes_driver.py b/tacker/tests/unit/vnfm/infra_drivers/kubernetes/test_kubernetes_driver.py index d055dcc10..48e688b74 100644 --- a/tacker/tests/unit/vnfm/infra_drivers/kubernetes/test_kubernetes_driver.py +++ b/tacker/tests/unit/vnfm/infra_drivers/kubernetes/test_kubernetes_driver.py @@ -2745,7 +2745,8 @@ class TestKubernetes(base.TestCase): context=self.context, vnf_instance=self.vnf_instance, scale_vnf_request=scale_vnf_req, - vim_connection_info=fakes.fake_vim_connection_info()) + vim_connection_info=fakes.fake_vim_connection_info(), + vnf_info=None) self.assertEqual(mock_list_namespaced_pod.call_count, 1) # validate added VnfcResourceInfo vnfc_resource_info_after = \ @@ -2798,7 +2799,8 @@ class TestKubernetes(base.TestCase): context=self.context, vnf_instance=self.vnf_instance, scale_vnf_request=scale_vnf_req, - vim_connection_info=fakes.fake_vim_connection_info()) + vim_connection_info=fakes.fake_vim_connection_info(), + vnf_info=None) self.assertEqual(mock_list_namespaced_pod.call_count, 1) # validate VnfcResourceInfo vnfc_resource_info_after = \ @@ -2844,7 +2846,7 @@ class TestKubernetes(base.TestCase): self.assertRaises(client.rest.ApiException, self.kubernetes.scale_resource_update, self.context, self.vnf_instance, - scale_vnf_req, + scale_vnf_req, None, fakes.fake_vim_connection_info()) @mock.patch.object(client.CoreV1Api, 'list_namespaced_pod') diff --git a/tacker/tests/unit/vnfm/infra_drivers/openstack/data/hot_scale_grant.yaml b/tacker/tests/unit/vnfm/infra_drivers/openstack/data/hot_scale_grant.yaml index 084b69b9e..e41a3d687 100644 --- a/tacker/tests/unit/vnfm/infra_drivers/openstack/data/hot_scale_grant.yaml +++ b/tacker/tests/unit/vnfm/infra_drivers/openstack/data/hot_scale_grant.yaml @@ -12,7 +12,7 @@ resources: auto_scaling_group_id: {get_resource: SP1_group} adjustment_type: change_in_capacity scaling_adjustment: 1 - SP1_group: + SP1: type: OS::Heat::AutoScalingGroup properties: min_size: 0 @@ -25,7 +25,7 @@ resources: SP1_scale_in: type: OS::Heat::ScalingPolicy properties: - auto_scaling_group_id: {get_resource: SP1_group} + auto_scaling_group_id: {get_resource: SP1} adjustment_type: change_in_capacity scaling_adjustment: -1 outputs: {} diff --git a/tacker/tests/unit/vnfm/infra_drivers/openstack/fixture_data/fixture_data_utils.py b/tacker/tests/unit/vnfm/infra_drivers/openstack/fixture_data/fixture_data_utils.py index 7e523a022..e77078547 100644 --- a/tacker/tests/unit/vnfm/infra_drivers/openstack/fixture_data/fixture_data_utils.py +++ b/tacker/tests/unit/vnfm/infra_drivers/openstack/fixture_data/fixture_data_utils.py @@ -572,3 +572,50 @@ def get_lcm_op_occs_object(operation="INSTANTIATE", error_point=error_point) return vnf_lcm_op_occs + + +def get_vnfc_resource_info_with_vnf_info(vdu_id="workerNode", + storage_resource_ids=None): + storage_resource_ids = storage_resource_ids or [] + + vnfc_cp_info = _get_vnfc_cp_info_with_vnf_info(cpd_id='workerNode_CP2') + + vnfc_resource_info = objects.VnfcResourceInfo( + id=uuidsentinel.vnfc_resource_id, vdu_id=vdu_id, + vnfc_cp_info=[vnfc_cp_info], + storage_resource_ids=storage_resource_ids) + + return vnfc_resource_info + + +def _get_vnfc_cp_info_with_vnf_info(cpd_id="CP1"): + vnfc_cp_info = objects.VnfcCpInfo( + id=uuidsentinel.vnfc_cp_info_id, + cpd_id=cpd_id) + + return vnfc_cp_info + + +def get_virtual_storage_resource_info_for_grant(desc_id="VirtualStorage"): + + storage_resource_info = objects.VirtualStorageResourceInfo( + id=uuidsentinel.storage_id_1, + virtual_storage_desc_id=desc_id) + + return storage_resource_info + + +def _make_add_resources(req_add_resources): + add_resources = [] + for req_add_resource in req_add_resources: + res_add_resource = { + "resourceDefinitionId": req_add_resource['id'], + "vimConnectionId": uuidsentinel.vim_connection_id + } + + if req_add_resource['type'] == 'COMPUTE': + res_add_resource["zoneId"] = uuidsentinel.zone_id + + add_resources.append(res_add_resource) + + return add_resources 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 24d61deed..90aba0e37 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 @@ -2472,6 +2472,7 @@ class TestOpenStack(base.FixturedTestCase): vnf_info['attributes']['SP1_res.yaml'] = utils.\ get_dummy_scale_nest_grant_hot() self.openstack.get_grant_resource( + self, vnf_instance, vnf_info, scale_vnf_request, @@ -2547,6 +2548,7 @@ class TestOpenStack(base.FixturedTestCase): res_list.append(resource4) mock_list.return_value = res_list self.openstack.get_grant_resource( + self, vnf_instance, vnf_info, scale_vnf_request, @@ -3031,3 +3033,221 @@ class TestOpenStack(base.FixturedTestCase): vnf_instance.instantiated_vnf_info. vnf_virtual_link_resource_info[0].vnf_link_ports[0]. resource_handle.resource_id) + + @mock.patch.object(hc.HeatClient, "resource_get") + @mock.patch.object(hc.HeatClient, "resource_get_list") + def test_scale_resource_update_scale_out_with_grant( + self, mock_list, mock_resource): + inst_vnf_info = fd_utils.get_vnf_instantiated_info() + vnf_instance = fd_utils.get_vnf_instance_object( + instantiated_vnf_info=inst_vnf_info) + scale_vnf_request = objects.ScaleVnfRequest(type='SCALE_OUT', + aspect_id='worker_instance', + number_of_steps=1) + vim_connection_info = fd_utils.get_vim_connection_info_object() + vnf_info = {} + v_s_resource_info = fd_utils.\ + get_virtual_storage_resource_info_for_grant(desc_id="storage1") + storage_resource_ids = [v_s_resource_info.id] + vnfc_resource_info = fd_utils.get_vnfc_resource_info_with_vnf_info( + vdu_id="workerNode", storage_resource_ids=storage_resource_ids) + vnfc_resource_info_list = [] + vnfc_resource_info_list.append(vnfc_resource_info) + virtual_st_rsc_list = [] + virtual_st_rsc_list.append(v_s_resource_info) + vnf_info['scale_out_vnfc_res_info_list'] = vnfc_resource_info_list + vnf_info['scale_out_virtual_st_rsc_list'] = virtual_st_rsc_list + resource = resources.Resource(None, { + 'resource_name': 'worker_instance', + 'creation_time': '2020-01-01T00:00:00', + 'resource_status': 'CREATE_COMPLETE', + 'physical_resource_id': '30435eb8-1472-4cbc-abbe-00b395165ce7', + 'id': '1111' + }) + resource1 = resources.Resource(None, { + 'resource_name': 'workerNode', + 'creation_time': '2020-01-01T00:00:00', + 'resource_status': 'CREATE_COMPLETE', + 'physical_resource_id': '30435eb8-1472-4cbc-abbe-00b395165ce7', + 'id': '1111', + 'attributes': {'key': 'value'} + }) + resource_cp = resources.Resource(None, { + 'resource_name': 'workerNode_CP2', + 'creation_time': '2020-01-01T00:00:00', + 'resource_status': 'CREATE_COMPLETE', + 'physical_resource_id': '30435eb8-1472-4cbc-abbe-00b395165ce7', + 'id': '1111', + 'required_by': 'workerNode', + 'attributes': {'fixed_ips': [{'ip_address': '10.10.0.2'}]} + }) + resource_st = resources.Resource(None, { + 'resource_name': 'storage1', + 'creation_time': '2020-01-01T00:00:00', + 'resource_status': 'CREATE_COMPLETE', + 'physical_resource_id': '30435eb8-1472-4cbc-abbe-00b395165ce7', + 'id': '1111', + 'required_by': 'workerNode' + }) + mock_resource.side_effect = [resource, resource1, resource_cp, + resource_st] + res_list1 = [] + res_list2 = [] + resource2 = resources.Resource(None, { + 'resource_name': 'aaaaaaaa', + 'creation_time': '2020-01-01T00:00:00', + 'resource_status': 'CREATE_COMPLETE', + 'physical_resource_id': '89bd4a61-d33c-cbdd-0329-6429a6083139', + 'id': '1111' + }) + res_list1.append(resource2) + resource3 = resources.Resource(None, { + 'resource_name': 'workerNode', + 'creation_time': '2020-01-01T00:00:00', + 'resource_type': 'OS::Nova::Server', + 'resource_status': 'CREATE_COMPLETE', + 'physical_resource_id': '5a169308-7865-9296-5a4b-a39c743dbe6e', + 'id': '1111' + }) + res_list2.append(resource3) + resource4 = resources.Resource(None, { + 'resource_name': 'workerNode_CP2', + 'creation_time': '2020-01-01T00:00:00', + 'resource_type': 'OS::Neutron::Port', + 'resource_status': 'CREATE_COMPLETE', + 'physical_resource_id': '5a169308-7865-9296-5a4b-a39c743dbe6e', + 'id': '1111' + }) + res_list2.append(resource4) + resource5 = resources.Resource(None, { + 'resource_name': 'storage1', + 'creation_time': '2020-01-01T00:00:00', + 'resource_type': 'OS::Cinder::Volume', + 'resource_status': 'CREATE_COMPLETE', + 'physical_resource_id': '5a169308-7865-9296-5a4b-a39c743dbe6e', + 'id': '1111' + }) + res_list2.append(resource5) + mock_list.side_effect = [res_list1, res_list2] + self.openstack.scale_resource_update( + context, vnf_instance, + scale_vnf_request, vnf_info, + vim_connection_info) + self.assertEqual( + vnf_instance.instantiated_vnf_info.vnfc_resource_info[0].id, + uuidsentinel.vnfc_resource_id) + return_vnfc_res = \ + vnf_instance.instantiated_vnf_info.vnfc_resource_info[0] + self.assertEqual(return_vnfc_res.vnfc_cp_info[0].id, + uuidsentinel.vnfc_cp_info_id) + self.assertEqual(uuidsentinel.storage_id_1, + vnf_instance.instantiated_vnf_info. + virtual_storage_resource_info[0].id) + + @mock.patch.object(hc.HeatClient, "resource_get") + @mock.patch.object(hc.HeatClient, "resource_get_list") + def test_scale_resource_update_scale_out(self, mock_list, mock_resource): + inst_vnf_info = fd_utils.get_vnf_instantiated_info() + vnf_instance = fd_utils.get_vnf_instance_object( + instantiated_vnf_info=inst_vnf_info) + scale_vnf_request = objects.ScaleVnfRequest(type='SCALE_OUT', + aspect_id='worker_instance', + number_of_steps=1) + vim_connection_info = fd_utils.get_vim_connection_info_object() + v_s_resource_info = fd_utils. \ + get_virtual_storage_resource_info(desc_id="storage1") + storage_resource_ids = [v_s_resource_info.id] + vnfc_resource_info = fd_utils.get_vnfc_resource_info_with_vnf_info( + vdu_id="workerNode", storage_resource_ids=storage_resource_ids) + vnfc_resource_info_list = [] + vnfc_resource_info_list.append(vnfc_resource_info) + virtual_st_rsc_list = [] + virtual_st_rsc_list.append(v_s_resource_info) + + vnf_info = {'grant': 'test'} + resource = resources.Resource(None, { + 'resource_name': 'worker_instance', + 'creation_time': '2020-01-01T00:00:00', + 'resource_status': 'CREATE_COMPLETE', + 'physical_resource_id': '30435eb8-1472-4cbc-abbe-00b395165ce7', + 'id': '1111' + }) + resource1 = resources.Resource(None, { + 'resource_name': 'workerNode', + 'creation_time': '2020-01-01T00:00:00', + 'resource_status': 'CREATE_COMPLETE', + 'physical_resource_id': '30435eb8-1472-4cbc-abbe-00b395165ce7', + 'id': '1111', + 'attributes': {'key': 'value'} + }) + resource_cp = resources.Resource(None, { + 'resource_name': 'workerNode_CP2', + 'creation_time': '2020-01-01T00:00:00', + 'resource_status': 'CREATE_COMPLETE', + 'physical_resource_id': '30435eb8-1472-4cbc-abbe-00b395165ce7', + 'id': '1111', + 'required_by': 'workerNode', + 'attributes': {'fixed_ips': [{'ip_address': '10.10.0.2'}]} + }) + resource_st = resources.Resource(None, { + 'resource_name': 'storage1', + 'creation_time': '2020-01-01T00:00:00', + 'resource_status': 'CREATE_COMPLETE', + 'physical_resource_id': '30435eb8-1472-4cbc-abbe-00b395165ce7', + 'id': '1111', + 'required_by': 'workerNode' + }) + mock_resource.side_effect = [resource, resource1, resource_cp, + resource_st] + res_list1 = [] + res_list2 = [] + resource2 = resources.Resource(None, { + 'resource_name': 'aaaaaaaa', + 'creation_time': '2020-01-01T00:00:00', + 'resource_status': 'CREATE_COMPLETE', + 'physical_resource_id': '89bd4a61-d33c-cbdd-0329-6429a6083139', + 'id': '1111' + }) + res_list1.append(resource2) + resource3 = resources.Resource(None, { + 'resource_name': 'workerNode', + 'creation_time': '2020-01-01T00:00:00', + 'resource_type': 'OS::Nova::Server', + 'resource_status': 'CREATE_COMPLETE', + 'physical_resource_id': '5a169308-7865-9296-5a4b-a39c743dbe6e', + 'id': '1111' + }) + res_list2.append(resource3) + resource4 = resources.Resource(None, { + 'resource_name': 'workerNode_CP2', + 'creation_time': '2020-01-01T00:00:00', + 'resource_type': 'OS::Neutron::Port', + 'resource_status': 'CREATE_COMPLETE', + 'physical_resource_id': '5a169308-7865-9296-5a4b-a39c743dbe6e', + 'id': '1111' + }) + res_list2.append(resource4) + resource5 = resources.Resource(None, { + 'resource_name': 'storage1', + 'creation_time': '2020-01-01T00:00:00', + 'resource_type': 'OS::Cinder::Volume', + 'resource_status': 'CREATE_COMPLETE', + 'physical_resource_id': '5a169308-7865-9296-5a4b-a39c743dbe6e', + 'id': '1111' + }) + res_list2.append(resource5) + mock_list.side_effect = [res_list1, res_list2] + self.openstack.scale_resource_update( + context, vnf_instance, + scale_vnf_request, vnf_info, + vim_connection_info) + self.assertNotEqual( + vnf_instance.instantiated_vnf_info.vnfc_resource_info[0].id, + uuidsentinel.vnfc_resource_id) + return_vnfc_res = \ + vnf_instance.instantiated_vnf_info.vnfc_resource_info[0] + self.assertNotEqual(return_vnfc_res.vnfc_cp_info[0].id, + uuidsentinel.vnfc_cp_info_id) + self.assertNotEqual(uuidsentinel.storage_id, + vnf_instance.instantiated_vnf_info. + virtual_storage_resource_info[0].id) diff --git a/tacker/vnflcm/vnflcm_driver.py b/tacker/vnflcm/vnflcm_driver.py index df2da368e..8b526b530 100644 --- a/tacker/vnflcm/vnflcm_driver.py +++ b/tacker/vnflcm/vnflcm_driver.py @@ -443,6 +443,13 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver): id=vnf_instance.id, error=encodeutils.exception_to_unicode(exp)) + if vnf_instance.instantiated_vnf_info.instance_id: + self._vnf_manager.invoke(vim_connection_info.vim_type, + 'post_vnf_instantiation', context=context, + vnf_instance=vnf_instance, + vim_connection_info=vim_connection_info, + instantiate_vnf_req=instantiate_vnf_req) + def _get_file_hash(self, path): hash_obj = hashlib.sha256() with open(path) as f: @@ -746,9 +753,13 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver): vnf_instance.task_state = fields.VnfInstanceTaskState.INSTANTIATING vnfd_dict = vnflcm_utils._get_vnfd_dict( context, vnf_instance.vnfd_id, instantiate_vnf_request.flavour_id) - vnflcm_utils._build_instantiated_vnf_info( - vnfd_dict, instantiate_vnf_request, vnf_instance, - vim_connection_info.vim_id) + if vnf_dict.get('vnf_instance_after'): + vnf_instance.instantiated_vnf_info = \ + vnf_dict.get('vnf_instance_after').instantiated_vnf_info + else: + vnflcm_utils._build_instantiated_vnf_info( + vnfd_dict, instantiate_vnf_request, vnf_instance, + vim_connection_info.vim_id) try: self._instantiate_vnf(context, vnf_instance, vnf_dict, @@ -1022,6 +1033,7 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver): 'scale_resource_update', context=context, vnf_instance=vnf_instance, + vnf_info=vnf_info, scale_vnf_request=scale_vnf_request, vim_connection_info=vim_connection_info ) diff --git a/tacker/vnfm/infra_drivers/kubernetes/kubernetes_driver.py b/tacker/vnfm/infra_drivers/kubernetes/kubernetes_driver.py index 338ab6dad..d98d0fba7 100644 --- a/tacker/vnfm/infra_drivers/kubernetes/kubernetes_driver.py +++ b/tacker/vnfm/infra_drivers/kubernetes/kubernetes_driver.py @@ -2069,7 +2069,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, return return_id_list, return_name_list, return_grp_id, return_res_num def scale_resource_update(self, context, vnf_instance, - scale_vnf_request, + scale_vnf_request, vnf_info, vim_connection_info): """Update VnfcResourceInfo after scaling""" auth_attr = vim_connection_info.access_info @@ -2238,6 +2238,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, pass def get_grant_resource(self, + plugin, vnf_instance, vnf_info, scale_vnf_request, diff --git a/tacker/vnfm/infra_drivers/openstack/openstack.py b/tacker/vnfm/infra_drivers/openstack/openstack.py index a52412833..e12e39a48 100644 --- a/tacker/vnfm/infra_drivers/openstack/openstack.py +++ b/tacker/vnfm/infra_drivers/openstack/openstack.py @@ -1577,13 +1577,37 @@ class OpenStack(abstract_driver.VnfAbstractDriver, return return_list, return_rs_list, grp.physical_resource_id, cap_size + def _get_resource_info_from_grant(self, grant_res_info_list, rsc, + vnfc_resource=None): + for i in range(len(grant_res_info_list) - 1, -1, -1): + if rsc.resource_type == 'OS::Nova::Server': + if grant_res_info_list[i].vdu_id == rsc.resource_name: + instantiated_info = grant_res_info_list.pop(i) + break + elif rsc.resource_type == 'OS::Neutron::Port': + if grant_res_info_list[i].cpd_id == rsc.resource_name: + instantiated_info = \ + vnfc_resource.vnfc_cp_info.pop(i) + break + elif rsc.resource_type == 'OS::Cinder::Volume': + if grant_res_info_list[i].virtual_storage_desc_id ==\ + rsc.resource_name: + instantiated_info = grant_res_info_list.pop(i) + break + + return instantiated_info + @log.log def scale_resource_update(self, context, vnf_instance, - scale_vnf_request, + scale_vnf_request, vnf_info, vim_connection_info): inst_vnf_info = vnf_instance.instantiated_vnf_info vnfc_rsc_list = [] st_rsc_list = [] + grant_vnfc_res_info_list = \ + vnf_info.get('scale_out_vnfc_res_info_list', {}) + grant_st_rsc_list = vnf_info.get( + 'scale_out_virtual_st_rsc_list', {}) for vnfc in vnf_instance.instantiated_vnf_info.vnfc_resource_info: vnfc_rsc_list.append(vnfc.compute_resource.resource_id) for st in vnf_instance.instantiated_vnf_info.\ @@ -1613,10 +1637,20 @@ class OpenStack(abstract_driver.VnfAbstractDriver, LOG.debug("rsc %s", rsc_info) if 'COMPLETE' in rsc.resource_status and '\ INIT_COMPLETE' != rsc.resource_status: - vnfc_resource_info = objects.VnfcResourceInfo() - vnfc_resource_info.id =\ - uuidutils.generate_uuid() - vnfc_resource_info.vdu_id = rsc.resource_name + if grant_vnfc_res_info_list: + vnfc_resource_info = \ + self._get_resource_info_from_grant( + grant_vnfc_res_info_list, rsc) + else: + vnfc_resource_info = \ + objects.VnfcResourceInfo() + vnfc_resource_info.id =\ + uuidutils.generate_uuid() + vnfc_resource_info.vdu_id = \ + rsc.resource_name + vnfc_resource_info.vnfc_cp_info = [] + vnfc_resource_info.\ + storage_resource_ids = [] resource = objects.ResourceHandle() resource.vim_connection_id =\ vim_connection_info.id @@ -1628,12 +1662,10 @@ class OpenStack(abstract_driver.VnfAbstractDriver, vnfc_resource_info.metadata.update( {"stack_id": scale_rsc.physical_resource_id}) - vnfc_resource_info.vnfc_cp_info = [] volumes_attached = rsc_info.attributes.get( 'os-extended-volumes:volumes_attached') if not volumes_attached: volumes_attached = [] - vnfc_resource_info.storage_resource_ids = [] for vol in volumes_attached: vnfc_resource_info.\ storage_resource_ids.\ @@ -1652,9 +1684,16 @@ class OpenStack(abstract_driver.VnfAbstractDriver, LOG.debug("rsc %s", rsc_info) for vnfc_rsc in vnfc_rscs: if vnfc_rsc.vdu_id in rsc_info.required_by: - vnfc_cp = objects.VnfcCpInfo() - vnfc_cp.id = uuidutils.generate_uuid() - vnfc_cp.cpd_id = rsc.resource_name + if vnfc_rsc.vnfc_cp_info and \ + grant_vnfc_res_info_list: + vnfc_cp = self.\ + _get_resource_info_from_grant( + vnfc_rsc.vnfc_cp_info, + rsc, vnfc_rsc) + else: + vnfc_cp = objects.VnfcCpInfo() + vnfc_cp.id = uuidutils.generate_uuid() + vnfc_cp.cpd_id = rsc.resource_name vnfc_cp.cp_protocol_info = [] cp_protocol_info = objects.CpProtocolInfo() @@ -1722,12 +1761,18 @@ class OpenStack(abstract_driver.VnfAbstractDriver, vnfc_rsc.vnfc_cp_info.append(vnfc_cp) if rsc.resource_type == 'OS::Cinder::Volume': if rsc.physical_resource_id not in st_rsc_list: - virtual_storage_resource_info =\ - objects.VirtualStorageResourceInfo() - virtual_storage_resource_info.id =\ - uuidutils.generate_uuid() - virtual_storage_resource_info.\ - virtual_storage_desc_id = rsc.resource_name + if grant_st_rsc_list: + virtual_storage_resource_info = \ + self._get_resource_info_from_grant( + grant_st_rsc_list, rsc) + else: + virtual_storage_resource_info =\ + objects.VirtualStorageResourceInfo() + virtual_storage_resource_info.id =\ + uuidutils.generate_uuid() + virtual_storage_resource_info.\ + virtual_storage_desc_id = \ + rsc.resource_name resource = objects.ResourceHandle() resource.vim_connection_id =\ vim_connection_info.id @@ -1902,6 +1947,7 @@ class OpenStack(abstract_driver.VnfAbstractDriver, def get_grant_resource( self, + plugin, vnf_instance, vnf_info, scale_vnf_request, @@ -1927,23 +1973,34 @@ class OpenStack(abstract_driver.VnfAbstractDriver, affinity_list = [] placement_constraint_list = [] uuid_list = [] - port_uuid_list = [] - storage_uuid_list = [] + vnfc_res_info_list = [] + virtual_st_rsc_list = [] heat_template = vnf_info['attributes']['heat_template'] heat_resource = yaml.safe_load(heat_template) key_vnfd = scale_vnf_request.aspect_id + '_scale_out' ajust_prop = heat_resource['resources'][key_vnfd]['properties'] ajust = ajust_prop['scaling_adjustment'] size = ajust * scale_vnf_request.number_of_steps - yaml_name = scale_vnf_request.aspect_id + '_res.yaml' - if not vnf_info['attributes'].get(yaml_name): - yaml_name = scale_vnf_request.aspect_id + '.hot.yaml' + scale_resource_name = scale_vnf_request.aspect_id + scale_resource = heat_resource['resources'].\ + get(scale_resource_name, {}) + if not scale_resource: + scale_resource_name = scale_vnf_request.aspect_id + scale_resource = heat_resource['resources'].\ + get(scale_resource_name) + yaml_name = scale_resource['properties']['resource']['type'] nested_hot = yaml.safe_load( vnf_info['attributes'][yaml_name]) for resource_name, resource in nested_hot['resources'].items(): if resource['type'] == NOVA_SERVER_RESOURCE: for i in range(size): - add_uuid = uuidutils.generate_uuid() + vnfc_resource_info = objects.VnfcResourceInfo() + vnfc_resource_info.id = uuidutils.generate_uuid() + vnfc_resource_info.vdu_id = resource_name + vnfc_resource_info.vnfc_cp_info = [] + vnfc_resource_info.storage_resource_ids = [] + + add_uuid = vnfc_resource_info.id rsc = objects.ResourceDefinition( id=add_uuid, type=constants.TYPE_COMPUTE, @@ -1951,8 +2008,14 @@ class OpenStack(abstract_driver.VnfAbstractDriver, resource_template_id=resource_name) add_resources.append(rsc) uuid_list.append(add_uuid) - for net in resource.get('networks', []): - add_uuid = uuidutils.generate_uuid() + + for net in resource['properties'].get('networks', []): + vnfc_cp = objects.VnfcCpInfo() + vnfc_cp.id = uuidutils.generate_uuid() + vnfc_cp.cpd_id = net['port']['get_resource'] + vnfc_resource_info.vnfc_cp_info.append(vnfc_cp) + + add_uuid = vnfc_cp.id port_rsc = net['port']['get_resource'] rsc = objects.ResourceDefinition( id=add_uuid, @@ -1960,12 +2023,24 @@ class OpenStack(abstract_driver.VnfAbstractDriver, vdu_id=resource_name, resource_template_id=port_rsc) add_resources.append(rsc) - port_uuid_list.append(add_uuid) - if resource['properties'].get('block_device_mapping_v2'): - for i in range(size): + + if resource['properties'].get('block_device_mapping_v2'): for cinder in resource['properties'].get( 'block_device_mapping_v2', []): - add_uuid = uuidutils.generate_uuid() + virtual_storage_resource_info = \ + objects.VirtualStorageResourceInfo() + virtual_storage_resource_info.id = \ + uuidutils.generate_uuid() + virtual_storage_resource_info.\ + virtual_storage_desc_id = \ + cinder['volume_id']['get_resource'] + virtual_st_rsc_list = \ + virtual_st_rsc_list.append( + virtual_storage_resource_info) + vnfc_resource_info.storage_resource_ids.\ + append(virtual_storage_resource_info.id) + + add_uuid = virtual_storage_resource_info.id vol_rsc = cinder['volume_id']['get_resource'] rsc = objects.ResourceDefinition( id=add_uuid, @@ -1973,7 +2048,8 @@ class OpenStack(abstract_driver.VnfAbstractDriver, vdu_id=resource_name, resource_template_id=vol_rsc) add_resources.append(rsc) - storage_uuid_list.append(add_uuid) + vnfc_res_info_list.append(vnfc_resource_info) + if resource['properties'].get('scheduler_hints'): sch_hint = resource['properties']['scheduler_hints'] if sch_hint['group'].get('get_param'): @@ -2000,10 +2076,8 @@ class OpenStack(abstract_driver.VnfAbstractDriver, vdu_id=vol_rsc['get_resource'], resource_template_id=resource_name) add_resources.append(rsc) - storage_uuid_list.append(add_uuid) - vnf_info['uuid_list'] = uuid_list - vnf_info['port_uuid_list'] = port_uuid_list - vnf_info['storage_uuid_list'] = storage_uuid_list + vnf_info['scale_out_vnfc_res_info_list'] = vnfc_res_info_list + vnf_info['scale_out_virtual_st_rsc_list'] = virtual_st_rsc_list for placement in placement_obj_list: if placement.server_group_name in affinity_list: plm = jsonutils.loads(placement.resource) @@ -2015,8 +2089,10 @@ class OpenStack(abstract_driver.VnfAbstractDriver, id_type=pl['id_type'], resource_id=pl['resource_id'], vim_connection_id=vim_id)) + affinity_or_anti_affinity = \ + placement.affinity_or_anti_affinity placement_constraint = objects.PlacementConstraint( - affinity_or_anti_affinity='ANTI_AFFINITY', + affinity_or_anti_affinity=affinity_or_anti_affinity, scope='ZONE', resource=addRsc, fallback_best_effort=True) diff --git a/tacker/vnfm/infra_drivers/scale_driver.py b/tacker/vnfm/infra_drivers/scale_driver.py index fb8e7a315..882d92b2e 100644 --- a/tacker/vnfm/infra_drivers/scale_driver.py +++ b/tacker/vnfm/infra_drivers/scale_driver.py @@ -61,7 +61,7 @@ class VnfScaleAbstractDriver(extensions.PluginInterface, @abc.abstractmethod def scale_resource_update(self, context, vnf_instance, - scale_vnf_request, + scale_vnf_request, vnf_info, vim_connection_info): pass @@ -103,6 +103,7 @@ class VnfScaleAbstractDriver(extensions.PluginInterface, @abc.abstractmethod def get_grant_resource(self, + plugin, vnf_instance, vnf_info, scale_vnf_request,