Merge lp:~jtv/maas/bug-1116700 into lp:~maas-committers/maas/trunk

Proposed by Jeroen T. Vermeulen
Status: Merged
Approved by: Jeroen T. Vermeulen
Approved revision: no longer in the source branch.
Merged at revision: 1430
Proposed branch: lp:~jtv/maas/bug-1116700
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 111 lines (+43/-32)
2 files modified
src/maasserver/models/dhcplease.py (+24/-24)
src/maasserver/tests/test_dhcplease.py (+19/-8)
To merge this branch: bzr merge lp:~jtv/maas/bug-1116700
Reviewer Review Type Date Requested Status
Raphaël Badin (community) Approve
Review via email: mp+146835@code.launchpad.net

Commit message

Generate CNAME for a node's oldest MACAddress that has a lease. (We used to generate a CNAME for a node's oldest MACAddress *if it had* a lease).

We don't know why the code was originally written this way, although it was deliberate at the time (originally proposed at https://code.launchpad.net/~rvb/maas/maas-dns2/+merge/114707 ). Our best recollection now is that it was done that way to mimick how things worked with Cobbler, but it led to problems when network interfaces weren't in the expected order.

Description of the change

We're taking a bit of a risk with this one, because we've lost the reason for the original behaviour. We do know that we had to do things like this for the original Cobbler-based MAAS, where things worked the way they did simply because that's how they worked in Cobbler, or because it was the way we ended up interfacing with Cobbler. DHCPLease was bolted on later, so there may have been problems with incorporating it nicely.

Raphaël sees no problem with the change. It seems more sensible than what we did before.

To post a comment you must log in.
Revision history for this message
Raphaël Badin (rvb) wrote :

Looks good to me.

Thanks for updating the test's name and adding a new test.

3 tiny notes:

[0]

50 + node.hostname, lease.ip, mac.id
[...]
59 - for hostname, ip in cursor.fetchall()
60 + for hostname, ip, mac in cursor.fetchall()

You probably don't need to return the 'mac' in the query since you're ignoring it later.

[1]

101 + lease_for_newer_mac = factory.make_dhcp_lease(
102 + nodegroup=node.nodegroup, mac=newer_mac.mac_address)
103 + ignore_unused(lease_for_newer_mac)

A bit far fetched :) I'd get rid of the assignment and the ignore_unused() statement because I think the name of the other variable 'lease_for_older_mac' make the whole design pretty clear.

[2]

69 -# Copyright 2012 Canonical Ltd. This software is licensed under the
70 +# Copyright 2013 Canonical Ltd. This software is licensed under the

That's a nice touch but now you got me wondering about why you did not update dhcplease.py's header ;).

review: Approve
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

> 59 - for hostname, ip in cursor.fetchall()
> 60 + for hostname, ip, mac in cursor.fetchall()
>
> You probably don't need to return the 'mac' in the query since you're ignoring
> it later.

You're right. There are some requirements with DISTINCT ON that can be surprising, and I was in a hurry to knock something out that other people could work with, without such puzzling surprises. So I erred on the side of caution in the prototype for my fix, and didn't tighten it up fully later.

> 101 + lease_for_newer_mac = factory.make_dhcp_lease(
> 102 + nodegroup=node.nodegroup, mac=newer_mac.mac_address)
> 103 + ignore_unused(lease_for_newer_mac)
>
> A bit far fetched :) I'd get rid of the assignment and the ignore_unused()
> statement because I think the name of the other variable 'lease_for_older_mac'
> make the whole design pretty clear.

Changed.

> 69 -# Copyright 2012 Canonical Ltd. This software is licensed under the
> 70 +# Copyright 2013 Canonical Ltd. This software is licensed under the
>
> That's a nice touch but now you got me wondering about why you did not update
> dhcplease.py's header ;).

