Add functional tests for vnflcm APIs

Added functional tests for all support vnflcm APIs:-
1. Create Vnf instance API
2. Instantiate Vnf API
3. Heal Vnf API
4. Terminate Vnf API
5. List Vnf API
6. Show API
7. Delete API

Change-Id: Id8aff0f41caed6d52b20e7e5604dc28d8c56c567
blueprint: bp/support-etsi-nfv-specs
This commit is contained in:
tpatil 2020-02-19 05:46:46 +00:00
parent 840d754822
commit 428096a247
19 changed files with 3452 additions and 0 deletions

View File

@ -0,0 +1,204 @@
# TODO:Manually change from version 1.2 to 1.0
tosca_definitions_version: tosca_simple_yaml_1_2
#tosca_definitions_version: tosca_simple_yaml_1_2
description: ETSI NFV SOL 001 common types definitions version 2.6.1
metadata:
template_name: etsi_nfv_sol001_common_types
template_author: ETSI_NFV
template_version: 2.6.1
data_types:
tosca.datatypes.nfv.L2AddressData:
derived_from: tosca.datatypes.Root
description: Describes the information on the MAC addresses to be assigned to a connection point.
properties:
mac_address_assignment:
type: boolean
description: Specifies if the address assignment is the responsibility of management and orchestration function or not. If it is set to True, it is the management and orchestration function responsibility
required: true
tosca.datatypes.nfv.L3AddressData:
derived_from: tosca.datatypes.Root
description: Provides information about Layer 3 level addressing scheme and parameters applicable to a CP
properties:
ip_address_assignment:
type: boolean
description: Specifies if the address assignment is the responsibility of management and orchestration function or not. If it is set to True, it is the management and orchestration function responsibility
required: true
floating_ip_activated:
type: boolean
description: Specifies if the floating IP scheme is activated on the Connection Point or not
required: true
ip_address_type:
type: string
description: Defines address type. The address type should be aligned with the address type supported by the layer_protocols properties of the parent VnfExtCp
required: false
constraints:
- valid_values: [ ipv4, ipv6 ]
number_of_ip_address:
type: integer
description: Minimum number of IP addresses to be assigned
required: false
constraints:
- greater_than: 0
tosca.datatypes.nfv.AddressData:
derived_from: tosca.datatypes.Root
description: Describes information about the addressing scheme and parameters applicable to a CP
properties:
address_type:
type: string
description: Describes the type of the address to be assigned to a connection point. The content type shall be aligned with the address type supported by the layerProtocol property of the connection point
required: true
constraints:
- valid_values: [ mac_address, ip_address ]
l2_address_data:
type: tosca.datatypes.nfv.L2AddressData
description: Provides the information on the MAC addresses to be assigned to a connection point.
required: false
l3_address_data:
type: tosca.datatypes.nfv.L3AddressData
description: Provides the information on the IP addresses to be assigned to a connection point
required: false
tosca.datatypes.nfv.ConnectivityType:
derived_from: tosca.datatypes.Root
description: describes additional connectivity information of a virtualLink
properties:
layer_protocols:
type: list
description: Identifies the protocol a virtualLink gives access to (ethernet, mpls, odu2, ipv4, ipv6, pseudo-wire).The top layer protocol of the virtualLink protocol stack shall always be provided. The lower layer protocols may be included when there are specific requirements on these layers.
required: true
entry_schema:
type: string
constraints:
- valid_values: [ ethernet, mpls, odu2, ipv4, ipv6, pseudo-wire ]
flow_pattern:
type: string
description: Identifies the flow pattern of the connectivity
required: false
constraints:
- valid_values: [ line, tree, mesh ]
tosca.datatypes.nfv.LinkBitrateRequirements:
derived_from: tosca.datatypes.Root
description: describes the requirements in terms of bitrate for a virtual link
properties:
root:
type: integer # in bits per second
description: Specifies the throughput requirement in bits per second of the link (e.g. bitrate of E-Line, root bitrate of E-Tree, aggregate capacity of E-LAN).
required: true
constraints:
- greater_or_equal: 0
leaf:
type: integer # in bits per second
description: Specifies the throughput requirement in bits per second of leaf connections to the link when applicable to the connectivity type (e.g. for E-Tree and E LAN branches).
required: false
constraints:
- greater_or_equal: 0
tosca.datatypes.nfv.CpProtocolData:
derived_from: tosca.datatypes.Root
description: Describes and associates the protocol layer that a CP uses together with other protocol and connection point information
properties:
associated_layer_protocol:
type: string
required: true
description: One of the values of the property layer_protocols of the CP
constraints:
- valid_values: [ ethernet, mpls, odu2, ipv4, ipv6, pseudo-wire ]
address_data:
type: list
description: Provides information on the addresses to be assigned to the CP
entry_schema:
type: tosca.datatypes.nfv.AddressData
required: false
tosca.datatypes.nfv.VnfProfile:
derived_from: tosca.datatypes.Root
description: describes a profile for instantiating VNFs of a particular NS DF according to a specific VNFD and VNF DF.
properties:
instantiation_level:
type: string
description: Identifier of the instantiation level of the VNF DF to be used for instantiation. If not present, the default instantiation level as declared in the VNFD shall be used.
required: false
min_number_of_instances:
type: integer
description: Minimum number of instances of the VNF based on this VNFD that is permitted to exist for this VnfProfile.
required: true
constraints:
- greater_or_equal: 0
max_number_of_instances:
type: integer
description: Maximum number of instances of the VNF based on this VNFD that is permitted to exist for this VnfProfile.
required: true
constraints:
- greater_or_equal: 0
tosca.datatypes.nfv.Qos:
derived_from: tosca.datatypes.Root
description: describes QoS data for a given VL used in a VNF deployment flavour
properties:
latency:
type: scalar-unit.time #Number
description: Specifies the maximum latency
required: true
constraints:
- greater_than: 0 s
packet_delay_variation:
type: scalar-unit.time #Number
description: Specifies the maximum jitter
required: true
constraints:
- greater_or_equal: 0 s
packet_loss_ratio:
type: float
description: Specifies the maximum packet loss ratio
required: false
constraints:
- in_range: [ 0.0, 1.0 ]
capability_types:
tosca.capabilities.nfv.VirtualLinkable:
derived_from: tosca.capabilities.Node
description: A node type that includes the VirtualLinkable capability indicates that it can be pointed by tosca.relationships.nfv.VirtualLinksTo relationship type
relationship_types:
tosca.relationships.nfv.VirtualLinksTo:
derived_from: tosca.relationships.DependsOn
description: Represents an association relationship between the VduCp and VnfVirtualLink node types
valid_target_types: [ tosca.capabilities.nfv.VirtualLinkable ]
node_types:
tosca.nodes.nfv.Cp:
derived_from: tosca.nodes.Root
description: Provides information regarding the purpose of the connection point
properties:
layer_protocols:
type: list
description: Identifies which protocol the connection point uses for connectivity purposes
required: true
entry_schema:
type: string
constraints:
- valid_values: [ ethernet, mpls, odu2, ipv4, ipv6, pseudo-wire ]
role: #Name in ETSI NFV IFA011 v0.7.3: cpRole
type: string
description: Identifies the role of the port in the context of the traffic flow patterns in the VNF or parent NS
required: false
constraints:
- valid_values: [ root, leaf ]
description:
type: string
description: Provides human-readable information on the purpose of the connection point
required: false
protocol:
type: list
description: Provides information on the addresses to be assigned to the connection point(s) instantiated from this Connection Point Descriptor
required: false
entry_schema:
type: tosca.datatypes.nfv.CpProtocolData
trunk_mode:
type: boolean
description: Provides information about whether the CP instantiated from this Cp is in Trunk mode (802.1Q or other), When operating in "trunk mode", the Cp is capable of carrying traffic for several VLANs. Absence of this property implies that trunkMode is not configured for the Cp i.e. It is equivalent to boolean value "false".
required: false

