diff --git a/neutron_tempest_plugin/tap_as_a_service/__init__.py b/neutron_tempest_plugin/tap_as_a_service/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/neutron_tempest_plugin/tap_as_a_service/api/__init__.py b/neutron_tempest_plugin/tap_as_a_service/api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/neutron_tempest_plugin/tap_as_a_service/api/test_taas.py b/neutron_tempest_plugin/tap_as_a_service/api/test_taas.py new file mode 100644 index 00000000..06dce53a --- /dev/null +++ b/neutron_tempest_plugin/tap_as_a_service/api/test_taas.py @@ -0,0 +1,129 @@ +# 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 tempest.common import utils +from tempest import config +from tempest.lib import decorators +from tempest.lib import exceptions as lib_exc + +from neutron_tempest_plugin.tap_as_a_service import base + +CONF = config.CONF + + +class TaaSExtensionTestJSON(base.BaseTaasTest): + + @classmethod + @utils.requires_ext(extension='taas', service='network') + def skip_checks(cls): + super(TaaSExtensionTestJSON, cls).skip_checks() + + @classmethod + def resource_setup(cls): + super(TaaSExtensionTestJSON, cls).resource_setup() + cls.network = cls.create_network() + cls.ts_port = cls.create_port(cls.network) + cls.tf_port = cls.create_port(cls.network) + cls.tf2_port = cls.create_port(cls.network) + + @decorators.idempotent_id('b993c14e-797a-4c91-b4da-8cb1a450aa2f') + def test_create_tap_service_and_flow(self): + """create tap service adn tap flow + + Test create tap service and flow. + """ + tap_service = self.create_tap_service(port_id=self.ts_port['id']) + self.create_tap_flow(tap_service_id=tap_service['id'], + direction='BOTH', source_port=self.tf_port['id']) + + @decorators.idempotent_id('897a0aaf-1b55-4ea8-9d9f-1bc0fd09cb60') + @utils.requires_ext(extension='taas-vlan-filter', service='network') + def test_create_tap_service_and_flow_vlan_filter(self): + """create tap service with vlan_filter + + Test create tap service with additional vlan_filter argument. + """ + tap_service = self.create_tap_service(port_id=self.ts_port['id']) + tap_flow = self.create_tap_flow(tap_service_id=tap_service['id'], + direction='BOTH', + source_port=self.tf_port['id'], + vlan_filter='189,279,999-1008') + self.assertEqual(tap_flow['vlan_filter'], '189,279,999-1008') + + @decorators.idempotent_id('d7a2115d-16b4-41cf-95a6-dcebc3682b24') + def test_delete_tap_resources_after_ts_port_delete(self): + """delete tap resources after ts port delete + + Test delete tap resources after deletion of ts port. + """ + tap_service = self.create_tap_service(port_id=self.ts_port['id']) + tap_flow = self.create_tap_flow(tap_service_id=tap_service['id'], + direction='BOTH', + source_port=self.tf2_port['id']) + # delete ts_port; it shall also delete the associated tap-service and + # subsequently the tap-flow as well + self.ports_client.delete_port(self.ts_port['id']) + # Attempt tap-service deletion; it should throw not found exception. + self.assertRaises(lib_exc.NotFound, + self.tap_services_client.delete_tap_service, + tap_service['id']) + # Attempt tap-flow deletion; it should throw not found exception. + self.assertRaises(lib_exc.NotFound, + self.tap_flows_client.delete_tap_flow, + tap_flow['id']) + + @decorators.idempotent_id('9ba4edfd-4002-4c44-b02b-6c4f71b40a92') + def test_delete_tap_resources_after_tf_port_delete(self): + """delete tap resources after tf port delete + + Test delete tap service after deletion of tf port. + """ + tap_service = self.create_tap_service(port_id=self.ts_port['id']) + tap_flow = self.create_tap_flow(tap_service_id=tap_service['id'], + direction='BOTH', + source_port=self.tf_port['id']) + # delete tf port; it shall also delete the associated tap-flow + self.ports_client.delete_port(self.tf_port['id']) + # Attempt tap-flow deletion; it should throw not found exception. + self.assertRaises(lib_exc.NotFound, + self.tap_flows_client.delete_tap_flow, + tap_flow['id']) + # delete tap service; it shall go fine + self.tap_services_client.delete_tap_service(tap_service['id']) + + @decorators.idempotent_id('687089b8-b045-496d-86bf-030b380039d1') + def test_create_and_update_tap_service(self): + """create and update tap service + + Test update tap service - update description. + """ + tap_service = self.create_tap_service(port_id=self.ts_port['id']) + + # Update description of the tap service + self.update_tap_service( + tap_service['id'], + description='Tap Service Description Updated') + + @decorators.idempotent_id('bb4d5482-37fc-46b5-85a5-5867e9adbfae') + def test_create_and_update_tap_flow(self): + """create and update tap flow + + Test update tap flow - update description. + """ + tap_service = self.create_tap_service(port_id=self.ts_port['id']) + tap_flow = self.create_tap_flow( + tap_service_id=tap_service['id'], + direction='BOTH', source_port=self.tf_port['id']) + # Update description of the tap flow + self.update_tap_flow( + tap_flow['id'], + description='Tap Flow Description Updated') diff --git a/neutron_tempest_plugin/tap_as_a_service/base.py b/neutron_tempest_plugin/tap_as_a_service/base.py new file mode 100644 index 00000000..3ddc7977 --- /dev/null +++ b/neutron_tempest_plugin/tap_as_a_service/base.py @@ -0,0 +1,82 @@ +# 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 tempest.api.network.base as test +from tempest import config +from tempest.lib.common.utils import data_utils +from tempest.lib.common.utils import test_utils + +from neutron_tempest_plugin.tap_as_a_service.services import taas_client + +CONF = config.CONF + + +class BaseTaasTest(test.BaseAdminNetworkTest): + + @classmethod + def resource_setup(cls): + super(BaseTaasTest, cls).resource_setup() + os_primary = cls.os_primary + cls.tap_services_client = taas_client.TapServicesClient( + os_primary.auth_provider, + CONF.network.catalog_type, + CONF.network.region or CONF.identity.region, + endpoint_type=CONF.network.endpoint_type, + build_interval=CONF.network.build_interval, + build_timeout=CONF.network.build_timeout, + **os_primary.default_params) + cls.tap_flows_client = taas_client.TapFlowsClient( + os_primary.auth_provider, + CONF.network.catalog_type, + CONF.network.region or CONF.identity.region, + endpoint_type=CONF.network.endpoint_type, + build_interval=CONF.network.build_interval, + build_timeout=CONF.network.build_timeout, + **os_primary.default_params) + + def create_tap_service(self, **kwargs): + body = self.tap_services_client.create_tap_service( + name=data_utils.rand_name("tap_service"), + **kwargs) + tap_service = body['tap_service'] + self.addCleanup(test_utils.call_and_ignore_notfound_exc, + self.tap_services_client.delete_tap_service, + tap_service['id']) + return tap_service + + def create_tap_flow(self, **kwargs): + body = self.tap_flows_client.create_tap_flow( + name=data_utils.rand_name("tap_service"), + **kwargs) + tap_flow = body['tap_flow'] + self.addCleanup(test_utils.call_and_ignore_notfound_exc, + self.tap_flows_client.delete_tap_flow, + tap_flow['id']) + return tap_flow + + def update_tap_service(self, tap_service_id, **kwargs): + body = self.tap_services_client.update_tap_service( + tap_service_id, + **kwargs) + tap_service = body['tap_service'] + self.addCleanup(test_utils.call_and_ignore_notfound_exc, + self.tap_services_client.delete_tap_service, + tap_service['id']) + + def update_tap_flow(self, tap_flow_id, **kwargs): + body = self.tap_flows_client.update_tap_flow( + tap_flow_id, + **kwargs) + tap_flow = body['tap_flow'] + self.addCleanup(test_utils.call_and_ignore_notfound_exc, + self.tap_flows_client.delete_tap_flow, + tap_flow['id']) diff --git a/neutron_tempest_plugin/tap_as_a_service/services/__init__.py b/neutron_tempest_plugin/tap_as_a_service/services/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/neutron_tempest_plugin/tap_as_a_service/services/taas_client.py b/neutron_tempest_plugin/tap_as_a_service/services/taas_client.py new file mode 100644 index 00000000..7230cbbe --- /dev/null +++ b/neutron_tempest_plugin/tap_as_a_service/services/taas_client.py @@ -0,0 +1,63 @@ +# 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 tempest.lib.services.network import base + + +class TapServicesClient(base.BaseNetworkClient): + + def create_tap_service(self, **kwargs): + uri = '/taas/tap_services' + post_data = {'tap_service': kwargs} + return self.create_resource(uri, post_data) + + def update_tap_service(self, tap_service_id, **kwargs): + uri = '/taas/tap_services/%s' % tap_service_id + post_data = {'tap_service': kwargs} + return self.update_resource(uri, post_data) + + def show_tap_service(self, tap_service_id, **fields): + uri = '/taas/tap_services/%s' % tap_service_id + return self.show_resource(uri, **fields) + + def delete_tap_service(self, tap_service_id): + uri = '/taas/tap_services/%s' % tap_service_id + return self.delete_resource(uri) + + def list_tap_services(self, **filters): + uri = '/taas/tap_services' + return self.list_resources(uri, **filters) + + +class TapFlowsClient(base.BaseNetworkClient): + + def create_tap_flow(self, **kwargs): + uri = '/taas/tap_flows' + post_data = {'tap_flow': kwargs} + return self.create_resource(uri, post_data) + + def update_tap_flow(self, tap_flow_id, **kwargs): + uri = '/taas/tap_flows/%s' % tap_flow_id + post_data = {'tap_flow': kwargs} + return self.update_resource(uri, post_data) + + def show_tap_flow(self, tap_flow_id, **fields): + uri = '/taas/tap_flows/%s' % tap_flow_id + return self.show_resource(uri, **fields) + + def delete_tap_flow(self, tap_flow_id): + uri = '/taas/tap_flows/%s' % tap_flow_id + return self.delete_resource(uri) + + def list_tap_flows(self, **filters): + uri = '/taas/tap_flows' + return self.list_resources(uri, **filters) diff --git a/zuul.d/master_jobs.yaml b/zuul.d/master_jobs.yaml index 0c6785de..97be1ced 100644 --- a/zuul.d/master_jobs.yaml +++ b/zuul.d/master_jobs.yaml @@ -896,3 +896,93 @@ devstack_localrc: IPSEC_PACKAGE: strongswan NETWORK_API_EXTENSIONS: "{{ (network_api_extensions_common + network_api_extensions_vpnaas) | join(',') }}" + +- job: + name: neutron-tempest-plugin-tap-as-a-service + parent: neutron-tempest-plugin-base + description: | + Perform setup common to all tap-as-a-service tempest tests + roles: + - zuul: openstack/devstack + required-projects: + - openstack/devstack-gate + - openstack/neutron + - openstack/neutron-tempest-plugin + - openstack/tap-as-a-service + - openstack/tempest + vars: + tempest_test_regex: ^neutron_tempest_plugin\.tap_as_a_service + tox_envlist: all + network_api_extensions_common: *api_extensions + network_api_extensions_tempest: + - taas + - taas-vlan-filter + devstack_localrc: + NETWORK_API_EXTENSIONS: "{{ (network_api_extensions_common + network_api_extensions_tempest) | join(',') }}" + DOWNLOAD_DEFAULT_IMAGES: false + IMAGE_URLS: "http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-i386-disk.img,https://cloud-images.ubuntu.com/releases/bionic/release/ubuntu-18.04-server-cloudimg-amd64.img" + DEFAULT_IMAGE_NAME: cirros-0.3.4-i386-disk + ADVANCED_IMAGE_NAME: ubuntu-18.04-server-cloudimg-amd64 + BUILD_TIMEOUT: 784 + Q_AGENT: openvswitch + Q_ML2_TENANT_NETWORK_TYPE: vxlan + Q_ML2_PLUGIN_MECHANISM_DRIVERS: openvswitch + devstack_local_conf: + post-config: + /$NEUTRON_CORE_PLUGIN_CONF: + AGENT: + tunnel_types: vxlan,gre + test-config: + $TEMPEST_CONFIG: + taas_plugin_options: + advanced_image_ref: ubuntu-18.04-server-cloudimg-amd64 + advanced_image_ssh_user: ubuntu + provider_physical_network: public + provider_segmentation_id: 100 + image_feature_enabled: + api_v2: true + devstack_plugins: + neutron: git://opendev.org/openstack/neutron.git + neutron-tempest-plugin: https://opendev.org/openstack/neutron-tempest-plugin.git + tap-as-a-service: git://opendev.org/openstack/tap-as-a-service.git + devstack_services: + # Disable OVN services + ovn-controller: false + ovn-northd: false + ovs-vswitchd: false + ovsdb-server: false + q-ovn-metadata-agent: false + # Enable Neutron services that are not used by OVN + q-agt: true + q-dhcp: true + q-l3: true + q-meta: true + q-metering: true + br-ex-tcpdump: true + br-int-flows: true + base: false + key: true + mysql: true + rabbit: true + g-api: true + g-reg: true + n-api: true + n-cond: true + n-cpu: true + n-crt: true + n-sch: true + placement-api: true + n-api-meta: true + q-svc: true + quantum: true + taas: true + taas_openvswitch_agent: true + tempest: true + dstat: true + irrelevant-files: &tempest-irrelevant-files + - ^(test-|)requirements.txt$ + - ^releasenotes/.*$ + - ^doc/.*$ + - ^.*\.rst$ + - ^tools/.*$ + - ^tox.ini$ diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml index 08fb58cc..031860f8 100644 --- a/zuul.d/project.yaml +++ b/zuul.d/project.yaml @@ -186,6 +186,7 @@ - neutron-tempest-plugin-vpnaas-ussuri - neutron-tempest-plugin-vpnaas-victoria - neutron-tempest-plugin-vpnaas-wallaby + - neutron-tempest-plugin-tap-as-a-service gate: jobs: