Capture signals from dnsmasq and use them to update network state.
This commit is contained in:
parent
5e8337aec0
commit
b7ea2f7058
68
bin/dhcpleasor.py
Executable file
68
bin/dhcpleasor.py
Executable file
@ -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())
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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,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)
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user