Apply Robot Framework for testing

Support ETSI NFV compliant automated testing by using
the Robot Framework and ETSI NFV-TST API test codes.

Implements: blueprint use-robot-api-tests
Spec: https://specs.openstack.org/openstack/tacker-specs/specs/victoria/use_robot_api_tests.html
Change-Id: Ic2fe5e3eb8b279f9a9d193a00e0cf9ac97fe75a2
This commit is contained in:
Tsukasa Inoue 2021-03-23 17:53:16 +09:00 committed by Aldinson Esto
parent 233a12c378
commit e71fd7667c
23 changed files with 2015 additions and 0 deletions

View File

@ -542,6 +542,16 @@
kubernetes_vim_rsc_wait_timeout: 800
tox_envlist: dsvm-functional-sol-kubernetes-v2
- job:
name: tacker-compliance-devstack-multinode-sol
parent: tacker-functional-devstack-multinode-legacy
description: |
Multinodes job for SOL devstack-based compliance tests
host-vars:
controller-tacker:
tox_envlist: dsvm-compliance-sol-api
voting: false
- project:
templates:
- check-requirements
@ -561,3 +571,4 @@
- tacker-functional-devstack-multinode-sol-kubernetes-v2
- tacker-functional-devstack-multinode-sol-multi-tenant
- tacker-functional-devstack-multinode-sol-kubernetes-multi-tenant
- tacker-compliance-devstack-multinode-sol

View File

@ -11,4 +11,5 @@
bindep_profile: test
bindep_dir: "{{ zuul_work_dir }}"
- ensure-tox
- ensure-java
- show-net-setup

View File

@ -0,0 +1,21 @@
---
features:
- |
Support ETSI NFV compliant automated testing by using the Robot Framework
and ETSI NFV-TST API test codes. This feature uses the Robot Framework
which utilizes the ETSI NFV-TST API test codes to test tacker for ETSI
NFV compliance in a Black Box testing level. This is an additional quality
test measure to ensure that the added tacker features comply to the ETSI
NFV standards.
issues:
- |
Regarding ETSI NFV compliant automated testing, some of the tests are
failing due to bugs (1) Tacker Bug and (2) Issues in the ETSI NFV-TST API
test codes.
(1) Tacker Bug such as Bug-#1945387 was detected in this compliance test
and the corresponding test is currently failing. Test will pass once the
bug is fixed.
(2) Issues in the ETSI NFV-TST API test codes such as schema and mock
server location issues also caused some tests to fail. Tests will pass once
the issues are resolved.
Due to these existing failed items, this test is currently non-voting.

View File

View File

