diff --git a/dragonflow/controller/apps/trunk.py b/dragonflow/controller/apps/trunk.py index b2a1253bd..913bcf257 100644 --- a/dragonflow/controller/apps/trunk.py +++ b/dragonflow/controller/apps/trunk.py @@ -18,7 +18,9 @@ from oslo_log import log from dragonflow.common import exceptions from dragonflow.controller.common import constants from dragonflow.controller import df_base_app +from dragonflow.controller import port_locator from dragonflow.db.models import constants as model_constants +from dragonflow.db.models import l2 from dragonflow.db.models import trunk LOG = log.getLogger(__name__) @@ -29,8 +31,31 @@ class TrunkApp(df_base_app.DFlowApp): @df_base_app.register_event(trunk.ChildPortSegmentation, model_constants.EVENT_CREATED) def _child_port_segmentation_created(self, child_port_segmentation): + parent_port = child_port_segmentation.parent + parent_binding = port_locator.get_port_binding(parent_port) + if parent_binding is None: + return + + if parent_binding.is_local: + self._install_local_cps(child_port_segmentation) + else: + self._install_remote_cps(child_port_segmentation) + + def _install_local_cps(self, child_port_segmentation): self._add_classification_rule(child_port_segmentation) self._add_dispatch_rule(child_port_segmentation) + port_locator.copy_port_binding( + child_port_segmentation.port, + child_port_segmentation.parent, + ) + child_port_segmentation.port.emit_bind_local() + + def _install_remote_cps(self, child_port_segmentation): + port_locator.copy_port_binding( + child_port_segmentation.port, + child_port_segmentation.parent, + ) + child_port_segmentation.port.emit_bind_remote() def _get_classification_params_vlan(self, child_port_segmentation): vlan_vid = (self.ofproto.OFPVID_PRESENT | @@ -55,9 +80,7 @@ class TrunkApp(df_base_app.DFlowApp): def _get_classification_actions(self, child_port_segmentation): segmentation_type = child_port_segmentation.segmentation_type - lport = child_port_segmentation.port.get_object() - if not lport: - lport = self.nb_api.get(child_port_segmentation.port) + lport = child_port_segmentation.port network_id = lport.lswitch.unique_key unique_key = lport.unique_key # TODO(oanson) This code is very similar to classifier app. @@ -97,9 +120,7 @@ class TrunkApp(df_base_app.DFlowApp): ) def _get_dispatch_match(self, child_port_segmentation): - lport = child_port_segmentation.port.get_object() - if not lport: - lport = self.nb_api.get(child_port_segmentation.port) + lport = child_port_segmentation.port match = self.parser.OFPMatch(reg7=lport.unique_key) return match @@ -132,9 +153,26 @@ class TrunkApp(df_base_app.DFlowApp): @df_base_app.register_event(trunk.ChildPortSegmentation, model_constants.EVENT_DELETED) def _child_port_segmentation_deleted(self, child_port_segmentation): + parent_port = child_port_segmentation.parent + parent_binding = port_locator.get_port_binding(parent_port) + if parent_binding is None: + return + + if parent_binding.is_local: + self._uninstall_local_cps(child_port_segmentation) + else: + self._uninstall_remote_cps(child_port_segmentation) + + def _uninstall_local_cps(self, child_port_segmentation): + child_port_segmentation.port.emit_unbind_local() + port_locator.clear_port_binding(child_port_segmentation.port) self._delete_classification_rule(child_port_segmentation) self._delete_dispatch_rule(child_port_segmentation) + def _uninstall_remote_cps(self, child_port_segmentation): + child_port_segmentation.port.emit_unbind_remote() + port_locator.clear_port_binding(child_port_segmentation.port) + def _delete_classification_rule(self, child_port_segmentation): match = self._get_classification_match(child_port_segmentation) self.mod_flow( @@ -152,3 +190,29 @@ class TrunkApp(df_base_app.DFlowApp): match=match, command=self.ofproto.OFPFC_DELETE_STRICT, ) + + def _get_all_cps_by_parent(self, lport): + return self.db_store.get_all( + trunk.ChildPortSegmentation(parent=lport.id), + index=trunk.ChildPortSegmentation.get_index('parent_id'), + ) + + @df_base_app.register_event(l2.LogicalPort, l2.EVENT_BIND_LOCAL) + def _local_port_bound(self, lport): + for cps in self._get_all_cps_by_parent(lport): + self._install_local_cps(cps) + + @df_base_app.register_event(l2.LogicalPort, l2.EVENT_UNBIND_LOCAL) + def _local_port_unbound(self, lport): + for cps in self._get_all_cps_by_parent(lport): + self._uninstall_local_cps(cps) + + @df_base_app.register_event(l2.LogicalPort, l2.EVENT_BIND_REMOTE) + def _remote_port_bound(self, lport): + for cps in self._get_all_cps_by_parent(lport): + self._install_remote_cps(cps) + + @df_base_app.register_event(l2.LogicalPort, l2.EVENT_UNBIND_REMOTE) + def _remote_port_unbound(self, lport): + for cps in self._get_all_cps_by_parent(lport): + self._uninstall_remote_cps(cps) diff --git a/dragonflow/controller/df_local_controller.py b/dragonflow/controller/df_local_controller.py index ec72fb563..d1b58bdad 100644 --- a/dragonflow/controller/df_local_controller.py +++ b/dragonflow/controller/df_local_controller.py @@ -268,12 +268,6 @@ class DfLocalController(object): method_name = 'delete_{0}'.format(table) return getattr(self, method_name, self.delete_model_object) - def update_child_port_segmentation(self, obj): - self.update_model_object(obj) - child = obj.port.get_object() - if child: - self.update(child) - def update(self, obj): handler = getattr( self, diff --git a/dragonflow/controller/port_locator.py b/dragonflow/controller/port_locator.py index b0e160d00..7371e62d7 100644 --- a/dragonflow/controller/port_locator.py +++ b/dragonflow/controller/port_locator.py @@ -32,6 +32,10 @@ def set_port_binding(lport, binding): _locations[lport.id] = binding +def copy_port_binding(lport, source): + set_port_binding(lport, get_port_binding(source)) + + def clear_port_binding(lport): _locations.pop(lport.id) diff --git a/dragonflow/db/models/trunk.py b/dragonflow/db/models/trunk.py index f3ddda7ba..00c409b2e 100644 --- a/dragonflow/db/models/trunk.py +++ b/dragonflow/db/models/trunk.py @@ -28,7 +28,12 @@ UUID_NAMESPACE = uuid.UUID('a11fee2a-d833-4e22-be31-f915b55f1f77') @mf.register_model -@mf.construct_nb_db_model(indexes={'lport_id': 'port.id'}) +@mf.construct_nb_db_model( + indexes={ + 'lport_id': 'port.id', + 'parent_id': 'parent.id', + } +) class ChildPortSegmentation(mf.ModelBase, mixins.Topic, mixins.BasicEvents): table_name = 'child_port_segmentation' diff --git a/dragonflow/neutron/services/trunk/driver.py b/dragonflow/neutron/services/trunk/driver.py index 9f154c4e7..d0d1ff548 100644 --- a/dragonflow/neutron/services/trunk/driver.py +++ b/dragonflow/neutron/services/trunk/driver.py @@ -120,8 +120,6 @@ class DragonflowDriver(base.DriverBase, mixins.LazyNbApiMixin): segmentation_id=subport.segmentation_id, ) self.nb_api.create(model) - self.nb_api.update(l2.LogicalPort(id=subport.port_id, - binding=df_parent.binding)) def _delete_subports_handler(self, *args, **kwargs): """Handle the event that subports were deleted""" @@ -150,8 +148,6 @@ class DragonflowDriver(base.DriverBase, mixins.LazyNbApiMixin): topic=trunk.project_id ) self.nb_api.delete(model) - self.nb_api.update(l2.LogicalPort(id=subport.port_id, - binding=None)) def _update_port_handler(self, *args, **kwargs): """Handle the event that a port changes status to ACTIVE or DOWN""" diff --git a/dragonflow/tests/fullstack/test_apps.py b/dragonflow/tests/fullstack/test_apps.py index a15af6740..1166023db 100644 --- a/dragonflow/tests/fullstack/test_apps.py +++ b/dragonflow/tests/fullstack/test_apps.py @@ -2461,7 +2461,6 @@ class TestDNATApp(test_base.DFTestBase): class TestTrunkApp(test_base.DFTestBase): - @testtools.skip("bug/1710335") def test_icmp_ping_pong(self): # Setup base components - two ports on 1 network self.topology = app_testing_objects.Topology(self.neutron, self.nb_api) diff --git a/dragonflow/tests/unit/test_trunk_app.py b/dragonflow/tests/unit/test_trunk_app.py index 02696fd63..2f9dc1919 100644 --- a/dragonflow/tests/unit/test_trunk_app.py +++ b/dragonflow/tests/unit/test_trunk_app.py @@ -16,6 +16,7 @@ import mock from dragonflow.controller.common import constants +from dragonflow.controller import port_locator from dragonflow.db import db_store from dragonflow.db.models import l2 from dragonflow.db.models import trunk @@ -117,6 +118,7 @@ class TestTrunkApp(test_app_base.DFAppTestBase): self.db_store.update(lport) segmentation = self._create_segmentation() self.db_store.update(segmentation) + port_locator.set_port_binding(lport, object()) self.controller.delete_by_id(type(segmentation), segmentation.id)