Merge "FT of instantiate/scale's ErrorHandling"

This commit is contained in:
Zuul 2022-03-10 07:09:31 +00:00 committed by Gerrit Code Review
commit 17b03eb78f
29 changed files with 2473 additions and 1705 deletions

View File

@ -273,12 +273,6 @@
Multinodes job for SOL V2 devstack-based functional tests
host-vars:
controller-tacker:
devstack_local_conf:
post-config:
# Skip notification endpoint testing in create subscription
$TACKER_CONF:
v2_nfvo:
test_callback_uri: False
tox_envlist: dsvm-functional-sol-v2
- job:

View File

@ -28,10 +28,13 @@ from tempest.lib import base
from tacker.sol_refactored.common import http_client
from tacker.sol_refactored.infra_drivers.openstack import heat_utils
from tacker.sol_refactored import objects
from tacker.tests.functional.common.fake_server import FakeServerManager
from tacker.tests.functional.sol_v2 import utils
from tacker.tests import utils as base_utils
from tacker import version
FAKE_SERVER_MANAGER = FakeServerManager.get_instance()
MOCK_NOTIFY_CALLBACK_URL = '/notification/callback'
LOG = logging.getLogger(__name__)
@ -45,6 +48,9 @@ class BaseSolV2Test(base.BaseTestCase):
def setUpClass(cls):
super(BaseSolV2Test, cls).setUpClass()
FAKE_SERVER_MANAGER.prepare_http_server()
FAKE_SERVER_MANAGER.start_server()
cfg.CONF(args=['--config-file', '/etc/tacker/tacker.conf'],
project='tacker',
version='%%prog %s' % version.version_info.release_string())
@ -70,6 +76,11 @@ class BaseSolV2Test(base.BaseTestCase):
service_type='compute')
cls.heat_client = heat_utils.HeatClient(vim_info)
@classmethod
def tearDownClass(cls):
super(BaseSolV2Test, cls).tearDownClass()
FAKE_SERVER_MANAGER.stop_server()
@classmethod
def get_vim_info(cls):
vim_params = yaml.safe_load(base_utils.read_file('local-vim.yaml'))
@ -143,6 +154,16 @@ class BaseSolV2Test(base.BaseTestCase):
cls.tacker_client.do_request(path, "DELETE")
def setUp(self):
super().setUp()
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)
FAKE_SERVER_MANAGER.set_callback('GET', callback_url, status_code=204)
def get_vnf_package(self, pkg_id):
path = "/vnfpkgm/v1/vnf_packages/{}".format(pkg_id)
resp, body = self.tacker_client.do_request(path, "GET")
@ -287,6 +308,11 @@ class BaseSolV2Test(base.BaseTestCase):
return self.tacker_client.do_request(
path, "POST", body=req_body, version="2.0.0")
def scale_vnf_instance(self, inst_id, req_body):
path = f"/vnflcm/v2/vnf_instances/{inst_id}/scale"
return self.tacker_client.do_request(
path, "POST", body=req_body, version="2.0.0")
def terminate_vnf_instance(self, inst_id, req_body):
path = "/vnflcm/v2/vnf_instances/{}/terminate".format(inst_id)
return self.tacker_client.do_request(
@ -326,6 +352,22 @@ class BaseSolV2Test(base.BaseTestCase):
else: # ROLLED_BACK
raise Exception("Operation failed. state: %s" % state)
def wait_lcmocc_rolled_back(self, lcmocc_id):
# NOTE: It is not necessary to set timeout because the operation
# itself set timeout and the state will become 'FAILED_TEMP'.
path = f"/vnflcm/v2/vnf_lcm_op_occs/{lcmocc_id}"
while True:
time.sleep(5)
_, body = self.tacker_client.do_request(
path, "GET", expected_status=[200], version="2.0.0")
state = body['operationState']
if state == 'ROLLED_BACK':
return
if state in ['ROLLING_BACK']:
continue
raise Exception(f"Operation failed. state: {state}")
def show_lcmocc(self, lcmocc_id):
path = "/vnflcm/v2/vnf_lcm_op_occs/{}".format(lcmocc_id)
return self.tacker_client.do_request(
@ -338,6 +380,21 @@ class BaseSolV2Test(base.BaseTestCase):
return self.tacker_client.do_request(
path, "GET", version="2.0.0")
def rollback_lcmocc(self, lcmocc_id):
path = f"/vnflcm/v2/vnf_lcm_op_occs/{lcmocc_id}/rollback"
return self.tacker_client.do_request(
path, "POST", version="2.0.0")
def retry_lcmocc(self, lcmocc_id):
path = f"/vnflcm/v2/vnf_lcm_op_occs/{lcmocc_id}/retry"
return self.tacker_client.do_request(
path, "POST", version="2.0.0")
def fail_lcmocc(self, lcmocc_id):
path = f"/vnflcm/v2/vnf_lcm_op_occs/{lcmocc_id}/fail"
return self.tacker_client.do_request(
path, "POST", version="2.0.0")
def create_subscription(self, req_body):
path = "/vnflcm/v2/subscriptions"
return self.tacker_client.do_request(
@ -396,3 +453,11 @@ class BaseSolV2Test(base.BaseTestCase):
for attr in expected_attrs:
if attr not in body:
raise Exception("Expected attribute doesn't exist: %s" % attr)
def assert_notification_get(self, callback_url):
notify_mock_responses = FAKE_SERVER_MANAGER.get_history(
callback_url)
FAKE_SERVER_MANAGER.clear_history(
callback_url)
self.assertEqual(1, len(notify_mock_responses))
self.assertEqual(204, notify_mock_responses[0].status_code)

View File

@ -16,7 +16,17 @@
from oslo_utils import uuidutils
def sub1_create():
def sub_create_min(callback_uri):
# Omit except for required attributes
# NOTE: Only the following cardinality attributes are set.
# - 1
# - 1..N (1)
return {
"callbackUri": callback_uri
}
def sub_create_max(callback_uri):
# All attributes are set.
# NOTE: All of the following cardinality attributes are set.
# In addition, 0..N or 1..N attributes are set to 2 or more.
@ -110,19 +120,29 @@ def sub1_create():
]
},
"notificationTypes": [
"VnfLcmOperationOccurrenceNotification",
"VnfIdentifierCreationNotification",
"VnfLcmOperationOccurrenceNotification"
],
"operationTypes": [
"INSTANTIATE",
"TERMINATE"
"SCALE",
"TERMINATE",
"HEAL",
"MODIFY_INFO",
"CHANGE_EXT_CONN"
],
"operationStates": [
"COMPLETED",
"FAILED"
"FAILED",
"FAILED_TEMP",
"PROCESSING",
"ROLLING_BACK",
"ROLLED_BACK",
"STARTING"
]
},
"callbackUri": "http://127.0.0.1/",
"callbackUri": callback_uri,
"authentication": {
"authType": [
"BASIC"
@ -137,17 +157,41 @@ def sub1_create():
}
def sub2_create():
# Omit except for required attributes
# NOTE: Only the following cardinality attributes are set.
# - 1
# - 1..N (1)
return {
"callbackUri": "http://127.0.0.1/"
}
def sample1_create(vnfd_id):
# All attributes are set.
# NOTE: All of the following cardinality attributes are set.
# In addition, 0..N or 1..N attributes are set to 2 or more.
# - 0..1 (1)
# - 0..N (2 or more)
# - 1
# - 1..N (2 or more)
return create_vnf_max(vnfd_id)
def sample1_terminate():
# All attributes are set.
# NOTE: All of the following cardinality attributes are set.
# In addition, 0..N or 1..N attributes are set to 2 or more.
# - 0..1 (1)
# - 0..N (2 or more)
# - 1
# - 1..N (2 or more)
return terminate_vnf_max()
def sample1_instantiate(net_ids, subnets, ports, auth_url):
# All attributes are set.
# NOTE: All of the following cardinality attributes are set.
# In addition, 0..N or 1..N attributes are set to 2 or more.
# - 0..1 (1)
# - 0..N (2 or more)
# - 1
# - 1..N (2 or more)
return instantiate_vnf_max(net_ids, subnets, ports, auth_url)
def create_vnf_max(vnfd_id):
# All attributes are set.
# NOTE: All of the following cardinality attributes are set.
# In addition, 0..N or 1..N attributes are set to 2 or more.
@ -157,13 +201,13 @@ def sample1_create(vnfd_id):
# - 1..N (2 or more)
return {
"vnfdId": vnfd_id,
"vnfInstanceName": "sample1",
"vnfInstanceDescription": "test sample1",
"vnfInstanceName": "sample",
"vnfInstanceDescription": "test sample",
"metadata": {"dummy-key": "dummy-val"}
}
def sample1_terminate():
def terminate_vnf_max():
# All attributes are set.
# NOTE: All of the following cardinality attributes are set.
# In addition, 0..N or 1..N attributes are set to 2 or more.
@ -178,7 +222,7 @@ def sample1_terminate():
}
def sample1_instantiate(net_ids, subnets, ports, auth_url):
def instantiate_vnf_max(net_ids, subnets, ports, auth_url):
# All attributes are set.
# NOTE: All of the following cardinality attributes are set.
# In addition, 0..N or 1..N attributes are set to 2 or more.
@ -409,7 +453,7 @@ def sample1_instantiate(net_ids, subnets, ports, auth_url):
},
"extra": {"dummy-key": "dummy-val"}
}
addParams = {
add_params = {
"lcm-operation-user-data": "./UserData/userdata.py",
"lcm-operation-user-data-class": "UserData",
"nfv": {"CP": {"VDU2_CP1-2": {"port": ports['VDU2_CP1-2']}}}
@ -431,12 +475,37 @@ def sample1_instantiate(net_ids, subnets, ports, auth_url):
"vim2": vim_2
},
"localizationLanguage": "ja",
"additionalParams": addParams,
"extensions": {"dummy-key": "dummy-val"}
"additionalParams": add_params,
"extensions": {"dummy-key": "dummy-val"},
"vnfConfigurableProperties": {"dummy-key": "dummy-val"}
}
def sample2_create(vnfd_id):
# Omit except for required attributes
# NOTE: Only the following cardinality attributes are set.
# - 1
# - 1..N (1)
return create_vnf_min(vnfd_id)
def sample2_terminate():
# Omit except for required attributes
# NOTE: Only the following cardinality attributes are set.
# - 1
# - 1..N (1)
return terminate_vnf_min()
def sample2_instantiate():
# Omit except for required attributes
# NOTE: Only the following cardinality attributes are set.
# - 1
# - 1..N (1)
return instantiate_vnf_min()
def create_vnf_min(vnfd_id):
# Omit except for required attributes
# NOTE: Only the following cardinality attributes are set.
# - 1
@ -446,7 +515,7 @@ def sample2_create(vnfd_id):
}
def sample2_terminate():
def terminate_vnf_min():
# Omit except for required attributes
# NOTE: Only the following cardinality attributes are set.
# - 1
@ -456,7 +525,7 @@ def sample2_terminate():
}
def sample2_instantiate():
def instantiate_vnf_min():
# Omit except for required attributes
# NOTE: Only the following cardinality attributes are set.
# - 1
@ -464,3 +533,19 @@ def sample2_instantiate():
return {
"flavourId": "simple"
}
def scaleout_vnf_max():
# All attributes are set.
# NOTE: All of the following cardinality attributes are set.
# In addition, 0..N or 1..N attributes are set to 2 or more.
# - 0..1 (1)
# - 0..N (2 or more)
# - 1
# - 1..N (2 or more)
return {
"type": "SCALE_OUT",
"aspectId": "VDU1_scale",
"numberOfSteps": 1,
"additionalParams": {"dummy-key": "dummy-value"}
}

