From dbe324f7254dd3e01de44bb908150fb8397fe118 Mon Sep 17 00:00:00 2001 From: Joshua McKenty Date: Wed, 7 Jul 2010 12:15:11 -0700 Subject: [PATCH] Got dhcpleasor working, with test ENV for testing, and rpc.cast for real world. --- bin/dhcpleasor.py | 36 ++++++++++++++++++++++-------- docs/conf.py | 2 +- nova/compute/linux_net.py | 7 +++--- nova/compute/network.py | 25 +++++++++++++++++---- nova/endpoint/cloud.py | 8 +++++++ nova/tests/network_unittest.py | 40 ++++++++++++++++++++++++++-------- nova/utils.py | 2 -- 7 files changed, 91 insertions(+), 29 deletions(-) diff --git a/bin/dhcpleasor.py b/bin/dhcpleasor.py index 07e63884f09d..63ed524207e7 100755 --- a/bin/dhcpleasor.py +++ b/bin/dhcpleasor.py @@ -27,32 +27,50 @@ sys.path.append(os.path.abspath(os.path.join(__file__, "../../"))) logging.debug(sys.path) import getopt from os import environ +from nova.compute import linux_net from nova.compute import network +from nova import rpc + from nova import flags FLAGS = flags.FLAGS def add_lease(mac, ip, hostname, interface): - pass + if FLAGS.fake_rabbit: + network.lease_ip(ip) + else: + rpc.cast(FLAGS.cloud_topic, {"method": "lease_ip", + "args" : {"address": ip}}) def old_lease(mac, ip, hostname, interface): - pass + logging.debug("Adopted old lease or got a change of mac/hostname") def del_lease(mac, ip, hostname, interface): - # TODO - get net from interface instead - net = network.get_network_by_address(ip) - net.release_ip(ip) + if FLAGS.fake_rabbit: + network.release_ip(ip) + else: + rpc.cast(FLAGS.cloud_topic, {"method": "release_ip", + "args" : {"address": ip}}) def init_leases(interface): - return "" + net = network.get_network_by_interface(interface) + res = "" + for host_name in net.hosts: + res += "%s\n" % linux_net.hostDHCP(net, host_name, net.hosts[host_name]) + return res def main(argv=None): if argv is None: argv = sys.argv interface = environ.get('DNSMASQ_INTERFACE', 'br0') - old_redis_db = FLAGS.redis_db - FLAGS.redis_db = int(environ.get('REDIS_DB', '0')) + if int(environ.get('TESTING', '0')): + FLAGS.fake_rabbit = True + FLAGS.redis_db = 8 + FLAGS.network_size = 32 + FLAGS.fake_libvirt=True + FLAGS.fake_network=True + FLAGS.fake_users = True action = argv[1] if action in ['add','del','old']: mac = argv[2] @@ -62,7 +80,7 @@ def main(argv=None): globals()[action+'_lease'](mac, ip, hostname, interface) else: print init_leases(interface) - FLAGS.redis_db = old_redis_db + exit(0) if __name__ == "__main__": sys.exit(main()) diff --git a/docs/conf.py b/docs/conf.py index 9dfdfc8be65c..bc61f438c994 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -16,7 +16,7 @@ import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.append(os.path.abspath('.')) +sys.path.append(os.path.abspath('/Users/jmckenty/Projects/cc')) sys.path.append([os.path.abspath('../nova'),os.path.abspath('../'),os.path.abspath('../vendor')]) from nova import vendor diff --git a/nova/compute/linux_net.py b/nova/compute/linux_net.py index b44cf9437a51..c9e5bb1a74e0 100644 --- a/nova/compute/linux_net.py +++ b/nova/compute/linux_net.py @@ -98,11 +98,10 @@ def dnsmasq_cmd(net): ' --pid-file=%s' % dhcp_file(net['vlan'], 'pid'), ' --listen-address=%s' % net.dhcp_listen_address, ' --except-interface=lo', - ' --dhcp-range=%s,static,120s' % (net.dhcp_range_start), - ' --dhcp-lease-max=61', + ' --dhcp-range=%s,static,600s' % (net.dhcp_range_start), ' --dhcp-hostsfile=%s' % dhcp_file(net['vlan'], 'conf'), - ' --dhcp-leasefile=%s' % dhcp_file(net['vlan'], 'leases'), - ' ---dhcp-script=%s' % bin_file('dhcpleasor.py')] + ' --dhcp-script=%s' % bin_file('dhcpleasor.py'), + ' --leasefile-ro'] return ''.join(cmd) def hostDHCP(network, host, mac): diff --git a/nova/compute/network.py b/nova/compute/network.py index 7a347aa1103d..96b8e9627058 100644 --- a/nova/compute/network.py +++ b/nova/compute/network.py @@ -160,7 +160,10 @@ class BaseNetwork(datastore.RedisModel): self._add_host(user_id, project_id, address, mac) self.express(address=address) return address - raise exception.NoMoreAddresses() + raise exception.NoMoreAddresses("Project %s with network %s" % (project_id, str(self.network))) + + def lease_ip(self, ip_str): + logging.debug("Leasing allocated IP %s" % (ip_str)) def release_ip(self, ip_str): if not ip_str in self.assigned: @@ -419,14 +422,22 @@ def get_vlan_for_project(project_id): return vlan raise exception.AddressNotAllocated("Out of VLANs") +def get_project_id_for_vlan(vlan): + assigned_vlans = get_assigned_vlans() + for project_id, project_vlan in assigned_vlans.iteritems(): + if vlan == project_vlan: + return project_id + +def get_network_by_interface(iface, security_group='default'): + vlan = iface.rpartition("br")[2] + return get_project_network(get_project_id_for_vlan(vlan), security_group) def get_network_by_address(address): - logging.debug("Get Network By Address:") + logging.debug("Get Network By Address: %s" % address) for project in users.UserManager.instance().get_projects(): - logging.debug(" looking at project %s", project.id) net = get_project_network(project.id) - logging.debug(" is %s in %s ?" % (address, str(net.assigned))) if address in net.assigned: + logging.debug("Found %s in %s" % (address, project.id)) return net raise exception.AddressNotAllocated() @@ -438,6 +449,12 @@ def allocate_ip(user_id, project_id, mac): def deallocate_ip(address): return get_network_by_address(address).deallocate_ip(address) + +def release_ip(address): + return get_network_by_address(address).release_ip(address) + +def lease_ip(address): + return get_network_by_address(address).lease_ip(address) def get_project_network(project_id, security_group='default'): """ get a project's private network, allocating one if needed """ diff --git a/nova/endpoint/cloud.py b/nova/endpoint/cloud.py index 931c6c6e1a07..269fb3950400 100644 --- a/nova/endpoint/cloud.py +++ b/nova/endpoint/cloud.py @@ -498,6 +498,14 @@ class CloudController(object): # TODO - Strip the IP from the instance return defer.succeed({'disassociateResponse': ["Address disassociated."]}) + def release_ip(self, context, private_ip, **kwargs): + self.network.release_ip(private_ip) + return defer.succeed({'releaseResponse': ["Address released."]}) + + def lease_ip(self, context, private_ip, **kwargs): + self.network.lease_ip(private_ip) + return defer.succeed({'leaseResponse': ["Address lease."]}) + @rbac.allow('projectmanager', 'sysadmin') def run_instances(self, context, **kwargs): # make sure user can access the image diff --git a/nova/tests/network_unittest.py b/nova/tests/network_unittest.py index 4c9f340c1a47..8cbc2b7cd8eb 100644 --- a/nova/tests/network_unittest.py +++ b/nova/tests/network_unittest.py @@ -21,6 +21,7 @@ import os import logging import unittest +import time from nova import vendor import IPy @@ -86,18 +87,36 @@ class NetworkTestCase(test.TrialTestCase): self.assertEqual(False, address in self._get_project_addresses("project0")) def test_range_allocation(self): + mac = utils.generate_mac() + secondmac = utils.generate_mac() + hostname = "test-host" address = network.allocate_ip( - "netuser", "project0", utils.generate_mac()) + "netuser", "project0", mac) secondaddress = network.allocate_ip( - "netuser", "project1", utils.generate_mac()) + "netuser", "project1", secondmac) + net = network.get_project_network("project0", "default") + secondnet = network.get_project_network("project1", "default") + self.assertEqual(True, address in self._get_project_addresses("project0")) self.assertEqual(True, secondaddress in self._get_project_addresses("project1")) self.assertEqual(False, address in self._get_project_addresses("project1")) + # Addresses are allocated before they're issued + self.dnsmasq.issue_ip(mac, address, hostname, net.bridge_name) + self.dnsmasq.issue_ip(secondmac, secondaddress, + hostname, secondnet.bridge_name) + rv = network.deallocate_ip(address) + self.dnsmasq.release_ip(mac, address, hostname, net.bridge_name) self.assertEqual(False, address in self._get_project_addresses("project0")) + # First address release shouldn't affect the second + self.assertEqual(True, + secondaddress in self._get_project_addresses("project1")) + rv = network.deallocate_ip(secondaddress) + self.dnsmasq.release_ip(secondmac, secondaddress, + hostname, secondnet.bridge_name) self.assertEqual(False, secondaddress in self._get_project_addresses("project1")) @@ -127,9 +146,14 @@ class NetworkTestCase(test.TrialTestCase): for i in range(0, 30): name = 'toomany-project%s' % i self.manager.create_project(name, 'netuser', name) + net = network.get_project_network(name, "default") + mac = utils.generate_mac() + hostname = "toomany-hosts" address = network.allocate_ip( - "netuser", name, utils.generate_mac()) + "netuser", name, mac) + self.dnsmasq.issue_ip(mac, address, hostname, net.bridge_name) rv = network.deallocate_ip(address) + self.dnsmasq.release_ip(mac, address, hostname, net.bridge_name) self.manager.delete_project(name) def _get_project_addresses(self, project_id): @@ -144,15 +168,13 @@ def binpath(script): class FakeDNSMasq(object): def issue_ip(self, mac, ip, hostname, interface): cmd = "%s add %s %s %s" % (binpath('dhcpleasor.py'), mac, ip, hostname) - env = {'DNSMASQ_INTERFACE': interface, 'REDIS_DB' : '8'} + env = {'DNSMASQ_INTERFACE': interface, 'TESTING' : '1'} (out, err) = utils.execute(cmd, addl_env=env) - logging.debug(out) - logging.debug(err) + logging.debug("ISSUE_IP: %s, %s " % (out, err)) def release_ip(self, mac, ip, hostname, interface): cmd = "%s del %s %s %s" % (binpath('dhcpleasor.py'), mac, ip, hostname) - env = {'DNSMASQ_INTERFACE': interface, 'REDIS_DB' : '8'} + env = {'DNSMASQ_INTERFACE': interface, 'TESTING' : '1'} (out, err) = utils.execute(cmd, addl_env=env) - logging.debug(out) - logging.debug(err) + logging.debug("RELEASE_IP: %s, %s " % (out, err)) \ No newline at end of file diff --git a/nova/utils.py b/nova/utils.py index cbfdd835d9ac..62ad00aecddf 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -48,11 +48,9 @@ def fetchfile(url, target): execute("curl %s -o %s" % (url, target)) def execute(cmd, input=None, addl_env=None): - #logging.debug("Running %s" % (cmd)) env = os.environ.copy() if addl_env: env.update(addl_env) - logging.debug(env) obj = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) result = None