Skip to content

Commit 9cb83c2

Browse files
cshivashgitcodor07
andauthored
[dhcp_relay] CLI: sonic dhcp relay agent for IPv4 (sonic-net#23242)
Why I did it Currently SONiC uses the 'isc-dhcp-relay' package to allow DHCP relay functionality on IPv4 networks. With this PR we are adding sonic dhcp relay agent for IPv4 as described in this HLD(sonic-net/SONiC#1938). Docker changes PR : sonic-net#22486 How I did it Included new commands to support dhcpv4_relay in dockers/docker-dhcp-relay folder. Corresponding cli-plugin-tests are included. How to verify it Configure sonic DHCPv4 agent as described in the feature HLD(sonic-net/SONiC#1938) Test it with real client/server with IPv4 or use the sonic-mgmt suite Co-authored-by: Prashant Srivastava <[email protected]>
1 parent bfbd830 commit 9cb83c2

File tree

11 files changed

+1901
-128
lines changed

11 files changed

+1901
-128
lines changed

dockers/docker-dhcp-relay/cli-plugin-tests/conftest.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,57 @@ def mock_cfgdb():
99
'VLAN': {
1010
'Vlan1000': {
1111
'dhcp_servers': ['192.0.0.1']
12-
}
12+
},
13+
'Vlan200': {},
14+
},
15+
'DHCPV4_RELAY': {
16+
'Vlan1000': {
17+
'dhcpv4_servers': ['192.0.0.1']
18+
},
19+
'Vlan200': {},
1320
},
1421
'DHCP_RELAY': {
1522
'Vlan1000': {
1623
'dhcpv6_servers': ['fc02:2000::1']
1724
}
25+
},
26+
'DEVICE_METADATA': {
27+
'localhost': {
28+
'has_sonic_dhcpv4_relay' : "False"
29+
}
30+
},
31+
'VRF': {
32+
"default": {
33+
'VRF': 'default'
34+
},
35+
"VrfRED": {
36+
'VRF': 'VrfRED'
37+
},
38+
"VrfBLUE": {
39+
'VRF': 'VrfBLUE'
40+
}
41+
},
42+
'PORTCHANNEL_INTERFACE': {
43+
"PortChannel5": {},
44+
"PortChannel6": {},
45+
"PortChannel5|192.168.0.1/31": {},
46+
"PortChannel6|192.168.0.3/31": {}
47+
},
48+
'LOOPBACK_INTERFACE': {
49+
"Loopback0": {},
50+
"Loopback2": {},
51+
"Loopback3": {},
52+
"Loopback0|10.1.0.1/32": {},
53+
"Loopback2|10.1.0.1/32": {},
54+
"Loopback3|10.1.0.2/32": {}
55+
},
56+
'INTERFACE': {
57+
"Ethernet0": {},
58+
"Ethernet0|10.0.0.0/31": {},
59+
"Ethernet8": {},
60+
"Ethernet8|10.0.0.4/31": {},
61+
"Ethernet12": {},
62+
"Ethernet12|10.0.0.6/31": {},
1863
}
1964
}
2065

@@ -34,6 +79,10 @@ def set_entry(table, key, data):
3479
def get_keys(table):
3580
return CONFIG[table].keys()
3681

82+
def get_table(table):
83+
return CONFIG.get(table, {})
84+
85+
cfgdb.get_table = mock.Mock(side_effect=get_table)
3786
cfgdb.get_entry = mock.Mock(side_effect=get_entry)
3887
cfgdb.set_entry = mock.Mock(side_effect=set_entry)
3988
cfgdb.get_keys = mock.Mock(side_effect=get_keys)

dockers/docker-dhcp-relay/cli-plugin-tests/mock_config.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@
4242
"192.0.0.2"
4343
]
4444
}
45+
},
46+
"DHCPV4_RELAY": {
47+
"Vlan1000": {
48+
"dhcpv4_servers": [
49+
"192.0.0.1",
50+
"192.0.0.2"
51+
]
52+
}
4553
}
4654
}
4755
}
@@ -170,5 +178,38 @@
170178
}
171179
}
172180
}
181+
],
182+
[
183+
"ipv4_dhcp",
184+
{
185+
"config_db": {
186+
"DHCPV4_RELAY": {
187+
"Vlan1000": {
188+
"dhcpv4_servers": [
189+
"192.0.0.1",
190+
"192.0.0.2"
191+
],
192+
"source_interface": "Ethernet112",
193+
"link_selection": "enable",
194+
"server_vrf": "default",
195+
"vrf_selection": "enable"
196+
},
197+
"Vlan1001": {
198+
"vlanid": "1001",
199+
"dhcpv4_servers": [
200+
"192.0.0.3",
201+
"192.0.0.4"
202+
],
203+
"agent_relay_mode": "discard",
204+
"max_hop_count": "5"
205+
}
206+
},
207+
'DEVICE_METADATA': {
208+
'localhost': {
209+
'has_sonic_dhcpv4_relay' : "False"
210+
}
211+
}
212+
}
213+
}
173214
]
174215
]