@ -0,0 +1,433 @@
# Copyright (C) 2022 NEC, Corp.
# 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 time
import xml.etree.ElementTree as et
from oslo_serialization import jsonutils
from oslo_utils import uuidutils
import robot
from urllib.parse import urlparse
from tacker.tests.functional import base
from tacker.tests.functional.sol.vnflcm import test_vnf_instance as vnflcmtest
VNFPKG_PATH = '/vnfpkgm/v1/vnf_packages/%s'
VNFINSS_PATH = '/vnflcm/v1/vnf_instances'
VNFINS_PATH = '/vnflcm/v1/vnf_instances/%s'
VNFINS_INST_PATH = '/vnflcm/v1/vnf_instances/%s/instantiate'
VNFINS_TERM_PATH = '/vnflcm/v1/vnf_instances/%s/terminate'
VNFINS_GET_LCM_OP_OCCS_PATH = '/vnflcm/v1/vnf_lcm_op_occs'
VNFINS_GET_IND_LCM_OP_OCCS_PATH = '/vnflcm/v1/vnf_lcm_op_occs/%s'
VNFINS_CREATE_SUBSC_PATH = '/vnflcm/v1/subscriptions'
VNFINS_DEL_SUBSC_PATH = '/vnflcm/v1/subscriptions/%s'
VNFINS_SCALE_PATH = '/vnflcm/v1/vnf_instances/%s/scale'
INSTANTIATION_BODY = {
'flavourId': 'simple',
'extVirtualLinks': [
{
'id': 'net0',
'resourceId': None,
'extCps': [
{
'cpdId': 'CP1',
'cpConfig': [
{
'cpProtocolData': [
{
'layerProtocol': 'IP_OVER_ETHERNET'
}
]
}
]
}
]
}
],
'vimConnectionInfo': [
{
'id': None,
'vimId': None,
'vimType': 'ETSINFV.OPENSTACK_KEYSTONE.v_2'
}
]
}
TERMINATION_BODY = {
'terminationType': 'GRACEFUL',
'gracefulTerminationTimeout': 120
}
PATCH_BODY = {
'vnfInstanceName': 'vnf new name',
'vnfInstanceDescription': 'new description'
}
SUBSCRIPTION_BODY = {
'filter': {
'vnfInstanceSubscriptionFilter': {
'vnfdIds': [
''
]
}
},
'callbackUri': 'http://localhost:9091/endpoint'
}
SCALE_BODY = {
'type': 'SCALE_OUT',
'aspectId': 'VDU1',
'numberOfSteps': 1,
'additionalParams': {
'samplekey': 'samplevalue'
}
}
HEAL_BODY = {
'vnfcInstanceId': None,
'cause': 'healing'
}
CHG_EXT_CONN_BODY = {
"extVirtualLinks": [{
"id": "8877c521-7c51-4da8-a5b9-308b40437fd2",
"resourceId": "dfc1872e-69a7-4f14-a2c7-5bac8bd545eb",
"extCps": [{
"cpdId": "CP1",
"cpConfig": [{
}]
}]
}],
"vimConnectionInfo": [{
"id": "748f7d54-9fdf-4e7a-a180-ec057a9eefd8",
"vimId": "310e0d4c-7e85-42e4-b289-d09c0bdc44c8",
"vimType": "openstack",
"interfaceInfo": {
"endpoint": "http://127.0.0.1/identity"
}
}]
}
class VnfPkgInfo:
def __init__(self, vnfpkgid, vnfdid):
self._vnfpkgid = vnfpkgid
self._vnfdid = vnfdid
@property
def vnfpkgid(self):
return self._vnfpkgid
@property
def vnfdid(self):
return self._vnfdid
class BaseComplTest(base.BaseTackerTest):
@classmethod
def setUpClass(cls):
super(BaseComplTest, cls).setUpClass()
for vim_list in cls.client.list_vims().values():
for vim in vim_list:
if vim['name'] == 'VIM0':
cls.vimid = vim['id']
for net_list in cls.neutronclient().list_networks().values():
for net in net_list:
if net['name'] == 'net0':
cls.net0_id = net['id']
cls.base_dir = os.getcwd()
cls.test_root_dir = os.path.join(cls.base_dir, 'api-tests')
cls.sol_dir = os.path.join(cls.test_root_dir, cls.sol)
cls.api_dir = os.path.join(cls.sol_dir, cls.api)
cls.test_file = cls.resource + '.robot'
os.chdir(cls.api_dir)
parts = urlparse(cls.http_client.get_endpoint())
cls.common_variables = []
cls.common_variables.append('VNFM_SCHEMA:%s' % parts.scheme)
cls.common_variables.append('NFVO_SCHEMA:%s' % parts.scheme)
cls.common_variables.append('VNFM_HOST:%s' % parts.hostname)
cls.common_variables.append('NFVO_HOST:%s' % parts.hostname)
cls.common_variables.append('VNFM_PORT:%s' % parts.port)
cls.common_variables.append('NFVO_PORT:%s' % parts.port)
cls.common_variables.append('AUTH_USAGE:1')
cls.common_variables.append('AUTHORIZATION_HEADER:X-Auth-Token')
cls.common_variables.append('AUTHORIZATION_TOKEN:%s' %
cls.http_client.get_token())
@classmethod
def tearDownClass(cls):
os.chdir(cls.base_dir)
super(BaseComplTest, cls).tearDownClass()
@classmethod
def _get_responses_from_output(cls, output):
result = []
for el in et.fromstring(output).findall(
".//kw[@name='Output']/[@library='REST']/msg[1]"):
result.append(json.loads(el.text))
return result
@classmethod
def _get_id_from_output(cls, output):
res = cls._get_responses_from_output(output)
if ('status' in res[0] and
res[0]['status'] in [200, 201, 202, 203, 204]):
if ('body' in res[0] and 'id' in res[0]['body']):
return res[0]['body']['id']
return None
@classmethod
def _create_and_upload_vnf_packages(cls, pkgnames):
vnfpkginfos = []
for pkgname in pkgnames:
vnfpkgid, vnfdid = vnflcmtest._create_and_upload_vnf_package(
cls.http_client, pkgname, {})
vnfpkginfos.append(VnfPkgInfo(vnfpkgid, vnfdid))
return vnfpkginfos
@classmethod
def _disable_vnf_package(cls, vnfpkgid):
cls.http_client.do_request(VNFPKG_PATH % vnfpkgid,
'PATCH', content_type='application/json',
body=jsonutils.dumps({"operationalState": "DISABLED"}))
@classmethod
def _get_vnfpkgids(cls, vnfpkginfos):
vnfpkgids = []
for vnfpkginfo in vnfpkginfos:
vnfpkgids.append(vnfpkginfo.vnfpkgid)
return vnfpkgids
@classmethod
def _delete_vnf_package(cls, vnfpkgid):
cls.http_client.do_request(VNFPKG_PATH % vnfpkgid, 'DELETE')
@classmethod
def _disable_and_delete_vnf_packages(cls, vnfpkginfos):
for vnfpkginfo in vnfpkginfos:
cls._disable_vnf_package(vnfpkginfo.vnfpkgid)
cls._delete_vnf_package(vnfpkginfo.vnfpkgid)
@classmethod
def _create_vnf_instance(cls, vnfdid, name=None, description=None):
body = {'vnfdId': vnfdid}
if name:
body['vnfInstanceName'] = name
if description:
body['vnfInstanceDescription'] = description
res, resbody = cls.http_client.do_request(VNFINSS_PATH, 'POST',
body=jsonutils.dumps(body))
return res, resbody
@classmethod
def _delete_vnf_instance(cls, vnfid):
resp, body = cls.http_client.do_request(VNFINS_PATH % vnfid, 'DELETE')
@classmethod
def _instantiate_vnf_instance(cls, vnfid):
body = INSTANTIATION_BODY
body['extVirtualLinks'][0]['resourceId'] = cls.net0_id
body['vimConnectionInfo'][0]['id'] = uuidutils.generate_uuid()
body['vimConnectionInfo'][0]['vimId'] = cls.vimid
cls.http_client.do_request(VNFINS_INST_PATH % vnfid,
'POST', body=jsonutils.dumps(body))
cls._wait_vnf_status(vnfid, 'instantiationState', 'INSTANTIATED')
@classmethod
def _terminate_vnf_instance(cls, vnfid):
cls.http_client.do_request(VNFINS_TERM_PATH % vnfid,
'POST', body=jsonutils.dumps(TERMINATION_BODY))
cls._wait_vnf_status(vnfid, 'instantiationState', 'NOT_INSTANTIATED')
@classmethod
def _get_vnf_ind_instance(cls, vnfid):
res, resbody = cls.http_client.do_request(VNFINS_PATH % vnfid, 'GET')
return resbody
@classmethod
def _get_vnf_instance_id(cls):
res, resbody = cls.http_client.do_request(VNFINSS_PATH, 'GET')
return resbody[0]['id']
@classmethod
def _instantiate_vnf_instance_for_scale(cls, vnfid):
body = INSTANTIATION_BODY
body['flavourId'] = 'default'
body['extVirtualLinks'][0]['resourceId'] = cls.net0_id
body['vimConnectionInfo'][0]['id'] = uuidutils.generate_uuid()
body['vimConnectionInfo'][0]['vimId'] = cls.vimid
body['additionalParams'] = {
"lcm-operation-user-data": "./UserData/lcm_user_data.py",
"lcm-operation-user-data-class": "SampleUserData"
}
cls.http_client.do_request(VNFINS_INST_PATH % vnfid,
'POST', body=jsonutils.dumps(body))
cls._wait_vnf_status(vnfid, 'instantiationState', 'INSTANTIATED')
@classmethod
def _instantiate_error_vnf_instance(cls, vnfid):
body = INSTANTIATION_BODY
body['flavorId'] = 'sample'
body['extVirtualLinks'][0]['resourceId'] = cls.net0_id
body['vimConnectionInfo'][0]['id'] = uuidutils.generate_uuid()
body['vimConnectionInfo'][0]['vimId'] = cls.vimid
body['additionalParams'] = {
"lcm-operation-user-data": "./UserData/lcm_user_data2.py",
"lcm-operation-user-data-class": "SampleUserData"
}
cls.http_client.do_request(VNFINS_INST_PATH % vnfid,
'POST', body=jsonutils.dumps(body))
cls._wait_vnf_status(vnfid, 'instantiationState', 'INSTANTIATED')
@classmethod
def _get_lcm_op_occs_id(cls, vnfid, lcm='INSTANTIATE'):
res, resbody = cls.http_client.do_request(
VNFINS_GET_LCM_OP_OCCS_PATH, 'GET')
lcmid = None
for entry in resbody:
lcm_dict = entry
if ((lcm_dict['vnfInstanceId'] == vnfid) and
(lcm_dict['operation'] == lcm)):
lcmid = lcm_dict['id']
break
return lcmid
@classmethod
def _create_subscription(cls, vnfdid):
body = SUBSCRIPTION_BODY
body['filter']['vnfInstanceSubscriptionFilter']['vnfdIds'] = [vnfdid]
res, resbody = cls.http_client.do_request(VNFINS_CREATE_SUBSC_PATH,
'POST', body=jsonutils.dumps(body))
subscid = cls._get_id_from_output(resbody)
return subscid
@classmethod
def _get_subscription_id(cls):
res, resbody = cls.http_client.do_request(VNFINS_CREATE_SUBSC_PATH,
'GET')
subscid = resbody[0]['id']
return subscid
@classmethod
def _delete_subscription(cls, subscId):
cls.http_client.do_request(VNFINS_DEL_SUBSC_PATH % subscId,
'DELETE')
@classmethod
def _scaleout_vnf(cls, vnfid):
body = SCALE_BODY
body['type'] = 'SCALE_OUT'
res_scale, resbody = cls.http_client.do_request(
VNFINS_SCALE_PATH % vnfid,
'POST', body=jsonutils.dumps(body))
print("scaleout called")
print(res_scale)
print(resbody)
lcmid = cls._get_lcm_op_occs_id(vnfid, lcm='SCALE')
res = cls._wait_lcm_status(lcmid)
return res, lcmid
@classmethod
def _wait_lcm_status(cls, lcmid, value='COMPLETED', expire=600):
start_time = int(time.time())
res = 1
final_state = ''
while True:
resp, body = cls.http_client.do_request(
VNFINS_GET_IND_LCM_OP_OCCS_PATH % lcmid, 'GET')
if body is None:
break
if ((body['operationState'] == value) or
(((int(time.time()) - start_time) > expire)) or
(body['operationState'] == 'FAILED_TEMP')):
final_state = body['operationState']
break
time.sleep(5)
time.sleep(30)
if final_state == value:
res = 0
return res
@classmethod
def _wait_vnf_status(cls, vnfid, attr, value, expire=600):
start_time = int(time.time())
while True:
resp, body = cls.http_client.do_request(VNFINS_PATH % vnfid, 'GET')
if body[attr] == value:
break
if ((int(time.time()) - start_time) > expire):
break
time.sleep(5)
time.sleep(30)
def _run(self, test_case, variables=[], body=None, filename=None):
if (body is not None and filename is not None):
with open(os.path.join('jsons', filename), 'w') as f:
f.write(body)
all_vars = []
all_vars.extend(variables)
all_vars.extend(self.common_variables)
odir = os.path.join(self.base_dir, 'log',
self.sol, self.api, self.resource,
test_case.replace(' ', '_').replace('"', ''))
if not os.path.exists(odir):
os.makedirs(odir)
with open(os.path.join(odir, 'stdout.txt'), 'w') as stdout:
rc = robot.run(self.test_file, variable=all_vars, test=test_case,
outputdir=odir, stdout=stdout)
with open(os.path.join(odir, 'output.xml'), 'r') as ofile:
outputxml = ofile.read()
return rc, outputxml