View File

@ -0,0 +1,202 @@
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_external: [ CP1, 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:
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
VirtualStorage:
type: tosca.nodes.nfv.Vdu.VirtualBlockStorage
properties:
virtual_block_storage_data:
size_of_storage: 3 GB
rdma_enabled: true
sw_image_data:
name: VirtualStorage
version: '0.4.0'
checksum:
algorithm: sha-512
hash: 6513f21e44aa3da349f248188a44bc304a3653a04122d8fb4535423c8e1d14cd6a153f735bb0982e2161b5b5186106570c17a9e58b64dd39390617cd5a350f78
container_format: bare
disk_format: qcow2
min_disk: 2 GB
min_ram: 256 MB
size: 1 GB
artifacts:
sw_image:
type: tosca.artifacts.nfv.SwImage
file: ../Files/images/cirros-0.4.0-x86_64-disk.img
CP1:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 2
requirements:
- virtual_binding: VDU1
- virtual_link: internalVL1
internalVL1:
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.33.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 ]
- 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 ]
- VDU1_instantiation_levels:
type: tosca.policies.nfv.VduInstantiationLevels
properties:
levels:
instantiation_level_1:
number_of_instances: 1
instantiation_level_2:
number_of_instances: 1
targets: [ VDU1 ]
- 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 ]

View File

@ -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

View File

@ -0,0 +1,53 @@
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_external:
capability: tosca.capabilities.nfv.VirtualLinkable
- virtual_link_internal:
capability: tosca.capabilities.nfv.VirtualLinkable
interfaces:
Vnflcm:
type: tosca.interfaces.nfv.Vnflcm

View File

@ -0,0 +1,7 @@
TOSCA-Meta-File-Version: 1.0
Created-by: Hiroyuki JO
CSAR-Version: 1.1
Entry-Definitions: Definitions/helloworld3_top.vnfd.yaml
Name: Files/images/cirros-0.4.0-x86_64-disk.img
Content-type: application/x-iso9066-image

View File

@ -0,0 +1,193 @@
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_external: [ CP1, 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: 1
sw_image_data:
name: Software of VDU1
version: '0.4.0'
checksum:
algorithm: sha-512
hash: 6513f21e44aa3da349f248188a44bc304a3653a04122d8fb4535423c8e1d14cd6a153f735bb0982e2161b5b5186106570c17a9e58b64dd39390617cd5a350f78
container_format: bare
disk_format: qcow2
min_disk: 1 GB
size: 1 GB
artifacts:
sw_image:
type: tosca.artifacts.nfv.SwImage
file: ../Files/images/cirros-0.4.0-x86_64-disk.img
capabilities:
virtual_compute:
properties:
virtual_memory:
virtual_mem_size: 512 MB
virtual_cpu:
num_virtual_cpu: 1
virtual_local_storage:
- size_of_storage: 2 GB
CP1:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 2
requirements:
- virtual_binding: VDU1
- virtual_link: internalVL1
internalVL1:
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: 11.11.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 ]
- 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 ]
- VDU1_instantiation_levels:
type: tosca.policies.nfv.VduInstantiationLevels
properties:
levels:
instantiation_level_1:
number_of_instances: 1
instantiation_level_2:
number_of_instances: 1
targets: [ VDU1 ]
- 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 ]

View File

@ -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-4840d7000001
provider: Company
product_name: Sample VNF with instantiation level
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

View File

@ -0,0 +1,53 @@
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-4840d7000001 ] ]
default: b1bb0ce7-ebca-4fa7-95ed-4840d7000001
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 with instantiation level' ] ]
default: 'Sample VNF with instantiation level'
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: "flavour"
requirements:
- virtual_link_external:
capability: tosca.capabilities.nfv.VirtualLinkable
- virtual_link_internal:
capability: tosca.capabilities.nfv.VirtualLinkable
interfaces:
Vnflcm:
type: tosca.interfaces.nfv.Vnflcm

View File

@ -0,0 +1,7 @@
TOSCA-Meta-File-Version: 1.0
Created-by: Hiroyuki JO
CSAR-Version: 1.1
Entry-Definitions: Definitions/helloworld3_top.vnfd.yaml
Name: Files/images/cirros-0.4.0-x86_64-disk.img
Content-type: application/x-iso9066-image

View File

@ -0,0 +1,32 @@
tosca_definitions_version: tosca_simple_yaml_1_2
description: Sample VNF of NTT NS lab.
imports:
- etsi_nfv_sol001_common_types.yaml
- etsi_nfv_sol001_vnfd_types.yaml
- helloworld3VNF.yaml
- helloworld3simple.vnfd.tosca.yaml
# - helloworld3complex.vnfd.tosca.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: ntt.nslab.VNF
properties:
flavour_id: { get_input: selected_flavour }
descriptor_id: b1bb0ce7-ebca-4fa7-95ed-4840d7000022
provider: NTT NS lab
product_name: Sample VNF No 22 functional
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

View File

