Prevent dnsmasq from segfaulting. https://code.launchpad.net/~soren/nova/dnsmasq-leasesfile-init/+merge/52421 diff -ru nova-2011.1.1-orig//bin/nova-dhcpbridge nova-2011.1.1//bin/nova-dhcpbridge --- nova-2011.1.1-orig//bin/nova-dhcpbridge 2011-02-24 19:51:54.000000000 +0100 +++ nova-2011.1.1//bin/nova-dhcpbridge 2011-04-01 17:49:53.848659259 +0200 @@ -94,7 +94,7 @@ """Get the list of hosts for an interface.""" ctxt = context.get_admin_context() network_ref = db.network_get_by_bridge(ctxt, interface) - return linux_net.get_dhcp_hosts(ctxt, network_ref['id']) + return linux_net.get_dhcp_leases(ctxt, network_ref['id']) def main(): diff -ru nova-2011.1.1-orig//nova/network/linux_net.py nova-2011.1.1//nova/network/linux_net.py --- nova-2011.1.1-orig//nova/network/linux_net.py 2011-02-24 19:51:54.000000000 +0100 +++ nova-2011.1.1//nova/network/linux_net.py 2011-04-01 17:50:37.315585644 +0200 @@ -18,6 +18,7 @@ """ import os +import calendar from nova import db from nova import flags @@ -48,6 +49,8 @@ 'location of nova-dhcpbridge') flags.DEFINE_string('routing_source_ip', '$my_ip', 'Public IP of network host') +flags.DEFINE_integer('dhcp_lease_time', 120, + 'Lifetime of a DHCP lease') flags.DEFINE_bool('use_nova_chains', False, 'use the nova_ routing chains instead of default') @@ -218,8 +221,17 @@ _confirm_rule("FORWARD", "-j nova-local") +def get_dhcp_leases(context, network_id): + """Return a network's hosts config in dnsmasq leasefile format""" + hosts = [] + for fixed_ip_ref in db.network_get_associated_fixed_ips(context, + network_id): + hosts.append(_host_lease(fixed_ip_ref)) + return '\n'.join(hosts) + + def get_dhcp_hosts(context, network_id): - """Get a string containing a network's hosts config in dnsmasq format""" + """Get a string containing a network's hosts config in dhcp-host format""" hosts = [] for fixed_ip_ref in db.network_get_associated_fixed_ips(context, network_id): @@ -310,8 +322,24 @@ utils.get_my_linklocal(network_ref['bridge'])}) +def _host_lease(fixed_ip_ref): + """Return a host string for an address in leasefile format""" + instance_ref = fixed_ip_ref['instance'] + if instance_ref['updated_at']: + timestamp = instance_ref['updated_at'] + else: + timestamp = instance_ref['created_at'] + + seconds_since_epoch = calendar.timegm(timestamp.utctimetuple()) + + return "%d %s %s %s *" % (seconds_since_epoch + FLAGS.dhcp_lease_time, + instance_ref['mac_address'], + fixed_ip_ref['address'], + instance_ref['hostname'] or '*') + + def _host_dhcp(fixed_ip_ref): - """Return a host string for an address""" + """Return a host string for an address in dhcp-host format""" instance_ref = fixed_ip_ref['instance'] return "%s,%s.novalocal,%s" % (instance_ref['mac_address'], instance_ref['hostname'], diff -ru nova-2011.1.1-orig//nova/tests/test_network.py nova-2011.1.1//nova/tests/test_network.py --- nova-2011.1.1-orig//nova/tests/test_network.py 2011-02-24 19:51:54.000000000 +0100 +++ nova-2011.1.1//nova/tests/test_network.py 2011-04-01 17:49:53.849659365 +0200 @@ -20,6 +20,7 @@ """ import IPy import os +import time from nova import context from nova import db @@ -320,6 +321,31 @@ network['id']) self.assertEqual(ip_count, num_available_ips) + def test_dhcp_lease_output(self): + admin_ctxt = context.get_admin_context() + address = self._create_address(0, self.instance_id) + lease_ip(address) + network_ref = db.network_get_by_instance(admin_ctxt, self.instance_id) + leases = linux_net.get_dhcp_leases(context.get_admin_context(), + network_ref['id']) + for line in leases.split('\n'): + seconds, mac, ip, hostname, client_id = line.split(' ') + self.assertTrue(int(seconds) > time.time(), 'Lease expires in ' + 'the past') + octets = mac.split(':') + self.assertEqual(len(octets), 6, "Wrong number of octets " + "in %s" % (max,)) + for octet in octets: + self.assertEqual(len(octet), 2, "Oddly sized octet: %s" + % (octet,)) + # This will throw an exception if the octet is invalid + int(octet, 16) + + # And this will raise an exception in case of an invalid IP + IPy.IP(ip) + + release_ip(address) + def is_allocated_in_project(address, project_id): """Returns true if address is in specified project"""