Support Update function of FT

Added Update Functional tests for Tacker (Victoria).
Fix is also added for server shutdown since it has
a case where it is stopped unsafely.

Implements: blueprint support-etsi-nfv-specs
Change-Id: Ic424445ec9b836d83ffe4184d5b848fd17590606
This commit is contained in:
Koichi Edagawa
2020-09-11 16:40:59 +09:00
parent bf51fb25a4
commit f46885c450
16 changed files with 1694 additions and 105 deletions

View File

@@ -175,6 +175,15 @@
TACKER_HOST: "{{ hostvars['controller-tacker']['nodepool']['private_ipv4'] }}"
TACKER_MODE: standalone
IS_ZUUL_FT: True
# Since a VirtualInterfaceCreateException occurs during a test,
# the setting of network-vif-plugged is changed by the reference of
# the following URL.
# https://bugs.launchpad.net/heat/+bug/1694371
devstack_local_conf:
post-config:
$NOVA_CONF:
DEFAULT:
vif_plugging_is_fatal: False
devstack_services:
q-agt: true
n-api: false

View File

@@ -1,35 +1,35 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from tacker.vnfm.lcm_user_data.abstract_user_data import AbstractUserData
import tacker.vnfm.lcm_user_data.utils as UserDataUtil
class SampleUserData(AbstractUserData):
@staticmethod
def instantiate(base_hot_dict=None,
vnfd_dict=None,
inst_req_info=None,
grant_info=None):
api_param = UserDataUtil.get_diff_base_hot_param_from_api(
base_hot_dict, inst_req_info)
initial_param_dict = \
UserDataUtil.create_initial_param_server_port_dict(
base_hot_dict)
vdu_flavor_dict = \
UserDataUtil.create_vdu_flavor_capability_name_dict(vnfd_dict)
vdu_image_dict = UserDataUtil.create_sw_image_dict(vnfd_dict)
cpd_vl_dict = UserDataUtil.create_network_dict(
inst_req_info, initial_param_dict)
final_param_dict = UserDataUtil.create_final_param_dict(
initial_param_dict, vdu_flavor_dict, vdu_image_dict, cpd_vl_dict)
return {**final_param_dict, **api_param}
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from tacker.vnfm.lcm_user_data.abstract_user_data import AbstractUserData
import tacker.vnfm.lcm_user_data.utils as UserDataUtil
class SampleUserData(AbstractUserData):
@staticmethod
def instantiate(base_hot_dict=None,
vnfd_dict=None,
inst_req_info=None,
grant_info=None):
api_param = UserDataUtil.get_diff_base_hot_param_from_api(
base_hot_dict, inst_req_info)
initial_param_dict = \
UserDataUtil.create_initial_param_server_port_dict(
base_hot_dict)
vdu_flavor_dict = \
UserDataUtil.create_vdu_flavor_capability_name_dict(vnfd_dict)
vdu_image_dict = UserDataUtil.create_sw_image_dict(vnfd_dict)
cpd_vl_dict = UserDataUtil.create_network_dict(
inst_req_info, initial_param_dict)
final_param_dict = UserDataUtil.create_final_param_dict(
initial_param_dict, vdu_flavor_dict, vdu_image_dict, cpd_vl_dict)
return {**final_param_dict, **api_param}

View File

@@ -0,0 +1,98 @@
heat_template_version: 2013-05-23
description: 'Simple Base HOT for Sample VNF'
parameters:
nfv:
type: json
resources:
VDU1:
type: OS::Heat::AutoScalingGroup
properties:
min_size: 1
max_size: 3
desired_capacity: 1
resource:
type: VDU1.yaml
properties:
flavor: { get_param: [ nfv, VDU, VDU1, flavor ] }
image: { get_param: [ nfv, VDU, VirtualStorage, image ] }
zone: { get_param: [ nfv, vdu, VDU1, zone ] }
net1: { get_param: [ nfv, CP, VDU1_CP1, network ] }
net2: { get_param: [ nfv, CP, VDU1_CP2, network ] }
net3: { get_resource: extmanageNW_1 }
net4: { get_resource: extmanageNW_2 }
net5: { get_resource: internalNW_1 }
VDU1_scale_out:
type: OS::Heat::ScalingPolicy
properties:
scaling_adjustment: 1
auto_scaling_group_id:
get_resource: VDU1
adjustment_type: change_in_capacity
VDU1_scale_in:
type: OS::Heat::ScalingPolicy
properties:
scaling_adjustment: -1
auto_scaling_group_id:
get_resource: VDU1
adjustment_type: change_in_capacity
VDU2:
type: OS::Heat::AutoScalingGroup
properties:
min_size: 2
max_size: 2
desired_capacity: 2
resource:
type: VDU2.yaml
properties:
flavor: { get_param: [ nfv, VDU, VDU2, flavor ] }
image: { get_param: [ nfv, VDU, VDU2, image ] }
zone: { get_param: [ nfv, vdu, VDU2, zone ] }
net1: { get_param: [ nfv, CP, VDU2_CP1, network ] }
net2: { get_param: [ nfv, CP, VDU2_CP2, network ] }
net3: { get_resource: extmanageNW_1 }
net4: { get_resource: extmanageNW_2 }
net5: { get_resource: internalNW_1 }
VDU2_scale_out:
type: OS::Heat::ScalingPolicy
properties:
scaling_adjustment: 1
auto_scaling_group_id:
get_resource: VDU2
adjustment_type: change_in_capacity
VDU2_scale_in:
type: OS::Heat::ScalingPolicy
properties:
scaling_adjustment: -1
auto_scaling_group_id:
get_resource: VDU2
adjustment_type: change_in_capacity
extmanageNW_1:
type: OS::Neutron::Net
extmanageNW_2:
type: OS::Neutron::Net
internalNW_1:
type: OS::Neutron::Net
extmanageNW_1_subnet:
type: OS::Neutron::Subnet
properties:
ip_version: 4
network:
get_resource: extmanageNW_1
cidr: 192.168.3.0/24
extmanageNW_2_subnet:
type: OS::Neutron::Subnet
properties:
ip_version: 4
network:
get_resource: extmanageNW_2
cidr: 192.168.4.0/24
internalNW_1_subnet:
type: OS::Neutron::Subnet
properties:
ip_version: 4
network:
get_resource: internalNW_1
cidr: 192.168.5.0/24
outputs: {}

View File

@@ -0,0 +1,72 @@
heat_template_version: 2013-05-23
description: 'VDU1 HOT for Sample VNF'
parameters:
flavor:
type: string
image:
type: string
zone:
type: string
net1:
type: string
net2:
type: string
net3:
type: string
net4:
type: string
net5:
type: string
resources:
VDU1:
type: OS::Nova::Server
properties:
flavor: { get_param: flavor }
name: VDU1
block_device_mapping_v2: [{"volume_id": { get_resource: VirtualStorage }}]
networks:
- port:
get_resource: VDU1_CP1
- port:
get_resource: VDU1_CP2
- port:
get_resource: VDU1_CP3
- port:
get_resource: VDU1_CP4
- port:
get_resource: VDU1_CP5
availability_zone: { get_param: zone }
VirtualStorage:
type: OS::Cinder::Volume
properties:
image: { get_param: image }
size: 1
volume_type: { get_resource: multi }
multi:
type: OS::Cinder::VolumeType
properties:
name: { get_resource: VDU1_CP1 }
metadata: { multiattach: "<is> True" }
VDU1_CP1:
type: OS::Neutron::Port
properties:
network: { get_param: net1 }
VDU1_CP2:
type: OS::Neutron::Port
properties:
network: { get_param: net2 }
VDU1_CP3:
type: OS::Neutron::Port
properties:
network: { get_param: net3 }
VDU1_CP4:
type: OS::Neutron::Port
properties:
network: { get_param: net4 }
VDU1_CP5:
type: OS::Neutron::Port
properties:
network: { get_param: net5 }

View File

