FT of scale/modify v2 APIs

This patch add scale/modify functional tests of v2 APIs to the
following test cases:
* Test basic lcms for v2 api Max pattern.
* Test basic lcms for v2 api Min pattern.
* Test rollback update VNF for v2 api.
* Test update and scale out's before/after for v2 api.

Implements: blueprint support-nfv-solv3-scale-vnf
Implements: blueprint support-nfv-solv3-modify-vnf
Closes-bug: #1962564
Change-Id: I0826f1c580a56cec860d59ae0eeb197e8d30f344
This commit is contained in:
Yi Feng 2022-02-15 15:09:56 +09:00
parent 55a16877c8
commit 3635aa1476
33 changed files with 1709 additions and 167 deletions

View File

@ -98,12 +98,12 @@ The following is sample Base HOT corresponding to above sample userdata script.
**top Base HOT**
.. literalinclude:: ../../../tacker/tests/functional/sol_v2/samples/sample1/contents/BaseHOT/simple/sample1.yaml
.. literalinclude:: ../../../tacker/tests/functional/sol_v2/samples/basic_lcms_max/contents/BaseHOT/simple/sample1.yaml
:language: yaml
**nested Base HOT**
.. literalinclude:: ../../../tacker/tests/functional/sol_v2/samples/sample1/contents/BaseHOT/simple/nested/VDU1.yaml
.. literalinclude:: ../../../tacker/tests/functional/sol_v2/samples/basic_lcms_max/contents/BaseHOT/simple/nested/VDU1.yaml
:language: yaml

View File

@ -102,8 +102,10 @@ class VnfLcmDriverV2(object):
'operation': operation,
'request': req.to_dict(),
'vnf_instance': inst.to_dict(),
'grant_request': grant_req.to_dict(),
'grant_response': grant.to_dict(),
'grant_request': (grant_req.to_dict()
if grant_req is not None else None),
'grant_response': (grant.to_dict()
if grant is not None else None),
'tmp_csar_dir': tmp_csar_dir
}
# script is relative path to Definitions/xxx.yaml

View File

@ -313,6 +313,11 @@ class BaseSolV2Test(base.BaseTestCase):
return self.tacker_client.do_request(
path, "POST", body=req_body, version="2.0.0")
def update_vnf_instance(self, inst_id, req_body):
path = f"/vnflcm/v2/vnf_instances/{inst_id}"
return self.tacker_client.do_request(
path, "PATCH", 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(

View File

@ -157,40 +157,6 @@ def sub_create_max(callback_uri):
}
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.
@ -481,30 +447,6 @@ def instantiate_vnf_max(net_ids, subnets, ports, auth_url):
}
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.
@ -549,3 +491,109 @@ def scaleout_vnf_max():
"numberOfSteps": 1,
"additionalParams": {"dummy-key": "dummy-value"}
}
def scaleout_vnf_min():
# Omit except for required attributes
# NOTE: Only the following cardinality attributes are set.
# - 1
# - 1..N (1)
return {
"type": "SCALE_OUT",
"aspectId": "VDU1_scale"
}
def scalein_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_IN",
"aspectId": "VDU1_scale",
"numberOfSteps": 1,
"additionalParams": {"dummy-key": "dummy-value"}
}
def scalein_vnf_min():
# Omit except for required attributes
# NOTE: Only the following cardinality attributes are set.
# - 1
# - 1..N (1)
return {
"type": "SCALE_IN",
"aspectId": "VDU1_scale"
}
def update_vnf_max(vnfd_id, vnfc_id_1, vnfc_id_2):
# 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 {
"vnfInstanceName": "new name",
"vnfInstanceDescription": "new description",
"vnfdId": vnfd_id,
"vnfConfigurableProperties": {"dummy-key": "dummy-value"},
"metadata": {"dummy-key": "dummy-value"},
"extensions": {"dummy-key": "dummy-value"},
"vimConnectionInfo": {
"vim2": {
"vimId": "ac2d2ece-5e49-4b15-b92d-b681e9c096d8",
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
"interfaceInfo": {
"endpoint": "http://127.0.0.1/identity/v3"
},
"accessInfo": {
"username": "dummy_user",
"region": "RegionOne",
"password": "dummy_password",
"project": "dummy_project",
"projectDomain": "Default",
"userDomain": "Default"
},
"extra": {
"dummy-key": "dummy-val"
}
}
},
"vnfcInfoModifications": [
{
"id": vnfc_id_1,
"vnfcConfigurableProperties": {"dummy-key": "dummy-value"}
},
{
"id": vnfc_id_2,
"vnfcConfigurableProperties": {"dummy-key": "dummy-value"}
}
]
}
def update_vnf_min():
# Omit except for required attributes
# NOTE: Only the following cardinality attributes are set.
# - 1
# - 1..N (1)
return {
"vnfInstanceName": "new name"
}
def update_vnf_min_with_parameter(vnfd_id):
# Omit except for required attributes
# NOTE: Only the following cardinality attributes are set.
# - 1
# - 1..N (1)
return {
"vnfdId": vnfd_id
}