@ -0,0 +1,53 @@
tosca_definitions_version: tosca_simple_yaml_1_2
description: ntt.nslab.VNF type definition
imports:
- etsi_nfv_sol001_common_types.yaml
- etsi_nfv_sol001_vnfd_types.yaml
node_types:
ntt.nslab.VNF:
derived_from: tosca.nodes.nfv.VNF
properties:
descriptor_id:
type: string
constraints: [ valid_values: [ b1bb0ce7-ebca-4fa7-95ed-4840d7000022 ] ]
default: b1bb0ce7-ebca-4fa7-95ed-4840d7000022
descriptor_version:
type: string
constraints: [ valid_values: [ '1.0' ] ]
default: '1.0'
provider:
type: string
constraints: [ valid_values: [ 'NTT NS lab' ] ]
default: 'NTT NS lab'
product_name:
type: string
constraints: [ valid_values: [ 'Sample VNF No 22 functional' ] ]
default: 'Sample VNF No 22 functional'
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: "test_flavour"
requirements:
- virtual_link_external:
capability: tosca.capabilities.nfv.VirtualLinkable
- virtual_link_internal:
capability: tosca.capabilities.nfv.VirtualLinkable
interfaces:
Vnflcm:
type: tosca.interfaces.nfv.Vnflcm

View File

@ -0,0 +1,129 @@
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
- helloworld3VNF.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: ntt.nslab.VNF
properties:
flavour_id: simple
requirements:
virtual_link_external: [ CP1, virtual_link ]
node_templates:
VNF:
type: ntt.nslab.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: Software of VDU1
version: '0.4.0'
checksum:
algorithm: sha-512
hash: 6513f21e44aa3da349f248188a44bc304a3653a04122d8fb4535423c8e1d14cd6a153f735bb0982e2161b5b5186106570c17a9e58b64dd39390617cd5a350f78
container_format: bare
disk_format: qcow2
min_disk: 1 GB
size: 1 GB
artifacts:
sw_image:
type: tosca.artifacts.nfv.SwImage
file: ../Files/images/cirros-0.4.0-x86_64-disk.img
capabilities:
virtual_compute:
properties:
virtual_memory:
virtual_mem_size: 512 MB
virtual_cpu:
num_virtual_cpu: 1
virtual_local_storage:
- size_of_storage: 2 GB
CP1:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 1
requirements:
- virtual_binding: VDU1
CP2:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 1
requirements:
- virtual_binding: VDU1
CP3:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 2
requirements:
- virtual_binding: VDU1
- virtual_link: VL3
VL3:
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.33.0.0/24

View File

@ -0,0 +1,7 @@
TOSCA-Meta-File-Version: 1.0
Created-by: Hiroyuki JO
CSAR-Version: 1.1
Entry-Definitions: Definitions/helloworld3.vnfd.tosca.yaml
Name: Files/images/cirros-0.4.0-x86_64-disk.img
Content-type: application/x-iso9066-image

View File

@ -333,3 +333,23 @@ class BaseTackerTest(base.BaseTestCase):
}
}
self.client.post('/vnfs/%s/triggers' % vnf, body)
def assertDictSupersetOf(self, expected_subset, actual_superset):
"""Checks that actual dict contains the expected dict.
After checking that the arguments are of the right type, this checks
that each item in expected_subset is in, and matches, what is in
actual_superset. Separate tests are done, so that detailed info can
be reported upon failure.
"""
if not isinstance(expected_subset, dict):
self.fail("expected_subset (%s) is not an instance of dict" %
type(expected_subset))
if not isinstance(actual_superset, dict):
self.fail("actual_superset (%s) is not an instance of dict" %
type(actual_superset))
for k, v in expected_subset.items():
self.assertIn(k, actual_superset)
self.assertEqual(v, actual_superset[k],
"Key %(key)s expected: %(exp)r, actual %(act)r" %
{'key': k, 'exp': v, 'act': actual_superset[k]})

View File

