Skip to content

Commit a100f15

Browse files
authored
[dhcp_server] add config dhcp server range (#17741)
* add range related function and ut
1 parent 00fa567 commit a100f15

File tree

4 files changed

+217
-15
lines changed

4 files changed

+217
-15
lines changed

dockers/docker-dhcp-server/cli-plugin-tests/mock_config_db.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@
4949
"ips": "100.1.1.10,10.1.1.11"
5050
},
5151
"DHCP_SERVER_IPV4_PORT|Vlan100|Ethernet7": {
52-
"ranges": "range1,range2"
52+
"ranges": "range1,range3"
5353
},
5454
"DHCP_SERVER_IPV4_PORT|Vlan200|Ethernet8": {
55-
"ranges": "range3,range4"
55+
"ranges": "range1,range4"
5656
},
5757
"DHCP_SERVER_IPV4_PORT|Ethernet9": {
5858
"ranges": "range5,range6"

dockers/docker-dhcp-server/cli-plugin-tests/test_config_dhcp_server.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,3 +240,133 @@ def test_config_dhcp_server_ipv4_update_wrong_netmask(self, mock_db):
240240
result = runner.invoke(dhcp_server.dhcp_server.commands["ipv4"].commands["update"], ["Vlan100", "--netmask=255.255.254"], obj=db)
241241
assert result.exit_code == 2, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info)
242242