View File

@ -0,0 +1,24 @@
# Copyright (C) 2022 NEC, Corp.
# 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.compliance import base
class BaseComplSolTest(base.BaseComplTest):
@classmethod
def setUpClass(cls):
cls.sol = 'SOL002'
super(BaseComplSolTest, cls).setUpClass()

View File

@ -0,0 +1,413 @@
# Copyright (C) 2022 NEC, Corp.
# 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 oslo_serialization import jsonutils
from oslo_utils import uuidutils
from tacker.tests.compliance import base as rootbase
from tacker.tests.compliance.sol002 import base
class BaseVNFLifecycleManagementTest(base.BaseComplSolTest):
@classmethod
def setUpClass(cls):
cls.api = 'VNFLifecycleManagement-API'
super(BaseVNFLifecycleManagementTest, cls).setUpClass()
cls.vnfpkginfos = cls._create_and_upload_vnf_packages(['vnflcm1'])
@classmethod
def tearDownClass(cls):
cls._disable_and_delete_vnf_packages(cls.vnfpkginfos)
super(BaseVNFLifecycleManagementTest, cls).tearDownClass()
class BaseVNFLifecycleManagementScaleTest(base.BaseComplSolTest):
@classmethod
def setUpClass(cls):
cls.api = 'VNFLifecycleManagement-API'
super(BaseVNFLifecycleManagementScaleTest, cls).setUpClass()
cls.vnfpkginfos = cls._create_and_upload_vnf_packages(
['sample_compliance_test'])
@classmethod
def tearDownClass(cls):
cls._disable_and_delete_vnf_packages(cls.vnfpkginfos)
super(BaseVNFLifecycleManagementScaleTest, cls).tearDownClass()
class VNFInstancesTest(BaseVNFLifecycleManagementTest):
@classmethod
def setUpClass(cls):
cls.resource = 'VNFInstances'
super(VNFInstancesTest, cls).setUpClass()
def test_post_create_new_vnfinstance(self):
# Pre-conditions:
body = jsonutils.dumps({"vnfdId": "%s" % self.vnfpkginfos[0].vnfdid,
"vnfInstanceName": "sol_api_test"})
rc, output = self._run('POST Create a new vnfInstance', body=body,
filename='createVnfRequest.json')
# Post-Conditions: VNF instance created
vnfid = self._get_id_from_output(output)
if (vnfid is not None):
self._delete_vnf_instance(vnfid)
self.assertEqual(0, rc)
def test_get_information_about_multiple_vnf_instances(self):
# Pre-conditions:
res, vnf = self._create_vnf_instance(self.vnfpkginfos[0].vnfdid)
rc, output = self._run('GET information about multiple VNF instances')
# Post-Conditions:
self._delete_vnf_instance(vnf['id'])
self.assertEqual(0, rc)
class IndividualVNFInstanceTest(BaseVNFLifecycleManagementTest):
@classmethod
def setUpClass(cls):
cls.resource = 'IndividualVNFInstance'
super(IndividualVNFInstanceTest, cls).setUpClass()
def test_get_information_about_individual_vnf_instance(self):
# Pre-conditions: none
res, vnf = self._create_vnf_instance(self.vnfpkginfos[0].vnfdid,
description="testvnf")
variables = ['vnfInstanceId:' + vnf['id']]
rc, output = self._run(
'GET Information about an individual VNF Instance', variables)
# Post-Conditions:
self._delete_vnf_instance(vnf['id'])
self.assertEqual(0, rc)
def test_delete_individual_vnfinstance(self):
# Pre-conditions: none
res, vnf = self._create_vnf_instance(self.vnfpkginfos[0].vnfdid)
variables = ['vnfInstanceId:' + vnf['id']]
rc, output = self._run('DELETE Individual VNFInstance', variables)
# Post-Conditions: VNF instance deleted
self.assertEqual(0, rc)
def test_modify_information_about_individual_vnf_instance(self):
# Pre-conditions: none
res, vnf = self._create_vnf_instance(self.vnfpkginfos[0].vnfdid)
vnfid = vnf['id']
variables = ['vnfInstanceId:' + vnfid]
body = rootbase.PATCH_BODY
body['vnfInstanceName'] = "modify_test"
body['vnfInstanceDescription'] = "sample_testcode"
rc, output = self._run('PATCH Individual VNFInstance',
variables=variables,
body=jsonutils.dumps(body))
# Post-Conditions:
self._delete_vnf_instance(vnfid)
self.assertEqual(0, rc)
class InstantiateVNFTaskTest(BaseVNFLifecycleManagementTest):
@classmethod
def setUpClass(cls):
cls.resource = 'InstantiateVNFTask'
super(InstantiateVNFTaskTest, cls).setUpClass()
def test_post_instantiate_vnfinstance(self):
# Pre-conditions: none
res, vnf = self._create_vnf_instance(self.vnfpkginfos[0].vnfdid)
variables = ['vnfInstanceId:' + vnf['id']]
body = rootbase.INSTANTIATION_BODY
body['extVirtualLinks'][0]['resourceId'] = self.net0_id
body['vimConnectionInfo'][0]['id'] = uuidutils.generate_uuid()
body['vimConnectionInfo'][0]['vimId'] = self.vimid
rc, output = self._run('POST Instantiate a vnfInstance',
variables=variables,
body=jsonutils.dumps(body),
filename='instantiateVnfRequest.json')
# Post-Conditions:
self._wait_vnf_status(vnf['id'], 'instantiationState', 'INSTANTIATED')
self._terminate_vnf_instance(vnf['id'])
self._delete_vnf_instance(vnf['id'])
self.assertEqual(0, rc)
class TerminateVNFTaskTest(BaseVNFLifecycleManagementTest):
@classmethod
def setUpClass(cls):
cls.resource = 'TerminateVNFTask'
super(TerminateVNFTaskTest, cls).setUpClass()
def test_post_terminate_vnfinstance(self):
# Pre-conditions: none
res, vnf = self._create_vnf_instance(self.vnfpkginfos[0].vnfdid)
self._instantiate_vnf_instance(vnf['id'])
variables = ['vnfInstanceId:' + vnf['id']]
rc, output = self._run('POST Terminate a vnfInstance',
variables=variables,
body=jsonutils.dumps(rootbase.TERMINATION_BODY),
filename='terminateVnfRequest.json')
# Post-Conditions:
self._wait_vnf_status(vnf['id'], 'instantiationState',
'NOT_INSTANTIATED')
self._delete_vnf_instance(vnf['id'])
self.assertEqual(0, rc)
class ScaleVNFTaskTest(BaseVNFLifecycleManagementScaleTest):
@classmethod
def setUpClass(cls):
cls.resource = 'ScaleVNFTask'
super(ScaleVNFTaskTest, cls).setUpClass()
def test_post_expand_by_scaleout(self):
# Pre-conditions: none
res, vnf = self._create_vnf_instance(self.vnfpkginfos[0].vnfdid)
self._instantiate_vnf_instance_for_scale(vnf['id'])
variables = ['vnfInstanceId:' + vnf['id']]
body = rootbase.SCALE_BODY
body['type'] = 'SCALE_OUT'
rc, output = self._run('POST Scale a vnfInstance',
variables=variables,
body=jsonutils.dumps(body),
filename='scaleVnfRequest.json')
res = self._get_responses_from_output(output)
# Post-Conditions:
lcmid = self._get_lcm_op_occs_id(vnf['id'], lcm='SCALE')
res = self._wait_lcm_status(lcmid)
self.assertEqual(0, res)
self.assertEqual(0, rc)
def test_post_reduce_by_scalein(self):
vnf_id = self._get_vnf_instance_id()
body = rootbase.SCALE_BODY
body['type'] = 'SCALE_IN'
rc, output = self._run('POST Scale a vnfInstance',
variables=['vnfInstanceId:' + vnf_id],
body=jsonutils.dumps(body),
filename='scaleVnfRequest.json')
res = self._get_responses_from_output(output)
# Post-Conditions:
lcmid = self._get_lcm_op_occs_id(vnf_id, lcm='SCALE')
res = self._wait_lcm_status(lcmid)
self.assertEqual(0, res)
self._terminate_vnf_instance(vnf_id)
self._delete_vnf_instance(vnf_id)
self.assertEqual(0, rc)
class CreateAndGetSubscriptionsTest(BaseVNFLifecycleManagementTest):
@classmethod
def setUpClass(cls):
cls.resource = 'Subscriptions'
super(CreateAndGetSubscriptionsTest, cls).setUpClass()
def test_create_subscription(self):
# Pre-conditions: none
body = rootbase.SUBSCRIPTION_BODY
body['filter']['vnfInstanceSubscriptionFilter']['vnfdIds'] = \
[self.vnfpkginfos[0].vnfdid]
rc, output = self._run('POST Create a new subscription',
body=jsonutils.dumps(body),
filename='lccnSubscriptionRequest.json')
self.assertEqual(0, rc)
def test_get_subscriptions(self):
# Pre-conditions: none
rc, output = self._run('GET Subscriptions')
self.assertEqual(0, rc)
class IndividualSubscriptionTest(BaseVNFLifecycleManagementTest):
@classmethod
def setUpClass(cls):
cls.resource = 'IndividualSubscription'
super(IndividualSubscriptionTest, cls).setUpClass()
def test_get_individual_subscription(self):
# Pre-conditions: none
subscid = self._get_subscription_id()
variables = ['subscriptionId:' + subscid]
rc, output = self._run('GET Individual Subscription',
variables=variables)
self.assertEqual(0, rc)
def test_remove_individual_subscription(self):
# Pre-conditions: none
subscid = self._get_subscription_id()
variables = ['subscriptionId:' + subscid]
rc, output = self._run('DELETE an individual subscription',
variables=variables)
self.assertEqual(0, rc)
class HealVNFTaskTest(BaseVNFLifecycleManagementTest):
@classmethod
def setUpClass(cls):
cls.resource = 'HealVNFTask'
super(HealVNFTaskTest, cls).setUpClass()
def test_post_heal_vnfinstance(self):
# Pre-conditions: none
res, vnf = self._create_vnf_instance(self.vnfpkginfos[0].vnfdid)
self._instantiate_vnf_instance(vnf['id'])
variables = ['vnfInstanceId:' + vnf['id']]
resbody = self._get_vnf_ind_instance(vnf['id'])
body = rootbase.HEAL_BODY
body['vnfcInstanceId'] = resbody.get('instantiated'
'VnfInfo').get('vnfcResourceInfo')[0].get('id')
rc, output = self._run('POST Heal a vnfInstance',
variables=variables,
body=jsonutils.dumps(body),
filename=' healVnfRequest.json')
# Post-Conditions:
lcmid = self._get_lcm_op_occs_id(vnf['id'], lcm='HEAL')
res = self._wait_lcm_status(lcmid)
self.assertEqual(0, res)
self._terminate_vnf_instance(vnf['id'])
self._delete_vnf_instance(vnf['id'])
self.assertEqual(0, rc)
class RetryOperationTaskTest(BaseVNFLifecycleManagementTest):
@classmethod
def setUpClass(cls):
cls.resource = 'RetryOperationTask'
super(RetryOperationTaskTest, cls).setUpClass()
def test_post_retry_operation(self):
# Pre-conditions: none
res, vnf = self._create_vnf_instance(self.vnfpkginfos[0].vnfdid)
self._instantiate_error_vnf_instance(vnf['id'])
lcmid = self._get_lcm_op_occs_id(vnf['id'])
variables = ['vnfLcmOpOccId:' + lcmid]
rc, output = self._run('Post Retry operation task',
variables=variables)
# Post-Conditions:
res = self._wait_lcm_status(lcmid, value='FAILED_TEMP')
self.assertEqual(0, res)
self._terminate_vnf_instance(vnf['id'])
self._delete_vnf_instance(vnf['id'])
self.assertEqual(1, rc)
class ChangeExternalVNFConnectivityTaskTest(BaseVNFLifecycleManagementTest):
@classmethod
def setUpClass(cls):
cls.resource = 'ChangeExternalVNFConnectivityTask'
super(ChangeExternalVNFConnectivityTaskTest, cls).setUpClass()
def test_post_chgextconn_vnfinstance(self):
# Pre-conditions: none
res, vnf = self._create_vnf_instance(self.vnfpkginfos[0].vnfdid)
self._instantiate_vnf_instance(vnf['id'])
variables = ['vnfInstanceId:' + vnf['id']]
resbody = self._get_vnf_ind_instance(vnf['id'])
body = rootbase.CHG_EXT_CONN_BODY
body['extVirtualLinks'][0]['resourceId'] = resbody.get('instantiated'
'VnfInfo').get('extVirtualLinkInfo')[0].get('resource'
'Handle').get('resourceId')
rc, output = self._run('POST Change external VNF connectivity',
variables=variables,
body=jsonutils.dumps(body),
filename='changeExtVnfConnectivityRequest.json')
# Post-Conditions:
lcmid = self._get_lcm_op_occs_id(vnf['id'], lcm='CHANGE_EXT_CONN')
res = self._wait_lcm_status(lcmid)
self.assertEqual(0, res)
self._terminate_vnf_instance(vnf['id'])
self._delete_vnf_instance(vnf['id'])
self.assertEqual(0, rc)