@@ -0,0 +1,61 @@
heat_template_version: 2013-05-23
description: 'VDU2 HOT for Sample VNF'
parameters:
flavor:
type: string
image:
type: string
zone:
type: string
net1:
type: string
net2:
type: string
net3:
type: string
net4:
type: string
net5:
type: string
resources:
VDU2:
type: OS::Nova::Server
properties:
flavor: { get_param: flavor }
name: VDU2
image: { get_param: image }
networks:
- port:
get_resource: VDU2_CP1
- port:
get_resource: VDU2_CP2
- port:
get_resource: VDU2_CP3
- port:
get_resource: VDU2_CP4
- port:
get_resource: VDU2_CP5
availability_zone: { get_param: zone }
VDU2_CP1:
type: OS::Neutron::Port
properties:
network: { get_param: net1 }
VDU2_CP2:
type: OS::Neutron::Port
properties:
network: { get_param: net2 }
VDU2_CP3:
type: OS::Neutron::Port
properties:
network: { get_param: net3 }
VDU2_CP4:
type: OS::Neutron::Port
properties:
network: { get_param: net4 }
VDU2_CP5:
type: OS::Neutron::Port
properties:
network: { get_param: net5 }

View File

@@ -0,0 +1,403 @@
tosca_definitions_version: tosca_simple_yaml_1_2
description: Simple deployment flavour for Sample VNF
imports:
- etsi_nfv_sol001_common_types.yaml
- etsi_nfv_sol001_vnfd_types.yaml
- helloworld3_types.yaml
topology_template:
inputs:
descriptor_id:
type: string
descriptor_version:
type: string
provider:
type: string
product_name:
type: string
software_version:
type: string
vnfm_info:
type: list
entry_schema:
type: string
flavour_id:
type: string
flavour_description:
type: string
substitution_mappings:
node_type: company.provider.VNF
properties:
flavour_id: simple
requirements:
virtual_link_external1_1: [ VDU1_CP1, virtual_link ]
virtual_link_external1_2: [ VDU2_CP1, virtual_link ]
virtual_link_external2_1: [ VDU1_CP2, virtual_link ]
virtual_link_external2_2: [ VDU2_CP2, virtual_link ]
node_templates:
VNF:
type: company.provider.VNF
properties:
flavour_description: A simple flavour
interfaces:
Vnflcm:
instantiate: []
instantiate_start: []
instantiate_end: []
terminate: []
terminate_start: []
terminate_end: []
modify_information: []
modify_information_start: []
modify_information_end: []
VDU1:
type: tosca.nodes.nfv.Vdu.Compute
properties:
name: VDU1
description: VDU1 compute node
vdu_profile:
min_number_of_instances: 1
max_number_of_instances: 3
capabilities:
virtual_compute:
properties:
requested_additional_capabilities:
properties:
requested_additional_capability_name: m1.tiny
support_mandatory: true
target_performance_parameters:
entry_schema: test
virtual_memory:
virtual_mem_size: 512 MB
virtual_cpu:
num_virtual_cpu: 1
virtual_local_storage:
- size_of_storage: 3 GB
requirements:
- virtual_storage: VirtualStorage
VDU2:
type: tosca.nodes.nfv.Vdu.Compute
properties:
name: VDU2
description: VDU2 compute node
vdu_profile:
min_number_of_instances: 2
max_number_of_instances: 2
sw_image_data:
name: cirros-0.4.0-x86_64-disk2
version: '0.4.0'
checksum:
algorithm: sha-256
hash: a8dd75ecffd4cdd96072d60c2237b448e0c8b2bc94d57f10fdbc8c481d9005b8
container_format: bare
disk_format: qcow2
min_disk: 0 GB
min_ram: 256 MB
size: 12 GB
capabilities:
virtual_compute:
properties:
requested_additional_capabilities:
properties:
requested_additional_capability_name: m1.tiny
support_mandatory: true
target_performance_parameters:
entry_schema: test
virtual_memory:
virtual_mem_size: 512 MB
virtual_cpu:
num_virtual_cpu: 1
virtual_local_storage:
- size_of_storage: 3 GB
VirtualStorage:
type: tosca.nodes.nfv.Vdu.VirtualBlockStorage
properties:
virtual_block_storage_data:
size_of_storage: 1 GB
rdma_enabled: true
sw_image_data:
name: cirros-0.4.0-x86_64-disk
version: '0.4.0'
checksum:
algorithm: sha-256
hash: a8dd75ecffd4cdd96072d60c2237b448e0c8b2bc94d57f10fdbc8c481d9005b8
container_format: bare
disk_format: qcow2
min_disk: 0 GB
min_ram: 256 MB
size: 12 GB
VDU1_CP1:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 0
requirements:
- virtual_binding: VDU1
VDU1_CP2:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 1
requirements:
- virtual_binding: VDU1
VDU1_CP3:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 2
requirements:
- virtual_binding: VDU1
- virtual_link: internalVL1
VDU1_CP4:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 3
requirements:
- virtual_binding: VDU1
- virtual_link: internalVL2
VDU1_CP5:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 4
requirements:
- virtual_binding: VDU1
- virtual_link: internalVL3
VDU2_CP1:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 0
requirements:
- virtual_binding: VDU2
VDU2_CP2:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 1
requirements:
- virtual_binding: VDU2
VDU2_CP3:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 2
requirements:
- virtual_binding: VDU2
- virtual_link: internalVL1
VDU2_CP4:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 3
requirements:
- virtual_binding: VDU2
- virtual_link: internalVL2
VDU2_CP5:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 4
requirements:
- virtual_binding: VDU2
- virtual_link: internalVL3
internalVL1:
type: tosca.nodes.nfv.VnfVirtualLink
properties:
connectivity_type:
layer_protocols: [ ipv4 ]
description: External Managed Virtual link in the VNF
vl_profile:
max_bitrate_requirements:
root: 1048576
leaf: 1048576
min_bitrate_requirements:
root: 1048576
leaf: 1048576
virtual_link_protocol_data:
- associated_layer_protocol: ipv4
l3_protocol_data:
ip_version: ipv4
cidr: 33.33.0.0/24
internalVL2:
type: tosca.nodes.nfv.VnfVirtualLink
properties:
connectivity_type:
layer_protocols: [ ipv4 ]
description: External Managed Virtual link in the VNF
vl_profile:
max_bitrate_requirements:
root: 1048576
leaf: 1048576
min_bitrate_requirements:
root: 1048576
leaf: 1048576
virtual_link_protocol_data:
- associated_layer_protocol: ipv4
l3_protocol_data:
ip_version: ipv4
cidr: 33.34.0.0/24
internalVL3:
type: tosca.nodes.nfv.VnfVirtualLink
properties:
connectivity_type:
layer_protocols: [ ipv4 ]
description: Internal Virtual link in the VNF
vl_profile:
max_bitrate_requirements:
root: 1048576
leaf: 1048576
min_bitrate_requirements:
root: 1048576
leaf: 1048576
virtual_link_protocol_data:
- associated_layer_protocol: ipv4
l3_protocol_data:
ip_version: ipv4
cidr: 33.35.0.0/24
policies:
- scaling_aspects:
type: tosca.policies.nfv.ScalingAspects
properties:
aspects:
worker_instance:
name: worker_instance_aspect
description: worker_instance scaling aspect
max_scale_level: 2
step_deltas:
- delta_1
- VDU1_initial_delta:
type: tosca.policies.nfv.VduInitialDelta
properties:
initial_delta:
number_of_instances: 1
targets: [ VDU1 ]
- VDU2_initial_delta:
type: tosca.policies.nfv.VduInitialDelta
properties:
initial_delta:
number_of_instances: 2
targets: [ VDU2 ]
- VDU1_scaling_aspect_deltas:
type: tosca.policies.nfv.VduScalingAspectDeltas
properties:
aspect: worker_instance
deltas:
delta_1:
number_of_instances: 1
targets: [ VDU1 ]
- instantiation_levels:
type: tosca.policies.nfv.InstantiationLevels
properties:
levels:
instantiation_level_1:
description: Smallest size
scale_info:
worker_instance:
scale_level: 0
instantiation_level_2:
description: Largest size
scale_info:
worker_instance:
scale_level: 2
default_level: instantiation_level_1
- VDU1_instantiation_levels:
type: tosca.policies.nfv.VduInstantiationLevels
properties:
levels:
instantiation_level_1:
number_of_instances: 1
instantiation_level_2:
number_of_instances: 3
targets: [ VDU1 ]
- VDU2_instantiation_levels:
type: tosca.policies.nfv.VduInstantiationLevels
properties:
levels:
instantiation_level_1:
number_of_instances: 2
instantiation_level_2:
number_of_instances: 2
targets: [ VDU2 ]
- internalVL1_instantiation_levels:
type: tosca.policies.nfv.VirtualLinkInstantiationLevels
properties:
levels:
instantiation_level_1:
bitrate_requirements:
root: 1048576
leaf: 1048576
instantiation_level_2:
bitrate_requirements:
root: 1048576
leaf: 1048576
targets: [ internalVL1 ]
- internalVL2_instantiation_levels:
type: tosca.policies.nfv.VirtualLinkInstantiationLevels
properties:
levels:
instantiation_level_1:
bitrate_requirements:
root: 1048576
leaf: 1048576
instantiation_level_2:
bitrate_requirements:
root: 1048576
leaf: 1048576
targets: [ internalVL2 ]
- internalVL3_instantiation_levels:
type: tosca.policies.nfv.VirtualLinkInstantiationLevels
properties:
levels:
instantiation_level_1:
bitrate_requirements:
root: 1048576
leaf: 1048576
instantiation_level_2:
bitrate_requirements:
root: 1048576
leaf: 1048576
targets: [ internalVL3 ]
- policy_antiaffinity_vdu1:
type: tosca.policies.nfv.AntiAffinityRule
targets: [ VDU1 ]
properties:
scope: zone
- policy_antiaffinity_vdu2:
type: tosca.policies.nfv.AntiAffinityRule
targets: [ VDU2 ]
properties:
scope: zone

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,55 @@
tosca_definitions_version: tosca_simple_yaml_1_2
description: VNF type definition
imports:
- etsi_nfv_sol001_common_types.yaml
- etsi_nfv_sol001_vnfd_types.yaml
node_types:
company.provider.VNF:
derived_from: tosca.nodes.nfv.VNF
properties:
descriptor_id:
type: string
constraints: [ valid_values: [ b1bb0ce7-ebca-4fa7-95ed-4840d7000000 ] ]
default: b1bb0ce7-ebca-4fa7-95ed-4840d7000000
descriptor_version:
type: string
constraints: [ valid_values: [ '1.0' ] ]
default: '1.0'
provider:
type: string
constraints: [ valid_values: [ 'Company' ] ]
default: 'Company'
product_name:
type: string
constraints: [ valid_values: [ 'Sample VNF' ] ]
default: 'Sample VNF'
software_version:
type: string
constraints: [ valid_values: [ '1.0' ] ]
default: '1.0'
vnfm_info:
type: list
entry_schema:
type: string
constraints: [ valid_values: [ Tacker ] ]
default: [ Tacker ]
flavour_id:
type: string
constraints: [ valid_values: [ simple ] ]
default: simple
flavour_description:
type: string
default: "falvour"
requirements:
- virtual_link_external1:
capability: tosca.capabilities.nfv.VirtualLinkable
- virtual_link_external2:
capability: tosca.capabilities.nfv.VirtualLinkable
- virtual_link_internal:
capability: tosca.capabilities.nfv.VirtualLinkable
interfaces:
Vnflcm:
type: tosca.interfaces.nfv.Vnflcm