243+
def test_config_dhcp_server_ipv4_range_add(self, mock_db):
244+
expected_value = {
245+
"range": "10.10.10.10,10.10.10.11"
246+
}
247+
runner = CliRunner()
248+
db = clicommon.Db()
249+
db.db = mock_db
250+
result = runner.invoke(dhcp_server.dhcp_server.commands["ipv4"].commands["range"].commands["add"], \
251+
["range4", "10.10.10.10", "10.10.10.11"], obj=db)
252+
assert result.exit_code == 0, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info)
253+
assert mock_db.get_all("CONFIG_DB", "DHCP_SERVER_IPV4_RANGE|range4") == expected_value
254+
255+
def test_config_dhcp_server_ipv4_range_add_existing(self, mock_db):
256+
runner = CliRunner()
257+
db = clicommon.Db()
258+
db.db = mock_db
259+
result = runner.invoke(dhcp_server.dhcp_server.commands["ipv4"].commands["range"].commands["add"], \
260+
["range1", "10.10.10.10", "10.10.10.11"], obj=db)
261+
assert result.exit_code == 2, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info)
262+
263+
def test_config_dhcp_server_ipv4_range_add_single_ip(self, mock_db):
264+
expected_value = {
265+
"range": "10.10.10.10,10.10.10.10"
266+
}
267+
runner = CliRunner()
268+
db = clicommon.Db()
269+
db.db = mock_db
270+
result = runner.invoke(dhcp_server.dhcp_server.commands["ipv4"].commands["range"].commands["add"], \
271+
["range4", "10.10.10.10"], obj=db)
272+
assert result.exit_code == 0, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info)
273+
assert mock_db.get_all("CONFIG_DB", "DHCP_SERVER_IPV4_RANGE|range4") == expected_value
274+
275+
def test_config_dhcp_server_ipv4_range_add_wrong_ip(self, mock_db):
276+
runner = CliRunner()
277+
db = clicommon.Db()
278+
db.db = mock_db
279+
result = runner.invoke(dhcp_server.dhcp_server.commands["ipv4"].commands["range"].commands["add"], \
280+
["range4", "10.10.10"], obj=db)
281+
assert result.exit_code == 2, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info)
282+
283+
def test_config_dhcp_server_ipv4_range_add_wrong_order(self, mock_db):
284+
runner = CliRunner()
285+
db = clicommon.Db()
286+
db.db = mock_db
287+
result = runner.invoke(dhcp_server.dhcp_server.commands["ipv4"].commands["range"].commands["add"], \
288+
["range4", "10.10.10.10", "10.10.10.9"], obj=db)
289+
assert result.exit_code == 2, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info)
290+
291+
def test_config_dhcp_server_ipv4_range_update(self, mock_db):
292+
expected_value = {
293+
"range": "10.10.10.10,10.10.10.11"
294+
}
295+
runner = CliRunner()
296+
db = clicommon.Db()
297+
db.db = mock_db
298+
result = runner.invoke(dhcp_server.dhcp_server.commands["ipv4"].commands["range"].commands["update"], \
299+
["range1", "10.10.10.10", "10.10.10.11"], obj=db)
300+
assert result.exit_code == 0, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info)
301+
assert mock_db.get_all("CONFIG_DB", "DHCP_SERVER_IPV4_RANGE|range1") == expected_value
302+
303+
def test_config_dhcp_server_ipv4_range_update_nonexisting(self, mock_db):
304+
runner = CliRunner()
305+
db = clicommon.Db()
306+
db.db = mock_db
307+
result = runner.invoke(dhcp_server.dhcp_server.commands["ipv4"].commands["range"].commands["update"], \
308+
["range4", "10.10.10.10", "10.10.10.11"], obj=db)
309+
assert result.exit_code == 2, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info)
310+
311+
def test_config_dhcp_server_ipv4_range_update_single_ip(self, mock_db):
312+
expected_value = {
313+
"range": "10.10.10.10,10.10.10.10"
314+
}
315+
runner = CliRunner()
316+
db = clicommon.Db()
317+
db.db = mock_db
318+
result = runner.invoke(dhcp_server.dhcp_server.commands["ipv4"].commands["range"].commands["update"], \
319+
["range1", "10.10.10.10"], obj=db)
320+
assert result.exit_code == 0, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info)
321+
assert mock_db.get_all("CONFIG_DB", "DHCP_SERVER_IPV4_RANGE|range1") == expected_value
322+
323+
def test_config_dhcp_server_ipv4_range_update_wrong_ip(self, mock_db):
324+
runner = CliRunner()
325+
db = clicommon.Db()
326+
db.db = mock_db
327+
result = runner.invoke(dhcp_server.dhcp_server.commands["ipv4"].commands["range"].commands["update"], \
328+
["range1", "10.10.10"], obj=db)
329+
assert result.exit_code == 2, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info)
330+
331+
def test_config_dhcp_server_ipv4_range_add_wrong_order(self, mock_db):
332+
runner = CliRunner()
333+
db = clicommon.Db()
334+
db.db = mock_db
335+
result = runner.invoke(dhcp_server.dhcp_server.commands["ipv4"].commands["range"].commands["update"], \
336+
["range1", "10.10.10.10", "10.10.10.9"], obj=db)
337+
assert result.exit_code == 2, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info)
338+
339+
def test_config_dhcp_server_ipv4_range_delete(self, mock_db):
340+
runner = CliRunner()
341+
db = clicommon.Db()
342+
db.db = mock_db
343+
result = runner.invoke(dhcp_server.dhcp_server.commands["ipv4"].commands["range"].commands["del"], \
344+
["range2"], obj=db)
345+
assert result.exit_code == 0, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info)
346+
assert mock_db.exists("CONFIG_DB", "DHCP_SERVER_IPV4_RANGE|range2") == False
347+
348+
def test_config_dhcp_server_ipv4_range_delete_nonexisting(self, mock_db):
349+
runner = CliRunner()
350+
db = clicommon.Db()
351+
db.db = mock_db
352+
result = runner.invoke(dhcp_server.dhcp_server.commands["ipv4"].commands["range"].commands["del"], \
353+
["range4"], obj=db)
354+
assert result.exit_code == 2, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info)
355+
356+
def test_config_dhcp_server_ipv4_range_delete_referenced(self, mock_db):
357+
runner = CliRunner()
358+
db = clicommon.Db()
359+
db.db = mock_db
360+
result = runner.invoke(dhcp_server.dhcp_server.commands["ipv4"].commands["range"].commands["del"], \
361+
["range1"], obj=db)
362+
assert result.exit_code == 2, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info)
363+
364+
def test_config_dhcp_server_ipv4_range_delete_referenced_force(self, mock_db):
365+
runner = CliRunner()
366+
db = clicommon.Db()
367+
db.db = mock_db
368+
result = runner.invoke(dhcp_server.dhcp_server.commands["ipv4"].commands["range"].commands["del"], \
369+
["range1", "--force"], obj=db)
370+
assert result.exit_code == 0, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info)
371+
assert mock_db.exists("CONFIG_DB", "DHCP_SERVER_IPV4_RANGE|range1") == False
372+