Done.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/maasserver/models/dhcplease.py'
--- src/maasserver/models/dhcplease.py 2012-11-08 06:34:48 +0000
+++ src/maasserver/models/dhcplease.py 2013-02-07 04:00:32 +0000
@@ -1,4 +1,4 @@
1# Copyright 2012 Canonical Ltd. This software is licensed under the1# Copyright 2013 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Node IP/MAC mappings as leased from the workers' DHCP servers."""4"""Node IP/MAC mappings as leased from the workers' DHCP servers."""
@@ -111,32 +111,32 @@
111 """Return a mapping {hostnames -> ips} for the currently leased111 """Return a mapping {hostnames -> ips} for the currently leased
112 IP addresses for the nodes in `nodegroup`.112 IP addresses for the nodes in `nodegroup`.
113113
114 This will consider only the first interface (i.e. the first114 For each node, this will consider only the oldest `MACAddress` that
115 MAC Address) associated with each node withing the given115 has a `DHCPLease`.
116 `nodegroup`.116
117 If the hostnames contain a domain, it gets removed.117 Any domain will be stripped from the hostnames.
118 """118 """
119 cursor = connection.cursor()119 cursor = connection.cursor()
120 # The subquery fetches the IDs of the first MAC Address for120
121 # all the nodes in this nodegroup.121 # The "DISTINCT ON" gives us the first matching row for any
122 # Then the main query returns the hostname -> ip mapping for122 # given hostname, in the query's ordering.
123 # these MAC Addresses.123 # The ordering must start with the hostname so that the database
124 # can do this efficiently. The next ordering criterion is the
125 # MACAddress id, so that if there are multiple rows with the
126 # same hostname, we get the one with the oldest MACAddress.
127 #
128 # If this turns out to be inefficient, be sure to try selecting
129 # on node.nodegroup_id instead of lease.nodegroup_id. It has
130 # the same effect but may perform differently.
124 cursor.execute("""131 cursor.execute("""
125 SELECT node.hostname, lease.ip132 SELECT DISTINCT ON (node.hostname)
126 FROM maasserver_macaddress as mac,133 node.hostname, lease.ip
127 maasserver_node as node,134 FROM maasserver_macaddress AS mac
128 maasserver_dhcplease as lease135 JOIN maasserver_node AS node ON node.id = mac.node_id
129 WHERE mac.id IN (136 JOIN maasserver_dhcplease AS lease ON lease.mac = mac.mac_address
130 SELECT DISTINCT ON (node_id) mac.id137 WHERE lease.nodegroup_id = %s
131 FROM maasserver_macaddress as mac,138 ORDER BY node.hostname, mac.id
132 maasserver_node as node139 """, (nodegroup.id, ))
133 WHERE node.nodegroup_id = %s AND mac.node_id = node.id
134 ORDER BY node_id, mac.id
135 )
136 AND mac.node_id = node.id
137 AND mac.mac_address = lease.mac
138 AND lease.nodegroup_id = %s
139 """, (nodegroup.id, nodegroup.id))
140 return dict(140 return dict(
141 (strip_domain(hostname), ip)141 (strip_domain(hostname), ip)
142 for hostname, ip in cursor.fetchall()142 for hostname, ip in cursor.fetchall()
143143
=== modified file 'src/maasserver/tests/test_dhcplease.py'
--- src/maasserver/tests/test_dhcplease.py 2012-11-08 06:34:48 +0000
+++ src/maasserver/tests/test_dhcplease.py 2013-02-07 04:00:32 +0000
@@ -1,4 +1,4 @@
1# Copyright 2012 Canonical Ltd. This software is licensed under the1# Copyright 2013 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Tests for the :class:`DHCPLease` model."""4"""Tests for the :class:`DHCPLease` model."""
@@ -190,17 +190,28 @@
190 mapping = DHCPLease.objects.get_hostname_ip_mapping(nodegroup)190 mapping = DHCPLease.objects.get_hostname_ip_mapping(nodegroup)
191 self.assertEqual({hostname: lease.ip}, mapping)191 self.assertEqual({hostname: lease.ip}, mapping)
192192
193 def test_get_hostname_ip_mapping_considers_only_first_mac(self):193 def test_get_hostname_ip_mapping_picks_mac_with_lease(self):
194 nodegroup = factory.make_node_group()194 node = factory.make_node(hostname=factory.make_name('host'))
195 node = factory.make_node(
196 nodegroup=nodegroup)
197 factory.make_mac_address(node=node)195 factory.make_mac_address(node=node)
198 second_mac = factory.make_mac_address(node=node)196 second_mac = factory.make_mac_address(node=node)
199 # Create a lease for the second MAC Address.197 # Create a lease for the second MAC Address.
198 lease = factory.make_dhcp_lease(
199 nodegroup=node.nodegroup, mac=second_mac.mac_address)
200 mapping = DHCPLease.objects.get_hostname_ip_mapping(node.nodegroup)
201 self.assertEqual({node.hostname: lease.ip}, mapping)
202
203 def test_get_hostname_ip_mapping_picks_oldest_mac_with_lease(self):
204 node = factory.make_node(hostname=factory.make_name('host'))
205 older_mac = factory.make_mac_address(node=node)
206 newer_mac = factory.make_mac_address(node=node)
207
200 factory.make_dhcp_lease(208 factory.make_dhcp_lease(
201 nodegroup=nodegroup, mac=second_mac.mac_address)209 nodegroup=node.nodegroup, mac=newer_mac.mac_address)
202 mapping = DHCPLease.objects.get_hostname_ip_mapping(nodegroup)210 lease_for_older_mac = factory.make_dhcp_lease(
203 self.assertEqual({}, mapping)211 nodegroup=node.nodegroup, mac=older_mac.mac_address)
212
213 mapping = DHCPLease.objects.get_hostname_ip_mapping(node.nodegroup)
214 self.assertEqual({node.hostname: lease_for_older_mac.ip}, mapping)
204215
205 def test_get_hostname_ip_mapping_considers_given_nodegroup(self):216 def test_get_hostname_ip_mapping_considers_given_nodegroup(self):
206 nodegroup = factory.make_node_group()217 nodegroup = factory.make_node_group()