From e71fd7667cfc1ca93f40ad3cfeadc5026d6fdf7d Mon Sep 17 00:00:00 2001 From: Tsukasa Inoue Date: Tue, 23 Mar 2021 17:53:16 +0900 Subject: [PATCH] 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 --- .zuul.yaml | 11 + playbooks/devstack/pre.yaml | 1 + ...ramework-for-testing-d04256887fd9bee7.yaml | 21 + tacker/tests/compliance/__init__.py | 0 tacker/tests/compliance/base.py | 433 ++++++++++++++++ tacker/tests/compliance/sol002/__init__.py | 0 tacker/tests/compliance/sol002/base.py | 24 + tacker/tests/compliance/sol002/test_vnflcm.py | 413 +++++++++++++++ tacker/tests/compliance/sol003/__init__.py | 0 tacker/tests/compliance/sol003/base.py | 24 + tacker/tests/compliance/sol003/test_vnflcm.py | 481 ++++++++++++++++++ tacker/tests/compliance/sol005/__init__.py | 0 tacker/tests/compliance/sol005/base.py | 24 + .../tests/compliance/sol005/test_vnfpkgm.py | 143 ++++++ .../BaseHOT/default/VNF-hot.yaml | 50 ++ .../BaseHOT/default/nested/VDU1.yaml | 45 ++ .../Definitions/helloworld3_df_default.yaml | 212 ++++++++ .../Definitions/helloworld3_top.vnfd.yaml | 28 + .../Definitions/helloworld3_types.yaml | 47 ++ .../TOSCA-Metadata/TOSCA.meta | 7 + .../UserData/__init__.py | 0 .../UserData/lcm_user_data.py | 35 ++ tox.ini | 16 + 23 files changed, 2015 insertions(+) create mode 100644 releasenotes/notes/apply-robot-framework-for-testing-d04256887fd9bee7.yaml create mode 100644 tacker/tests/compliance/__init__.py create mode 100644 tacker/tests/compliance/base.py create mode 100644 tacker/tests/compliance/sol002/__init__.py create mode 100644 tacker/tests/compliance/sol002/base.py create mode 100644 tacker/tests/compliance/sol002/test_vnflcm.py create mode 100644 tacker/tests/compliance/sol003/__init__.py create mode 100644 tacker/tests/compliance/sol003/base.py create mode 100644 tacker/tests/compliance/sol003/test_vnflcm.py create mode 100644 tacker/tests/compliance/sol005/__init__.py create mode 100644 tacker/tests/compliance/sol005/base.py create mode 100644 tacker/tests/compliance/sol005/test_vnfpkgm.py create mode 100644 tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/BaseHOT/default/VNF-hot.yaml create mode 100644 tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/BaseHOT/default/nested/VDU1.yaml create mode 100644 tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/Definitions/helloworld3_df_default.yaml create mode 100644 tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/Definitions/helloworld3_top.vnfd.yaml create mode 100644 tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/Definitions/helloworld3_types.yaml create mode 100644 tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/TOSCA-Metadata/TOSCA.meta create mode 100644 tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/UserData/__init__.py create mode 100644 tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/UserData/lcm_user_data.py diff --git a/.zuul.yaml b/.zuul.yaml index 31603e3a9..7770fb3a0 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -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 diff --git a/playbooks/devstack/pre.yaml b/playbooks/devstack/pre.yaml index b6bac0314..15043ad1a 100644 --- a/playbooks/devstack/pre.yaml +++ b/playbooks/devstack/pre.yaml @@ -11,4 +11,5 @@ bindep_profile: test bindep_dir: "{{ zuul_work_dir }}" - ensure-tox + - ensure-java - show-net-setup diff --git a/releasenotes/notes/apply-robot-framework-for-testing-d04256887fd9bee7.yaml b/releasenotes/notes/apply-robot-framework-for-testing-d04256887fd9bee7.yaml new file mode 100644 index 000000000..28ea72a34 --- /dev/null +++ b/releasenotes/notes/apply-robot-framework-for-testing-d04256887fd9bee7.yaml @@ -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. diff --git a/tacker/tests/compliance/__init__.py b/tacker/tests/compliance/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tacker/tests/compliance/base.py b/tacker/tests/compliance/base.py new file mode 100644 index 000000000..2b348a2ea --- /dev/null +++ b/tacker/tests/compliance/base.py @@ -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 diff --git a/tacker/tests/compliance/sol002/__init__.py b/tacker/tests/compliance/sol002/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tacker/tests/compliance/sol002/base.py b/tacker/tests/compliance/sol002/base.py new file mode 100644 index 000000000..081c9f3b0 --- /dev/null +++ b/tacker/tests/compliance/sol002/base.py @@ -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() diff --git a/tacker/tests/compliance/sol002/test_vnflcm.py b/tacker/tests/compliance/sol002/test_vnflcm.py new file mode 100644 index 000000000..8e3a7b107 --- /dev/null +++ b/tacker/tests/compliance/sol002/test_vnflcm.py @@ -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) diff --git a/tacker/tests/compliance/sol003/__init__.py b/tacker/tests/compliance/sol003/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tacker/tests/compliance/sol003/base.py b/tacker/tests/compliance/sol003/base.py new file mode 100644 index 000000000..c55402b3d --- /dev/null +++ b/tacker/tests/compliance/sol003/base.py @@ -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() diff --git a/tacker/tests/compliance/sol003/test_vnflcm.py b/tacker/tests/compliance/sol003/test_vnflcm.py new file mode 100644 index 000000000..5ecb72da8 --- /dev/null +++ b/tacker/tests/compliance/sol003/test_vnflcm.py @@ -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) diff --git a/tacker/tests/compliance/sol005/__init__.py b/tacker/tests/compliance/sol005/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tacker/tests/compliance/sol005/base.py b/tacker/tests/compliance/sol005/base.py new file mode 100644 index 000000000..060650ee0 --- /dev/null +++ b/tacker/tests/compliance/sol005/base.py @@ -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() diff --git a/tacker/tests/compliance/sol005/test_vnfpkgm.py b/tacker/tests/compliance/sol005/test_vnfpkgm.py new file mode 100644 index 000000000..b839d0a14 --- /dev/null +++ b/tacker/tests/compliance/sol005/test_vnfpkgm.py @@ -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) diff --git a/tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/BaseHOT/default/VNF-hot.yaml b/tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/BaseHOT/default/VNF-hot.yaml new file mode 100644 index 000000000..4aa7e11ea --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/BaseHOT/default/VNF-hot.yaml @@ -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: {} diff --git a/tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/BaseHOT/default/nested/VDU1.yaml b/tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/BaseHOT/default/nested/VDU1.yaml new file mode 100644 index 000000000..61bb48705 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/BaseHOT/default/nested/VDU1.yaml @@ -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 + diff --git a/tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/Definitions/helloworld3_df_default.yaml b/tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/Definitions/helloworld3_df_default.yaml new file mode 100644 index 000000000..b44813377 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/Definitions/helloworld3_df_default.yaml @@ -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 ] diff --git a/tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/Definitions/helloworld3_top.vnfd.yaml b/tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/Definitions/helloworld3_top.vnfd.yaml new file mode 100644 index 000000000..d8d702ca9 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/Definitions/helloworld3_top.vnfd.yaml @@ -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 diff --git a/tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/Definitions/helloworld3_types.yaml b/tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/Definitions/helloworld3_types.yaml new file mode 100644 index 000000000..bd7967d90 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/Definitions/helloworld3_types.yaml @@ -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 diff --git a/tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/TOSCA-Metadata/TOSCA.meta b/tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/TOSCA-Metadata/TOSCA.meta new file mode 100644 index 000000000..473d35e43 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/TOSCA-Metadata/TOSCA.meta @@ -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 diff --git a/tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/UserData/__init__.py b/tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/UserData/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/UserData/lcm_user_data.py b/tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/UserData/lcm_user_data.py new file mode 100644 index 000000000..e9fbd4f79 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/sample_compliance_test/UserData/lcm_user_data.py @@ -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} diff --git a/tox.ini b/tox.ini index 531f2988d..a5da2cce4 100644 --- a/tox.ini +++ b/tox.ini @@ -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}