View File

@@ -0,0 +1,4 @@
TOSCA-Meta-File-Version: 1.0
CSAR-Version: 1.1
Created-by: Onboarding portal
Entry-Definitions: Definitions/helloworld3_top.vnfd.yaml

View File

@@ -0,0 +1,35 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from tacker.vnfm.lcm_user_data.abstract_user_data import AbstractUserData
import tacker.vnfm.lcm_user_data.utils as UserDataUtil
class SampleUserData(AbstractUserData):
@staticmethod
def instantiate(base_hot_dict=None,
vnfd_dict=None,
inst_req_info=None,
grant_info=None):
api_param = UserDataUtil.get_diff_base_hot_param_from_api(
base_hot_dict, inst_req_info)
initial_param_dict = \
UserDataUtil.create_initial_param_server_port_dict(
base_hot_dict)
vdu_flavor_dict = \
UserDataUtil.create_vdu_flavor_capability_name_dict(vnfd_dict)
vdu_image_dict = UserDataUtil.create_sw_image_dict(vnfd_dict)
cpd_vl_dict = UserDataUtil.create_network_dict(
inst_req_info, initial_param_dict)
final_param_dict = UserDataUtil.create_final_param_dict(
initial_param_dict, vdu_flavor_dict, vdu_image_dict, cpd_vl_dict)
return {**final_param_dict, **api_param}

View File

@@ -97,6 +97,7 @@ class BaseTackerTest(base.BaseTestCase):
cls.client = cls.tackerclient()
cls.http_client = cls.tacker_http_client()
cls.h_client = cls.heatclient()
cls.glance_client = cls.glanceclient()
@classmethod
def get_credentials(cls):

View File

