Merge of DHCP changes including dnsmasq callbacks
Conflicts: nova/utils.py
This commit is contained in:
		
							
								
								
									
										92
									
								
								bin/dhcpleasor.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										92
									
								
								bin/dhcpleasor.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,92 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 | 
			
		||||
 | 
			
		||||
# 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.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
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 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):
 | 
			
		||||
    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):
 | 
			
		||||
    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)
 | 
			
		||||
    else:
 | 
			
		||||
        rpc.cast(FLAGS.cloud_topic, {"method": "release_ip",
 | 
			
		||||
                "args" : {"address": ip}})
 | 
			
		||||
 | 
			
		||||
def init_leases(interface):
 | 
			
		||||
    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')
 | 
			
		||||
    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]
 | 
			
		||||
        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)
 | 
			
		||||
    exit(0)
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    sys.exit(main())
 | 
			
		||||
@@ -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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -500,6 +500,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 leased."]})
 | 
			
		||||
 | 
			
		||||
    @rbac.allow('projectmanager', 'sysadmin')
 | 
			
		||||
    def run_instances(self, context, **kwargs):
 | 
			
		||||
        # make sure user can access the image
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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,8 @@ 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
 | 
			
		||||
@@ -40,6 +43,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
 | 
			
		||||
@@ -66,59 +70,128 @@ class NetworkTestCase(test.TrialTestCase):
 | 
			
		||||
        address = network.allocate_ip(
 | 
			
		||||
                "netuser", "project0", utils.generate_mac())
 | 
			
		||||
        logging.debug("Was allocated %s" % (address))
 | 
			
		||||
        self.assertEqual(True, address in self._get_project_addresses("project0"))
 | 
			
		||||
        net = network.get_project_network("project0", "default")
 | 
			
		||||
        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)
 | 
			
		||||
        self.assertEqual(False, address in self._get_project_addresses("project0"))
 | 
			
		||||
 | 
			
		||||
        # 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"))
 | 
			
		||||
 | 
			
		||||
    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())
 | 
			
		||||
        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"))
 | 
			
		||||
                "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,
 | 
			
		||||
                                hostname, secondnet.bridge_name)
 | 
			
		||||
 | 
			
		||||
        rv = network.deallocate_ip(address)
 | 
			
		||||
        self.assertEqual(False, address in self._get_project_addresses("project0"))
 | 
			
		||||
        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.assertEqual(False,
 | 
			
		||||
                         secondaddress in self._get_project_addresses("project1"))
 | 
			
		||||
        self.dnsmasq.release_ip(secondmac, secondaddress,
 | 
			
		||||
                                hostname, secondnet.bridge_name)
 | 
			
		||||
        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)
 | 
			
		||||
            address = network.allocate_ip(
 | 
			
		||||
                    "netuser", name, utils.generate_mac())
 | 
			
		||||
            rv = network.deallocate_ip(address)
 | 
			
		||||
            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 23 usable addresses
 | 
			
		||||
        """
 | 
			
		||||
        net = network.get_project_network("project0", "default")
 | 
			
		||||
        hostname = "toomany-hosts"
 | 
			
		||||
        macs = {}
 | 
			
		||||
        addresses = {}
 | 
			
		||||
        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, 22):
 | 
			
		||||
            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(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))
 | 
			
		||||
 | 
			
		||||
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, '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))
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user