Josh's networking refactor, modified to work with projects

This commit is contained in:
Vishvananda Ishaya
2010-05-31 18:56:20 -07:00
parent 94518726fb
commit f04c6ab2d0
6 changed files with 357 additions and 510 deletions

View File

@@ -1,25 +1,11 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # 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
#
# 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.
import logging import logging
import os
import signal import signal
import os
import nova.utils
import subprocess import subprocess
from nova import utils
# todo(ja): does the definition of network_path belong here? # todo(ja): does the definition of network_path belong here?
from nova import flags from nova import flags
@@ -30,13 +16,13 @@ def execute(cmd):
logging.debug("FAKE NET: %s" % cmd) logging.debug("FAKE NET: %s" % cmd)
return "fake", 0 return "fake", 0
else: else:
utils.execute(cmd) return nova.utils.execute(cmd)
def runthis(desc, cmd): def runthis(desc, cmd):
if FLAGS.fake_network: if FLAGS.fake_network:
execute(cmd) return execute(cmd)
else: else:
utils.runthis(desc,cmd) return nova.utils.runthis(desc,cmd)
def Popen(cmd): def Popen(cmd):
if FLAGS.fake_network: if FLAGS.fake_network:
@@ -61,44 +47,46 @@ def bind_public_ip(ip, interface):
def vlan_create(net): def vlan_create(net):
""" create a vlan on on a bridge device unless vlan already exists """ """ create a vlan on on a bridge device unless vlan already exists """
if not device_exists("vlan%s" % net.vlan): if not device_exists("vlan%s" % net['vlan']):
logging.debug("Starting VLAN inteface for %s network", (net['vlan']))
execute("sudo vconfig set_name_type VLAN_PLUS_VID_NO_PAD") execute("sudo vconfig set_name_type VLAN_PLUS_VID_NO_PAD")
execute("sudo vconfig add %s %s" % (net.bridge_dev, net.vlan)) execute("sudo vconfig add %s %s" % (FLAGS.bridge_dev, net['vlan']))
execute("sudo ifconfig vlan%s up" % (net.vlan)) execute("sudo ifconfig vlan%s up" % (net['vlan']))
def bridge_create(net): def bridge_create(net):
""" create a bridge on a vlan unless it already exists """ """ create a bridge on a vlan unless it already exists """
if not device_exists(net.bridge_name): if not device_exists(net['bridge_name']):
execute("sudo brctl addbr %s" % (net.bridge_name)) logging.debug("Starting Bridge inteface for %s network", (net['vlan']))
execute("sudo brctl addbr %s" % (net['bridge_name']))
# execute("sudo brctl setfd %s 0" % (net.bridge_name)) # execute("sudo brctl setfd %s 0" % (net.bridge_name))
# execute("sudo brctl setageing %s 10" % (net.bridge_name)) # execute("sudo brctl setageing %s 10" % (net.bridge_name))
execute("sudo brctl stp %s off" % (net.bridge_name)) execute("sudo brctl stp %s off" % (net['bridge_name']))
execute("sudo brctl addif %s vlan%s" % (net.bridge_name, net.vlan)) execute("sudo brctl addif %s vlan%s" % (net['bridge_name'], net['vlan']))
if net.bridge_gets_ip: if net.bridge_gets_ip:
execute("sudo ifconfig %s %s broadcast %s netmask %s up" % \ execute("sudo ifconfig %s %s broadcast %s netmask %s up" % \
(net.bridge_name, net.gateway, net.broadcast, net.netmask)) (net['bridge_name'], net.gateway, net.broadcast, net.netmask))
confirm_rule("FORWARD --in-interface %s -j ACCEPT" % (net.bridge_name)) confirm_rule("FORWARD --in-interface %s -j ACCEPT" % (net['bridge_name']))
else: else:
execute("sudo ifconfig %s up" % net.bridge_name) execute("sudo ifconfig %s up" % net['bridge_name'])
def dnsmasq_cmd(net): def dnsmasq_cmd(net):
cmd = ['sudo dnsmasq', cmd = ['sudo dnsmasq',
' --strict-order', ' --strict-order',
' --bind-interfaces', ' --bind-interfaces',
' --conf-file=', ' --conf-file=',
' --pid-file=%s' % dhcp_file(net.vlan, 'pid'), ' --pid-file=%s' % dhcp_file(net['vlan'], 'pid'),
' --listen-address=%s' % net.dhcp_listen_address, ' --listen-address=%s' % net.dhcp_listen_address,
' --except-interface=lo', ' --except-interface=lo',
' --dhcp-range=%s,%s,120s' % (net.dhcp_range_start, net.dhcp_range_end), ' --dhcp-range=%s,static,120s' % (net.dhcp_range_start),
' --dhcp-lease-max=61', ' --dhcp-lease-max=61',
' --dhcp-hostsfile=%s' % dhcp_file(net.vlan, 'conf'), ' --dhcp-hostsfile=%s' % dhcp_file(net['vlan'], 'conf'),
' --dhcp-leasefile=%s' % dhcp_file(net.vlan, 'leases')] ' --dhcp-leasefile=%s' % dhcp_file(net['vlan'], 'leases')]
return ''.join(cmd) return ''.join(cmd)
def hostDHCP(network, host): def hostDHCP(network, host, mac):
idx = host['address'].split(".")[-1] # Logically, the idx of instances they've launched in this net idx = host.split(".")[-1] # Logically, the idx of instances they've launched in this net
return "%s,%s-%s-%s.novalocal,%s" % \ return "%s,%s-%s-%s.novalocal,%s" % \
(host['mac'], host['user_id'], network.vlan, idx, host['address']) (mac, network['user_id'], network['vlan'], idx, host)
# todo(ja): if the system has restarted or pid numbers have wrapped # todo(ja): if the system has restarted or pid numbers have wrapped
# then you cannot be certain that the pid refers to the # then you cannot be certain that the pid refers to the
@@ -111,9 +99,9 @@ def start_dnsmasq(network):
if a dnsmasq instance is already running then send a HUP if a dnsmasq instance is already running then send a HUP
signal causing it to reload, otherwise spawn a new instance signal causing it to reload, otherwise spawn a new instance
""" """
with open(dhcp_file(network.vlan, 'conf'), 'w') as f: with open(dhcp_file(network['vlan'], 'conf'), 'w') as f:
for host_name in network.hosts: for host_name in network.hosts:
f.write("%s\n" % hostDHCP(network, network.hosts[host_name])) f.write("%s\n" % hostDHCP(network, host_name, network.hosts[host_name]))
pid = dnsmasq_pid_for(network) pid = dnsmasq_pid_for(network)
@@ -123,12 +111,11 @@ def start_dnsmasq(network):
# correct dnsmasq process # correct dnsmasq process
try: try:
os.kill(pid, signal.SIGHUP) os.kill(pid, signal.SIGHUP)
return
except Exception, e: except Exception, e:
logging.debug("Hupping dnsmasq threw %s", e) logging.debug("Hupping dnsmasq threw %s", e)
# otherwise delete the existing leases file and start dnsmasq # otherwise delete the existing leases file and start dnsmasq
lease_file = dhcp_file(network.vlan, 'leases') lease_file = dhcp_file(network['vlan'], 'leases')
if os.path.exists(lease_file): if os.path.exists(lease_file):
os.unlink(lease_file) os.unlink(lease_file)
@@ -156,7 +143,7 @@ def dnsmasq_pid_for(network):
if machine has rebooted pid might be incorrect (caller should check) if machine has rebooted pid might be incorrect (caller should check)
""" """
pid_file = dhcp_file(network.vlan, 'pid') pid_file = dhcp_file(network['vlan'], 'pid')
if os.path.exists(pid_file): if os.path.exists(pid_file):
with open(pid_file, 'r') as f: with open(pid_file, 'r') as f:

