Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix][cherry-pick] Handle error when switch is not connected #49

Merged
merged 9 commits into from
Dec 8, 2021
Merged
4 changes: 4 additions & 0 deletions exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
39 changes: 25 additions & 14 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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:
italovalcy marked this conversation as resolved.
Show resolved Hide resolved
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":
return jsonify({"response": "switch is disabled."}), 404
viniarck marked this conversation as resolved.
Show resolved Hide resolved

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.
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 24 additions & 0 deletions tests/unit/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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."""
Expand Down