diff options
-rwxr-xr-x | bin/nova-manage | 33 | ||||
-rw-r--r-- | nova/auth/novarc.template | 8 | ||||
-rw-r--r-- | nova/auth/users.py | 16 | ||||
-rw-r--r-- | nova/compute/linux_net.py | 69 | ||||
-rw-r--r-- | nova/compute/network.py | 644 | ||||
-rw-r--r-- | nova/compute/node.py | 38 | ||||
-rw-r--r-- | nova/datastore.py | 4 | ||||
-rw-r--r-- | nova/endpoint/cloud.py | 45 | ||||
-rw-r--r-- | nova/tests/network_unittest.py | 77 |
9 files changed, 405 insertions, 529 deletions
diff --git a/bin/nova-manage b/bin/nova-manage index d2108626b..765eb1f53 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -71,15 +71,35 @@ class UserCommands(object): for user in self.manager.get_users(): print user.name - def zip(self, name, filename='nova.zip'): +class ProjectCommands(object): + def __init__(self): + self.manager = users.UserManager.instance() + + def create(self, name, project_manager, description=None): + """creates a new project + arguments: name project_manager [description]""" + user = self.manager.create_project(name, project_manager, description) + + def delete(self, name): + """deletes an existing project + arguments: name""" + self.manager.delete_project(name) + + def list(self): + """lists all projects + arguments: <none>""" + for project in self.manager.get_projects(): + print project.name + + def zip(self, project_id, user_id, filename='nova.zip'): """exports credentials for user to a zip file - arguments: name [filename='nova.zip]""" - user = self.manager.get_user(name) - if user: + arguments: project_id user_id [filename='nova.zip]""" + project = self.manager.get_project(project_id) + if project: with open(filename, 'w') as f: - f.write(user.get_credentials()) + f.write(project.get_credentials(user_id)) else: - print "User %s doesn't exist" % name + print "Project %s doesn't exist" % project def usage(script_name): @@ -88,6 +108,7 @@ def usage(script_name): categories = [ ('user', UserCommands), + ('project', ProjectCommands), ] diff --git a/nova/auth/novarc.template b/nova/auth/novarc.template index a993d1882..ee1bc75f2 100644 --- a/nova/auth/novarc.template +++ b/nova/auth/novarc.template @@ -1,11 +1,11 @@ # 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. @@ -13,7 +13,7 @@ # limitations under the License. NOVA_KEY_DIR=$(pushd $(dirname $BASH_SOURCE)>/dev/null; pwd; popd>/dev/null) -export EC2_ACCESS_KEY="%(access)s" +export EC2_ACCESS_KEY="%(access)s:%(project)s" export EC2_SECRET_KEY="%(secret)s" export EC2_URL="%(ec2)s" export S3_URL="%(s3)s" diff --git a/nova/auth/users.py b/nova/auth/users.py index b09bcfcf2..c60922feb 100644 --- a/nova/auth/users.py +++ b/nova/auth/users.py @@ -93,9 +93,12 @@ class User(AuthBase): def is_project_manager(self, project): return UserManager.instance().is_project_manager(self, project) - def generate_rc(self): + def generate_rc(self, project=None): + if project is None: + project = self.id rc = open(FLAGS.credentials_template).read() rc = rc % { 'access': self.access, + 'project': project, 'secret': self.secret, 'ec2': FLAGS.ec2_url, 's3': 'http://%s:%s' % (FLAGS.s3_host, FLAGS.s3_port), @@ -168,7 +171,9 @@ class Project(Group): return User.safe_id(user) == self.project_manager_id def get_credentials(self, user): - rc = user.generate_rc() + if not isinstance(user, User): + user = UserManager.instance().get_user(user) + rc = user.generate_rc(self.id) private_key, signed_cert = self.generate_x509_cert(user) tmpdir = tempfile.mkdtemp() @@ -238,7 +243,7 @@ class UserManager(object): raise exception.NotAuthorized('Signature does not match') return (user, project) - def create_project(self, name, manager_user, description, member_users=None): + def create_project(self, name, manager_user, description=None, member_users=None): if member_users: member_users = [User.safe_id(u) for u in member_users] with LDAPWrapper() as conn: @@ -462,12 +467,15 @@ class LDAPWrapper(object): self.conn.add_s(self.__uid_to_dn(name), attr) return self.__to_user(dict(attr)) - def create_project(self, name, manager_uid, description, member_uids = None): + def create_project(self, name, manager_uid, description=None, member_uids=None): if self.project_exists(name): raise exception.Duplicate("Project can't be created because project %s already exists" % name) if not self.user_exists(manager_uid): raise exception.NotFound("Project can't be created because manager %s doesn't exist" % manager_uid) manager_dn = self.__uid_to_dn(manager_uid) + # description is a required attribute + if description is None: + description = name members = [] if member_uids != None: for member_uid in member_uids: diff --git a/nova/compute/linux_net.py b/nova/compute/linux_net.py index 319ccbdd6..b9f775aa3 100644 --- a/nova/compute/linux_net.py +++ b/nova/compute/linux_net.py @@ -1,25 +1,11 @@ # 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 os import signal +import os +import nova.utils import subprocess -from nova import utils - # todo(ja): does the definition of network_path belong here? from nova import flags @@ -30,13 +16,13 @@ def execute(cmd): logging.debug("FAKE NET: %s" % cmd) return "fake", 0 else: - utils.execute(cmd) + return nova.utils.execute(cmd) def runthis(desc, cmd): if FLAGS.fake_network: - execute(cmd) + return execute(cmd) else: - utils.runthis(desc,cmd) + return nova.utils.runthis(desc,cmd) def Popen(cmd): if FLAGS.fake_network: @@ -61,44 +47,46 @@ def bind_public_ip(ip, interface): def vlan_create(net): """ 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 add %s %s" % (net.bridge_dev, net.vlan)) - execute("sudo ifconfig vlan%s up" % (net.vlan)) + execute("sudo vconfig add %s %s" % (FLAGS.bridge_dev, net['vlan'])) + execute("sudo ifconfig vlan%s up" % (net['vlan'])) def bridge_create(net): """ create a bridge on a vlan unless it already exists """ - if not device_exists(net.bridge_name): - execute("sudo brctl addbr %s" % (net.bridge_name)) + if not device_exists(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 setageing %s 10" % (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 stp %s off" % (net['bridge_name'])) + execute("sudo brctl addif %s vlan%s" % (net['bridge_name'], net['vlan'])) if net.bridge_gets_ip: execute("sudo ifconfig %s %s broadcast %s netmask %s up" % \ - (net.bridge_name, net.gateway, net.broadcast, net.netmask)) - confirm_rule("FORWARD --in-interface %s -j ACCEPT" % (net.bridge_name)) + (net['bridge_name'], net.gateway, net.broadcast, net.netmask)) + confirm_rule("FORWARD --in-interface %s -j ACCEPT" % (net['bridge_name'])) else: - execute("sudo ifconfig %s up" % net.bridge_name) + execute("sudo ifconfig %s up" % net['bridge_name']) def dnsmasq_cmd(net): cmd = ['sudo dnsmasq', ' --strict-order', ' --bind-interfaces', ' --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, ' --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-hostsfile=%s' % dhcp_file(net.vlan, 'conf'), - ' --dhcp-leasefile=%s' % dhcp_file(net.vlan, 'leases')] + ' --dhcp-hostsfile=%s' % dhcp_file(net['vlan'], 'conf'), + ' --dhcp-leasefile=%s' % dhcp_file(net['vlan'], 'leases')] return ''.join(cmd) -def hostDHCP(network, host): - idx = host['address'].split(".")[-1] # Logically, the idx of instances they've launched in this net +def hostDHCP(network, host, mac): + idx = host.split(".")[-1] # Logically, the idx of instances they've launched in this net 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 # 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 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: - 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) @@ -123,12 +111,11 @@ def start_dnsmasq(network): # correct dnsmasq process try: os.kill(pid, signal.SIGHUP) - return except Exception, e: logging.debug("Hupping dnsmasq threw %s", e) # 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): os.unlink(lease_file) @@ -156,7 +143,7 @@ def dnsmasq_pid_for(network): 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): with open(pid_file, 'r') as f: diff --git a/nova/compute/network.py b/nova/compute/network.py index 9a9bb9527..4d5f4bf5f 100644 --- a/nova/compute/network.py +++ b/nova/compute/network.py @@ -17,15 +17,16 @@ Classes for network control, including VLANs, DHCP, and IP allocation. """ -import json import logging import os +import time # TODO(termie): clean up these imports from nova import vendor import IPy from nova import datastore +import nova.exception from nova.compute import exception from nova import flags from nova import utils @@ -34,145 +35,129 @@ from nova.auth import users import linux_net 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'), 'Location to keep network config files') 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', 'network device for bridges') 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('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('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) -# 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): - def __init__(self, *args, **kwargs): - self.bridge_gets_ip = False - 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()) +class BaseNetwork(datastore.RedisModel): + bridge_gets_ip = False + object_type = 'network' @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 + def get_all_hosts(cls): + for vlan in get_assigned_vlans().values(): + network_str = get_subnet_from_vlan(vlan) + for addr in datastore.Redis.instance().hgetall( + "network:%s:hosts" % (network_str)): + yield addr @classmethod - def from_json(cls, json_string): - parsed = json.loads(json_string) - return cls.from_dict(parsed) + def create(cls, user_id, project_id, security_group, vlan, network_str): + network_id = "%s:%s" % (project_id, security_group) + 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 __init__(self, network_id, network_str=None): + super(BaseNetwork, self).__init__(object_id=network_id) + self['network_id'] = network_id + self['network_str'] = network_str + self.save() - def range(self): - for idx in range(3, len(self.network)-2): - yield self.network[idx] + @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"]) - def allocate_ip(self, user_id, project_id, mac): - for ip in self.range(): - address = str(ip) - if not address in self.hosts.keys(): - logging.debug("Allocating IP %s to %s" % (address, project_id)) - self.hosts[address] = { - "address" : address, "user_id": user_id, "project_id" : project_id, 'mac' : mac - } - self.express(address=address) - return address - raise exception.NoMoreAddresses() + @property + def user(self): + return users.UserManager.instance().get_user(self['user_id']) - def deallocate_ip(self, ip_str): - if not ip_str in self.hosts.keys(): - raise exception.AddressNotAllocated() - del self.hosts[ip_str] - # TODO(joshua) SCRUB from the leases file somehow - self.deexpress(address=ip_str) + @property + def project(self): + return users.UserManager.instance().get_project(self['project_id']) - def list_addresses(self): - for address in self.hosts.values(): - yield address + @property + def _hosts_key(self): + return "network:%s:hosts" % (self['network_str']) - def express(self, address=None): - pass + @property + def hosts(self): + return datastore.Redis.instance().hgetall(self._hosts_key) - def deexpress(self, address=None): - pass + 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) -class Vlan(Network): - """ - VLAN configuration, that when expressed creates the vlan + @property + def assigned(self): + return datastore.Redis.instance().hkeys(self._hosts_key) - properties: + @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) - vlan - integer (example: 42) - bridge_dev - string (example: eth0) - """ + def allocate_ip(self, user_id, project_id, mac): + for address in self.available: + logging.debug("Allocating IP %s to %s" % (address, project_id)) + self._add_host(user_id, project_id, address, mac) + self.express(address=address) + return address + raise exception.NoMoreAddresses() - def __init__(self, *args, **kwargs): - super(Vlan, self).__init__(*args, **kwargs) - self.bridge_dev = FLAGS.bridge_dev + def deallocate_ip(self, ip_str): + if not ip_str in self.assigned: + raise exception.AddressNotAllocated() + self._rem_host(ip_str) + self.deexpress(address=ip_str) - 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 + def list_addresses(self): + for address in self.hosts: + yield address + def express(self, address=None): pass + def deexpress(self, address=None): pass -class VirtNetwork(Vlan): + +class BridgedNetwork(BaseNetwork): """ - Virtual Network that can export libvirt configuration or express itself to - create a bridge (with or without an IP address/netmask/gateway) + Virtual Network that can express itself to create a vlan and + a bridge (with or without an IP address/netmask/gateway) properties: bridge_name - string (example value: br42) vlan - integer (example value: 42) + bridge_dev - string (example: eth0) bridge_gets_ip - boolean used during bridge creation if bridge_gets_ip then network address for bridge uses the properties: @@ -181,333 +166,242 @@ class VirtNetwork(Vlan): netmask """ - def __init__(self, *args, **kwargs): - super(VirtNetwork, self).__init__(*args, **kwargs) - - def virtXML(self): - """ generate XML for libvirt network """ + @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) - 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 __init__(self, *args, **kwargs): + super(BridgedNetwork, self).__init__(*args, **kwargs) + self['bridge_dev'] = FLAGS.bridge_dev + self.save() def express(self, address=None): - """ creates a bridge device on top of the Vlan """ - super(VirtNetwork, self).express(address=address) - try: - logging.debug("Starting Bridge inteface for %s network" % (self.vlan)) - linux_net.bridge_create(self) - except: - pass + super(BridgedNetwork, self).express(address=address) + linux_net.vlan_create(self) + linux_net.bridge_create(self) -class DHCPNetwork(VirtNetwork): +class DHCPNetwork(BridgedNetwork): """ properties: dhcp_listen_address: the ip of the gateway / dhcp host dhcp_range_start: the first ip to give out dhcp_range_end: the last ip to give out """ + bridge_gets_ip = True + def __init__(self, *args, **kwargs): super(DHCPNetwork, self).__init__(*args, **kwargs) logging.debug("Initing DHCPNetwork object...") - self.bridge_gets_ip = True self.dhcp_listen_address = self.network[1] self.dhcp_range_start = self.network[3] - self.dhcp_range_end = self.network[-2] + self.dhcp_range_end = self.network[-1] + try: + os.makedirs(FLAGS.networks_path) + except Exception, err: + pass def express(self, address=None): super(DHCPNetwork, self).express(address=address) - if len(self.hosts.values()) > 0: - logging.debug("Starting dnsmasq server for network with vlan %s" % self.vlan) + if len(self.assigned) > 0: + logging.debug("Starting dnsmasq server for network with vlan %s", + self['vlan']) linux_net.start_dnsmasq(self) 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): # if this is the last address, stop dns super(DHCPNetwork, self).deexpress(address=address) - if len(self.hosts.values()) == 0: + if len(self.assigned) == 0: linux_net.stop_dnsmasq(self) else: linux_net.start_dnsmasq(self) -class PrivateNetwork(DHCPNetwork): - def __init__(self, **kwargs): - super(PrivateNetwork, self).__init__(**kwargs) - # self.express() +class PublicAddress(datastore.RedisModel): + object_type="address" - def to_dict(self): - return {'vlan': self.vlan, - 'network': self.network_str, - 'hosts': self.hosts} + def __init__(self, address): + super(PublicAddress, self).__init__(address) -class PublicNetwork(Network): - def __init__(self, network="192.168.216.0/24", **kwargs): - super(PublicNetwork, self).__init__(network=network, **kwargs) + @classmethod + def create(cls, user_id, project_id, address): + 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() - def allocate_ip(self, user_id, project_id, mac): - for ip in self.range(): - address = str(ip) + @property + def available(self): + for idx in range(2, len(self.network)-1): + address = str(self.network[idx]) if not address in self.hosts.keys(): - logging.debug("Allocating IP %s to %s" % (address, project_id)) - self.hosts[address] = { - "address" : address, "user_id": user_id, "project_id" : project_id, 'mac' : mac - } - self.express(address=address) - return address - raise exception.NoMoreAddresses() + yield address - def deallocate_ip(self, ip_str): - if not ip_str in self.hosts: - raise exception.AddressNotAllocated() - del self.hosts[ip_str] - # TODO(joshua) SCRUB from the leases file somehow - self.deexpress(address=ip_str) + @property + def host_objs(self): + for address in self.assigned: + yield PublicAddress(address) + + 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): - if not public_ip in self.hosts: + if not public_ip in self.assigned: raise exception.AddressNotAllocated() - for addr in self.hosts.values(): - if addr.has_key('private_ip') and addr['private_ip'] == private_ip: + # TODO(joshua): Keep an index going both ways + for addr in self.host_objs: + if addr.get('private_ip', None) == private_ip: 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() - self.hosts[public_ip]['private_ip'] = private_ip - self.hosts[public_ip]['instance_id'] = instance_id + addr['private_ip'] = private_ip + addr['instance_id'] = instance_id + addr.save() self.express(address=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() - 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() - self.deexpress(public_ip) - del self.hosts[public_ip]['private_ip'] - del self.hosts[public_ip]['instance_id'] - # TODO Express the removal - - 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)) + self.deexpress(address=public_ip) + addr['private_ip'] = 'available' + addr['instance_id'] = 'available' + addr.save() def express(self, address=None): - logging.debug("Todo - need to create IPTables natting entries for this net.") - addresses = self.hosts.values() + addresses = self.host_objs if address: - addresses = [self.hosts[address]] + addresses = [self.get_host(address)] for addr in addresses: - if not addr.has_key('private_ip'): + if addr.get('private_ip','available') == 'available': continue public_ip = addr['address'] private_ip = addr['private_ip'] 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("POSTROUTING -t nat -s %s -j SNAT --to %s" % (private_ip, public_ip)) + linux_net.confirm_rule("PREROUTING -t nat -d %s -j DNAT --to %s" + % (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 - linux_net.confirm_rule("FORWARD -d %s -p icmp -j ACCEPT" % (private_ip)) - for (protocol, port) in [("tcp",80), ("tcp",22), ("udp",1194), ("tcp",443)]: - linux_net.confirm_rule("FORWARD -d %s -p %s --dport %s -j ACCEPT" % (private_ip, protocol, port)) + linux_net.confirm_rule("FORWARD -d %s -p icmp -j ACCEPT" + % (private_ip)) + 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): - # TODO - Allocations need to be system global - - def __init__(self): - self.network = IPy.IP(FLAGS.private_range) - 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): - start = (vlan-self.startvlan) * self.netsize - net_str = "%s-%s" % (self.network[start], self.network[start + self.netsize - 1]) - logging.debug("Allocating %s" % net_str) - return net_str - - -class VlanPool(object): - def __init__(self, **kwargs): - self.start = FLAGS.vlan_start - self.end = FLAGS.vlan_end - self.vlans = kwargs.get('vlans', {}) - self.vlanpool = {} - self.manager = users.UserManager.instance() - for project_id, vlan in self.vlans.iteritems(): - self.vlanpool[vlan] = project_id - - def to_dict(self): - return {'vlans': self.vlans} - - def __str__(self): - return json.dumps(self.to_dict()) - - def __unicode__(self): - 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") - return self.assign_vlan(project_id, nextvlan) - - -class NetworkController(object): - """ The network controller is in charge of network connections """ - - def __init__(self, **kwargs): - logging.debug("Starting up the network controller.") - 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']) +VLANS_KEY = "vlans" +def _add_vlan(project_id, vlan): + datastore.Redis.instance().hset(VLANS_KEY, project_id, vlan) - 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 _rem_vlan(project_id): + datastore.Redis.instance().hdel(VLANS_KEY, project_id) - 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() - - def describe_addresses(self, type=PrivateNetwork): - if type == PrivateNetwork: - 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): - net = self.public_net - rv = net.associate_address(address, private_ip, instance_id) - _get_keeper()['public'] = net.to_dict() - return rv - - def disassociate_address(self, address): - net = self.public_net - rv = net.disassociate_address(address) - _get_keeper()['public'] = net.to_dict() - return rv - - def express(self,address=None): - for project in self.manager.get_projects(): - self.get_project_network(project.id).express() - - def report_state(self): - pass +def get_assigned_vlans(): + """ Returns a dictionary, with keys of project_id and values of vlan_id """ + return datastore.Redis.instance().hgetall(VLANS_KEY) +def get_vlan_for_project(project_id): + """ + Allocate vlan IDs to individual users. + """ + vlan = datastore.Redis.instance().hget(VLANS_KEY, project_id) + if vlan: + return vlan + assigned_vlans = get_assigned_vlans() + # TODO(joshua) I can do this in one loop, I think + for old_project_id, vlan in assigned_vlans.iteritems(): + if not users.UserManager.instance().get_project(old_project_id): + _rem_vlan(old_project_id) + _add_vlan(project_id, vlan) + return vlan + for vlan in range(FLAGS.vlan_start, FLAGS.vlan_end): + if not str(vlan) in assigned_vlans.values(): + _add_vlan(project_id, vlan) + return vlan + raise exception.AddressNotAllocated("Out of VLANs") + + +def get_network_by_address(address): + for project in users.UserManager.instance().get_projects(): + net = get_project_network(project.id) + if address in net.assigned: + return net + raise exception.AddressNotAllocated() + +def allocate_ip(user_id, project_id, mac): + return get_project_network(project_id).allocate_ip(user_id, project_id, mac) + +def deallocate_ip(address): + return get_network_by_address(address).deallocate_ip(address) + +def get_project_network(project_id, security_group='default'): + """ get a project's private network, allocating one if needed """ + project = users.UserManager.instance().get_project(project_id) + if not project: + 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 get_subnet_from_vlan(vlan): + """Assign one subnet to each VLAN, for now.""" + vlan = int(vlan) + network = IPy.IP(FLAGS.private_range) + start = (vlan-FLAGS.vlan_start) * FLAGS.network_size + 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() diff --git a/nova/compute/node.py b/nova/compute/node.py index 0f9a15672..7c1636f34 100644 --- a/nova/compute/node.py +++ b/nova/compute/node.py @@ -137,11 +137,15 @@ class Node(object, service.Service): logging.debug("Reporting State") return - @exception.wrap_exception + # @exception.wrap_exception def run_instance(self, instance_id, **_kwargs): """ launch a new instance with specified options """ logging.debug("Starting instance %s..." % (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.save() # TODO(vish) check to make sure the availability zone matches @@ -337,8 +341,6 @@ class Instance(object): 'basepath', os.path.abspath( os.path.join(FLAGS.instances_path, self.name))) 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['kernel_id'] = data.get('kernel_id', FLAGS.default_kernel) 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['availability_zone'] = data.get('availability_zone', 'fixme') + self._s['bridge_name'] = data.get('bridge_name', None) #TODO: put real dns items here self._s['private_dns_name'] = data.get('private_dns_name', 'fixme') self._s['dns_name'] = data.get('dns_name', @@ -476,7 +479,7 @@ class Instance(object): logging.debug('rebooted instance %s' % self.name) defer.returnValue(None) - @exception.wrap_exception + # @exception.wrap_exception def spawn(self): self.datamodel['state'] = "spawning" self.datamodel.save() @@ -516,30 +519,3 @@ class Instance(object): else: console = 'FAKE CONSOLE OUTPUT' 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) - diff --git a/nova/datastore.py b/nova/datastore.py index 97c00264d..0da89d594 100644 --- a/nova/datastore.py +++ b/nova/datastore.py @@ -28,6 +28,7 @@ import json import logging import os import sqlite3 +import time from nova import vendor import redis @@ -77,10 +78,11 @@ class RedisModel(object): def set_default_state(self): self.state = {'state' : 'pending'} self.state[self.object_type+"_id"] = self.object_id + self.state["create_time"] = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()) @property def __redis_key(self): - """ Magic string for instance keys """ + """ Magic string for keys """ return '%s:%s' % (self.object_type, self.object_id) def __repr__(self): diff --git a/nova/endpoint/cloud.py b/nova/endpoint/cloud.py index 53290e386..768a6b3e0 100644 --- a/nova/endpoint/cloud.py +++ b/nova/endpoint/cloud.py @@ -60,7 +60,7 @@ class CloudController(object): """ def __init__(self): self.instdir = model.InstanceDirectory() - self.network = network.NetworkController() + self.network = network.PublicNetworkController() self.setup() @property @@ -254,21 +254,10 @@ class CloudController(object): res.addCallback(_format_result) 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): - # right now all addresses are allocated locally # FIXME(vish) this should move into network.py - for network_address in self.network.describe_addresses(): - if network_address[u'address'] == public_ip: - address = self._convert_address(network_address) + for address in self.network.hosts: + if address['address'] == public_ip: if context.user.is_admin() or address['project_id'] == context.project.id: return address raise exception.NotFound("Address at ip %s not found" % public_ip) @@ -398,14 +387,12 @@ class CloudController(object): def format_addresses(self, context): addresses = [] # 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) - address = self._convert_address(network_address) address_rv = { - 'public_ip': address['public_ip'], + 'public_ip': address['address'], 'instance_id' : address.get('instance_id', 'free') } - # FIXME: add another field for user id if context.user.is_admin(): address_rv['instance_id'] = "%s (%s, %s)" % ( address['instance_id'], @@ -417,17 +404,17 @@ class CloudController(object): return {'addressesSet': addresses} def allocate_address(self, context, **kwargs): - (address,network_name) = self.network.allocate_address( - context.user.id, context.project_id, type=network.PublicNetwork) + address = self.network.allocate_ip( + context.user.id, context.project.id, 'public') return defer.succeed({'addressSet': [{'publicIp' : address}]}) 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."]}) def associate_address(self, context, instance_id, **kwargs): instance = self._get_instance(context, instance_id) - rv = self.network.associate_address( + self.network.associate_address( kwargs['public_ip'], instance['private_dns_name'], instance_id) @@ -435,7 +422,7 @@ class CloudController(object): def disassociate_address(self, context, public_ip, **kwargs): 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 return defer.succeed({'disassociateResponse': ["Address disassociated."]}) @@ -466,14 +453,10 @@ class CloudController(object): inst['project_id'] = context.project.id inst['mac_address'] = utils.generate_mac() inst['ami_launch_index'] = num - address, _netname = self.network.allocate_address( - user_id=inst['user_id'], - 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 + address = network.allocate_ip( + inst['user_id'], inst['project_id'], mac=inst['mac_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 inst.save() rpc.cast(FLAGS.compute_topic, @@ -502,7 +485,7 @@ class CloudController(object): if instance.get('private_dns_name', None): logging.debug("Deallocating address %s" % instance.get('private_dns_name', None)) try: - self.network.deallocate_address(instance.get('private_dns_name', None)) + self.network.deallocate_ip(instance.get('private_dns_name', None)) except Exception, _err: pass if instance.get('node_name', 'unassigned') != 'unassigned': #It's also internal default diff --git a/nova/tests/network_unittest.py b/nova/tests/network_unittest.py index a78d9075d..a2ad89a32 100644 --- a/nova/tests/network_unittest.py +++ b/nova/tests/network_unittest.py @@ -23,11 +23,17 @@ from nova import flags from nova import test from nova.compute import network from nova.auth import users +from nova import utils class NetworkTestCase(test.TrialTestCase): def setUp(self): 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) self.manager = users.UserManager.instance() try: @@ -37,7 +43,7 @@ class NetworkTestCase(test.TrialTestCase): name = 'project%s' % i if not self.manager.get_project(name): self.manager.create_project(name, 'netuser', name) - self.network = network.NetworkController(netsize=16) + self.network = network.PublicNetworkController() def tearDown(self): super(NetworkTestCase, self).tearDown() @@ -46,70 +52,69 @@ class NetworkTestCase(test.TrialTestCase): self.manager.delete_project(name) self.manager.delete_user('netuser') - def test_network_serialization(self): - net1 = network.Network(vlan=100, network="192.168.100.0/24", conn=None) - address = net1.allocate_ip("netuser", "project0", "01:24:55:36:f2:a0") - net_json = str(net1) - net2 = network.Network.from_json(net_json) - self.assertEqual(net_json, str(net2)) - self.assertTrue(IPy.IP(address) in net2.network) + def test_public_network_allocation(self): + pubnet = IPy.IP(flags.FLAGS.public_range) + address = self.network.allocate_ip("netuser", "project0", "public") + self.assertTrue(IPy.IP(address) in pubnet) + self.assertTrue(IPy.IP(address) in self.network.network) - def test_allocate_deallocate_address(self): - (address, net_name) = self.network.allocate_address("netuser", - "project0", "01:24:55:36:f2:a0") + def test_allocate_deallocate_ip(self): + 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")) - rv = self.network.deallocate_address(address) + rv = network.deallocate_ip(address) self.assertEqual(False, address in self._get_project_addresses("project0")) def test_range_allocation(self): - (address, net_name) = self.network.allocate_address("netuser", - "project0", "01:24:55:36:f2:a0") - (secondaddress, net_name) = self.network.allocate_address("netuser", - "project1", "01:24:55:36:f2:a0") - self.assertEqual(True, address in self._get_project_addresses("project0")) + address = network.allocate_ip( + "netuser", "project0", utils.generate_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")) - rv = self.network.deallocate_address(address) + rv = network.deallocate_ip(address) self.assertEqual(False, address in self._get_project_addresses("project0")) - rv = self.network.deallocate_address(secondaddress) + rv = network.deallocate_ip(secondaddress) self.assertEqual(False, secondaddress in self._get_project_addresses("project1")) 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): project_id = "project%s" % (project) - (address, net_name) = self.network.allocate_address("netuser", - project_id, "01:24:55:36:f2:a0") - (address2, net_name) = self.network.allocate_address("netuser", - project_id, "01:24:55:36:f2:a0") - (address3, net_name) = self.network.allocate_address("netuser", - project_id, "01:24:55:36:f2:a0") + address = network.allocate_ip( + "netuser", project_id, utils.generate_mac()) + address2 = network.allocate_ip( + "netuser", project_id, utils.generate_mac()) + 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")) - rv = self.network.deallocate_address(address) - rv = self.network.deallocate_address(address2) - rv = self.network.deallocate_address(address3) - rv = self.network.deallocate_address(secondaddress) + rv = network.deallocate_ip(address) + rv = network.deallocate_ip(address2) + rv = network.deallocate_ip(address3) + rv = network.deallocate_ip(secondaddress) 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, net_name) = self.network.allocate_address("netuser", - name, "01:24:55:36:f2:a0") + address = network.allocate_ip( + "netuser", name, utils.generate_mac()) + rv = network.deallocate_ip(address) self.manager.delete_project(name) def _get_project_addresses(self, project_id): - rv = self.network.describe_addresses() project_addresses = [] - for item in rv: - if item['project_id'] == project_id: - project_addresses.append(item['address']) + for addr in network.get_project_network(project_id).list_addresses(): + project_addresses.append(addr) return project_addresses |