From 792505eec7766306a68e5d2b50245d655f0170aa Mon Sep 17 00:00:00 2001 From: David Ramirez Date: Thu, 18 Apr 2024 12:53:15 +0000 Subject: [PATCH 01/12] Add pacing to send_flow_mod --- main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/main.py b/main.py index 4f40daf..a10d5ab 100644 --- a/main.py +++ b/main.py @@ -800,6 +800,7 @@ def _send_barrier_request(self, switch, flow_mods): self.controller.buffers.msg_out.put(event) def _send_flow_mod(self, switch, flow_mod): + self.controller.pacer.hit("flow_manager.send_flow_mod", switch.dpid) if not switch.is_connected(): raise SwitchNotConnectedError( f"switch {switch.id} isn't connected", flow_mod From 3a0e0c5604c0f57ca1d0e5dc1aefa5f6e7fb556a Mon Sep 17 00:00:00 2001 From: David Ramirez Date: Fri, 31 May 2024 19:07:13 +0000 Subject: [PATCH 02/12] Switched to local pace settings --- main.py | 7 ++++++- settings.py | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index a10d5ab..446da90 100644 --- a/main.py +++ b/main.py @@ -20,6 +20,7 @@ from kytos.core import KytosEvent, KytosNApp, log, rest from kytos.core.helpers import listen_to, now +from kytos.core.pacing import PacerWrapper from kytos.core.rest_api import ( HTTPException, JSONResponse, @@ -48,6 +49,7 @@ ENABLE_BARRIER_REQUEST, ENABLE_CONSISTENCY_CHECK, FLOWS_DICT_MAX_SIZE, + ACTION_PACES, ) from .utils import ( _valid_consistency_ignored, @@ -100,6 +102,9 @@ def setup(self): self._flow_mods_retry_count_lock = Lock() self.resent_flows = set() + self.pacer = PacerWrapper("flow_manager", self.controller.pacer) + self.pacer.inject_config(ACTION_PACES) + @staticmethod def get_flow_controller() -> FlowController: """Get FlowController.""" @@ -800,7 +805,7 @@ def _send_barrier_request(self, switch, flow_mods): self.controller.buffers.msg_out.put(event) def _send_flow_mod(self, switch, flow_mod): - self.controller.pacer.hit("flow_manager.send_flow_mod", switch.dpid) + self.pacer.hit("send_flow_mod", switch.dpid) if not switch.is_connected(): raise SwitchNotConnectedError( f"switch {switch.id} isn't connected", flow_mod diff --git a/settings.py b/settings.py index 67bf2e9..5d62866 100644 --- a/settings.py +++ b/settings.py @@ -22,3 +22,10 @@ to be at least greater than FLOW_STATS and ideally it slightly greater than whichever longest network convergence FlowMods operations that your network has. """ + +ACTION_PACES = { + "send_flow_mod": { + "pace": "100/second", + "strategy": "fixed_window", + }, +} From 928de398b4955f30789a84fe836d4c5eaf2e2e93 Mon Sep 17 00:00:00 2001 From: David Ramirez Date: Mon, 1 Jul 2024 21:36:14 +0000 Subject: [PATCH 03/12] linter cleanup --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index 446da90..c683099 100644 --- a/main.py +++ b/main.py @@ -40,6 +40,7 @@ SwitchNotConnectedError, ) from .settings import ( + ACTION_PACES, CONN_ERR_MAX_RETRIES, CONN_ERR_MIN_WAIT, CONN_ERR_MULTIPLIER, @@ -49,7 +50,6 @@ ENABLE_BARRIER_REQUEST, ENABLE_CONSISTENCY_CHECK, FLOWS_DICT_MAX_SIZE, - ACTION_PACES, ) from .utils import ( _valid_consistency_ignored, From 3cb493ade42b6c5e9c3833cc68b00b65aab897ab Mon Sep 17 00:00:00 2001 From: David Ramirez Date: Wed, 3 Jul 2024 21:14:19 +0000 Subject: [PATCH 04/12] Added per owner pacing --- main.py | 44 +++++++++++++++++++++++++++++++------------- settings.py | 8 ++++++++ 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/main.py b/main.py index c683099..62f69bc 100644 --- a/main.py +++ b/main.py @@ -209,7 +209,7 @@ def _on_ofpt_barrier_reply(self, event): flows = [] with self._flow_mods_sent_lock: for flow_xid in flow_xids: - flow, cmd = self._flow_mods_sent[flow_xid] + flow, cmd, _ = self._flow_mods_sent[flow_xid] if ( cmd != "add" or flow_xid not in self._flow_mods_sent @@ -293,7 +293,7 @@ def _retry_on_openflow_connection_error( try: xid = int(event.message.header.xid) - flow, command = self._flow_mods_sent[xid] + flow, command, owner = self._flow_mods_sent[xid] except KeyError: raise ValueError( f"Aborting retries, xid: {xid} not found on flow mods sent" @@ -329,7 +329,7 @@ def _retry_on_openflow_connection_error( ) flow_mod = build_flow_mod_from_command(flow, command) flow_mod.header.xid = xid - self._send_flow_mod(flow.switch, flow_mod) + self._send_flow_mod(flow.switch, flow_mod, owner) if send_barrier: self._send_barrier_request(flow.switch, [flow_mod]) return True @@ -713,7 +713,7 @@ def _install_flows( reraise_conn: True to reraise switch connection errors send_barrier: True to send barrier_request """ - flow_mods, flows, flow_dicts = [], [], [] + flow_mods, flows, flow_dicts, owners, commands = [], [], [], [], [] for switch in switches: serializer = FlowFactory.get_class(switch, Flow04) flows_list = flows_dict.get("flows", []) @@ -730,6 +730,8 @@ def _install_flows( flow_mod.pack() flow_mods.append(flow_mod) flows.append(flow) + owners.append(flow_dict.get('owner')) + commands.append(command) flow_dicts.append( {**{"flow": flow_dict}, **{"flow_id": flow.id, "switch": switch.id}} ) @@ -741,21 +743,32 @@ def _install_flows( self.delete_matched_flows( flow_dicts, {switch.id: switch for switch in switches} ) - self._send_flow_mods(switches, flow_mods, flows, reraise_conn, send_barrier) + self._send_flow_mods( + switches, + flow_mods, + flows, + owners, + commands, + reraise_conn, + send_barrier + ) def _send_flow_mods( self, switches, flow_mods, flows, + owners, + commands, reraise_conn=True, send_barrier=ENABLE_BARRIER_REQUEST, + owner=None ): """Send FlowMod (and BarrierRequest) given a list of flow_dicts to switches.""" for switch in switches: - for i, (flow_mod, flow) in enumerate(zip(flow_mods, flows)): + for i, (flow_mod, flow, owner, command) in enumerate(zip(flow_mods, flows, owners, commands)): try: - self._send_flow_mod(switch, flow_mod) + self._send_flow_mod(switch, flow_mod, owner) if send_barrier and i == len(flow_mods) - 1: self._send_barrier_request(switch, flow_mods) except SwitchNotConnectedError: @@ -765,16 +778,17 @@ def _send_flow_mods( self._add_flow_mod_sent( flow_mod.header.xid, flow, - build_command_from_flow_mod(flow_mod), + command, + owner ) self._send_napp_event(switch, flow, "pending") return flow_mods - def _add_flow_mod_sent(self, xid, flow, command): + def _add_flow_mod_sent(self, xid, flow, command, owner): """Add the flow mod to the list of flow mods sent.""" if len(self._flow_mods_sent) >= self._flow_mods_sent_max_size: self._flow_mods_sent.popitem(last=False) - self._flow_mods_sent[xid] = (flow, command) + self._flow_mods_sent[xid] = (flow, command, owner) def _add_barrier_request(self, dpid, barrier_xid, flow_mods): """Add a barrier request.""" @@ -804,7 +818,11 @@ def _send_barrier_request(self, switch, flow_mods): ) self.controller.buffers.msg_out.put(event) - def _send_flow_mod(self, switch, flow_mod): + def _send_flow_mod(self, switch, flow_mod, owner=None): + owner = owner or "no_owner" + if not self.pacer.is_configured(f"send_flow_mod.{owner}"): + owner = "no_owner" + self.pacer.hit(f"send_flow_mod.{owner}", switch.dpid) self.pacer.hit("send_flow_mod", switch.dpid) if not switch.is_connected(): raise SwitchNotConnectedError( @@ -852,7 +870,7 @@ def _send_openflow_connection_error(self, event): switch = event.content["destination"].switch flow = event.message try: - _, error_command = self._flow_mods_sent[event.message.header.xid] + _, error_command, _ = self._flow_mods_sent[event.message.header.xid] except KeyError: error_command = "unknown" error_kwargs = { @@ -885,7 +903,7 @@ def handle_errors(self, event): return xid = message.header.xid.value try: - flow, error_command = self._flow_mods_sent[xid] + flow, error_command, _ = self._flow_mods_sent[xid] except KeyError: pass else: diff --git a/settings.py b/settings.py index 5d62866..990c194 100644 --- a/settings.py +++ b/settings.py @@ -28,4 +28,12 @@ "pace": "100/second", "strategy": "fixed_window", }, + "send_flow_mod.no_owner": { + "pace": "50/second", + "strategy": "fixed_window", + }, + "send_flow_mod.mef_eline": { + "pace": "50/second", + "strategy": "fixed_window", + }, } From d226df00023352a6597e4dc7337499252548a33c Mon Sep 17 00:00:00 2001 From: David Ramirez Date: Wed, 10 Jul 2024 18:47:38 +0000 Subject: [PATCH 05/12] Remove commands list from _install_flows --- main.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/main.py b/main.py index 62f69bc..d75e8bf 100644 --- a/main.py +++ b/main.py @@ -713,7 +713,7 @@ def _install_flows( reraise_conn: True to reraise switch connection errors send_barrier: True to send barrier_request """ - flow_mods, flows, flow_dicts, owners, commands = [], [], [], [], [] + flow_mods, flows, flow_dicts, owners = [], [], [], [], [] for switch in switches: serializer = FlowFactory.get_class(switch, Flow04) flows_list = flows_dict.get("flows", []) @@ -731,7 +731,6 @@ def _install_flows( flow_mods.append(flow_mod) flows.append(flow) owners.append(flow_dict.get('owner')) - commands.append(command) flow_dicts.append( {**{"flow": flow_dict}, **{"flow_id": flow.id, "switch": switch.id}} ) @@ -759,14 +758,13 @@ def _send_flow_mods( flow_mods, flows, owners, - commands, reraise_conn=True, send_barrier=ENABLE_BARRIER_REQUEST, owner=None ): """Send FlowMod (and BarrierRequest) given a list of flow_dicts to switches.""" for switch in switches: - for i, (flow_mod, flow, owner, command) in enumerate(zip(flow_mods, flows, owners, commands)): + for i, (flow_mod, flow, owner) in enumerate(zip(flow_mods, flows, owners)): try: self._send_flow_mod(switch, flow_mod, owner) if send_barrier and i == len(flow_mods) - 1: @@ -778,7 +776,7 @@ def _send_flow_mods( self._add_flow_mod_sent( flow_mod.header.xid, flow, - command, + build_command_from_flow_mod(flow_mod), owner ) self._send_napp_event(switch, flow, "pending") From 7adff500bcb10a5c08f338a4e40c5c6f40110ee4 Mon Sep 17 00:00:00 2001 From: David Ramirez Date: Wed, 10 Jul 2024 18:49:45 +0000 Subject: [PATCH 06/12] Simplified ownership passing pipeline --- main.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/main.py b/main.py index d75e8bf..8bbd231 100644 --- a/main.py +++ b/main.py @@ -730,9 +730,9 @@ def _install_flows( flow_mod.pack() flow_mods.append(flow_mod) flows.append(flow) - owners.append(flow_dict.get('owner')) + owners.append(flow_dict.get('owner', 'no_owner')) flow_dicts.append( - {**{"flow": flow_dict}, **{"flow_id": flow.id, "switch": switch.id}} + {"flow": flow_dict, "flow_id": flow.id, "switch": switch.id} ) if save and command == "add": self.flow_controller.upsert_flows( @@ -747,7 +747,6 @@ def _install_flows( flow_mods, flows, owners, - commands, reraise_conn, send_barrier ) @@ -816,12 +815,8 @@ def _send_barrier_request(self, switch, flow_mods): ) self.controller.buffers.msg_out.put(event) - def _send_flow_mod(self, switch, flow_mod, owner=None): - owner = owner or "no_owner" - if not self.pacer.is_configured(f"send_flow_mod.{owner}"): - owner = "no_owner" + def _send_flow_mod(self, switch, flow_mod, owner): self.pacer.hit(f"send_flow_mod.{owner}", switch.dpid) - self.pacer.hit("send_flow_mod", switch.dpid) if not switch.is_connected(): raise SwitchNotConnectedError( f"switch {switch.id} isn't connected", flow_mod From 565d8c1a2cacb44ad888e670ce5dd36cbf61b8ad Mon Sep 17 00:00:00 2001 From: David Ramirez Date: Wed, 10 Jul 2024 18:54:48 +0000 Subject: [PATCH 07/12] Updated settings to include defaults for of_multi_table and telemetry_int --- settings.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/settings.py b/settings.py index 990c194..9eee9d2 100644 --- a/settings.py +++ b/settings.py @@ -23,17 +23,28 @@ whichever longest network convergence FlowMods operations that your network has. """ +# Rate limits for sending flow mods this can be set per NApp, +# and the pacing is per DPID when in the context of that NApp +# The NApp that the flow pertains to is determined via the `owner` attribute of the flow. ACTION_PACES = { - "send_flow_mod": { + "send_flow_mod.no_owner": { "pace": "100/second", "strategy": "fixed_window", }, - "send_flow_mod.no_owner": { - "pace": "50/second", + "send_flow_mod.mef_eline": { + "pace": "100/second", "strategy": "fixed_window", }, - "send_flow_mod.mef_eline": { - "pace": "50/second", + "send_flow_mod.of_multi_table": { + "pace": "100/second", + "strategy": "fixed_window", + }, + "send_flow_mod.of_multi_table": { + "pace": "100/second", + "strategy": "fixed_window", + }, + "send_flow_mod.telemetry_int": { + "pace": "100/second", "strategy": "fixed_window", }, } From 6a72a147abfc55877815ffb9ab2522d55bec346e Mon Sep 17 00:00:00 2001 From: David Ramirez Date: Mon, 15 Jul 2024 13:42:58 +0000 Subject: [PATCH 08/12] Update tests to add in ownership information. --- main.py | 14 ++++-------- settings.py | 4 ---- tests/integration/test_main.py | 10 ++++----- tests/unit/test_main.py | 39 +++++++++++++++++++++------------- 4 files changed, 33 insertions(+), 34 deletions(-) diff --git a/main.py b/main.py index 8bbd231..98beb31 100644 --- a/main.py +++ b/main.py @@ -713,7 +713,7 @@ def _install_flows( reraise_conn: True to reraise switch connection errors send_barrier: True to send barrier_request """ - flow_mods, flows, flow_dicts, owners = [], [], [], [], [] + flow_mods, flows, flow_dicts, owners = [], [], [], [] for switch in switches: serializer = FlowFactory.get_class(switch, Flow04) flows_list = flows_dict.get("flows", []) @@ -730,7 +730,7 @@ def _install_flows( flow_mod.pack() flow_mods.append(flow_mod) flows.append(flow) - owners.append(flow_dict.get('owner', 'no_owner')) + owners.append(flow_dict.get("owner", "no_owner")) flow_dicts.append( {"flow": flow_dict, "flow_id": flow.id, "switch": switch.id} ) @@ -743,12 +743,7 @@ def _install_flows( flow_dicts, {switch.id: switch for switch in switches} ) self._send_flow_mods( - switches, - flow_mods, - flows, - owners, - reraise_conn, - send_barrier + switches, flow_mods, flows, owners, reraise_conn, send_barrier ) def _send_flow_mods( @@ -759,7 +754,6 @@ def _send_flow_mods( owners, reraise_conn=True, send_barrier=ENABLE_BARRIER_REQUEST, - owner=None ): """Send FlowMod (and BarrierRequest) given a list of flow_dicts to switches.""" for switch in switches: @@ -776,7 +770,7 @@ def _send_flow_mods( flow_mod.header.xid, flow, build_command_from_flow_mod(flow_mod), - owner + owner, ) self._send_napp_event(switch, flow, "pending") return flow_mods diff --git a/settings.py b/settings.py index 9eee9d2..c8baf9d 100644 --- a/settings.py +++ b/settings.py @@ -39,10 +39,6 @@ "pace": "100/second", "strategy": "fixed_window", }, - "send_flow_mod.of_multi_table": { - "pace": "100/second", - "strategy": "fixed_window", - }, "send_flow_mod.telemetry_int": { "pace": "100/second", "strategy": "fixed_window", diff --git a/tests/integration/test_main.py b/tests/integration/test_main.py index aeca7b9..94beb7d 100644 --- a/tests/integration/test_main.py +++ b/tests/integration/test_main.py @@ -29,10 +29,10 @@ def test_add_flow_mod_sent_ok(self): xid = "12345" command = "add" initial_len = len(self.napp._flow_mods_sent) - self.napp._add_flow_mod_sent(xid, flow, command) + self.napp._add_flow_mod_sent(xid, flow, command, "no_owner") assert len(self.napp._flow_mods_sent) == initial_len + 1 - assert self.napp._flow_mods_sent.get(xid, None) == (flow, command) + assert self.napp._flow_mods_sent.get(xid, None) == (flow, command, "no_owner") def test_add_flow_mod_sent_overlimit(self): self.napp._flow_mods_sent_max_size = 5 @@ -41,12 +41,12 @@ def test_add_flow_mod_sent_overlimit(self): while len(self.napp._flow_mods_sent) < 5: xid += "1" flow = Mock() - self.napp._add_flow_mod_sent(xid, flow, command) + self.napp._add_flow_mod_sent(xid, flow, command, "no_owner") xid = "90876" flow = Mock() initial_len = len(self.napp._flow_mods_sent) - self.napp._add_flow_mod_sent(xid, flow, command) + self.napp._add_flow_mod_sent(xid, flow, command, "no_owner") assert len(self.napp._flow_mods_sent) == initial_len - assert self.napp._flow_mods_sent.get(xid, None) == (flow, command) + assert self.napp._flow_mods_sent.get(xid, None) == (flow, command, "no_owner") diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 7ecc430..8ec4472 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -485,14 +485,21 @@ def test_install_flows(self, *args): serializer.from_dict.return_value = flow mock_flow_factory.return_value = serializer - flows_dict = {"flows": [MagicMock(), MagicMock()]} + flows_dict = { + "flows": [ + MagicMock(get=lambda x, y=None: y), + MagicMock(get=lambda x, y=None: y), + ] + } switches = [self.switch_01] self.napp._install_flows("add", flows_dict, switches) - mock_send_flow_mod.assert_called_with(self.switch_01, flow_mod) + mock_send_flow_mod.assert_called_with(self.switch_01, flow_mod, "no_owner") assert mock_send_flow_mod.call_count == len(flows_dict["flows"]) assert mock_send_barrier_request.call_count == 1 - mock_add_flow_mod_sent.assert_called_with(flow_mod.header.xid, flow, "add") + mock_add_flow_mod_sent.assert_called_with( + flow_mod.header.xid, flow, "add", "no_owner" + ) mock_send_napp_event.assert_called_with(self.switch_01, flow, "pending") self.napp.flow_controller.upsert_flows.assert_called() @@ -519,13 +526,13 @@ def test_install_flows_with_delete_strict(self, *args): serializer.from_dict.return_value = flow mock_flow_factory.return_value = serializer - flows_dict = {"flows": [MagicMock()]} + flows_dict = {"flows": [MagicMock(get=lambda x, y=None: y)]} switches = [self.switch_01] self.napp._install_flows("delete_strict", flows_dict, switches) - mock_send_flow_mod.assert_called_with(self.switch_01, flow_mod) + mock_send_flow_mod.assert_called_with(self.switch_01, flow_mod, "no_owner") mock_add_flow_mod_sent.assert_called_with( - flow_mod.header.xid, flow, "delete_strict" + flow_mod.header.xid, flow, "delete_strict", "no_owner" ) mock_send_napp_event.assert_called_with(self.switch_01, flow, "pending") mock_send_barrier_request.assert_called() @@ -658,9 +665,9 @@ def test_add_flow_mod_sent(self): xid = 0 flow = MagicMock() - self.napp._add_flow_mod_sent(xid, flow, "add") + self.napp._add_flow_mod_sent(xid, flow, "add", "no_owner") - assert self.napp._flow_mods_sent[xid] == (flow, "add") + assert self.napp._flow_mods_sent[xid] == (flow, "add", "no_owner") def test_send_flow_mod(self): """Test _send_flow_mod method.""" @@ -669,7 +676,7 @@ def test_send_flow_mod(self): switch = get_switch_mock("00:00:00:00:00:00:00:01", 0x04) flow_mod = MagicMock() - self.napp._send_flow_mod(switch, flow_mod) + self.napp._send_flow_mod(switch, flow_mod, "no_owner") mock_buffers_put.assert_called() @@ -681,7 +688,7 @@ def test_send_flow_mod_error(self, mock_buffers_put): flow_mod = MagicMock() with pytest.raises(SwitchNotConnectedError): - self.napp._send_flow_mod(switch, flow_mod) + self.napp._send_flow_mod(switch, flow_mod, "no_owner") mock_buffers_put.assert_not_called() @@ -704,7 +711,7 @@ def test_handle_errors(self, mock_send_napp_event): flow.id = "1" flow.as_dict.return_value = {} flow.cookie = 0 - self.napp._flow_mods_sent[0] = (flow, "add") + self.napp._flow_mods_sent[0] = (flow, "add", "no_owner") switch = get_switch_mock("00:00:00:00:00:00:00:01") switch.connection = get_connection_mock( @@ -1068,7 +1075,9 @@ def test_on_ofpt_barrier_reply(self, mock_publish): barrier_xid = list(self.napp._pending_barrier_reply[switch.id].keys())[-1] for flow_mod in flow_mods: - self.napp._add_flow_mod_sent(flow_mod.header.xid, flow_mod, "add") + self.napp._add_flow_mod_sent( + flow_mod.header.xid, flow_mod, "add", "no_owner" + ) event = MagicMock() event.message.header.xid = barrier_xid @@ -1139,7 +1148,7 @@ def test_retry_on_openflow_connection_error(self, mock_barrier): flow.as_dict.return_value = {} flow.header.message_type = Type.OFPT_FLOW_MOD flow.xid = 1 - self.napp._flow_mods_sent[flow.xid] = (flow, "add") + self.napp._flow_mods_sent[flow.xid] = (flow, "add", "no_owner") mock_ev = MagicMock() mock_ev.message = flow @@ -1169,7 +1178,7 @@ def test_retry_on_openflow_connection_error_send_event(self, mock_send): flow.as_dict.return_value = {} flow.header.message_type = Type.OFPT_FLOW_MOD flow.xid = 1 - self.napp._flow_mods_sent[flow.xid] = (flow, "add") + self.napp._flow_mods_sent[flow.xid] = (flow, "add", "no_owner") # make sure a previous retry has stored executed self.napp._flow_mods_retry_count[flow.xid] = (3, now(), 10) @@ -1223,7 +1232,7 @@ def test_send_openflow_connection_error(self, mock_send): flow = MagicMock() flow.as_dict.return_value = {} flow.xid = 1 - self.napp._flow_mods_sent[flow.xid] = (flow, "add") + self.napp._flow_mods_sent[flow.xid] = (flow, "add", "no_owner") mock_ev = MagicMock() mock_ev.event.content = {"destination": switch} From 86c307bd06b1ec19616c8de2111b7885cd0fd174 Mon Sep 17 00:00:00 2001 From: David Ramirez Date: Mon, 22 Jul 2024 12:39:22 -0400 Subject: [PATCH 09/12] Added code to handle uncofigured owner for pacing --- main.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index 98beb31..d5010ea 100644 --- a/main.py +++ b/main.py @@ -810,7 +810,10 @@ def _send_barrier_request(self, switch, flow_mods): self.controller.buffers.msg_out.put(event) def _send_flow_mod(self, switch, flow_mod, owner): - self.pacer.hit(f"send_flow_mod.{owner}", switch.dpid) + owner_pacer = f"send_flow_mod.{owner}" + if not self.pacer.is_configured(owner_pacer): + owner_pacer = "send_flow_mod.no_owner" + self.pacer.hit(owner_pacer, switch.dpid) if not switch.is_connected(): raise SwitchNotConnectedError( f"switch {switch.id} isn't connected", flow_mod From fe0faf8f0b2716403d326f695d0944013972d884 Mon Sep 17 00:00:00 2001 From: David Ramirez Date: Mon, 22 Jul 2024 12:39:49 -0400 Subject: [PATCH 10/12] Added default pace for of_lldp flowmods --- settings.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/settings.py b/settings.py index c8baf9d..8169fda 100644 --- a/settings.py +++ b/settings.py @@ -43,4 +43,8 @@ "pace": "100/second", "strategy": "fixed_window", }, + "send_flow_mod.of_lldp": { + "pace": "100/second", + "strategy": "fixed_window", + }, } From d7481a69ebe6d7d073f5d7ddc5ee1a82325931a0 Mon Sep 17 00:00:00 2001 From: David Ramirez Date: Thu, 25 Jul 2024 14:48:42 -0400 Subject: [PATCH 11/12] Updated changelog --- CHANGELOG.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6cbefa4..8aadb8a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,6 +7,11 @@ file. [UNRELEASED] - Under development ******************************** +Added +===== + +- Added pacing for sending flow mods. Pacing can be configured in ``ACTION_PACES`` in the NApp's ``setings.py`` file. + [2023.2.0] - 2024-02-16 *********************** From 5d4b8f5f4abefda761d27f86a4a5df6d87f14df7 Mon Sep 17 00:00:00 2001 From: David Ramirez Date: Mon, 29 Jul 2024 11:19:25 -0400 Subject: [PATCH 12/12] Add pacing for coloring to settings --- settings.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/settings.py b/settings.py index 8169fda..42a36fe 100644 --- a/settings.py +++ b/settings.py @@ -47,4 +47,8 @@ "pace": "100/second", "strategy": "fixed_window", }, + "send_flow_mod.coloring": { + "pace": "100/second", + "strategy": "fixed_window", + }, }