Skip to content

Commit

Permalink
Test DHCP replies related to DNS settings
Browse files Browse the repository at this point in the history
This ensures we do not send any domain search options:

- `option domain-search`
- `option dhcp6.domain-search`

See `man dhcp-options`.

It also asserts that updates to the DNS servers of the subnet, are
reflected in the DHCP replies shortly after.
  • Loading branch information
href committed Dec 21, 2023
1 parent eb2cd2a commit 304d701
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ These tests are run regularly against our public infrastructure as well as our i
| | [test_private_network_mtu](./test_private_network.py#L244) | default |
| | [test_private_network_only_on_all_images](./test_private_network.py#L302) | all |
| | [test_private_network_attach_later](./test_private_network.py#L324) | default |
| | [test_private_network_dhcp_dns_replies](./test_private_network.py#L358) | default |
| **Public Network** | [test_public_ip_address_on_all_images](./test_public_network.py#L22) | all |
| | [test_public_network_connectivity_on_all_images](./test_public_network.py#L51) | all |
| | [test_public_network_mtu](./test_public_network.py#L70) | default |
Expand Down
6 changes: 6 additions & 0 deletions events.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,12 @@ def on_test_teardown(name, outcome):
'dns_servers': 'args.self.dns_servers',
})

track_in_event_log('subnet.change-dns-servers.after', include={
**RESOURCE_ID,
**RESULT,
'dns_servers': 'args.self.dns_servers',
})


# Keep track of custom images
track_in_event_log('custom-image.import.after', include={
Expand Down
21 changes: 21 additions & 0 deletions resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,23 @@ def interface_name(self, floating_ip):

return f'f-{digest}'

def dhcp_reply(self, interface_name, ip_version, timeout=2.5):
""" Starts a DHCP discovery on the interface and returns a reply,
without configuring the interface.
This is used to assert DHCP replies and requires dhclient to be
installed.
"""
return self.output_of(oneliner(f"""
sudo timeout {timeout}s dhclient {interface_name}
-{ip_version}
-lf /dev/stdout
-n -d -q -1
--no-pid
2>/dev/null || true
"""))

def configure_floating_ip(self, floating_ip):
interface = self.interface_name(floating_ip)

Expand Down Expand Up @@ -975,6 +992,10 @@ def __contains__(self, address):
def create(self):
self.info = self.api.post('/subnets', json=self.spec).json()

@with_trigger('subnet.change-dns-servers')
def change_dns_servers(self, dns_servers):
self.api.patch(self.href, json={'dns_servers': dns_servers})

def delete(self):
""" Subnets are not explicitly deleted as they are automatically
removed when their network is removed.
Expand Down
55 changes: 54 additions & 1 deletion test_private_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def assert_a_private_address():
retry_for(seconds=5).or_warn(assert_a_private_address, msg=(
f'{server.name}: No private IP address after 5s'))

# if this all together takes more than 30 seconds, we count it as a failure
# If this all together takes more than 30 seconds, we count it as a failure
retry_for(seconds=25).or_fail(assert_a_private_address, msg=(
f'{server.name}: No private IP address after 30s'))

Expand Down Expand Up @@ -353,3 +353,56 @@ def assert_private_network_is_configured():
assert_private_network_is_configured,
msg='Failed to configure private network.',
)


def test_private_network_dhcp_dns_replies(server, private_network):
""" Subnets contain a DHCP server, whose DNS server list can be
configured by the user.
"""

# Create a default subnet
subnet = private_network.add_subnet('10.1.2.0/24')

server.update(
interfaces=[{"network": "public"},
{"network": private_network.info["uuid"]}]
)

assert server.private_interface.exists

# No DHCP reply sets a search domain
reply = server.dhcp_reply(server.public_interface.name, ip_version=4)
assert "domain-search" not in reply

reply = server.dhcp_reply(server.public_interface.name, ip_version=6)
assert "domain-search" not in reply

reply = server.dhcp_reply(server.private_interface.name, ip_version=4)
assert "domain-search" not in reply

# The DNS server of the private subnet can be changed
subnet.change_dns_servers(['10.0.0.8', '10.0.0.9'])

def assert_custom_dns_servers():
reply = server.dhcp_reply(server.private_interface.name, ip_version=4)
assert "domain-name-servers 10.0.0.8,10.0.0.9" in reply
assert "domain-search" not in reply

retry_for(seconds=10).or_fail(
assert_custom_dns_servers,
msg='Failed to configure custom DNS servers.',
)

# We can also use an empty set of DNS servers
subnet.change_dns_servers([])

def assert_no_dns_servers():
reply = server.dhcp_reply(server.private_interface.name, ip_version=4)
assert "domain-name-servers" not in reply
assert "domain-search" not in reply

retry_for(seconds=10).or_fail(
assert_no_dns_servers,
msg='Failed to configure an empty set of DNS servers.',
)

0 comments on commit 304d701

Please sign in to comment.