FT for multi tenant policy in LCM

This patch adds functional test cases to validate the subscription
and vnf package functionality in a multi-tenant environment.
Validates VNF instantiation is only allowed when VNF and VIM belongs
to same tenants.

The patch covers only positive test cases of the feature.
Validation of negative test cases would require design changes in Fake
NFVO server, which could be implemented in the upcoming cycle.

Additionally, add missing "domain-name" field while creating VIM config
file in tools/gen_vim_config.sh.

Implement: blueprint multi-tenant-policy

Change-Id: I57d2ec780bd65423820c291bc67d1328bcf9f620
This commit is contained in:
Manpreet Kaur 2022-01-10 16:31:42 +05:30
parent 1cb068c74a
commit a039fa329e
21 changed files with 1623 additions and 9 deletions

View File

@ -275,6 +275,15 @@
controller-tacker:
tox_envlist: dsvm-functional-sol-v2
- job:
name: tacker-functional-devstack-multinode-sol-multi-tenant
parent: tacker-functional-devstack-multinode-sol
description: |
Multinodes job for SOL Multi tenant devstack-based functional tests
host-vars:
controller-tacker:
tox_envlist: dsvm-functional-sol-multi-tenant
- job:
name: tacker-functional-devstack-multinode-sol-separated-nfvo
parent: tacker-functional-devstack-multinode-sol
@ -522,3 +531,4 @@
- tacker-functional-devstack-multinode-sol-kubernetes
- tacker-functional-devstack-multinode-libs-master
- tacker-functional-devstack-multinode-sol-v2
- tacker-functional-devstack-multinode-sol-multi-tenant

View File