View File

@@ -17,15 +17,16 @@
Classes for network control, including VLANs, DHCP, and IP allocation. Classes for network control, including VLANs, DHCP, and IP allocation.
""" """
import json
import logging import logging
import os import os
import time
# TODO(termie): clean up these imports # TODO(termie): clean up these imports
from nova import vendor from nova import vendor
import IPy import IPy
from nova import datastore from nova import datastore
import nova.exception
from nova.compute import exception from nova.compute import exception
from nova import flags from nova import flags
from nova import utils from nova import utils
@@ -34,145 +35,129 @@ from nova.auth import users
import linux_net import linux_net
FLAGS = flags.FLAGS FLAGS = flags.FLAGS
flags.DEFINE_string('net_libvirt_xml_template',
utils.abspath('compute/net.libvirt.xml.template'),
'Template file for libvirt networks')
flags.DEFINE_string('networks_path', utils.abspath('../networks'), flags.DEFINE_string('networks_path', utils.abspath('../networks'),
'Location to keep network config files') 'Location to keep network config files')
flags.DEFINE_integer('public_vlan', 1, 'VLAN for public IP addresses') flags.DEFINE_integer('public_vlan', 1, 'VLAN for public IP addresses')
flags.DEFINE_string('public_interface', 'vlan1', 'Interface for public IP addresses') flags.DEFINE_string('public_interface', 'vlan1',
'Interface for public IP addresses')
flags.DEFINE_string('bridge_dev', 'eth1', flags.DEFINE_string('bridge_dev', 'eth1',
'network device for bridges') 'network device for bridges')
flags.DEFINE_integer('vlan_start', 100, 'First VLAN for private networks') flags.DEFINE_integer('vlan_start', 100, 'First VLAN for private networks')
flags.DEFINE_integer('vlan_end', 4093, 'Last VLAN for private networks') flags.DEFINE_integer('vlan_end', 4093, 'Last VLAN for private networks')
flags.DEFINE_integer('network_size', 256, 'Number of addresses in each private subnet') flags.DEFINE_integer('network_size', 256,
'Number of addresses in each private subnet')
flags.DEFINE_string('public_range', '4.4.4.0/24', 'Public IP address block') flags.DEFINE_string('public_range', '4.4.4.0/24', 'Public IP address block')
flags.DEFINE_string('private_range', '10.0.0.0/8', 'Private IP address block') flags.DEFINE_string('private_range', '10.0.0.0/8', 'Private IP address block')
# HACK(vish): to delay _get_keeper() loading
def _get_keeper():
if _get_keeper.keeper == None:
_get_keeper.keeper = datastore.Keeper(prefix="net")
return _get_keeper.keeper
_get_keeper.keeper = None
logging.getLogger().setLevel(logging.DEBUG) logging.getLogger().setLevel(logging.DEBUG)
# CLEANUP:
# TODO(ja): use singleton for usermanager instead of self.manager in vlanpool et al
# TODO(ja): does vlanpool "keeper" need to know the min/max - shouldn't FLAGS always win?
class Network(object): class BaseNetwork(datastore.RedisModel):
def __init__(self, *args, **kwargs): bridge_gets_ip = False
self.bridge_gets_ip = False object_type = 'network'
try:
os.makedirs(FLAGS.networks_path)
except Exception, err:
pass
self.load(**kwargs)
def to_dict(self):
return {'vlan': self.vlan,
'network': self.network_str,
'hosts': self.hosts}
def load(self, **kwargs):
self.network_str = kwargs.get('network', "192.168.100.0/24")
self.hosts = kwargs.get('hosts', {})
self.vlan = kwargs.get('vlan', 100)
self.name = "nova-%s" % (self.vlan)
self.network = IPy.IP(self.network_str)
self.gateway = self.network[1]
self.netmask = self.network.netmask()
self.broadcast = self.network.broadcast()
self.bridge_name = "br%s" % (self.vlan)
def __str__(self):
return json.dumps(self.to_dict())
def __unicode__(self):
return json.dumps(self.to_dict())
@classmethod @classmethod
def from_dict(cls, args): def get_all_hosts(cls):
for arg in args.keys(): for vlan in get_assigned_vlans().values():
value = args[arg] network_str = get_subnet_from_vlan(vlan)
del args[arg] for addr in datastore.Redis.instance().hgetall(
args[str(arg)] = value "network:%s:hosts" % (network_str)):
self = cls(**args) yield addr
return self
@classmethod @classmethod
def from_json(cls, json_string): def create(cls, user_id, project_id, security_group, vlan, network_str):
parsed = json.loads(json_string) network_id = "%s:%s" % (project_id, security_group)
return cls.from_dict(parsed) net = cls(network_id, network_str)
net['user_id'] = user_id
net['project_id'] = project_id
net["vlan"] = vlan
net["bridge_name"] = "br%s" % vlan
net.save()
return net
def range(self): def __init__(self, network_id, network_str=None):
for idx in range(3, len(self.network)-2): super(BaseNetwork, self).__init__(object_id=network_id)
yield self.network[idx] self['network_id'] = network_id
self['network_str'] = network_str
self.save()
@property
def network(self):
return IPy.IP(self['network_str'])
@property
def netmask(self):
return self.network.netmask()
@property
def broadcast(self):
return self.network.broadcast()
@property
def bridge_name(self):
return "br%s" % (self["vlan"])
@property
def user(self):
return users.UserManager.instance().get_user(self['user_id'])
@property
def project(self):
return users.UserManager.instance().get_project(self['project_id'])
@property
def _hosts_key(self):
return "network:%s:hosts" % (self['network_str'])
@property
def hosts(self):
return datastore.Redis.instance().hgetall(self._hosts_key)
def _add_host(self, _user_id, _project_id, host, target):
datastore.Redis.instance().hset(self._hosts_key, host, target)
def _rem_host(self, host):
datastore.Redis.instance().hdel(self._hosts_key, host)
@property
def assigned(self):
return datastore.Redis.instance().hkeys(self._hosts_key)
@property
def available(self):
for idx in range(3, len(self.network) - 1):
address = str(self.network[idx])
if not address in self.hosts.keys():
yield str(address)
def allocate_ip(self, user_id, project_id, mac): def allocate_ip(self, user_id, project_id, mac):
for ip in self.range(): for address in self.available:
address = str(ip)
if not address in self.hosts.keys():
logging.debug("Allocating IP %s to %s" % (address, project_id)) logging.debug("Allocating IP %s to %s" % (address, project_id))
self.hosts[address] = { self._add_host(user_id, project_id, address, mac)
"address" : address, "user_id": user_id, "project_id" : project_id, 'mac' : mac
}
self.express(address=address) self.express(address=address)
return address return address
raise exception.NoMoreAddresses() raise exception.NoMoreAddresses()
def deallocate_ip(self, ip_str): def deallocate_ip(self, ip_str):
if not ip_str in self.hosts.keys(): if not ip_str in self.assigned:
raise exception.AddressNotAllocated() raise exception.AddressNotAllocated()
del self.hosts[ip_str] self._rem_host(ip_str)
# TODO(joshua) SCRUB from the leases file somehow
self.deexpress(address=ip_str) self.deexpress(address=ip_str)
def list_addresses(self): def list_addresses(self):
for address in self.hosts.values(): for address in self.hosts:
yield address yield address
def express(self, address=None): def express(self, address=None): pass
pass def deexpress(self, address=None): pass
def deexpress(self, address=None):
pass
class Vlan(Network): class BridgedNetwork(BaseNetwork):
""" """
VLAN configuration, that when expressed creates the vlan Virtual Network that can express itself to create a vlan and
a bridge (with or without an IP address/netmask/gateway)
properties:
vlan - integer (example: 42)
bridge_dev - string (example: eth0)
"""
def __init__(self, *args, **kwargs):
super(Vlan, self).__init__(*args, **kwargs)
self.bridge_dev = FLAGS.bridge_dev
def express(self, address=None):
super(Vlan, self).express(address=address)
try:
logging.debug("Starting VLAN inteface for %s network" % (self.vlan))
linux_net.vlan_create(self)
except:
pass
class VirtNetwork(Vlan):
"""
Virtual Network that can export libvirt configuration or express itself to
create a bridge (with or without an IP address/netmask/gateway)
properties: properties:
bridge_name - string (example value: br42) bridge_name - string (example value: br42)
vlan - integer (example value: 42) vlan - integer (example value: 42)
bridge_dev - string (example: eth0)
bridge_gets_ip - boolean used during bridge creation bridge_gets_ip - boolean used during bridge creation
if bridge_gets_ip then network address for bridge uses the properties: if bridge_gets_ip then network address for bridge uses the properties:
@@ -181,333 +166,242 @@ class VirtNetwork(Vlan):
netmask netmask
""" """
@classmethod
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))
return cls.create(user_id, project_id, security_group, vlan, network_str)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(VirtNetwork, self).__init__(*args, **kwargs) super(BridgedNetwork, self).__init__(*args, **kwargs)
self['bridge_dev'] = FLAGS.bridge_dev
def virtXML(self): self.save()
""" generate XML for libvirt network """
libvirt_xml = open(FLAGS.net_libvirt_xml_template).read()
xml_info = {'name' : self.name,
'bridge_name' : self.bridge_name,
'device' : "vlan%s" % (self.vlan),
'gateway' : self.gateway,
'netmask' : self.netmask,
}
libvirt_xml = libvirt_xml % xml_info
return libvirt_xml
def express(self, address=None): def express(self, address=None):
""" creates a bridge device on top of the Vlan """ super(BridgedNetwork, self).express(address=address)
super(VirtNetwork, self).express(address=address) linux_net.vlan_create(self)
try:
logging.debug("Starting Bridge inteface for %s network" % (self.vlan))
linux_net.bridge_create(self) linux_net.bridge_create(self)
except:
pass
class DHCPNetwork(VirtNetwork): class DHCPNetwork(BridgedNetwork):
""" """
properties: properties:
dhcp_listen_address: the ip of the gateway / dhcp host dhcp_listen_address: the ip of the gateway / dhcp host
dhcp_range_start: the first ip to give out dhcp_range_start: the first ip to give out
dhcp_range_end: the last ip to give out dhcp_range_end: the last ip to give out
""" """
bridge_gets_ip = True
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(DHCPNetwork, self).__init__(*args, **kwargs) super(DHCPNetwork, self).__init__(*args, **kwargs)
logging.debug("Initing DHCPNetwork object...") logging.debug("Initing DHCPNetwork object...")
self.bridge_gets_ip = True
self.dhcp_listen_address = self.network[1] self.dhcp_listen_address = self.network[1]
self.dhcp_range_start = self.network[3] self.dhcp_range_start = self.network[3]
self.dhcp_range_end = self.network[-2] self.dhcp_range_end = self.network[-(1 + FLAGS.cnt_vpn_clients)]
try:
os.makedirs(FLAGS.networks_path)
except Exception, err:
pass
def express(self, address=None): def express(self, address=None):
super(DHCPNetwork, self).express(address=address) super(DHCPNetwork, self).express(address=address)
if len(self.hosts.values()) > 0: if len(self.assigned) > 0:
logging.debug("Starting dnsmasq server for network with vlan %s" % self.vlan) logging.debug("Starting dnsmasq server for network with vlan %s",
self['vlan'])
linux_net.start_dnsmasq(self) linux_net.start_dnsmasq(self)
else: else:
logging.debug("Not launching dnsmasq cause I don't think we have any hosts.") logging.debug("Not launching dnsmasq: no hosts.")
def deexpress(self, address=None): def deexpress(self, address=None):
# if this is the last address, stop dns # if this is the last address, stop dns
super(DHCPNetwork, self).deexpress(address=address) super(DHCPNetwork, self).deexpress(address=address)
if len(self.hosts.values()) == 0: if len(self.assigned) == 0:
linux_net.stop_dnsmasq(self) linux_net.stop_dnsmasq(self)
else: else:
linux_net.start_dnsmasq(self) linux_net.start_dnsmasq(self)
class PrivateNetwork(DHCPNetwork): class PublicAddress(datastore.RedisModel):
def __init__(self, **kwargs): object_type="address"
super(PrivateNetwork, self).__init__(**kwargs)
# self.express()
def to_dict(self): def __init__(self, address):
return {'vlan': self.vlan, super(PublicAddress, self).__init__(address)
'network': self.network_str,
'hosts': self.hosts}
class PublicNetwork(Network): @classmethod
def __init__(self, network="192.168.216.0/24", **kwargs): def create(cls, user_id, project_id, address):
super(PublicNetwork, self).__init__(network=network, **kwargs) addr = cls(address=address)
addr['address'] = address
addr['user_id'] = user_id
addr['project_id'] = project_id
addr['instance_id'] = 'available'
addr['private_ip'] = 'available'
addr["create_time"] = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
addr.save()
return addr
DEFAULT_PORTS = [("tcp",80), ("tcp",22), ("udp",1194), ("tcp",443)]
class PublicNetworkController(BaseNetwork):
def __init__(self, *args, **kwargs):
network_id = "public:default"
super(PublicNetworkController, self).__init__(network_id, FLAGS.public_range)
self['user_id'] = "public"
self['project_id'] = "public"
self["create_time"] = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
self["vlan"] = FLAGS.public_vlan
self.save()
self.express() self.express()
def allocate_ip(self, user_id, project_id, mac): @property
for ip in self.range(): def available(self):
address = str(ip) for idx in range(2, len(self.network)-1):
address = str(self.network[idx])
if not address in self.hosts.keys(): if not address in self.hosts.keys():
logging.debug("Allocating IP %s to %s" % (address, project_id)) yield address
self.hosts[address] = {
"address" : address, "user_id": user_id, "project_id" : project_id, 'mac' : mac
}
self.express(address=address)
return address
raise exception.NoMoreAddresses()
def deallocate_ip(self, ip_str): @property
if not ip_str in self.hosts: def host_objs(self):
raise exception.AddressNotAllocated() for address in self.assigned:
del self.hosts[ip_str] yield PublicAddress(address)
# TODO(joshua) SCRUB from the leases file somehow
self.deexpress(address=ip_str) def get_public_ip_for_instance(self, instance_id):
# FIXME: this should be a lookup - iteration won't scale
for address_record in self.host_objs:
if address_record.get('instance_id', 'available') == instance_id:
return address_record['address']
def get_host(self, host):
if host in self.assigned:
return PublicAddress(host)
return None
def _add_host(self, user_id, project_id, host, _target):
datastore.Redis.instance().hset(self._hosts_key, host, project_id)
PublicAddress.create(user_id, project_id, host)
def _rem_host(self, host):
PublicAddress(host).destroy()
datastore.Redis.instance().hdel(self._hosts_key, host)
def associate_address(self, public_ip, private_ip, instance_id): def associate_address(self, public_ip, private_ip, instance_id):
if not public_ip in self.hosts: if not public_ip in self.assigned:
raise exception.AddressNotAllocated() raise exception.AddressNotAllocated()
for addr in self.hosts.values(): # TODO(joshua): Keep an index going both ways
if addr.has_key('private_ip') and addr['private_ip'] == private_ip: for addr in self.host_objs:
if addr.get('private_ip', None) == private_ip:
raise exception.AddressAlreadyAssociated() raise exception.AddressAlreadyAssociated()
if self.hosts[public_ip].has_key('private_ip'): addr = self.get_host(public_ip)
if addr.get('private_ip', 'available') != 'available':
raise exception.AddressAlreadyAssociated() raise exception.AddressAlreadyAssociated()
self.hosts[public_ip]['private_ip'] = private_ip addr['private_ip'] = private_ip
self.hosts[public_ip]['instance_id'] = instance_id addr['instance_id'] = instance_id
addr.save()
self.express(address=public_ip) self.express(address=public_ip)
def disassociate_address(self, public_ip): def disassociate_address(self, public_ip):
if not public_ip in self.hosts: if not public_ip in self.assigned:
raise exception.AddressNotAllocated() raise exception.AddressNotAllocated()
if not self.hosts[public_ip].has_key('private_ip'): addr = self.get_host(public_ip)
if addr.get('private_ip', 'available') == 'available':
raise exception.AddressNotAssociated() raise exception.AddressNotAssociated()
self.deexpress(public_ip) self.deexpress(address=public_ip)
del self.hosts[public_ip]['private_ip'] addr['private_ip'] = 'available'
del self.hosts[public_ip]['instance_id'] addr['instance_id'] = 'available'
# TODO Express the removal addr.save()
def deexpress(self, address):
addr = self.hosts[address]
public_ip = addr['address']
private_ip = addr['private_ip']
linux_net.remove_rule("PREROUTING -t nat -d %s -j DNAT --to %s" % (public_ip, private_ip))
linux_net.remove_rule("POSTROUTING -t nat -s %s -j SNAT --to %s" % (private_ip, public_ip))
linux_net.remove_rule("FORWARD -d %s -p icmp -j ACCEPT" % (private_ip))
for (protocol, port) in [("tcp",80), ("tcp",22), ("udp",1194), ("tcp",443)]:
linux_net.remove_rule("FORWARD -d %s -p %s --dport %s -j ACCEPT" % (private_ip, protocol, port))
def express(self, address=None): def express(self, address=None):
logging.debug("Todo - need to create IPTables natting entries for this net.") addresses = self.host_objs
addresses = self.hosts.values()
if address: if address:
addresses = [self.hosts[address]] addresses = [self.get_host(address)]
for addr in addresses: for addr in addresses:
if not addr.has_key('private_ip'): if addr.get('private_ip','available') == 'available':
continue continue
public_ip = addr['address'] public_ip = addr['address']
private_ip = addr['private_ip'] private_ip = addr['private_ip']
linux_net.bind_public_ip(public_ip, FLAGS.public_interface) linux_net.bind_public_ip(public_ip, FLAGS.public_interface)
linux_net.confirm_rule("PREROUTING -t nat -d %s -j DNAT --to %s" % (public_ip, private_ip)) linux_net.confirm_rule("PREROUTING -t nat -d %s -j DNAT --to %s"
linux_net.confirm_rule("POSTROUTING -t nat -s %s -j SNAT --to %s" % (private_ip, public_ip)) % (public_ip, private_ip))
linux_net.confirm_rule("POSTROUTING -t nat -s %s -j SNAT --to %s"
% (private_ip, public_ip))
# TODO: Get these from the secgroup datastore entries # TODO: Get these from the secgroup datastore entries
linux_net.confirm_rule("FORWARD -d %s -p icmp -j ACCEPT" % (private_ip)) linux_net.confirm_rule("FORWARD -d %s -p icmp -j ACCEPT"
for (protocol, port) in [("tcp",80), ("tcp",22), ("udp",1194), ("tcp",443)]: % (private_ip))
linux_net.confirm_rule("FORWARD -d %s -p %s --dport %s -j ACCEPT" % (private_ip, protocol, port)) for (protocol, port) in DEFAULT_PORTS:
linux_net.confirm_rule("FORWARD -d %s -p %s --dport %s -j ACCEPT"
% (private_ip, protocol, port))
def deexpress(self, address=None):
addr = self.get_host(address)
private_ip = addr['private_ip']
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"
% (private_ip, address))
linux_net.remove_rule("FORWARD -d %s -p icmp -j ACCEPT"
% (private_ip))
for (protocol, port) in DEFAULT_PORTS:
linux_net.remove_rule("FORWARD -d %s -p %s --dport %s -j ACCEPT"
% (private_ip, protocol, port))
class NetworkPool(object): VLANS_KEY = "vlans"
# TODO - Allocations need to be system global def _add_vlan(project_id, vlan):
datastore.Redis.instance().hset(VLANS_KEY, project_id, vlan)
def __init__(self): def _rem_vlan(project_id):
self.network = IPy.IP(FLAGS.private_range) datastore.Redis.instance().hdel(VLANS_KEY, project_id)
netsize = FLAGS.network_size
if not netsize in [4,8,16,32,64,128,256,512,1024]:
raise exception.NotValidNetworkSize()
self.netsize = netsize
self.startvlan = FLAGS.vlan_start
def get_from_vlan(self, vlan): def get_assigned_vlans():
start = (vlan-self.startvlan) * self.netsize """ Returns a dictionary, with keys of project_id and values of vlan_id """
net_str = "%s-%s" % (self.network[start], self.network[start + self.netsize - 1]) return datastore.Redis.instance().hgetall(VLANS_KEY)
logging.debug("Allocating %s" % net_str)
return net_str
def get_vlan_for_project(project_id):
class VlanPool(object): """
def __init__(self, **kwargs): Allocate vlan IDs to individual users.
self.start = FLAGS.vlan_start """
self.end = FLAGS.vlan_end vlan = datastore.Redis.instance().hget(VLANS_KEY, project_id)
self.vlans = kwargs.get('vlans', {}) if vlan:
self.vlanpool = {} return vlan
self.manager = users.UserManager.instance() assigned_vlans = get_assigned_vlans()
for project_id, vlan in self.vlans.iteritems(): # TODO(joshua) I can do this in one loop, I think
self.vlanpool[vlan] = project_id for old_project_id, vlan in assigned_vlans.iteritems():
if not users.UserManager.instance().get_project(old_project_id):
def to_dict(self): _rem_vlan(old_project_id)
return {'vlans': self.vlans} _add_vlan(project_id, vlan)
return vlan
def __str__(self): for vlan in range(FLAGS.vlan_start, FLAGS.vlan_end):
return json.dumps(self.to_dict()) if not str(vlan) in assigned_vlans.values():
_add_vlan(project_id, vlan)
def __unicode__(self): return vlan
return json.dumps(self.to_dict())
@classmethod
def from_dict(cls, args):
for arg in args.keys():
value = args[arg]
del args[arg]
args[str(arg)] = value
self = cls(**args)
return self
@classmethod
def from_json(cls, json_string):
parsed = json.loads(json_string)
return cls.from_dict(parsed)
def assign_vlan(self, project_id, vlan):
logging.debug("Assigning vlan %s to project %s" % (vlan, project_id))
self.vlans[project_id] = vlan
self.vlanpool[vlan] = project_id
return self.vlans[project_id]
def next(self, project_id):
for old_project_id, vlan in self.vlans.iteritems():
if not self.manager.get_project(old_project_id):
_get_keeper()["%s-default" % old_project_id] = {}
del _get_keeper()["%s-default" % old_project_id]
del self.vlans[old_project_id]
return self.assign_vlan(project_id, vlan)
vlans = self.vlanpool.keys()
vlans.append(self.start)
nextvlan = max(vlans) + 1
if nextvlan == self.end:
raise exception.AddressNotAllocated("Out of VLANs") raise exception.AddressNotAllocated("Out of VLANs")
return self.assign_vlan(project_id, nextvlan)
class NetworkController(object): def get_network_by_address(address):
""" The network controller is in charge of network connections """ for project in users.UserManager.instance().get_projects():
net = get_project_network(project.id)
def __init__(self, **kwargs): if address in net.assigned:
logging.debug("Starting up the network controller.") return net
self.manager = users.UserManager.instance()
self._pubnet = None
if not _get_keeper()['vlans']:
_get_keeper()['vlans'] = {}
if not _get_keeper()['public']:
_get_keeper()['public'] = {'vlan': FLAGS.public_vlan, 'network' : FLAGS.public_range}
self.express()
def reset(self):
_get_keeper()['public'] = {'vlan': FLAGS.public_vlan, 'network': FLAGS.public_range }
_get_keeper()['vlans'] = {}
# TODO : Get rid of old interfaces, bridges, and IPTables rules.
@property
def public_net(self):
if not self._pubnet:
self._pubnet = PublicNetwork.from_dict(_get_keeper()['public'])
self._pubnet.load(**_get_keeper()['public'])
return self._pubnet
@property
def vlan_pool(self):
return VlanPool.from_dict(_get_keeper()['vlans'])
def get_network_from_name(self, network_name):
net_dict = _get_keeper()[network_name]
if net_dict:
return PrivateNetwork.from_dict(net_dict)
return None
def get_public_ip_for_instance(self, instance_id):
# FIXME: this should be a lookup - iteration won't scale
for address_record in self.describe_addresses(type=PublicNetwork):
if address_record.get(u'instance_id', 'free') == instance_id:
return address_record[u'address']
def get_project_network(self, project_id):
""" get a project's private network, allocating one if needed """
project = self.manager.get_project(project_id)
if not project:
raise Exception("Project %s doesn't exist, uhoh." % project_id)
project_net = self.get_network_from_name("%s-default" % project_id)
if not project_net:
pool = self.vlan_pool
vlan = pool.next(project_id)
private_pool = NetworkPool()
network_str = private_pool.get_from_vlan(vlan)
logging.debug("Constructing network %s and %s for %s" % (network_str, vlan, project_id))
project_net = PrivateNetwork(
network=network_str,
vlan=vlan)
_get_keeper()["%s-default" % project_id] = project_net.to_dict()
_get_keeper()['vlans'] = pool.to_dict()
return project_net
def allocate_address(self, user_id, project_id, mac=None, type=PrivateNetwork):
ip = None
net_name = None
if type == PrivateNetwork:
net = self.get_project_network(project_id)
ip = net.allocate_ip(user_id, project_id, mac)
net_name = net.name
_get_keeper()["%s-default" % project_id] = net.to_dict()
else:
net = self.public_net
ip = net.allocate_ip(user_id, project_id, mac)
net_name = net.name
_get_keeper()['public'] = net.to_dict()
return (ip, net_name)
def deallocate_address(self, address):
if address in self.public_net.network:
net = self.public_net
rv = net.deallocate_ip(str(address))
_get_keeper()['public'] = net.to_dict()
return rv
for project in self.manager.get_projects():
if address in self.get_project_network(project.id).network:
net = self.get_project_network(project.id)
rv = net.deallocate_ip(str(address))
_get_keeper()["%s-default" % project.id] = net.to_dict()
return rv
raise exception.AddressNotAllocated() raise exception.AddressNotAllocated()
def describe_addresses(self, type=PrivateNetwork): def allocate_ip(user_id, project_id, mac):
if type == PrivateNetwork: return get_project_network(project_id).allocate_ip(user_id, project_id, mac)
addresses = []
for project in self.manager.get_projects():
addresses.extend(self.get_project_network(project.id).list_addresses())
return addresses
return self.public_net.list_addresses()
def associate_address(self, address, private_ip, instance_id): def deallocate_ip(address):
net = self.public_net return get_network_by_address(address).deallocate_ip(address)
rv = net.associate_address(address, private_ip, instance_id)
_get_keeper()['public'] = net.to_dict()
return rv
def disassociate_address(self, address): def get_project_network(project_id, security_group='default'):
net = self.public_net """ get a project's private network, allocating one if needed """
rv = net.disassociate_address(address) project = users.UserManager.instance().get_project(project_id)
_get_keeper()['public'] = net.to_dict() if not project:
return rv raise nova.exception.Error("Project %s doesn't exist, uhoh." % project_id)
return DHCPNetwork.get_network_for_project(project.project_manager_id, project.id, security_group)
def express(self,address=None): def get_subnet_from_vlan(vlan):
for project in self.manager.get_projects(): """Assign one subnet to each VLAN, for now."""
self.get_project_network(project.id).express() vlan = int(vlan)
network = IPy.IP(FLAGS.private_range)
def report_state(self): start = (vlan-FLAGS.vlan_start) * FLAGS.network_size
pass return "%s-%s" % (network[start], network[start + FLAGS.network_size - 1])
def restart_nets():
""" Ensure the network for each user is enabled"""
for project in users.UserManager.instance().get_projects():
get_project_network(project.id).express()