View File

@ -0,0 +1,30 @@
heat_template_version: 2013-05-23
description: 'VDU1 HOT for Sample VNF'
parameters:
flavor:
type: string
image:
type: string
net5:
type: string
affinity:
type: string
resources:
VDU1:
type: OS::Nova::Server
properties:
flavor: { get_param: flavor }
image: { get_param: image }
name: VDU1
networks:
- port:
get_resource: VDU1_CP1
scheduler_hints:
group: {get_param: affinity }
VDU1_CP1:
type: OS::Neutron::Port
properties:
network: errornetwork

View File

@ -0,0 +1,61 @@
heat_template_version: 2013-05-23
description: 'Simple Base HOT for Sample VNF'
parameters:
nfv:
type: json
resources:
VDU1_scale_group:
type: OS::Heat::AutoScalingGroup
properties:
min_size: 1
max_size: 3
desired_capacity: { get_param: [ nfv, VDU, VDU1, desired_capacity ] }
resource:
type: VDU1.yaml
properties:
flavor: { get_param: [ nfv, VDU, VDU1, computeFlavourId ] }
image: { get_param: [ nfv, VDU, VDU1, vcImageId ] }
net5: { get_resource: internalVL3 }
affinity: { get_resource: nfvi_node_affinity }
# NOTE: Resource definition of OS::Heat::ScalingPolicy is omitted.
# It is not used by v2 scale implementation unlike v1.
VDU2:
type: OS::Nova::Server
properties:
flavor: { get_param: [ nfv, VDU, VDU2, computeFlavourId ] }
name: VDU2
image: { get_param: [ nfv, VDU, VDU2, vcImageId] }
networks:
- port:
get_resource: VDU2_CP1
scheduler_hints:
group: {get_resource: nfvi_node_affinity }
VDU2_CP1:
type: OS::Neutron::Port
properties:
network: { get_resource: internalVL3 }
internalVL3:
type: OS::Neutron::Net
internalVL3_subnet:
type: OS::Neutron::Subnet
properties:
ip_version: 4
network:
get_resource: internalVL3
cidr: 192.168.5.0/24
nfvi_node_affinity:
type: OS::Nova::ServerGroup
properties:
name: nfvi_node_affinity
policies: [ 'affinity' ]
outputs: {}

View File

