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

Replace deprecated and obsoleted IPDB with NDB #758

Closed
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3a22664
Replace deprecated `IPDB` with `NDB`
gab-arrobo Feb 5, 2024
16cc53a
Merge branch 'master' into replace-deprecated-ipdb
gab-arrobo Feb 7, 2024
5664b7b
Update test file
gab-arrobo Feb 7, 2024
1a4a15f
Apply suggestions from code review
gab-arrobo Feb 7, 2024
526d168
Use get method
gab-arrobo Feb 7, 2024
e656a2e
Merge branch 'master' into replace-deprecated-ipdb
gab-arrobo Feb 7, 2024
7c696da
Merge branch 'omec-project:master' into replace-deprecated-ipdb
gab-arrobo Feb 7, 2024
16f68db
No need to register callbacks because NDB continuously monitors the s…
gab-arrobo Feb 7, 2024
55de595
Merge branch 'omec-project:master' into replace-deprecated-ipdb
gab-arrobo Feb 15, 2024
2e05e4f
Merge branch 'omec-project:master' into replace-deprecated-ipdb
gab-arrobo Feb 22, 2024
094e687
Partially address issue with registrer_handler
gab-arrobo Feb 22, 2024
56b95ba
Update route control script. Still need to address issue with `core` …
gab-arrobo Feb 22, 2024
0982fb7
Merge branch 'omec-project:master' into replace-deprecated-ipdb
gab-arrobo Feb 27, 2024
25b1a1d
Merge branch 'omec-project:master' into replace-deprecated-ipdb
gab-arrobo Mar 5, 2024
1da2ecd
Merge branch 'omec-project:master' into replace-deprecated-ipdb
gab-arrobo Mar 6, 2024
a390989
Merge branch 'omec-project:master' into replace-deprecated-ipdb
gab-arrobo Mar 8, 2024
824ed04
Merge branch 'omec-project:master' into replace-deprecated-ipdb
gab-arrobo Mar 9, 2024
6b7b490
Merge branch 'omec-project:master' into replace-deprecated-ipdb
gab-arrobo Mar 11, 2024
33c5cbd
Merge branch 'omec-project:master' into replace-deprecated-ipdb
gab-arrobo Mar 21, 2024
5ff4806
Merge branch 'omec-project:master' into replace-deprecated-ipdb
gab-arrobo Mar 26, 2024
011dd93
Merge branch 'omec-project:master' into replace-deprecated-ipdb
gab-arrobo Apr 11, 2024
a31d6fa
Merge branch 'omec-project:master' into replace-deprecated-ipdb
gab-arrobo Apr 24, 2024
68e9893
Merge branch 'omec-project:master' into replace-deprecated-ipdb
gab-arrobo Apr 26, 2024
690531e
Merge branch 'omec-project:master' into replace-deprecated-ipdb
gab-arrobo May 2, 2024
748412e
Merge branch 'omec-project:master' into replace-deprecated-ipdb
gab-arrobo May 28, 2024
c035ec0
Merge branch 'omec-project:master' into replace-deprecated-ipdb
gab-arrobo Jun 4, 2024
5cde3ce
Merge branch 'omec-project:master' into replace-deprecated-ipdb
gab-arrobo Jun 12, 2024
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
39 changes: 19 additions & 20 deletions conf/route_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from typing import Dict, List, Optional, Tuple

from pybess.bess import *
from pyroute2 import IPDB, IPRoute
from pyroute2 import NDB, IPRoute
from scapy.all import ICMP, IP, send