dockers/docker-dhcp-relay/cli-plugin-tests/test_clear_dhcp_relay.py

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,17 @@
1919
@pytest.fixture(scope="module")
2020
def patch_import_module():
2121
# We need to mock import module because clear_dhcp_relay.py has below import
22-
# dhcp6_relay = importlib.import_module('show.plugins.dhcp-relay')
22+
# dhcprelay = importlib.import_module('show.plugins.dhcp-relay')
2323
# When install current container, sonic-application-extension would move below file to destination in switch
2424
# Src: dockers/docker-dhcp-relay/cli/show/plugins/show_dhcp_relay.py
2525
# Dst: python-package-patch/show/plugins/dhcp-relay.py
2626
# The dst path doesn't exist in UT env, hence we need to mock it
27-
fake_dhcp6_relay = MagicMock()
27+
fake_dhcprelay = MagicMock()
2828

2929
with patch('importlib.import_module') as mock_import:
3030
def side_effect(name):
3131
if name == 'show.plugins.dhcp-relay':
32-
return fake_dhcp6_relay
32+
return fake_dhcprelay
3333
return original_import_module(name) # fallback
3434

3535
mock_import.side_effect = side_effect
@@ -54,7 +54,7 @@ def test_clear_dhcp_relay_ipv6_counter(interface, patch_import_module):
5454
gotten_interfaces = ["Ethernet0, Ethernet1"]
5555

5656
mock_counter = MagicMock()
57-
clear_dhcp_relay.dhcp6_relay.DHCPv6_Counter.return_value = mock_counter
57+
clear_dhcp_relay.dhcprelay.DHCPv6_Counter.return_value = mock_counter
5858
mock_counter.get_interface.return_value = gotten_interfaces
5959
clear_dhcp_relay.clear_dhcp_relay_ipv6_counter(interface)
6060
if interface:
@@ -319,3 +319,30 @@ def test_is_write_db_paused(patch_import_module):
319319
mock_db = MagicMock()
320320
clear_dhcp_relay.is_write_db_paused(mock_db, "table_name", "vlan_interface")
321321
mock_db.get.assert_called_once_with(ANY, "table_name|vlan_interface", "pause_write_to_db")
322+
323+
@pytest.mark.parametrize("direction, pkt_type, interface", [
324+
(None, None, None),
325+
("TX", "DISCOVER", "Vlan1000"),
326+
("RX", "OFFER", None),
327+
])
328+
def test_clear_dhcp_relay_ipv4_vlan_counter(patch_import_module, direction, pkt_type, interface):
329+
clear_dhcp_relay = patch_import_module
330+
mock_counter = MagicMock()
331+
clear_dhcp_relay.dhcprelay.DHCPv4_Counter.return_value = mock_counter
332+
clear_dhcp_relay.clear_dhcp_relay_ipv4_vlan_counter(direction, pkt_type, interface)
333+
mock_counter.clear_table.assert_called_once_with(direction, pkt_type, interface)
334+
335+
def test_dhcp4relay_clear_vlan_counters_command(patch_import_module):
336+
clear_dhcp_relay = patch_import_module
337+
runner = CliRunner()
338+
# Patch the actual clear function to verify call
339+
with patch.object(clear_dhcp_relay, "clear_dhcp_relay_ipv4_vlan_counter") as mock_clear:
340+
result = runner.invoke(clear_dhcp_relay.dhcp4relay_clear.commands['dhcp4relay-vlan-counters'], ['-d', 'TX', 'Vlan1000'])
341+
assert result.exit_code == 0
342+
343+
def test_register_adds_dhcp4relay_clear_vlan_counters(patch_import_module):
344+
clear_dhcp_relay = patch_import_module
345+
cli = MagicMock()
346+
clear_dhcp_relay.register(cli)
347+
# Check that dhcp4relay_clear_vlan_counters was registered as a command
348+
cli.add_command.assert_any_call(clear_dhcp_relay.dhcp4relay_clear_vlan_counters)

0 commit comments

Comments
 (0)