@ -0,0 +1,258 @@
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
- v2_sample2_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
node_templates:
VNF:
type: company.provider.VNF
properties:
flavour_description: A simple flavour
interfaces:
Vnflcm:
instantiate_start:
implementation: sample-script
instantiate_end:
implementation: sample-script
terminate_start:
implementation: sample-script
terminate_end:
implementation: sample-script
artifacts:
sample-script:
description: Sample script
type: tosca.artifacts.Implementation.Python
file: ../Scripts/sample_script.py
VDU1:
type: tosca.nodes.nfv.Vdu.Compute
properties:
name: VDU1
description: VDU1 compute node
vdu_profile:
min_number_of_instances: 1
max_number_of_instances: 3
sw_image_data:
name: 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
VDU2:
type: tosca.nodes.nfv.Vdu.Compute
properties:
name: VDU2
description: VDU2 compute node
vdu_profile:
min_number_of_instances: 1
max_number_of_instances: 1
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
VDU1_CP1:
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: 4
requirements:
- virtual_binding: VDU2
- virtual_link: internalVL3
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: 192.168.5.0/24
groups:
affinityOrAntiAffinityGroup1:
type: tosca.groups.nfv.PlacementGroup
members: [ VDU1, VDU2 ]
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: 1
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: 1
instantiation_level_2:
number_of_instances: 1
targets: [ VDU2 ]
- 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_affinity_group:
type: tosca.policies.nfv.AffinityRule
targets: [ affinityOrAntiAffinityGroup1 ]
properties:
scope: nfvi_node

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
- v2_sample2_types.yaml
- v2_sample2_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: "flavour"
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,67 @@
# Copyright (C) 2022 Nippon Telegraph and Telephone Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
import pickle
import sys
class SampleScript(object):
def __init__(self, req, inst, grant_req, grant, csar_dir):
self.req = req
self.inst = inst
self.grant_req = grant_req
self.grant = grant
self.csar_dir = csar_dir
def instantiate_start(self):
pass
def instantiate_end(self):
pass
def terminate_start(self):
pass
def terminate_end(self):
pass
def main():
script_dict = pickle.load(sys.stdin.buffer)
operation = script_dict['operation']
req = script_dict['request']
inst = script_dict['vnf_instance']
grant_req = script_dict['grant_request']
grant = script_dict['grant_response']
csar_dir = script_dict['tmp_csar_dir']
script = SampleScript(req, inst, grant_req, grant, csar_dir)
try:
getattr(script, operation)()
except AttributeError:
raise Exception("{} is not included in the script.".format(operation))
if __name__ == "__main__":
try:
main()
os._exit(0)
except Exception as ex:
sys.stderr.write(str(ex))
sys.stderr.flush()
os._exit(1)

View File

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

View File

@ -0,0 +1,50 @@
# Copyright (C) 2022 Nippon Telegraph and Telephone Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import json
import os
import shutil
import tempfile
from oslo_utils import uuidutils
from tacker.tests.functional.sol_v2 import paramgen
from tacker.tests.functional.sol_v2 import utils
zip_file_name = os.path.basename(os.path.abspath(".")) + '.zip'
tmp_dir = tempfile.mkdtemp()
vnfd_id = uuidutils.generate_uuid()
utils.make_zip(".", tmp_dir, vnfd_id)
shutil.copy(os.path.join(tmp_dir, zip_file_name), ".")
shutil.rmtree(tmp_dir)
subscription_req = paramgen.sub_create_min("http://127.0.0.1/")
create_req = paramgen.create_vnf_min(vnfd_id)
terminate_req = paramgen.terminate_vnf_min()
instantiate_req = paramgen.instantiate_vnf_min()
with open("subscription_req", "w", encoding='utf-8') as f:
f.write(json.dumps(subscription_req, indent=2))
with open("create_req", "w", encoding='utf-8') as f:
f.write(json.dumps(create_req, indent=2))
with open("terminate_req", "w", encoding='utf-8') as f:
f.write(json.dumps(terminate_req, indent=2))
with open("instantiate_req", "w", encoding='utf-8') as f:
f.write(json.dumps(instantiate_req, indent=2))

View File

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

View File

@ -0,0 +1,91 @@
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
subnet1:
type: string
subnet2:
type: string
resources:
VDU1:
type: OS::Nova::Server
properties:
flavor: { get_param: flavor }
name: VDU1
block_device_mapping_v2: [{"volume_id": { get_resource: VDU1-VirtualStorage }}]
networks:
- port:
get_resource: VDU1_CP1
- port:
get_resource: VDU1_CP2
# replace the following line to Port ID when extmanagedVLs' Ports are specified in instantiatevnfrequest
- port:
get_resource: VDU1_CP3
- port:
get_resource: VDU1_CP4
- port:
get_resource: VDU1_CP5
availability_zone: { get_param: zone }
VDU1-VirtualStorage:
type: OS::Cinder::Volume
properties:
image: { get_param: image }
size: 1
volume_type: { get_resource: multi }
multi:
type: OS::Cinder::VolumeType
properties:
name: multivolume
metadata: { multiattach: "<is> True" }
# extVL without FixedIP or with numDynamicAddresses
VDU1_CP1:
type: OS::Neutron::Port
properties:
network: { get_param: net1 }
fixed_ips:
- subnet: { get_param: subnet1}
# extVL with numDynamicAddresses and subnet
VDU1_CP2:
type: OS::Neutron::Port
properties:
network: { get_param: net2 }
fixed_ips:
- subnet: { get_param: subnet2}
# delete the following line when extmanagedVLs' Ports are specified in instantiatevnfrequest
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,120 @@
heat_template_version: 2013-05-23
description: 'Simple Base HOT for Sample VNF'
parameters:
nfv:
type: json
resources:
VDU1_scale_group:
type: OS::Heat::AutoScalingGroup
properties:
min_size: 1
max_size: 3
desired_capacity: { get_param: [ nfv, VDU, VDU1, desired_capacity ] }
resource:
type: VDU1.yaml
properties:
flavor: { get_param: [ nfv, VDU, VDU1, computeFlavourId ] }
image: { get_param: [ nfv, VDU, VDU1-VirtualStorage, vcImageId ] }
zone: { get_param: [ nfv, VDU, VDU1, locationConstraints] }
net1: { get_param: [ nfv, CP, VDU1_CP1, network] }
net2: { get_param: [ nfv, CP, VDU1_CP2, network ] }
subnet1: { get_param: [nfv, CP, VDU1_CP1, fixed_ips, 0, subnet ]}
subnet2: { get_param: [nfv, CP, VDU1_CP2, fixed_ips, 0, subnet ]}
net3: { get_resource: internalVL1 }
net4: { get_resource: internalVL2 }
net5: { get_resource: internalVL3 }
# NOTE: Resource definition of OS::Heat::ScalingPolicy is omitted.
# It is not used by v2 scale implementation unlike v1.
VDU2:
type: OS::Nova::Server
properties:
flavor: { get_param: [ nfv, VDU, VDU2, computeFlavourId ] }
name: VDU2
availability_zone: { get_param: [ nfv, VDU, VDU2, locationConstraints ] }
block_device_mapping_v2: [{"volume_id": { get_resource: VDU2-VirtualStorage }}]
networks:
- port: { get_param: [ nfv, CP, VDU2_CP1-1, port ] }
- port: { get_param: [ nfv, CP, VDU2_CP1-2, port ] }
- port:
get_resource: VDU2_CP2
- port:
get_resource: VDU2_CP3
- port:
get_resource: VDU2_CP4
- port:
get_resource: VDU2_CP5
VDU2-VirtualStorage:
type: OS::Cinder::Volume
properties:
image: { get_param: [ nfv, VDU, VDU2-VirtualStorage, vcImageId] }
size: 1
volume_type: { get_resource: multi }
multi:
type: OS::Cinder::VolumeType
properties:
name: VDU2-multi
metadata: { multiattach: "<is> True" }
# extVL with FixedIP and Subnet
VDU2_CP2:
type: OS::Neutron::Port
properties:
network: { get_param: [ nfv, CP, VDU2_CP2, network ] }
fixed_ips:
- ip_address: { get_param: [nfv, CP, VDU2_CP2, fixed_ips, 0, ip_address]}
subnet: { get_param: [nfv, CP, VDU2_CP2, fixed_ips, 0, subnet]}
- subnet: { get_param: [nfv, CP, VDU2_CP2, fixed_ips, 1, subnet]}
VDU2_CP3:
type: OS::Neutron::Port
properties:
# replace the following line to VL's ID when extmanagedVLs are specified in instantiatevnfrequest
network: { get_resource: internalVL1 }
VDU2_CP4:
type: OS::Neutron::Port
properties:
# replace the following line to VL's ID when extmanagedVLs are specified in instantiatevnfrequest
network: { get_resource: internalVL2 }
VDU2_CP5:
type: OS::Neutron::Port
properties:
# replace the following line to VL's ID when extmanagedVLs are specified in instantiatevnfrequest
network: { get_resource: internalVL3 }
# delete the following lines when extmanagedVLs are specified in instantiatevnfrequest
internalVL1:
type: OS::Neutron::Net
internalVL2:
type: OS::Neutron::Net
internalVL3:
type: OS::Neutron::Net
internalVL1_subnet:
type: OS::Neutron::Subnet
properties:
ip_version: 4
network:
get_resource: internalVL1
cidr: 192.168.3.0/24
internalVL2_subnet:
type: OS::Neutron::Subnet
properties:
ip_version: 4
network:
get_resource: internalVL2
cidr: 192.168.4.0/24
internalVL3_subnet:
type: OS::Neutron::Subnet
properties:
ip_version: 4
network:
get_resource: internalVL3
cidr: 192.168.5.0/24
outputs: {}