View File

@ -0,0 +1,24 @@
# Copyright (C) 2022 NEC, Corp.
# 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.compliance import base
class BaseComplSolTest(base.BaseComplTest):
@classmethod
def setUpClass(cls):
cls.sol = 'SOL003'
super(BaseComplSolTest, cls).setUpClass()

View File

@ -0,0 +1,481 @@
# Copyright (C) 2022 NEC, Corp.
# 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 oslo_serialization import jsonutils
from oslo_utils import uuidutils
from tacker.tests.compliance import base as rootbase
from tacker.tests.compliance.sol003 import base
class BaseVNFLifecycleManagementTest(base.BaseComplSolTest):
@classmethod
def setUpClass(cls):
cls.api = 'VNFLifecycleManagement-API'
super(BaseVNFLifecycleManagementTest, cls).setUpClass()
cls.vnfpkginfos = cls._create_and_upload_vnf_packages(['vnflcm1'])
@classmethod
def tearDownClass(cls):
cls._disable_and_delete_vnf_packages(cls.vnfpkginfos)
super(BaseVNFLifecycleManagementTest, cls).tearDownClass()
class BaseVNFLifecycleManagementScaleTest(base.BaseComplSolTest):
@classmethod
def setUpClass(cls):
cls.api = 'VNFLifecycleManagement-API'
super(BaseVNFLifecycleManagementScaleTest, cls).setUpClass()
cls.vnfpkginfos = cls._create_and_upload_vnf_packages(
['sample_compliance_test'])
@classmethod
def tearDownClass(cls):
cls._disable_and_delete_vnf_packages(cls.vnfpkginfos)
super(BaseVNFLifecycleManagementScaleTest, cls).tearDownClass()
class VNFInstancesTest(BaseVNFLifecycleManagementTest):
@classmethod
def setUpClass(cls):
cls.resource = 'VNFInstances'
super(VNFInstancesTest, cls).setUpClass()
def test_post_create_new_vnfinstance(self):
# Pre-conditions:
body = jsonutils.dumps({"vnfdId": "%s" % self.vnfpkginfos[0].vnfdid,
"vnfInstanceName": "sol_api_test"})
rc, output = self._run('POST Create a new vnfInstance', body=body,
filename='createVnfRequest.json')
# Post-Conditions: VNF instance created
vnfid = self._get_id_from_output(output)
if (vnfid is not None):
self._delete_vnf_instance(vnfid)
self.assertEqual(0, rc)
def test_get_information_about_multiple_vnf_instances(self):
# Pre-conditions:
res, vnf = self._create_vnf_instance(self.vnfpkginfos[0].vnfdid)
rc, output = self._run('GET information about multiple VNF instances')
# Post-Conditions:
self._delete_vnf_instance(vnf['id'])
self.assertEqual(0, rc)
class IndividualVNFInstanceTest(BaseVNFLifecycleManagementTest):
@classmethod
def setUpClass(cls):
cls.resource = 'IndividualVNFInstance'
super(IndividualVNFInstanceTest, cls).setUpClass()
def test_get_information_about_individual_vnf_instance(self):
# Pre-conditions: none
res, vnf = self._create_vnf_instance(self.vnfpkginfos[0].vnfdid,
description="testvnf")
variables = ['vnfInstanceId:' + vnf['id']]
rc, output = self._run(
'Get Information about an individual VNF Instance', variables)
# Post-Conditions:
self._delete_vnf_instance(vnf['id'])
self.assertEqual(0, rc)
def test_delete_individual_vnfinstance(self):
# Pre-conditions: none
res, vnf = self._create_vnf_instance(self.vnfpkginfos[0].vnfdid)
variables = ['notInstantiatedVnfInstanceId:' + vnf['id']]
rc, output = self._run('DELETE Individual VNFInstance', variables)
# Post-Conditions: VNF instance deleted
self.assertEqual(0, rc)
def test_modify_information_about_individual_vnf_instance(self):
# Pre-conditions: none
res, vnf = self._create_vnf_instance(self.vnfpkginfos[0].vnfdid)
vnfid = vnf['id']
variables = ['vnfInstanceId:' + vnfid]
body = rootbase.PATCH_BODY
body['vnfInstanceName'] = "modify_test"
body['vnfInstanceDescription'] = "sample_testcode"
rc, output = self._run('PATCH Individual VNFInstance',
variables=variables,
body=jsonutils.dumps(body))
# Post-Conditions:
self._delete_vnf_instance(vnfid)
self.assertEqual(0, rc)
class InstantiateVNFTaskTest(BaseVNFLifecycleManagementTest):
@classmethod
def setUpClass(cls):
cls.resource = 'InstantiateVNFTask'
super(InstantiateVNFTaskTest, cls).setUpClass()
def test_post_instantiate_vnfinstance(self):
# Pre-conditions: none
res, vnf = self._create_vnf_instance(self.vnfpkginfos[0].vnfdid)
variables = ['vnfInstanceId:' + vnf['id']]
body = rootbase.INSTANTIATION_BODY
body['extVirtualLinks'][0]['resourceId'] = self.net0_id
body['vimConnectionInfo'][0]['id'] = uuidutils.generate_uuid()
body['vimConnectionInfo'][0]['vimId'] = self.vimid
rc, output = self._run('Instantiate a vnfInstance',
variables=variables,
body=jsonutils.dumps(body),
filename='instantiateVnfRequest.json')
# Post-Conditions:
self._wait_vnf_status(vnf['id'], 'instantiationState', 'INSTANTIATED')
self._terminate_vnf_instance(vnf['id'])
self._delete_vnf_instance(vnf['id'])
self.assertEqual(0, rc)
class TerminateVNFTaskTest(BaseVNFLifecycleManagementTest):
@classmethod
def setUpClass(cls):
cls.resource = 'TerminateVNFTask'
super(TerminateVNFTaskTest, cls).setUpClass()
def test_post_terminate_vnfinstance(self):
# Pre-conditions: none
res, vnf = self._create_vnf_instance(self.vnfpkginfos[0].vnfdid)
self._instantiate_vnf_instance(vnf['id'])
variables = ['vnfInstanceId:' + vnf['id']]
rc, output = self._run('POST Terminate a vnfInstance',
variables=variables,
body=jsonutils.dumps(rootbase.TERMINATION_BODY),
filename='terminateVnfRequest.json')
# Post-Conditions:
self._wait_vnf_status(vnf['id'], 'instantiationState',
'NOT_INSTANTIATED')
self._delete_vnf_instance(vnf['id'])
self.assertEqual(0, rc)
class VnfLcmOperationOccurencesTest(BaseVNFLifecycleManagementTest):
@classmethod
def setUpClass(cls):
cls.resource = 'VnfLcmOperationOccurences'
super(VnfLcmOperationOccurencesTest, cls).setUpClass()
def test_get_vnf_lcm_op_occs(self):
# Pre-conditions: none
rc, output = self._run('GET status information about multiple'
'VNF LCM Operation OCC')
self.assertEqual(0, rc)
class IndividualVnfLcmOperationOccurenceTest(BaseVNFLifecycleManagementTest):
@classmethod
def setUpClass(cls):
cls.resource = 'IndividualVnfLcmOperationOccurence'
super(IndividualVnfLcmOperationOccurenceTest, cls).setUpClass()
def test_get_ind_vnf_lcm_op_occs(self):
# Pre-conditions: none
res, vnf = self._create_vnf_instance(self.vnfpkginfos[0].vnfdid)
self._instantiate_vnf_instance(vnf['id'])
lcmid = self._get_lcm_op_occs_id(vnf['id'])
variables = ['vnfLcmOpOccId:' + lcmid]
rc, output = self._run('Get Individual VNF LCM OP occurrence',
variables=variables)
# Post-Conditions:
self._delete_vnf_instance(vnf['id'])
self.assertEqual(0, rc)
class CreateAndGetSubscriptionsTest(BaseVNFLifecycleManagementTest):
@classmethod
def setUpClass(cls):
cls.resource = 'Subscriptions'
super(CreateAndGetSubscriptionsTest, cls).setUpClass()
def test_create_subscription(self):
# Pre-conditions: none
body = rootbase.SUBSCRIPTION_BODY
body['filter']['vnfInstanceSubscriptionFilter']['vnfdIds'] = \
[self.vnfpkginfos[0].vnfdid]
rc, output = self._run('POST Create a new subscription',
body=jsonutils.dumps(body),
filename='lccnSubscriptionRequest.json')
self.assertEqual(0, rc)
def test_get_subscriptions(self):
# Pre-conditions: none
rc, output = self._run('GET Subscriptions')
self.assertEqual(0, rc)
class IndividualSubscriptionTest(BaseVNFLifecycleManagementTest):
@classmethod
def setUpClass(cls):
cls.resource = 'IndividualSubscription'
super(IndividualSubscriptionTest, cls).setUpClass()
def test_get_individual_subscription(self):
# Pre-conditions: none
subscid = self._get_subscription_id()
variables = ['subscriptionId:' + subscid]
rc, output = self._run('GET Individual Subscription',
variables=variables)
self.assertEqual(0, rc)
def test_remove_individual_subscription(self):
# Pre-conditions: none
subscid = self._get_subscription_id()
variables = ['subscriptionId:' + subscid]
rc, output = self._run('DELETE an individual subscription',
variables=variables)
self.assertEqual(0, rc)
class ScaleVNFTaskTest(BaseVNFLifecycleManagementScaleTest):
@classmethod
def setUpClass(cls):
cls.resource = 'ScaleVNFTask'
super(ScaleVNFTaskTest, cls).setUpClass()
def test_post_expand_by_scaleout(self):
# Pre-conditions: none
res, vnf = self._create_vnf_instance(self.vnfpkginfos[0].vnfdid)
self._instantiate_vnf_instance_for_scale(vnf['id'])
variables = ['vnfInstanceId:' + vnf['id']]
body = rootbase.SCALE_BODY
body['type'] = 'SCALE_OUT'
rc, output = self._run('POST Scale a vnfInstance',
variables=variables,
body=jsonutils.dumps(body),
filename='scaleVnfRequest.json')
res = self._get_responses_from_output(output)
# Post-Conditions:
lcmid = self._get_lcm_op_occs_id(vnf['id'], lcm='SCALE')
res = self._wait_lcm_status(lcmid)
self.assertEqual(0, res)
self.assertEqual(0, rc)
def test_post_reduce_by_scalein(self):
vnf_id = self._get_vnf_instance_id()
body = rootbase.SCALE_BODY
body['type'] = 'SCALE_IN'
rc, output = self._run('POST Scale a vnfInstance',
variables=['vnfInstanceId:' + vnf_id],
body=jsonutils.dumps(body),
filename='scaleVnfRequest.json')
res = self._get_responses_from_output(output)
# Post-Conditions:
lcmid = self._get_lcm_op_occs_id(vnf_id, lcm='SCALE')
res = self._wait_lcm_status(lcmid)
self.assertEqual(0, res)
self._terminate_vnf_instance(vnf_id)
self._delete_vnf_instance(vnf_id)
self.assertEqual(0, rc)
class HealVNFTaskTest(BaseVNFLifecycleManagementTest):
@classmethod
def setUpClass(cls):
cls.resource = 'HealVNFTask'
super(HealVNFTaskTest, cls).setUpClass()
def test_post_heal_vnfinstance(self):
# Pre-conditions: none
res, vnf = self._create_vnf_instance(self.vnfpkginfos[0].vnfdid)
self._instantiate_vnf_instance(vnf['id'])
variables = ['vnfInstanceId:' + vnf['id']]
resbody = self._get_vnf_ind_instance(vnf['id'])
body = rootbase.HEAL_BODY
body['vnfcInstanceId'] = resbody.get('instantiated'
'VnfInfo').get('vnfcResourceInfo')[0].get('id')
rc, output = self._run('POST Heal a vnfInstance',
variables=variables,
body=jsonutils.dumps(body),
filename=' healVnfRequest.json')
# Post-Conditions:
lcmid = self._get_lcm_op_occs_id(vnf['id'], lcm='HEAL')
res = self._wait_lcm_status(lcmid)
self.assertEqual(0, res)
self._terminate_vnf_instance(vnf['id'])
self._delete_vnf_instance(vnf['id'])
self.assertEqual(0, rc)
class ChangeExternalVNFConnectivityTaskTest(BaseVNFLifecycleManagementTest):
@classmethod
def setUpClass(cls):
cls.resource = 'ChangeExternalVNFConnectivityTask'
super(ChangeExternalVNFConnectivityTaskTest, cls).setUpClass()
def test_post_chgextconn_vnfinstance(self):
# Pre-conditions: none
res, vnf = self._create_vnf_instance(self.vnfpkginfos[0].vnfdid)
self._instantiate_vnf_instance(vnf['id'])
variables = ['vnfInstanceId:' + vnf['id']]
resbody = self._get_vnf_ind_instance(vnf['id'])
body = rootbase.CHG_EXT_CONN_BODY
body['extVirtualLinks'][0]['resourceId'] = resbody.get('instantiated'
'VnfInfo').get('extVirtualLinkInfo')[0].get('resource'
'Handle').get('resourceId')
rc, output = self._run('POST Change external VNF connectivity',
variables=variables,
body=jsonutils.dumps(body),
filename='changeExtVnfConnectivityRequest.json')
# Post-Conditions:
lcmid = self._get_lcm_op_occs_id(vnf['id'], lcm='CHANGE_EXT_CONN')
res = self._wait_lcm_status(lcmid)
self.assertEqual(0, res)
self._terminate_vnf_instance(vnf['id'])
self._delete_vnf_instance(vnf['id'])
self.assertEqual(0, rc)
class RetryOperationTaskTest(BaseVNFLifecycleManagementTest):
@classmethod
def setUpClass(cls):
cls.resource = 'RetryOperationTask'
super(RetryOperationTaskTest, cls).setUpClass()
def test_post_retry_operation(self):
# Pre-conditions: none
res, vnf = self._create_vnf_instance(self.vnfpkginfos[0].vnfdid)
self._instantiate_error_vnf_instance(vnf['id'])
lcmid = self._get_lcm_op_occs_id(vnf['id'])
variables = ['vnfLcmOpOccId:' + lcmid]
rc, output = self._run('Post Retry operation task',
variables=variables)
# Post-Conditions:
res = self._wait_lcm_status(lcmid, value='FAILED_TEMP')
self.assertEqual(0, res)
self._terminate_vnf_instance(vnf['id'])
self._delete_vnf_instance(vnf['id'])
self.assertEqual(1, rc)
class RollbackOperationTaskTest(BaseVNFLifecycleManagementTest):
@classmethod
def setUpClass(cls):
cls.resource = 'RollbackOperationTask'
super(RollbackOperationTaskTest, cls).setUpClass()
def test_post_rollback_operation(self):
# Pre-conditions: none
res, vnf = self._create_vnf_instance(self.vnfpkginfos[0].vnfdid)
self._instantiate_error_vnf_instance(vnf['id'])
lcmid = self._get_lcm_op_occs_id(vnf['id'])
variables = ['vnfLcmOpOccId:' + lcmid]
rc, output = self._run('Post Rollback operation task',
variables=variables)
# Post-Conditions:
res = self._wait_lcm_status(lcmid, value='ROLLED_BACK')
self.assertEqual(0, res)
self._terminate_vnf_instance(vnf['id'])
self._delete_vnf_instance(vnf['id'])
self.assertEqual(0, rc)

