summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVishvananda Ishaya <vishvananda@yahoo.com>2010-08-23 13:55:16 -0700
committerVishvananda Ishaya <vishvananda@yahoo.com>2010-08-23 13:55:16 -0700
commit78c2175898a468ae734e27dfbc8f5b70f90fd477 (patch)
tree33e7bb0f56c1b44d1ba46c36f7c82be3997dfecb
parent152baf34247c5a4b76f643cac0d33c0158de0bfa (diff)
Refactored network model access into data abstraction layer.
Also changed the name to floating_ip.
-rwxr-xr-xbin/nova-dhcpbridge23
-rw-r--r--nova/db/api.py112
-rw-r--r--nova/db/sqlalchemy/api.py187
-rw-r--r--nova/endpoint/cloud.py28
-rw-r--r--nova/models.py8
-rw-r--r--nova/network/linux_net.py24
-rw-r--r--nova/network/service.py328
-rw-r--r--nova/tests/network_unittest.py70
8 files changed, 497 insertions, 283 deletions
diff --git a/bin/nova-dhcpbridge b/bin/nova-dhcpbridge
index b17a56e6e..8008100f6 100755
--- a/bin/nova-dhcpbridge
+++ b/bin/nova-dhcpbridge
@@ -25,9 +25,9 @@ import logging
import os
import sys
-#TODO(joshua): there is concern that the user dnsmasq runs under will not
-# have nova in the path. This should be verified and if it is
-# not true the ugly line below can be removed
+# TODO(joshua): there is concern that the user dnsmasq runs under will not
+# have nova in the path. This should be verified and if it is
+# not true the ugly line below can be removed
sys.path.append(os.path.abspath(os.path.join(__file__, "../../")))
from nova import flags
@@ -36,6 +36,7 @@ from nova import utils
from nova.network import linux_net
from nova.network import service
from nova import datastore # for redis_db flag
+from nova.auth import manager # for auth flags
FLAGS = flags.FLAGS
@@ -43,16 +44,16 @@ FLAGS = flags.FLAGS
def add_lease(_mac, ip, _hostname, _interface):
"""Set the IP that was assigned by the DHCP server."""
if FLAGS.fake_rabbit:
- logging.debug("leasing_ip")
+ logging.debug("leasing ip")
from nova import models
print models.FixedIp.count()
print models.Network.count()
print FLAGS.sql_connection
- service.VlanNetworkService().lease_ip(ip)
+ service.VlanNetworkService().lease_fixed_ip(ip)
else:
rpc.cast("%s.%s" % (FLAGS.network_topic, FLAGS.node_name),
- {"method": "lease_ip",
- "args": {"fixed_ip_str": ip}})
+ {"method": "lease_fixed_ip",
+ "args": {"address": ip}})
def old_lease(_mac, _ip, _hostname, _interface):
@@ -63,12 +64,12 @@ def old_lease(_mac, _ip, _hostname, _interface):
def del_lease(_mac, ip, _hostname, _interface):
"""Called when a lease expires."""
if FLAGS.fake_rabbit:
- logging.debug("releasing_ip")
- service.VlanNetworkService().release_ip(ip)
+ logging.debug("releasing ip")
+ service.VlanNetworkService().release_fixed_ip(ip)
else:
rpc.cast("%s.%s" % (FLAGS.network_topic, FLAGS.node_name),
- {"method": "release_ip",
- "args": {"fixed_ip_str": ip}})
+ {"method": "release_fixed_ip",
+ "args": {"address": ip}})
def init_leases(interface):
diff --git a/nova/db/api.py b/nova/db/api.py
index bbd69ec65..a0e2b3715 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -30,16 +30,24 @@ _impl = utils.LazyPluggable(FLAGS['db_backend'],
sqlalchemy='nova.db.sqlalchemy.api')
+class NoMoreAddresses(exception.Error):
+ pass
+
+
class NoMoreBlades(exception.Error):
pass
+class NoMoreNetworks(exception.Error):
+ pass
+
+
###################
def daemon_get(context, node_name, binary):
return _impl.daemon_get(context, node_name, binary)
-
+
def daemon_create(context, values):
return _impl.daemon_create(context, values)
@@ -52,6 +60,78 @@ def daemon_update(context, values):
###################
+def floating_ip_allocate_address(context, node_name, project_id):
+ """Allocate free floating ip and return the address.
+
+ Raises if one is not available.
+ """
+ return _impl.floating_ip_allocate_address(context, node_name, project_id)
+
+
+def floating_ip_fixed_ip_associate(context, floating_address, fixed_address):
+ """Associate an floating ip to a fixed_ip by address."""
+ return _impl.floating_ip_fixed_ip_associate(context,
+ floating_address,
+ fixed_address)
+
+
+def floating_ip_disassociate(context, address):
+ """Disassociate an floating ip from a fixed ip by address.
+
+ Returns the address of the existing fixed ip.
+ """
+ return _impl.floating_ip_disassociate(context, address)
+
+
+def floating_ip_deallocate(context, address):
+ """Deallocate an floating ip by address"""
+ return _impl.floating_ip_deallocate(context, address)
+
+
+####################
+
+
+def fixed_ip_allocate_address(context, network_id):
+ """Allocate free fixed ip and return the address.
+
+ Raises if one is not available.
+ """
+ return _impl.fixed_ip_allocate_address(context, network_id)
+
+
+def fixed_ip_get_by_address(context, address):
+ """Get a fixed ip by address."""
+ return _impl.fixed_ip_get_by_address(context, address)
+
+
+def fixed_ip_lease(context, address):
+ """Lease a fixed ip by address."""
+ return _impl.fixed_ip_lease(context, address)
+
+
+def fixed_ip_release(context, address):
+ """Un-Lease a fixed ip by address."""
+ return _impl.fixed_ip_release(context, address)
+
+
+def fixed_ip_deallocate(context, address):
+ """Deallocate a fixed ip by address."""
+ return _impl.fixed_ip_deallocate(context, address)
+
+
+def fixed_ip_instance_associate(context, address, instance_id):
+ """Associate a fixed ip to an instance by address."""
+ return _impl.fixed_ip_instance_associate(context, address, instance_id)
+
+
+def fixed_ip_instance_disassociate(context, address):
+ """Disassociate a fixed ip from an instance by address."""
+ return _impl.fixed_ip_instance_disassociate(context, address)
+
+
+####################
+
+
def instance_create(context, values):
"""Create an instance from the values dictionary."""
return _impl.instance_create(context, values)
@@ -89,16 +169,46 @@ def network_create(context, values):
return _impl.network_create(context, values)
+def network_create_fixed_ips(context, network_id, num_vpn_clients):
+ """Create the ips for the network, reserving sepecified ips."""
+ return _impl.network_create_fixed_ips(context, network_id, num_vpn_clients)
+
+
def network_destroy(context, network_id):
"""Destroy the network or raise if it does not exist."""
return _impl.network_destroy(context, network_id)
+def network_ensure_indexes(context, num_networks):
+ """Ensure that network indexes exist, creating them if necessary."""
+ return _impl.network_ensure_indexes(context, num_networks)
+
+
def network_get(context, network_id):
"""Get an network or raise if it does not exist."""
return _impl.network_get(context, network_id)
+def network_get_host(context, network_id):
+ """Get host assigned to network or raise"""
+ return _impl.network_get_host(context, network_id)
+
+
+def network_get_index(context, network_id):
+ """Gets non-conflicting index for network"""
+ return _impl.network_get_index(context, network_id)
+
+
+def network_set_cidr(context, network_id, cidr):
+ """Set the Classless Inner Domain Routing for the network"""
+ return _impl.network_set_cidr(context, network_id, cidr)
+
+
+def network_set_host(context, network_id, host_id):
+ """Safely set the host for network"""
+ return _impl.network_set_host(context, network_id, host_id)
+
+
def network_update(context, network_id, values):
"""Set the given properties on an network and update it.
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index e883e14cb..a3a5ff8de 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -16,6 +16,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import IPy
+
from nova import db
from nova import exception
from nova import models
@@ -27,7 +29,7 @@ from nova import models
def daemon_get(context, node_name, binary):
return None
return models.Daemon.find_by_args(node_name, binary)
-
+
def daemon_create(context, values):
daemon_ref = models.Daemon(**values)
@@ -45,6 +47,99 @@ def daemon_update(context, node_name, binary, values):
###################
+def floating_ip_allocate_address(context, node_name, project_id):
+ session = models.NovaBase.get_session()
+ query = session.query(models.FloatingIp).filter_by(node_name=node_name)
+ query = query.filter_by(fixed_ip_id=None).with_lockmode("update")
+ floating_ip_ref = query.first()
+ # NOTE(vish): if with_lockmode isn't supported, as in sqlite,
+ # then this has concurrency issues
+ if not floating_ip_ref:
+ raise db.NoMoreAddresses()
+ floating_ip_ref['project_id'] = project_id
+ session.add(floating_ip_ref)
+ session.commit()
+ return floating_ip_ref['ip_str']
+
+
+def floating_ip_fixed_ip_associate(context, floating_address, fixed_address):
+ floating_ip_ref = models.FloatingIp.find_by_ip_str(floating_address)
+ fixed_ip_ref = models.FixedIp.find_by_ip_str(fixed_address)
+ floating_ip_ref.fixed_ip = fixed_ip_ref
+ floating_ip_ref.save()
+
+
+def floating_ip_disassociate(context, address):
+ floating_ip_ref = models.FloatingIp.find_by_ip_str(address)
+ fixed_ip_address = floating_ip_ref.fixed_ip['ip_str']
+ floating_ip_ref['fixed_ip'] = None
+ floating_ip_ref.save()
+ return fixed_ip_address
+
+def floating_ip_deallocate(context, address):
+ floating_ip_ref = models.FloatingIp.find_by_ip_str(address)
+ floating_ip_ref['project_id'] = None
+ floating_ip_ref.save()
+
+###################
+
+
+def fixed_ip_allocate_address(context, network_id):
+ session = models.NovaBase.get_session()
+ query = session.query(models.FixedIp).filter_by(network_id=network_id)
+ query = query.filter_by(reserved=False).filter_by(allocated=False)
+ query = query.filter_by(leased=False).with_lockmode("update")
+ fixed_ip_ref = query.first()
+ # NOTE(vish): if with_lockmode isn't supported, as in sqlite,
+ # then this has concurrency issues
+ if not fixed_ip_ref:
+ raise db.NoMoreAddresses()
+ fixed_ip_ref['allocated'] = True
+ session.add(fixed_ip_ref)
+ session.commit()
+ return fixed_ip_ref['ip_str']
+
+
+def fixed_ip_get_by_address(context, address):
+ return models.FixedIp.find_by_ip_str(address)
+
+
+def fixed_ip_lease(context, address):
+ fixed_ip_ref = fixed_ip_get_by_address(context, address)
+ if not fixed_ip_ref['allocated']:
+ raise db.AddressNotAllocated(address)
+ fixed_ip_ref['leased'] = True
+ fixed_ip_ref.save()
+
+
+def fixed_ip_release(context, address):
+ fixed_ip_ref = fixed_ip_get_by_address(context, address)
+ fixed_ip_ref['allocated'] = False
+ fixed_ip_ref['leased'] = False
+ fixed_ip_ref.save()
+
+
+def fixed_ip_deallocate(context, address):
+ fixed_ip_ref = fixed_ip_get_by_address(context, address)
+ fixed_ip_ref['allocated'] = False
+ fixed_ip_ref.save()
+
+
+def fixed_ip_instance_associate(context, address, instance_id):
+ fixed_ip_ref = fixed_ip_get_by_address(context, address)
+ fixed_ip_ref.instance = instance_get(context, instance_id)
+ fixed_ip_ref.save()
+
+
+def fixed_ip_instance_disassociate(context, address):
+ fixed_ip_ref = fixed_ip_get_by_address(context, address)
+ fixed_ip_ref.instance = None
+ fixed_ip_ref.save()
+
+
+###################
+
+
def instance_create(context, values):
instance_ref = models.Instance()
for (key, value) in values.iteritems():
@@ -85,13 +180,99 @@ def network_create(context, values):
return network_ref
+def network_create_fixed_ips(context, network_id, num_vpn_clients):
+ network_ref = network_get(context, network_id)
+ # NOTE(vish): should these be properties of the network as opposed
+ # to constants?
+ BOTTOM_RESERVED = 3
+ TOP_RESERVED = 1 + num_vpn_clients
+ project_net = IPy.IP(network_ref['cidr'])
+ num_ips = len(project_net)
+ session = models.NovaBase.get_session()
+ for i in range(num_ips):
+ fixed_ip = models.FixedIp()
+ fixed_ip.ip_str = str(project_net[i])
+ if i < BOTTOM_RESERVED or num_ips - i < TOP_RESERVED:
+ fixed_ip['reserved'] = True
+ fixed_ip['network'] = network_get(context, network_id)
+ session.add(fixed_ip)
+ session.commit()
+
+
+def network_ensure_indexes(context, num_networks):
+ if models.NetworkIndex.count() == 0:
+ session = models.NovaBase.get_session()
+ for i in range(num_networks):
+ network_index = models.NetworkIndex()
+ network_index.index = i
+ session.add(network_index)
+ session.commit()
+
+
def network_destroy(context, network_id):
network_ref = network_get(context, network_id)
network_ref.delete()
def network_get(context, network_id):
- return models.Instance.find(network_id)
+ return models.Network.find(network_id)
+
+
+def network_get_vpn_ip(context, network_id):
+ # TODO(vish): possible concurrency issue here
+ network = network_get(context, network_id)
+ address = network['vpn_private_ip_str']
+ fixed_ip = fixed_ip_get_by_address(context, address)
+ if fixed_ip['allocated']:
+ raise db.AddressAlreadyAllocated()
+ db.fixed_ip_allocate(context, {'allocated': True})
+
+
+def network_get_host(context, network_id):
+ network_ref = network_get(context, network_id)
+ return network_ref['node_name']
+
+
+def network_get_index(context, network_id):
+ session = models.NovaBase.get_session()
+ query = session.query(models.NetworkIndex).filter_by(network_id=None)
+ network_index = query.with_lockmode("update").first()
+ if not network_index:
+ raise db.NoMoreNetworks()
+ network_index['network'] = network_get(context, network_id)
+ session.add(network_index)
+ session.commit()
+ return network_index['index']
+
+
+def network_set_cidr(context, network_id, cidr):
+ network_ref = network_get(context, network_id)
+ project_net = IPy.IP(cidr)
+ network_ref['cidr'] = cidr
+ # FIXME we can turn these into properties
+ network_ref['netmask'] = str(project_net.netmask())
+ network_ref['gateway'] = str(project_net[1])
+ network_ref['broadcast'] = str(project_net.broadcast())
+ network_ref['vpn_private_ip_str'] = str(project_net[2])
+
+
+def network_set_host(context, network_id, host_id):
+ session = models.NovaBase.get_session()
+ # FIXME will a second request fail or wait for first to finish?
+ query = session.query(models.Network).filter_by(id=network_id)
+ network = query.with_lockmode("update").first()
+ if not network:
+ raise exception.NotFound("Couldn't find network with %s" %
+ network_id)
+ # NOTE(vish): if with_lockmode isn't supported, as in sqlite,
+ # then this has concurrency issues
+ if network.node_name:
+ session.commit()
+ return network['node_name']
+ network['node_name'] = host_id
+ session.add(network)
+ session.commit()
+ return network['node_name']
def network_update(context, network_id, values):
@@ -110,7 +291,7 @@ def project_get_network(context, project_id):
if not rv:
raise exception.NotFound('No network for project: %s' % project_id)
return rv
-
+
###################
diff --git a/nova/endpoint/cloud.py b/nova/endpoint/cloud.py
index e5d4661df..e64005c2e 100644
--- a/nova/endpoint/cloud.py
+++ b/nova/endpoint/cloud.py
@@ -311,7 +311,7 @@ class CloudController(object):
def _get_address(self, context, public_ip):
# FIXME(vish) this should move into network.py
- address = network_model.ElasticIp.lookup(public_ip)
+ address = network_model.FloatingIp.lookup(public_ip)
if address and (context.user.is_admin() or address['project_id'] == context.project.id):
return address
raise exception.NotFound("Address at ip %s not found" % public_ip)
@@ -459,7 +459,7 @@ class CloudController(object):
def format_addresses(self, context):
addresses = []
- for address in network_model.ElasticIp.all():
+ for address in network_model.FloatingIp.all():
# TODO(vish): implement a by_project iterator for addresses
if (context.user.is_admin() or
address['project_id'] == context.project.id):
@@ -481,7 +481,7 @@ class CloudController(object):
def allocate_address(self, context, **kwargs):
network_topic = yield self._get_network_topic(context)
public_ip = yield rpc.call(network_topic,
- {"method": "allocate_elastic_ip",
+ {"method": "allocate_floating_ip",
"args": {"user_id": context.user.id,
"project_id": context.project.id}})
defer.returnValue({'addressSet': [{'publicIp': public_ip}]})
@@ -492,8 +492,8 @@ class CloudController(object):
# NOTE(vish): Should we make sure this works?
network_topic = yield self._get_network_topic(context)
rpc.cast(network_topic,
- {"method": "deallocate_elastic_ip",
- "args": {"elastic_ip": public_ip}})
+ {"method": "deallocate_floating_ip",
+ "args": {"floating_ip": public_ip}})
defer.returnValue({'releaseResponse': ["Address released."]})
@rbac.allow('netadmin')
@@ -503,8 +503,8 @@ class CloudController(object):
address = self._get_address(context, public_ip)
network_topic = yield self._get_network_topic(context)
rpc.cast(network_topic,
- {"method": "associate_elastic_ip",
- "args": {"elastic_ip": address['address'],
+ {"method": "associate_floating_ip",
+ "args": {"floating_ip": address['address'],
"fixed_ip": instance['private_dns_name'],
"instance_id": instance['instance_id']}})
defer.returnValue({'associateResponse': ["Address associated."]})
@@ -515,8 +515,8 @@ class CloudController(object):
address = self._get_address(context, public_ip)
network_topic = yield self._get_network_topic(context)
rpc.cast(network_topic,
- {"method": "disassociate_elastic_ip",
- "args": {"elastic_ip": address['address']}})
+ {"method": "disassociate_floating_ip",
+ "args": {"floating_ip": address['address']}})
defer.returnValue({'disassociateResponse': ["Address disassociated."]})
@defer.inlineCallbacks
@@ -617,15 +617,15 @@ class CloudController(object):
logging.warning("Instance %s was not found during terminate"
% i)
continue
- elastic_ip = network_model.get_public_ip_for_instance(i)
- if elastic_ip:
- logging.debug("Disassociating address %s" % elastic_ip)
+ floating_ip = network_model.get_public_ip_for_instance(i)
+ if floating_ip:
+ logging.debug("Disassociating address %s" % floating_ip)
# NOTE(vish): Right now we don't really care if the ip is
# disassociated. We may need to worry about
# checking this later. Perhaps in the scheduler?
rpc.cast(network_topic,
- {"method": "disassociate_elastic_ip",
- "args": {"elastic_ip": elastic_ip}})
+ {"method": "disassociate_floating_ip",
+ "args": {"floating_ip": floating_ip}})
fixed_ip = instance.get('private_dns_name', None)
if fixed_ip:
diff --git a/nova/models.py b/nova/models.py
index e4cd37336..70caeff76 100644
--- a/nova/models.py
+++ b/nova/models.py
@@ -278,12 +278,12 @@ class FixedIp(Base, NovaBase):
raise exception.NotFound("No model for ip str %s" % ip_str)
-class ElasticIp(Base, NovaBase):
- __tablename__ = 'elastic_ips'
+class FloatingIp(Base, NovaBase):
+ __tablename__ = 'floating_ips'
id = Column(Integer, primary_key=True)
ip_str = Column(String(255), unique=True)
fixed_ip_id = Column(Integer, ForeignKey('fixed_ips.id'), nullable=True)
- fixed_ip = relationship(FixedIp, backref=backref('elastic_ips'))
+ fixed_ip = relationship(FixedIp, backref=backref('floating_ips'))
project_id = Column(String(255)) #, ForeignKey('projects.id'), nullable=False)
node_name = Column(String(255)) #, ForeignKey('physical_node.id'))
@@ -305,7 +305,7 @@ class Network(Base, NovaBase):
kind = Column(String(255))
injected = Column(Boolean, default=False)
- network_str = Column(String(255))
+ cidr = Column(String(255))
netmask = Column(String(255))
bridge = Column(String(255))
gateway = Column(String(255))
diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py
index 6fa3bae73..4a57a8393 100644
--- a/nova/network/linux_net.py
+++ b/nova/network/linux_net.py
@@ -40,15 +40,15 @@ flags.DEFINE_string('public_interface', 'vlan1',
flags.DEFINE_string('bridge_dev', 'eth0',
'network device for bridges')
-def bind_elastic_ip(elastic_ip):
+def bind_floating_ip(floating_ip):
"""Bind ip to public interface"""
- _execute("sudo ip addr add %s dev %s" % (elastic_ip,
+ _execute("sudo ip addr add %s dev %s" % (floating_ip,
FLAGS.public_interface))
-def unbind_elastic_ip(elastic_ip):
+def unbind_floating_ip(floating_ip):
"""Unbind a public ip from public interface"""
- _execute("sudo ip addr del %s dev %s" % (elastic_ip,
+ _execute("sudo ip addr del %s dev %s" % (floating_ip,
FLAGS.public_interface))
@@ -61,12 +61,12 @@ def ensure_vlan_forward(public_ip, port, private_ip):
DEFAULT_PORTS = [("tcp", 80), ("tcp", 22), ("udp", 1194), ("tcp", 443)]
-def ensure_elastic_forward(elastic_ip, fixed_ip):
- """Ensure elastic ip forwarding rule"""
+def ensure_floating_forward(floating_ip, fixed_ip):
+ """Ensure floating ip forwarding rule"""
_confirm_rule("PREROUTING -t nat -d %s -j DNAT --to %s"
- % (elastic_ip, fixed_ip))
+ % (floating_ip, fixed_ip))
_confirm_rule("POSTROUTING -t nat -s %s -j SNAT --to %s"
- % (fixed_ip, elastic_ip))
+ % (fixed_ip, floating_ip))
# TODO(joshua): Get these from the secgroup datastore entries
_confirm_rule("FORWARD -d %s -p icmp -j ACCEPT"
% (fixed_ip))
@@ -75,12 +75,12 @@ def ensure_elastic_forward(elastic_ip, fixed_ip):
"FORWARD -d %s -p %s --dport %s -j ACCEPT"
% (fixed_ip, protocol, port))
-def remove_elastic_forward(elastic_ip, fixed_ip):
- """Remove forwarding for elastic ip"""
+def remove_floating_forward(floating_ip, fixed_ip):
+ """Remove forwarding for floating ip"""
_remove_rule("PREROUTING -t nat -d %s -j DNAT --to %s"
- % (elastic_ip, fixed_ip))
+ % (floating_ip, fixed_ip))
_remove_rule("POSTROUTING -t nat -s %s -j SNAT --to %s"
- % (fixed_ip, elastic_ip))
+ % (fixed_ip, floating_ip))
_remove_rule("FORWARD -d %s -p icmp -j ACCEPT"
% (fixed_ip))
for (protocol, port) in DEFAULT_PORTS:
diff --git a/nova/network/service.py b/nova/network/service.py
index e47f07ef0..bb2e4ae8a 100644
--- a/nova/network/service.py
+++ b/nova/network/service.py
@@ -21,17 +21,15 @@ Network Hosts are responsible for allocating ips and setting up network
"""
import logging
+import math
import IPy
from nova import db
from nova import exception
from nova import flags
-from nova import models
from nova import service
from nova import utils
-from nova.auth import manager
-from nova.network import exception as network_exception
from nova.network import linux_net
@@ -67,9 +65,19 @@ flags.DEFINE_string('private_range', '10.0.0.0/8', 'Private IP address block')
flags.DEFINE_integer('cnt_vpn_clients', 5,
'Number of addresses reserved for vpn clients')
+
+class AddressAlreadyAllocated(exception.Error):
+ pass
+
+
+class AddressNotAllocated(exception.Error):
+ pass
+
+
# TODO(vish): some better type of dependency injection?
_driver = linux_net
+
def type_to_class(network_type):
"""Convert a network_type string into an actual Python class"""
if not network_type:
@@ -85,22 +93,14 @@ def type_to_class(network_type):
def setup_compute_network(project_id):
"""Sets up the network on a compute host"""
- network = get_network_for_project(project_id)
+ network = db.project_get_network(None, project_id)
srv = type_to_class(network.kind)
srv.setup_compute_network(network)
-def get_network_for_project(project_id, context=None):
- """Get network allocated to project from datastore"""
- project = manager.AuthManager().get_project(project_id)
- if not project:
- raise exception.NotFound("Couldn't find project %s" % project_id)
- return db.project_get_network(context, project_id)
-
-
def get_host_for_project(project_id):
"""Get host allocated to project from datastore"""
- return get_network_for_project(project_id).node_name
+ return db.project_get_network(None, project_id).node_name
class BaseNetworkService(service.Service):
@@ -109,57 +109,35 @@ class BaseNetworkService(service.Service):
This class must be subclassed.
"""
- def set_network_host(self, project_id):
+ def set_network_host(self, project_id, context=None):
"""Safely sets the host of the projects network"""
- # FIXME abstract this
- session = models.NovaBase.get_session()
- # FIXME will a second request fail or wait for first to finish?
- query = session.query(models.Network).filter_by(project_id=project_id)
- network = query.with_lockmode("update").first()
- if not network:
- raise exception.NotFound("Couldn't find network for %s" %
- project_id)
- # NOTE(vish): if with_lockmode isn't supported, as in sqlite,
- # then this has concurrency issues
- if network.node_name:
- session.commit()
- return network.node_name
- network.node_name = FLAGS.node_name
- network.kind = FLAGS.network_type
- session.add(network)
- session.commit()
- self._on_set_network_host(network)
- return network.node_name
-
- def allocate_fixed_ip(self, project_id, instance_id, *args, **kwargs):
+ network_ref = db.project_get_network(context, project_id)
+ # TODO(vish): can we minimize db access by just getting the
+ # id here instead of the ref?
+ network_id = network_ref['id']
+ host = db.network_set_host(context,
+ network_id,
+ FLAGS.node_name)
+ self._on_set_network_host(context, network_id)
+ return host
+
+ def allocate_fixed_ip(self, project_id, instance_id, context=None,
+ *args, **kwargs):
"""Gets fixed ip from the pool"""
- # FIXME abstract this
- network = get_network_for_project(project_id)
- session = models.NovaBase.get_session()
- query = session.query(models.FixedIp).filter_by(network_id=network.id)
- query = query.filter_by(reserved=False).filter_by(allocated=False)
- query = query.filter_by(leased=False).with_lockmode("update")
- fixed_ip = query.first()
- # NOTE(vish): if with_lockmode isn't supported, as in sqlite,
- # then this has concurrency issues
- if not fixed_ip:
- raise network_exception.NoMoreAddresses()
- # FIXME will this set backreference?
- fixed_ip.instance_id = instance_id
- fixed_ip.allocated = True
- session.add(fixed_ip)
- session.commit()
- return fixed_ip.ip_str
-
- def deallocate_fixed_ip(self, fixed_ip_str, *args, **kwargs):
+ network_ref = db.project_get_network(context, project_id)
+ address = db.fixed_ip_allocate_address(context, network_ref['id'])
+ db.fixed_ip_instance_associate(context,
+ address,
+ instance_id)
+ return address
+
+ def deallocate_fixed_ip(self, address, context=None):
"""Returns a fixed ip to the pool"""
- fixed_ip = models.FixedIp.find_by_ip_str(fixed_ip_str)
- fixed_ip.instance = None
- fixed_ip.allocated = False
- fixed_ip.save()
+ db.fixed_ip_deallocate(context, address)
+ db.fixed_ip_instance_disassociate(context, address)
- def _on_set_network_host(self, network, *args, **kwargs):
+ def _on_set_network_host(self, context, network_id):
"""Called when this host becomes the host for a project"""
pass
@@ -168,45 +146,32 @@ class BaseNetworkService(service.Service):
"""Sets up matching network for compute hosts"""
raise NotImplementedError()
- def allocate_elastic_ip(self, project_id):
- """Gets an elastic ip from the pool"""
- # FIXME: add elastic ips through manage command
- # FIXME: abstract this
- session = models.NovaBase.get_session()
- node_name = FLAGS.node_name
- query = session.query(models.ElasticIp).filter_by(node_name=node_name)
- query = query.filter_by(fixed_ip_id=None).with_lockmode("update")
- elastic_ip = query.first()
- if not elastic_ip:
- raise network_exception.NoMoreAddresses()
- elastic_ip.project_id = project_id
- session.add(elastic_ip)
- session.commit()
- return elastic_ip.ip_str
-
- def associate_elastic_ip(self, elastic_ip_str, fixed_ip_str):
- """Associates an elastic ip to a fixed ip"""
- elastic_ip = models.ElasticIp.find_by_ip_str(elastic_ip_str)
- fixed_ip = models.FixedIp.find_by_ip_str(fixed_ip_str)
- elastic_ip.fixed_ip = fixed_ip
- _driver.bind_elastic_ip(elastic_ip_str)
- _driver.ensure_elastic_forward(elastic_ip_str, fixed_ip_str)
- elastic_ip.save()
-
- def disassociate_elastic_ip(self, elastic_ip_str):
- """Disassociates a elastic ip"""
- elastic_ip = models.ElasticIp.find_by_ip_str(elastic_ip_str)
- fixed_ip_str = elastic_ip.fixed_ip.ip_str
- elastic_ip.fixed_ip = None
- _driver.unbind_elastic_ip(elastic_ip_str)
- _driver.remove_elastic_forward(elastic_ip_str, fixed_ip_str)
- elastic_ip.save()
-
- def deallocate_elastic_ip(self, elastic_ip_str):
- """Returns an elastic ip to the pool"""
- elastic_ip = models.ElasticIp.find_by_ip_str(elastic_ip_str)
- elastic_ip.project_id = None
- elastic_ip.save()
+ def allocate_floating_ip(self, project_id, context=None):
+ """Gets an floating ip from the pool"""
+ # TODO(vish): add floating ips through manage command
+ return db.floating_ip_allocate_address(context,
+ FLAGS.node_name,
+ project_id)
+
+ def associate_floating_ip(self, floating_address, fixed_address,
+ context=None):
+ """Associates an floating ip to a fixed ip"""
+ db.floating_ip_fixed_ip_associate(context,
+ floating_address,
+ fixed_address)
+ _driver.bind_floating_ip(floating_address)
+ _driver.ensure_floating_forward(floating_address, fixed_address)
+
+ def disassociate_floating_ip(self, floating_address, context=None):
+ """Disassociates a floating ip"""
+ fixed_address = db.floating_ip_disassociate(context,
+ floating_address)
+ _driver.unbind_floating_ip(floating_address)
+ _driver.remove_floating_forward(floating_address, fixed_address)
+
+ def deallocate_floating_ip(self, floating_address, context=None):
+ """Returns an floating ip to the pool"""
+ db.floating_ip_deallocate(context, floating_address)
class FlatNetworkService(BaseNetworkService):
@@ -217,141 +182,96 @@ class FlatNetworkService(BaseNetworkService):
"""Network is created manually"""
pass
- def _on_set_network_host(self, network, *args, **kwargs):
+ def _on_set_network_host(self, context, network_id):
"""Called when this host becomes the host for a project"""
- # FIXME should there be two types of network objects in the database?
- network.injected = True
- network.network_str=FLAGS.flat_network_network
- network.netmask=FLAGS.flat_network_netmask
- network.bridge=FLAGS.flat_network_bridge
- network.gateway=FLAGS.flat_network_gateway
- network.broadcast=FLAGS.flat_network_broadcast
- network.dns=FLAGS.flat_network_dns
- network.save()
- # FIXME add public ips from flags to the datastore
+ # NOTE(vish): should there be two types of network objects
+ # in the database?
+ net = {}
+ net['injected'] = True
+ net['kind'] = FLAGS.network_type
+ net['network_str']=FLAGS.flat_network_network
+ net['netmask']=FLAGS.flat_network_netmask
+ net['bridge']=FLAGS.flat_network_bridge
+ net['gateway']=FLAGS.flat_network_gateway
+ net['broadcast']=FLAGS.flat_network_broadcast
+ net['dns']=FLAGS.flat_network_dns
+ db.network_update(context, network_id, net)
+ # TODO(vish): add public ips from flags to the datastore
class VlanNetworkService(BaseNetworkService):
"""Vlan network with dhcp"""
def __init__(self, *args, **kwargs):
super(VlanNetworkService, self).__init__(*args, **kwargs)
- self._ensure_network_indexes()
-
- def _ensure_network_indexes(self):
# NOTE(vish): this should probably be removed and added via
# admin command or fixtures
- if models.NetworkIndex.count() == 0:
- session = models.NovaBase.get_session()
- for i in range(FLAGS.num_networks):
- network_index = models.NetworkIndex()
- network_index.index = i
- session.add(network_index)
- session.commit()
+ db.network_ensure_indexes(None, FLAGS.num_networks)
def allocate_fixed_ip(self, project_id, instance_id, is_vpn=False,
- *args, **kwargs):
+ context=None, *args, **kwargs):
"""Gets a fixed ip from the pool"""
- network = get_network_for_project(project_id)
+ network_ref = db.project_get_network(context, project_id)
if is_vpn:
- # FIXME concurrency issue?
- fixed_ip = models.FixedIp.find_by_ip_str(network.vpn_private_ip_str)
- if fixed_ip.allocated:
- raise network_exception.AddressAlreadyAllocated()
- # FIXME will this set backreference?
- fixed_ip.instance_id = instance_id
- fixed_ip.allocated = True
- fixed_ip.save()
- _driver.ensure_vlan_forward(network.vpn_public_ip_str,
- network.vpn_public_port,
- network.vpn_private_ip_str)
- ip_str = fixed_ip.ip_str
- logging.debug("Allocating vpn IP %s", ip_str)
+ address = db.network_get_vpn_ip_address(context,
+ network_ref['id'])
+ logging.debug("Allocating vpn IP %s", address)
+ db.fixed_ip_instance_associate(context,
+ address,
+ instance_id)
+ _driver.ensure_vlan_forward(network_ref['vpn_public_ip_str'],
+ network_ref['vpn_public_port'],
+ network_ref['vpn_private_ip_str'])
else:
parent = super(VlanNetworkService, self)
- ip_str = parent.allocate_fixed_ip(project_id, instance_id)
- _driver.ensure_vlan_bridge(network.vlan, network.bridge)
- return ip_str
-
- def deallocate_fixed_ip(self, fixed_ip_str):
+ address = parent.allocate_fixed_ip(project_id,
+ instance_id,
+ context)
+ _driver.ensure_vlan_bridge(network_ref['vlan'],
+ network_ref['bridge'])
+ return address
+
+ def deallocate_fixed_ip(self, address, context=None):
"""Returns an ip to the pool"""
- fixed_ip = models.FixedIp.find_by_ip_str(fixed_ip_str)
- if fixed_ip.leased:
- logging.debug("Deallocating IP %s", fixed_ip_str)
- fixed_ip.allocated = False
- # keep instance id until release occurs
- fixed_ip.save()
+ fixed_ip_ref = db.fixed_ip_get_by_address(context, address)
+ if fixed_ip_ref['leased']:
+ logging.debug("Deallocating IP %s", address)
+ db.fixed_ip_deallocate(context, address)
+ # NOTE(vish): we keep instance id until release occurs
else:
- self.release_ip(fixed_ip_str)
+ self.release_fixed_ip(address, context)
- def lease_ip(self, fixed_ip_str):
+ def lease_fixed_ip(self, address, context=None):
"""Called by bridge when ip is leased"""
- fixed_ip = models.FixedIp.find_by_ip_str(fixed_ip_str)
- if not fixed_ip.allocated:
- raise network_exception.AddressNotAllocated(fixed_ip_str)
- logging.debug("Leasing IP %s", fixed_ip_str)
- fixed_ip.leased = True
- fixed_ip.save()
-
- def release_ip(self, fixed_ip_str):
- """Called by bridge when ip is released"""
- fixed_ip = models.FixedIp.find_by_ip_str(fixed_ip_str)
- logging.debug("Releasing IP %s", fixed_ip_str)
- fixed_ip.leased = False
- fixed_ip.allocated = False
- fixed_ip.instance = None
- fixed_ip.save()
+ logging.debug("Leasing IP %s", address)
+ db.fixed_ip_lease(context, address)
+ def release_fixed_ip(self, address, context=None):
+ """Called by bridge when ip is released"""
+ logging.debug("Releasing IP %s", address)
+ db.fixed_ip_release(context, address)
+ db.fixed_ip_instance_disassociate(context, address)
def restart_nets(self):
"""Ensure the network for each user is enabled"""
# FIXME
pass
- def _on_set_network_host(self, network):
+ def _on_set_network_host(self, context, network_id):
"""Called when this host becomes the host for a project"""
- index = self._get_network_index(network)
+ index = db.network_get_index(context, network_id)
private_net = IPy.IP(FLAGS.private_range)
start = index * FLAGS.network_size
- # minus one for the gateway.
- network_str = "%s-%s" % (private_net[start],
- private_net[start + FLAGS.network_size - 1])
+ significant_bits = 32 - int(math.log(FLAGS.network_size, 2))
+ cidr = "%s/%s" % (private_net[start], significant_bits)
+ db.network_set_cidr(context, network_id, cidr)
vlan = FLAGS.vlan_start + index
- project_net = IPy.IP(network_str)
- network.network_str = network_str
- network.netmask = str(project_net.netmask())
- network.vlan = vlan
- network.bridge = 'br%s' % vlan
- network.gateway = str(project_net[1])
- network.broadcast = str(project_net.broadcast())
- network.vpn_private_ip_str = str(project_net[2])
- network.vpn_public_ip_str = FLAGS.vpn_ip
- network.vpn_public_port = FLAGS.vpn_start + index
- # create network fixed ips
- BOTTOM_RESERVED = 3
- TOP_RESERVED = 1 + FLAGS.cnt_vpn_clients
- num_ips = len(project_net)
- session = models.NovaBase.get_session()
- for i in range(num_ips):
- fixed_ip = models.FixedIp()
- fixed_ip.ip_str = str(project_net[i])
- if i < BOTTOM_RESERVED or num_ips - i < TOP_RESERVED:
- fixed_ip.reserved = True
- fixed_ip.network = network
- session.add(fixed_ip)
- session.commit()
-
-
- def _get_network_index(self, network):
- """Get non-conflicting index for network"""
- session = models.NovaBase.get_session()
- node_name = FLAGS.node_name
- query = session.query(models.NetworkIndex).filter_by(network_id=None)
- network_index = query.with_lockmode("update").first()
- if not network_index:
- raise network_exception.NoMoreNetworks()
- network_index.network = network
- session.add(network_index)
- session.commit()
- return network_index.index
+ net = {}
+ net['kind'] = FLAGS.network_type
+ net['vlan'] = vlan
+ net['bridge'] = 'br%s' % vlan
+ net['vpn_public_ip_str'] = FLAGS.vpn_ip
+ net['vpn_public_port'] = FLAGS.vpn_start + index
+ db.network_update(context, network_id, net)
+ db.network_create_fixed_ips(context, network_id, FLAGS.cnt_vpn_clients)
@classmethod
diff --git a/nova/tests/network_unittest.py b/nova/tests/network_unittest.py
index 76c76edbf..c4c496219 100644
--- a/nova/tests/network_unittest.py
+++ b/nova/tests/network_unittest.py
@@ -21,8 +21,8 @@ Unit Tests for network code
import IPy
import os
import logging
-import tempfile
+from nova import db
from nova import exception
from nova import flags
from nova import models
@@ -30,7 +30,6 @@ from nova import test
from nova import utils
from nova.auth import manager
from nova.network import service
-from nova.network.exception import NoMoreAddresses, NoMoreNetworks
FLAGS = flags.FLAGS
@@ -59,49 +58,52 @@ class NetworkTestCase(test.TrialTestCase):
name))
# create the necessary network data for the project
self.service.set_network_host(self.projects[i].id)
- instance = models.Instance()
- instance.mac_address = utils.generate_mac()
- instance.hostname = 'fake'
- instance.save()
- self.instance_id = instance.id
+ instance_id = db.instance_create(None,
+ {'mac_address': utils.generate_mac()})
+ self.instance_id = instance_id
+ instance_id = db.instance_create(None,
+ {'mac_address': utils.generate_mac()})
+ self.instance2_id = instance_id
def tearDown(self): # pylint: disable=C0103
super(NetworkTestCase, self).tearDown()
# TODO(termie): this should really be instantiating clean datastores
# in between runs, one failure kills all the tests
+ db.instance_destroy(None, self.instance_id)
+ db.instance_destroy(None, self.instance2_id)
for project in self.projects:
self.manager.delete_project(project)
self.manager.delete_user(self.user)
def test_public_network_association(self):
"""Makes sure that we can allocaate a public ip"""
- # FIXME better way of adding elastic ips
+ # TODO(vish): better way of adding floating ips
pubnet = IPy.IP(flags.FLAGS.public_range)
ip_str = str(pubnet[0])
try:
- elastic_ip = models.ElasticIp.find_by_ip_str(ip_str)
+ floating_ip = models.FloatingIp.find_by_ip_str(ip_str)
except exception.NotFound:
- elastic_ip = models.ElasticIp()
- elastic_ip.ip_str = ip_str
- elastic_ip.node_name = FLAGS.node_name
- elastic_ip.save()
- eaddress = self.service.allocate_elastic_ip(self.projects[0].id)
+ floating_ip = models.FloatingIp()
+ floating_ip.ip_str = ip_str
+ floating_ip.node_name = FLAGS.node_name
+ floating_ip.save()
+ eaddress = self.service.allocate_floating_ip(self.projects[0].id)
faddress = self.service.allocate_fixed_ip(self.projects[0].id,
self.instance_id)
self.assertEqual(eaddress, str(pubnet[0]))
- self.service.associate_elastic_ip(eaddress, faddress)
+ self.service.associate_floating_ip(eaddress, faddress)
# FIXME datamodel abstraction
- self.assertEqual(elastic_ip.fixed_ip.ip_str, faddress)
- self.service.disassociate_elastic_ip(eaddress)
- self.assertEqual(elastic_ip.fixed_ip, None)
- self.service.deallocate_elastic_ip(eaddress)
+ self.assertEqual(floating_ip.fixed_ip.ip_str, faddress)
+ self.service.disassociate_floating_ip(eaddress)
+ self.assertEqual(floating_ip.fixed_ip, None)
+ self.service.deallocate_floating_ip(eaddress)
self.service.deallocate_fixed_ip(faddress)
def test_allocate_deallocate_fixed_ip(self):
"""Makes sure that we can allocate and deallocate a fixed ip"""
address = self.service.allocate_fixed_ip(self.projects[0].id,
self.instance_id)
- net = service.get_network_for_project(self.projects[0].id)
+ net = db.project_get_network(None, self.projects[0].id)
self.assertTrue(is_allocated_in_project(address, self.projects[0].id))
issue_ip(address, net.bridge)
self.service.deallocate_fixed_ip(address)
@@ -117,10 +119,10 @@ class NetworkTestCase(test.TrialTestCase):
address = self.service.allocate_fixed_ip(self.projects[0].id,
self.instance_id)
address2 = self.service.allocate_fixed_ip(self.projects[1].id,
- self.instance_id)
+ self.instance2_id)
- net = service.get_network_for_project(self.projects[0].id)
- net2 = service.get_network_for_project(self.projects[1].id)
+ net = db.project_get_network(None, self.projects[0].id)
+ net2 = db.project_get_network(None, self.projects[1].id)
self.assertTrue(is_allocated_in_project(address, self.projects[0].id))
self.assertTrue(is_allocated_in_project(address2, self.projects[1].id))
@@ -151,7 +153,7 @@ class NetworkTestCase(test.TrialTestCase):
address = self.service.allocate_fixed_ip(project_id, self.instance_id)
address2 = self.service.allocate_fixed_ip(project_id, self.instance_id)
address3 = self.service.allocate_fixed_ip(project_id, self.instance_id)
- net = service.get_network_for_project(project_id)
+ net = db.project_get_network(None, project_id)
issue_ip(address, net.bridge)
issue_ip(address2, net.bridge)
issue_ip(address3, net.bridge)
@@ -167,7 +169,7 @@ class NetworkTestCase(test.TrialTestCase):
release_ip(address, net.bridge)
release_ip(address2, net.bridge)
release_ip(address3, net.bridge)
- net = service.get_network_for_project(self.projects[0].id)
+ net = db.project_get_network(None, self.projects[0].id)
self.service.deallocate_fixed_ip(first)
def test_vpn_ip_and_port_looks_valid(self):
@@ -186,7 +188,7 @@ class NetworkTestCase(test.TrialTestCase):
self.service.set_network_host(project.id)
projects.append(project)
project = self.manager.create_project('boom' , self.user)
- self.assertRaises(NoMoreNetworks,
+ self.assertRaises(db.NoMoreNetworks,
self.service.set_network_host,
project.id)
self.manager.delete_project(project)
@@ -198,7 +200,7 @@ class NetworkTestCase(test.TrialTestCase):
"""Makes sure that ip addresses that are deallocated get reused"""
address = self.service.allocate_fixed_ip(self.projects[0].id,
self.instance_id)
- net = service.get_network_for_project(self.projects[0].id)
+ net = db.project_get_network(None, self.projects[0].id)
issue_ip(address, net.bridge)
self.service.deallocate_fixed_ip(address)
release_ip(address, net.bridge)
@@ -219,7 +221,7 @@ class NetworkTestCase(test.TrialTestCase):
There are ips reserved at the bottom and top of the range.
services (network, gateway, CloudPipe, broadcast)
"""
- network = service.get_network_for_project(self.projects[0].id)
+ network = db.project_get_network(None, self.projects[0].id)
net_size = flags.FLAGS.network_size
total_ips = (available_ips(network) +
reserved_ips(network) +
@@ -229,7 +231,7 @@ class NetworkTestCase(test.TrialTestCase):
def test_too_many_addresses(self):
"""Test for a NoMoreAddresses exception when all fixed ips are used.
"""
- network = service.get_network_for_project(self.projects[0].id)
+ network = db.project_get_network(None, self.projects[0].id)
# Number of availaible ips is len of the available list
@@ -242,7 +244,7 @@ class NetworkTestCase(test.TrialTestCase):
issue_ip(addresses[i],network.bridge)
self.assertEqual(available_ips(network), 0)
- self.assertRaises(NoMoreAddresses,
+ self.assertRaises(db.NoMoreAddresses,
self.service.allocate_fixed_ip,
self.projects[0].id,
self.instance_id)
@@ -274,11 +276,11 @@ def reserved_ips(network):
def is_allocated_in_project(address, project_id):
"""Returns true if address is in specified project"""
- fixed_ip = models.FixedIp.find_by_ip_str(address)
- project_net = service.get_network_for_project(project_id)
+ fixed_ip = db.fixed_ip_get_by_address(None, address)
+ project_net = db.project_get_network(None, project_id)
# instance exists until release
- logging.error('fixed_ip.instance: %s', fixed_ip.instance)
- logging.error('project_net: %s', project_net)
+ logging.debug('fixed_ip.instance: %s', fixed_ip.instance)
+ logging.debug('project_net: %s', project_net)
return fixed_ip.instance is not None and fixed_ip.network == project_net