View File

@ -0,0 +1,428 @@
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
- v2_sample1_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-1, virtual_link ]
virtual_link_external1_3: [ VDU2_CP1-2, 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: VDU1-VirtualStorage
VDU2:
type: tosca.nodes.nfv.Vdu.Compute
properties:
name: VDU2
description: VDU2 compute node
vdu_profile:
min_number_of_instances: 1
max_number_of_instances: 1
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: VDU2-VirtualStorage
VDU1-VirtualStorage:
type: tosca.nodes.nfv.Vdu.VirtualBlockStorage
properties:
virtual_block_storage_data:
size_of_storage: 1 GB
rdma_enabled: true
sw_image_data:
name: VDU1-VirtualStorage-image
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
artifacts:
sw_image:
type: tosca.artifacts.nfv.SwImage
file: ../Files/images/cirros-0.5.2-x86_64-disk.img
VDU2-VirtualStorage:
type: tosca.nodes.nfv.Vdu.VirtualBlockStorage
properties:
virtual_block_storage_data:
size_of_storage: 1 GB
rdma_enabled: true
sw_image_data:
name: VDU2-VirtualStorage-image
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
artifacts:
sw_image:
type: tosca.artifacts.nfv.SwImage
file: ../Files/images/cirros-0.5.2-x86_64-disk.img
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-1:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 0
requirements:
- virtual_binding: VDU2
VDU2_CP1-2:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 1
requirements:
- virtual_binding: VDU2
VDU2_CP2:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 2
requirements:
- virtual_binding: VDU2
VDU2_CP3:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 3
requirements:
- virtual_binding: VDU2
- virtual_link: internalVL1
VDU2_CP4:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 4
requirements:
- virtual_binding: VDU2
- virtual_link: internalVL2
VDU2_CP5:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 5
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: 192.168.3.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: 192.168.4.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: 192.168.5.0/24
groups:
affinityOrAntiAffinityGroup1:
type: tosca.groups.nfv.PlacementGroup
members: [ VDU1, VDU2 ]
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: 1
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: 1
instantiation_level_2:
number_of_instances: 1
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_affinity_group:
type: tosca.policies.nfv.AffinityRule
targets: [ affinityOrAntiAffinityGroup1 ]
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
- v2_sample1_types.yaml
- v2_sample1_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: "flavour"
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,67 @@
# Copyright (C) 2022 Nippon Telegraph and Telephone Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
import pickle
import sys
class SampleScript(object):
def __init__(self, req, inst, grant_req, grant, csar_dir):
self.req = req
self.inst = inst
self.grant_req = grant_req
self.grant = grant
self.csar_dir = csar_dir
def instantiate_start(self):
pass
def instantiate_end(self):
pass
def terminate_start(self):
pass
def terminate_end(self):
pass
def main():
script_dict = pickle.load(sys.stdin.buffer)
operation = script_dict['operation']
req = script_dict['request']
inst = script_dict['vnf_instance']
grant_req = script_dict['grant_request']
grant = script_dict['grant_response']
csar_dir = script_dict['tmp_csar_dir']
script = SampleScript(req, inst, grant_req, grant, csar_dir)
try:
getattr(script, operation)()
except AttributeError:
raise Exception("{} is not included in the script.".format(operation))
if __name__ == "__main__":
try:
main()
os._exit(0)
except Exception as ex:
sys.stderr.write(str(ex))
sys.stderr.flush()
os._exit(1)

View File

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

View File

@ -0,0 +1,124 @@
# Copyright (C) 2022 Nippon Telegraph and Telephone Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import yaml
from tacker.sol_refactored.common import vnf_instance_utils as inst_utils
from tacker.sol_refactored.infra_drivers.openstack import userdata_utils
class UserData(userdata_utils.AbstractUserData):
@staticmethod
def instantiate(req, inst, grant_req, grant, tmp_csar_dir):
def _get_param_port(cp_name, grant, req):
# see grant first then instantiateVnfRequest
vls = grant.get('extVirtualLinks', []) + req.get('extVirtualLinks',
[])
port_ids = []
for vl in vls:
link_port_ids = []
for extcp in vl['extCps']:
if extcp['cpdId'] == cp_name:
link_port_ids = _get_link_port_ids_from_extcp(extcp)
if 'extLinkPorts' not in vl:
continue
for extlp in vl['extLinkPorts']:
if extlp['id'] in link_port_ids:
port_ids.append(extlp['resourceHandle']['resourceId'])
return port_ids
def _get_link_port_ids_from_extcp(extcp):
link_port_ids = []
for cp_conf in extcp['cpConfig'].values():
if 'linkPortId' in cp_conf:
link_port_ids.append(cp_conf['linkPortId'])
return link_port_ids
vnfd = userdata_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
flavour_id = req['flavourId']
hot_dict = vnfd.get_base_hot(flavour_id)
top_hot = hot_dict['template']
nfv_dict = userdata_utils.init_nfv_dict(top_hot)
vdus = nfv_dict.get('VDU', {})
for vdu_name, vdu_value in vdus.items():
if 'computeFlavourId' in vdu_value:
vdu_value['computeFlavourId'] = (
userdata_utils.get_param_flavor(
vdu_name, flavour_id, vnfd, grant))
if 'vcImageId' in vdu_value:
vdu_value['vcImageId'] = userdata_utils.get_param_image(
vdu_name, flavour_id, vnfd, grant)
if 'locationConstraints' in vdu_value:
vdu_value['locationConstraints'] = (
userdata_utils.get_param_zone(
vdu_name, grant_req, grant))
if 'desired_capacity' in vdu_value:
vdu_value['desired_capacity'] = (
userdata_utils.get_param_capacity(
vdu_name, inst, grant_req))
cps = nfv_dict.get('CP', {})
for cp_name, cp_value in cps.items():
if 'network' in cp_value:
cp_value['network'] = userdata_utils.get_param_network(
cp_name, grant, req)
if 'fixed_ips' in cp_value:
ext_fixed_ips = userdata_utils.get_param_fixed_ips(
cp_name, grant, req)
fixed_ips = []
for i in range(len(ext_fixed_ips)):
if i not in cp_value['fixed_ips']:
break
ips_i = cp_value['fixed_ips'][i]
if 'subnet' in ips_i:
ips_i['subnet'] = ext_fixed_ips[i].get('subnet')
if 'ip_address' in ips_i:
ips_i['ip_address'] = ext_fixed_ips[i].get(
'ip_address')
fixed_ips.append(ips_i)
cp_value['fixed_ips'] = fixed_ips
# NOTE: In the case where multiple cpConfigs corresponding
# to a single cpdId are defined, always get the first element
# of cpConfig. This is because, according to the current
# SOL definitions, the key of cpConfig is the ID managed by
# the API consumer, and it is not possible to uniquely determine
# which element of cpConfig should be selected by cpdId.
# See SOL003 v3.3.1 4.4.1.10 Type: VnfExtCpData.
if 'port' in cp_value:
cp_value['port'] = _get_param_port(
cp_name, grant, req).pop()
userdata_utils.apply_ext_managed_vls(top_hot, req, grant)
if 'nfv' in req.get('additionalParams', {}):
nfv_dict = inst_utils.json_merge_patch(nfv_dict,
req['additionalParams']['nfv'])
if 'nfv' in grant.get('additionalParams', {}):
nfv_dict = inst_utils.json_merge_patch(nfv_dict,
grant['additionalParams']['nfv'])
fields = {
'template': yaml.safe_dump(top_hot),
'parameters': {'nfv': nfv_dict},
'files': {}
}
for key, value in hot_dict.get('files', {}).items():
fields['files'][key] = yaml.safe_dump(value)
return fields

View File

@ -0,0 +1,77 @@
# Copyright (C) 2022 Nippon Telegraph and Telephone Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import json
import os
import shutil
import tempfile
from oslo_utils import uuidutils
from tacker.tests.functional.sol_v2 import paramgen
from tacker.tests.functional.sol_v2 import utils
zip_file_name = os.path.basename(os.path.abspath(".")) + '.zip'
tmp_dir = tempfile.mkdtemp()
vnfd_id = uuidutils.generate_uuid()
# tacker/tests/etc...
# /functional/sol_v2/samples/smapleX
image_dir = "../../../../etc/samples/etsi/nfv/common/Files/images/"
image_file = "cirros-0.5.2-x86_64-disk.img"
image_path = os.path.abspath(image_dir + image_file)
utils.make_zip(".", tmp_dir, vnfd_id, image_path)
shutil.move(os.path.join(tmp_dir, zip_file_name), ".")
shutil.rmtree(tmp_dir)
subscription_req = paramgen.sub_create_max("http://127.0.0.1/")
create_req = paramgen.create_vnf_max(vnfd_id)
scaleout_req = paramgen.scaleout_vnf_max()
terminate_req = paramgen.terminate_vnf_max()
print('#####################################################################\n'
'# Run pre.py if an error occurs #\n'
'# - If an error occurs, run the pre.py script in advance #\n'
'# to create the openstack resource required to run this script. #\n'
'# Run post.py when you finish tests #\n'
'# - When you no longer need these openstack resources #\n'
'# after testing, run post.py and delete them. #\n'
'#####################################################################')
net_ids = utils.get_network_ids(['net0', 'net1', 'net_mgmt', 'ft-net0'])
subnet_ids = utils.get_subnet_ids(
['subnet0', 'subnet1', 'ft-ipv4-subnet0', 'ft-ipv6-subnet0'])
port_ids = utils.get_port_ids(['VDU2_CP1-1', 'VDU2_CP1-2'])
instantiate_req = paramgen.instantiate_vnf_max(
net_ids, subnet_ids, port_ids, "http://localhost/identity/v3")
with open("subscription_req", "w", encoding='utf-8') as f:
f.write(json.dumps(subscription_req, indent=2))
with open("create_req", "w", encoding='utf-8') as f:
f.write(json.dumps(create_req, indent=2))
with open("terminate_req", "w", encoding='utf-8') as f:
f.write(json.dumps(terminate_req, indent=2))
with open("scaleout_req", "w", encoding='utf-8') as f:
f.write(json.dumps(scaleout_req, indent=2))
with open("instantiate_req", "w", encoding='utf-8') as f:
f.write(json.dumps(instantiate_req, indent=2))

View File

@ -0,0 +1,20 @@
# Copyright (C) 2022 Nippon Telegraph and Telephone Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from tacker.tests.functional.sol_v2 import utils
utils.delete_network('ft-net0')
# NOTE: subnet is automatically deleted by network deletion
utils.delete_port('VDU2_CP1-1')
utils.delete_port('VDU2_CP1-2')

View File

@ -0,0 +1,21 @@
# Copyright (C) 2022 Nippon Telegraph and Telephone Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from tacker.tests.functional.sol_v2 import utils
utils.create_network('ft-net0')
utils.create_subnet('ft-ipv4-subnet0', 'ft-net0', '100.100.100.0/24', '4')
utils.create_subnet('ft-ipv6-subnet0', 'ft-net0', '1111:2222:3333::/64', '6')
utils.create_port('VDU2_CP1-1', 'net0')
utils.create_port('VDU2_CP1-2', 'net0')

View File

@ -97,9 +97,9 @@ class VnfLcmTest(base_v2.BaseSolV2Test):
- is_all=True
All of the following cardinality attributes are set.
In addition, 0..N or 1..N attributes are set to 2 or more.
0..1 is set to 1.
- 0..1 (1)
- 0..N (2 or more)
- 1
- 1..N (2 or more)
- is_all=False
Omit except for required attributes.
@ -115,14 +115,17 @@ class VnfLcmTest(base_v2.BaseSolV2Test):
- 3. List subscription with attribute-based filtering
- 4. Delete a subscription
"""
# NOTE: Skip notification endpoint testing in subscription creation
# by setting "v2_nfvo.test_callback_uri = False" to 'tacker.conf'
# in '.zuul.yaml'.
# 0. Pre-setting
sub_req = paramgen.sub2_create()
callback_url = os.path.join(base_v2.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)
callback_uri = ('http://localhost:'
f'{base_v2.FAKE_SERVER_MANAGER.SERVER_PORT}'
f'{callback_url}')
sub_req = paramgen.sub_create_min(callback_uri)
if is_all:
sub_req = paramgen.sub1_create()
sub_req = paramgen.sub_create_max(callback_uri)
# 1. Create a new subscription
resp, body = self.create_subscription(sub_req)
@ -162,9 +165,9 @@ class VnfLcmTest(base_v2.BaseSolV2Test):
* About attributes:
All of the following cardinality attributes are set.
In addition, 0..N or 1..N attributes are set to 2 or more.
0..1 is set to 1.
- 0..1 (1)
- 0..N (2 or more)
- 1
- 1..N (2 or more)
* About LCM operations:
@ -240,7 +243,7 @@ class VnfLcmTest(base_v2.BaseSolV2Test):
inst_id = body['id']
# check usageState of VNF Package
usage_state = self.get_vnf_package(self.vnf_pkg_1).get('usageState')
usage_state = self.get_vnf_package(self.vnf_pkg_1)['usageState']
self.assertEqual('IN_USE', usage_state)
# 2. Instantiate a VNF instance
@ -444,7 +447,7 @@ class VnfLcmTest(base_v2.BaseSolV2Test):
self.assertEqual(404, resp.status_code)
# check usageState of VNF Package
usage_state = self.get_vnf_package(self.vnf_pkg_1).get('usageState')
usage_state = self.get_vnf_package(self.vnf_pkg_1)['usageState']
self.assertEqual('NOT_IN_USE', usage_state)
def test_sample2(self):
@ -490,7 +493,7 @@ class VnfLcmTest(base_v2.BaseSolV2Test):
inst_id = body['id']
# check usageState of VNF Package
usage_state = self.get_vnf_package(self.vnf_pkg_2).get('usageState')
usage_state = self.get_vnf_package(self.vnf_pkg_2)['usageState']
self.assertEqual('IN_USE', usage_state)
# 2. Instantiate a VNF instance
@ -554,5 +557,5 @@ class VnfLcmTest(base_v2.BaseSolV2Test):
self.assertEqual(404, resp.status_code)
# check usageState of VNF Package
usage_state = self.get_vnf_package(self.vnf_pkg_2).get('usageState')
usage_state = self.get_vnf_package(self.vnf_pkg_2)['usageState']
self.assertEqual('NOT_IN_USE', usage_state)

View File

@ -0,0 +1,686 @@
# Copyright (C) 2022 Nippon Telegraph and Telephone Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
import os
import time
from tacker.objects import fields
from tacker.tests.functional.sol_v2 import base_v2
from tacker.tests.functional.sol_v2 import paramgen
@ddt.ddt
class VnfLcmErrorHandlingTest(base_v2.BaseSolV2Test):
@classmethod
def setUpClass(cls):
super(VnfLcmErrorHandlingTest, cls).setUpClass()
cur_dir = os.path.dirname(__file__)
# tacker/tests/etc...
# /functional/sol_v2
image_dir = os.path.join(
cur_dir, "../../etc/samples/etsi/nfv/common/Files/images")
image_file = "cirros-0.5.2-x86_64-disk.img"
image_path = os.path.abspath(os.path.join(image_dir, image_file))
# Scale operation will fail
scale_ng_path = os.path.join(cur_dir, "samples/scale_ng")
cls.vnf_pkg_1, cls.vnfd_id_1 = cls.create_vnf_package(
scale_ng_path, image_path=image_path)
# Instantiate VNF will fail
error_network_path = os.path.join(cur_dir, "samples/error_network")
# no image contained
cls.vnf_pkg_2, cls.vnfd_id_2 = cls.create_vnf_package(
error_network_path)
@classmethod
def tearDownClass(cls):
super(VnfLcmErrorHandlingTest, cls).tearDownClass()
cls.delete_vnf_package(cls.vnf_pkg_1)
cls.delete_vnf_package(cls.vnf_pkg_2)
def setUp(self):
super(VnfLcmErrorHandlingTest, self).setUp()
def test_retry_rollback_scale_out(self):
"""Test retry and rollback scale out operations
* About attributes:
All of the following cardinality attributes are set.
In addition, 0..N or 1..N attributes are set to 2 or more.
0..1 is set to 1.
- 0..1 (1)
- 0..N (2 or more)
- 1..N (2 or more)
* About LCM operations:
This test includes the following operations.
- 0. Pre-setting
- 1. Create subscription
- 2. Test notification
- 3. Create VNF instance
- 4. Instantiate VNF
- 5. Show VNF instance
- 6. Scale out operation(will fail)
- 7. Show VNF instance
- 8. Retry operation
- 9. Rollback scale out operation
- 10. Show VNF LCM operation occurrence
- 11. List VNF LCM operation occurrence
- 12. Terminate VNF
- 13. Delete VNF instance
- 14. Delete subscription
- 15. Show subscription
"""
# 0. Pre-setting
# Create a new network and subnet to check the IP allocation of
# IPv4 and IPv6
ft_net0_name = 'ft-net0'
ft_net0_subs = {
'ft-ipv4-subnet0': {
'range': '100.100.100.0/24',
'ip_version': 4
},
'ft-ipv6-subnet0': {
'range': '1111:2222:3333::/64',
'ip_version': 6
}
}
ft_net0_id = self.create_network(ft_net0_name)
self.addCleanup(self.delete_network, ft_net0_id)
for sub_name, val in ft_net0_subs.items():
# subnet is automatically deleted with network deletion
self.create_subnet(
ft_net0_id, sub_name, val['range'], val['ip_version'])
net_ids = self.get_network_ids(
['net0', 'net1', 'net_mgmt', 'ft-net0'])
subnet_ids = self.get_subnet_ids(
['subnet0', 'subnet1', 'ft-ipv4-subnet0', 'ft-ipv6-subnet0'])
port_names = ['VDU2_CP1-1', 'VDU2_CP1-2']
port_ids = {}
for port_name in port_names:
port_id = self.create_port(net_ids['net0'], port_name)
port_ids[port_name] = port_id
self.addCleanup(self.delete_port, port_id)
# 1. Create subscription
callback_url = os.path.join(base_v2.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)
callback_uri = ('http://localhost:'
f'{base_v2.FAKE_SERVER_MANAGER.SERVER_PORT}'
f'{callback_url}')
sub_req = paramgen.sub_create_max(callback_uri)
resp, body = self.create_subscription(sub_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
sub_id = body['id']
# 2. Test notification
self.assert_notification_get(callback_url)
# check usageState of VNF Package
usage_state = self.get_vnf_package(self.vnf_pkg_1)['usageState']
self.assertEqual('NOT_IN_USE', usage_state)
# 3. Create VNF instance
expected_inst_attrs = [
'id',
'vnfInstanceName',
'vnfInstanceDescription',
'vnfdId',
'vnfProvider',
'vnfProductName',
'vnfSoftwareVersion',
'vnfdVersion',
# 'vnfConfigurableProperties', # omitted
# 'vimConnectionInfo', # omitted
'instantiationState',
# 'instantiatedVnfInfo', # omitted
'metadata',
# 'extensions', # omitted
'_links'
]
create_req = paramgen.create_vnf_max(self.vnfd_id_1)
resp, body = self.create_vnf_instance(create_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
self.check_resp_body(body, expected_inst_attrs)
inst_id = body['id']
# check usageState of VNF Package
usage_state = self.get_vnf_package(self.vnf_pkg_1)['usageState']
self.assertEqual('IN_USE', usage_state)
# check instantiationState of VNF
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body['instantiationState'])
# 4. Instantiate VNF
instantiate_req = paramgen.instantiate_vnf_max(
net_ids, subnet_ids, port_ids, self.auth_url)
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check usageState of VNF Package
usage_state = self.get_vnf_package(self.vnf_pkg_1)['usageState']
self.assertEqual('IN_USE', usage_state)
# 5. Show VNF instance
additional_inst_attrs = [
'vimConnectionInfo',
'instantiatedVnfInfo',
'extensions',
'vnfConfigurableProperties'
]
expected_inst_attrs.extend(additional_inst_attrs)
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# check instantiationState of VNF
self.assertEqual(fields.VnfInstanceState.INSTANTIATED,
body.get('instantiationState'))
# check vnfState of VNF
self.assertEqual(fields.VnfOperationalStateType.STARTED,
body['instantiatedVnfInfo'].get('vnfState'))
# 6. Scale out operation(will fail)
scaleout_req = paramgen.scaleout_vnf_max()
resp, body = self.scale_vnf_instance(inst_id, scaleout_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_failed_temp(lcmocc_id)
# check usageState of VNF Package
usage_state = self.get_vnf_package(self.vnf_pkg_1)['usageState']
self.assertEqual('IN_USE', usage_state)
# 7. Show VNF instance
expected_inst_attrs.extend(additional_inst_attrs)
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# 8. Retry scale out operation
resp, body = self.retry_lcmocc(lcmocc_id)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_delete(resp)
self.wait_lcmocc_failed_temp(lcmocc_id)
# 9. Rollback scale out operation
resp, body = self.rollback_lcmocc(lcmocc_id)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_delete(resp)
self.wait_lcmocc_rolled_back(lcmocc_id)
# 10. Show VNF LCM operation occurrence
# NOTE: omitted values are not supported at that time
expected_attrs = [
'id',
'operationState',
'stateEnteredTime',
'startTime',
'vnfInstanceId',
'grantId',
'operation',
'isAutomaticInvocation',
'operationParams',
'isCancelPending',
# 'cancelMode', # omitted
'error',
# 'resourceChanges', # omitted
# 'changedInfo', # omitted
# 'changedExtConnectivity', # omitted
# 'modificationsTriggeredByVnfPkgChange', # omitted
# 'vnfSnapshotInfoId', # omitted
'_links'
]
resp, body = self.show_lcmocc(lcmocc_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_attrs)
# 11. List VNF LCM operation occurrence
# NOTE: omitted values are not supported at that time
expected_attrs = [
'id',
'operationState',
'stateEnteredTime',
'startTime',
'vnfInstanceId',
# 'grantId', # omitted
'operation',
'isAutomaticInvocation',
# 'operationParams', # omitted
'isCancelPending',
# 'cancelMode', # omitted
# 'error', # omitted
# 'resourceChanges', # omitted
# 'changedInfo', # omitted
# 'changedExtConnectivity', # omitted
# 'modificationsTriggeredByVnfPkgChange', # omitted
# 'vnfSnapshotInfoId', # omitted
'_links'
]
resp, body = self.list_lcmocc()
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
for lcmocc in body:
self.check_resp_body(lcmocc, expected_attrs)
# 12. Terminate VNF instance
terminate_req = paramgen.terminate_vnf_max()
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check usageState of VNF Package
usage_state = self.get_vnf_package(self.vnf_pkg_1)['usageState']
self.assertEqual('IN_USE', usage_state)
# check instantiationState of VNF
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body.get('instantiationState'))
# wait a bit because there is a bit time lag between vnf instance DB
# terminate and delete completion.
time.sleep(5)
# 13. Delete VNF instance
resp, body = self.delete_vnf_instance(inst_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
# check usageState of VNF Package
usage_state = self.get_vnf_package(self.vnf_pkg_1)['usageState']
self.assertEqual('NOT_IN_USE', usage_state)
# 14. Delete subscription
resp, body = self.delete_subscription(sub_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
# 15. Show subscription
resp, body = self.show_subscription(sub_id)
self.assertEqual(404, resp.status_code)
self.check_resp_headers_in_get(resp)
def test_rollback_instantiate(self):
"""Test rollback instantiate operation
* About attributes:
Omit except for required attributes.
Only the following cardinality attributes are set.
- 1
- 1..N (1)
* About LCM operations:
This test includes the following operations.
- 1. Create subscription
- 2. Test notification
- 3. Create VNF instance
- 4. Instantiate VNF(will fail)
- 5. Show VNF instance
- 6. Rollback instantiation operation
- 7. Show VNF LCM operation occurrence
- 8. List VNF LCM operation occurrence
- 9. Delete VNF instance
- 10. Delete subscription
"""
# 1. Create subscription
callback_url = os.path.join(base_v2.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)
callback_uri = (f'http://localhost:'
f'{base_v2.FAKE_SERVER_MANAGER.SERVER_PORT}'
f'{callback_url}')
sub_req = paramgen.sub_create_min(callback_uri)
resp, body = self.create_subscription(sub_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
sub_id = body['id']
# 2. Test notification
self.assert_notification_get(callback_url)
# check usageState of VNF Package
usage_state = self.get_vnf_package(self.vnf_pkg_2)['usageState']
self.assertEqual('NOT_IN_USE', usage_state)
# 3. Create VNF instance
expected_inst_attrs = [
'id',
# 'vnfInstanceName', # omitted
# 'vnfInstanceDescription', # omitted
'vnfdId',
'vnfProvider',
'vnfProductName',
'vnfSoftwareVersion',
'vnfdVersion',
# 'vnfConfigurableProperties', # omitted
# 'vimConnectionInfo', # omitted
'instantiationState',
# 'instantiatedVnfInfo', # omitted
# 'metadata', # omitted
# 'extensions', # omitted
'_links'
]
create_req = paramgen.create_vnf_min(self.vnfd_id_2)
resp, body = self.create_vnf_instance(create_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
self.check_resp_body(body, expected_inst_attrs)
inst_id = body['id']
# check usageState of VNF Package
usage_state = self.get_vnf_package(self.vnf_pkg_2)['usageState']
self.assertEqual('IN_USE', usage_state)
# check instantiationState of VNF
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body.get('instantiationState'))
# 4. Instantiate VNF(will fail)
instantiate_req = paramgen.instantiate_vnf_min()
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_failed_temp(lcmocc_id)
# check usageState of VNF Package
usage_state = self.get_vnf_package(self.vnf_pkg_2)['usageState']
self.assertEqual('IN_USE', usage_state)
# 5. Show VNF instance
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# check instantiationState of VNF
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body.get('instantiationState'))
# 6. Rollback instantiation operation
resp, body = self.rollback_lcmocc(lcmocc_id)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_delete(resp)
self.wait_lcmocc_rolled_back(lcmocc_id)
# 7. Show VNF LCM operation occurrence
# NOTE: omitted values are not supported at that time
expected_attrs = [
'id',
'operationState',
'stateEnteredTime',
'startTime',
'vnfInstanceId',
'grantId',
'operation',
'isAutomaticInvocation',
'operationParams',
'isCancelPending',
# 'cancelMode', # omitted
'error',
# 'resourceChanges', # omitted
# 'changedInfo', # omitted
# 'changedExtConnectivity', # omitted
# 'modificationsTriggeredByVnfPkgChange', # omitted
# 'vnfSnapshotInfoId', # omitted
'_links'
]
resp, body = self.show_lcmocc(lcmocc_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_attrs)
# 8. List VNF LCM operation occurrence
# NOTE: omitted values are not supported at that time
expected_attrs = [
'id',
'operationState',
'stateEnteredTime',
'startTime',
'vnfInstanceId',
# 'grantId', # omitted
'operation',
'isAutomaticInvocation',
# 'operationParams', # omitted
'isCancelPending',
# 'cancelMode', # omitted
# 'error', # omitted
# 'resourceChanges', # omitted
# 'changedInfo', # omitted
# 'changedExtConnectivity', # omitted
# 'modificationsTriggeredByVnfPkgChange', # omitted
# 'vnfSnapshotInfoId', # omitted
'_links'
]
resp, body = self.list_lcmocc()
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
for lcmocc in body:
self.check_resp_body(lcmocc, expected_attrs)
# 9. Delete VNF instance
resp, body = self.delete_vnf_instance(inst_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
# check usageState of VNF Package
usage_state = self.get_vnf_package(self.vnf_pkg_2)['usageState']
self.assertEqual('NOT_IN_USE', usage_state)
# 10. Delete subscription
resp, body = self.delete_subscription(sub_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
def test_fail_instantiate(self):
"""Test fail instantiate operation
* About attributes:
Omit except for required attributes.
Only the following cardinality attributes are set.
- 1
- 1..N (1)
* About LCM operations:
This test includes the following operations.
- 1. Create subscription
- 2. Test notification
- 3. Create VNF instance
- 4. Instantiate VNF(will fail)
- 5. Show VNF instance
- 6. Fail instantiation operation
- 7. Show VNF LCM operation occurrence
- 8. List VNF LCM operation occurrence
- 9. Delete VNF instance
- 10. Delete subscription
"""
# 1. Create subscription
callback_url = os.path.join(base_v2.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)
callback_uri = (f'http://localhost:'
f'{base_v2.FAKE_SERVER_MANAGER.SERVER_PORT}'
f'{callback_url}')
sub_req = paramgen.sub_create_min(callback_uri)
resp, body = self.create_subscription(sub_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
sub_id = body['id']
# 2. Test notification
self.assert_notification_get(callback_url)
# check usageState of VNF Package
usage_state = self.get_vnf_package(self.vnf_pkg_2)['usageState']
self.assertEqual('NOT_IN_USE', usage_state)
# 3. Create VNF instance
expected_inst_attrs = [
'id',
# 'vnfInstanceName', # omitted
# 'vnfInstanceDescription', # omitted
'vnfdId',
'vnfProvider',
'vnfProductName',
'vnfSoftwareVersion',
'vnfdVersion',
# 'vnfConfigurableProperties', # omitted
# 'vimConnectionInfo', # omitted
'instantiationState',
# 'instantiatedVnfInfo', # omitted
# 'metadata', # omitted
# 'extensions', # omitted
'_links'
]
create_req = paramgen.create_vnf_min(self.vnfd_id_2)
resp, body = self.create_vnf_instance(create_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
self.check_resp_body(body, expected_inst_attrs)
inst_id = body['id']
# check usageState of VNF Package
usage_state = self.get_vnf_package(self.vnf_pkg_2)['usageState']
self.assertEqual('IN_USE', usage_state)
# check instantiationState of VNF
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body.get('instantiationState'))
# 4. Instantiate VNF(will fail)
instantiate_req = paramgen.instantiate_vnf_min()
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_failed_temp(lcmocc_id)
# check usageState of VNF Package
usage_state = self.get_vnf_package(self.vnf_pkg_2)['usageState']
self.assertEqual('IN_USE', usage_state)
# 5. Show VNF instance
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# check instantiationState of VNF
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body.get('instantiationState'))
# 6. Fail instantiation operation
expected_attrs = [
'id',
'operationState',
'stateEnteredTime',
'startTime',
'vnfInstanceId',
'grantId',
'operation',
'isAutomaticInvocation',
'operationParams',
'isCancelPending',
# 'cancelMode', # omitted
'error',
# 'resourceChanges', # omitted
# 'changedInfo', # omitted
# 'changedExtConnectivity', # omitted
# 'modificationTriggeredByVnfPkgChange', # omitted
# 'vnfSnapshotInfoId', # omitted
'_links'
]
resp, body = self.fail_lcmocc(lcmocc_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_attrs)
self.assertEqual('FAILED', body['operationState'])
# 7. Show VNF LCM operation occurrence
resp, body = self.show_lcmocc(lcmocc_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_attrs)
# 8. List VNF LCM operation occurrence
# NOTE: omitted values are not supported at that time
expected_attrs = [
'id',
'operationState',
'stateEnteredTime',
'startTime',
'vnfInstanceId',
# 'grantId', # omitted
'operation',
'isAutomaticInvocation',
# 'operationParams', # omitted
'isCancelPending',
# 'cancelMode', # omitted
# 'error', # omitted
# 'resourceChanges', # omitted
# 'changedInfo', # omitted
# 'changedExtConnectivity', # omitted
# 'modificationsTriggeredByVnfPkgChange', # omitted
# 'vnfSnapshotInfoId', # omitted
'_links'
]
resp, body = self.list_lcmocc()
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
for lcmocc in body:
self.check_resp_body(lcmocc, expected_attrs)
# 9. Delete VNF instance
# Delete Stack
self.heat_client.delete_stack(f'vnf-{inst_id}')
resp, body = self.delete_vnf_instance(inst_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
# check usageState of VNF Package
usage_state = self.get_vnf_package(self.vnf_pkg_2)['usageState']
self.assertEqual('NOT_IN_USE', usage_state)
# 10. Delete subscription
resp, body = self.delete_subscription(sub_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)

View File

@ -31,6 +31,12 @@ def make_zip(sample_dir, tmp_dir, vnfd_id, image_path=None):
tmp_contents = os.path.join(tmp_dir, "contents")
shutil.copytree(os.path.join(sample_dir, "contents"), tmp_contents)
# add common vnfd files
common_dir = os.path.join(sample_dir, "../common/Definitions/")
for entry in os.listdir(common_dir):
shutil.copy(os.path.join(common_dir, entry),
os.path.join(tmp_contents, "Definitions"))
# replace vnfd_id
def_path = os.path.join(tmp_contents, "Definitions")
for entry in os.listdir(def_path):