View File

@ -0,0 +1,24 @@
# Copyright (C) 2022 NEC, Corp.
# 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.compliance import base
class BaseComplSolTest(base.BaseComplTest):
@classmethod
def setUpClass(cls):
cls.sol = 'SOL005'
super(BaseComplSolTest, cls).setUpClass()

View File

@ -0,0 +1,143 @@
# Copyright (C) 2022 NEC, Corp.
# 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.compliance.sol005 import base
class BaseVNFPackageManagementTest(base.BaseComplSolTest):
@classmethod
def setUpClass(cls):
cls.api = 'VNFPackageManagement-API'
super(BaseVNFPackageManagementTest, cls).setUpClass()
class VNFPackagesTest(BaseVNFPackageManagementTest):
@classmethod
def setUpClass(cls):
cls.resource = 'VNFPackages'
super(VNFPackagesTest, cls).setUpClass()
def test_get_all_vnf_packages(self):
# Pre-conditions: One or more VNF packages are onboarded in the NFVO.
vnfpkginfos = self._create_and_upload_vnf_packages(
['vnflcm1', 'vnflcm2'])
rc, output = self._run('GET all VNF Packages')
# Post-Conditions: none
self._disable_and_delete_vnf_packages(vnfpkginfos)
self.assertEqual(0, rc)
def test_get_vnf_packages_with_attribute_based_filter(self):
# Pre-conditions: One or more VNF packages are onboarded in the NFVO.
self._run('GET VNF Packages with attribute-based filter')
# Post-Conditions: none
def test_get_vnf_packages_with_all_fields_attribute_selector(self):
# Pre-conditions: One or more VNF packages are onboarded in the NFVO.
self._run('GET VNF Packages with "all_fields" attribute selector')
# Post-Conditions: none
def test_vnf_packages_with_exclude_default_attribute_selector(self):
# Pre-conditions: One or more VNF packages are onboarded in the NFVO.
self._run('GET VNF Packages with "exclude_default" attribute selector')
# Post-Conditions: none
def test_get_vnf_packages_with_fields_attribute_selector(self):
# Pre-conditions: One or more VNF packages are onboarded in the NFVO.
self._run('GET VNF Packages with "fields" attribute selector')
# Post-Conditions: none
def test_get_vnf_packages_with_exclude_fields_attribute_selector(self):
# Pre-conditions: One or more VNF packages are onboarded in the NFVO.
self._run('GET VNF Packages with "exclude_fields" attribute selector')
# Post-Conditions: none
def test_create_vnf_package(self):
# Pre-conditions: none
rc, output = self._run('Create new VNF Package Resource')
# Post-Conditions: The VNF Package Resource is successfully created
# on the NFVO
res = self._get_responses_from_output(output)
if ('status' in res[0] and res[0]['status'] == 201):
if ('body' in res[0] and 'id' in res[0]['body']):
# delete created vnf package
self._delete_vnf_package(res[0]['body']['id'])
self.assertEqual(0, rc)
class IndividualVNFPackageTest(BaseVNFPackageManagementTest):
@classmethod
def setUpClass(cls):
cls.resource = 'IndividualVNFPackage'
super(IndividualVNFPackageTest, cls).setUpClass()
def test_get_individual_vnf_package(self):
# Pre-conditions: One or more VNF packages are onboarded in the NFVO.
vnfpkginfos = self._create_and_upload_vnf_packages(['vnflcm1'])
variables = ['vnfPackageId:' + vnfpkginfos[0].vnfpkgid]
rc, output = self._run('GET Individual VNF Package', variables)
# Post-Conditions: none
self._disable_and_delete_vnf_packages(vnfpkginfos)
self.assertEqual(0, rc)
def test_disable_individual_vnf_package(self):
# Pre-conditions: One or more VNF Packages are onboarded in the NFVO
# in ENABLED operational state.
vnfpkginfos = self._create_and_upload_vnf_packages(['vnflcm1'])
variables = ['vnfPackageId:' + vnfpkginfos[0].vnfpkgid]
rc, output = self._run('Disable Individual VNF Package', variables)
# Post-Conditions: The VNF Package is in operational state DISABLED
self._delete_vnf_package(vnfpkginfos[0].vnfpkgid)
self.assertEqual(0, rc)
def test_enable_individual_vnf_package(self):
# Pre-conditions: One or more VNF Packages are onboarded in the NFVO
# in DISABLED operational state.
vnfpkginfos = self._create_and_upload_vnf_packages(['vnflcm1'])
self._disable_vnf_package(vnfpkginfos[0].vnfpkgid)
variables = ['vnfPackageId:' + vnfpkginfos[0].vnfpkgid]
rc, output = self._run('Enable Individual VNF Package', variables)
# Post-Conditions: The VNF Package is in operational state ENABLED
self._disable_and_delete_vnf_packages(vnfpkginfos)
self.assertEqual(0, rc)
def test_delete_individual_vnf_package(self):
# Pre-conditions: One or more VNF packages are onboarded in the NFVO
# in DISABLED operational state
vnfpkginfos = self._create_and_upload_vnf_packages(['vnflcm1'])
self._disable_vnf_package(vnfpkginfos[0].vnfpkgid)
variables = ['disabledVnfPackageId:' + vnfpkginfos[0].vnfpkgid]
rc, output = self._run('DELETE Individual VNF Package', variables)
# Post-Conditions: The VNF Package is not available anymore in the NFVO
self.assertEqual(0, rc)