dockers/docker-dhcp-server/cli-plugin-tests/test_show_dhcp_server.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -215,9 +215,9 @@ def test_show_dhcp_server_ipv4_port_without_intf(self, mock_db):
215215
| | 10.1.1.11 |
216216
+-------------------+------------+
217217
| Vlan100|Ethernet7 | range1 |
218-
| | range2 |
218+
| | range3 |
219219
+-------------------+------------+
220-
| Vlan200|Ethernet8 | range3 |
220+
| Vlan200|Ethernet8 | range1 |
221221
| | range4 |
222222
+-------------------+------------+
223223
| Ethernet9 | range5 |
@@ -237,7 +237,7 @@ def test_show_dhcp_server_ipv4_port_with_port(self, mock_db):
237237
| Interface | Bind |
238238
+===================+========+
239239
| Vlan100|Ethernet7 | range1 |
240-
| | range2 |
240+
| | range3 |
241241
+-------------------+--------+
242242
"""
243243
runner = CliRunner()
@@ -256,7 +256,7 @@ def test_show_dhcp_server_ipv4_port_with_vlan(self, mock_db):
256256
| | 10.1.1.11 |
257257
+-------------------+------------+
258258
| Vlan100|Ethernet7 | range1 |
259-
| | range2 |
259+
| | range3 |
260260
+-------------------+------------+
261261
"""
262262
runner = CliRunner()
@@ -271,7 +271,7 @@ def test_show_dhcp_server_ipv4_port_with_port_and_vlan(self, mock_db):
271271
+-------------------+--------+
272272
| Interface | Bind |
273273
+===================+========+
274-
| Vlan200|Ethernet8 | range3 |
274+
| Vlan200|Ethernet8 | range1 |
275275
| | range4 |
276276
+-------------------+--------+
277277
"""

dockers/docker-dhcp-server/cli/config/plugins/dhcp_server.py

Lines changed: 80 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
SUPPORT_TYPE = ["binary", "boolean", "ipv4-address", "string", "uint8", "uint16", "uint32"]
99

1010

11-
def validate_str_type(type, value):
11+
def validate_str_type(type_, value):
1212
"""
1313
To validate whether type is consistent with string value
1414
Args:
@@ -20,27 +20,27 @@ def validate_str_type(type, value):
2020
"""
2121
if not isinstance(value, str):
2222
return False
23-
if type not in SUPPORT_TYPE:
23+
if type_ not in SUPPORT_TYPE:
2424
return False
25-
if type == "string":
25+
if type_ == "string":
2626
return True
27-
if type == "binary":
27+
if type_ == "binary":
2828
if len(value) == 0 or len(value) % 2 != 0:
2929
return False
3030
return all(c in set(string.hexdigits) for c in value)
31-
if type == "boolean":
31+
if type_ == "boolean":
3232
return value in ["true", "false"]
33-
if type == "ipv4-address":
33+
if type_ == "ipv4-address":
3434
try:
3535
if len(value.split(".")) != 4:
3636
return False
3737
return ipaddress.ip_address(value).version == 4
3838
except ValueError:
3939
return False
40-
if type.startswith("uint"):
40+
if type_.startswith("uint"):
4141
if not value.isdigit():
4242
return False
43-
length = int("".join([c for c in type if c.isdigit()]))
43+
length = int("".join([c for c in type_ if c.isdigit()]))
4444
return 0 <= int(value) <= int(pow(2, length)) - 1
4545
return False
4646

@@ -186,6 +186,78 @@ def dhcp_server_ipv4_disable(db, dhcp_interface):
186186
ctx.fail("Failed to disable, dhcp interface {} does not exist".format(dhcp_interface))
187187

188188

189+
@dhcp_server_ipv4.group(cls=clicommon.AliasedGroup, name="range")
190+
def dhcp_server_ipv4_range():
191+
pass
192+
193+
194+
def count_ipv4(start, end):
195+
ip1 = int(ipaddress.IPv4Address(start))
196+
ip2 = int(ipaddress.IPv4Address(end))
197+
return ip2 - ip1 + 1
198+
199+
200+
@dhcp_server_ipv4_range.command(name="add")
201+
@click.argument("range_name", required=True)
202+
@click.argument("ip_start", required=True)
203+
@click.argument("ip_end", required=False)
204+
@clicommon.pass_db
205+
def dhcp_server_ipv4_range_add(db, range_name, ip_start, ip_end):
206+
ctx = click.get_current_context()
207+
if not ip_end:
208+
ip_end = ip_start
209+
if not validate_str_type("ipv4-address", ip_start) or not validate_str_type("ipv4-address", ip_end):
210+
ctx.fail("ip_start or ip_end is not valid ipv4 address")
211+
if count_ipv4(ip_start, ip_end) < 1:
212+
ctx.fail("range value is illegal")
213+
dbconn = db.db
214+
key = "DHCP_SERVER_IPV4_RANGE|" + range_name
215+
if dbconn.exists("CONFIG_DB", key):
216+
ctx.fail("Range {} already exist".format(range_name))
217+
else:
218+
dbconn.hmset("CONFIG_DB", key, {"range": ip_start + "," + ip_end})
219+
220+
221+
@dhcp_server_ipv4_range.command(name="update")
222+
@click.argument("range_name", required=True)
223+
@click.argument("ip_start", required=True)
224+
@click.argument("ip_end", required=False)
225+
@clicommon.pass_db
226+
def dhcp_server_ipv4_range_update(db, range_name, ip_start, ip_end):
227+
ctx = click.get_current_context()
228+
if not ip_end:
229+
ip_end = ip_start
230+
if not validate_str_type("ipv4-address", ip_start) or not validate_str_type("ipv4-address", ip_end):
231+
ctx.fail("ip_start or ip_end is not valid ipv4 address")
232+
if count_ipv4(ip_start, ip_end) < 1:
233+
ctx.fail("range value is illegal")
234+
dbconn = db.db
235+
key = "DHCP_SERVER_IPV4_RANGE|" + range_name
236+
if dbconn.exists("CONFIG_DB", key):
237+
dbconn.set("CONFIG_DB", key, "range", ip_start + "," + ip_end)
238+
else:
239+
ctx.fail("Range {} does not exist, cannot update".format(range_name))
240+
241+
242+
@dhcp_server_ipv4_range.command(name="del")
243+
@click.argument("range_name", required=True)
244+
@click.option("--force", required=False, default=False, is_flag=True)
245+
@clicommon.pass_db
246+
def dhcp_sever_ipv4_range_del(db, range_name, force):
247+
ctx = click.get_current_context()
248+
dbconn = db.db
249+
key = "DHCP_SERVER_IPV4_RANGE|" + range_name
250+
if dbconn.exists("CONFIG_DB", key):
251+
if not force:
252+
for port in dbconn.keys("CONFIG_DB", "DHCP_SERVER_IPV4_PORT*"):
253+
ranges = dbconn.get("CONFIG_DB", port, "ranges")
254+
if ranges and range_name in ranges.split(","):
255+
ctx.fail("Range {} is referenced in {}, cannot delete, add --force to bypass".format(range_name, port))
256+
dbconn.delete("CONFIG_DB", key)
257+
else:
258+
ctx.fail("Range {} does not exist, cannot delete".format(range_name))
259+
260+
189261
def register(cli):
190262
# cli.add_command(dhcp_server)
191263
pass

0 commit comments

Comments
 (0)