Skip to content

Commit df1df83

Browse files
committed
test: new test, dhcp hostname priority test
Verify deterministic hostname management via /etc/hostname.d/, rules: 1. Configured hostname takes precedence over default 2. DHCP hostname takes precedence over configured 3. Hostname reverts when DHCP lease ends Signed-off-by: Joachim Wiberg <[email protected]>
1 parent 0d44b44 commit df1df83

File tree

7 files changed

+166
-3
lines changed

7 files changed

+166
-3
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test.adoc
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
=== DHCP Hostname Priority
2+
3+
ifdef::topdoc[:imagesdir: {topdoc}../../test/case/infix_dhcp/client_hostname]
4+
5+
==== Description
6+
7+
Verify deterministic hostname management: a DHCP acquired hostname takes
8+
precedence over a configured hostname. When a DHCP lease ends, or the
9+
hostname option is removed, the system should revert to the configured
10+
hostname.
11+
12+
==== Topology
13+
14+
image::topology.svg[DHCP Hostname Priority topology, align=center, scaledwidth=75%]
15+
16+
==== Sequence
17+
18+
. Set up topology and attach to target DUT
19+
. Configure static system hostname
20+
. Verify configured hostname is set
21+
. Enable DHCP client requesting hostname option
22+
. Verify DHCP hostname takes precedence
23+
. Drop hostname option from client request
24+
. Verify hostname reverts to configured value
25+
26+
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#!/usr/bin/env python3
2+
"""DHCP Hostname Priority
3+
4+
Verify deterministic hostname management: a DHCP acquired hostname takes
5+
precedence over a configured hostname. When a DHCP lease ends, or the
6+
hostname option is removed, the system should revert to the configured
7+
hostname.
8+
9+
"""
10+
11+
import infamy, infamy.dhcp
12+
from infamy.util import until
13+
14+
15+
def verify_hostname(node, expected):
16+
"""Verify operational hostname matches expected value"""
17+
data = node.get_data("/ietf-system:system")
18+
return data["system"]["hostname"] == expected
19+
20+
21+
with infamy.Test() as test:
22+
DHCP_HOSTNAME = "dhcp-assigned"
23+
CONF_HOSTNAME = "configured-host"
24+
25+
with test.step("Set up topology and attach to target DUT"):
26+
env = infamy.Env()
27+
client = env.attach("client", "mgmt")
28+
_, host = env.ltop.xlate("host", "mgmt")
29+
_, port = env.ltop.xlate("client", "mgmt")
30+
31+
with test.step("Configure static system hostname"):
32+
client.put_config_dict("ietf-system", {
33+
"system": {
34+
"hostname": CONF_HOSTNAME
35+
}
36+
})
37+
38+
with test.step("Verify configured hostname is set"):
39+
until(lambda: verify_hostname(client, CONF_HOSTNAME))
40+
41+
with infamy.IsolatedMacVlan(host, mode="private") as netns:
42+
netns.addip("10.0.0.1")
43+
with infamy.dhcp.Server(netns, ip="10.0.0.42", hostname=DHCP_HOSTNAME):
44+
with test.step("Enable DHCP client requesting hostname option"):
45+
client.put_config_dict("ietf-interfaces", {
46+
"interfaces": {
47+
"interface": [{
48+
"name": port,
49+
"ipv4": {
50+
"infix-dhcp-client:dhcp": {
51+
"option": [
52+
{"id": "vendor-class", "value": "infamy"},
53+
{"id": "hostname"},
54+
{"id": "netmask"},
55+
{"id": "router"}
56+
]
57+
}
58+
}
59+
}]
60+
}
61+
})
62+
63+
with test.step("Verify DHCP hostname takes precedence"):
64+
until(lambda: verify_hostname(client, DHCP_HOSTNAME))
65+
66+
with test.step("Drop hostname option from client request"):
67+
path = f"/ietf-interfaces:interfaces/interface[name='{port}']" \
68+
+ "/ietf-ip:ipv4/infix-dhcp-client:dhcp/option[id='hostname']"
69+
client.delete_xpath(path)
70+
71+
with test.step("Verify hostname reverts to configured value"):
72+
until(lambda: verify_hostname(client, CONF_HOSTNAME))
73+
74+
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/infix_dhcp/dhcp_client.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
- name: DHCP option 121 vs option 3
1212
case: client_routes/test.py
1313

14+
- name: DHCP Hostname Priority
15+
case: client_hostname/test.py
16+
1417
- name: DHCPv6 Basic
1518
case: client6_basic/test.py
1619

test/infamy/dhcp.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ class Server:
88
config_file = '/tmp/udhcpd.conf'
99
leases_file = '/tmp/udhcpd.leases'
1010

11-
def __init__(self, netns, start='192.168.0.100', end='192.168.0.110', netmask='255.255.255.0', ip=None, router=None, prefix=None, iface="iface"):
11+
def __init__(self, netns, start='192.168.0.100', end='192.168.0.110',
12+
netmask='255.255.255.0', ip=None, router=None, prefix=None,
13+
hostname=None, iface="iface"):
1214
self.process = None
1315
self.netns = netns
1416
self.iface = iface
15-
self._create_files(start, end, netmask, ip, router, prefix)
17+
self._create_files(start, end, netmask, ip, router, prefix, hostname)
1618

1719
def __del__(self):
1820
#print(self.config_file)
@@ -26,7 +28,7 @@ def __enter__(self):
2628
def __exit__(self, _, __, ___):
2729
self.stop()
2830

29-
def _create_files(self, start, end, netmask, ip, router, prefix):
31+
def _create_files(self, start, end, netmask, ip, router, prefix, hostname):
3032
f = open(self.leases_file, "w")
3133
f.close()
3234

@@ -47,6 +49,8 @@ def _create_files(self, start, end, netmask, ip, router, prefix):
4749
f.write(f"option router {router}\n")
4850
if prefix and router:
4951
f.write(f"option staticroutes {prefix} {router}\n")
52+
if hostname:
53+
f.write(f"option hostname {hostname}\n")
5054

5155
def get_pid(self):
5256
return self.process.pid

0 commit comments

Comments
 (0)