View File

@ -0,0 +1,50 @@
heat_template_version: 2013-05-23
description: 'default deployment flavour for Sample VNF'
parameters:
nfv:
type: json
resources:
VDU1:
type: OS::Heat::AutoScalingGroup
properties:
min_size: 1
max_size: 50
desired_capacity: 1
resource:
type: VDU1.yaml
properties:
flavor: { get_param: [ nfv, VDU, VDU1, flavor ] }
image: { get_param: [ nfv, VDU, VDU1, image ] }
name: vdu1
net1: { get_resource: internalNW_1 }
VDU1_scale_out:
type: OS::Heat::ScalingPolicy
properties:
scaling_adjustment: 1
auto_scaling_group_id:
get_resource: VDU1
adjustment_type: change_in_capacity
VDU1_scale_in:
type: OS::Heat::ScalingPolicy
properties:
scaling_adjustment: -1
auto_scaling_group_id:
get_resource: VDU1
adjustment_type: change_in_capacity
internalNW_1:
type: OS::Neutron::Net
internalNW_1_subnet:
type: OS::Neutron::Subnet
properties:
ip_version: 4
network:
get_resource: internalNW_1
cidr: 192.168.0.0/24
outputs: {}

View File

@ -0,0 +1,45 @@
heat_template_version: 2013-05-23
description: 'Child HOT for mmp scale'
parameters:
flavor:
type: string
image:
type: string
name:
type: string
net1:
type: string
resources:
VDU1:
type: OS::Nova::Server
properties:
flavor: { get_param: flavor }
name: { get_param: name }
image: { get_param: image }
networks:
- port:
get_resource: CP1
- port:
get_resource: CP2
CP1:
type: OS::Neutron::Port
properties:
network: { get_param: net1 }
CP2:
type: OS::Neutron::Port
properties:
network: { get_param: net1 }
outputs:
mgmt_ip-VDU1:
value:
get_attr:
- CP1
- fixed_ips
- 0
- ip_address

