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:
parent
840d754822
commit
428096a247
|
@ -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
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
202
tacker/tests/etc/samples/etsi/nfv/vnflcm1/Definitions/helloworld3_df_simple.yaml
Executable file
202
tacker/tests/etc/samples/etsi/nfv/vnflcm1/Definitions/helloworld3_df_simple.yaml
Executable 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 ]
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
193
tacker/tests/etc/samples/etsi/nfv/vnflcm2/Definitions/helloworld3_df_simple.yaml
Executable file
193
tacker/tests/etc/samples/etsi/nfv/vnflcm2/Definitions/helloworld3_df_simple.yaml
Executable 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 ]
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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]})
|
||||
|
|
|
@ -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'])
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue