diff --git a/neutron_taas/services/taas/taas_plugin.py b/neutron_taas/services/taas/taas_plugin.py index ac52c181..95fb18f5 100644 --- a/neutron_taas/services/taas/taas_plugin.py +++ b/neutron_taas/services/taas/taas_plugin.py @@ -17,6 +17,9 @@ from neutron.db import servicetype_db as st_db from neutron.services import provider_configuration as pconf from neutron.services import service_base +from neutron_lib.callbacks import events +from neutron_lib.callbacks import registry +from neutron_lib.callbacks import resources from neutron_lib import exceptions as n_exc from neutron_taas.common import constants @@ -37,6 +40,7 @@ def add_provider_configuration(type_manager, service_type): pconf.ProviderConfiguration('neutron_taas')) +@registry.has_registry_receivers class TaasPlugin(taas_db.Taas_db_Mixin): supported_extension_aliases = ["taas"] @@ -174,3 +178,34 @@ class TaasPlugin(taas_db.Taas_db_Mixin): with excutils.save_and_reraise_exception(): LOG.error("Failed to delete tap flow on driver. " "tap_flow: %s", id) + + @registry.receives(resources.PORT, [events.PRECOMMIT_DELETE]) + def handle_delete_port(self, resource, event, trigger, context, **kwargs): + deleted_port = kwargs['port'] + if not deleted_port: + LOG.error("TaaS: Handle Delete Port: Invalid port object received") + return + + deleted_port_id = deleted_port['id'] + LOG.info("TaaS: Handle Delete Port: %s", deleted_port_id) + + # Get list of configured tap-services + t_s_collection = self.get_tap_services( + context, + filters={'port_id': [deleted_port_id]}, fields=['id']) + + for t_s in t_s_collection: + try: + self.delete_tap_service(context, t_s['id']) + except taas_ex.TapServiceNotFound: + LOG.debug("Not found tap_service: %s", t_s['id']) + + t_f_collection = self.get_tap_flows( + context, + filters={'source_port': [deleted_port_id]}, fields=['id']) + + for t_f in t_f_collection: + try: + self.delete_tap_flow(context, t_f['id']) + except taas_ex.TapFlowNotFound: + LOG.debug("Not found tap_flow: %s", t_f['id']) diff --git a/neutron_taas/tests/tempest_plugin/tests/api/test_taas.py b/neutron_taas/tests/tempest_plugin/tests/api/test_taas.py index b5af8568..5e9b84e2 100644 --- a/neutron_taas/tests/tempest_plugin/tests/api/test_taas.py +++ b/neutron_taas/tests/tempest_plugin/tests/api/test_taas.py @@ -1,3 +1,4 @@ +# Copyright (c) 2018 AT&T Intellectual Property. All other rights reserved. # Copyright (c) 2015 Midokura SARL # All Rights Reserved. # @@ -16,6 +17,7 @@ from tempest.common import utils from tempest import config from tempest.lib import decorators +from tempest.lib import exceptions as lib_exc from neutron_taas.tests.tempest_plugin.tests.api import base @@ -24,48 +26,93 @@ 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() - if not utils.is_extension_enabled('taas', 'network'): - msg = "TaaS Extension not enabled." - raise cls.skipException(msg) + 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): - network = self.create_network() - port = self.create_port(network) - tap_service = self.create_tap_service(port_id=port['id']) + """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=port['id']) + direction='BOTH', source_port=self.tf_port['id']) @decorators.idempotent_id('d7a2115d-16b4-41cf-95a6-dcebc3682b24') - def test_delete_tap_service_after_delete_port(self): - network = self.create_network() - port = self.create_port(network) - tap_service = self.create_tap_service(port_id=port['id']) - # delete port - self.ports_client.delete_port(port['id']) + 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_update_tap_service(self): - network = self.create_network() - port = self.create_port(network) - tap_service = self.create_tap_service(port_id=port['id']) + 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_update_tap_flow(self): - network = self.create_network() - port = self.create_port(network) - tap_service = self.create_tap_service(port_id=port['id']) + 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=port['id']) + direction='BOTH', source_port=self.tf_port['id']) # Update description of the tap flow self.update_tap_flow( tap_flow['id'], diff --git a/releasenotes/notes/clear-tap-resources-on-port-delete-9583cdd7cd6098ea.yaml b/releasenotes/notes/clear-tap-resources-on-port-delete-9583cdd7cd6098ea.yaml new file mode 100644 index 00000000..c6ba6227 --- /dev/null +++ b/releasenotes/notes/clear-tap-resources-on-port-delete-9583cdd7cd6098ea.yaml @@ -0,0 +1,9 @@ +--- +prelude: > + Deleting a port associated with some tap resources (tap service or tap + flow) shall now cascade delete those associated tap resources as well. + It should be noted that this change alters the API behaviour in the sense + that earlier deleting a port did not clear the associated tap resources. +issues: + - | + Fixes bug `1814937 `_.