diff --git a/.zuul.yaml b/.zuul.yaml index 86664f637..6362a5901 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -175,6 +175,15 @@ TACKER_HOST: "{{ hostvars['controller-tacker']['nodepool']['private_ipv4'] }}" TACKER_MODE: standalone IS_ZUUL_FT: True + # Since a VirtualInterfaceCreateException occurs during a test, + # the setting of network-vif-plugged is changed by the reference of + # the following URL. + # https://bugs.launchpad.net/heat/+bug/1694371 + devstack_local_conf: + post-config: + $NOVA_CONF: + DEFAULT: + vif_plugging_is_fatal: False devstack_services: q-agt: true n-api: false diff --git a/tacker/tests/etc/samples/etsi/nfv/functional/UserData/lcm_user_data.py b/tacker/tests/etc/samples/etsi/nfv/functional/UserData/lcm_user_data.py index 7efbf0c41..725c5430a 100644 --- a/tacker/tests/etc/samples/etsi/nfv/functional/UserData/lcm_user_data.py +++ b/tacker/tests/etc/samples/etsi/nfv/functional/UserData/lcm_user_data.py @@ -1,35 +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} +# +# 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/functional2/BaseHOT/simple/helloworld3.yaml b/tacker/tests/etc/samples/etsi/nfv/functional2/BaseHOT/simple/helloworld3.yaml new file mode 100644 index 000000000..67e9bf189 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/functional2/BaseHOT/simple/helloworld3.yaml @@ -0,0 +1,98 @@ +heat_template_version: 2013-05-23 +description: 'Simple Base HOT for Sample VNF' + +parameters: + nfv: + type: json + +resources: + VDU1: + 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, VirtualStorage, 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 } + VDU1_scale_out: + type: OS::Heat::ScalingPolicy + properties: + scaling_adjustment: 1 + auto_scaling_group_id: + get_resource: VDU1 + adjustment_type: change_in_capacity + VDU1_scale_in: + type: OS::Heat::ScalingPolicy + properties: + scaling_adjustment: -1 + auto_scaling_group_id: + get_resource: VDU1 + adjustment_type: change_in_capacity + VDU2: + type: OS::Heat::AutoScalingGroup + properties: + min_size: 2 + max_size: 2 + desired_capacity: 2 + resource: + type: VDU2.yaml + properties: + flavor: { get_param: [ nfv, VDU, VDU2, flavor ] } + image: { get_param: [ nfv, VDU, VDU2, image ] } + zone: { get_param: [ nfv, vdu, VDU2, zone ] } + net1: { get_param: [ nfv, CP, VDU2_CP1, network ] } + net2: { get_param: [ nfv, CP, VDU2_CP2, network ] } + net3: { get_resource: extmanageNW_1 } + net4: { get_resource: extmanageNW_2 } + net5: { get_resource: internalNW_1 } + VDU2_scale_out: + type: OS::Heat::ScalingPolicy + properties: + scaling_adjustment: 1 + auto_scaling_group_id: + get_resource: VDU2 + adjustment_type: change_in_capacity + VDU2_scale_in: + type: OS::Heat::ScalingPolicy + properties: + scaling_adjustment: -1 + auto_scaling_group_id: + get_resource: VDU2 + 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/functional2/BaseHOT/simple/nested/VDU1.yaml b/tacker/tests/etc/samples/etsi/nfv/functional2/BaseHOT/simple/nested/VDU1.yaml new file mode 100644 index 000000000..60fc60313 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/functional2/BaseHOT/simple/nested/VDU1.yaml @@ -0,0 +1,72 @@ +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 + +resources: + VDU1: + type: OS::Nova::Server + properties: + flavor: { get_param: flavor } + name: VDU1 + block_device_mapping_v2: [{"volume_id": { get_resource: VirtualStorage }}] + 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 } + + VirtualStorage: + type: OS::Cinder::Volume + properties: + image: { get_param: image } + size: 1 + volume_type: { get_resource: multi } + multi: + type: OS::Cinder::VolumeType + properties: + name: { get_resource: VDU1_CP1 } + metadata: { multiattach: " True" } + VDU1_CP1: + type: OS::Neutron::Port + properties: + network: { get_param: net1 } + VDU1_CP2: + type: OS::Neutron::Port + properties: + network: { get_param: net2 } + 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/functional2/BaseHOT/simple/nested/VDU2.yaml b/tacker/tests/etc/samples/etsi/nfv/functional2/BaseHOT/simple/nested/VDU2.yaml new file mode 100644 index 000000000..c4772dad6 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/functional2/BaseHOT/simple/nested/VDU2.yaml @@ -0,0 +1,61 @@ +heat_template_version: 2013-05-23 +description: 'VDU2 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 + +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 + availability_zone: { get_param: zone } + + VDU2_CP1: + type: OS::Neutron::Port + properties: + network: { get_param: net1 } + VDU2_CP2: + type: OS::Neutron::Port + properties: + network: { get_param: net2 } + 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/functional2/Definitions/helloworld3_df_simple.yaml b/tacker/tests/etc/samples/etsi/nfv/functional2/Definitions/helloworld3_df_simple.yaml new file mode 100644 index 000000000..120044934 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/functional2/Definitions/helloworld3_df_simple.yaml @@ -0,0 +1,403 @@ +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 + 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 + requirements: + - virtual_storage: VirtualStorage + + VDU2: + type: tosca.nodes.nfv.Vdu.Compute + properties: + name: VDU2 + description: VDU2 compute node + vdu_profile: + min_number_of_instances: 2 + max_number_of_instances: 2 + sw_image_data: + name: cirros-0.4.0-x86_64-disk2 + version: '0.4.0' + checksum: + algorithm: sha-256 + hash: a8dd75ecffd4cdd96072d60c2237b448e0c8b2bc94d57f10fdbc8c481d9005b8 + 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 + + VirtualStorage: + type: tosca.nodes.nfv.Vdu.VirtualBlockStorage + properties: + virtual_block_storage_data: + size_of_storage: 1 GB + rdma_enabled: true + sw_image_data: + name: cirros-0.4.0-x86_64-disk + version: '0.4.0' + checksum: + algorithm: sha-256 + hash: a8dd75ecffd4cdd96072d60c2237b448e0c8b2bc94d57f10fdbc8c481d9005b8 + container_format: bare + disk_format: qcow2 + min_disk: 0 GB + min_ram: 256 MB + size: 12 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: + worker_instance: + name: worker_instance_aspect + description: worker_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: 1 + targets: [ VDU1 ] + + - VDU2_initial_delta: + type: tosca.policies.nfv.VduInitialDelta + properties: + initial_delta: + number_of_instances: 2 + targets: [ VDU2 ] + + - VDU1_scaling_aspect_deltas: + type: tosca.policies.nfv.VduScalingAspectDeltas + properties: + aspect: worker_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: + worker_instance: + scale_level: 0 + instantiation_level_2: + description: Largest size + scale_info: + worker_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: 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: 2 + instantiation_level_2: + number_of_instances: 2 + 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/functional2/Definitions/helloworld3_top.vnfd.yaml b/tacker/tests/etc/samples/etsi/nfv/functional2/Definitions/helloworld3_top.vnfd.yaml new file mode 100644 index 000000000..0be18659b --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/functional2/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/functional2/Definitions/helloworld3_types.yaml b/tacker/tests/etc/samples/etsi/nfv/functional2/Definitions/helloworld3_types.yaml new file mode 100644 index 000000000..5c026d6a2 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/functional2/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/functional2/TOSCA-Metadata/TOSCA.meta b/tacker/tests/etc/samples/etsi/nfv/functional2/TOSCA-Metadata/TOSCA.meta new file mode 100644 index 000000000..cfd7444ac --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/functional2/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/functional2/UserData/__init__.py b/tacker/tests/etc/samples/etsi/nfv/functional2/UserData/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tacker/tests/etc/samples/etsi/nfv/functional2/UserData/lcm_user_data.py b/tacker/tests/etc/samples/etsi/nfv/functional2/UserData/lcm_user_data.py new file mode 100644 index 000000000..725c5430a --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/functional2/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/functional/base.py b/tacker/tests/functional/base.py index 90083b9ce..05beb4e91 100644 --- a/tacker/tests/functional/base.py +++ b/tacker/tests/functional/base.py @@ -97,6 +97,7 @@ class BaseTackerTest(base.BaseTestCase): cls.client = cls.tackerclient() cls.http_client = cls.tacker_http_client() cls.h_client = cls.heatclient() + cls.glance_client = cls.glanceclient() @classmethod def get_credentials(cls): diff --git a/tacker/tests/functional/common/fake_server.py b/tacker/tests/functional/common/fake_server.py index e5e42bc11..f6caf7013 100644 --- a/tacker/tests/functional/common/fake_server.py +++ b/tacker/tests/functional/common/fake_server.py @@ -18,6 +18,7 @@ import inspect import json import os import threading +import time from oslo_log import log as logging @@ -391,8 +392,16 @@ class FakeServerManager(SingletonMixin): '[Start] %s.%s()' % (self.__class__.__name__, inspect.currentframe().f_code.co_name)) - self.objHttpd = http.server.HTTPServer( - (address, port), DummyRequestHander) + while True: + try: + self.objHttpd = http.server.HTTPServer( + (address, port), DummyRequestHander) + except OSError: + time.sleep(10) + continue + else: + break + self.thread_server = threading.Thread(None, self.run) LOG.debug( '[ End ] %s.%s()' % (self.__class__.__name__, @@ -401,7 +410,7 @@ class FakeServerManager(SingletonMixin): def start_server(self): """Start server in thread.""" LOG.debug('[START] %s()' % inspect.currentframe().f_code.co_name) - threading.Thread(None, self.run).start() + self.thread_server.start() LOG.debug('[ END ] %s()' % inspect.currentframe().f_code.co_name) def run(self): @@ -411,10 +420,15 @@ class FakeServerManager(SingletonMixin): self.objHttpd.serve_forever() except KeyboardInterrupt: self.stop_server() + finally: + self.objHttpd.server_close() LOG.debug('[ END ] %s()' % inspect.currentframe().f_code.co_name) def stop_server(self): """Stop HTTP Server""" LOG.debug('[START] %s()' % inspect.currentframe().f_code.co_name) - self.objHttpd.shutdown() + thread_shutdown = threading.Thread(None, self.objHttpd.shutdown) + thread_shutdown.daemon = True + thread_shutdown.start() + self.thread_server.join() LOG.debug('[ END ] %s()' % inspect.currentframe().f_code.co_name) diff --git a/tacker/tests/functional/vnflcm/base.py b/tacker/tests/functional/vnflcm/base.py index 5707db3ca..0f9da1996 100644 --- a/tacker/tests/functional/vnflcm/base.py +++ b/tacker/tests/functional/vnflcm/base.py @@ -122,7 +122,8 @@ def _create_and_upload_vnf_package( vnfd_id = None while True: resp, body = tacker_client.do_request(show_url, "GET") - if body['onboardingState'] == "ONBOARDED": + if (200 <= resp.status_code < 300) and ( + body['onboardingState'] == "ONBOARDED"): vnfd_id = body['vnfdId'] break @@ -204,6 +205,8 @@ def _create_instantiate_vnf_request_body(flavour_id, class BaseVnfLcmTest(base.BaseTackerTest): + is_setup_error = False + @classmethod def setUpClass(cls): '''Set up test class. @@ -214,12 +217,6 @@ class BaseVnfLcmTest(base.BaseTackerTest): FAKE_SERVER_MANAGER.prepare_http_server() FAKE_SERVER_MANAGER.start_server() - FAKE_SERVER_MANAGER.set_callback( - 'POST', - MOCK_NOTIFY_CALLBACK_URL, - status_code=204 - ) - @classmethod def tearDownClass(cls): super(BaseVnfLcmTest, cls).tearDownClass() @@ -228,6 +225,18 @@ class BaseVnfLcmTest(base.BaseTackerTest): def setUp(self): super(BaseVnfLcmTest, self).setUp() + if self.is_setup_error: + self.fail("Faild, not exists pre-registered image.") + + callback_url = \ + os.path.join(MOCK_NOTIFY_CALLBACK_URL, self._testMethodName) + FAKE_SERVER_MANAGER.clear_history(callback_url) + FAKE_SERVER_MANAGER.set_callback( + 'POST', + callback_url, + status_code=204 + ) + self.tacker_client = base.BaseTackerTest.tacker_http_client() self.base_vnf_instances_url = "/vnflcm/v1/vnf_instances" @@ -248,14 +257,17 @@ class BaseVnfLcmTest(base.BaseTackerTest): for nw in networks.get('networks'): if nw['name'] == 'net0': self.ext_networks.append(nw['id']) + self.ext_vl = _get_external_virtual_links(nw['id']) elif nw['name'] == 'net1': self.ext_mngd_networks.append(nw['id']) # create new network. - self.ext_networks.append( - self._create_network("external_net")) - self.ext_mngd_networks.append( - self._create_network("external_managed_internal_net")) + ext_net_id, ext_net_name = \ + self._create_network("external_net") + self.ext_networks.append(ext_net_id) + ext_mngd_net_id, _ = \ + self._create_network("external_managed_internal_net") + self.ext_mngd_networks.append(ext_mngd_net_id) # Create external link ports in net0 self.ext_link_ports = list() @@ -265,21 +277,21 @@ class BaseVnfLcmTest(base.BaseTackerTest): # Chack how many networks are created. networks = self.neutronclient().list_networks() for nw in networks.get('networks'): - if nw['name'] not in ['net0', 'external_net']: - continue - if self.vim['tenant_id'] != nw['tenant_id']: + if nw['name'] not in ['net0', ext_net_name]: continue - self.ext_networks.append(nw['id']) + if nw['name'] == 'net0': + self.ext_subnets.append(nw['subnets'][0]) + else: + self.ext_subnets.append(self._create_subnet(nw)) self.ext_link_ports.append(self._create_port(nw['id'])) - self.ext_subnets.append(self._create_subnet(nw)) @classmethod def _list_glance_image(cls, filter_name='cirros-0.4.0-x86_64-disk'): try: images = cls.glance_client.images.list() except Exception: - print("glance-image does not exists.") + print("glance-image does not exists.", flush=True) return [] if filter_name is None: @@ -292,7 +304,7 @@ class BaseVnfLcmTest(base.BaseTackerTest): try: image = cls.glance_client.images.get(image_id) except Exception: - print("glance-image does not exists.") + print("glance-image does not exists.", image_id, flush=True) return None return image @@ -353,6 +365,7 @@ class BaseVnfLcmTest(base.BaseTackerTest): return self._create_vnf_instance_from_body(request_body) def _create_vnf_instance_from_body(self, request_body): + request_body['vnfInstanceName'] = self._testMethodName resp, response_body = self.http_client.do_request( self.base_vnf_instances_url, "POST", @@ -374,7 +387,7 @@ class BaseVnfLcmTest(base.BaseTackerTest): def _list_vnf_instance(self, **kwargs): resp, vnf_instances = self.http_client.do_request( - self.base_vnf_instances_url, "GET") + self.base_vnf_instances_url, "GET", **kwargs) return resp, vnf_instances @@ -418,6 +431,13 @@ class BaseVnfLcmTest(base.BaseTackerTest): return resp, body + def _update_vnf_instance(self, vnf_instance_id, request_body): + url = os.path.join(self.base_vnf_instances_url, vnf_instance_id) + resp, body = self.http_client.do_request(url, "PATCH", + body=jsonutils.dumps(request_body)) + + return resp, body + def _wait_terminate_vnf_instance(self, id, timeout=None): start_time = int(time.time()) @@ -441,7 +461,6 @@ class BaseVnfLcmTest(base.BaseTackerTest): try: stacks = self.h_client.stacks.list() except Exception: - print("heat-stacks does not exists.") return None target_stack_name = prefix_id + vnf_instance_id @@ -460,7 +479,6 @@ class BaseVnfLcmTest(base.BaseTackerTest): resources = self.h_client.resources.list( stack_id, nested_depth=nested_depth) except Exception: - print("heat-stacks-resources does not exists.") return None return resources @@ -470,7 +488,6 @@ class BaseVnfLcmTest(base.BaseTackerTest): resource = self.h_client.resources.get( stack_id, resource_name) except Exception: - print("heat-stacks-resource does not exists.") return None return resource @@ -610,15 +627,28 @@ class BaseVnfLcmTest(base.BaseTackerTest): image = self._get_glance_image(glance_image_id) self.assertIsNone(image) - def _wait_lcm_done(self, expected_operation_status=None): + def _wait_lcm_done(self, + expected_operation_status=None, + vnf_instance_id=None): start_time = int(time.time()) - while True: + callback_url = \ + os.path.join(MOCK_NOTIFY_CALLBACK_URL, self._testMethodName) + while True: actual_status = None vnf_lcm_op_occ_id = None notify_mock_responses = FAKE_SERVER_MANAGER.get_history( - MOCK_NOTIFY_CALLBACK_URL) + callback_url) + print( + ("Wait:callback_url=<%s>, " + + "wait_status=<%s>, " + + "vnf_instance_id=<%s>") % + (callback_url, expected_operation_status, vnf_instance_id), + flush=True) + for res in notify_mock_responses: + if vnf_instance_id != res.request_body.get('vnfInstanceId'): + continue if expected_operation_status is None: return @@ -631,8 +661,9 @@ class BaseVnfLcmTest(base.BaseTackerTest): if ((int(time.time()) - start_time) > VNF_LCM_DONE_TIMEOUT): if actual_status: error = ( - "LCM incomplete timeout, %s is %s," + - "expected status should be %s") + "LCM incomplete timeout, %(vnf_lcm_op_occ_id)s" + + " is %(actual)s," + + "expected status should be %(expected)s") self.fail( error % { "vnf_lcm_op_occ_id": vnf_lcm_op_occ_id, @@ -669,9 +700,11 @@ class BaseVnfLcmTest(base.BaseTackerTest): fields.VnfInstanceState.NOT_INSTANTIATED) # FT-checkpoint: Notification - notify_mock_responses = FAKE_SERVER_MANAGER.get_history( - MOCK_NOTIFY_CALLBACK_URL) - FAKE_SERVER_MANAGER.clear_history(MOCK_NOTIFY_CALLBACK_URL) + callback_url = \ + os.path.join(MOCK_NOTIFY_CALLBACK_URL, self._testMethodName) + notify_mock_responses = self._filter_notify_history(callback_url, + vnf_instance.get('id')) + self.assertEqual(1, len(notify_mock_responses)) self.assert_notification_mock_response( notify_mock_responses[0], @@ -684,9 +717,11 @@ class BaseVnfLcmTest(base.BaseTackerTest): self.assertEqual(404, resp.status_code) # FT-checkpoint: Notification - notify_mock_responses = FAKE_SERVER_MANAGER.get_history( - MOCK_NOTIFY_CALLBACK_URL) - FAKE_SERVER_MANAGER.clear_history(MOCK_NOTIFY_CALLBACK_URL) + callback_url = \ + os.path.join(MOCK_NOTIFY_CALLBACK_URL, self._testMethodName) + notify_mock_responses = self._filter_notify_history(callback_url, + vnf_instance_id) + self.assertEqual(1, len(notify_mock_responses)) self.assert_notification_mock_response( notify_mock_responses[0], @@ -707,9 +742,10 @@ class BaseVnfLcmTest(base.BaseTackerTest): expected_resource_status='CREATE_COMPLETE') # FT-checkpoint: Notification - notify_mock_responses = FAKE_SERVER_MANAGER.get_history( - MOCK_NOTIFY_CALLBACK_URL) - FAKE_SERVER_MANAGER.clear_history(MOCK_NOTIFY_CALLBACK_URL) + callback_url = \ + os.path.join(MOCK_NOTIFY_CALLBACK_URL, self._testMethodName) + notify_mock_responses = self._filter_notify_history(callback_url, + vnf_instance_id) self.assertEqual(3, len(notify_mock_responses)) self.assert_notification_mock_response( @@ -743,9 +779,10 @@ class BaseVnfLcmTest(base.BaseTackerTest): expected_stack_status=expected_stack_status) # FT-checkpoint: Notification - notify_mock_responses = FAKE_SERVER_MANAGER.get_history( - MOCK_NOTIFY_CALLBACK_URL) - FAKE_SERVER_MANAGER.clear_history(MOCK_NOTIFY_CALLBACK_URL) + callback_url = \ + os.path.join(MOCK_NOTIFY_CALLBACK_URL, self._testMethodName) + notify_mock_responses = self._filter_notify_history(callback_url, + vnf_instance_id) self.assertEqual(3, len(notify_mock_responses)) self.assert_notification_mock_response( @@ -787,9 +824,10 @@ class BaseVnfLcmTest(base.BaseTackerTest): glance_image_id_list=glance_image_id_list) # FT-checkpoint: Notification - notify_mock_responses = FAKE_SERVER_MANAGER.get_history( - MOCK_NOTIFY_CALLBACK_URL) - FAKE_SERVER_MANAGER.clear_history(MOCK_NOTIFY_CALLBACK_URL) + callback_url = \ + os.path.join(MOCK_NOTIFY_CALLBACK_URL, self._testMethodName) + notify_mock_responses = self._filter_notify_history(callback_url, + vnf_instance_id) self.assertEqual(3, len(notify_mock_responses)) self.assert_notification_mock_response( @@ -807,6 +845,41 @@ class BaseVnfLcmTest(base.BaseTackerTest): 'VnfLcmOperationOccurrenceNotification', 'COMPLETED') + def assert_update_vnf( + self, + resp, + vnf_instance_id, + expected_stack_status='CREATE_COMPLETE'): + self.assertEqual(202, resp.status_code) + self.assert_http_header_location_for_lcm_op_occs(resp.headers) + + resp, vnf_instance = self._show_vnf_instance(vnf_instance_id) + self.assertEqual(200, resp.status_code) + + self.assert_vnf_state(vnf_instance) + self.assert_instantiation_state(vnf_instance) + + self.assert_heat_stack_status( + vnf_instance['id'], + expected_stack_status=expected_stack_status) + + # FT-checkpoint: Notification + callback_url = \ + os.path.join(MOCK_NOTIFY_CALLBACK_URL, self._testMethodName) + notify_mock_responses = self._filter_notify_history(callback_url, + vnf_instance_id) + + self.assertEqual(2, len(notify_mock_responses)) + self.assert_notification_mock_response( + notify_mock_responses[0], + 'VnfLcmOperationOccurrenceNotification', + 'PROCESSING') + + self.assert_notification_mock_response( + notify_mock_responses[1], + 'VnfLcmOperationOccurrenceNotification', + 'COMPLETED') + def assert_notification_mock_response( self, notify_mock_response, @@ -826,34 +899,64 @@ class BaseVnfLcmTest(base.BaseTackerTest): def _create_network(self, name): # First, we have to check network name passed by caller is # already exists or not. - netlist = self.neutronclient().list_networks(name=name) - if netlist is not None: - print('%s is already exist' % name) - # OK, we can create this. - net = self.neutronclient().create_network({'network': {'name': name}}) - net_id = net['network']['id'] - self.addCleanup(self.neutronclient().delete_network, net_id) - - return net_id + try: + uniq_name = name + '-' + uuidutils.generate_uuid() + net = \ + self.neutronclient().create_network( + {'network': {'name': uniq_name}}) + net_id = net['network']['id'] + self.addCleanup(self._delete_network, net_id) + print("Create network success, %s" % uniq_name, flush=True) + return net_id, uniq_name + except Exception as e: + self.fail("Failed, create network=<%s>, %s" % + (uniq_name, e)) def _create_subnet(self, network): + cidr_prefix = "22.22.{}".format(str(len(self.ext_subnets) + 1)) body = {'subnet': {'network_id': network['id'], 'name': "subnet-%s" % uuidutils.generate_uuid(), - 'cidr': "22.22.{}.0/24".format(str(len(self.ext_subnets) % 2)), + 'cidr': "{}.0/24".format(cidr_prefix), 'ip_version': 4, - 'gateway_ip': '22.22.0.1', + 'gateway_ip': "{}.1".format(cidr_prefix), "enable_dhcp": True}} - subnet = self.neutronclient().create_subnet(body=body)["subnet"] - self.addCleanup(self.neutronclient().delete_subnet, subnet['id']) - return subnet['id'] + try: + subnet = self.neutronclient().create_subnet(body=body)["subnet"] + self.addCleanup(self._delete_subnet, subnet['id']) + return subnet['id'] + except Exception as e: + self.fail("Failed, create subnet for net_id=<%s>, %s" % + (network['id'], e)) def _create_port(self, network_id): body = {'port': {'network_id': network_id}} - port = self.neutronclient().create_port(body=body)["port"] - self.addCleanup(self.neutronclient().delete_port, port['id']) - return port['id'] + try: + port = self.neutronclient().create_port(body=body)["port"] + self.addCleanup(self._delete_port, port['id']) + return port['id'] + except Exception as e: + self.fail("Failed, create port for net_id=<%s>, %s" % + (network_id, e)) + + def _delete_network(self, network_id): + try: + self.neutronclient().delete_network(network_id) + except Exception: + print("Failed, delete network.", network_id, flush=True) + + def _delete_subnet(self, subnet_id): + try: + self.neutronclient().delete_subnet(subnet_id) + except Exception: + print("Failed, delete subnet.", subnet_id, flush=True) + + def _delete_port(self, port_id): + try: + self.neutronclient().delete_port(port_id) + except Exception: + print("Failed, delete port.", port_id, flush=True) def assert_subscription_show(self, resp, response_body): """Assert that subscription informations has mandatory keys.""" @@ -869,3 +972,12 @@ class BaseVnfLcmTest(base.BaseTackerTest): self.assertIsNotNone(_links) self.assertIsNotNone(_links.get('self')) self.assertIsNotNone(_links.get('self').get('href')) + + def _filter_notify_history(self, callback_url, vnf_instance_id): + notify_histories = FAKE_SERVER_MANAGER.get_history( + callback_url) + FAKE_SERVER_MANAGER.clear_history(callback_url) + + return [ + h for h in notify_histories + if h.request_body.get('vnfInstanceId') == vnf_instance_id] diff --git a/tacker/tests/functional/vnflcm/fake_vnflcm.py b/tacker/tests/functional/vnflcm/fake_vnflcm.py index 0ea5ee0e0..b23356c12 100644 --- a/tacker/tests/functional/vnflcm/fake_vnflcm.py +++ b/tacker/tests/functional/vnflcm/fake_vnflcm.py @@ -53,7 +53,7 @@ class VnfInstances: def make_create_request_body(vnfd_id): return { "vnfdId": vnfd_id, - "vnfInstanceName": "helloworld3", + "vnfInstanceName": "", "vnfInstanceDescription": "Sample VNF", "metadata": { "samplekey": "samplevalue" @@ -97,7 +97,6 @@ class VnfInstances: } ext_virtual_link_cp1 = { "id": uuidsentinel.evl1_id, - "vimConnectionId": uuidsentinel.vim_connection_id, # set external nw_id on vim. "resourceId": networks_id[0], "extCps": [ext_vdu1_cp1, ext_vdu2_cp1], @@ -113,7 +112,7 @@ class VnfInstances: "ipOverEthernet": { "ipAddresses": [{ "type": "IPV4", - "numDynamicAddresses": 1, + "fixedAddresses": ["22.22.2.10"], "subnetId": external_subnets_id[0] }] } @@ -129,7 +128,7 @@ class VnfInstances: "ipOverEthernet": { "ipAddresses": [{ "type": "IPV4", - "numDynamicAddresses": "1", + "fixedAddresses": ["22.22.2.20"], "subnetId": external_subnets_id[1] }] } @@ -139,7 +138,6 @@ class VnfInstances: ext_virtual_link_cp2 = { "id": uuidsentinel.evl2_id, - "vimConnectionId": uuidsentinel.vim_connection_id, "resourceId": networks_id[1], "extCps": [ ext_cps_vdu1_cp2, ext_cps_vdu2_cp2 @@ -150,12 +148,10 @@ class VnfInstances: ext_mng_vtl_lnks = [{ "id": uuidsentinel.emvl1_id, "vnfVirtualLinkDescId": "internalVL1", - "vimConnectionId": uuidsentinel.vim_connection_id, "resourceId": ext_mngd_networks_id[0] }, { "id": uuidsentinel.emvl2_id, "vnfVirtualLinkDescId": "internalVL2", - "vimConnectionId": uuidsentinel.vim_connection_id, "resourceId": ext_mngd_networks_id[1] }] @@ -169,6 +165,7 @@ class VnfInstances: "vimConnectionInfo": [{ "id": uuidsentinel.vim_connection_id, "vimType": "ETSINFV.OPENSTACK_KEYSTONE.v_2", + "vimConnectionId": uuidsentinel.vim_connection_id, "interfaceInfo": { "endpoint": "http://127.0.0.1/identity" }, @@ -214,3 +211,31 @@ class VnfInstances: "samplekey": "samplevalue" } } + + @staticmethod + def make_update_request_body(vnfd_id=None, vnf_package_id=None): + """Parameter selection policy. + + vimConnectionInfo is not set. + + Args: + vnfd_id (str, optional): vnfdId(2.6.1) + vnf_package_id (str, optional): vnfPkgId(2.4.1) + + Returns: + dict: Request body + """ + data = { + "vnfInstanceName": "helloworld3_modify", + "vnfInstanceDescription": "Sample VNF Modify", + "metadata": { + "samplekey": "samplevalue_modified" + } + } + + if vnfd_id: + data["vnfdId"] = vnfd_id + elif vnf_package_id: + data["vnfPkgId"] = vnf_package_id + + return data diff --git a/tacker/tests/functional/vnflcm/test_vnf_instance_with_user_data.py b/tacker/tests/functional/vnflcm/test_vnf_instance_with_user_data.py index 8e2a56545..643572cd0 100644 --- a/tacker/tests/functional/vnflcm/test_vnf_instance_with_user_data.py +++ b/tacker/tests/functional/vnflcm/test_vnf_instance_with_user_data.py @@ -11,13 +11,53 @@ # License for the specific language governing permissions and limitations # under the License. +import os +from oslo_utils import uuidutils from tacker.objects import fields from tacker.tests.functional.vnflcm import base as vnflcm_base +from tacker.tests.functional.vnflcm import fake_vnflcm +import tempfile import time class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest): + @classmethod + def setUpClass(cls): + super(VnfLcmWithUserDataTest, cls).setUpClass() + images = cls._list_glance_image() + if len(images) == 0: + cls.is_setup_error = True + return + + # ModifyVNF specific image create. + for image in images: + specific_image_name = image.name + '2' + image_data = { + "min_disk": image.min_disk, + "min_ram": image.min_ram, + "disk_format": image.disk_format, + "container_format": image.container_format, + "visibility": image.visibility, + "name": specific_image_name} + + try: + images = cls._list_glance_image(specific_image_name) + if len(images) == 1: + break + + _, body = cls.glance_client.http_client.get( + cls.glance_client.http_client.get_endpoint() + image.file) + + with tempfile.TemporaryFile('w+b') as f: + for content in body: + f.write(content) + cls._create_glance_image(image_data, f.read()) + except Exception as e: + print("Fail, Modify-VNF specific image create.", e, flush=True) + cls.is_setup_error = True + return + def _vnf_instance_wait_until_fail_detected(self, id, instantiation_state=fields.VnfInstanceState.NOT_INSTANTIATED, timeout=vnflcm_base.VNF_INSTANTIATE_ERROR_WAIT): @@ -53,6 +93,592 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest): time.sleep(1) + def test_inst_update_heal_term(self): + """Test base life cycle operations. + + In this test case, we do following steps. + - Create subscription. + - Get subscription informations. + - Get list of subscriptions + - Create VNF package. + - Upload VNF package. + - Create VNF instance. + - Instantiate VNF. + - Get list of VNF instances. + - Get information of instantiated VNF. + - Heal VNF. + - Create new VNF package for update. + - Upload new VNF package. + - Update VNF with new package. + - Terminate VNF. + - Delete subscription. + """ + # Create subscription and register it. + request_body = fake_vnflcm.Subscription.make_create_request_body( + 'http://localhost:{}{}'.format( + vnflcm_base.FAKE_SERVER_MANAGER.SERVER_PORT, + os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL, + self._testMethodName))) + resp, response_body = self._register_subscription(request_body) + self.assertEqual(201, resp.status_code) + self.assert_http_header_location_for_subscription(resp.headers) + subscription_id = response_body.get('id') + self.addCleanup( + self._delete_subscription, + subscription_id) + + # Subscription show + resp, body = self._wait_show_subscription(subscription_id) + self.assert_subscription_show(resp, body) + + # Subscription list + resp, _ = self._list_subscription() + self.assertEqual(200, resp.status_code) + + # Pre Setting: Create vnf package. + sample_name = 'functional' + csar_package_path = os.path.abspath( + os.path.join( + os.path.dirname(__file__), + "../../etc/samples/etsi/nfv", + sample_name)) + tempname, _ = vnflcm_base._create_csar_with_unique_vnfd_id( + csar_package_path) + # upload vnf package + vnf_package_id, vnfd_id = vnflcm_base._create_and_upload_vnf_package( + self.tacker_client, user_defined_data={ + "key": sample_name}, temp_csar_path=tempname) + + # Post Setting: Reserve deleting vnf package. + self.addCleanup( + vnflcm_base._delete_vnf_package, + self.tacker_client, + vnf_package_id) + + # Create vnf instance + resp, vnf_instance = self._create_vnf_instance_from_body( + fake_vnflcm.VnfInstances.make_create_request_body(vnfd_id)) + vnf_instance_id = vnf_instance['id'] + self._wait_lcm_done(vnf_instance_id=vnf_instance_id) + self.assert_create_vnf(resp, vnf_instance, vnf_package_id) + vnf_instance_name = vnf_instance['vnfInstanceName'] + self.addCleanup( + self._delete_vnf_instance, + vnf_instance_id) + + # Instantiate vnf instance + request_body = fake_vnflcm.VnfInstances.make_inst_request_body( + self.vim['tenant_id'], + self.ext_networks, + self.ext_mngd_networks, + self.ext_link_ports, + self.ext_subnets) + 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, vnf_package_id) + + # List vnf instance + filter_expr = { + 'filter': "(eq,id,{});(eq,vnfInstanceName,{})".format( + vnf_instance_id, vnf_instance_name)} + resp, vnf_instances = self._list_vnf_instance(params=filter_expr) + self.assertEqual(200, resp.status_code) + self.assertEqual(1, len(vnf_instances)) + + # Show vnf instance + resp, vnf_instance = self._show_vnf_instance(vnf_instance_id) + self.assertEqual(200, resp.status_code) + + # Update vnf (vnfdId) + sample_name = 'functional2' + csar_package_path = os.path.abspath( + os.path.join( + os.path.dirname(__file__), + "../../etc/samples/etsi/nfv", + sample_name)) + tempname, _ = vnflcm_base._create_csar_with_unique_vnfd_id( + csar_package_path) + # upload vnf package + update_vnf_package_id, update_vnfd_id = \ + vnflcm_base._create_and_upload_vnf_package( + self.tacker_client, + user_defined_data={"key": sample_name}, + temp_csar_path=tempname) + + request_body = fake_vnflcm.VnfInstances.make_update_request_body( + vnfd_id=update_vnfd_id) + resp, _ = self._update_vnf_instance(vnf_instance_id, request_body) + self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id) + self.assert_update_vnf( + resp, + vnf_instance_id, + after_id=request_body['vnfdId'], + old_id=vnfd_id) + + vnf_package_id = update_vnf_package_id + + # Heal vnf (exists vnfc_instace_id) + vnfc_instance_id_list = [ + vnfc.get('id') for vnfc in vnf_instance.get( + 'instantiatedVnfInfo', {}).get( + 'vnfcResourceInfo', [])] + request_body = fake_vnflcm.VnfInstances.make_heal_request_body( + vnfc_instance_id_list) + resp, _ = self._heal_vnf_instance(vnf_instance_id, request_body) + self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id) + self.assert_heal_vnf(resp, vnf_instance_id, vnf_package_id) + + # Terminate VNF + 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_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, + vnf_package_id) + + # 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, vnf_package_id) + + # Subscription delete + resp, response_body = self._delete_subscription(subscription_id) + self.assertEqual(204, resp.status_code) + + resp, show_body = self._show_subscription(subscription_id) + self.assertEqual(404, resp.status_code) + + def test_inst_update_pkgid_heal_all(self): + """Test basic life cycle operations with pkg update. + + In this test case, we do following steps. + - Create subscription. + - Create VNF package. + - Upload VNF package. + - Create VNF instance. + - Instantiate VNF + - Heal VNF + - Create new VNF package for update. + - Upload new VNF package. + - Update VNF with new package. + - Terminate VNF. + - Delete VNF. + - Delete subscription. + """ + # Create subscription and register it. + request_body = fake_vnflcm.Subscription.make_create_request_body( + 'http://localhost:{}{}'.format( + vnflcm_base.FAKE_SERVER_MANAGER.SERVER_PORT, + os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL, + self._testMethodName))) + resp, response_body = self._register_subscription(request_body) + self.assertEqual(201, resp.status_code) + self.assert_http_header_location_for_subscription(resp.headers) + subscription_id = response_body.get('id') + self.addCleanup( + self._delete_subscription, + subscription_id) + + # Pre Setting: Create vnf package. + sample_name = 'functional' + csar_package_path = os.path.abspath( + os.path.join( + os.path.dirname(__file__), + "../../etc/samples/etsi/nfv", + sample_name)) + tempname, _ = vnflcm_base._create_csar_with_unique_vnfd_id( + csar_package_path) + # upload vnf package + vnf_package_id, vnfd_id = vnflcm_base._create_and_upload_vnf_package( + self.tacker_client, user_defined_data={ + "key": sample_name}, temp_csar_path=tempname) + + # Post Setting: Reserve deleting vnf package. + self.addCleanup( + vnflcm_base._delete_vnf_package, + self.tacker_client, + vnf_package_id) + + # Create vnf instance + resp, vnf_instance = self._create_vnf_instance_from_body( + fake_vnflcm.VnfInstances.make_create_request_body(vnfd_id)) + vnf_instance_id = vnf_instance['id'] + self._wait_lcm_done(vnf_instance_id=vnf_instance_id) + self.assert_create_vnf(resp, vnf_instance, vnf_package_id) + self.addCleanup( + self._delete_vnf_instance, + vnf_instance_id) + + # Instantiate vnf instance + request_body = fake_vnflcm.VnfInstances.make_inst_request_body( + self.vim['tenant_id'], + self.ext_networks, + self.ext_mngd_networks, + self.ext_link_ports, + self.ext_subnets) + 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, vnf_package_id) + + # Heal vnf (do not specify vnfc_instace_id) + # pre check heat status. + self.assert_heat_stack_status(vnf_instance_id) + + request_body = fake_vnflcm.VnfInstances.make_heal_request_body() + resp, _ = self._heal_vnf_instance(vnf_instance_id, request_body) + self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id) + + # post check heat status. + self.assert_heal_vnf( + resp, + vnf_instance_id, + vnf_package_id, + expected_stack_status='CREATE_COMPLETE') + + # Update vnf (vnfPkgId) + sample_name = 'functional2' + csar_package_path = os.path.abspath( + os.path.join( + os.path.dirname(__file__), + "../../etc/samples/etsi/nfv", + sample_name)) + tempname, _ = vnflcm_base._create_csar_with_unique_vnfd_id( + csar_package_path) + # upload vnf package + update_vnf_package_id, _ = vnflcm_base._create_and_upload_vnf_package( + self.tacker_client, user_defined_data={ + "key": sample_name}, temp_csar_path=tempname) + + request_body = fake_vnflcm.VnfInstances.make_update_request_body( + vnf_package_id=update_vnf_package_id) + resp, _ = self._update_vnf_instance(vnf_instance_id, request_body) + self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id) + self.assert_update_vnf( + resp, + vnf_instance_id, + is_vnfd=False, + after_id=request_body['vnfPkgId'], + old_id=vnf_package_id) + vnf_package_id = update_vnf_package_id + + # Terminate VNF + 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_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, + vnf_package_id) + + # 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, vnf_package_id) + + # Subscription delete + resp, response_body = self._delete_subscription(subscription_id) + self.assertEqual(204, resp.status_code) + + def test_instantiate_vnf_basehot_invalid(self): + """Test instantiation operation with invalid HOT data. + + In this test case, we do following steps. + - Create VNF package. + - Upload VNF package. + - Create VNF instance. + - Instantiate VNF + """ + # Pre Setting: Create vnf package. + sample_name = "user_data_sample_basehot_invalid" + csar_package_path = os.path.abspath( + os.path.join( + os.path.dirname(__file__), + "../../etc/samples/etsi/nfv", + sample_name)) + tempname, _ = vnflcm_base._create_csar_user_data_common( + csar_package_path) + # upload vnf package + vnf_package_id, vnfd_id = vnflcm_base._create_and_upload_vnf_package( + self.tacker_client, user_defined_data={ + "key": sample_name}, temp_csar_path=tempname) + + # Reserve deleting vnf package + self.addCleanup( + vnflcm_base._delete_vnf_package, + self.tacker_client, + vnf_package_id) + + # Settings + vnf_instance_name = "vnf_with_user_data-%s" % \ + uuidutils.generate_uuid() + vnf_instance_description = "vnf_with_user_data_basehot_invalid" + add_params = { + "lcm-operation-user-data": "./UserData/lcm_user_data.py", + "lcm-operation-user-data-class": "SampleUserData"} + + # Create vnf instance + resp, vnf_instance = self._create_vnf_instance(vnfd_id, + vnf_instance_name=vnf_instance_name, + vnf_instance_description=vnf_instance_description) + self.assertIsNotNone(vnf_instance['id']) + self.assertEqual(201, resp.status_code) + + # Reserve deleting vnf instance + self.addCleanup(self._delete_vnf_instance, vnf_instance['id']) + + request_body = \ + vnflcm_base._create_instantiate_vnf_request_body("simple", + vim_id=self.vim['id'], + ext_vl=self.ext_vl, + add_params=add_params) + + self._instantiate_vnf_instance_fail(vnf_instance['id'], request_body) + + def test_instantiate_vnf_userdata_timeout(self): + """Test instantiation operation timeout with long-running script. + + In this test case, we do following steps. + - Create VNF package. + - Upload VNF package. + - Create VNF instance. + - Instantiate VNF + """ + # Pre Setting: Create vnf package. + sample_name = "user_data_sample_userdata_timeout" + csar_package_path = os.path.abspath( + os.path.join( + os.path.dirname(__file__), + "../../etc/samples/etsi/nfv", + sample_name)) + tempname, _ = vnflcm_base._create_csar_user_data_common( + csar_package_path) + # upload vnf package + vnf_package_id, vnfd_id = vnflcm_base._create_and_upload_vnf_package( + self.tacker_client, user_defined_data={ + "key": sample_name}, temp_csar_path=tempname) + + # Reserve deleting vnf package + self.addCleanup( + vnflcm_base._delete_vnf_package, + self.tacker_client, + vnf_package_id) + + # Settings + vnf_instance_name = "vnf_with_user_data-%s" % \ + uuidutils.generate_uuid() + vnf_instance_description = "vnf_with_user_data_timeout" + add_params = { + "lcm-operation-user-data": "./UserData/lcm_user_data_sleeping.py", + "lcm-operation-user-data-class": "SampleUserData"} + + # Create vnf instance + resp, vnf_instance = self._create_vnf_instance(vnfd_id, + vnf_instance_name=vnf_instance_name, + vnf_instance_description=vnf_instance_description) + self.assertIsNotNone(vnf_instance['id']) + self.assertEqual(201, resp.status_code) + + # Reserve deleting vnf instance + self.addCleanup(self._delete_vnf_instance, vnf_instance['id']) + + request_body = \ + vnflcm_base._create_instantiate_vnf_request_body("simple", + vim_id=self.vim['id'], + ext_vl=self.ext_vl, + add_params=add_params) + + self._instantiate_vnf_instance_fail(vnf_instance['id'], request_body) + + def test_instantiate_vnf_userdata_invalid_hot_param(self): + """Test instantiation operation with invalid HOT and user data. + + In this test case, we do following steps. + - Create VNF package. + - Upload VNF package. + - Create VNF instance. + - Instantiate VNF + """ + # Pre Setting: Create vnf package. + sample_name = "user_data_sample_userdata_invalid_hot_param" + csar_package_path = os.path.abspath( + os.path.join( + os.path.dirname(__file__), + "../../etc/samples/etsi/nfv", + sample_name)) + tempname, _ = vnflcm_base._create_csar_user_data_common( + csar_package_path) + # upload vnf package + vnf_package_id, vnfd_id = vnflcm_base._create_and_upload_vnf_package( + self.tacker_client, user_defined_data={ + "key": sample_name}, temp_csar_path=tempname) + + # Reserve deleting vnf package + self.addCleanup( + vnflcm_base._delete_vnf_package, + self.tacker_client, + vnf_package_id) + + # Settings + vnf_instance_name = "vnf_with_user_data-%s" % \ + uuidutils.generate_uuid() + vnf_instance_description = "vnf_with_user_data_timeout" + add_params = { + "lcm-operation-user-data": "./UserData/" + "lcm_user_data_invalid_hot_param.py", + "lcm-operation-user-data-class": "SampleUserData"} + + # Create vnf instance + resp, vnf_instance = self._create_vnf_instance(vnfd_id, + vnf_instance_name=vnf_instance_name, + vnf_instance_description=vnf_instance_description) + self.assertIsNotNone(vnf_instance['id']) + self.assertEqual(201, resp.status_code) + + # Reserve deleting vnf instance + self.addCleanup(self._delete_vnf_instance, vnf_instance['id']) + + request_body = \ + vnflcm_base._create_instantiate_vnf_request_body("simple", + vim_id=self.vim['id'], + ext_vl=self.ext_vl, + add_params=add_params) + + self._instantiate_vnf_instance_fail(vnf_instance['id'], request_body) + + def test_instantiate_vnf_userdata_none(self): + """Test instantiation operation timeout with none user data. + + In this test case, we do following steps. + - Create VNF package. + - Upload VNF package. + - Create VNF instance. + - Instantiate VNF + """ + # Pre Setting: Create vnf package. + sample_name = "user_data_sample_userdata_none" + csar_package_path = os.path.abspath( + os.path.join( + os.path.dirname(__file__), + "../../etc/samples/etsi/nfv", + sample_name)) + tempname, _ = vnflcm_base._create_csar_user_data_common( + csar_package_path) + # upload vnf package + vnf_package_id, vnfd_id = vnflcm_base._create_and_upload_vnf_package( + self.tacker_client, user_defined_data={ + "key": sample_name}, temp_csar_path=tempname) + + # Reserve deleting vnf package + self.addCleanup( + vnflcm_base._delete_vnf_package, + self.tacker_client, + vnf_package_id) + + # Settings + vnf_instance_name = "vnf_with_user_data-%s" % \ + uuidutils.generate_uuid() + vnf_instance_description = "vnf_with_user_data_timeout" + add_params = { + "lcm-operation-user-data": "./UserData/lcm_user_data.py", + "lcm-operation-user-data-class": "SampleUserData"} + + # Create vnf instance + resp, vnf_instance = self._create_vnf_instance(vnfd_id, + vnf_instance_name=vnf_instance_name, + vnf_instance_description=vnf_instance_description) + self.assertIsNotNone(vnf_instance['id']) + self.assertEqual(201, resp.status_code) + + # Reserve deleting vnf instance + self.addCleanup(self._delete_vnf_instance, vnf_instance['id']) + + request_body = \ + vnflcm_base._create_instantiate_vnf_request_body("simple", + vim_id=self.vim['id'], + ext_vl=self.ext_vl, + add_params=add_params) + + self._instantiate_vnf_instance_fail(vnf_instance['id'], request_body) + + def test_instantiate_vnf_userdata_invalid_script(self): + """Test instantiation operation with invalid user script. + + In this test case, we do following steps. + - Create VNF package. + - Upload VNF package. + - Create VNF instance. + - Instantiate VNF + """ + # Pre Setting: Create vnf package. + sample_name = "user_data_sample_userdata_invalid_script" + csar_package_path = os.path.abspath( + os.path.join( + os.path.dirname(__file__), + "../../etc/samples/etsi/nfv", + sample_name)) + tempname, _ = vnflcm_base._create_csar_user_data_common( + csar_package_path) + # upload vnf package + vnf_package_id, vnfd_id = vnflcm_base._create_and_upload_vnf_package( + self.tacker_client, user_defined_data={ + "key": sample_name}, temp_csar_path=tempname) + + # Reserve deleting vnf package + self.addCleanup( + vnflcm_base._delete_vnf_package, + self.tacker_client, + vnf_package_id) + + # Settings + vnf_instance_name = "vnf_with_user_data-%s" % \ + uuidutils.generate_uuid() + vnf_instance_description = "vnf_with_user_data_timeout" + add_params = { + "lcm-operation-user-data": "./UserData/" + "lcm_user_data_invalid_script.py", + "lcm-operation-user-data-class": "SampleUserData"} + + # Create vnf instance + resp, vnf_instance = self._create_vnf_instance(vnfd_id, + vnf_instance_name=vnf_instance_name, + vnf_instance_description=vnf_instance_description) + self.assertIsNotNone(vnf_instance['id']) + self.assertEqual(201, resp.status_code) + + # Reserve deleting vnf instance + self.addCleanup(self._delete_vnf_instance, vnf_instance['id']) + + request_body = \ + vnflcm_base._create_instantiate_vnf_request_body("simple", + vim_id=self.vim['id'], + ext_vl=self.ext_vl, + add_params=add_params) + + self._instantiate_vnf_instance_fail(vnf_instance['id'], request_body) + def assert_create_vnf(self, resp, vnf_instance, vnf_pkg_id): super().assert_create_vnf(resp, vnf_instance) @@ -112,6 +738,49 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest): self.tacker_client, vnf_pkg_id) self.assert_vnf_package_usage_state(vnf_pkg_info) + def assert_update_vnf(self, resp, vnf_instance_id, is_vnfd=True, + after_id=None, old_id=None, + expected_stack_status='CREATE_COMPLETE'): + """Assert that VNF was updated. + + This method checks if a VNF was really updated or not. + We use the same name method of super class to check + lifecycle event(e.g. LcmOpOccs, heat stack status). + And then, in this method, it gets vnf package info from tacker. + We confirm that the old package status is 'NOT_IN_USE' and also + a new package status is the same as expected. + + Args: + resp (Response): Response headers for HTTP requests. + vnf_instance_id (str): Self explanatly + is_vnfd (bool, optional): Specify target VNF is VNFD or + not. Defaults to True. + after_id (str): Updated VNF id. It should be id of VNFD + or VNF Package. Defaults to None. + old_id (str): Present VNF id. Defaults to None. + expected_stack_status (str, optional): The expected status + of updated VNF. Defaults to 'CREATE_COMPLETE'. + """ + super().assert_update_vnf( + resp, vnf_instance_id, expected_stack_status=expected_stack_status) + + if is_vnfd: + after_filter_attr = {'filter': "(eq,vnfdId,{})".format(after_id)} + old_filter_attr = {'filter': "(eq,vnfdId,{})".format(old_id)} + else: + after_filter_attr = {'filter': "(eq,id,{})".format(after_id)} + old_filter_attr = {'filter': "(eq,id,{})".format(old_id)} + + # assert old/new package status. + resp, after_vnf_pkg_info = vnflcm_base._list_vnf_package( + self.tacker_client, params=after_filter_attr) + self.assert_vnf_package_usage_state(after_vnf_pkg_info[0]) + + resp, old_vnf_pkg_info = vnflcm_base._list_vnf_package( + self.tacker_client, params=old_filter_attr) + self.assert_vnf_package_usage_state(old_vnf_pkg_info[0], + expected_usage_state=fields.PackageUsageStateType.NOT_IN_USE) + def assert_vnf_package_usage_state( self, vnf_package_info,