@ -0,0 +1,883 @@
# Copyright (C) 2020 NTT DATA
# All Rights Reserved.
#
# 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.
import os
import random
import time
from oslo_serialization import jsonutils
from oslo_utils import uuidutils
from tacker.objects import fields
from tacker.tests.functional import base
from tacker.tests import utils
from tacker.vnfm.infra_drivers.openstack import constants as infra_cnst
VNF_PACKAGE_UPLOAD_TIMEOUT = 300
VNF_INSTANTIATE_TIMEOUT = 600
VNF_TERMINATE_TIMEOUT = 600
VNF_HEAL_TIMEOUT = 600
RETRY_WAIT_TIME = 5
def get_ext_managed_virtual_link(id, vl_desc_id, resource_id):
return [{"id": id, "vnfVirtualLinkDescId": vl_desc_id,
"resourceId": resource_id}]
def generate_mac_address():
"""Generate an Ethernet MAC address."""
mac = [0xfa, 0x16, 0x3e,
random.randint(0x00, 0xff),
random.randint(0x00, 0xff),
random.randint(0x00, 0xff)]
return ':'.join(map(lambda x: "%02x" % x, mac))
def get_external_virtual_links(net_0_resource_id, net_mgmt_resource_id,
port_uuid):
return [
{
"id": "net0",
"resourceId": net_0_resource_id,
"extCps": [{
"cpdId": "CP1",
"cpConfig": [{
"cpProtocolData": [{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"macAddress": generate_mac_address()
}
}]
}]
}]},
{
"id": "external_network",
"resourceId": net_mgmt_resource_id,
"extCps": [{
"cpdId": "CP2",
"cpConfig": [{
"linkPortId": "413f4e46-21cf-41b1-be0f-de8d23f76cfe",
"cpProtocolData": [{
"layerProtocol": "IP_OVER_ETHERNET"
}]
}]
}],
"extLinkPorts": [{
"id": "413f4e46-21cf-41b1-be0f-de8d23f76cfe",
"resourceHandle": {
"resourceId": port_uuid,
"vimLevelResourceType": "LINKPORT"
}
}]
}
]
def _create_and_upload_vnf_package(tacker_client, csar_package_name,
user_defined_data):
# create vnf package
body = jsonutils.dumps({"userDefinedData": user_defined_data})
resp, vnf_package = tacker_client.do_request(
'/vnfpkgm/v1/vnf_packages', "POST", body=body)
# upload vnf package
csar_package_path = "../../etc/samples/etsi/nfv/%s" % csar_package_name
file_path = os.path.abspath(os.path.join(os.path.dirname(__file__),
csar_package_path))
# Generating unique vnfd id. This is required when multiple workers
# are running concurrently. The call below creates a new temporary
# CSAR with unique vnfd id.
file_path, uniqueid = utils.create_csar_with_unique_vnfd_id(file_path)
with open(file_path, 'rb') as file_object:
resp, resp_body = tacker_client.do_request(
'/vnfpkgm/v1/vnf_packages/{id}/package_content'.format(
id=vnf_package['id']),
"PUT", body=file_object, content_type='application/zip')
# wait for onboard
timeout = VNF_PACKAGE_UPLOAD_TIMEOUT
start_time = int(time.time())
show_url = os.path.join('/vnfpkgm/v1/vnf_packages', vnf_package['id'])
vnfd_id = None
while True:
resp, body = tacker_client.do_request(show_url, "GET")
if body['onboardingState'] == "ONBOARDED":
vnfd_id = body['vnfdId']
break
if ((int(time.time()) - start_time) > timeout):
raise Exception("Failed to onboard vnf package")
time.sleep(1)
# remove temporarily created CSAR file
os.remove(file_path)
return vnf_package['id'], vnfd_id
class VnfLcmTest(base.BaseTackerTest):
@classmethod
def setUpClass(cls):
cls.tacker_client = base.BaseTackerTest.tacker_http_client()
cls.vnf_package_1, cls.vnfd_id_1 = _create_and_upload_vnf_package(
cls.tacker_client, "vnflcm1", {"key": "sample_1_functional"})
cls.vnf_package_2, cls.vnfd_id_2 = _create_and_upload_vnf_package(
cls.tacker_client, "vnflcm2", {"key": "sample_2_functional"})
cls.vnf_package_3, cls.vnfd_id_3 = _create_and_upload_vnf_package(
cls.tacker_client, "vnflcm3", {"key": "sample_3_functional"})
super(VnfLcmTest, cls).setUpClass()
@classmethod
def tearDownClass(cls):
# Update vnf package operational state to DISABLED
update_req_body = jsonutils.dumps({
"operationalState": "DISABLED"})
base_path = "/vnfpkgm/v1/vnf_packages"
for package_id in [cls.vnf_package_1, cls.vnf_package_2,
cls.vnf_package_3]:
resp, resp_body = cls.tacker_client.do_request(
'{base_path}/{id}'.format(id=package_id,
base_path=base_path),
"PATCH", content_type='application/json', body=update_req_body)
# Delete vnf package
url = '/vnfpkgm/v1/vnf_packages/%s' % package_id
cls.tacker_client.do_request(url, "DELETE")
super(VnfLcmTest, cls).tearDownClass()
def setUp(self):
super(VnfLcmTest, self).setUp()
self.base_url = "/vnflcm/v1/vnf_instances"
vim_list = self.client.list_vims()
if not vim_list:
self.skipTest("Vims are not configured")
vim_id = 'VIM0'
vim = self.get_vim(vim_list, vim_id)
if not vim:
self.skipTest("Default VIM '%s' is missing" % vim_id)
self.vim_id = vim['id']
def _instantiate_vnf_request(self, flavour_id,
instantiation_level_id=None, vim_id=None, ext_vl=None,
ext_managed_vl=None):
request_body = {"flavourId": flavour_id}
if instantiation_level_id:
request_body["instantiationLevelId"] = instantiation_level_id
if ext_managed_vl:
request_body["extManagedVirtualLinks"] = ext_managed_vl
if ext_vl:
request_body["extVirtualLinks"] = ext_vl
if vim_id:
request_body["vimConnectionInfo"] = [
{"id": uuidutils.generate_uuid(),
"vimId": vim_id,
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.v_2"}]
return request_body
def _create_vnf_instance(self, vnfd_id, vnf_instance_name=None,
vnf_instance_description=None):
request_body = {'vnfdId': vnfd_id}
if vnf_instance_name:
request_body['vnfInstanceName'] = vnf_instance_name
if vnf_instance_description:
request_body['vnfInstanceDescription'] = vnf_instance_description
resp, response_body = self.http_client.do_request(
self.base_url, "POST", body=jsonutils.dumps(request_body))
return resp, response_body
def _delete_wait_vnf_instance(self, id):
timeout = VNF_TERMINATE_TIMEOUT
url = os.path.join(self.base_url, id)
start_time = int(time.time())
while True:
resp, body = self.http_client.do_request(url, "DELETE")
if 204 == resp.status_code:
break
if ((int(time.time()) - start_time) > timeout):
error = "Failed to delete vnf instance %s"
self.fail(error % id)
time.sleep(RETRY_WAIT_TIME)
def _delete_vnf_instance(self, id):
self._delete_wait_vnf_instance(id)
# verify vnf instance is deleted
url = os.path.join(self.base_url, id)
resp, body = self.http_client.do_request(url, "GET")
self.assertEqual(404, resp.status_code)
def _show_vnf_instance(self, id, expected_result=None):
show_url = os.path.join(self.base_url, id)
resp, vnf_instance = self.http_client.do_request(show_url, "GET")
self.assertEqual(200, resp.status_code)
if expected_result:
self.assertDictSupersetOf(expected_result, vnf_instance)
return vnf_instance
def _list_vnf_instances(self):
resp, vnf_instances = self.http_client.do_request(self.base_url, "GET")
self.assertEqual(200, resp.status_code)
return vnf_instances
def _stack_update_wait(self, stack_id, expected_status):
timeout = VNF_HEAL_TIMEOUT
start_time = int(time.time())
while True:
stack = self.h_client.stacks.get(stack_id)
if stack.stack_status == expected_status:
break
if ((int(time.time()) - start_time) > timeout):
error = ("Stack %(id)s status is %(current)s, expected status "
"should be %(expected)s")
self.fail(error % {"id": stack_id, "current": stack.status,
"expected": expected_status})
time.sleep(RETRY_WAIT_TIME)
def _vnf_instance_wait(self, id,
instantiation_state=fields.VnfInstanceState.INSTANTIATED,
timeout=VNF_INSTANTIATE_TIMEOUT):
show_url = os.path.join(self.base_url, id)
start_time = int(time.time())
while True:
resp, body = self.http_client.do_request(show_url, "GET")
if body['instantiationState'] == instantiation_state:
break
if ((int(time.time()) - start_time) > timeout):
error = ("Vnf instance %(id)s status is %(current)s, "
"expected status should be %(expected)s")
self.fail(error % {"id": id,
"current": body['instantiationState'],
"expected": instantiation_state})
time.sleep(RETRY_WAIT_TIME)
def _create_network(self, neutron_client, network_name):
net = neutron_client.create_network(
{'network': {'name': "network-%s" % uuidutils.generate_uuid()}})
net_id = net['network']['id']
self.addCleanup(neutron_client.delete_network, net_id)
return net_id
def _create_subnet(self, neutron_client, network_id):
body = {'subnet': {'network_id': network_id,
'name': "subnet-%s" % uuidutils.generate_uuid(),
'cidr': "22.22.0.0/24",
'ip_version': 4,
'gateway_ip': '22.22.0.1',
"enable_dhcp": True}}
subnet = neutron_client.create_subnet(body=body)["subnet"]
self.addCleanup(neutron_client.delete_subnet, subnet['id'])
return subnet['id']
def _create_port(self, neutron_client, network_id):
body = {'port': {'network_id': network_id}}
port = neutron_client.create_port(body=body)["port"]
self.addCleanup(neutron_client.delete_port, port['id'])
return port['id']
def _instantiate_vnf_instance(self, id, request_body):
url = os.path.join(self.base_url, id, "instantiate")
resp, body = self.http_client.do_request(url, "POST",
body=jsonutils.dumps(request_body))
self.assertEqual(202, resp.status_code)
self._vnf_instance_wait(id)
def _terminate_vnf_instance(self, id, request_body):
url = os.path.join(self.base_url, id, "terminate")
resp, body = self.http_client.do_request(url, "POST",
body=jsonutils.dumps(request_body))
self.assertEqual(202, resp.status_code)
timeout = request_body.get('gracefulTerminationTimeout')
start_time = int(time.time())
self._vnf_instance_wait(id,
instantiation_state=fields.VnfInstanceState.NOT_INSTANTIATED,
timeout=VNF_TERMINATE_TIMEOUT)
# If gracefulTerminationTimeout is set, check whether vnf
# instantiation_state is set to NOT_INSTANTIATED after
# gracefulTerminationTimeout seconds.
if timeout and int(time.time()) - start_time < timeout:
self.fail("Vnf is terminated before graceful termination"
"timeout period")
def _heal_vnf_instance(self, vnf_instance, request_body,
expected_stack_status=infra_cnst.STACK_UPDATE_COMPLETE):
url = os.path.join(self.base_url, vnf_instance['id'], "heal")
resp, body = self.http_client.do_request(url, "POST",
body=jsonutils.dumps(request_body))
self.assertEqual(202, resp.status_code)
stack = self.h_client.stacks.get(vnf_instance['vnfInstanceName'])
# Wait until tacker heals the stack resources as requested in
# in the heal request
self._stack_update_wait(stack.id, expected_stack_status)
def _heal_sol_003_vnf_instance(self, vnf_instance, request_body):
url = os.path.join(self.base_url, vnf_instance['id'], "heal")
resp, body = self.http_client.do_request(url, "POST",
body=jsonutils.dumps(request_body))
self.assertEqual(202, resp.status_code)
# If healing is done without vnfc components, it will delete the
# stack and create a new one. So wait until vnf is deleted and then
# wait until a new stack is created using vnfInstanceName and once
# the stack is created, wait until it's status becomes
# CREATE_COMPLETE.
stack = self.h_client.stacks.get(vnf_instance['vnfInstanceName'])
self._stack_update_wait(stack.id, infra_cnst.STACK_DELETE_COMPLETE)
start_time = int(time.time())
timeout = VNF_INSTANTIATE_TIMEOUT
while True:
try:
stack = self.h_client.stacks.get(
vnf_instance['vnfInstanceName'])
if stack.stack_status == infra_cnst.STACK_CREATE_COMPLETE:
break
except Exception:
pass
if ((int(time.time()) - start_time) > timeout):
self.fail("Failed to heal vnf during instantiation")
def _get_server(self, server_id):
try:
self.novaclient().servers.get(server_id)
except Exception:
self.fail("Failed to get vdu resource %s id" % server_id)
def _verify_vnfc_resource_info(self, vnf_instance_old,
vnf_instance_current, vdu_count):
vnfc_resource_info_old = (vnf_instance_old['instantiatedVnfInfo']
['vnfcResourceInfo'])
vnfc_resource_info_current = (vnf_instance_current
['instantiatedVnfInfo']['vnfcResourceInfo'])
for index in range(vdu_count):
# compare computeResource resourceId is different
vdu_resource_id_old = (vnfc_resource_info_old[index]
['computeResource']['resourceId'])
vdu_resource_id_current = (vnfc_resource_info_current[index]
['computeResource']['resourceId'])
self.assertNotEqual(vdu_resource_id_old, vdu_resource_id_current)
# Now check whether vdus are healed properly and servers exists
# in nova.
self._get_server(vdu_resource_id_current)
def test_create_show_delete_vnf_instance(self):
"""Create, show and delete a vnf instance."""
# Create vnf instance
vnf_instance_name = "Test-VNf-Instance"
vnf_instance_description = "Sample VNF for LCM Testing"
resp, vnf_instance = self._create_vnf_instance(self.vnfd_id_1,
vnf_instance_name=vnf_instance_name,
vnf_instance_description=vnf_instance_description)
self.assertIsNotNone(vnf_instance['id'])
self.assertEqual(201, resp.status_code)
expected_result = {
"instantiationState": fields.VnfInstanceState.NOT_INSTANTIATED,
"vnfInstanceName": vnf_instance_name,
"vnfInstanceDescription": vnf_instance_description
}
self._show_vnf_instance(vnf_instance['id'], expected_result)
self._delete_vnf_instance(vnf_instance['id'])
def test_list_vnf_instances(self):
"""Create vnf instances and check list API display those vnfs."""
# Create vnf instance 01 and don't instantiate this one.
vnf_instance_name = "List-VNF-Instance-0"
resp, vnf_instance_0 = self._create_vnf_instance(self.vnfd_id_1,
vnf_instance_name=vnf_instance_name)
self.assertIsNotNone(vnf_instance_0['id'])
self.assertEqual(201, resp.status_code)
self.addCleanup(self._delete_vnf_instance, vnf_instance_0['id'])
# Create vnf instance 02 and instantiate this one.
vnf_instance_name = "List-VNF-Instance-1"
resp, vnf_instance_1 = self._create_vnf_instance(self.vnfd_id_1,
vnf_instance_name=vnf_instance_name)
self.assertIsNotNone(vnf_instance_1['id'])
self.assertEqual(201, resp.status_code)
request_body = self._instantiate_vnf_request("simple",
vim_id=self.vim_id)
self._instantiate_vnf_instance(vnf_instance_1['id'], request_body)
# Terminate vnf gracefully with graceful timeout set to 60
terminate_req_body = {
"terminationType": fields.VnfInstanceTerminationType.GRACEFUL,
'gracefulTerminationTimeout': 60
}
self.addCleanup(self._delete_vnf_instance, vnf_instance_1['id'])
self.addCleanup(self._terminate_vnf_instance, vnf_instance_1['id'],
terminate_req_body)
# List vnf instances to check if first one is in NOT_INSTANTIATED
# state and the second one is INSTANTIATED
vnf_instances = self._list_vnf_instances()
for vnf_instance in vnf_instances:
if vnf_instance['id'] == vnf_instance_0['id']:
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
vnf_instance['instantiationState'])
elif vnf_instance['id'] == vnf_instance_1['id']:
self.assertEqual(fields.VnfInstanceState.INSTANTIATED,
vnf_instance['instantiationState'])
def test_instantiate_vnf_with_flavour(self):
"""Test instantiation and heal API without instantiation level
This test will instantiate vnf using flavour. Heal API will be invoked
by passing vnfcInstanceId parameter in the request body as per SOL002
HealVnfRequest.
"""
# Create vnf instance
vnf_instance_name = "vnf_with_flavour-%s" % uuidutils.generate_uuid()
vnf_instance_description = "vnf with instantiation level and no ext vl"
resp, vnf_instance = self._create_vnf_instance(self.vnfd_id_1,
vnf_instance_name=vnf_instance_name,
vnf_instance_description=vnf_instance_description)
self.assertIsNotNone(vnf_instance['id'])
self.assertEqual(201, resp.status_code)
request_body = self._instantiate_vnf_request("simple",
vim_id=self.vim_id)
self._instantiate_vnf_instance(vnf_instance['id'], request_body)
vnf_instance = self._show_vnf_instance(vnf_instance['id'])
vdu_count = len(vnf_instance['instantiatedVnfInfo']
['vnfcResourceInfo'])
self.assertEqual(1, vdu_count)
# heal as per SOL002 API check, i.e. pass vnfcInstanceId in the
# HealVnfRequest.
vnfc_resource_info = (vnf_instance['instantiatedVnfInfo']
['vnfcResourceInfo'])
vnfInstanceIds = [vnfc_res_info['id'] for vnfc_res_info in
vnfc_resource_info]
heal_request_body = {
"cause": "Heal as per SOL002 API check",
"vnfcInstanceId": vnfInstanceIds
}
self._heal_vnf_instance(vnf_instance, heal_request_body)
# NOTE(tpatil) Wait for sometime as it takes a while to update
# vnfcResourceInfo after the stack status becomes UPDATE_COMPLETE.
# There is no intermediate status set to VNF which can be used here
# to confirm healing action is completed successfully.
time.sleep(20)
vnf_instance_current = self._show_vnf_instance(vnf_instance['id'])
self._verify_vnfc_resource_info(vnf_instance, vnf_instance_current, 1)
# Terminate vnf gracefully with graceful timeout set to 60
terminate_req_body = {
"terminationType": fields.VnfInstanceTerminationType.GRACEFUL,
'gracefulTerminationTimeout': 60
}
self._terminate_vnf_instance(vnf_instance['id'], terminate_req_body)
self._delete_vnf_instance(vnf_instance['id'])
def test_instantiate_vnf_with_instantiation_level(self):
"""Test instantiation and heal API with instantiation level
This test will instantiate vnf with instantiation level. Heal API
will be invoked by passing vnfcInstanceId parameter in the request
body as per SOL002 HealVnfRequest.
"""
# Create vnf instance
vnf_instance_name = "vnf_with_instantiation_level-%s" % \
uuidutils.generate_uuid()
vnf_instance_description = "vnf with instantiation level 2"
resp, vnf_instance = self._create_vnf_instance(self.vnfd_id_2,
vnf_instance_name=vnf_instance_name,
vnf_instance_description=vnf_instance_description)
self.assertIsNotNone(vnf_instance['id'])
self.assertEqual(201, resp.status_code)
request_body = self._instantiate_vnf_request("simple",
instantiation_level_id="instantiation_level_2",
vim_id=self.vim_id)
self._instantiate_vnf_instance(vnf_instance['id'], request_body)
vnf_instance = self._show_vnf_instance(vnf_instance['id'])
vdu_count = len(vnf_instance['instantiatedVnfInfo']
['vnfcResourceInfo'])
self.assertEqual(3, vdu_count)
# heal as per SOL002 API check, i.e.vnfcInstanceId is passed in
# the HealVnfRequest.
vnfc_resource_info = (vnf_instance['instantiatedVnfInfo']
['vnfcResourceInfo'])
vnfInstanceIds = [vnfc_res_info['id'] for vnfc_res_info in
vnfc_resource_info]
heal_request_body = {
"cause": "Heal as per SOL002 API check",
"vnfcInstanceId": vnfInstanceIds
}
self._heal_vnf_instance(vnf_instance, heal_request_body)
# NOTE(tpatil) Wait for sometime as it takes a while to update
# vnfcResourceInfo after the stack status becomes UPDATE_COMPLETE.
# There is no intermediate status set to VNF which can be used here
# to confirm healing action is completed successfully.
time.sleep(20)
vnf_instance_current = self._show_vnf_instance(vnf_instance['id'])
self._verify_vnfc_resource_info(vnf_instance, vnf_instance_current, 3)
# Terminate vnf forcefully
terminate_req_body = {
"terminationType": fields.VnfInstanceTerminationType.FORCEFUL
}
self._terminate_vnf_instance(vnf_instance['id'], terminate_req_body)
self._delete_vnf_instance(vnf_instance['id'])
def test_instantiate_vnf_with_ext_vl_and_ext_managed_vl(self):
"""Test instantiation vnf with external virtual links
This test will instantiate vnf with external virtual links and
external managed virtual links. Heal API will be invoked by
passing vnfcInstanceId parameter in the request body as per SOL002
HealVnfRequest.
"""
# Create vnf instance
vnf_instance_name = "vnf_with_ext_vl_and_ext_managed_vl-%s" % \
uuidutils.generate_uuid()
vnf_instance_description = "vnf_with_ext_vl_and_ext_managed_vl"
resp, vnf_instance = self._create_vnf_instance(self.vnfd_id_3,
vnf_instance_name=vnf_instance_name,
vnf_instance_description=vnf_instance_description)
self.assertIsNotNone(vnf_instance['id'])
self.assertEqual(201, resp.status_code)
neutron_client = self.neutronclient()
net = neutron_client.list_networks()
networks = {}
for network in net['networks']:
networks[network['name']] = network['id']
net1_id = networks.get('net1')
if not net1_id:
self.fail("net1 network is not available")
net0_id = networks.get('net0')
if not net0_id:
self.fail("net0 network is not available")
net_mgmt_id = networks.get('net_mgmt')
if not net_mgmt_id:
self.fail("net_mgmt network is not available")
ext_managed_vl = get_ext_managed_virtual_link("net1", "VL3",
net1_id)
network_uuid = self._create_network(neutron_client,
"external_network")
self._create_subnet(neutron_client, network_uuid)
port_uuid = self._create_port(neutron_client, network_uuid)
ext_vl = get_external_virtual_links(net0_id, net_mgmt_id,
port_uuid)
request_body = self._instantiate_vnf_request("simple",
vim_id=self.vim_id, ext_vl=ext_vl, ext_managed_vl=ext_managed_vl)
self._instantiate_vnf_instance(vnf_instance['id'], request_body)
vnf_instance = self._show_vnf_instance(vnf_instance['id'])
vdu_count = len(vnf_instance['instantiatedVnfInfo']
['vnfcResourceInfo'])
self.assertEqual(1, vdu_count)
# heal as per SOL002 API check, i.e.vnfcInstanceId is passed in
# the HealVnfRequest.
vnfc_resource_info = (vnf_instance['instantiatedVnfInfo']
['vnfcResourceInfo'])
vnfInstanceIds = [vnfc_res_info['id'] for vnfc_res_info in
vnfc_resource_info]
heal_request_body = {
"cause": "Heal as per SOL002 API check",
"vnfcInstanceId": vnfInstanceIds
}
self._heal_vnf_instance(vnf_instance, heal_request_body)
# NOTE(tpatil) Wait for sometime as it takes a while to update
# vnfcResourceInfo after the stack status becomes UPDATE_COMPLETE.
# There is no intermediate status set to VNF which can be used here
# to confirm healing action is completed successfully.
time.sleep(20)
vnf_instance_current = self._show_vnf_instance(vnf_instance['id'])
self._verify_vnfc_resource_info(vnf_instance, vnf_instance_current, 1)
# Terminate vnf forcefully
terminate_req_body = {
"terminationType": fields.VnfInstanceTerminationType.FORCEFUL
}
self._terminate_vnf_instance(vnf_instance['id'], terminate_req_body)
self._delete_vnf_instance(vnf_instance['id'])
def test_heal_vnf_sol_003_with_flavour(self):
"""Test heal API as per SOL 003 for VNF created with flavor
This test will instantiate vnf using flavour. Heal API will be invoked
as per SOL003 i.e. without passing vnfcInstanceId, so that the entire
vnf is healed which includes VDU/CP/VL/STORAGE.
"""
# Create vnf instance
vnf_instance_name = "heal_vnf_sol_003_with_flavour-%s" % \
uuidutils.generate_uuid()
vnf_instance_description = "vnf with instantiation level and no ext vl"
resp, vnf_instance = self._create_vnf_instance(self.vnfd_id_1,
vnf_instance_name=vnf_instance_name,
vnf_instance_description=vnf_instance_description)
self.assertIsNotNone(vnf_instance['id'])
self.assertEqual(201, resp.status_code)
request_body = self._instantiate_vnf_request("simple",
vim_id=self.vim_id)
self._instantiate_vnf_instance(vnf_instance['id'], request_body)
vnf_instance = self._show_vnf_instance(vnf_instance['id'])
vdu_count = len(vnf_instance['instantiatedVnfInfo']
['vnfcResourceInfo'])
self.assertEqual(1, vdu_count)
# heal as per SOL003 API check, i.e. without passing vnfcInstanceId in
# the HealVnfRequest.
heal_request_body = {
"cause": "Heal as per SOL003 API check",
}
self._heal_sol_003_vnf_instance(vnf_instance, heal_request_body)
# NOTE(tpatil) Wait for sometime as it takes a while to update
# vnfcResourceInfo after the stack status becomes UPDATE_COMPLETE.
# There is no intermediate status set to VNF which can be used here
# to confirm healing action is completed successfully.
time.sleep(20)
vnf_instance_current = self._show_vnf_instance(vnf_instance['id'])
self._verify_vnfc_resource_info(vnf_instance, vnf_instance_current, 1)
# Terminate vnf gracefully with graceful timeout set to 60
terminate_req_body = {
"terminationType": fields.VnfInstanceTerminationType.GRACEFUL,
'gracefulTerminationTimeout': 60
}
self._terminate_vnf_instance(vnf_instance['id'], terminate_req_body)
self._delete_vnf_instance(vnf_instance['id'])
def test_heal_vnf_sol_003_with_instantiation_level(self):
"""Test heal as per SOL003 for VNF created with instantiation level
This test will instantiate vnf with instantiation level. Heal API will
be invoked as per SOL003 i.e. without passing vnfcInstanceId, so that
the entire vnf is healed which includes VDU/CP/VL/STORAGE.
"""
# Create vnf instance
vnf_instance_name = "heal_vnf_sol_003_with_instantiation_level-%s" % \
uuidutils.generate_uuid()
vnf_instance_description = "vnf with instantiation level 2"
resp, vnf_instance = self._create_vnf_instance(self.vnfd_id_2,
vnf_instance_name=vnf_instance_name,
vnf_instance_description=vnf_instance_description)
self.assertIsNotNone(vnf_instance['id'])
self.assertEqual(201, resp.status_code)
request_body = self._instantiate_vnf_request("simple",
instantiation_level_id="instantiation_level_2",
vim_id=self.vim_id)
self._instantiate_vnf_instance(vnf_instance['id'], request_body)
vnf_instance = self._show_vnf_instance(vnf_instance['id'])
vdu_count = len(vnf_instance['instantiatedVnfInfo']
['vnfcResourceInfo'])
self.assertEqual(3, vdu_count)
# heal as per SOL003 API check, i.e. without passing vnfcInstanceId
# in the HealVnfRequest.
heal_request_body = {
"cause": "Heal as per SOL003 API check",
}
self._heal_sol_003_vnf_instance(vnf_instance, heal_request_body)
# NOTE(tpatil) Wait for sometime as it takes a while to update
# vnfcResourceInfo after the stack status becomes UPDATE_COMPLETE.
# There is no intermediate status set to VNF which can be used here
# to confirm healing action is completed successfully.
time.sleep(20)
vnf_instance_current = self._show_vnf_instance(vnf_instance['id'])
self._verify_vnfc_resource_info(vnf_instance, vnf_instance_current, 3)
# Terminate vnf gracefully with graceful timeout set to 60
terminate_req_body = {
"terminationType": fields.VnfInstanceTerminationType.GRACEFUL,
'gracefulTerminationTimeout': 60
}
self._terminate_vnf_instance(vnf_instance['id'], terminate_req_body)
self._delete_vnf_instance(vnf_instance['id'])
def test_heal_vnf_sol_003_ext_vl_and_ext_managed_vl(self):
"""Test heal vnf as per SOL003 with vnf created using external vl.
This test will instantiate vnf with external virtual links and
external managed virtual links. Heal API will be invoked as per SOL003
i.e. without passing vnfcInstanceId, so that the entire vnf is healed
which includes VDU/CP/VL/STORAGE.
"""
# Create vnf instance
vnf_instance_name = "vnf_with_ext_vl_and_ext_managed_vl-%s" % \
uuidutils.generate_uuid()
vnf_instance_description = "vnf_with_ext_vl_and_ext_managed_vl"
resp, vnf_instance = self._create_vnf_instance(self.vnfd_id_3,
vnf_instance_name=vnf_instance_name,
vnf_instance_description=vnf_instance_description)
self.assertIsNotNone(vnf_instance['id'])
self.assertEqual(201, resp.status_code)
neutron_client = self.neutronclient()
net = neutron_client.list_networks()
networks = {}
for network in net['networks']:
networks[network['name']] = network['id']
net1_id = networks.get('net1')
if not net1_id:
self.fail("net1 network is not available")
net0_id = networks.get('net0')
if not net0_id:
self.fail("net0 network is not available")
net_mgmt_id = networks.get('net_mgmt')
if not net_mgmt_id:
self.fail("net_mgmt network is not available")
ext_managed_vl = get_ext_managed_virtual_link("net1", "VL3",
net1_id)
network_uuid = self._create_network(neutron_client,
"external_network")
self._create_subnet(neutron_client, network_uuid)
port_uuid = self._create_port(neutron_client, network_uuid)
ext_vl = get_external_virtual_links(net0_id, net_mgmt_id,
port_uuid)
request_body = self._instantiate_vnf_request("simple",
vim_id=self.vim_id, ext_vl=ext_vl, ext_managed_vl=ext_managed_vl)
self._instantiate_vnf_instance(vnf_instance['id'], request_body)
vnf_instance = self._show_vnf_instance(vnf_instance['id'])
vdu_count = len(vnf_instance['instantiatedVnfInfo']
['vnfcResourceInfo'])
self.assertEqual(1, vdu_count)
# heal as per SOL003 API check, i.e. without passing vnfcInstanceId
# in the HealVnfRequest.
heal_request_body = {
"cause": "Heal as per SOL003 API check",
}
self._heal_sol_003_vnf_instance(vnf_instance, heal_request_body)
# NOTE(tpatil) Wait for sometime as it takes a while to update
# vnfcResourceInfo after the stack status becomes UPDATE_COMPLETE.
# There is no intermediate status set to VNF which can be used here
# to confirm healing action is completed successfully.
time.sleep(20)
vnf_instance_current = self._show_vnf_instance(vnf_instance['id'])
self._verify_vnfc_resource_info(vnf_instance, vnf_instance_current, 1)
# Terminate vnf gracefully with graceful timeout set to 60
terminate_req_body = {
"terminationType": fields.VnfInstanceTerminationType.GRACEFUL,
'gracefulTerminationTimeout': 60
}
self._terminate_vnf_instance(vnf_instance['id'], terminate_req_body)
self._delete_vnf_instance(vnf_instance['id'])

View File

@ -13,6 +13,11 @@
# under the License.
import os
import tempfile
import yaml
import zipfile
from oslo_utils import uuidutils
def read_file(input_file):
@ -20,3 +25,77 @@ def read_file(input_file):
'etc/samples/' + str(input_file)))
with open(yaml_file, 'r') as f:
return f.read()
def _update_unique_id_in_yaml(data, uid):
try:
prop = data['topology_template']['node_templates']['VNF'][
'properties']
if (prop.get('descriptor_id', None)):
prop['descriptor_id'] = uid
except KeyError:
# Let's check for 'node_types'
pass
if not data.get('node_types', None):
return
for ntype in data['node_types'].values():
if ntype['derived_from'] != 'tosca.nodes.nfv.VNF':
continue
try:
desc_id = ntype['properties']['descriptor_id']
if desc_id.get('constraints', None):
for constraint in desc_id.get('constraints'):
if constraint.get('valid_values', None):
constraint['valid_values'] = [uid]
if desc_id.get('default', None):
desc_id['default'] = uid
except KeyError:
# Let's check next node_type
pass
def create_csar_with_unique_vnfd_id(csar_dir):
"""Create CSAR file from a directory structure
For various tests it is necessary to have a CSAR having unique vnfd id.
This function reads a directory structure, updates vnfd id in yaml files
and creates a temporary CSAR zip file.
:returns:
- csar_file_name
- vnfd_id
"""
unique_id = uuidutils.generate_uuid()
tempfd, tempname = tempfile.mkstemp(suffix=".zip",
dir=os.path.dirname(csar_dir))
os.close(tempfd)
common_dir = os.path.join(csar_dir, "../common/")
zcsar = zipfile.ZipFile(tempname, 'w')
for (dpath, _, fnames) in os.walk(csar_dir):
if not fnames:
continue
for fname in fnames:
src_file = os.path.join(dpath, fname)
dst_file = os.path.relpath(os.path.join(dpath, fname), csar_dir)
if fname.endswith('.yaml') or fname.endswith('.yml'):
with open(src_file, 'rb') as yfile:
data = yaml.safe_load(yfile)
_update_unique_id_in_yaml(data, unique_id)
zcsar.writestr(dst_file, yaml.dump(
data, default_flow_style=False, allow_unicode=True))
else:
zcsar.write(src_file, dst_file)
for (dpath, _, fnames) in os.walk(common_dir):
if not fnames:
continue
for fname in fnames:
src_file = os.path.join(dpath, fname)
dst_file = os.path.relpath(os.path.join(dpath, fname), common_dir)
zcsar.write(src_file, dst_file)
zcsar.close()
return tempname, unique_id