View File

@ -0,0 +1,212 @@
tosca_definitions_version: tosca_simple_yaml_1_2
description: Simple deployment flavour for Sample VNF
imports:
- etsi_nfv_sol001_common_types.yaml
- etsi_nfv_sol001_vnfd_types.yaml
- helloworld3_types.yaml
topology_template:
inputs:
descriptor_id:
type: string
descriptor_version:
type: string
provider:
type: string
product_name:
type: string
software_version:
type: string
vnfm_info:
type: list
entry_schema:
type: string
flavour_id:
type: string
flavour_description:
type: string
substitution_mappings:
node_type: SAMPLE.VNF
properties:
flavour_id: default
requirements:
virtual_link_external: []
node_templates:
VNF:
type: SAMPLE.VNF
properties:
flavour_description: 'n-vnf'
vnfm_info:
- Tacker
interfaces:
Vnflcm:
instantiate: []
instantiate_start: []
instantiate_end: []
scale: []
scale_start: []
scale_end: []
heal: []
heal_start: []
heal_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
vdu_profile:
min_number_of_instances: 1
max_number_of_instances: 50
sw_image_data:
name: DemoVirtualStorage
version: '0.5.2'
checksum:
algorithm: sha-512
hash: 6b813aa46bb90b4da216a4d19376593fa3f4fc7e617f03a92b7fe11e9a3981cbe8f0959dbebe36225e5f53dc4492341a4863cac4ed1ee0909f3fc78ef9c3e869
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
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: 8192 MB
virtual_cpu:
num_virtual_cpu: 2
virtual_local_storage:
- size_of_storage: 200 GB
CP1:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
protocol:
- associated_layer_protocol: ipv4
address_data:
- address_type: ip_address
l3_address_data:
ip_address_assignment: true
floating_ip_activated: false
requirements:
- virtual_binding: VDU1
- virtual_link: internalNW_1
CP2:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
protocol:
- associated_layer_protocol: ipv4
address_data:
- address_type: ip_address
l3_address_data:
ip_address_assignment: true
floating_ip_activated: false
requirements:
- virtual_binding: VDU1
- virtual_link: internalNW_1
internalNW_1:
type: tosca.nodes.nfv.VnfVirtualLink
properties:
connectivity_type:
layer_protocols: [ ipv4 ]
vl_profile:
max_bitrate_requirements:
root: 2000000
min_bitrate_requirements:
root: 1000000
virtual_link_protocol_data:
- associated_layer_protocol: ipv4
l2_protocol_data:
network_type: vlan
l3_protocol_data:
ip_version: ipv4
cidr: '192.168.0.0/24'
dhcp_enabled: true
policies:
- vnf:
type: tosca.policies.nfv.ScalingAspects
properties:
aspects:
VDU1:
name: vdu1
description: vdu1
max_scale_level: 49
step_deltas:
- delta_1
- vdu1_initial_delta:
type: tosca.policies.nfv.VduInitialDelta
properties:
initial_delta:
number_of_instances: 1
targets: [ VDU1 ]
- vdu1_scaling_aspect_deltas:
type: tosca.policies.nfv.VduScalingAspectDeltas
properties:
aspect: VDU1
deltas:
delta_1:
number_of_instances: 1
targets: [ VDU1 ]
- instantiation_levels:
type: tosca.policies.nfv.InstantiationLevels
properties:
levels:
n-vnf-min:
description: n-msc-min structure
scale_info:
VDU1:
scale_level: 0
n-vnf-ten:
description: n-msc-max structure
scale_info:
VDU1:
scale_level: 9
n-vnf-max:
description: n-msc-max structure
scale_info:
VDU1:
scale_level: 49
default_level: n-vnf-min
- vdu1_instantiation_levels:
type: tosca.policies.nfv.VduInstantiationLevels
properties:
levels:
n-vnf-min:
number_of_instances: 1
n-vnf-ten:
number_of_instances: 10
n-vnf-max:
number_of_instances: 50
targets: [ VDU1 ]

