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] Overlapping stored flows #44

Merged
merged 15 commits into from
Dec 8, 2021
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ Removed
Security
========

[5.1.0] - 2021-11.08
********************

Added
=====
- Augmented ``_add_flow_store`` to overwrite overlapping flows

[5.0.0] - 2021-11.05
********************

Expand Down
2 changes: 1 addition & 1 deletion kytos.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"username": "kytos",
"name": "flow_manager",
"description": "Manage switches' flows through a REST API.",
"version": "5.0.0",
"version": "5.1.0",
"napp_dependencies": ["kytos/of_core", "kytos/storehouse"],
"license": "MIT",
"url": "https://github.com/kytos/flow_manager.git",
Expand Down
19 changes: 15 additions & 4 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,9 +314,8 @@ def _del_matched_flows_store(self, flow_dict, switch):
del stored_flows_box["id"]
self.stored_flows = deepcopy(stored_flows_box)

# pylint: disable=fixme
def _add_flow_store(self, flow_dict, switch):
"""Try to add a flow dict in the store."""
"""Try to add a flow dict in the store idempotently."""
installed_flow = {}
installed_flow["flow"] = flow_dict
installed_flow["created_at"] = now().strftime("%Y-%m-%dT%H:%M:%S")
Expand All @@ -326,11 +325,23 @@ def _add_flow_store(self, flow_dict, switch):
if switch.id not in stored_flows_box:
stored_flows_box[switch.id] = OrderedDict()

# TODO handle issue 23 (overlapping FlowMod add)
if not stored_flows_box[switch.id].get(cookie):
stored_flows_box[switch.id][cookie] = [installed_flow]
else:
stored_flows_box[switch.id][cookie].append(installed_flow)
version = switch.connection.protocol.version
stored_flows = stored_flows_box[switch.id].get(cookie, [])
for i, stored_flow in enumerate(stored_flows):
if all(
(
stored_flow["flow"].get("priority", 0)
== flow_dict.get("priority", 0),
match_flow(flow_dict, version, stored_flow["flow"]),
italovalcy marked this conversation as resolved.
Show resolved Hide resolved
)
):
stored_flows_box[switch.id][cookie][i] = installed_flow
break
else:
stored_flows_box[switch.id][cookie].append(installed_flow)

stored_flows_box["id"] = "flow_persistence"
self.storehouse.save_flow(stored_flows_box)
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
BASE_ENV = Path(os.environ.get("VIRTUAL_ENV", "/"))

NAPP_NAME = "flow_manager"
NAPP_VERSION = "5.0.0"
NAPP_VERSION = "5.1.0"

# Kytos var folder
VAR_PATH = BASE_ENV / "var" / "lib" / "kytos"
Expand Down
86 changes: 86 additions & 0 deletions tests/unit/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
from unittest import TestCase
from unittest.mock import MagicMock, patch

# pylint: disable=too-many-lines,fixme
# TODO split this test suite in smaller ones

from kytos.core.helpers import now
from kytos.lib.helpers import (
get_connection_mock,
Expand Down Expand Up @@ -405,6 +408,89 @@ def test_check_switch_consistency_add(self, *args):
self.napp.check_switch_consistency(switch)
mock_install_flows.assert_called()

@patch("napps.kytos.flow_manager.storehouse.StoreHouse.save_flow")
def test_add_overlapping_flow(self, *args):
italovalcy marked this conversation as resolved.
Show resolved Hide resolved
"""Test add an overlapping flow."""
(_,) = args
dpid = "00:00:00:00:00:00:00:01"
switch = get_switch_mock(dpid, 0x04)
switch.id = dpid
cookie = 0x20

self.napp.stored_flows = {
dpid: {
cookie: [
{
"flow": {
"priority": 10,
"cookie": 84114904,
"match": {
"ipv4_src": "192.168.1.1",
"ipv4_dst": "192.168.0.2",
},
"actions": [{"action_type": "output", "port": 2}],
}
}
]
}
}

new_actions = [{"action_type": "output", "port": 3}]
flow_dict = {
"priority": 10,
"cookie": cookie,
"match": {
"ipv4_src": "192.168.1.1",
"ipv4_dst": "192.168.0.2",
},
"actions": new_actions,
}

self.napp._add_flow_store(flow_dict, switch)
assert len(self.napp.stored_flows[dpid]) == 1
assert self.napp.stored_flows[dpid][0x20][0]["flow"]["actions"] == new_actions

@patch("napps.kytos.flow_manager.storehouse.StoreHouse.save_flow")
def test_add_overlapping_flow_diff_priority(self, *args):
"""Test that a different priority wouldn't overlap."""
(_,) = args
dpid = "00:00:00:00:00:00:00:01"
switch = get_switch_mock(dpid, 0x04)
switch.id = dpid

cookie = 0x20
self.napp.stored_flows = {
dpid: {
cookie: [
{
"flow": {
"priority": 10,
"cookie": 84114904,
"match": {
"ipv4_src": "192.168.1.1",
"ipv4_dst": "192.168.0.2",
},
"actions": [{"action_type": "output", "port": 2}],
}
}
]
}
}

new_actions = [{"action_type": "output", "port": 3}]
flow_dict = {
"priority": 11,
"cookie": cookie,
"match": {
"ipv4_src": "192.168.1.1",
"ipv4_dst": "192.168.0.2",
},
"actions": new_actions,
}

self.napp._add_flow_store(flow_dict, switch)
assert len(self.napp.stored_flows[dpid][cookie]) == 2

@patch("napps.kytos.flow_manager.main.Main._install_flows")
@patch("napps.kytos.flow_manager.main.FlowFactory.get_class")
def test_check_switch_flow_not_missing(self, *args):
Expand Down