Skip to content

Commit 0bedde5

Browse files
authored
Merge pull request #1410 from saba8814/issue-782-dhcp-ntp-dns
Issue 782 dhcp ntp dns
2 parents b315e44 + 1a1d917 commit 0bedde5

File tree

7 files changed

+313
-0
lines changed

7 files changed

+313
-0
lines changed

test/case/use_case/Readme.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,7 @@ Integration tests verifying multiple software components working
55
together:
66

77
- OSPF routing with containerized applications and network segmentation
8+
- Combined static and DHCP-provided DNS and NTP servers
89

10+
include::dhcp_ntp_dns_combination/Readme.adoc[]
911
include::ospf_container/Readme.adoc[]

test/case/use_case/all.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
---
2+
- name: DHCP NTP DNS combination
3+
case: dhcp_ntp_dns_combination/test.py
4+
25
- name: OSPF Container
36
case: ospf_container/test.py
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test.adoc
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
=== DHCP NTP DNS combination
2+
3+
ifdef::topdoc[:imagesdir: {topdoc}../../test/case/use_case/dhcp_ntp_dns_combination]
4+
5+
==== Description
6+
7+
Verify statically configured DNS and NTP servers are not lost when receiving
8+
servers from a DHCP server.
9+
10+
==== Topology
11+
12+
image::topology.svg[DHCP NTP DNS combination topology, align=center, scaledwidth=75%]
13+
14+
==== Sequence
15+
16+
. Set up topology and configure static IP on client
17+
. Configure static DNS and NTP on client and verify
18+
. Set up DHCP server with NTP and DNS options
19+
. Configure client to use DHCP and verify combined servers
20+
21+
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Combined test of DHCP + NTP + DNS
4+
5+
Verify statically configured DNS and NTP servers are not lost when receiving
6+
servers from a DHCP server.
7+
"""
8+
import infamy
9+
import infamy.iface as iface
10+
from infamy.util import until
11+
12+
def any_dhcp_address(target, iface_name):
13+
try:
14+
addrs = iface.get_ipv4_address(target, iface_name)
15+
if addrs:
16+
for addr in addrs:
17+
# Origin can be 'dhcp' or 'ietf-ip:dhcp' depending on how it's reported
18+
if addr.get('origin') in ['dhcp', 'ietf-ip:dhcp']:
19+
return True
20+
except Exception:
21+
return False
22+
return False
23+
24+
def has_dns_server(target, dns_servers):
25+
"""Verify system has all the given DNS servers in operational state"""
26+
data = target.get_data("/ietf-system:system-state/infix-system:dns-resolver")
27+
if not data:
28+
return False
29+
30+
# In Infix, augmented nodes show up under their name in system-state
31+
resolver = data.get('system-state', {}).get('dns-resolver', {})
32+
servers = resolver.get('server', [])
33+
current_addresses = {s.get('address') for s in servers}
34+
return all(addr in current_addresses for addr in dns_servers)
35+
36+
def has_ntp_server(target, ntp_servers):
37+
"""Verify system has all the given NTP servers in operational state"""
38+
data = target.get_data("/ietf-system:system-state/infix-system:ntp")
39+
if not data:
40+
return False
41+
42+
ntp_state = data.get('system-state', {}).get('ntp', {})
43+
sources = ntp_state.get('sources', {}).get('source', [])
44+
current_addresses = {s.get('address') for s in sources}
45+
return all(addr in current_addresses for addr in ntp_servers)
46+
47+
def has_system_servers(target, dns, ntp_list):
48+
"""Verify DUT has all DNS and NTP server(s)"""
49+
return has_dns_server(target, dns) and has_ntp_server(target, ntp_list)
50+
51+
52+
with infamy.Test() as test:
53+
SERVER_IP = '192.168.3.1'
54+
CLIENT_STATIC_IP = '192.168.3.10'
55+
56+
STATIC_DNS = '1.1.1.1'
57+
STATIC_NTP = '2.2.2.2'
58+
59+
DHCP_DNS = SERVER_IP
60+
DHCP_NTP = SERVER_IP
61+
62+
with test.step("Set up topology and configure static IP on client"):
63+
env = infamy.Env()
64+
server = env.attach("server", "mgmt")
65+
client = env.attach("client", "mgmt")
66+
67+
client_data = client["server"]
68+
server_data = server["client"]
69+
70+
# Configure server with static IP and NTP server
71+
server.put_config_dicts({
72+
"ietf-interfaces": {
73+
"interfaces": {
74+
"interface": [{
75+
"name": server_data,
76+
"type": "infix-if-type:ethernet",
77+
"enabled": True,
78+
"ipv4": {
79+
"address": [{"ip": SERVER_IP, "prefix-length": 24}]
80+
}
81+
}]
82+
}
83+
},
84+
"ietf-ntp": {
85+
"ntp": {
86+
"refclock-master": {"master-stratum": 8}
87+
}
88+
}
89+
})
90+
91+
# Configure client with static IP
92+
client.put_config_dicts({
93+
"ietf-interfaces": {
94+
"interfaces": {
95+
"interface": [{
96+
"name": client_data,
97+
"type": "infix-if-type:ethernet",
98+
"enabled": True,
99+
"ipv4": {
100+
"address": [{"ip": CLIENT_STATIC_IP, "prefix-length": 24}]
101+
}
102+
}]
103+
}
104+
}
105+
})
106+
107+
until(lambda: iface.address_exist(client, client_data, CLIENT_STATIC_IP, proto="static"))
108+
print("Initial IP connectivity established")
109+
110+
with test.step("Configure static DNS and NTP on client and verify"):
111+
client.put_config_dicts({
112+
"ietf-system": {
113+
"system": {
114+
"ntp": {
115+
"enabled": True,
116+
"server": [{
117+
"name": "static-ntp",
118+
"udp": {"address": STATIC_NTP},
119+
"iburst": True
120+
}]
121+
},
122+
"dns-resolver": {
123+
"server": [{
124+
"name": "static-dns",
125+
"udp-and-tcp": {"address": STATIC_DNS}
126+
}]
127+
}
128+
}
129+
}
130+
})
131+
132+
print("Waiting for static servers to appear in operational state...")
133+
until(lambda: has_system_servers(client, [STATIC_DNS], [STATIC_NTP]),
134+
attempts=30)
135+
print("Static DNS and NTP servers verified")
136+
137+
with test.step("Set up DHCP server with NTP and DNS options"):
138+
server.put_config_dicts({
139+
"infix-dhcp-server": {
140+
"dhcp-server": {
141+
"subnet": [{
142+
"subnet": "192.168.3.0/24",
143+
"interface": server_data,
144+
"pool": {
145+
"start-address": "192.168.3.100",
146+
"end-address": "192.168.3.150"
147+
},
148+
"option": [
149+
{"id": "router", "address": SERVER_IP},
150+
{"id": "dns-server", "address": DHCP_DNS},
151+
{"id": "ntp-server", "address": DHCP_NTP}
152+
]
153+
}]
154+
}
155+
}
156+
})
157+
print("DHCP server configured on server DUT")
158+
159+
with test.step("Configure client to use DHCP and verify combined servers"):
160+
# Explicitly delete static config using full path
161+
try:
162+
# Try to delete the entire ipv4 container from ietf-ip
163+
client.delete_xpath(f"ietf-interfaces:interfaces/interface[name='{client_data}']/ietf-ip:ipv4")
164+
except Exception:
165+
pass
166+
167+
# Enable DHCP
168+
client.put_config_dicts({
169+
"ietf-interfaces": {
170+
"interfaces": {
171+
"interface": [{
172+
"name": client_data,
173+
"ipv4": {
174+
"infix-dhcp-client:dhcp": {
175+
"client-id": "client-1",
176+
"option": [
177+
{"id": "dns-server"},
178+
{"id": "ntp-server"}
179+
]
180+
}
181+
}
182+
}]
183+
}
184+
}
185+
})
186+
187+
print("Waiting for DHCP lease...")
188+
until(lambda: any_dhcp_address(client, client_data), attempts=120)
189+
print("Client got DHCP address")
190+
191+
print("Verifying combined DNS and NTP servers...")
192+
# Expected DNS: STATIC_DNS (1.1.1.1) AND DHCP_DNS (192.168.3.1)
193+
# Expected NTP: STATIC_NTP (2.2.2.2) AND DHCP_NTP (192.168.3.1)
194+
until(lambda: has_system_servers(client, [STATIC_DNS, DHCP_DNS], [STATIC_NTP, DHCP_NTP]),
195+
attempts=60)
196+
print("SUCCESS: Combined static and DHCP servers verified")
197+
198+
test.succeed()
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
graph "dhcp_ntp_dns_combination" {
2+
layout="neato";
3+
overlap="false";
4+
esep="+40";
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 | { <client> client | <server> server }",
11+
pos="0,120!",
12+
requires="controller",
13+
];
14+
15+
client [
16+
label="{ <mgmt> mgmt | <server> server} | client",
17+
pos="20,130!",
18+
requires="infix",
19+
];
20+
21+
server [
22+
label="{ <client> client | <mgmt> mgmt } | server",
23+
pos="20,120!",
24+
requires="infix",
25+
];
26+
27+
host:server -- server:mgmt [requires="mgmt", color=lightgrey]
28+
host:client -- client:mgmt [requires="mgmt", color=lightgrey]
29+
30+
server:client -- client:server [color=blue, fontcolor=black]
31+
}
Lines changed: 57 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)