View File

@ -0,0 +1,28 @@
tosca_definitions_version: tosca_simple_yaml_1_2
description: VNF
imports:
- etsi_nfv_sol001_common_types.yaml
- etsi_nfv_sol001_vnfd_types.yaml
- helloworld3_types.yaml
- helloworld3_df_default.yaml
topology_template:
inputs:
selected_flavour:
type: string
description: VNF deployment flavour selected by the consumer. Itis provided in the API.
node_templates:
VNF:
type: SAMPLE.VNF
properties:
flavour_id: { get_input: selected_flavour }
descriptor_id: 72700000-0000-0000-0000-202101290004
provider: SAMPLE
product_name: VNF
software_version: '1.0'
descriptor_version: 'VNF_1.0'
vnfm_info:
- Tacker

View File

@ -0,0 +1,47 @@
tosca_definitions_version: tosca_simple_yaml_1_2
description: Sample template definition
imports:
- etsi_nfv_sol001_common_types.yaml
- etsi_nfv_sol001_vnfd_types.yaml
node_types:
SAMPLE.VNF:
derived_from: tosca.nodes.nfv.VNF
properties:
descriptor_id:
type: string
default: 72700000-0000-0000-0000-202101290004
descriptor_version:
type: string
constraints: [ valid_values: [ 'VNF_1.0' ] ]
default: 'VNF_1.0'
provider:
type: string
constraints: [ valid_values: [ SAMPLE ] ]
default: SAMPLE
product_name:
type: string
constraints: [ valid_values: [ VNF ] ]
default: 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: [ default ] ]
default: default
flavour_description:
type: string
default: 'n-vnf'
interfaces:
Vnflcm:
type: tosca.interfaces.nfv.Vnflcm

View File

@ -0,0 +1,7 @@
TOSCA-Meta-File-Version: 1.0
CSAR-Version: 1.1
Created-by: Onboarding portal
Entry-Definitions: Definitions/helloworld3_top.vnfd.yaml
Name: Files/images/cirros-0.5.2-x86_64-disk.img
Content-type: application/x-iso9066-image

View File

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

16
tox.ini
View File

@ -79,6 +79,22 @@ setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_kubernetes_multi_tenant run --slowest --concurrency 1 {posargs}
[testenv:dsvm-compliance-sol-api]
passenv = {[testenv]passenv} *_PROXY
commands_pre =
git clone https://forge.etsi.org/rep/nfv/api-tests.git
git -C api-tests checkout 2.6.1-fix-plu
pip install -U -r{envdir}/api-tests/requirements.txt
commands =
stestr --test-path={toxinidir}/tacker/tests/compliance --top-dir={toxinidir} run --slowest --concurrency 1 {posargs}
commands_post =
rm -rf api-tests
allowlist_externals =
git
rm
changedir = {envdir}
[testenv:debug]
commands = oslo_debug_helper {posargs}