View File

@@ -137,11 +137,15 @@ class Node(object, service.Service):
logging.debug("Reporting State") logging.debug("Reporting State")
return return
@exception.wrap_exception # @exception.wrap_exception
def run_instance(self, instance_id, **_kwargs): def run_instance(self, instance_id, **_kwargs):
""" launch a new instance with specified options """ """ launch a new instance with specified options """
logging.debug("Starting instance %s..." % (instance_id)) logging.debug("Starting instance %s..." % (instance_id))
inst = self.instdir.get(instance_id) inst = self.instdir.get(instance_id)
# TODO: Get the real security group of launch in here
security_group = "default"
net = network.BridgedNetwork.get_network_for_project(inst['user_id'], inst['project_id'],
security_group).express()
inst['node_name'] = FLAGS.node_name inst['node_name'] = FLAGS.node_name
inst.save() inst.save()
# TODO(vish) check to make sure the availability zone matches # TODO(vish) check to make sure the availability zone matches
@@ -337,8 +341,6 @@ class Instance(object):
'basepath', os.path.abspath( 'basepath', os.path.abspath(
os.path.join(FLAGS.instances_path, self.name))) os.path.join(FLAGS.instances_path, self.name)))
self._s['memory_kb'] = int(self._s['memory_mb']) * 1024 self._s['memory_kb'] = int(self._s['memory_mb']) * 1024
# TODO(joshua) - Get this from network directory controller later
self._s['bridge_name'] = data.get('bridge_name', 'br0')
self._s['image_id'] = data.get('image_id', FLAGS.default_image) self._s['image_id'] = data.get('image_id', FLAGS.default_image)
self._s['kernel_id'] = data.get('kernel_id', FLAGS.default_kernel) self._s['kernel_id'] = data.get('kernel_id', FLAGS.default_kernel)
self._s['ramdisk_id'] = data.get('ramdisk_id', FLAGS.default_ramdisk) self._s['ramdisk_id'] = data.get('ramdisk_id', FLAGS.default_ramdisk)
@@ -360,6 +362,7 @@ class Instance(object):
self._s['addressing_type'] = data.get('addressing_type', None) self._s['addressing_type'] = data.get('addressing_type', None)
self._s['availability_zone'] = data.get('availability_zone', 'fixme') self._s['availability_zone'] = data.get('availability_zone', 'fixme')
self._s['bridge_name'] = data.get('bridge_name', None)
#TODO: put real dns items here #TODO: put real dns items here
self._s['private_dns_name'] = data.get('private_dns_name', 'fixme') self._s['private_dns_name'] = data.get('private_dns_name', 'fixme')
self._s['dns_name'] = data.get('dns_name', self._s['dns_name'] = data.get('dns_name',
@@ -476,7 +479,7 @@ class Instance(object):
logging.debug('rebooted instance %s' % self.name) logging.debug('rebooted instance %s' % self.name)
defer.returnValue(None) defer.returnValue(None)
@exception.wrap_exception # @exception.wrap_exception
def spawn(self): def spawn(self):
self.datamodel['state'] = "spawning" self.datamodel['state'] = "spawning"
self.datamodel.save() self.datamodel.save()
@@ -516,30 +519,3 @@ class Instance(object):
else: else:
console = 'FAKE CONSOLE OUTPUT' console = 'FAKE CONSOLE OUTPUT'
return defer.succeed(console) return defer.succeed(console)
def generate_mac(self):
mac = [0x00, 0x16, 0x3e, random.randint(0x00, 0x7f),
random.randint(0x00, 0xff), random.randint(0x00, 0xff)
]
return ':'.join(map(lambda x: "%02x" % x, mac))
class NetworkNode(Node):
def __init__(self, **kwargs):
super(NetworkNode, self).__init__(**kwargs)
self.virtNets = {}
def add_network(self, net_dict):
net = network.VirtNetwork(**net_dict)
self.virtNets[net.name] = net
self.virtNets[net.name].express()
return defer.succeed({'retval': 'network added'})
@exception.wrap_exception
def run_instance(self, instance_id, **kwargs):
inst = self.instdir.get(instance_id)
net_dict = json.loads(inst.get('network_str', "{}"))
self.add_network(net_dict)
return super(NetworkNode, self).run_instance(instance_id, **kwargs)

View File

@@ -28,6 +28,7 @@ import json
import logging import logging
import os import os
import sqlite3 import sqlite3
import time
from nova import vendor from nova import vendor
import redis import redis
@@ -77,10 +78,11 @@ class RedisModel(object):
def set_default_state(self): def set_default_state(self):
self.state = {'state' : 'pending'} self.state = {'state' : 'pending'}
self.state[self.object_type+"_id"] = self.object_id self.state[self.object_type+"_id"] = self.object_id
self.state["create_time"] = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
@property @property
def __redis_key(self): def __redis_key(self):
""" Magic string for instance keys """ """ Magic string for keys """
return '%s:%s' % (self.object_type, self.object_id) return '%s:%s' % (self.object_type, self.object_id)
def __repr__(self): def __repr__(self):

View File

@@ -60,7 +60,7 @@ class CloudController(object):
""" """
def __init__(self): def __init__(self):
self.instdir = model.InstanceDirectory() self.instdir = model.InstanceDirectory()
self.network = network.NetworkController() self.network = network.PublicNetworkController()
self.setup() self.setup()
@property @property
@@ -254,21 +254,10 @@ class CloudController(object):
res.addCallback(_format_result) res.addCallback(_format_result)
return res return res
def _convert_address(self, network_address):
# FIXME(vish): this should go away when network.py stores info properly
address = {}
address['public_ip'] == network_address[u'address']
address['user_id'] == network_address[u'user_id']
address['project_id'] == network_address.get(u'project_id', address['user_id'])
address['instance_id'] == network_address.get(u'instance_id', None)
return address
def _get_address(self, context, public_ip): def _get_address(self, context, public_ip):
# right now all addresses are allocated locally
# FIXME(vish) this should move into network.py # FIXME(vish) this should move into network.py
for network_address in self.network.describe_addresses(): for address in self.network.hosts:
if network_address[u'address'] == public_ip: if address['address'] == public_ip:
address = self._convert_address(network_address)
if context.user.is_admin() or address['project_id'] == context.project.id: if context.user.is_admin() or address['project_id'] == context.project.id:
return address return address
raise exception.NotFound("Address at ip %s not found" % public_ip) raise exception.NotFound("Address at ip %s not found" % public_ip)
@@ -398,14 +387,12 @@ class CloudController(object):
def format_addresses(self, context): def format_addresses(self, context):
addresses = [] addresses = []
# TODO(vish): move authorization checking into network.py # TODO(vish): move authorization checking into network.py
for network_address in self.network.describe_addresses(type=network.PublicNetwork): for address in self.network.hosts:
#logging.debug(address_record) #logging.debug(address_record)
address = self._convert_address(network_address)
address_rv = { address_rv = {
'public_ip': address['public_ip'], 'public_ip': address['address'],
'instance_id' : address.get('instance_id', 'free') 'instance_id' : address.get('instance_id', 'free')
} }
# FIXME: add another field for user id
if context.user.is_admin(): if context.user.is_admin():
address_rv['instance_id'] = "%s (%s, %s)" % ( address_rv['instance_id'] = "%s (%s, %s)" % (
address['instance_id'], address['instance_id'],
@@ -417,17 +404,17 @@ class CloudController(object):
return {'addressesSet': addresses} return {'addressesSet': addresses}
def allocate_address(self, context, **kwargs): def allocate_address(self, context, **kwargs):
(address,network_name) = self.network.allocate_address( address = self.network.allocate_ip(
context.user.id, context.project_id, type=network.PublicNetwork) context.user.id, context.project.id, 'public')
return defer.succeed({'addressSet': [{'publicIp' : address}]}) return defer.succeed({'addressSet': [{'publicIp' : address}]})
def release_address(self, context, public_ip, **kwargs): def release_address(self, context, public_ip, **kwargs):
address = self._get_address(public_ip) self.network.deallocate_ip(public_ip)
return defer.succeed({'releaseResponse': ["Address released."]}) return defer.succeed({'releaseResponse': ["Address released."]})
def associate_address(self, context, instance_id, **kwargs): def associate_address(self, context, instance_id, **kwargs):
instance = self._get_instance(context, instance_id) instance = self._get_instance(context, instance_id)
rv = self.network.associate_address( self.network.associate_address(
kwargs['public_ip'], kwargs['public_ip'],
instance['private_dns_name'], instance['private_dns_name'],
instance_id) instance_id)
@@ -435,7 +422,7 @@ class CloudController(object):
def disassociate_address(self, context, public_ip, **kwargs): def disassociate_address(self, context, public_ip, **kwargs):
address = self._get_address(public_ip) address = self._get_address(public_ip)
rv = self.network.disassociate_address(public_ip) self.network.disassociate_address(public_ip)
# TODO - Strip the IP from the instance # TODO - Strip the IP from the instance
return defer.succeed({'disassociateResponse': ["Address disassociated."]}) return defer.succeed({'disassociateResponse': ["Address disassociated."]})
@@ -466,14 +453,10 @@ class CloudController(object):
inst['project_id'] = context.project.id inst['project_id'] = context.project.id
inst['mac_address'] = utils.generate_mac() inst['mac_address'] = utils.generate_mac()
inst['ami_launch_index'] = num inst['ami_launch_index'] = num
address, _netname = self.network.allocate_address( address = network.allocate_ip(
user_id=inst['user_id'], inst['user_id'], inst['project_id'], mac=inst['mac_address'])
project_id=inst['project_id'],
mac=inst['mac_address'])
network = self.network.get_users_network(str(context.user.id))
inst['network_str'] = json.dumps(network.to_dict())
inst['bridge_name'] = network.bridge_name
inst['private_dns_name'] = str(address) inst['private_dns_name'] = str(address)
inst['bridge_name'] = network.BridgedNetwork.get_network_for_project(inst['user_id'], inst['project_id'])['bridge_name']
# TODO: allocate expresses on the router node # TODO: allocate expresses on the router node
inst.save() inst.save()
rpc.cast(FLAGS.compute_topic, rpc.cast(FLAGS.compute_topic,
@@ -502,7 +485,7 @@ class CloudController(object):
if instance.get('private_dns_name', None): if instance.get('private_dns_name', None):
logging.debug("Deallocating address %s" % instance.get('private_dns_name', None)) logging.debug("Deallocating address %s" % instance.get('private_dns_name', None))
try: try:
self.network.deallocate_address(instance.get('private_dns_name', None)) self.network.deallocate_ip(instance.get('private_dns_name', None))
except Exception, _err: except Exception, _err:
pass pass
if instance.get('node_name', 'unassigned') != 'unassigned': #It's also internal default if instance.get('node_name', 'unassigned') != 'unassigned': #It's also internal default

View File

@@ -23,11 +23,17 @@ from nova import flags
from nova import test from nova import test
from nova.compute import network from nova.compute import network
from nova.auth import users from nova.auth import users
from nova import utils
class NetworkTestCase(test.TrialTestCase): class NetworkTestCase(test.TrialTestCase):
def setUp(self): def setUp(self):
super(NetworkTestCase, self).setUp() super(NetworkTestCase, self).setUp()
self.flags(fake_libvirt=True,
fake_storage=True,
fake_network=True,
network_size=32,
redis_db=8)
logging.getLogger().setLevel(logging.DEBUG) logging.getLogger().setLevel(logging.DEBUG)
self.manager = users.UserManager.instance() self.manager = users.UserManager.instance()
try: try:
@@ -37,7 +43,7 @@ class NetworkTestCase(test.TrialTestCase):
name = 'project%s' % i name = 'project%s' % i
if not self.manager.get_project(name): if not self.manager.get_project(name):
self.manager.create_project(name, 'netuser', name) self.manager.create_project(name, 'netuser', name)
self.network = network.NetworkController(netsize=16) self.network = network.PublicNetworkController()
def tearDown(self): def tearDown(self):
super(NetworkTestCase, self).tearDown() super(NetworkTestCase, self).tearDown()
@@ -46,70 +52,69 @@ class NetworkTestCase(test.TrialTestCase):
self.manager.delete_project(name) self.manager.delete_project(name)
self.manager.delete_user('netuser') self.manager.delete_user('netuser')
def test_network_serialization(self): def test_public_network_allocation(self):
net1 = network.Network(vlan=100, network="192.168.100.0/24", conn=None) pubnet = IPy.IP(flags.FLAGS.public_range)
address = net1.allocate_ip("netuser", "project0", "01:24:55:36:f2:a0") address = self.network.allocate_ip("netuser", "project0", "public")
net_json = str(net1) self.assertTrue(IPy.IP(address) in pubnet)
net2 = network.Network.from_json(net_json) self.assertTrue(IPy.IP(address) in self.network.network)
self.assertEqual(net_json, str(net2))
self.assertTrue(IPy.IP(address) in net2.network)
def test_allocate_deallocate_address(self): def test_allocate_deallocate_ip(self):
(address, net_name) = self.network.allocate_address("netuser", address = network.allocate_ip(
"project0", "01:24:55:36:f2:a0") "netuser", "project0", utils.generate_mac())
logging.debug("Was allocated %s" % (address)) logging.debug("Was allocated %s" % (address))
self.assertEqual(True, address in self._get_project_addresses("project0")) self.assertEqual(True, address in self._get_project_addresses("project0"))
rv = self.network.deallocate_address(address) rv = network.deallocate_ip(address)
self.assertEqual(False, address in self._get_project_addresses("project0")) self.assertEqual(False, address in self._get_project_addresses("project0"))
def test_range_allocation(self): def test_range_allocation(self):
(address, net_name) = self.network.allocate_address("netuser", address = network.allocate_ip(
"project0", "01:24:55:36:f2:a0") "netuser", "project0", utils.generate_mac())
(secondaddress, net_name) = self.network.allocate_address("netuser", secondaddress = network.allocate_ip(
"project1", "01:24:55:36:f2:a0") "netuser", "project1", utils.generate_mac())
self.assertEqual(True, address in self._get_project_addresses("project0")) self.assertEqual(True,
address in self._get_project_addresses("project0"))
self.assertEqual(True, self.assertEqual(True,
secondaddress in self._get_project_addresses("project1")) secondaddress in self._get_project_addresses("project1"))
self.assertEqual(False, address in self._get_project_addresses("project1")) self.assertEqual(False, address in self._get_project_addresses("project1"))
rv = self.network.deallocate_address(address) rv = network.deallocate_ip(address)
self.assertEqual(False, address in self._get_project_addresses("project0")) self.assertEqual(False, address in self._get_project_addresses("project0"))
rv = self.network.deallocate_address(secondaddress) rv = network.deallocate_ip(secondaddress)
self.assertEqual(False, self.assertEqual(False,
secondaddress in self._get_project_addresses("project1")) secondaddress in self._get_project_addresses("project1"))
def test_subnet_edge(self): def test_subnet_edge(self):
(secondaddress, net_name) = self.network.allocate_address("netuser", "project0") secondaddress = network.allocate_ip("netuser", "project0",
utils.generate_mac())
for project in range(1,5): for project in range(1,5):
project_id = "project%s" % (project) project_id = "project%s" % (project)
(address, net_name) = self.network.allocate_address("netuser", address = network.allocate_ip(
project_id, "01:24:55:36:f2:a0") "netuser", project_id, utils.generate_mac())
(address2, net_name) = self.network.allocate_address("netuser", address2 = network.allocate_ip(
project_id, "01:24:55:36:f2:a0") "netuser", project_id, utils.generate_mac())
(address3, net_name) = self.network.allocate_address("netuser", address3 = network.allocate_ip(
project_id, "01:24:55:36:f2:a0") "netuser", project_id, utils.generate_mac())
self.assertEqual(False, self.assertEqual(False,
address in self._get_project_addresses("project0")) address in self._get_project_addresses("project0"))
self.assertEqual(False, self.assertEqual(False,
address2 in self._get_project_addresses("project0")) address2 in self._get_project_addresses("project0"))
self.assertEqual(False, self.assertEqual(False,
address3 in self._get_project_addresses("project0")) address3 in self._get_project_addresses("project0"))
rv = self.network.deallocate_address(address) rv = network.deallocate_ip(address)
rv = self.network.deallocate_address(address2) rv = network.deallocate_ip(address2)
rv = self.network.deallocate_address(address3) rv = network.deallocate_ip(address3)
rv = self.network.deallocate_address(secondaddress) rv = network.deallocate_ip(secondaddress)
def test_too_many_projects(self): def test_too_many_projects(self):
for i in range(0, 30): for i in range(0, 30):
name = 'toomany-project%s' % i name = 'toomany-project%s' % i
self.manager.create_project(name, 'netuser', name) self.manager.create_project(name, 'netuser', name)
(address, net_name) = self.network.allocate_address("netuser", address = network.allocate_ip(
name, "01:24:55:36:f2:a0") "netuser", name, utils.generate_mac())
rv = network.deallocate_ip(address)
self.manager.delete_project(name) self.manager.delete_project(name)
def _get_project_addresses(self, project_id): def _get_project_addresses(self, project_id):
rv = self.network.describe_addresses()
project_addresses = [] project_addresses = []
for item in rv: for addr in network.get_project_network(project_id).list_addresses():
if item['project_id'] == project_id: project_addresses.append(addr)
project_addresses.append(item['address'])
return project_addresses return project_addresses