From b7ea2f70581f6acd927ea7b65adaffeeb4b8d2ba Mon Sep 17 00:00:00 2001 From: Joshua McKenty Date: Wed, 7 Jul 2010 12:06:34 -0700 Subject: Capture signals from dnsmasq and use them to update network state. --- bin/dhcpleasor.py | 68 ++++++++++++++++++++++++++++++++++++++++++ nova/compute/linux_net.py | 9 +++++- nova/compute/network.py | 14 +++++++-- nova/tests/fake_flags.py | 2 +- nova/tests/network_unittest.py | 34 +++++++++++++++++++++ nova/utils.py | 10 +++++-- 6 files changed, 129 insertions(+), 8 deletions(-) create mode 100755 bin/dhcpleasor.py diff --git a/bin/dhcpleasor.py b/bin/dhcpleasor.py new file mode 100755 index 000000000..07e63884f --- /dev/null +++ b/bin/dhcpleasor.py @@ -0,0 +1,68 @@ +#!/opt/local/bin/python + +# Copyright [2010] [Anso Labs, LLC] +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +dhcpleasor.py + +Handle lease database updates from DHCP servers. +""" + +import sys +import os +import logging +sys.path.append(os.path.abspath(os.path.join(__file__, "../../"))) + +logging.debug(sys.path) +import getopt +from os import environ +from nova.compute import network +from nova import flags +FLAGS = flags.FLAGS + + +def add_lease(mac, ip, hostname, interface): + pass + +def old_lease(mac, ip, hostname, interface): + pass + +def del_lease(mac, ip, hostname, interface): + # TODO - get net from interface instead + net = network.get_network_by_address(ip) + net.release_ip(ip) + +def init_leases(interface): + return "" + + +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')) + action = argv[1] + if action in ['add','del','old']: + mac = argv[2] + ip = argv[3] + hostname = argv[4] + logging.debug("Called %s for mac %s with ip %s and hostname %s on interface %s" % (action, mac, ip, hostname, interface)) + globals()[action+'_lease'](mac, ip, hostname, interface) + else: + print init_leases(interface) + FLAGS.redis_db = old_redis_db + +if __name__ == "__main__": + sys.exit(main()) diff --git a/nova/compute/linux_net.py b/nova/compute/linux_net.py index 0bd5ce007..b44cf9437 100644 --- a/nova/compute/linux_net.py +++ b/nova/compute/linux_net.py @@ -62,6 +62,9 @@ def remove_rule(cmd): def bind_public_ip(ip, interface): runthis("Binding IP to interface: %s", "sudo ip addr add %s dev %s" % (ip, interface)) + +def unbind_public_ip(ip, interface): + runthis("Binding IP to interface: %s", "sudo ip addr del %s dev %s" % (ip, interface)) def vlan_create(net): """ create a vlan on on a bridge device unless vlan already exists """ @@ -98,7 +101,8 @@ def dnsmasq_cmd(net): ' --dhcp-range=%s,static,120s' % (net.dhcp_range_start), ' --dhcp-lease-max=61', ' --dhcp-hostsfile=%s' % dhcp_file(net['vlan'], 'conf'), - ' --dhcp-leasefile=%s' % dhcp_file(net['vlan'], 'leases')] + ' --dhcp-leasefile=%s' % dhcp_file(net['vlan'], 'leases'), + ' ---dhcp-script=%s' % bin_file('dhcpleasor.py')] return ''.join(cmd) def hostDHCP(network, host, mac): @@ -154,6 +158,9 @@ def dhcp_file(vlan, kind): return os.path.abspath("%s/nova-%s.%s" % (FLAGS.networks_path, vlan, kind)) +def bin_file(script): + return os.path.abspath(os.path.join(__file__, "../../../bin", script)) + def dnsmasq_pid_for(network): """ the pid for prior dnsmasq instance for a vlan, returns None if no pid file exists diff --git a/nova/compute/network.py b/nova/compute/network.py index 911d0344a..7a347aa11 100644 --- a/nova/compute/network.py +++ b/nova/compute/network.py @@ -162,12 +162,16 @@ class BaseNetwork(datastore.RedisModel): return address raise exception.NoMoreAddresses() - def deallocate_ip(self, ip_str): + def release_ip(self, ip_str): if not ip_str in self.assigned: raise exception.AddressNotAllocated() self.deexpress(address=ip_str) self._rem_host(ip_str) + def deallocate_ip(self, ip_str): + # Do nothing for now, cleanup on ip release + pass + def list_addresses(self): for address in self.hosts: yield address @@ -197,7 +201,7 @@ class BridgedNetwork(BaseNetwork): def get_network_for_project(cls, user_id, project_id, security_group): vlan = get_vlan_for_project(project_id) network_str = get_subnet_from_vlan(vlan) - logging.debug("creating network on vlan %s with network string %s" % (vlan, network_str)) + # logging.debug("creating network on vlan %s with network string %s" % (vlan, network_str)) return cls.create(user_id, project_id, security_group, vlan, network_str) def __init__(self, *args, **kwargs): @@ -221,7 +225,7 @@ class DHCPNetwork(BridgedNetwork): def __init__(self, *args, **kwargs): super(DHCPNetwork, self).__init__(*args, **kwargs) - logging.debug("Initing DHCPNetwork object...") + # logging.debug("Initing DHCPNetwork object...") self.dhcp_listen_address = self.network[1] self.dhcp_range_start = self.network[3] self.dhcp_range_end = self.network[-(1 + FLAGS.cnt_vpn_clients)] @@ -372,6 +376,7 @@ class PublicNetworkController(BaseNetwork): def deexpress(self, address=None): addr = self.get_host(address) private_ip = addr['private_ip'] + linux_net.unbind_public_ip(address, FLAGS.public_interface) linux_net.remove_rule("PREROUTING -t nat -d %s -j DNAT --to %s" % (address, private_ip)) linux_net.remove_rule("POSTROUTING -t nat -s %s -j SNAT --to %s" @@ -416,8 +421,11 @@ def get_vlan_for_project(project_id): def get_network_by_address(address): + logging.debug("Get Network By 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: return net raise exception.AddressNotAllocated() diff --git a/nova/tests/fake_flags.py b/nova/tests/fake_flags.py index d40172cfc..3f5d9ff54 100644 --- a/nova/tests/fake_flags.py +++ b/nova/tests/fake_flags.py @@ -28,5 +28,5 @@ FLAGS.fake_rabbit = True FLAGS.fake_network = True FLAGS.fake_users = True #FLAGS.keeper_backend = 'sqlite' -FLAGS.datastore_path = ':memory:' +# FLAGS.datastore_path = ':memory:' FLAGS.verbose = True diff --git a/nova/tests/network_unittest.py b/nova/tests/network_unittest.py index f215c0b3f..4c9f340c1 100644 --- a/nova/tests/network_unittest.py +++ b/nova/tests/network_unittest.py @@ -18,6 +18,7 @@ # License for the specific language governing permissions and limitations # under the License. +import os import logging import unittest @@ -26,6 +27,7 @@ import IPy from nova import flags from nova import test +from nova import exception from nova.compute import network from nova.auth import users from nova import utils @@ -40,6 +42,7 @@ class NetworkTestCase(test.TrialTestCase): network_size=32) logging.getLogger().setLevel(logging.DEBUG) self.manager = users.UserManager.instance() + self.dnsmasq = FakeDNSMasq() try: self.manager.create_user('netuser', 'netuser', 'netuser') except: pass @@ -63,11 +66,23 @@ class NetworkTestCase(test.TrialTestCase): self.assertTrue(IPy.IP(address) in self.network.network) def test_allocate_deallocate_ip(self): + # Address should be allocated + # Then, simulate acquisition of the address + # Deallocate it, and wait for simulated ip release + # then confirm it's gone. address = network.allocate_ip( "netuser", "project0", utils.generate_mac()) logging.debug("Was allocated %s" % (address)) + net = network.get_project_network("project0", "default") self.assertEqual(True, address in self._get_project_addresses("project0")) + mac = utils.generate_mac() + hostname = "test-host" + self.dnsmasq.issue_ip(mac, address, hostname, net.bridge_name) rv = network.deallocate_ip(address) + # Doesn't go away until it's dhcp released + self.assertEqual(True, address in self._get_project_addresses("project0")) + + self.dnsmasq.release_ip(mac, address, hostname, net.bridge_name) self.assertEqual(False, address in self._get_project_addresses("project0")) def test_range_allocation(self): @@ -122,3 +137,22 @@ class NetworkTestCase(test.TrialTestCase): for addr in network.get_project_network(project_id).list_addresses(): project_addresses.append(addr) return project_addresses + +def binpath(script): + return os.path.abspath(os.path.join(__file__, "../../../bin", 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'} + (out, err) = utils.execute(cmd, addl_env=env) + logging.debug(out) + logging.debug(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'} + (out, err) = utils.execute(cmd, addl_env=env) + logging.debug(out) + logging.debug(err) + \ No newline at end of file diff --git a/nova/utils.py b/nova/utils.py index 325b062ee..cbfdd835d 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -26,6 +26,7 @@ import logging import socket import sys import os.path +from os import environ import inspect import subprocess import random @@ -46,11 +47,14 @@ def fetchfile(url, target): # fp.close() execute("curl %s -o %s" % (url, target)) - -def execute(cmd, input=None): +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) + stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) result = None if input != None: result = obj.communicate(input) -- cgit From dbe324f7254dd3e01de44bb908150fb8397fe118 Mon Sep 17 00:00:00 2001 From: Joshua McKenty Date: Wed, 7 Jul 2010 12:15:11 -0700 Subject: 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 07e63884f..63ed52420 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 9dfdfc8be..bc61f438c 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 b44cf9437..c9e5bb1a7 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 7a347aa11..96b8e9627 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 931c6c6e1..269fb3950 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 4c9f340c1..8cbc2b7cd 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 cbfdd835d..62ad00aec 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 -- cgit From d831ef39e2d84aa3e39bc3861085d563575efa43 Mon Sep 17 00:00:00 2001 From: Joshua McKenty Date: Wed, 7 Jul 2010 12:15:11 -0700 Subject: Adding more tests, refactoring for dhcp logic. --- nova/tests/network_unittest.py | 106 ++++++++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 44 deletions(-) diff --git a/nova/tests/network_unittest.py b/nova/tests/network_unittest.py index 8cbc2b7cd..67ae7ad2d 100644 --- a/nova/tests/network_unittest.py +++ b/nova/tests/network_unittest.py @@ -29,6 +29,7 @@ import IPy from nova import flags from nova import test from nova import exception +from nova.compute.exception import NoMoreAddresses from nova.compute import network from nova.auth import users from nova import utils @@ -67,24 +68,21 @@ class NetworkTestCase(test.TrialTestCase): self.assertTrue(IPy.IP(address) in self.network.network) def test_allocate_deallocate_ip(self): - # Address should be allocated - # Then, simulate acquisition of the address - # Deallocate it, and wait for simulated ip release - # then confirm it's gone. address = network.allocate_ip( "netuser", "project0", utils.generate_mac()) logging.debug("Was allocated %s" % (address)) net = network.get_project_network("project0", "default") - self.assertEqual(True, address in self._get_project_addresses("project0")) + self.assertEqual(True, is_in_project(address, "project0")) mac = utils.generate_mac() hostname = "test-host" self.dnsmasq.issue_ip(mac, address, hostname, net.bridge_name) rv = network.deallocate_ip(address) + # Doesn't go away until it's dhcp released - self.assertEqual(True, address in self._get_project_addresses("project0")) + self.assertEqual(True, is_in_project(address, "project0")) self.dnsmasq.release_ip(mac, address, hostname, net.bridge_name) - self.assertEqual(False, address in self._get_project_addresses("project0")) + self.assertEqual(False, is_in_project(address, "project0")) def test_range_allocation(self): mac = utils.generate_mac() @@ -97,11 +95,10 @@ class NetworkTestCase(test.TrialTestCase): 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")) + self.assertEqual(True, is_in_project(address, "project0")) + self.assertEqual(True, is_in_project(secondaddress, "project1")) + self.assertEqual(False, is_in_project(address, "project1")) + # Addresses are allocated before they're issued self.dnsmasq.issue_ip(mac, address, hostname, net.bridge_name) self.dnsmasq.issue_ip(secondmac, secondaddress, @@ -109,58 +106,79 @@ class NetworkTestCase(test.TrialTestCase): 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")) + self.assertEqual(False, is_in_project(address, "project0")) + # First address release shouldn't affect the second - self.assertEqual(True, - secondaddress in self._get_project_addresses("project1")) + self.assertEqual(True, is_in_project(secondaddress, "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")) + self.assertEqual(False, is_in_project(secondaddress, "project1")) def test_subnet_edge(self): secondaddress = network.allocate_ip("netuser", "project0", utils.generate_mac()) + hostname = "toomany-hosts" for project in range(1,5): project_id = "project%s" % (project) + mac = utils.generate_mac() + mac2 = utils.generate_mac() + mac3 = utils.generate_mac() address = network.allocate_ip( - "netuser", project_id, utils.generate_mac()) + "netuser", project_id, mac) address2 = network.allocate_ip( - "netuser", project_id, utils.generate_mac()) + "netuser", project_id, mac2) address3 = network.allocate_ip( - "netuser", project_id, utils.generate_mac()) - self.assertEqual(False, - address in self._get_project_addresses("project0")) - self.assertEqual(False, - address2 in self._get_project_addresses("project0")) - self.assertEqual(False, - address3 in self._get_project_addresses("project0")) + "netuser", project_id, mac3) + self.assertEqual(False, is_in_project(address, "project0")) + self.assertEqual(False, is_in_project(address2, "project0")) + self.assertEqual(False, is_in_project(address3, "project0")) rv = network.deallocate_ip(address) rv = network.deallocate_ip(address2) rv = network.deallocate_ip(address3) + net = network.get_project_network(project_id, "default") + self.dnsmasq.release_ip(mac, address, hostname, net.bridge_name) + self.dnsmasq.release_ip(mac2, address2, hostname, net.bridge_name) + self.dnsmasq.release_ip(mac3, address3, hostname, net.bridge_name) + net = network.get_project_network("project0", "default") rv = network.deallocate_ip(secondaddress) + self.dnsmasq.release_ip(mac, address, hostname, net.bridge_name) - def test_too_many_projects(self): - 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, 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 test_release_before_deallocate(self): + pass + + def test_deallocate_before_issued(self): + pass + + def test_too_many_addresses(self): + """ + Network size is 32, there are 5 addresses reserved for VPN. + So we should get 24 usable addresses + """ + net = network.get_project_network("project0", "default") + hostname = "toomany-hosts" + macs = {} + addresses = {} + for i in range(0, 23): + macs[i] = utils.generate_mac() + addresses[i] = network.allocate_ip("netuser", "project0", macs[i]) + self.dnsmasq.issue_ip(macs[i], addresses[i], hostname, net.bridge_name) + + self.assertRaises(NoMoreAddresses, network.allocate_ip, "netuser", "project0", utils.generate_mac()) + + for i in range(0, 23): + rv = network.deallocate_ip(addresses[i]) + self.dnsmasq.release_ip(macs[i], addresses[i], hostname, net.bridge_name) + +def is_in_project(address, project_id): + return address in network.get_project_network(project_id).list_addresses() - def _get_project_addresses(self, project_id): - project_addresses = [] - for addr in network.get_project_network(project_id).list_addresses(): - project_addresses.append(addr) - return project_addresses +def _get_project_addresses(project_id): + project_addresses = [] + for addr in network.get_project_network(project_id).list_addresses(): + project_addresses.append(addr) + return project_addresses def binpath(script): return os.path.abspath(os.path.join(__file__, "../../../bin", script)) -- cgit From 84c69c32297a1f79e8630102497e1e7e0041a324 Mon Sep 17 00:00:00 2001 From: Joshua McKenty Date: Wed, 7 Jul 2010 12:24:17 -0700 Subject: Off by one error in the allocation test (can someone check my subnet math?) --- nova/tests/network_unittest.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/nova/tests/network_unittest.py b/nova/tests/network_unittest.py index 67ae7ad2d..4d79758fb 100644 --- a/nova/tests/network_unittest.py +++ b/nova/tests/network_unittest.py @@ -21,7 +21,6 @@ import os import logging import unittest -import time from nova import vendor import IPy @@ -154,20 +153,20 @@ class NetworkTestCase(test.TrialTestCase): def test_too_many_addresses(self): """ Network size is 32, there are 5 addresses reserved for VPN. - So we should get 24 usable addresses + So we should get 23 usable addresses """ net = network.get_project_network("project0", "default") hostname = "toomany-hosts" macs = {} addresses = {} - for i in range(0, 23): + for i in range(0, 22): macs[i] = utils.generate_mac() addresses[i] = network.allocate_ip("netuser", "project0", macs[i]) self.dnsmasq.issue_ip(macs[i], addresses[i], hostname, net.bridge_name) self.assertRaises(NoMoreAddresses, network.allocate_ip, "netuser", "project0", utils.generate_mac()) - for i in range(0, 23): + for i in range(0, 22): rv = network.deallocate_ip(addresses[i]) self.dnsmasq.release_ip(macs[i], addresses[i], hostname, net.bridge_name) -- cgit From b9a8bd2d55f016fba305a00c985c7f6769afddd2 Mon Sep 17 00:00:00 2001 From: Joshua McKenty Date: Wed, 7 Jul 2010 12:24:22 -0700 Subject: Fixes as per Vish review (whitespace, import statements) --- nova/endpoint/cloud.py | 2 +- nova/utils.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/endpoint/cloud.py b/nova/endpoint/cloud.py index 269fb3950..a2f60b47f 100644 --- a/nova/endpoint/cloud.py +++ b/nova/endpoint/cloud.py @@ -504,7 +504,7 @@ class CloudController(object): def lease_ip(self, context, private_ip, **kwargs): self.network.lease_ip(private_ip) - return defer.succeed({'leaseResponse': ["Address lease."]}) + return defer.succeed({'leaseResponse': ["Address leased."]}) @rbac.allow('projectmanager', 'sysadmin') def run_instances(self, context, **kwargs): diff --git a/nova/utils.py b/nova/utils.py index 62ad00aec..1041733f1 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -26,7 +26,7 @@ import logging import socket import sys import os.path -from os import environ +import os.environ import inspect import subprocess import random -- cgit From 6d695e8410ad40b886cf6aff3a1e41598dcdfc66 Mon Sep 17 00:00:00 2001 From: Joshua McKenty Date: Wed, 7 Jul 2010 12:24:24 -0700 Subject: Fixed the os.environ patch (bogus) --- nova/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/utils.py b/nova/utils.py index 1041733f1..2f0ce33cd 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -26,7 +26,7 @@ import logging import socket import sys import os.path -import os.environ +import os import inspect import subprocess import random -- cgit From faada0612d8e8580a2a932626c8972b7c2a4ef59 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 7 Jul 2010 12:25:22 -0700 Subject: whitespace fixes and header changes --- bin/dhcpleasor.py | 42 ++++++++++++++++++++++++------------------ nova/tests/network_unittest.py | 36 ++++++++++++++++++------------------ 2 files changed, 42 insertions(+), 36 deletions(-) diff --git a/bin/dhcpleasor.py b/bin/dhcpleasor.py index 63ed52420..30f8fbdc3 100755 --- a/bin/dhcpleasor.py +++ b/bin/dhcpleasor.py @@ -1,18 +1,24 @@ -#!/opt/local/bin/python +#!/usr/bin/env python +# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright [2010] [Anso Labs, LLC] -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Copyright 2010 Anso Labs, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + """ dhcpleasor.py @@ -37,21 +43,21 @@ FLAGS = flags.FLAGS def add_lease(mac, ip, hostname, interface): if FLAGS.fake_rabbit: - network.lease_ip(ip) + network.lease_ip(ip) else: rpc.cast(FLAGS.cloud_topic, {"method": "lease_ip", "args" : {"address": ip}}) def old_lease(mac, ip, hostname, interface): logging.debug("Adopted old lease or got a change of mac/hostname") - + def del_lease(mac, ip, hostname, interface): if FLAGS.fake_rabbit: - network.release_ip(ip) + network.release_ip(ip) else: rpc.cast(FLAGS.cloud_topic, {"method": "release_ip", - "args" : {"address": ip}}) - + "args" : {"address": ip}}) + def init_leases(interface): net = network.get_network_by_interface(interface) res = "" diff --git a/nova/tests/network_unittest.py b/nova/tests/network_unittest.py index 4d79758fb..bccaacfa7 100644 --- a/nova/tests/network_unittest.py +++ b/nova/tests/network_unittest.py @@ -76,10 +76,10 @@ class NetworkTestCase(test.TrialTestCase): hostname = "test-host" self.dnsmasq.issue_ip(mac, address, hostname, net.bridge_name) rv = network.deallocate_ip(address) - + # Doesn't go away until it's dhcp released self.assertEqual(True, is_in_project(address, "project0")) - + self.dnsmasq.release_ip(mac, address, hostname, net.bridge_name) self.assertEqual(False, is_in_project(address, "project0")) @@ -93,25 +93,25 @@ class NetworkTestCase(test.TrialTestCase): "netuser", "project1", secondmac) net = network.get_project_network("project0", "default") secondnet = network.get_project_network("project1", "default") - + self.assertEqual(True, is_in_project(address, "project0")) self.assertEqual(True, is_in_project(secondaddress, "project1")) self.assertEqual(False, is_in_project(address, "project1")) - + # Addresses are allocated before they're issued self.dnsmasq.issue_ip(mac, address, hostname, net.bridge_name) - self.dnsmasq.issue_ip(secondmac, secondaddress, + 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, is_in_project(address, "project0")) - + # First address release shouldn't affect the second self.assertEqual(True, is_in_project(secondaddress, "project1")) - + rv = network.deallocate_ip(secondaddress) - self.dnsmasq.release_ip(secondmac, secondaddress, + self.dnsmasq.release_ip(secondmac, secondaddress, hostname, secondnet.bridge_name) self.assertEqual(False, is_in_project(secondaddress, "project1")) @@ -146,15 +146,15 @@ class NetworkTestCase(test.TrialTestCase): def test_release_before_deallocate(self): pass - + def test_deallocate_before_issued(self): pass - - def test_too_many_addresses(self): + + def test_too_many_addresses(self): """ Network size is 32, there are 5 addresses reserved for VPN. So we should get 23 usable addresses - """ + """ net = network.get_project_network("project0", "default") hostname = "toomany-hosts" macs = {} @@ -163,10 +163,10 @@ class NetworkTestCase(test.TrialTestCase): macs[i] = utils.generate_mac() addresses[i] = network.allocate_ip("netuser", "project0", macs[i]) self.dnsmasq.issue_ip(macs[i], addresses[i], hostname, net.bridge_name) - + self.assertRaises(NoMoreAddresses, network.allocate_ip, "netuser", "project0", utils.generate_mac()) - - for i in range(0, 22): + + for i in range(0, 22): rv = network.deallocate_ip(addresses[i]) self.dnsmasq.release_ip(macs[i], addresses[i], hostname, net.bridge_name) @@ -188,10 +188,10 @@ class FakeDNSMasq(object): env = {'DNSMASQ_INTERFACE': interface, 'TESTING' : '1'} (out, err) = utils.execute(cmd, addl_env=env) 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, 'TESTING' : '1'} (out, err) = utils.execute(cmd, addl_env=env) logging.debug("RELEASE_IP: %s, %s " % (out, err)) - \ No newline at end of file + -- cgit