View File

@ -421,8 +421,8 @@ topology_template:
leaf: 1048576
targets: [ internalVL3 ]
- policy_antiaffinity_group:
type: tosca.policies.nfv.AntiAffinityRule
- policy_affinity_group:
type: tosca.policies.nfv.AffinityRule
targets: [ affinityOrAntiAffinityGroup1 ]
properties:
scope: zone

View File

@ -39,8 +39,10 @@ utils.make_zip(".", tmp_dir, vnfd_id, image_path)
shutil.move(os.path.join(tmp_dir, zip_file_name), ".")
shutil.rmtree(tmp_dir)
create_req = paramgen.sample1_create(vnfd_id)
terminate_req = paramgen.sample1_terminate()
create_req = paramgen.create_vnf_max(vnfd_id)
scaleout_req = paramgen.scaleout_vnf_max()
scalein_req = paramgen.scalein_vnf_max()
terminate_req = paramgen.terminate_vnf_max()
print('#####################################################################\n'
'# Run pre.py if an error occurs #\n'
@ -56,14 +58,20 @@ 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.sample1_instantiate(
instantiate_req = paramgen.instantiate_vnf_max(
net_ids, subnet_ids, port_ids, "http://localhost/identity/v3")
with open("create_req", "w") as f:
with open("create_req", "w", encoding='utf-8') as f:
f.write(json.dumps(create_req, indent=2))
with open("terminate_req", "w") as f:
with open("terminate_req", "w", encoding='utf-8') as f:
f.write(json.dumps(terminate_req, indent=2))
with open("instantiate_req", "w") as f:
with open("scaleout_req", "w", encoding='utf-8') as f:
f.write(json.dumps(scaleout_req, indent=2))
with open("scalein_req", "w", encoding='utf-8') as f:
f.write(json.dumps(scalein_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,262 @@
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
change_external_connectivity_start:
implementation: sample-script
modify_information_start:
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,77 @@
# Copyright (C) 2021 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 change_external_connectivity_start(self):
if os.path.exists('/tmp/change_external_connectivity_start'):
raise Exception("test change_external_connectivity_start error")
pass
def modify_information_start(self):
if os.path.exists('/tmp/modify_information_start'):
raise Exception("test modify_information_start error")
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,58 @@
# Copyright (C) 2021 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)
create_req = paramgen.create_vnf_min(vnfd_id)
terminate_req = paramgen.terminate_vnf_min()
instantiate_req = paramgen.instantiate_vnf_min()
scaleout_req = paramgen.scaleout_vnf_min()
scalein_req = paramgen.scalein_vnf_min()
update_seq = paramgen.update_vnf_min()
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))
with open("scaleout_req", "w", encoding='utf-8') as f:
f.write(json.dumps(scaleout_req, indent=2))
with open("scalein_req", "w", encoding='utf-8') as f:
f.write(json.dumps(scalein_req, indent=2))
with open("update_seq", "w", encoding='utf-8') as f:
f.write(json.dumps(update_seq, indent=2))

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: { get_param: net5 }

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

@ -98,7 +98,7 @@ topology_template:
min_number_of_instances: 1
max_number_of_instances: 1
sw_image_data:
name: cirros-0.5.2-x86_64-disk
name: cirros-0.5.2-x86_64-disk2
version: '0.5.2'
checksum:
algorithm: sha-256
@ -251,8 +251,8 @@ topology_template:
leaf: 1048576
targets: [ internalVL3 ]
- policy_antiaffinity_group:
type: tosca.policies.nfv.AntiAffinityRule
- 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,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

@ -32,15 +32,14 @@ utils.make_zip(".", tmp_dir, vnfd_id)
shutil.copy(os.path.join(tmp_dir, zip_file_name), ".")
shutil.rmtree(tmp_dir)
create_req = paramgen.sample2_create(vnfd_id)
terminate_req = paramgen.sample2_terminate()
instantiate_req = paramgen.sample2_instantiate()
update_min_req = paramgen.update_vnf_min_with_parameter(vnfd_id)
# fake vnfc id, shoule be get from show vnf
vnfc_id_1 = "VDU1-9300a3cb-bd3b-45e4-9967-095040caf827"
vnfc_id_2 = "VDU2-39681281-e6e6-4179-8898-d9ec70f1642a"
update_max_req = paramgen.update_vnf_max(vnfd_id, vnfc_id_1, vnfc_id_2)
with open("create_req", "w") as f:
f.write(json.dumps(create_req, indent=2))
with open("update_min_req", "w", encoding='utf-8') as f:
f.write(json.dumps(update_min_req, indent=2))
with open("terminate_req", "w") as f:
f.write(json.dumps(terminate_req, indent=2))
with open("instantiate_req", "w") as f:
f.write(json.dumps(instantiate_req, indent=2))
with open("update_max_req", "w", encoding='utf-8') as f:
f.write(json.dumps(update_max_req, indent=2))

File diff suppressed because it is too large Load Diff

View File

@ -48,15 +48,23 @@ class VnfLcmErrorHandlingTest(base_v2.BaseSolV2Test):
cls.vnf_pkg_2, cls.vnfd_id_2 = cls.create_vnf_package(
error_network_path)
# update VNF or change external VNF connectivity will fail
update_change_ng_path = os.path.join(cur_dir,
"samples/basic_lcms_min")
# no image contained
cls.vnf_pkg_3, cls.vnfd_id_3 = cls.create_vnf_package(
update_change_ng_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)
cls.delete_vnf_package(cls.vnf_pkg_3)
def setUp(self):
super(VnfLcmErrorHandlingTest, self).setUp()
super().setUp()
def test_retry_rollback_scale_out(self):
"""Test retry and rollback scale out operations
@ -141,6 +149,7 @@ class VnfLcmErrorHandlingTest(base_v2.BaseSolV2Test):
self.assertEqual('NOT_IN_USE', usage_state)
# 3. Create VNF instance
# ETSI NFV SOL003 v3.3.1 5.5.2.2 VnfInstance
expected_inst_attrs = [
'id',
'vnfInstanceName',
@ -206,7 +215,7 @@ class VnfLcmErrorHandlingTest(base_v2.BaseSolV2Test):
# check vnfState of VNF
self.assertEqual(fields.VnfOperationalStateType.STARTED,
body['instantiatedVnfInfo'].get('vnfState'))
body['instantiatedVnfInfo']['vnfState'])
# 6. Scale out operation(will fail)
scaleout_req = paramgen.scaleout_vnf_max()
@ -221,7 +230,6 @@ class VnfLcmErrorHandlingTest(base_v2.BaseSolV2Test):
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)
@ -240,6 +248,7 @@ class VnfLcmErrorHandlingTest(base_v2.BaseSolV2Test):
self.wait_lcmocc_rolled_back(lcmocc_id)
# 10. Show VNF LCM operation occurrence
# ETSI NFV SOL003 v3.3.1 5.5.2.13 VnfLcmOpOcc
# NOTE: omitted values are not supported at that time
expected_attrs = [
'id',
@ -362,7 +371,7 @@ class VnfLcmErrorHandlingTest(base_v2.BaseSolV2Test):
# 1. Create subscription
callback_url = os.path.join(base_v2.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)
callback_uri = (f'http://localhost:'
callback_uri = ('http://localhost:'
f'{base_v2.FAKE_SERVER_MANAGER.SERVER_PORT}'
f'{callback_url}')
@ -379,6 +388,7 @@ class VnfLcmErrorHandlingTest(base_v2.BaseSolV2Test):
self.assertEqual('NOT_IN_USE', usage_state)
# 3. Create VNF instance
# ETSI NFV SOL003 v3.3.1 5.5.2.2 VnfInstance
expected_inst_attrs = [
'id',
# 'vnfInstanceName', # omitted
@ -441,6 +451,7 @@ class VnfLcmErrorHandlingTest(base_v2.BaseSolV2Test):
self.wait_lcmocc_rolled_back(lcmocc_id)
# 7. Show VNF LCM operation occurrence
# ETSI NFV SOL003 v3.3.1 5.5.2.13 VnfLcmOpOcc
# NOTE: omitted values are not supported at that time
expected_attrs = [
'id',
@ -535,7 +546,7 @@ class VnfLcmErrorHandlingTest(base_v2.BaseSolV2Test):
# 1. Create subscription
callback_url = os.path.join(base_v2.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)
callback_uri = (f'http://localhost:'
callback_uri = ('http://localhost:'
f'{base_v2.FAKE_SERVER_MANAGER.SERVER_PORT}'
f'{callback_url}')
@ -552,6 +563,7 @@ class VnfLcmErrorHandlingTest(base_v2.BaseSolV2Test):
self.assertEqual('NOT_IN_USE', usage_state)
# 3. Create VNF instance
# ETSI NFV SOL003 v3.3.1 5.5.2.2 VnfInstance
expected_inst_attrs = [
'id',
# 'vnfInstanceName', # omitted
@ -635,6 +647,7 @@ class VnfLcmErrorHandlingTest(base_v2.BaseSolV2Test):
self.assertEqual('FAILED', body['operationState'])
# 7. Show VNF LCM operation occurrence
# ETSI NFV SOL003 v3.3.1 5.5.2.13 VnfLcmOpOcc
resp, body = self.show_lcmocc(lcmocc_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
@ -684,3 +697,205 @@ class VnfLcmErrorHandlingTest(base_v2.BaseSolV2Test):
resp, body = self.delete_subscription(sub_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
def test_rollback_update(self):
"""Test rollback update VNF 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
- 5. Show VNF instance
- 6. Update VNF(will fail)
- 7. Rollback update operation
- 8. Show VNF LCM operation occurrence
- 9. List VNF LCM operation occurrence
- 10. Terminate VNF
- 11. Delete VNF instance
- 12. Delete subscription
"""
# 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_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_3)['usageState']
self.assertEqual('NOT_IN_USE', usage_state)
# 3. Create VNF instance
# ETSI NFV SOL003 v3.3.1 5.5.2.2 VnfInstance
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_3)
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_3)['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_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_complete(lcmocc_id)
# check usageState of VNF Package
usage_state = self.get_vnf_package(self.vnf_pkg_3)['usageState']
self.assertEqual('IN_USE', usage_state)
# 5. Show VNF instance
additional_inst_attrs = [
'vimConnectionInfo',
'instantiatedVnfInfo'
]
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['instantiationState'])
# check vnfState of VNF
self.assertEqual(fields.VnfOperationalStateType.STARTED,
body['instantiatedVnfInfo']['vnfState'])
# 6. Update VNF(will fail)
# NOTE: Create a file so that an error occurs in mgmtDriver
path = '/tmp/modify_information_start'
with open(path, 'w', encoding='utf-8') as f:
f.write('')
self.addCleanup(os.remove, path)
update_req = paramgen.update_vnf_min()
resp, body = self.update_vnf_instance(inst_id, update_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)
# 7. Rollback update 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)
# 8. Show VNF LCM operation occurrence
# ETSI NFV SOL003 v3.3.1 5.5.2.13 VnfLcmOpOcc
# 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.show_lcmocc(lcmocc_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_attrs)
# 9. List VNF LCM operation occurrence
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)
# 10. Terminate a VNF instance
terminate_req = paramgen.terminate_vnf_min()
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)
# wait a bit because there is a bit time lag between lcmocc DB
# update and terminate completion.
time.sleep(10)
# check usageState of VNF Package
usage_state = self.get_vnf_package(self.vnf_pkg_3)['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['instantiationState'])
# 11. 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_3)['usageState']
self.assertEqual('NOT_IN_USE', usage_state)
# 12. Delete subscription
resp, body = self.delete_subscription(sub_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)