Skip to content

Commit 17965bf

Browse files
committed
test: add DHCPv4 client hostname resend regression test
Signed-off-by: Ejub Sabic <ejub1946@outlook.com>
1 parent 82ffb0f commit 17965bf

7 files changed

Lines changed: 198 additions & 0 deletions

File tree

test/case/dhcp/Readme.adoc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Tests verifying DHCPv4/DHCPv6 client and server functionality:
77
- DHCPv4 client with default gateway assignment
88
- DHCPv4 client with static route configuration
99
- DHCPv4 client hostname management and priority
10+
- DHCPv4 client hostname option is resent after system hostname changes
1011
- Basic DHCPv6 client operation with address assignment
1112
- DHCPv6 client with prefix delegation (IA_PD)
1213
- DHCPv6 SLAAC/RA (Stateless)
@@ -30,6 +31,10 @@ include::client_hostname/Readme.adoc[]
3031

3132
<<<
3233

34+
include::client_hostname_resend/Readme.adoc[]
35+
36+
<<<
37+
3338
include::client6_basic/Readme.adoc[]
3439

3540
<<<
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test.adoc
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
=== DHCP Hostname Resend
2+
3+
ifdef::topdoc[:imagesdir: {topdoc}../../test/case/dhcp/client_hostname_resend]
4+
5+
==== Description
6+
7+
Verify that updating the system hostname regenerates the DHCP client
8+
Finit service file so subsequent DHCP requests advertise the current
9+
hostname (option 12, RFC 2132).
10+
11+
Regression test for a bug where the DHCP client callback only reacts
12+
on diffs in infix-dhcp-client, so a standalone change of
13+
ietf-system:system/hostname leaves the previously written
14+
/etc/finit.d/available/dhcp-client-<iface>.conf untouched and the
15+
running udhcpc keeps announcing the old name.
16+
17+
==== Topology
18+
19+
image::topology.svg[DHCP Hostname Resend topology, align=center, scaledwidth=75%]
20+
21+
==== Sequence
22+
23+
. Set up topology and attach to target DUT
24+
. Configure initial hostname '{HOSTNM_A}'
25+
. Enable DHCP client sending hostname option
26+
. Verify Finit service carries hostname '{HOSTNM_A}'
27+
. Update system hostname to '{HOSTNM_B}'
28+
. Verify Finit service is regenerated with '{HOSTNM_B}'
29+
30+
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#!/usr/bin/env python3
2+
"""DHCP Hostname Resend
3+
4+
Verify that updating the system hostname regenerates the DHCP client
5+
Finit service file so subsequent DHCP requests advertise the current
6+
hostname (option 12, RFC 2132).
7+
8+
Regression test for a bug where the DHCP client callback only reacts
9+
on diffs in infix-dhcp-client, so a standalone change of
10+
ietf-system:system/hostname leaves the previously written
11+
/etc/finit.d/available/dhcp-client-<iface>.conf untouched and the
12+
running udhcpc keeps announcing the old name.
13+
14+
"""
15+
16+
import infamy
17+
from infamy.util import until
18+
19+
20+
def finit_conf(ssh, ifname):
21+
"""Return the contents of the generated DHCP client Finit service file."""
22+
path = f"/etc/finit.d/available/dhcp-client-{ifname}.conf"
23+
cmd = ssh.runsh(f"cat {path} 2>/dev/null")
24+
return cmd.stdout
25+
26+
27+
def udhcpc_cmdline(ssh, ifname):
28+
"""Return the NUL-separated argv of the running udhcpc for ifname."""
29+
pidfile = f"/run/dhcp-client-{ifname}.pid"
30+
cmd = ssh.runsh(
31+
f"p=$(cat {pidfile} 2>/dev/null); "
32+
f"[ -n \"$p\" ] && tr '\\0' ' ' < /proc/$p/cmdline"
33+
)
34+
return cmd.stdout
35+
36+
37+
def running_hostname(ssh, ifname):
38+
"""Extract the hostname udhcpc is currently announcing (-x hostname:<name>)."""
39+
for tok in udhcpc_cmdline(ssh, ifname).split():
40+
if tok.startswith("hostname:"):
41+
return tok.split(":", 1)[1]
42+
return None
43+
44+
45+
with infamy.Test() as test:
46+
HOSTNM_A = "infix-resend-a"
47+
HOSTNM_B = "infix-resend-b"
48+
49+
with test.step("Set up topology and attach to target DUT"):
50+
env = infamy.Env()
51+
client = env.attach("client", "mgmt")
52+
clissh = env.attach("client", "mgmt", "ssh")
53+
_, port = env.ltop.xlate("client", "mgmt")
54+
55+
with test.step(f"Configure initial hostname '{HOSTNM_A}'"):
56+
client.put_config_dict("ietf-system", {
57+
"system": {
58+
"hostname": HOSTNM_A
59+
}
60+
})
61+
until(lambda: client.get_data("/ietf-system:system")
62+
.get("system", {}).get("hostname") == HOSTNM_A)
63+
64+
with test.step("Enable DHCP client sending hostname option"):
65+
client.put_config_dict("ietf-interfaces", {
66+
"interfaces": {
67+
"interface": [{
68+
"name": port,
69+
"ipv4": {
70+
"infix-dhcp-client:dhcp": {
71+
"option": [
72+
{"id": "vendor-class", "value": "infamy"},
73+
{"id": "hostname", "value": "auto"},
74+
{"id": "netmask"},
75+
{"id": "router"}
76+
]
77+
}
78+
}
79+
}]
80+
}
81+
})
82+
83+
with test.step(f"Verify running udhcpc announces hostname '{HOSTNM_A}'"):
84+
until(lambda: running_hostname(clissh, port) == HOSTNM_A)
85+
86+
with test.step(f"Update system hostname to '{HOSTNM_B}'"):
87+
client.put_config_dict("ietf-system", {
88+
"system": {
89+
"hostname": HOSTNM_B
90+
}
91+
})
92+
until(lambda: client.get_data("/ietf-system:system")
93+
.get("system", {}).get("hostname") == HOSTNM_B)
94+
95+
with test.step(f"Verify running udhcpc announces hostname '{HOSTNM_B}'"):
96+
try:
97+
until(lambda: running_hostname(clissh, port) == HOSTNM_B,
98+
attempts=15)
99+
except Exception:
100+
cur = running_hostname(clissh, port)
101+
print(f"udhcpc still announcing hostname '{cur}', expected '{HOSTNM_B}'")
102+
test.fail()
103+
104+
test.succeed()
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
graph "1x1" {
2+
layout="neato";
3+
overlap="false";
4+
esep="+100";
5+
6+
node [shape=record, fontname="DejaVu Sans Mono, Book"];
7+
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"];
8+
9+
host [
10+
label="host | { <mgmt> mgmt }",
11+
pos="0,20!",
12+
requires="controller",
13+
];
14+
15+
client [
16+
label="{ <mgmt> mgmt } | client",
17+
pos="200,20!",
18+
requires="infix",
19+
];
20+
21+
host:mgmt -- client:mgmt [requires="mgmt", color=lightgrey]
22+
}
Lines changed: 33 additions & 0 deletions
Loading

test/case/dhcp/dhcp_client.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
- name: DHCP Hostname Priority
1515
case: client_hostname/test.py
1616

17+
- name: DHCP Hostname Resend
18+
case: client_hostname_resend/test.py
19+
1720
- name: DHCPv6 Basic
1821
case: client6_basic/test.py
1922

0 commit comments

Comments
 (0)