@ -163,7 +163,7 @@ def _vnf_lcm_subscriptions_show(context, subscriptionId):
sql = text(
"select "
"t1.id,t1.callback_uri,t2.filter "
"t1.id,t1.callback_uri,t1.tenant_id,t2.filter "
"from vnf_lcm_subscriptions t1, "
"(select distinct subscription_uuid,filter from vnf_lcm_filters) t2 "
"where t1.id = t2.subscription_uuid "

View File

@ -0,0 +1,99 @@
heat_template_version: 2013-05-23
description: 'Simple Base HOT for Sample VNF'
parameters:
nfv:
type: json
resources:
VDU1_scale:
type: OS::Heat::AutoScalingGroup
properties:
min_size: 1
max_size: 3
desired_capacity: 1
resource:
type: VDU1.yaml
properties:
flavor: { get_param: [ nfv, VDU, VDU1, flavor ] }
image: { get_param: [ nfv, VDU, 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_scale_out:
type: OS::Heat::ScalingPolicy
properties:
scaling_adjustment: 1
auto_scaling_group_id:
get_resource: VDU1_scale
adjustment_type: change_in_capacity
VDU1_scale_scale_in:
type: OS::Heat::ScalingPolicy
properties:
scaling_adjustment: -1
auto_scaling_group_id:
get_resource: VDU1_scale
adjustment_type: change_in_capacity
VDU2_scale:
type: OS::Heat::AutoScalingGroup
depends_on: VDU1_scale
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_scale_out:
type: OS::Heat::ScalingPolicy
properties:
scaling_adjustment: 1
auto_scaling_group_id:
get_resource: VDU2_scale
adjustment_type: change_in_capacity
VDU2_scale_scale_in:
type: OS::Heat::ScalingPolicy
properties:
scaling_adjustment: -1
auto_scaling_group_id:
get_resource: VDU2_scale
adjustment_type: change_in_capacity
extmanageNW_1:
type: OS::Neutron::Net
extmanageNW_2:
type: OS::Neutron::Net
internalNW_1:
type: OS::Neutron::Net
extmanageNW_1_subnet:
type: OS::Neutron::Subnet
properties:
ip_version: 4
network:
get_resource: extmanageNW_1
cidr: 192.168.3.0/24
extmanageNW_2_subnet:
type: OS::Neutron::Subnet
properties:
ip_version: 4
network:
get_resource: extmanageNW_2
cidr: 192.168.4.0/24
internalNW_1_subnet:
type: OS::Neutron::Subnet
properties:
ip_version: 4
network:
get_resource: internalNW_1
cidr: 192.168.5.0/24
outputs: {}

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: errornetwork
VDU2_CP5:
type: OS::Neutron::Port
properties:
network: errornetwork

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.5.2-x86_64-disk
version: '0.5.2'
checksum:
algorithm: sha-256
hash: 932fcae93574e242dc3d772d5235061747dfe537668443a1f0567d893614b464
container_format: bare
disk_format: qcow2
min_disk: 0 GB
min_ram: 256 MB
size: 12 GB
capabilities:
virtual_compute:
properties:
requested_additional_capabilities:
properties:
requested_additional_capability_name: m1.tiny
support_mandatory: true
target_performance_parameters:
entry_schema: test
virtual_memory:
virtual_mem_size: 512 MB
virtual_cpu:
num_virtual_cpu: 1
virtual_local_storage:
- size_of_storage: 3 GB
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.5.2-x86_64-disk
version: '0.5.2'
checksum:
algorithm: sha-256
hash: 932fcae93574e242dc3d772d5235061747dfe537668443a1f0567d893614b464
container_format: bare
disk_format: qcow2
min_disk: 0 GB
min_ram: 256 MB
size: 12 GB
VDU1_CP1:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 0
requirements:
- virtual_binding: VDU1
VDU1_CP2:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 1
requirements:
- virtual_binding: VDU1
VDU1_CP3:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 2
requirements:
- virtual_binding: VDU1
- virtual_link: internalVL1
VDU1_CP4:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 3
requirements:
- virtual_binding: VDU1
- virtual_link: internalVL2
VDU1_CP5:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 4
requirements:
- virtual_binding: VDU1
- virtual_link: internalVL3
VDU2_CP1:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 0
requirements:
- virtual_binding: VDU2
VDU2_CP2:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 1
requirements:
- virtual_binding: VDU2
VDU2_CP3:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 2
requirements:
- virtual_binding: VDU2
- virtual_link: internalVL1
VDU2_CP4:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 3
requirements:
- virtual_binding: VDU2
- virtual_link: internalVL2
VDU2_CP5:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 4
requirements:
- virtual_binding: VDU2
- virtual_link: internalVL3
internalVL1:
type: tosca.nodes.nfv.VnfVirtualLink
properties:
connectivity_type:
layer_protocols: [ ipv4 ]
description: External Managed Virtual link in the VNF
vl_profile:
max_bitrate_requirements:
root: 1048576
leaf: 1048576
min_bitrate_requirements:
root: 1048576
leaf: 1048576
virtual_link_protocol_data:
- associated_layer_protocol: ipv4
l3_protocol_data:
ip_version: ipv4
cidr: 33.33.0.0/24
internalVL2:
type: tosca.nodes.nfv.VnfVirtualLink
properties:
connectivity_type:
layer_protocols: [ ipv4 ]
description: External Managed Virtual link in the VNF
vl_profile:
max_bitrate_requirements:
root: 1048576
leaf: 1048576
min_bitrate_requirements:
root: 1048576
leaf: 1048576
virtual_link_protocol_data:
- associated_layer_protocol: ipv4
l3_protocol_data:
ip_version: ipv4
cidr: 33.34.0.0/24
internalVL3:
type: tosca.nodes.nfv.VnfVirtualLink
properties:
connectivity_type:
layer_protocols: [ ipv4 ]
description: Internal Virtual link in the VNF
vl_profile:
max_bitrate_requirements:
root: 1048576
leaf: 1048576
min_bitrate_requirements:
root: 1048576
leaf: 1048576
virtual_link_protocol_data:
- associated_layer_protocol: ipv4
l3_protocol_data:
ip_version: ipv4
cidr: 33.35.0.0/24
policies:
- scaling_aspects:
type: tosca.policies.nfv.ScalingAspects
properties:
aspects:
VDU1_scale:
name: VDU1_scale
description: VDU1 scaling aspect
max_scale_level: 2
step_deltas:
- delta_1
- 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: VDU1_scale
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:
VDU1_scale:
scale_level: 0
instantiation_level_2:
description: Largest size
scale_info:
VDU1_scale:
scale_level: 2
default_level: instantiation_level_1
- VDU1_instantiation_levels:
type: tosca.policies.nfv.VduInstantiationLevels
properties:
levels:
instantiation_level_1:
number_of_instances: 1
instantiation_level_2:
number_of_instances: 3
targets: [ VDU1 ]
- VDU2_instantiation_levels:
type: tosca.policies.nfv.VduInstantiationLevels
properties:
levels:
instantiation_level_1:
number_of_instances: 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

@ -454,10 +454,12 @@ class BaseTackerTest(base.BaseTestCase):
return vnf_instance, tosca_dict
def _list_op_occs(self, filter_string=''):
def _list_op_occs(self, filter_string='', http_client=None):
if http_client is None:
http_client = self.http_client
show_url = os.path.join(
self.base_vnf_lcm_op_occs_url)
resp, response_body = self.http_client.do_request(
resp, response_body = http_client.do_request(
show_url + filter_string, "GET")
return resp, response_body

View File

@ -206,6 +206,8 @@ class FakeServerManager(object):
"""Manager class to manage dummy server setting and control"""
SERVER_PORT = 9990
SERVER_PORT_T1 = 9995
SERVER_PORT_T2 = 9996
SERVER_INVOKE_CHECK_INTERVAL = 10
def __init__(self):

View File

@ -209,6 +209,12 @@ def _create_instantiate_vnf_request_body(flavour_id,
class BaseVnfLcmTest(base.BaseTackerTest):
is_setup_error = False
# NOTE: If prepare_fake_server is set(by default) then this base
# class will prepare(create and start) the fake http server which
# takes time and can add up more time in gate CI. Any child class
# can set it false. For example, BaseVnfLcmMultiTenantTest which
# create their own servers for two different tenants.
prepare_fake_server = True
@classmethod
def setUpClass(cls):
@ -217,13 +223,15 @@ class BaseVnfLcmTest(base.BaseTackerTest):
we set up fake NFVO server for test at here.
'''
super(BaseVnfLcmTest, cls).setUpClass()
cls._prepare_start_fake_server(FAKE_SERVER_MANAGER,
if cls.prepare_fake_server:
cls._prepare_start_fake_server(FAKE_SERVER_MANAGER,
FAKE_SERVER_PORT)
@classmethod
def tearDownClass(cls):
super(BaseVnfLcmTest, cls).tearDownClass()
FAKE_SERVER_MANAGER.stop_server()
if cls.prepare_fake_server:
FAKE_SERVER_MANAGER.stop_server()
def setUp(self):
super(BaseVnfLcmTest, self).setUp()
@ -231,10 +239,10 @@ class BaseVnfLcmTest(base.BaseTackerTest):
if self.is_setup_error:
self.fail("Faild, not exists pre-registered image.")
callback_url = os.path.join(
MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)
self._clear_history_and_set_callback(FAKE_SERVER_MANAGER,
if self.prepare_fake_server:
callback_url = os.path.join(MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)
self._clear_history_and_set_callback(FAKE_SERVER_MANAGER,
callback_url)
self.tacker_client = base.BaseTackerTest.tacker_http_client()

View File

@ -0,0 +1,118 @@
#
# 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
from tacker.tests.functional import base
from tacker.tests.functional.common.fake_server import FakeServerManager
from tacker.tests.functional.sol.vnflcm import base as vnflcm_base
FAKE_SERVER_MANAGER_T1 = FakeServerManager()
FAKE_SERVER_PORT_T1 = 9995
FAKE_SERVER_MANAGER_T2 = FakeServerManager()
FAKE_SERVER_PORT_T2 = 9996
class BaseVnfLcmMultiTenantTest(vnflcm_base.BaseVnfLcmTest):
prepare_fake_server = False
@classmethod
def setUpClass(cls):
super(BaseVnfLcmMultiTenantTest, cls).setUpClass()
result = cls.get_openstack_client_session(
vim_conf_file='local-tenant1-vim.yaml')
cls.client_tenant1 = result.get('client')
cls.http_client_tenant1 = result.get('http_client')
cls.h_client_tenant1 = result.get('h_client')
cls.glance_client_tenant1 = result.get('glance_client')
result = cls.get_openstack_client_session(
vim_conf_file='local-tenant2-vim.yaml')
cls.client_tenant2 = result.get('client')
cls.http_client_tenant2 = result.get('http_client')
cls.h_client_tenant2 = result.get('h_client')
cls.glance_client_tenant2 = result.get('glance_client')
cls.tacker_client_t1 = base.BaseTackerTest.tacker_http_client(
'local-tenant1-vim.yaml')
cls.tacker_client_t2 = base.BaseTackerTest.tacker_http_client(
'local-tenant2-vim.yaml')
# Set up fake NFVO server for tenant1 and tenant2
cls.servers = {FAKE_SERVER_PORT_T1: FAKE_SERVER_MANAGER_T1,
FAKE_SERVER_PORT_T2: FAKE_SERVER_MANAGER_T2}
# NOTE: Create both server in parallel, otherwise they can
# cause (especially server start) job timeout.
for port, manager in cls.servers.items():
cls._prepare_start_fake_server(manager, port)
@classmethod
def tearDownClass(cls):
super(BaseVnfLcmMultiTenantTest, cls).tearDownClass()
for _, manager in cls.servers.items():
manager.stop_server()
def setUp(self):
super(BaseVnfLcmMultiTenantTest, self).setUp()
self.base_url = "/vnfpkgm/v1/vnf_packages"
callback_url = os.path.join(
vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)
self._clear_history_and_set_callback(FAKE_SERVER_MANAGER_T1,
callback_url)
self._clear_history_and_set_callback(FAKE_SERVER_MANAGER_T2,
callback_url)
vim_list = self.client.list_vims()
self.vim_tenant1 = self.get_vim(vim_list, 'VIM_TEST')
if not self.vim_tenant1:
assert False, "vim_list is Empty: Tenant VIM is missing"
self.vim_tenant2 = self.get_vim(vim_list, 'VIM_DEMO')
if not self.vim_tenant2:
assert False, "vim_list is Empty: Tenant VIM is missing"
result = self._create_network_settings()
self.ext_networks_tenant1 = result.get('ext_networks')
self.ext_vl_tenant1 = result.get('ext_vl')
self.ext_mngd_networks_tenant1 = result.get('ext_mngd_networks')
self.ext_link_ports_tenant1 = result.get('ext_link_ports')
self.ext_subnets_tenant1 = result.get('ext_subnets')
self.changed_ext_networks_tenant1 = result.get(
'changed_ext_networks')
self.changed_ext_subnets_tenant1 = result.get(
'changed_ext_subnets')
result = self._create_network_settings()
self.ext_networks_tenant2 = result.get('ext_networks')
self.ext_vl_tenant2 = result.get('ext_vl')
self.ext_mngd_networks_tenant2 = result.get('ext_mngd_networks')
self.ext_link_ports_tenant2 = result.get('ext_link_ports')
self.ext_subnets_tenant2 = result.get('ext_subnets')
self.changed_ext_networks_tenant2 = result.get(
'changed_ext_networks')
self.changed_ext_subnets_tenant2 = result.get(
'changed_ext_subnets')
@classmethod
def get_openstack_client_session(cls, vim_conf_file):
client = base.BaseTackerTest.tackerclient(vim_conf_file)
http_client = base.BaseTackerTest.tacker_http_client(vim_conf_file)
h_client = base.BaseTackerTest.heatclient(vim_conf_file)
glance_client = base.BaseTackerTest.glanceclient(vim_conf_file)
return {'client': client,
'http_client': http_client,
'h_client': h_client,
'glance_client': glance_client}

View File

@ -0,0 +1,252 @@
#
# 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.tests import uuidsentinel
class Subscription:
@staticmethod
def make_create_request_body(callback_uri):
"""Parameter selection policy.
Set all Notification types and all life cycle types for filter.
Specify OAuth2 for authentication do not set authentication.
Args:
callback_uri (str): Notification URI.
Returns:
dict: Request body
"""
return {
"filter": {
"vnfInstanceSubscriptionFilter": {
"vnfdIds": ["b1bb0ce7-ebca-4fa7-95ed-4840d7000000"],
"vnfProductsFromProviders": [{
"vnfProvider": "Company",
"vnfProducts": [
{
"vnfProductName": "Sample VNF",
"versions": [
{
"vnfSoftwareVersion": "1.0",
"vnfdVersions": ["1.0"]
}
]
}
]
}]
},
"notificationTypes": [
"VnfLcmOperationOccurrenceNotification",
"VnfIdentifierCreationNotification",
"VnfIdentifierDeletionNotification"
],
"operationTypes": [
"INSTANTIATE",
"SCALE",
"TERMINATE",
"HEAL",
"MODIFY_INFO",
"CHANGE_EXT_CONN"
],
"operationStates": ["STARTING"]
},
"callbackUri": callback_uri
}
ext_vdu1_cp1 = {
"cpdId": "VDU1_CP1",
"cpConfig": [{
"linkPortId": uuidsentinel.elp1_id
}],
}
ext_vdu2_cp1 = {
"cpdId": "VDU2_CP1",
"cpConfig": [{
"linkPortId": uuidsentinel.elp2_id
}]
}
def _set_ext_link_port1(external_ports_id):
ext_link_port1 = {
"id": uuidsentinel.elp1_id,
"resourceHandle": {
"vimConnectionId": uuidsentinel.vim_connection_id,
"resourceId": external_ports_id[0]
}
}
return ext_link_port1
def _set_ext_link_port2(external_ports_id):
ext_link_port2 = {
"id": uuidsentinel.elp2_id,
"resourceHandle": {
"vimConnectionId": uuidsentinel.vim_connection_id,
"resourceId": external_ports_id[1]
}
}
return ext_link_port2
def _set_ext_virtual_link_cp1(networks_id, external_ports_id):
ext_virtual_link_cp1 = {
"id": uuidsentinel.evl1_id,
"resourceId": networks_id[0],
"vimConnectionId": uuidsentinel.vim_connection_id,
"extCps": [ext_vdu1_cp1, ext_vdu2_cp1],
"extLinkPorts": [
_set_ext_link_port1(external_ports_id),
_set_ext_link_port2(external_ports_id)]
}
return ext_virtual_link_cp1
def _set_ext_cps_vdu1_cp2(external_subnets_id):
ext_cps_vdu1_cp2 = {
"cpdId": "VDU1_CP2",
"cpConfig": [{
"cpProtocolData": [{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [{
"type": "IPV4",
"fixedAddresses": ["22.22.1.10"],
"subnetId": external_subnets_id[1]
}]
}
}]
}]
}
return ext_cps_vdu1_cp2
def _set_ext_cps_vdu2_cp2(external_subnets_id):
ext_cps_vdu2_cp2 = {
"cpdId": "VDU2_CP2",
"cpConfig": [{
"cpProtocolData": [{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [{
"type": "IPV4",
"fixedAddresses": ["22.22.1.20"],
"subnetId": external_subnets_id[1]
}]
}
}]
}]
}
return ext_cps_vdu2_cp2
def _set_ext_virtual_link_cp2(networks_id, external_subnets_id):
ext_virtual_link_cp2 = {
"id": uuidsentinel.evl2_id,
"resourceId": networks_id[1],
"vimConnectionId": uuidsentinel.vim_connection_id,
"extCps": [
_set_ext_cps_vdu1_cp2(external_subnets_id),
_set_ext_cps_vdu2_cp2(external_subnets_id)
]
}
return ext_virtual_link_cp2
def _set_ext_mng_vtl_lnks(ext_mngd_networks_id):
ext_mng_vtl_lnks = [{
"id": uuidsentinel.emvl1_id,
"vnfVirtualLinkDescId": "internalVL1",
"resourceId": ext_mngd_networks_id[0]
}, {
"id": uuidsentinel.emvl2_id,
"vnfVirtualLinkDescId": "internalVL2",
"resourceId": ext_mngd_networks_id[1]
}]
return ext_mng_vtl_lnks
class VnfInstances:
@staticmethod
def make_create_request_body(vnfd_id):
return {
"vnfdId": vnfd_id,
"vnfInstanceName": "",
"vnfInstanceDescription": "Sample VNF",
"metadata": {
"samplekey": "samplevalue"
}
}
@staticmethod
def make_inst_request_body(
user_name,
tenant_id,
networks_id,
ext_mngd_networks_id,
external_ports_id,
external_subnets_id):
data = {
"flavourId": "simple",
"instantiationLevelId": "instantiation_level_1",
"extVirtualLinks": [
_set_ext_virtual_link_cp1(
networks_id, external_ports_id),
_set_ext_virtual_link_cp2(
networks_id, external_subnets_id)
],
"extManagedVirtualLinks": _set_ext_mng_vtl_lnks(
ext_mngd_networks_id),
"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"
},
"accessInfo": {
"username": user_name,
"region": "RegionOne",
"password": "devstack",
"tenant": tenant_id
}
}],
"additionalParams": {
"lcm-operation-user-data": "./UserData/lcm_user_data.py",
"lcm-operation-user-data-class": "SampleUserData"
}
}
return data
@staticmethod
def make_term_request_body():
"""Parameter selection policy.
As all parameters are set, GRACEFUL is specified for terminationType.
(to specify gracefulTerminationTimeout)
Returns:
dict: Request body
"""
return {
"terminationType": "GRACEFUL",
"gracefulTerminationTimeout": 1,
"additionalParams": {
"samplekey": "samplevalue"
}
}

View File

@ -0,0 +1,455 @@
#
# 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
from oslo_serialization import jsonutils
from tacker.objects import fields
from tacker.tests.functional.sol.vnflcm import base as vnflcm_base
from tacker.tests.functional.sol_multi_tenant.vnflcm import base
from tacker.tests.functional.sol_multi_tenant.vnflcm import fake_vnflcm
import tempfile
import time
class VnfLcmWithMultiTenant(base.BaseVnfLcmMultiTenantTest):
VNF_PACKAGE_DELETE_TIMEOUT = 120
@classmethod
def setUpClass(cls):
super(VnfLcmWithMultiTenant, cls).setUpClass()
# ModifyVNF specific image create.
is_setup_error_tenant1 = cls._modify_vnf_specific_image_create(
cls.glance_client_tenant1)
if is_setup_error_tenant1:
cls.is_setup_error = True
return
is_setup_error_tenant2 = cls._modify_vnf_specific_image_create(
cls.glance_client_tenant2)
if is_setup_error_tenant2:
cls.is_setup_error = True
return
@classmethod
def _modify_vnf_specific_image_create(cls, glance_clt):
is_setup_error = False
images = cls._list_glance_image()
if len(images) == 0:
is_setup_error = True
return is_setup_error
for image in images:
specific_image_name = f'{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 = glance_clt.http_client.get(
f'{glance_clt.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)
is_setup_error = True
return is_setup_error
return is_setup_error
def _wait_show_subscription(self, subscription_id, tacker_client):
# wait for subscription creation
timeout = vnflcm_base.VNF_SUBSCRIPTION_TIMEOUT
start_time = int(time.time())
while True:
resp, body = self._show_subscription(subscription_id,
tacker_client)
if resp.ok or resp.status_code == 404:
return resp, body
if ((int(time.time()) - start_time) > timeout):
if resp:
resp.raise_for_status()
raise TimeoutError("Failed to show_subscription")
time.sleep(1)
def _delete_vnf_package(self, package_uuid, http_client):
url = os.path.join(self.base_url, package_uuid)
resp, _ = http_client.do_request(url, "DELETE")
self.assertEqual(204, resp.status_code)
def _wait_for_delete(self, package_uuid, http_client):
show_url = os.path.join(self.base_url, package_uuid)
timeout = self.VNF_PACKAGE_DELETE_TIMEOUT
start_time = int(time.time())
while True:
resp, body = http_client.do_request(show_url, "GET")
if (404 == resp.status_code):
return resp, body
if (int(time.time()) - start_time) > timeout:
raise Exception("Failed to delete package")
time.sleep(1)
def _disable_operational_state(self, package_uuid, http_client):
update_req_body = jsonutils.dumps({
"operationalState": "DISABLED"})
resp, _ = http_client.do_request(
'{base_path}/{id}'.format(id=package_uuid,
base_path=self.base_url),
"PATCH", content_type='application/json',
body=update_req_body)
self.assertEqual(200, resp.status_code)
def assert_vnf_package_usage_state(
self,
vnf_package_info,
expected_usage_state=fields.PackageUsageStateType.IN_USE):
self.assertEqual(
expected_usage_state,
vnf_package_info['usageState'])
def assert_create_vnf(self, resp, vnf_instance, vnf_pkg_id,
tacker_client, fake_server_manager):
super().assert_create_vnf(resp, vnf_instance,
fake_server_manager)
resp, vnf_pkg_info = vnflcm_base._show_vnf_package(
tacker_client, vnf_pkg_id)
self.assert_vnf_package_usage_state(vnf_pkg_info)
def _vnf_instance_wait_until_fail_detected(self, id,
instantiation_state=fields.VnfInstanceState.NOT_INSTANTIATED,
timeout=vnflcm_base.VNF_INSTANTIATE_ERROR_WAIT):
time.sleep(timeout)
_, body = self._show_vnf_instance(id)
if body['instantiationState'] != instantiation_state:
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})
def test_subscription_functionality(self):
"""Test subscription operations with member role users.
In this test case, we do following steps.
Note: User A belongs to Tenant 1(t1).
User B belongs to Tenant 2(t2).
- Create subscription.
- User A registers Subscription A(Notification Server A).
- User B registers Subscription B(Notification Server B).
- Show Subscription
- User A only gets information about Subscription A.
- User B only gets information about Subscription B.
- List Subscription
- User A gets subscription list and confirms only
Subscription A is output.
- User B gets subscription list and confirms only
Subscription B is output.
- Delete Subscription
- User A deletes Subscription A.
- User B deletes Subscription B.
TODO(manpreetk): Only positive test cases are validated in
Y-release.
Negative test cases
- User A fails to delete Subscription B.
- User B fails to delete Subscription A.
Validation of negative test cases would require design changes
in Fake NFVO server, which could be implemented in the upcoming
cycle.
"""
# Create subscription
# User A registers Subscription A.
callback_url = os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)
req_body = fake_vnflcm.Subscription.make_create_request_body(
'http://localhost:{}{}'.format(
base.FAKE_SERVER_MANAGER_T1.SERVER_PORT_T1,
callback_url))
resp_t1, resp_body_t1 = self._register_subscription(req_body,
self.http_client_tenant1)
self.assertEqual(201, resp_t1.status_code)
self.assert_http_header_location_for_subscription(
resp_t1.headers)
self.assert_notification_get(callback_url,
base.FAKE_SERVER_MANAGER_T1)
subscription_id_t1 = resp_body_t1.get('id')
# User B registers Subscription B
callback_url = os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)
req_body_t2 = fake_vnflcm.Subscription.make_create_request_body(
'http://localhost:{}{}'.format(
base.FAKE_SERVER_MANAGER_T2.SERVER_PORT_T2,
callback_url))
resp_t2, resp_body_t2 = self._register_subscription(
req_body_t2, self.http_client_tenant2)
self.assertEqual(201, resp_t2.status_code)
self.assert_http_header_location_for_subscription(
resp_t2.headers)
self.assert_notification_get(callback_url,
base.FAKE_SERVER_MANAGER_T2)
subscription_id_t2 = resp_body_t2.get('id')
# Show Subscription
# User A gets information for Subscription A
resp_t1, resp_body_show_t1 = self._wait_show_subscription(
subscription_id_t1, self.tacker_client_t1)
self.assert_subscription_show(resp_t1, resp_body_show_t1)
# User B gets information for Subscription B
resp_t2, resp_body_show_t2 = self._wait_show_subscription(
subscription_id_t2, self.tacker_client_t2)
self.assert_subscription_show(resp_t2, resp_body_show_t2)
# List Subscription
# User A gets subscription list
resp, _ = self._list_subscription(self.tacker_client_t1)
self.assertEqual(200, resp_t1.status_code)
# Confirm subscription A
filter_expr = {
'filter': "filter=(eq,id,{})".format(
resp_body_show_t1.get('id'))}
resp, subscription_body_t1 = self._list_subscription_filter(
self.http_client_tenant1,
params=filter_expr)
self.assertEqual(200, resp.status_code)
self.assertEqual(1, len(subscription_body_t1))
# User B gets subscription list
resp_t2, _ = self._list_subscription(
self.tacker_client_t2)
self.assertEqual(200, resp_t2.status_code)
# Confirm subscription B
filter_expr = {
'filter': "filter=(eq,id,{})".format(
resp_body_show_t2.get('id'))}
resp, subscription_body_t2 = self._list_subscription_filter(
self.http_client_tenant2,
params=filter_expr)
self.assertEqual(200, resp.status_code)
self.assertEqual(1, len(subscription_body_t2))
# Delete subscription
# User A deletes Subscription A
self.addCleanup(self._delete_subscription,
subscription_id_t1, self.tacker_client_t1)
# User B deletes Subscription B
self.addCleanup(self._delete_subscription,
subscription_id_t2, self.tacker_client_t2)
def test_vnf_package_functionality(self):
"""Test VNF package operations with member role users.
In this test case, we do following steps.
Note: User A belongs to Tenant 1.
User B belongs to Tenant 2.
- Create and Upload VNF Package
- User A creates VNF Package A.
- User A uploads VNF Package A.
- User B creates VNF Package B.
- User B uploads VNF Package B.
- List VNF Package
- User A gets VNF package list and confirms only
VNF Package A is output.
- User B gets VNF package list and confirms only
VNF Package B is output.
- Show VNF Package
- User A only gets information about VNF Package A.
- User B only gets information about VNF Package B.
- Delete VNF Package
- User A deletes VNF Package A.
- User B deletes VNF Package B.
TODO(manpreetk): Only positive test cases are validated in
Y-release.
Negative test cases
- User A fails to delete VNF Package B.
- User B fails to delete VNF Package A.
Validation of negative test cases would require design changes
in Fake NFVO server, which could be implemented in the upcoming
cycle.
"""
# Create and Upload VNF Package
# User A creates VNF Package A
sample_name = 'mt_functional1'
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)
# User A uploads VNF Package A
vnf_pkg_id, vnfd_id = vnflcm_base._create_and_upload_vnf_package(
self.tacker_client_t1, user_defined_data={
"key": sample_name}, temp_csar_path=tempname)
# User B creates VNF Package B
sample_name_t2 = 'mt_functional1'
csar_package_path_t2 = os.path.abspath(
os.path.join(
os.path.dirname(__file__),
"../../../etc/samples/etsi/nfv",
sample_name_t2))
tempname_t2, _ = vnflcm_base._create_csar_with_unique_vnfd_id(
csar_package_path_t2)
# User B uploads VNF Package B
vnf_pkg_id2, vnfd_id2 = vnflcm_base._create_and_upload_vnf_package(
self.tacker_client_t2, user_defined_data={
"key": sample_name_t2}, temp_csar_path=tempname_t2)
# List VNF Package
# User A gets VNF package list and confirms only VNF Package A
# is output.
resp, body = self.http_client_tenant1.do_request(
self.base_url, "GET")
self.assertEqual(200, resp.status_code)
package_id = [obj['id'] for obj in body]
self.assertIn(vnf_pkg_id, package_id)
# User B gets VNF package list and confirms only VNF Package B
# is output.
resp_t2, body_t2 = self.http_client_tenant2.do_request(
self.base_url, "GET")
self.assertEqual(200, resp_t2.status_code)
package_id = [obj['id'] for obj in body_t2]
self.assertIn(vnf_pkg_id2, package_id)
# Show VNF Package
# User A only gets information about VNF Package A
show_url = os.path.join(self.base_url, vnf_pkg_id)
resp, body = self.http_client_tenant1.do_request(
show_url, "GET")
self.assertEqual(200, resp.status_code)
# User B only gets information about VNF Package B
show_url_t2 = os.path.join(self.base_url, vnf_pkg_id2)
resp_t2, body_t2 = self.http_client_tenant2.do_request(
show_url_t2, "GET")
self.assertEqual(200, resp_t2.status_code)
# Delete VNF Package
# User A deletes VNF Package A
self._disable_operational_state(vnf_pkg_id,
self.http_client_tenant1)
self._delete_vnf_package(vnf_pkg_id, self.http_client_tenant1)
self._wait_for_delete(vnf_pkg_id, self.http_client_tenant1)
# User B deletes VNF Package B
self._disable_operational_state(vnf_pkg_id2,
self.http_client_tenant2)
self._delete_vnf_package(vnf_pkg_id2, self.http_client_tenant2)
self._wait_for_delete(vnf_pkg_id2, self.http_client_tenant2)
def test_vnf_instantiation_by_vim_of_different_tenant_and_role(self):
"""Test VNF instantiation by VIM of differnt tenant.
In this test case, we do following steps.
Note: User A is an admin role user belongs to Tenant 1.
User B is a non-admin role user belongs to Tenant 2.
- Create VNF Instance
- User B creates VNF Instance B using VNF Package B.
- Instantiate VNF
- User A fails to instantiates VNF Instance B, both
VNF and VIM belong to different tenant.
"""
# Pre-Setting
# User B registers Subscription B.
callback_url = os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)
req_body_t2 = fake_vnflcm.Subscription.make_create_request_body(
'http://localhost:{}{}'.format(
base.FAKE_SERVER_MANAGER_T2.SERVER_PORT_T2,
callback_url))
resp_t2, resp_body_t2 = self._register_subscription(
req_body_t2, self.http_client_tenant2)
self.assertEqual(201, resp_t2.status_code)
self.assert_http_header_location_for_subscription(
resp_t2.headers)
self.assert_notification_get(callback_url,
base.FAKE_SERVER_MANAGER_T2)
subscription_id_t2 = resp_body_t2.get('id')
self.addCleanup(
self._delete_subscription,
subscription_id_t2,
self.tacker_client_t2)
# Create and Upload VNF Package
# User B creates VNF Package B
sample_name_t2 = 'mt_functional1'
csar_package_path_t2 = os.path.abspath(
os.path.join(
os.path.dirname(__file__),
"../../../etc/samples/etsi/nfv",
sample_name_t2))
tempname_t2, _ = vnflcm_base._create_csar_with_unique_vnfd_id(
csar_package_path_t2)
# User B uploads VNF Package B
vnf_pkg_id, vnfd_id = vnflcm_base._create_and_upload_vnf_package(
self.tacker_client_t2, user_defined_data={
"key": sample_name_t2}, temp_csar_path=tempname_t2)
# Post Setting: Reserve deleting VNF package.
self.addCleanup(vnflcm_base._delete_vnf_package,
self.tacker_client_t2, vnf_pkg_id)
# Create VNF Instance
# User B creates VNF Instance B using VNF Package B
resp_t2, vnf_instance_t2 = self._create_vnf_instance_from_body(
fake_vnflcm.VnfInstances.make_create_request_body(vnfd_id),
self.http_client_tenant2)
vnf_instance_id_t2 = vnf_instance_t2.get('id')
self._wait_lcm_done(vnf_instance_id=vnf_instance_id_t2,
fake_server_manager=base.FAKE_SERVER_MANAGER_T2)
self.assert_create_vnf(resp_t2, vnf_instance_t2,
vnf_pkg_id,
self.tacker_client_t2,
base.FAKE_SERVER_MANAGER_T2)
# Instantiate VNF instance
# User A fails to instantiate VNF Instance B as both VIM and VNF
# belongs to differernt tenants
request_body = fake_vnflcm.VnfInstances.make_inst_request_body(
'nfv_user',
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_t2,
request_body, self.http_client)
self.assertEqual(202, resp.status_code)
self._vnf_instance_wait_until_fail_detected(vnf_instance_id_t2)

View File

@ -88,6 +88,7 @@ auth_url: "${VIMC_ENDPOINT}"
username: "${VIMC_OS_USER}"
password: "${VIMC_OS_PASSWORD}"
project_name: "${VIMC_PROJ}"
domain_name: "${VIMC_OS_PROJ_DOMAIN}"
project_domain_name: "${VIMC_OS_PROJ_DOMAIN}"
user_domain_name: "${VIMC_OS_USER_DOMAIN}"
cert_verify: "${_cert_verify}"

View File

@ -61,6 +61,12 @@ setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_v2 run --slowest --concurrency 1 {posargs}
[testenv:dsvm-functional-sol-multi-tenant]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_multi_tenant run --slowest --concurrency 1 {posargs}
[testenv:debug]
commands = oslo_debug_helper {posargs}