@@ -18,6 +18,7 @@ import inspect
import json
import os
import threading
import time
from oslo_log import log as logging
@@ -391,8 +392,16 @@ class FakeServerManager(SingletonMixin):
'[Start] %s.%s()' %
(self.__class__.__name__,
inspect.currentframe().f_code.co_name))
self.objHttpd = http.server.HTTPServer(
(address, port), DummyRequestHander)
while True:
try:
self.objHttpd = http.server.HTTPServer(
(address, port), DummyRequestHander)
except OSError:
time.sleep(10)
continue
else:
break
self.thread_server = threading.Thread(None, self.run)
LOG.debug(
'[ End ] %s.%s()' %
(self.__class__.__name__,
@@ -401,7 +410,7 @@ class FakeServerManager(SingletonMixin):
def start_server(self):
"""Start server in thread."""
LOG.debug('[START] %s()' % inspect.currentframe().f_code.co_name)
threading.Thread(None, self.run).start()
self.thread_server.start()
LOG.debug('[ END ] %s()' % inspect.currentframe().f_code.co_name)
def run(self):
@@ -411,10 +420,15 @@ class FakeServerManager(SingletonMixin):
self.objHttpd.serve_forever()
except KeyboardInterrupt:
self.stop_server()
finally:
self.objHttpd.server_close()
LOG.debug('[ END ] %s()' % inspect.currentframe().f_code.co_name)
def stop_server(self):
"""Stop HTTP Server"""
LOG.debug('[START] %s()' % inspect.currentframe().f_code.co_name)
self.objHttpd.shutdown()
thread_shutdown = threading.Thread(None, self.objHttpd.shutdown)
thread_shutdown.daemon = True
thread_shutdown.start()
self.thread_server.join()
LOG.debug('[ END ] %s()' % inspect.currentframe().f_code.co_name)

View File

@@ -122,7 +122,8 @@ def _create_and_upload_vnf_package(
vnfd_id = None
while True:
resp, body = tacker_client.do_request(show_url, "GET")
if body['onboardingState'] == "ONBOARDED":
if (200 <= resp.status_code < 300) and (
body['onboardingState'] == "ONBOARDED"):
vnfd_id = body['vnfdId']
break
@@ -204,6 +205,8 @@ def _create_instantiate_vnf_request_body(flavour_id,
class BaseVnfLcmTest(base.BaseTackerTest):
is_setup_error = False
@classmethod
def setUpClass(cls):
'''Set up test class.
@@ -214,12 +217,6 @@ class BaseVnfLcmTest(base.BaseTackerTest):
FAKE_SERVER_MANAGER.prepare_http_server()
FAKE_SERVER_MANAGER.start_server()
FAKE_SERVER_MANAGER.set_callback(
'POST',
MOCK_NOTIFY_CALLBACK_URL,
status_code=204
)
@classmethod
def tearDownClass(cls):
super(BaseVnfLcmTest, cls).tearDownClass()
@@ -228,6 +225,18 @@ class BaseVnfLcmTest(base.BaseTackerTest):
def setUp(self):
super(BaseVnfLcmTest, self).setUp()
if self.is_setup_error:
self.fail("Faild, not exists pre-registered image.")
callback_url = \
os.path.join(MOCK_NOTIFY_CALLBACK_URL, self._testMethodName)
FAKE_SERVER_MANAGER.clear_history(callback_url)
FAKE_SERVER_MANAGER.set_callback(
'POST',
callback_url,
status_code=204
)
self.tacker_client = base.BaseTackerTest.tacker_http_client()
self.base_vnf_instances_url = "/vnflcm/v1/vnf_instances"
@@ -248,14 +257,17 @@ class BaseVnfLcmTest(base.BaseTackerTest):
for nw in networks.get('networks'):
if nw['name'] == 'net0':
self.ext_networks.append(nw['id'])
self.ext_vl = _get_external_virtual_links(nw['id'])
elif nw['name'] == 'net1':
self.ext_mngd_networks.append(nw['id'])
# create new network.
self.ext_networks.append(
self._create_network("external_net"))
self.ext_mngd_networks.append(
self._create_network("external_managed_internal_net"))
ext_net_id, ext_net_name = \
self._create_network("external_net")
self.ext_networks.append(ext_net_id)
ext_mngd_net_id, _ = \
self._create_network("external_managed_internal_net")
self.ext_mngd_networks.append(ext_mngd_net_id)
# Create external link ports in net0
self.ext_link_ports = list()
@@ -265,21 +277,21 @@ class BaseVnfLcmTest(base.BaseTackerTest):
# Chack how many networks are created.
networks = self.neutronclient().list_networks()
for nw in networks.get('networks'):
if nw['name'] not in ['net0', 'external_net']:
continue
if self.vim['tenant_id'] != nw['tenant_id']:
if nw['name'] not in ['net0', ext_net_name]:
continue
self.ext_networks.append(nw['id'])
if nw['name'] == 'net0':
self.ext_subnets.append(nw['subnets'][0])
else:
self.ext_subnets.append(self._create_subnet(nw))
self.ext_link_ports.append(self._create_port(nw['id']))
self.ext_subnets.append(self._create_subnet(nw))
@classmethod
def _list_glance_image(cls, filter_name='cirros-0.4.0-x86_64-disk'):
try:
images = cls.glance_client.images.list()
except Exception:
print("glance-image does not exists.")
print("glance-image does not exists.", flush=True)
return []
if filter_name is None:
@@ -292,7 +304,7 @@ class BaseVnfLcmTest(base.BaseTackerTest):
try:
image = cls.glance_client.images.get(image_id)
except Exception:
print("glance-image does not exists.")
print("glance-image does not exists.", image_id, flush=True)
return None
return image
@@ -353,6 +365,7 @@ class BaseVnfLcmTest(base.BaseTackerTest):
return self._create_vnf_instance_from_body(request_body)
def _create_vnf_instance_from_body(self, request_body):
request_body['vnfInstanceName'] = self._testMethodName
resp, response_body = self.http_client.do_request(
self.base_vnf_instances_url,
"POST",
@@ -374,7 +387,7 @@ class BaseVnfLcmTest(base.BaseTackerTest):
def _list_vnf_instance(self, **kwargs):
resp, vnf_instances = self.http_client.do_request(
self.base_vnf_instances_url, "GET")
self.base_vnf_instances_url, "GET", **kwargs)
return resp, vnf_instances
@@ -418,6 +431,13 @@ class BaseVnfLcmTest(base.BaseTackerTest):
return resp, body
def _update_vnf_instance(self, vnf_instance_id, request_body):
url = os.path.join(self.base_vnf_instances_url, vnf_instance_id)
resp, body = self.http_client.do_request(url, "PATCH",
body=jsonutils.dumps(request_body))
return resp, body
def _wait_terminate_vnf_instance(self, id, timeout=None):
start_time = int(time.time())
@@ -441,7 +461,6 @@ class BaseVnfLcmTest(base.BaseTackerTest):
try:
stacks = self.h_client.stacks.list()
except Exception:
print("heat-stacks does not exists.")
return None
target_stack_name = prefix_id + vnf_instance_id
@@ -460,7 +479,6 @@ class BaseVnfLcmTest(base.BaseTackerTest):
resources = self.h_client.resources.list(
stack_id, nested_depth=nested_depth)
except Exception:
print("heat-stacks-resources does not exists.")
return None
return resources
@@ -470,7 +488,6 @@ class BaseVnfLcmTest(base.BaseTackerTest):
resource = self.h_client.resources.get(
stack_id, resource_name)
except Exception:
print("heat-stacks-resource does not exists.")
return None
return resource
@@ -610,15 +627,28 @@ class BaseVnfLcmTest(base.BaseTackerTest):
image = self._get_glance_image(glance_image_id)
self.assertIsNone(image)
def _wait_lcm_done(self, expected_operation_status=None):
def _wait_lcm_done(self,
expected_operation_status=None,
vnf_instance_id=None):
start_time = int(time.time())
while True:
callback_url = \
os.path.join(MOCK_NOTIFY_CALLBACK_URL, self._testMethodName)
while True:
actual_status = None
vnf_lcm_op_occ_id = None
notify_mock_responses = FAKE_SERVER_MANAGER.get_history(
MOCK_NOTIFY_CALLBACK_URL)
callback_url)
print(
("Wait:callback_url=<%s>, " +
"wait_status=<%s>, " +
"vnf_instance_id=<%s>") %
(callback_url, expected_operation_status, vnf_instance_id),
flush=True)
for res in notify_mock_responses:
if vnf_instance_id != res.request_body.get('vnfInstanceId'):
continue
if expected_operation_status is None:
return
@@ -631,8 +661,9 @@ class BaseVnfLcmTest(base.BaseTackerTest):
if ((int(time.time()) - start_time) > VNF_LCM_DONE_TIMEOUT):
if actual_status:
error = (
"LCM incomplete timeout, %s is %s," +
"expected status should be %s")
"LCM incomplete timeout, %(vnf_lcm_op_occ_id)s" +
" is %(actual)s," +
"expected status should be %(expected)s")
self.fail(
error % {
"vnf_lcm_op_occ_id": vnf_lcm_op_occ_id,
@@ -669,9 +700,11 @@ class BaseVnfLcmTest(base.BaseTackerTest):
fields.VnfInstanceState.NOT_INSTANTIATED)
# FT-checkpoint: Notification
notify_mock_responses = FAKE_SERVER_MANAGER.get_history(
MOCK_NOTIFY_CALLBACK_URL)
FAKE_SERVER_MANAGER.clear_history(MOCK_NOTIFY_CALLBACK_URL)
callback_url = \
os.path.join(MOCK_NOTIFY_CALLBACK_URL, self._testMethodName)
notify_mock_responses = self._filter_notify_history(callback_url,
vnf_instance.get('id'))
self.assertEqual(1, len(notify_mock_responses))
self.assert_notification_mock_response(
notify_mock_responses[0],
@@ -684,9 +717,11 @@ class BaseVnfLcmTest(base.BaseTackerTest):
self.assertEqual(404, resp.status_code)
# FT-checkpoint: Notification
notify_mock_responses = FAKE_SERVER_MANAGER.get_history(
MOCK_NOTIFY_CALLBACK_URL)
FAKE_SERVER_MANAGER.clear_history(MOCK_NOTIFY_CALLBACK_URL)
callback_url = \
os.path.join(MOCK_NOTIFY_CALLBACK_URL, self._testMethodName)
notify_mock_responses = self._filter_notify_history(callback_url,
vnf_instance_id)
self.assertEqual(1, len(notify_mock_responses))
self.assert_notification_mock_response(
notify_mock_responses[0],
@@ -707,9 +742,10 @@ class BaseVnfLcmTest(base.BaseTackerTest):
expected_resource_status='CREATE_COMPLETE')
# FT-checkpoint: Notification
notify_mock_responses = FAKE_SERVER_MANAGER.get_history(
MOCK_NOTIFY_CALLBACK_URL)
FAKE_SERVER_MANAGER.clear_history(MOCK_NOTIFY_CALLBACK_URL)
callback_url = \
os.path.join(MOCK_NOTIFY_CALLBACK_URL, self._testMethodName)
notify_mock_responses = self._filter_notify_history(callback_url,
vnf_instance_id)
self.assertEqual(3, len(notify_mock_responses))
self.assert_notification_mock_response(
@@ -743,9 +779,10 @@ class BaseVnfLcmTest(base.BaseTackerTest):
expected_stack_status=expected_stack_status)
# FT-checkpoint: Notification
notify_mock_responses = FAKE_SERVER_MANAGER.get_history(
MOCK_NOTIFY_CALLBACK_URL)
FAKE_SERVER_MANAGER.clear_history(MOCK_NOTIFY_CALLBACK_URL)
callback_url = \
os.path.join(MOCK_NOTIFY_CALLBACK_URL, self._testMethodName)
notify_mock_responses = self._filter_notify_history(callback_url,
vnf_instance_id)
self.assertEqual(3, len(notify_mock_responses))
self.assert_notification_mock_response(
@@ -787,9 +824,10 @@ class BaseVnfLcmTest(base.BaseTackerTest):
glance_image_id_list=glance_image_id_list)
# FT-checkpoint: Notification
notify_mock_responses = FAKE_SERVER_MANAGER.get_history(
MOCK_NOTIFY_CALLBACK_URL)
FAKE_SERVER_MANAGER.clear_history(MOCK_NOTIFY_CALLBACK_URL)
callback_url = \
os.path.join(MOCK_NOTIFY_CALLBACK_URL, self._testMethodName)
notify_mock_responses = self._filter_notify_history(callback_url,
vnf_instance_id)
self.assertEqual(3, len(notify_mock_responses))
self.assert_notification_mock_response(
@@ -807,6 +845,41 @@ class BaseVnfLcmTest(base.BaseTackerTest):
'VnfLcmOperationOccurrenceNotification',
'COMPLETED')
def assert_update_vnf(
self,
resp,
vnf_instance_id,
expected_stack_status='CREATE_COMPLETE'):
self.assertEqual(202, resp.status_code)
self.assert_http_header_location_for_lcm_op_occs(resp.headers)
resp, vnf_instance = self._show_vnf_instance(vnf_instance_id)
self.assertEqual(200, resp.status_code)
self.assert_vnf_state(vnf_instance)
self.assert_instantiation_state(vnf_instance)
self.assert_heat_stack_status(
vnf_instance['id'],
expected_stack_status=expected_stack_status)
# FT-checkpoint: Notification
callback_url = \
os.path.join(MOCK_NOTIFY_CALLBACK_URL, self._testMethodName)
notify_mock_responses = self._filter_notify_history(callback_url,
vnf_instance_id)
self.assertEqual(2, len(notify_mock_responses))
self.assert_notification_mock_response(
notify_mock_responses[0],
'VnfLcmOperationOccurrenceNotification',
'PROCESSING')
self.assert_notification_mock_response(
notify_mock_responses[1],
'VnfLcmOperationOccurrenceNotification',
'COMPLETED')
def assert_notification_mock_response(
self,
notify_mock_response,
@@ -826,34 +899,64 @@ class BaseVnfLcmTest(base.BaseTackerTest):
def _create_network(self, name):
# First, we have to check network name passed by caller is
# already exists or not.
netlist = self.neutronclient().list_networks(name=name)
if netlist is not None:
print('%s is already exist' % name)
# OK, we can create this.
net = self.neutronclient().create_network({'network': {'name': name}})
net_id = net['network']['id']
self.addCleanup(self.neutronclient().delete_network, net_id)
return net_id
try:
uniq_name = name + '-' + uuidutils.generate_uuid()
net = \
self.neutronclient().create_network(
{'network': {'name': uniq_name}})
net_id = net['network']['id']
self.addCleanup(self._delete_network, net_id)
print("Create network success, %s" % uniq_name, flush=True)
return net_id, uniq_name
except Exception as e:
self.fail("Failed, create network=<%s>, %s" %
(uniq_name, e))
def _create_subnet(self, network):
cidr_prefix = "22.22.{}".format(str(len(self.ext_subnets) + 1))
body = {'subnet': {'network_id': network['id'],
'name': "subnet-%s" % uuidutils.generate_uuid(),
'cidr': "22.22.{}.0/24".format(str(len(self.ext_subnets) % 2)),
'cidr': "{}.0/24".format(cidr_prefix),
'ip_version': 4,
'gateway_ip': '22.22.0.1',
'gateway_ip': "{}.1".format(cidr_prefix),
"enable_dhcp": True}}
subnet = self.neutronclient().create_subnet(body=body)["subnet"]
self.addCleanup(self.neutronclient().delete_subnet, subnet['id'])
return subnet['id']
try:
subnet = self.neutronclient().create_subnet(body=body)["subnet"]
self.addCleanup(self._delete_subnet, subnet['id'])
return subnet['id']
except Exception as e:
self.fail("Failed, create subnet for net_id=<%s>, %s" %
(network['id'], e))
def _create_port(self, network_id):
body = {'port': {'network_id': network_id}}
port = self.neutronclient().create_port(body=body)["port"]
self.addCleanup(self.neutronclient().delete_port, port['id'])
return port['id']
try:
port = self.neutronclient().create_port(body=body)["port"]
self.addCleanup(self._delete_port, port['id'])
return port['id']
except Exception as e:
self.fail("Failed, create port for net_id=<%s>, %s" %
(network_id, e))
def _delete_network(self, network_id):
try:
self.neutronclient().delete_network(network_id)
except Exception:
print("Failed, delete network.", network_id, flush=True)
def _delete_subnet(self, subnet_id):
try:
self.neutronclient().delete_subnet(subnet_id)
except Exception:
print("Failed, delete subnet.", subnet_id, flush=True)
def _delete_port(self, port_id):
try:
self.neutronclient().delete_port(port_id)
except Exception:
print("Failed, delete port.", port_id, flush=True)
def assert_subscription_show(self, resp, response_body):
"""Assert that subscription informations has mandatory keys."""
@@ -869,3 +972,12 @@ class BaseVnfLcmTest(base.BaseTackerTest):
self.assertIsNotNone(_links)
self.assertIsNotNone(_links.get('self'))
self.assertIsNotNone(_links.get('self').get('href'))
def _filter_notify_history(self, callback_url, vnf_instance_id):
notify_histories = FAKE_SERVER_MANAGER.get_history(
callback_url)
FAKE_SERVER_MANAGER.clear_history(callback_url)
return [
h for h in notify_histories
if h.request_body.get('vnfInstanceId') == vnf_instance_id]

View File

@@ -53,7 +53,7 @@ class VnfInstances:
def make_create_request_body(vnfd_id):
return {
"vnfdId": vnfd_id,
"vnfInstanceName": "helloworld3",
"vnfInstanceName": "",
"vnfInstanceDescription": "Sample VNF",
"metadata": {
"samplekey": "samplevalue"
@@ -97,7 +97,6 @@ class VnfInstances:
}
ext_virtual_link_cp1 = {
"id": uuidsentinel.evl1_id,
"vimConnectionId": uuidsentinel.vim_connection_id,
# set external nw_id on vim.
"resourceId": networks_id[0],
"extCps": [ext_vdu1_cp1, ext_vdu2_cp1],
@@ -113,7 +112,7 @@ class VnfInstances:
"ipOverEthernet": {
"ipAddresses": [{
"type": "IPV4",
"numDynamicAddresses": 1,
"fixedAddresses": ["22.22.2.10"],
"subnetId": external_subnets_id[0]
}]
}
@@ -129,7 +128,7 @@ class VnfInstances:
"ipOverEthernet": {
"ipAddresses": [{
"type": "IPV4",
"numDynamicAddresses": "1",
"fixedAddresses": ["22.22.2.20"],
"subnetId": external_subnets_id[1]
}]
}
@@ -139,7 +138,6 @@ class VnfInstances:
ext_virtual_link_cp2 = {
"id": uuidsentinel.evl2_id,
"vimConnectionId": uuidsentinel.vim_connection_id,
"resourceId": networks_id[1],
"extCps": [
ext_cps_vdu1_cp2, ext_cps_vdu2_cp2
@@ -150,12 +148,10 @@ class VnfInstances:
ext_mng_vtl_lnks = [{
"id": uuidsentinel.emvl1_id,
"vnfVirtualLinkDescId": "internalVL1",
"vimConnectionId": uuidsentinel.vim_connection_id,
"resourceId": ext_mngd_networks_id[0]
}, {
"id": uuidsentinel.emvl2_id,
"vnfVirtualLinkDescId": "internalVL2",
"vimConnectionId": uuidsentinel.vim_connection_id,
"resourceId": ext_mngd_networks_id[1]
}]
@@ -169,6 +165,7 @@ class VnfInstances:
"vimConnectionInfo": [{
"id": uuidsentinel.vim_connection_id,
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.v_2",
"vimConnectionId": uuidsentinel.vim_connection_id,
"interfaceInfo": {
"endpoint": "http://127.0.0.1/identity"
},
@@ -214,3 +211,31 @@ class VnfInstances:
"samplekey": "samplevalue"
}
}
@staticmethod
def make_update_request_body(vnfd_id=None, vnf_package_id=None):
"""Parameter selection policy.
vimConnectionInfo is not set.
Args:
vnfd_id (str, optional): vnfdId(2.6.1)
vnf_package_id (str, optional): vnfPkgId(2.4.1)
Returns:
dict: Request body
"""
data = {
"vnfInstanceName": "helloworld3_modify",
"vnfInstanceDescription": "Sample VNF Modify",
"metadata": {
"samplekey": "samplevalue_modified"
}
}
if vnfd_id:
data["vnfdId"] = vnfd_id
elif vnf_package_id:
data["vnfPkgId"] = vnf_package_id
return data

View File

@@ -11,13 +11,53 @@
# License for the specific language governing permissions and limitations
# under the License.
import os
from oslo_utils import uuidutils
from tacker.objects import fields
from tacker.tests.functional.vnflcm import base as vnflcm_base
from tacker.tests.functional.vnflcm import fake_vnflcm
import tempfile
import time
class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
@classmethod
def setUpClass(cls):
super(VnfLcmWithUserDataTest, cls).setUpClass()
images = cls._list_glance_image()
if len(images) == 0:
cls.is_setup_error = True
return
# ModifyVNF specific image create.
for image in images:
specific_image_name = image.name + '2'
image_data = {
"min_disk": image.min_disk,
"min_ram": image.min_ram,
"disk_format": image.disk_format,
"container_format": image.container_format,
"visibility": image.visibility,
"name": specific_image_name}
try:
images = cls._list_glance_image(specific_image_name)
if len(images) == 1:
break
_, body = cls.glance_client.http_client.get(
cls.glance_client.http_client.get_endpoint() + image.file)
with tempfile.TemporaryFile('w+b') as f:
for content in body:
f.write(content)
cls._create_glance_image(image_data, f.read())
except Exception as e:
print("Fail, Modify-VNF specific image create.", e, flush=True)
cls.is_setup_error = True
return
def _vnf_instance_wait_until_fail_detected(self, id,
instantiation_state=fields.VnfInstanceState.NOT_INSTANTIATED,
timeout=vnflcm_base.VNF_INSTANTIATE_ERROR_WAIT):
@@ -53,6 +93,592 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
time.sleep(1)
def test_inst_update_heal_term(self):
"""Test base life cycle operations.
In this test case, we do following steps.
- Create subscription.
- Get subscription informations.
- Get list of subscriptions
- Create VNF package.
- Upload VNF package.
- Create VNF instance.
- Instantiate VNF.
- Get list of VNF instances.
- Get information of instantiated VNF.
- Heal VNF.
- Create new VNF package for update.
- Upload new VNF package.
- Update VNF with new package.
- Terminate VNF.
- Delete subscription.
"""
# Create subscription and register it.
request_body = fake_vnflcm.Subscription.make_create_request_body(
'http://localhost:{}{}'.format(
vnflcm_base.FAKE_SERVER_MANAGER.SERVER_PORT,
os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)))
resp, response_body = self._register_subscription(request_body)
self.assertEqual(201, resp.status_code)
self.assert_http_header_location_for_subscription(resp.headers)
subscription_id = response_body.get('id')
self.addCleanup(
self._delete_subscription,
subscription_id)
# Subscription show
resp, body = self._wait_show_subscription(subscription_id)
self.assert_subscription_show(resp, body)
# Subscription list
resp, _ = self._list_subscription()
self.assertEqual(200, resp.status_code)
# Pre Setting: Create vnf package.
sample_name = 'functional'
csar_package_path = os.path.abspath(
os.path.join(
os.path.dirname(__file__),
"../../etc/samples/etsi/nfv",
sample_name))
tempname, _ = vnflcm_base._create_csar_with_unique_vnfd_id(
csar_package_path)
# upload vnf package
vnf_package_id, vnfd_id = vnflcm_base._create_and_upload_vnf_package(
self.tacker_client, user_defined_data={
"key": sample_name}, temp_csar_path=tempname)
# Post Setting: Reserve deleting vnf package.
self.addCleanup(
vnflcm_base._delete_vnf_package,
self.tacker_client,
vnf_package_id)
# Create vnf instance
resp, vnf_instance = self._create_vnf_instance_from_body(
fake_vnflcm.VnfInstances.make_create_request_body(vnfd_id))
vnf_instance_id = vnf_instance['id']
self._wait_lcm_done(vnf_instance_id=vnf_instance_id)
self.assert_create_vnf(resp, vnf_instance, vnf_package_id)
vnf_instance_name = vnf_instance['vnfInstanceName']
self.addCleanup(
self._delete_vnf_instance,
vnf_instance_id)
# Instantiate vnf instance
request_body = fake_vnflcm.VnfInstances.make_inst_request_body(
self.vim['tenant_id'],
self.ext_networks,
self.ext_mngd_networks,
self.ext_link_ports,
self.ext_subnets)
resp, _ = self._instantiate_vnf_instance(vnf_instance_id, request_body)
self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id)
self.assert_instantiate_vnf(resp, vnf_instance_id, vnf_package_id)
# List vnf instance
filter_expr = {
'filter': "(eq,id,{});(eq,vnfInstanceName,{})".format(
vnf_instance_id, vnf_instance_name)}
resp, vnf_instances = self._list_vnf_instance(params=filter_expr)
self.assertEqual(200, resp.status_code)
self.assertEqual(1, len(vnf_instances))
# Show vnf instance
resp, vnf_instance = self._show_vnf_instance(vnf_instance_id)
self.assertEqual(200, resp.status_code)
# Update vnf (vnfdId)
sample_name = 'functional2'
csar_package_path = os.path.abspath(
os.path.join(
os.path.dirname(__file__),
"../../etc/samples/etsi/nfv",
sample_name))
tempname, _ = vnflcm_base._create_csar_with_unique_vnfd_id(
csar_package_path)
# upload vnf package
update_vnf_package_id, update_vnfd_id = \
vnflcm_base._create_and_upload_vnf_package(
self.tacker_client,
user_defined_data={"key": sample_name},
temp_csar_path=tempname)
request_body = fake_vnflcm.VnfInstances.make_update_request_body(
vnfd_id=update_vnfd_id)
resp, _ = self._update_vnf_instance(vnf_instance_id, request_body)
self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id)
self.assert_update_vnf(
resp,
vnf_instance_id,
after_id=request_body['vnfdId'],
old_id=vnfd_id)
vnf_package_id = update_vnf_package_id
# Heal vnf (exists vnfc_instace_id)
vnfc_instance_id_list = [
vnfc.get('id') for vnfc in vnf_instance.get(
'instantiatedVnfInfo', {}).get(
'vnfcResourceInfo', [])]
request_body = fake_vnflcm.VnfInstances.make_heal_request_body(
vnfc_instance_id_list)
resp, _ = self._heal_vnf_instance(vnf_instance_id, request_body)
self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id)
self.assert_heal_vnf(resp, vnf_instance_id, vnf_package_id)
# Terminate VNF
stack = self._get_heat_stack(vnf_instance_id)
resources_list = self._get_heat_resource_list(stack.id)
resource_name_list = [r.resource_name for r in resources_list]
glance_image_id_list = self._get_glance_image_list_from_stack_resource(
stack.id,
resource_name_list)
terminate_req_body = fake_vnflcm.VnfInstances.make_term_request_body()
resp, _ = self._terminate_vnf_instance(
vnf_instance_id, terminate_req_body)
self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id)
self.assert_terminate_vnf(
resp,
vnf_instance_id,
stack.id,
resource_name_list,
glance_image_id_list,
vnf_package_id)
# Delete VNF
resp, _ = self._delete_vnf_instance(vnf_instance_id)
self._wait_lcm_done(vnf_instance_id=vnf_instance_id)
self.assert_delete_vnf(resp, vnf_instance_id, vnf_package_id)
# Subscription delete
resp, response_body = self._delete_subscription(subscription_id)
self.assertEqual(204, resp.status_code)
resp, show_body = self._show_subscription(subscription_id)
self.assertEqual(404, resp.status_code)
def test_inst_update_pkgid_heal_all(self):
"""Test basic life cycle operations with pkg update.
In this test case, we do following steps.
- Create subscription.
- Create VNF package.
- Upload VNF package.
- Create VNF instance.
- Instantiate VNF
- Heal VNF
- Create new VNF package for update.
- Upload new VNF package.
- Update VNF with new package.
- Terminate VNF.
- Delete VNF.
- Delete subscription.
"""
# Create subscription and register it.
request_body = fake_vnflcm.Subscription.make_create_request_body(
'http://localhost:{}{}'.format(
vnflcm_base.FAKE_SERVER_MANAGER.SERVER_PORT,
os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)))
resp, response_body = self._register_subscription(request_body)
self.assertEqual(201, resp.status_code)
self.assert_http_header_location_for_subscription(resp.headers)
subscription_id = response_body.get('id')
self.addCleanup(
self._delete_subscription,
subscription_id)
# Pre Setting: Create vnf package.
sample_name = 'functional'
csar_package_path = os.path.abspath(
os.path.join(
os.path.dirname(__file__),
"../../etc/samples/etsi/nfv",
sample_name))
tempname, _ = vnflcm_base._create_csar_with_unique_vnfd_id(
csar_package_path)
# upload vnf package
vnf_package_id, vnfd_id = vnflcm_base._create_and_upload_vnf_package(
self.tacker_client, user_defined_data={
"key": sample_name}, temp_csar_path=tempname)
# Post Setting: Reserve deleting vnf package.
self.addCleanup(
vnflcm_base._delete_vnf_package,
self.tacker_client,
vnf_package_id)
# Create vnf instance
resp, vnf_instance = self._create_vnf_instance_from_body(
fake_vnflcm.VnfInstances.make_create_request_body(vnfd_id))
vnf_instance_id = vnf_instance['id']
self._wait_lcm_done(vnf_instance_id=vnf_instance_id)
self.assert_create_vnf(resp, vnf_instance, vnf_package_id)
self.addCleanup(
self._delete_vnf_instance,
vnf_instance_id)
# Instantiate vnf instance
request_body = fake_vnflcm.VnfInstances.make_inst_request_body(
self.vim['tenant_id'],
self.ext_networks,
self.ext_mngd_networks,
self.ext_link_ports,
self.ext_subnets)
resp, _ = self._instantiate_vnf_instance(vnf_instance_id, request_body)
self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id)
self.assert_instantiate_vnf(resp, vnf_instance_id, vnf_package_id)
# Heal vnf (do not specify vnfc_instace_id)
# pre check heat status.
self.assert_heat_stack_status(vnf_instance_id)
request_body = fake_vnflcm.VnfInstances.make_heal_request_body()
resp, _ = self._heal_vnf_instance(vnf_instance_id, request_body)
self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id)
# post check heat status.
self.assert_heal_vnf(
resp,
vnf_instance_id,
vnf_package_id,
expected_stack_status='CREATE_COMPLETE')
# Update vnf (vnfPkgId)
sample_name = 'functional2'
csar_package_path = os.path.abspath(
os.path.join(
os.path.dirname(__file__),
"../../etc/samples/etsi/nfv",
sample_name))
tempname, _ = vnflcm_base._create_csar_with_unique_vnfd_id(
csar_package_path)
# upload vnf package
update_vnf_package_id, _ = vnflcm_base._create_and_upload_vnf_package(
self.tacker_client, user_defined_data={
"key": sample_name}, temp_csar_path=tempname)
request_body = fake_vnflcm.VnfInstances.make_update_request_body(
vnf_package_id=update_vnf_package_id)
resp, _ = self._update_vnf_instance(vnf_instance_id, request_body)
self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id)
self.assert_update_vnf(
resp,
vnf_instance_id,
is_vnfd=False,
after_id=request_body['vnfPkgId'],
old_id=vnf_package_id)
vnf_package_id = update_vnf_package_id
# Terminate VNF
stack = self._get_heat_stack(vnf_instance_id)
resources_list = self._get_heat_resource_list(stack.id)
resource_name_list = [r.resource_name for r in resources_list]
glance_image_id_list = self._get_glance_image_list_from_stack_resource(
stack.id,
resource_name_list)
terminate_req_body = fake_vnflcm.VnfInstances.make_term_request_body()
resp, _ = self._terminate_vnf_instance(
vnf_instance_id, terminate_req_body)
self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id)
self.assert_terminate_vnf(
resp,
vnf_instance_id,
stack.id,
resource_name_list,
glance_image_id_list,
vnf_package_id)
# Delete VNF
resp, _ = self._delete_vnf_instance(vnf_instance_id)
self._wait_lcm_done(vnf_instance_id=vnf_instance_id)
self.assert_delete_vnf(resp, vnf_instance_id, vnf_package_id)
# Subscription delete
resp, response_body = self._delete_subscription(subscription_id)
self.assertEqual(204, resp.status_code)
def test_instantiate_vnf_basehot_invalid(self):
"""Test instantiation operation with invalid HOT data.
In this test case, we do following steps.
- Create VNF package.
- Upload VNF package.
- Create VNF instance.
- Instantiate VNF
"""
# Pre Setting: Create vnf package.
sample_name = "user_data_sample_basehot_invalid"
csar_package_path = os.path.abspath(
os.path.join(
os.path.dirname(__file__),
"../../etc/samples/etsi/nfv",
sample_name))
tempname, _ = vnflcm_base._create_csar_user_data_common(
csar_package_path)
# upload vnf package
vnf_package_id, vnfd_id = vnflcm_base._create_and_upload_vnf_package(
self.tacker_client, user_defined_data={
"key": sample_name}, temp_csar_path=tempname)
# Reserve deleting vnf package
self.addCleanup(
vnflcm_base._delete_vnf_package,
self.tacker_client,
vnf_package_id)
# Settings
vnf_instance_name = "vnf_with_user_data-%s" % \
uuidutils.generate_uuid()
vnf_instance_description = "vnf_with_user_data_basehot_invalid"
add_params = {
"lcm-operation-user-data": "./UserData/lcm_user_data.py",
"lcm-operation-user-data-class": "SampleUserData"}
# Create vnf instance
resp, vnf_instance = self._create_vnf_instance(vnfd_id,
vnf_instance_name=vnf_instance_name,
vnf_instance_description=vnf_instance_description)
self.assertIsNotNone(vnf_instance['id'])
self.assertEqual(201, resp.status_code)
# Reserve deleting vnf instance
self.addCleanup(self._delete_vnf_instance, vnf_instance['id'])
request_body = \
vnflcm_base._create_instantiate_vnf_request_body("simple",
vim_id=self.vim['id'],
ext_vl=self.ext_vl,
add_params=add_params)
self._instantiate_vnf_instance_fail(vnf_instance['id'], request_body)
def test_instantiate_vnf_userdata_timeout(self):
"""Test instantiation operation timeout with long-running script.
In this test case, we do following steps.
- Create VNF package.
- Upload VNF package.
- Create VNF instance.
- Instantiate VNF
"""
# Pre Setting: Create vnf package.
sample_name = "user_data_sample_userdata_timeout"
csar_package_path = os.path.abspath(
os.path.join(
os.path.dirname(__file__),
"../../etc/samples/etsi/nfv",
sample_name))
tempname, _ = vnflcm_base._create_csar_user_data_common(
csar_package_path)
# upload vnf package
vnf_package_id, vnfd_id = vnflcm_base._create_and_upload_vnf_package(
self.tacker_client, user_defined_data={
"key": sample_name}, temp_csar_path=tempname)
# Reserve deleting vnf package
self.addCleanup(
vnflcm_base._delete_vnf_package,
self.tacker_client,
vnf_package_id)
# Settings
vnf_instance_name = "vnf_with_user_data-%s" % \
uuidutils.generate_uuid()
vnf_instance_description = "vnf_with_user_data_timeout"
add_params = {
"lcm-operation-user-data": "./UserData/lcm_user_data_sleeping.py",
"lcm-operation-user-data-class": "SampleUserData"}
# Create vnf instance
resp, vnf_instance = self._create_vnf_instance(vnfd_id,
vnf_instance_name=vnf_instance_name,
vnf_instance_description=vnf_instance_description)
self.assertIsNotNone(vnf_instance['id'])
self.assertEqual(201, resp.status_code)
# Reserve deleting vnf instance
self.addCleanup(self._delete_vnf_instance, vnf_instance['id'])
request_body = \
vnflcm_base._create_instantiate_vnf_request_body("simple",
vim_id=self.vim['id'],
ext_vl=self.ext_vl,
add_params=add_params)
self._instantiate_vnf_instance_fail(vnf_instance['id'], request_body)
def test_instantiate_vnf_userdata_invalid_hot_param(self):
"""Test instantiation operation with invalid HOT and user data.
In this test case, we do following steps.
- Create VNF package.
- Upload VNF package.
- Create VNF instance.
- Instantiate VNF
"""
# Pre Setting: Create vnf package.
sample_name = "user_data_sample_userdata_invalid_hot_param"
csar_package_path = os.path.abspath(
os.path.join(
os.path.dirname(__file__),
"../../etc/samples/etsi/nfv",
sample_name))
tempname, _ = vnflcm_base._create_csar_user_data_common(
csar_package_path)
# upload vnf package
vnf_package_id, vnfd_id = vnflcm_base._create_and_upload_vnf_package(
self.tacker_client, user_defined_data={
"key": sample_name}, temp_csar_path=tempname)
# Reserve deleting vnf package
self.addCleanup(
vnflcm_base._delete_vnf_package,
self.tacker_client,
vnf_package_id)
# Settings
vnf_instance_name = "vnf_with_user_data-%s" % \
uuidutils.generate_uuid()
vnf_instance_description = "vnf_with_user_data_timeout"
add_params = {
"lcm-operation-user-data": "./UserData/"
"lcm_user_data_invalid_hot_param.py",
"lcm-operation-user-data-class": "SampleUserData"}
# Create vnf instance
resp, vnf_instance = self._create_vnf_instance(vnfd_id,
vnf_instance_name=vnf_instance_name,
vnf_instance_description=vnf_instance_description)
self.assertIsNotNone(vnf_instance['id'])
self.assertEqual(201, resp.status_code)
# Reserve deleting vnf instance
self.addCleanup(self._delete_vnf_instance, vnf_instance['id'])
request_body = \
vnflcm_base._create_instantiate_vnf_request_body("simple",
vim_id=self.vim['id'],
ext_vl=self.ext_vl,
add_params=add_params)
self._instantiate_vnf_instance_fail(vnf_instance['id'], request_body)
def test_instantiate_vnf_userdata_none(self):
"""Test instantiation operation timeout with none user data.
In this test case, we do following steps.
- Create VNF package.
- Upload VNF package.
- Create VNF instance.
- Instantiate VNF
"""
# Pre Setting: Create vnf package.
sample_name = "user_data_sample_userdata_none"
csar_package_path = os.path.abspath(
os.path.join(
os.path.dirname(__file__),
"../../etc/samples/etsi/nfv",
sample_name))
tempname, _ = vnflcm_base._create_csar_user_data_common(
csar_package_path)
# upload vnf package
vnf_package_id, vnfd_id = vnflcm_base._create_and_upload_vnf_package(
self.tacker_client, user_defined_data={
"key": sample_name}, temp_csar_path=tempname)
# Reserve deleting vnf package
self.addCleanup(
vnflcm_base._delete_vnf_package,
self.tacker_client,
vnf_package_id)
# Settings
vnf_instance_name = "vnf_with_user_data-%s" % \
uuidutils.generate_uuid()
vnf_instance_description = "vnf_with_user_data_timeout"
add_params = {
"lcm-operation-user-data": "./UserData/lcm_user_data.py",
"lcm-operation-user-data-class": "SampleUserData"}
# Create vnf instance
resp, vnf_instance = self._create_vnf_instance(vnfd_id,
vnf_instance_name=vnf_instance_name,
vnf_instance_description=vnf_instance_description)
self.assertIsNotNone(vnf_instance['id'])
self.assertEqual(201, resp.status_code)
# Reserve deleting vnf instance
self.addCleanup(self._delete_vnf_instance, vnf_instance['id'])
request_body = \
vnflcm_base._create_instantiate_vnf_request_body("simple",
vim_id=self.vim['id'],
ext_vl=self.ext_vl,
add_params=add_params)
self._instantiate_vnf_instance_fail(vnf_instance['id'], request_body)
def test_instantiate_vnf_userdata_invalid_script(self):
"""Test instantiation operation with invalid user script.
In this test case, we do following steps.
- Create VNF package.
- Upload VNF package.
- Create VNF instance.
- Instantiate VNF
"""
# Pre Setting: Create vnf package.
sample_name = "user_data_sample_userdata_invalid_script"
csar_package_path = os.path.abspath(
os.path.join(
os.path.dirname(__file__),
"../../etc/samples/etsi/nfv",
sample_name))
tempname, _ = vnflcm_base._create_csar_user_data_common(
csar_package_path)
# upload vnf package
vnf_package_id, vnfd_id = vnflcm_base._create_and_upload_vnf_package(
self.tacker_client, user_defined_data={
"key": sample_name}, temp_csar_path=tempname)
# Reserve deleting vnf package
self.addCleanup(
vnflcm_base._delete_vnf_package,
self.tacker_client,
vnf_package_id)
# Settings
vnf_instance_name = "vnf_with_user_data-%s" % \
uuidutils.generate_uuid()
vnf_instance_description = "vnf_with_user_data_timeout"
add_params = {
"lcm-operation-user-data": "./UserData/"
"lcm_user_data_invalid_script.py",
"lcm-operation-user-data-class": "SampleUserData"}
# Create vnf instance
resp, vnf_instance = self._create_vnf_instance(vnfd_id,
vnf_instance_name=vnf_instance_name,
vnf_instance_description=vnf_instance_description)
self.assertIsNotNone(vnf_instance['id'])
self.assertEqual(201, resp.status_code)
# Reserve deleting vnf instance
self.addCleanup(self._delete_vnf_instance, vnf_instance['id'])
request_body = \
vnflcm_base._create_instantiate_vnf_request_body("simple",
vim_id=self.vim['id'],
ext_vl=self.ext_vl,
add_params=add_params)
self._instantiate_vnf_instance_fail(vnf_instance['id'], request_body)
def assert_create_vnf(self, resp, vnf_instance, vnf_pkg_id):
super().assert_create_vnf(resp, vnf_instance)
@@ -112,6 +738,49 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
self.tacker_client, vnf_pkg_id)
self.assert_vnf_package_usage_state(vnf_pkg_info)
def assert_update_vnf(self, resp, vnf_instance_id, is_vnfd=True,
after_id=None, old_id=None,
expected_stack_status='CREATE_COMPLETE'):
"""Assert that VNF was updated.
This method checks if a VNF was really updated or not.
We use the same name method of super class to check
lifecycle event(e.g. LcmOpOccs, heat stack status).
And then, in this method, it gets vnf package info from tacker.
We confirm that the old package status is 'NOT_IN_USE' and also
a new package status is the same as expected.
Args:
resp (Response): Response headers for HTTP requests.
vnf_instance_id (str): Self explanatly
is_vnfd (bool, optional): Specify target VNF is VNFD or
not. Defaults to True.
after_id (str): Updated VNF id. It should be id of VNFD
or VNF Package. Defaults to None.
old_id (str): Present VNF id. Defaults to None.
expected_stack_status (str, optional): The expected status
of updated VNF. Defaults to 'CREATE_COMPLETE'.
"""
super().assert_update_vnf(
resp, vnf_instance_id, expected_stack_status=expected_stack_status)
if is_vnfd:
after_filter_attr = {'filter': "(eq,vnfdId,{})".format(after_id)}
old_filter_attr = {'filter': "(eq,vnfdId,{})".format(old_id)}
else:
after_filter_attr = {'filter': "(eq,id,{})".format(after_id)}
old_filter_attr = {'filter': "(eq,id,{})".format(old_id)}
# assert old/new package status.
resp, after_vnf_pkg_info = vnflcm_base._list_vnf_package(
self.tacker_client, params=after_filter_attr)
self.assert_vnf_package_usage_state(after_vnf_pkg_info[0])
resp, old_vnf_pkg_info = vnflcm_base._list_vnf_package(
self.tacker_client, params=old_filter_attr)
self.assert_vnf_package_usage_state(old_vnf_pkg_info[0],
expected_usage_state=fields.PackageUsageStateType.NOT_IN_USE)
def assert_vnf_package_usage_state(
self,
vnf_package_info,