diff --git a/exceptions.py b/exceptions.py index b82b5983..aaa28bbc 100644 --- a/exceptions.py +++ b/exceptions.py @@ -3,3 +3,7 @@ class InvalidCommandError(Exception): """Command has an invalid value.""" + + +class SwitchNotConnectedError(Exception): + """Exception raised when a switch's connection isn't connected.""" diff --git a/main.py b/main.py index 98673f71..385efea4 100644 --- a/main.py +++ b/main.py @@ -14,12 +14,17 @@ from pyof.foundation.base import UBIntBase from pyof.v0x01.asynchronous.error_msg import BadActionCode from pyof.v0x01.common.phy_port import PortConfig -from werkzeug.exceptions import BadRequest, NotFound, UnsupportedMediaType +from werkzeug.exceptions import ( + BadRequest, + FailedDependency, + NotFound, + UnsupportedMediaType, +) from kytos.core import KytosEvent, KytosNApp, log, rest from kytos.core.helpers import get_time, listen_to, now -from .exceptions import InvalidCommandError +from .exceptions import InvalidCommandError, SwitchNotConnectedError from .settings import ( CONSISTENCY_COOKIE_IGNORED_RANGE, CONSISTENCY_TABLE_ID_IGNORED_RANGE, @@ -470,21 +475,25 @@ def _send_flow_mods_from_request(self, dpid, command, flows_dict=None): f"Send FlowMod from request dpid: {dpid} command: {command}" f" flows_dict: {flows_dict}" ) - if dpid: + try: + if not dpid: + self._install_flows( + command, flows_dict, self._get_all_switches_enabled() + ) + return jsonify({"response": "FlowMod Messages Sent"}), 202 + switch = self.controller.get_switch_by_dpid(dpid) if not switch: return jsonify({"response": "dpid not found."}), 404 - elif switch.is_enabled() is False: - if command == "delete": - self._install_flows(command, flows_dict, [switch]) - else: - return jsonify({"response": "switch is disabled."}), 404 - else: - self._install_flows(command, flows_dict, [switch]) - else: - self._install_flows(command, flows_dict, self._get_all_switches_enabled()) - return jsonify({"response": "FlowMod Messages Sent"}), 202 + if not switch.is_enabled() and command == "add": + raise NotFound("switch is disabled.") + + self._install_flows(command, flows_dict, [switch]) + return jsonify({"response": "FlowMod Messages Sent"}), 202 + + except SwitchNotConnectedError as error: + raise FailedDependency(str(error)) def _install_flows(self, command, flows_dict, switches=[], save=True): """Execute all procedures to install flows in the switches. @@ -523,8 +532,10 @@ def _add_flow_mod_sent(self, xid, flow, command): self._flow_mods_sent[xid] = (flow, command) def _send_flow_mod(self, switch, flow_mod): - event_name = "kytos/flow_manager.messages.out.ofpt_flow_mod" + if not switch.is_connected(): + raise SwitchNotConnectedError(f"switch {switch.id} isn't connected") + event_name = "kytos/flow_manager.messages.out.ofpt_flow_mod" content = {"destination": switch.connection, "message": flow_mod} event = KytosEvent(name=event_name, content=content) diff --git a/setup.cfg b/setup.cfg index b6c21ddb..05f155a0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,7 +6,7 @@ add-ignore = D105 [yala] radon mi args = --min C -pylint args = --disable==too-many-arguments,too-many-locals,too-few-public-methods,too-many-instance-attributes,no-else-return,dangerous-default-value,duplicate-code --ignored-modules=napps.kytos.topology +pylint args = --disable==too-many-arguments,too-many-locals,too-few-public-methods,too-many-instance-attributes,no-else-return,dangerous-default-value,duplicate-code,raise-missing-from --ignored-modules=napps.kytos.topology [flake8] max-line-length = 88 diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 79b738cd..de62e289 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -13,6 +13,7 @@ get_switch_mock, get_test_client, ) +from napps.kytos.flow_manager.exceptions import SwitchNotConnectedError # pylint: disable=protected-access, too-many-public-methods @@ -163,6 +164,17 @@ def test_rest_add_and_delete_with_dpi_fail(self, mock_install_flows): self.assertEqual(mock_install_flows.call_count, 0) + @patch("napps.kytos.flow_manager.main.Main._install_flows") + def test_rest_flow_mod_add_switch_not_connected(self, mock_install_flows): + """Test sending a flow mod when a swith isn't connected.""" + api = get_test_client(self.napp.controller, self.napp) + mock_install_flows.side_effect = SwitchNotConnectedError + + url = f"{self.API_URL}/v2/flows" + response = api.post(url, json={"flows": [{"priority": 25}]}) + + self.assertEqual(response.status_code, 424) + def test_get_all_switches_enabled(self): """Test _get_all_switches_enabled method.""" switches = self.napp._get_all_switches_enabled() @@ -278,6 +290,18 @@ def test_send_flow_mod(self, mock_buffers_put): mock_buffers_put.assert_called() + @patch("kytos.core.buffers.KytosEventBuffer.put") + def test_send_flow_mod_error(self, mock_buffers_put): + """Test _send_flow_mod method error.""" + switch = get_switch_mock("00:00:00:00:00:00:00:01", 0x04) + switch.is_connected = MagicMock(return_value=False) + flow_mod = MagicMock() + + with self.assertRaises(SwitchNotConnectedError): + self.napp._send_flow_mod(switch, flow_mod) + + mock_buffers_put.assert_not_called() + @patch("kytos.core.buffers.KytosEventBuffer.put") def test_send_napp_event(self, mock_buffers_put): """Test _send_napp_event method."""