LOG_FORMAT = "%(asctime)s %(levelname)s %(message)s"
Expand Down Expand Up @@ -308,7 +308,7 @@ class RouteController:
def __init__(
self,
bess_controller: BessController,
ipdb: IPDB,
ndb: NDB,
ipr: IPRoute,
interfaces: List[str],
):
Expand All @@ -319,7 +319,7 @@ def __init__(
bess_controller (BessController):
Controller for BESS (Berkeley Extensible Software Switch).
route_parser (RouteEntryParser): Parser for route entries.
ipdb (IPDB): IP database to manage IP configurations.
ndb (NDB): database to manage Network configurations.
ipr (IPRoute): IP routing control object.

Attributes:
Expand All @@ -336,7 +336,7 @@ def __init__(

self._lock = Lock()

self._ipdb = ipdb
self._ndb = ndb
self._ipr = ipr
self._bess_controller = bess_controller
self._ping_missing_thread = Thread(
Expand All @@ -348,7 +348,7 @@ def __init__(
def register_callbacks(self) -> None:
"""Register callback function."""
logger.info("Registering netlink event listener callback...")
self._event_callback = self._ipdb.register_callback(
self._event_callback = self._ndb.register_callback(
self._netlink_event_listener
)

Expand All @@ -373,7 +373,7 @@ def add_new_route_entry(self, route_entry: RouteEntry) -> None:
Args:
route_entry (RouteEntry): The route entry.
"""
if not (next_hop_mac := fetch_mac(self._ipdb, route_entry.next_hop_ip)):
if not (next_hop_mac := fetch_mac(self._ndb, route_entry.next_hop_ip)):
logger.info(
"mac address of the next hop %s is not stored in ARP table. Probing...",
route_entry.next_hop_ip,
Expand Down Expand Up @@ -603,12 +603,12 @@ def _get_gate_idx(self, route_entry: RouteEntry, module_name: str) -> int:
return self._module_gate_count_cache[module_name]

def _netlink_event_listener(
self, ipdb: IPDB, netlink_message: dict, action: str
self, ndb: NDB, netlink_message: dict, action: str
) -> None:
"""Listens for netlink events and handles them.

Args:
ipdb (IPDB): The IPDB object.
ndb (NDB): The NDB object.
netlink_message (dict): The netlink message.
action (str): The action.
"""
Expand All @@ -629,7 +629,7 @@ def _netlink_event_listener(
def cleanup(self, number: int) -> None:
"""Unregisters the netlink event listener callback and exits."""
logger.info("Received: %i Exiting", number)
self._ipdb.unregister_callback(self._event_callback)
self._ndb.unregister_callback(self._event_callback)
logger.info("Unregistered netlink event listener callback")
sys.exit()

Expand Down Expand Up @@ -667,7 +667,7 @@ def _parse_route_entry_msg(self, route_entry: dict) -> Optional[RouteEntry]:
if not attr_dict.get(KEY_INTERFACE):
return None
interface_index = int(attr_dict.get(KEY_INTERFACE))
interface = self._ipdb.interfaces[interface_index].ifname
interface = self._ndb.interfaces[interface_index]["ifname"]
gab-arrobo marked this conversation as resolved.
Show resolved Hide resolved
if interface not in self._interfaces:
return None

Expand Down Expand Up @@ -736,26 +736,25 @@ def send_ping(neighbor_ip):
send(IP(dst=neighbor_ip) / ICMP())


def fetch_mac(ipdb: IPDB, target_ip: str) -> Optional[str]:
"""Fetches the MAC address of the target IP from the ARP table using IPDB.
def fetch_mac(ndb: NDB, target_ip: str) -> Optional[str]:
"""Fetches the MAC address of the target IP from the ARP table using NDB.

Args:
ipdb (IPDB): The IPDB object.
ndb (NDB): The NDB object.
target_ip (str): The target IP address.

Returns:
Optional[str]: The MAC address of the target IP.
"""
neighbors = ipdb.nl.get_neighbours(dst=target_ip)
neighbors = ndb.neighbours.dump()
for neighbor in neighbors:
attrs = dict(neighbor["attrs"])
if attrs.get(KEY_NETWORK_LAYER_DEST_ADDR, "") == target_ip:
if neighbor["dst"] == target_ip:
logger.info(
"Mac address found for %s, Mac: %s",
target_ip,
attrs.get(KEY_LINK_LAYER_ADDRESS, ""),
neighbor["lladdr"],
)
return attrs.get(KEY_LINK_LAYER_ADDRESS, "")
return neighbor["lladdr"]
logger.info("Mac address not found for %s", target_ip)
return None

Expand Down Expand Up @@ -800,11 +799,11 @@ def register_signal_handlers(route_controller: RouteController) -> None:
if __name__ == "__main__":
interface_arg, ip_arg, port_arg = parse_args()
ipr = IPRoute()
ipdb = IPDB()
ndb = NDB()
bess_controller = BessController(ip_arg, port_arg)
route_controller = RouteController(
bess_controller=bess_controller,
ipdb=ipdb,
ndb=ndb,
ipr=ipr,
interfaces=interface_arg,
)
Expand Down
114 changes: 78 additions & 36 deletions conf/test_route_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import unittest
from unittest.mock import MagicMock, Mock, patch

from pyroute2 import IPDB # type: ignore[import]
from pyroute2 import NDB
gab-arrobo marked this conversation as resolved.
Show resolved Hide resolved

sys.modules["pybess.bess"] = MagicMock()

Expand Down Expand Up @@ -50,7 +50,7 @@ def test_given_valid_ip_when_validate_ipv4_then_returns_true(self):
def test_given_invalid_ip_when_validate_ipv4_then_returns_false(self):
self.assertFalse(validate_ipv4("192.168.300.1"))

def test_given_invalid_ip_when_validate_ipv4_then_returns_false(self):
def test_given_invalid_ip_when_validate_ipv6_then_returns_false(self):
self.assertFalse(validate_ipv4("::1"))
self.assertFalse(validate_ipv4(""))

Expand All @@ -67,27 +67,39 @@ def test_given_valid_mac_when_mac_to_hex_then_return_hex_string_representation(
self.assertEqual(mac_to_hex("00:1a:2b:3c:4d:5e"), "001A2B3C4D5E")

def test_given_known_destination_when_fetch_mac_then_returns_mac(self):
ipdb = IPDB()
ipdb.nl.get_neighbours = lambda dst, **kwargs: [
{"attrs": [("NDA_DST", dst), ("NDA_LLADDR", "00:1a:2b:3c:4d:5e")]}
]
self.assertEqual(fetch_mac(ipdb, "192.168.1.1"), "00:1a:2b:3c:4d:5e")
ndb = Mock()
kwargs = {
"ifindex": 1,
"dst": "192.168.1.1",
"lladdr": "00:1a:2b:3c:4d:5e"
}
neighbour = ndb.neighbours.create(**kwargs)
neighbour.commit()
gab-arrobo marked this conversation as resolved.
Show resolved Hide resolved
ndb.neighbours.dump.return_value = [kwargs]
self.assertEqual(fetch_mac(ndb, "192.168.1.1"), "00:1a:2b:3c:4d:5e")

def test_given_unkonw_destination_when_fetch_mac_then_returns_none(self):
ipdb = IPDB()
ipdb.nl.get_neighbours = lambda dst, **kwargs: []
self.assertIsNone(fetch_mac(ipdb, "192.168.1.1"))
ndb = Mock()
kwargs = {
"ifindex": 1,
"dst": "192.168.1.1",
"lladdr": None
}
neighbour = ndb.neighbours.create(**kwargs)
neighbour.commit()
gab-arrobo marked this conversation as resolved.
Show resolved Hide resolved
ndb.neighbours.dump.return_value = [kwargs]
self.assertIsNone(fetch_mac(ndb, "192.168.1.1"))


class TestRouteController(unittest.TestCase):
def setUp(self):
self.mock_bess_controller = Mock(BessControllerMock)
self.ipdb = Mock()
self.ndb = Mock()
self.ipr = Mock()
interfaces = ["access", "core"]
self.route_controller = RouteController(
self.mock_bess_controller,
self.ipdb,
self.ndb,
interfaces=interfaces,
ipr=self.ipr,
)
Expand All @@ -105,9 +117,14 @@ def add_route_entry(
mock_fetch_mac,
) -> None:
"""Adds a new route entry using the route controller."""
self.ipdb.nl.get_neighbours = lambda dst, **kwargs: [
{"attrs": [("NDA_DST", dst), ("NDA_LLADDR", "00:1a:2b:3c:4d:5e")]}
]
kwargs = {
"ifindex": 1,
"dst": "192.168.1.1",
"lladdr": "00:1a:2b:3c:4d:5e"
}
neighbour = self.ndb.neighbours.create(**kwargs)
neighbour.commit()
gab-arrobo marked this conversation as resolved.
Show resolved Hide resolved
self.ndb.neighbours.dump.return_value = [kwargs]
mock_get_update_module_name.return_value = "merge_module"
mock_get_route_module_name.return_value = "route_module"
mock_get_merge_module_name.return_value = "update_module"
Expand All @@ -116,7 +133,7 @@ def add_route_entry(
return route_entry

def test_given_valid_route_message_when_parse_message_then_parses_message(self):
self.ipdb.interfaces = {2: Mock(ifname="core")}
self.ndb.interfaces = {2: {"ifname": "core"}}
example_route_entry = {
"family": 2,
"dst_len": 24,
Expand All @@ -141,13 +158,13 @@ def test_given_valid_route_message_when_parse_message_then_parses_message(self):
self.assertIsInstance(result, RouteEntry)
self.assertEqual(result.dest_prefix, "192.168.1.0")
self.assertEqual(result.next_hop_ip, "172.31.48.1")
self.assertEqual(result.interface, self.ipdb.interfaces[2].ifname)
self.assertEqual(result.interface, self.ndb.interfaces[2]["ifname"])
self.assertEqual(result.prefix_len, 24)

def test_given_valid_route_message_and_dst_len_is_zero_when_parse_message_then_parses_message_as_default_route(
self,
):
self.ipdb.interfaces = {2: Mock(ifname="core")}
self.ndb.interfaces = {2: {"ifname": "core"}}
example_route_entry = {
"family": 2,
"dst_len": 0,
Expand All @@ -171,11 +188,11 @@ def test_given_valid_route_message_and_dst_len_is_zero_when_parse_message_then_p
self.assertIsInstance(result, RouteEntry)
self.assertEqual(result.dest_prefix, "0.0.0.0")
self.assertEqual(result.next_hop_ip, "172.31.48.1")
self.assertEqual(result.interface, self.ipdb.interfaces[2].ifname)
self.assertEqual(result.interface, self.ndb.interfaces[2]["ifname"])
self.assertEqual(result.prefix_len, 0)

def test_given_invalid_route_message_when_parse_message_then_returns_none(self):
self.ipdb.interfaces = {2: Mock(ifname="not the needed interface")}
self.ndb.interfaces = {2: {"ifname": "not the needed interface"}}
example_route_entry = {
"family": 2,
"flags": 0,
Expand All @@ -202,7 +219,14 @@ def test_given_new_route_when_add_new_route_entry_and_mac_not_known_then_destina
self,
mock_send_ping,
):
self.ipdb.nl.get_neighbours = lambda dst, **kwargs: []
kwargs = {
"ifindex": 1,
"dst": "192.168.1.1",
"lladdr": None
}
neighbour = self.ndb.neighbours.create(**kwargs)
neighbour.commit()
gab-arrobo marked this conversation as resolved.
Show resolved Hide resolved
self.ndb.neighbours.dump.return_value = [kwargs]
route_entry = RouteEntry(
next_hop_ip="1.2.3.4",
interface="random_interface",
Expand All @@ -215,9 +239,14 @@ def test_given_new_route_when_add_new_route_entry_and_mac_not_known_then_destina
def test_given_valid_new_route_when_add_new_route_entry_and_mac_known_then_route_is_added_in_bess(
self,
gab-arrobo marked this conversation as resolved.
Show resolved Hide resolved
):
self.ipdb.nl.get_neighbours = lambda dst, **kwargs: [
{"attrs": [("NDA_DST", dst), ("NDA_LLADDR", "00:1a:2b:3c:4d:5e")]}
]
kwargs = {
"ifindex": 1,
"dst": "192.168.1.1",
"lladdr": "00:1a:2b:3c:4d:5e"
}
neighbour = self.ndb.neighbours.create(**kwargs)
neighbour.commit()
gab-arrobo marked this conversation as resolved.
Show resolved Hide resolved
self.ndb.neighbours.dump.return_value = [kwargs]
mock_routes = [{"event": "RTM_NEWROUTE"}, {"event": "OTHER_ACTION"}]
self.ipr.get_routes.return_value = mock_routes
route_entry = RouteEntry(
Expand All @@ -227,14 +256,20 @@ def test_given_valid_new_route_when_add_new_route_entry_and_mac_known_then_route
prefix_len=24,
)
self.route_controller.add_new_route_entry(route_entry)
self.mock_bess_controller.add_route_to_module()
self.mock_bess_controller.add_route_to_module.assert_called_once()

def test_given_valid_new_route_when_add_new_route_entry_and_mac_known_and_neighbor_not_known_then_update_module_is_created_and_modules_are_linked(
self,
gab-arrobo marked this conversation as resolved.
Show resolved Hide resolved
):
self.ipdb.nl.get_neighbours = lambda dst, **kwargs: [
{"attrs": [("NDA_DST", dst), ("NDA_LLADDR", "00:1a:2b:3c:4d:5e")]}
]
kwargs = {
"ifindex": 1,
"dst": "192.168.1.1",
gab-arrobo marked this conversation as resolved.
Show resolved Hide resolved
"lladdr": "00:1a:2b:3c:4d:5e"
}
neighbour = self.ndb.neighbours.create(**kwargs)
neighbour.commit()
gab-arrobo marked this conversation as resolved.
Show resolved Hide resolved
self.ndb.neighbours.dump.return_value = [kwargs]
mock_routes = [{"event": "RTM_NEWROUTE"}, {"event": "OTHER_ACTION"}]
self.ipr.get_routes.return_value = mock_routes
route_entry = RouteEntry(
Expand All @@ -244,7 +279,9 @@ def test_given_valid_new_route_when_add_new_route_entry_and_mac_known_and_neighb
prefix_len=24,
)
self.route_controller.add_new_route_entry(route_entry)
self.mock_bess_controller.create_module()
self.mock_bess_controller.create_module.assert_called()
self.mock_bess_controller.link_modules()
self.mock_bess_controller.link_modules.assert_called()
gab-arrobo marked this conversation as resolved.
Show resolved Hide resolved

@patch.object(RouteController, "add_new_route_entry")
Expand All @@ -265,7 +302,7 @@ def test_given_new_route_when_bootstrap_routes_then_add_new_entry_is_called(
{"event": "OTHER_ACTION"},
]
self.ipr.get_routes.return_value = mock_routes
self.ipdb.interfaces = {2: Mock(ifname="core")}
self.ndb.interfaces = {2: {"ifname": "core"}}
valid_route_entry = RouteEntry(
next_hop_ip="1.2.3.4",
interface="core",
Expand Down Expand Up @@ -322,7 +359,7 @@ def test_given_new_route_and_invalid_message_when_bootstrap_routes_then_add_new_
def test_given_netlink_message_when_rtm_newroute_event_then_add_new_route_entry_is_called(
self, mock_add_new_route_entry
):
self.ipdb.interfaces = {2: Mock(ifname="core")}
self.ndb.interfaces = {2: {"ifname": "core"}}
example_route_entry = {
"family": 2,
"dst_len": 24,
Expand All @@ -344,7 +381,7 @@ def test_given_netlink_message_when_rtm_newroute_event_then_add_new_route_entry_
"event": "RTM_NEWROUTE",
}
self.route_controller._netlink_event_listener(
self.ipdb, example_route_entry, "RTM_NEWROUTE"
self.ndb, example_route_entry, "RTM_NEWROUTE"
)
mock_add_new_route_entry.assert_called()

Expand Down Expand Up @@ -398,7 +435,7 @@ def test_given_existing_neighbor_and_route_count_is_one_when_delete_route_entry_
def test_given_netlink_message_when_rtm_delroute_event_then_delete_route_entry_is_called(
self, mock_delete_route_entry
):
self.ipdb.interfaces = {2: Mock(ifname="core")}
self.ndb.interfaces = {2: {"ifname": "core"}}
example_route_entry = {
"family": 2,
"dst_len": 24,
Expand All @@ -420,7 +457,7 @@ def test_given_netlink_message_when_rtm_delroute_event_then_delete_route_entry_i
"event": "RTM_DELROUTE",
}
self.route_controller._netlink_event_listener(
self.ipdb, example_route_entry, "RTM_DELROUTE"
self.ndb, example_route_entry, "RTM_DELROUTE"
)
mock_delete_route_entry.assert_called()

Expand All @@ -429,9 +466,14 @@ def test_given_new_neighbor_in_unresolved_when_add_unresolved_new_neighbor_then_
self,
_,
):
self.ipdb.nl.get_neighbours = lambda dst, **kwargs: [
{"attrs": [("NDA_DST", dst), ("NDA_LLADDR", "00:1a:2b:3c:4d:5e")]}
]
kwargs = {
"ifindex": 1,
"dst": "192.168.1.1",
"lladdr": "00:1a:2b:3c:4d:5e"
}
neighbour = self.ndb.neighbours.create(**kwargs)
neighbour.commit()
gab-arrobo marked this conversation as resolved.
Show resolved Hide resolved
self.ndb.neighbours.dump.return_value = [kwargs]
mock_netlink_msg = {
"attrs": {
"NDA_DST": "1.2.3.4",
Expand All @@ -455,6 +497,6 @@ def test_given_netlink_message_when_rtm_newneigh_event_then_add_unresolved_new_n
self, mock_add_unresolved_new_neighbor
):
self.route_controller._netlink_event_listener(
self.ipdb, "new neighbour message", "RTM_NEWNEIGH"
self.ndb, "new neighbour message", "RTM_NEWNEIGH"
)
mock_add_unresolved_new_neighbor.assert_called()
Loading
Loading