summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrey Morris <trey.morris@rackspace.com>2011-09-16 14:07:41 -0500
committerTrey Morris <trey.morris@rackspace.com>2011-09-30 15:39:02 -0500
commit15b2a3b85b157e4a032d1fbb68bd3d7a509ed765 (patch)
tree6a4fde9971a746d4903e0dbf3d1e9023a36ff93a
parent070e60d21776ea2b32ac557a0661d2025ef111d8 (diff)
downloadnova-15b2a3b85b157e4a032d1fbb68bd3d7a509ed765.tar.gz
nova-15b2a3b85b157e4a032d1fbb68bd3d7a509ed765.tar.xz
nova-15b2a3b85b157e4a032d1fbb68bd3d7a509ed765.zip
moved floating ip db access and sanity checking from network api into network manager
added floating ip get by fixed address added fixed_ip_get moved floating ip testing from osapi into the network tests where they belong Change-Id: I3ee53971206e37405a2adc2491412f7896e1af87
-rw-r--r--nova/api/openstack/contrib/floating_ips.py9
-rw-r--r--nova/compute/api.py4
-rw-r--r--nova/db/api.py5
-rw-r--r--nova/db/sqlalchemy/api.py19
-rw-r--r--nova/exception.py8
-rw-r--r--nova/network/api.py113
-rw-r--r--nova/network/manager.py210
-rw-r--r--nova/test.py1
-rw-r--r--nova/tests/api/ec2/test_cloud.py18
-rw-r--r--nova/tests/api/openstack/contrib/test_floating_ips.py178
-rw-r--r--nova/tests/test_network.py197
11 files changed, 454 insertions, 308 deletions
diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py
index 8b5b19c21..22d7d1a1d 100644
--- a/nova/api/openstack/contrib/floating_ips.py
+++ b/nova/api/openstack/contrib/floating_ips.py
@@ -80,8 +80,8 @@ class FloatingIPController(object):
context = req.environ['nova.context']
try:
- # FIXME(ja) - why does self.network_api.list_floating_ips raise?
- floating_ips = self.network_api.list_floating_ips(context)
+ get_floating_ips = self.network_api.get_floating_ips_by_project
+ floating_ips = get_floating_ips(context)
except exception.FloatingIpNotFoundForProject:
floating_ips = []
@@ -92,7 +92,7 @@ class FloatingIPController(object):
try:
address = self.network_api.allocate_floating_ip(context)
- ip = self.network_api.get_floating_ip_by_ip(context, address)
+ ip = self.network_api.get_floating_ip_by_address(context, address)
except rpc.RemoteError as ex:
# NOTE(tr3buchet) - why does this block exist?
if ex.exc_type == 'NoMoreFloatingIps':
@@ -162,7 +162,8 @@ class Floating_ips(extensions.ExtensionDescriptor):
msg = _("Address not specified")
raise webob.exc.HTTPBadRequest(explanation=msg)
- floating_ip = self.network_api.get_floating_ip_by_ip(context, address)
+ floating_ip = self.network_api.get_floating_ip_by_address(context,
+ address)
if floating_ip.get('fixed_ip'):
try:
self.network_api.disassociate_floating_ip(context, address)
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 7bf28d24e..6837c7ae4 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -1521,8 +1521,8 @@ class API(base.Base):
LOG.warning(_("multiple fixed_ips exist, using the first: %s"),
fixed_ip_addrs[0])
self.network_api.associate_floating_ip(context,
- floating_ip=address,
- fixed_ip=fixed_ip_addrs[0])
+ floating_address=address,
+ fixed_address=fixed_ip_addrs[0])
def get_instance_metadata(self, context, instance_id):
"""Get all metadata associated with an instance."""
diff --git a/nova/db/api.py b/nova/db/api.py
index 7ad02cdd4..e0aa5541c 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -372,6 +372,11 @@ def fixed_ip_disassociate_all_by_timeout(context, host, time):
return IMPL.fixed_ip_disassociate_all_by_timeout(context, host, time)
+def fixed_ip_get(context, id):
+ """Get fixed ip by id or raise if it does not exist."""
+ return IMPL.fixed_ip_get(context, id)
+
+
def fixed_ip_get_all(context):
"""Get all defined fixed ips."""
return IMPL.fixed_ip_get_all(context)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 1dcd6d95c..aac288767 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -796,6 +796,25 @@ def fixed_ip_disassociate_all_by_timeout(_context, host, time):
return result
+@require_context
+def fixed_ip_get(context, id, session=None):
+ if not session:
+ session = get_session()
+ result = session.query(models.FixedIp).\
+ filter_by(id=id).\
+ filter_by(deleted=can_read_deleted(context)).\
+ options(joinedload('floating_ips')).\
+ options(joinedload('network')).\
+ first()
+ if not result:
+ raise exception.FixedIpNotFound(id=id)
+
+ if is_user_context(context):
+ authorize_project_context(context, result.instance.project_id)
+
+ return result
+
+
@require_admin_context
def fixed_ip_get_all(context, session=None):
if not session:
diff --git a/nova/exception.py b/nova/exception.py
index e4d3b16f8..ca9453172 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -539,8 +539,12 @@ class NoMoreFloatingIps(FloatingIpNotFound):
message = _("Zero floating ips available.")
-class FloatingIpAlreadyInUse(NovaException):
- message = _("Floating ip %(address)s already in use by %(fixed_ip)s.")
+class FloatingIpAssociated(NovaException):
+ message = _("Floating ip %(address)s is associated.")
+
+
+class FloatingIpNotAssociated(NovaException):
+ message = _("Floating ip %(address)s is not associated.")
class NoFloatingIpsDefined(NotFound):
diff --git a/nova/network/api.py b/nova/network/api.py
index 70de04569..639dc0dea 100644
--- a/nova/network/api.py
+++ b/nova/network/api.py
@@ -34,17 +34,27 @@ class API(base.Base):
"""API for interacting with the network manager."""
def get_floating_ip(self, context, id):
- rv = self.db.floating_ip_get(context, id)
- return dict(rv.iteritems())
+ return rpc.call(context,
+ FLAGS.network_topic,
+ {'method': 'get_floating_ip',
+ 'args': {'id': id}})
- def get_floating_ip_by_ip(self, context, address):
- res = self.db.floating_ip_get_by_address(context, address)
- return dict(res.iteritems())
+ def get_floating_ip_by_address(self, context, address):
+ return rpc.call(context,
+ FLAGS.network_topic,
+ {'method': 'get_floating_ip_by_address',
+ 'args': {'address': address}})
- def list_floating_ips(self, context):
- ips = self.db.floating_ip_get_all_by_project(context,
- context.project_id)
- return ips
+ def get_floating_ips_by_project(self, context):
+ return rpc.call(context,
+ FLAGS.network_topic,
+ {'method': 'get_floating_ips_by_project'})
+
+ def get_floating_ips_by_fixed_address(self, context, fixed_address):
+ return rpc.call(context,
+ FLAGS.network_topic,
+ {'method': 'get_floating_ips_by_fixed_address',
+ 'args': {'address': address}})
def get_floating_ips_by_fixed_address(self, context, fixed_address):
return rpc.call(context,
@@ -53,12 +63,13 @@ class API(base.Base):
'args': {'fixed_address': fixed_address}})
def get_vifs_by_instance(self, context, instance_id):
- return rpc.call(context, FLAGS.network_topic,
+ return rpc.call(context,
+ FLAGS.network_topic,
{'method': 'get_vifs_by_instance',
'args': {'instance_id': instance_id}})
def allocate_floating_ip(self, context):
- """Adds a floating ip to a project."""
+ """Adds a floating ip to a project. (allocates)"""
# NOTE(vish): We don't know which network host should get the ip
# when we allocate, so just send it to any one. This
# will probably need to move into a network supervisor
@@ -70,89 +81,33 @@ class API(base.Base):
def release_floating_ip(self, context, address,
affect_auto_assigned=False):
- """Removes floating ip with address from a project."""
- floating_ip = self.db.floating_ip_get_by_address(context, address)
- if floating_ip['fixed_ip']:
- raise exception.ApiError(_('Floating ip is in use. '
- 'Disassociate it before releasing.'))
- if not affect_auto_assigned and floating_ip.get('auto_assigned'):
- return
- # NOTE(vish): We don't know which network host should get the ip
- # when we deallocate, so just send it to any one. This
- # will probably need to move into a network supervisor
- # at some point.
+ """Removes floating ip with address from a project. (deallocates)"""
rpc.cast(context,
FLAGS.network_topic,
{'method': 'deallocate_floating_ip',
- 'args': {'floating_address': floating_ip['address']}})
+ 'args': {'floating_address': address,
+ 'affect_auto_assigned': affect_auto_assigned}})
- def associate_floating_ip(self, context, floating_ip, fixed_ip,
- affect_auto_assigned=False):
+ def associate_floating_ip(self, context, floating_address, fixed_address,
+ affect_auto_assigned=False):
"""Associates a floating ip with a fixed ip.
ensures floating ip is allocated to the project in context
-
- :param fixed_ip: is either fixed_ip object or a string fixed ip address
- :param floating_ip: is a string floating ip address
"""
- # NOTE(tr3buchet): i don't like the "either or" argument type
- # funcationility but i've left it alone for now
- # TODO(tr3buchet): this function needs to be rewritten to move
- # the network related db lookups into the network host code
- if isinstance(fixed_ip, basestring):
- fixed_ip = self.db.fixed_ip_get_by_address(context, fixed_ip)
- floating_ip = self.db.floating_ip_get_by_address(context, floating_ip)
- if not affect_auto_assigned and floating_ip.get('auto_assigned'):
- return
- # Check if the floating ip address is allocated
- if floating_ip['project_id'] is None:
- raise exception.ApiError(_('Address (%s) is not allocated') %
- floating_ip['address'])
- # Check if the floating ip address is allocated to the same project
- if floating_ip['project_id'] != context.project_id:
- LOG.warn(_('Address (%(address)s) is not allocated to your '
- 'project (%(project)s)'),
- {'address': floating_ip['address'],
- 'project': context.project_id})
- raise exception.ApiError(_('Address (%(address)s) is not '
- 'allocated to your project'
- '(%(project)s)') %
- {'address': floating_ip['address'],
- 'project': context.project_id})
-
- # If this address has been previously associated to a
- # different instance, disassociate the floating_ip
- if floating_ip['fixed_ip'] and floating_ip['fixed_ip'] is not fixed_ip:
- self.disassociate_floating_ip(context, floating_ip['address'])
-
- # NOTE(vish): if we are multi_host, send to the instances host
- if fixed_ip['network']['multi_host']:
- host = fixed_ip['instance']['host']
- else:
- host = fixed_ip['network']['host']
rpc.cast(context,
- self.db.queue_get_for(context, FLAGS.network_topic, host),
+ FLAGS.network_topic,
{'method': 'associate_floating_ip',
- 'args': {'floating_address': floating_ip['address'],
- 'fixed_address': fixed_ip['address']}})
+ 'args': {'floating_address': floating_address,
+ 'fixed_address': fixed_address,
+ 'affect_auto_assigned': affect_auto_assigned}})
def disassociate_floating_ip(self, context, address,
affect_auto_assigned=False):
"""Disassociates a floating ip from fixed ip it is associated with."""
- floating_ip = self.db.floating_ip_get_by_address(context, address)
- if not affect_auto_assigned and floating_ip.get('auto_assigned'):
- return
- if not floating_ip.get('fixed_ip'):
- raise exception.ApiError('Address is not associated.')
- # NOTE(vish): if we are multi_host, send to the instances host
- if floating_ip['fixed_ip']['network']['multi_host']:
- host = floating_ip['fixed_ip']['instance']['host']
- else:
- host = floating_ip['fixed_ip']['network']['host']
- rpc.call(context,
- self.db.queue_get_for(context, FLAGS.network_topic, host),
+ rpc.cast(context,
+ FLAGS.network_topic,
{'method': 'disassociate_floating_ip',
- 'args': {'floating_address': floating_ip['address']}})
+ 'args': {'address': address}})
def allocate_for_instance(self, context, instance, **kwargs):
"""Allocates all network structures for an instance.
diff --git a/nova/network/manager.py b/nova/network/manager.py
index 0e95fb36c..464f6f378 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -219,26 +219,26 @@ class FloatingIP(object):
# call the next inherited class's allocate_for_instance()
# which is currently the NetworkManager version
# do this first so fixed ip is already allocated
- ips = super(FloatingIP, self).allocate_for_instance(context, **kwargs)
+ nw_info = \
+ super(FloatingIP, self).allocate_for_instance(context, **kwargs)
if FLAGS.auto_assign_floating_ip:
- # allocate a floating ip (public_ip is just the address string)
- public_ip = self.allocate_floating_ip(context, project_id)
+ # allocate a floating ip
+ floating_address = self.allocate_floating_ip(context, project_id)
# set auto_assigned column to true for the floating ip
- self.db.floating_ip_set_auto_assigned(context, public_ip)
- # get the floating ip object from public_ip string
- floating_ip = self.db.floating_ip_get_by_address(context,
- public_ip)
+ self.db.floating_ip_set_auto_assigned(context, floating_address)
- # get the first fixed_ip belonging to the instance
- fixed_ips = self.db.fixed_ip_get_by_instance(context, instance_id)
- fixed_ip = fixed_ips[0] if fixed_ips else None
+ # get the first fixed address belonging to the instance
+ for nw, info in nw_info:
+ if info.get('ips'):
+ fixed_address = info['ips'][0]['ip']
+ break
- # call to correct network host to associate the floating ip
- self.network_api.associate_floating_ip(context,
- floating_ip,
- fixed_ip,
- affect_auto_assigned=True)
- return ips
+ # associate the floating ip to fixed_ip
+ self.associate_floating_ip(context,
+ floating_address,
+ fixed_address,
+ affect_auto_assigned=True)
+ return nw_info
def deallocate_for_instance(self, context, **kwargs):
"""Handles deallocating floating IP resources for an instance.
@@ -258,21 +258,33 @@ class FloatingIP(object):
# disassociate floating ips related to fixed_ip
for floating_ip in fixed_ip.floating_ips:
address = floating_ip['address']
- self.network_api.disassociate_floating_ip(context, address)
+ self.disassociate_floating_ip(context, address, True)
# deallocate if auto_assigned
if floating_ip['auto_assigned']:
- self.network_api.release_floating_ip(context,
- address,
- True)
+ self.release_floating_ip(context, address, True)
# call the next inherited class's deallocate_for_instance()
# which is currently the NetworkManager version
# call this after so floating IPs are handled first
super(FloatingIP, self).deallocate_for_instance(context, **kwargs)
+ def _floating_ip_owned_by_project(self, context, floating_ip):
+ """Raises if floating ip does not belong to project"""
+ if floating_ip['project_id'] != context.project_id:
+ if floating_ip['project_id'] is None:
+ LOG.warn(_('Address |%(address)s| is not allocated'),
+ {'address': floating_ip['address']})
+ raise exception.NotAuthorized()
+ else:
+ LOG.warn(_('Address |%(address)s| is not allocated to your '
+ 'project |%(project)s|'),
+ {'address': floating_ip['address'],
+ 'project': context.project_id})
+ raise exception.NotAuthorized()
+
def allocate_floating_ip(self, context, project_id):
"""Gets an floating ip from the pool."""
- # NOTE(tr3buchet): all networks hosts in zone now use the same pool
+ # NOTE(tr3buchet): all network hosts in zone now use the same pool
LOG.debug("QUOTA: %s" % quota.allowed_floating_ips(context, 1))
if quota.allowed_floating_ips(context, 1) < 1:
LOG.warn(_('Quota exceeded for %s, tried to allocate '
@@ -284,32 +296,146 @@ class FloatingIP(object):
return self.db.floating_ip_allocate_address(context,
project_id)
- def associate_floating_ip(self, context, floating_address, fixed_address):
- """Associates an floating ip to a fixed ip."""
+ def deallocate_floating_ip(self, context, address,
+ affect_auto_assigned=False):
+ """Returns an floating ip to the pool."""
+ floating_ip = self.db.floating_ip_get_by_address(context, address)
+
+ # handle auto_assigned
+ if not affect_auto_assigned and floating_ip.get('auto_assigned'):
+ return
+
+ # make sure project ownz this floating ip (allocated)
+ self._floating_ip_owned_by_project(context, floating_ip)
+
+ # make sure floating ip is not associated
+ if floating_ip['fixed_ip_id']:
+ floating_address = floating_ip['address']
+ raise exception.FloatingIpAssociated(address=floating_address)
+
+ self.db.floating_ip_deallocate(context, address)
+
+ def associate_floating_ip(self, context, floating_address, fixed_address,
+ affect_auto_assigned=False):
+ """Associates a floating ip with a fixed ip.
+
+ Makes sure everything makes sense then calls _associate_floating_ip,
+ rpc'ing to correct host if i'm not it.
+ """
floating_ip = self.db.floating_ip_get_by_address(context,
floating_address)
- if floating_ip['fixed_ip']:
- raise exception.FloatingIpAlreadyInUse(
- address=floating_ip['address'],
- fixed_ip=floating_ip['fixed_ip']['address'])
+ # handle auto_assigned
+ if not affect_auto_assigned and floating_ip.get('auto_assigned'):
+ return
+
+ # make sure project ownz this floating ip (allocated)
+ self._floating_ip_owned_by_project(context, floating_ip)
+
+ # make sure floating ip isn't already associated
+ if floating_ip['fixed_ip_id']:
+ floating_address = floating_ip['address']
+ raise exception.FloatingIpAssociated(address=floating_address)
+ fixed_ip = self.db.fixed_ip_get_by_address(context, fixed_address)
+
+ # send to correct host, unless i'm the correct host
+ if fixed_ip['network']['multi_host']:
+ instance = self.db.instance_get(context, fixed_ip['instance_id'])
+ host = instance['host']
+ else:
+ host = fixed_ip['network']['host']
+ LOG.info("%s", self.host)
+ if host == self.host:
+ # i'm the correct host
+ self._associate_floating_ip(context, floating_address,
+ fixed_address)
+ else:
+ # send to correct host
+ rpc.cast(context,
+ self.db.queue_get_for(context, FLAGS.network_topic, host),
+ {'method': '_associate_floating_ip',
+ 'args': {'floating_address': floating_ip['address'],
+ 'fixed_address': fixed_ip['address']}})
+
+ def _associate_floating_ip(self, context, floating_address, fixed_address):
+ """Performs db and driver calls to associate floating ip & fixed ip"""
+ # associate floating ip
self.db.floating_ip_fixed_ip_associate(context,
floating_address,
fixed_address,
self.host)
+ # gogo driver time
self.driver.bind_floating_ip(floating_address)
self.driver.ensure_floating_forward(floating_address, fixed_address)
- def disassociate_floating_ip(self, context, floating_address):
- """Disassociates a floating ip."""
- fixed_address = self.db.floating_ip_disassociate(context,
- floating_address)
- self.driver.unbind_floating_ip(floating_address)
- self.driver.remove_floating_forward(floating_address, fixed_address)
+ def disassociate_floating_ip(self, context, address,
+ affect_auto_assigned=False):
+ """Disassociates a floating ip from its fixed ip.
- def deallocate_floating_ip(self, context, floating_address):
- """Returns an floating ip to the pool."""
- self.db.floating_ip_deallocate(context, floating_address)
+ Makes sure everything makes sense then calls _disassociate_floating_ip,
+ rpc'ing to correct host if i'm not it.
+ """
+ floating_ip = self.db.floating_ip_get_by_address(context, address)
+
+ # handle auto assigned
+ if not affect_auto_assigned and floating_ip.get('auto_assigned'):
+ return
+
+ # make sure project ownz this floating ip (allocated)
+ self._floating_ip_owned_by_project(context, floating_ip)
+
+ # make sure floating ip is associated
+ if not floating_ip.get('fixed_ip_id'):
+ floating_address = floating_ip['address']
+ raise exception.FloatingIpNotAssociated(address=floating_address)
+
+ fixed_ip = self.db.fixed_ip_get(context, floating_ip['fixed_ip_id'])
+
+ # send to correct host, unless i'm the correct host
+ if fixed_ip['network']['multi_host']:
+ instance = self.db.instance_get(context, fixed_ip['instance_id'])
+ host = instance['host']
+ else:
+ host = fixed_ip['network']['host']
+ if host == self.host:
+ # i'm the correct host
+ self._disassociate_floating_ip(context, address)
+ else:
+ # send to correct host
+ rpc.cast(context,
+ self.db.queue_get_for(context, FLAGS.network_topic, host),
+ {'method': '_disassociate_floating_ip',
+ 'args': {'address': address}})
+
+ def _disassociate_floating_ip(self, context, address):
+ """Performs db and driver calls to disassociate floating ip"""
+ # disassociate floating ip
+ fixed_address = self.db.floating_ip_disassociate(context, address)
+
+ # go go driver time
+ self.driver.unbind_floating_ip(address)
+ self.driver.remove_floating_forward(address, fixed_address)
+
+ def get_floating_ip(self, context, id):
+ """Returns a floating IP as a dict"""
+ return dict(self.db.floating_ip_get(context, id).iteritems())
+
+ def get_floating_ip_by_address(self, context, address):
+ """Returns a floating IP as a dict"""
+ return dict(self.db.floating_ip_get_by_address(context,
+ address).iteritems())
+
+ def get_floating_ips_by_project(self, context):
+ """Returns the floating IPs allocated to a project"""
+ ips = self.db.floating_ip_get_all_by_project(context,
+ context.project_id)
+ return [dict(ip.iteritems()) for ip in ips]
+
+ def get_floating_ips_by_fixed_address(self, context, fixed_address):
+ """Returns the floating IPs associated with a fixed_address"""
+ floating_ips = self.db.floating_ip_get_by_fixed_address(context,
+ fixed_address)
+ return [floating_ip['address'] for floating_ip in floating_ips]
def get_floating_ips_by_fixed_address(self, context, fixed_address):
"""Returns the floating IPs associated with a fixed_address"""
@@ -414,11 +540,6 @@ class NetworkManager(manager.SchedulerDependentManager):
# floating ips MUST override this or use the Mixin
return []
- def get_vifs_by_instance(self, context, instance_id):
- vifs = self.db.virtual_interface_get_by_instance(context,
- instance_id)
- return vifs
-
def get_instance_uuids_by_ip_filter(self, context, filters):
fixed_ip_filter = filters.get('fixed_ip')
ip_filter = re.compile(str(filters.get('ip')))
@@ -946,6 +1067,11 @@ class NetworkManager(manager.SchedulerDependentManager):
def _get_networks_by_uuids(self, context, network_uuids):
return self.db.network_get_all_by_uuids(context, network_uuids)
+ def get_vifs_by_instance(self, context, instance_id):
+ """Returns the vifs associated with an instance"""
+ vifs = self.db.virtual_interface_get_by_instance(context, instance_id)
+ return [dict(vif.iteritems()) for vif in vifs]
+
class FlatManager(NetworkManager):
"""Basic network where no vlans are used.
@@ -1004,7 +1130,7 @@ class FlatManager(NetworkManager):
self.db.network_update(context, network_ref['id'], net)
-class FlatDHCPManager(FloatingIP, RPCAllocateFixedIP, NetworkManager):
+class FlatDHCPManager(RPCAllocateFixedIP, FloatingIP, NetworkManager):
"""Flat networking with dhcp.
FlatDHCPManager will start up one dhcp server to give out addresses.
diff --git a/nova/test.py b/nova/test.py
index d759aef60..b753f2314 100644
--- a/nova/test.py
+++ b/nova/test.py
@@ -56,6 +56,7 @@ LOG = log.getLogger('nova.tests')
class skip_test(object):
"""Decorator that skips a test."""
+ # TODO(tr3buchet): remember forever what comstud did here
def __init__(self, msg):
self.message = msg
diff --git a/nova/tests/api/ec2/test_cloud.py b/nova/tests/api/ec2/test_cloud.py
index 7053ebab7..0f22df751 100644
--- a/nova/tests/api/ec2/test_cloud.py
+++ b/nova/tests/api/ec2/test_cloud.py
@@ -130,24 +130,6 @@ class CloudTestCase(test.TestCase):
result = self.cloud.release_address(self.context, address)
self.assertEqual(result['releaseResponse'], ['Address released.'])
- def test_release_address_still_associated(self):
- address = "10.10.10.10"
- fixed_ip = {'instance': {'id': 1}}
- floating_ip = {'id': 0,
- 'address': address,
- 'fixed_ip_id': 0,
- 'fixed_ip': fixed_ip,
- 'project_id': None,
- 'auto_assigned': False}
- network_api = network.api.API()
- self.mox.StubOutWithMock(network_api.db, 'floating_ip_get_by_address')
- network_api.db.floating_ip_get_by_address(mox.IgnoreArg(),
- mox.IgnoreArg()).AndReturn(floating_ip)
- self.mox.ReplayAll()
- release = self.cloud.release_address
- # ApiError: Floating ip is in use. Disassociate it before releasing.
- self.assertRaises(exception.ApiError, release, self.context, address)
-
def test_associate_disassociate_address(self):
"""Verifies associate runs cleanly without raising an exception"""
address = "10.10.10.10"
diff --git a/nova/tests/api/openstack/contrib/test_floating_ips.py b/nova/tests/api/openstack/contrib/test_floating_ips.py
index d4e08b303..7f1583964 100644
--- a/nova/tests/api/openstack/contrib/test_floating_ips.py
+++ b/nova/tests/api/openstack/contrib/test_floating_ips.py
@@ -36,12 +36,12 @@ def network_api_get_floating_ip(self, context, id):
'fixed_ip': None}
-def network_api_get_floating_ip_by_ip(self, context, address):
+def network_api_get_floating_ip_by_address(self, context, address):
return {'id': 1, 'address': '10.10.10.10',
- 'fixed_ip': {'address': '10.0.0.1', 'instance_id': 1}},
+ 'fixed_ip': {'address': '10.0.0.1', 'instance_id': 1}}
-def network_api_list_floating_ips(self, context):
+def network_api_get_floating_ips_by_project(self, context):
return [{'id': 1,
'address': '10.10.10.10',
'fixed_ip': {'address': '10.0.0.1', 'instance_id': 1}},
@@ -57,11 +57,11 @@ def network_api_release(self, context, address):
pass
-def compute_api_associate(self, context, instance_id, floating_ip):
+def compute_api_associate(self, context, instance_id, address):
pass
-def network_api_associate(self, context, floating_ip, fixed_ip):
+def network_api_associate(self, context, floating_address, fixed_address):
pass
@@ -110,10 +110,10 @@ class FloatingIpTest(test.TestCase):
super(FloatingIpTest, self).setUp()
self.stubs.Set(network.api.API, "get_floating_ip",
network_api_get_floating_ip)
- self.stubs.Set(network.api.API, "get_floating_ip_by_ip",
- network_api_get_floating_ip)
- self.stubs.Set(network.api.API, "list_floating_ips",
- network_api_list_floating_ips)
+ self.stubs.Set(network.api.API, "get_floating_ip_by_address",
+ network_api_get_floating_ip_by_address)
+ self.stubs.Set(network.api.API, "get_floating_ips_by_project",
+ network_api_get_floating_ips_by_project)
self.stubs.Set(network.api.API, "release_floating_ip",
network_api_release)
self.stubs.Set(network.api.API, "disassociate_floating_ip",
@@ -184,6 +184,7 @@ class FloatingIpTest(test.TestCase):
self.assertEqual(res_dict['floating_ip']['ip'], '10.10.10.10')
self.assertEqual(res_dict['floating_ip']['instance_id'], 1)
+# test floating ip allocate/release(deallocate)
def test_floating_ip_allocate_no_free_ips(self):
def fake_call(*args, **kwargs):
raise(rpc.RemoteError('NoMoreFloatingIps', '', ''))
@@ -196,8 +197,16 @@ class FloatingIpTest(test.TestCase):
self.assertEqual(res.status_int, 400)
def test_floating_ip_allocate(self):
+ def fake1(*args, **kwargs):
+ pass
+
+ def fake2(*args, **kwargs):
+ return {'id': 1, 'address': '10.10.10.10'}
+
self.stubs.Set(network.api.API, "allocate_floating_ip",
- network_api_allocate)
+ fake1)
+ self.stubs.Set(network.api.API, "get_floating_ip_by_address",
+ fake2)
req = webob.Request.blank('/v1.1/123/os-floating-ips')
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
@@ -212,107 +221,15 @@ class FloatingIpTest(test.TestCase):
"fixed_ip": None}
self.assertEqual(ip, expected)
- def test_floating_ip_release_associated(self):
- self.disassociated = False
-
- def get_floating_ip(ignore, context, id):
- return {'id': 1, 'address': '10.10.10.10',
- 'fixed_ip': {'id': 1}}
-
- def disassociate(ignore, context, floating_address):
- self.disassociated = True
-
- self.stubs.Set(network.api.API, "get_floating_ip",
- get_floating_ip)
- self.stubs.Set(network.api.API, "disassociate_floating_ip",
- disassociate)
- req = webob.Request.blank('/v1.1/123/os-floating-ips/1')
- req.method = 'DELETE'
- res = req.get_response(fakes.wsgi_app())
- self.assertEqual(res.status_int, 202)
- self.assertTrue(self.disassociated)
-
- def test_floating_ip_release_disassociated(self):
- self.disassociated = False
-
- def fake_get_floating_ip(ignore, context, id):
- return {'id': 1, 'address': '10.10.10.10',
- 'fixed_ip': None}
-
- def fake_disassociate(ignore, context, floating_address):
- self.disassociated = True
-
- self.stubs.Set(network.api.API, "get_floating_ip",
- fake_get_floating_ip)
- self.stubs.Set(network.api.API, "disassociate_floating_ip",
- fake_disassociate)
+ def test_floating_ip_release(self):
req = webob.Request.blank('/v1.1/123/os-floating-ips/1')
req.method = 'DELETE'
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 202)
- self.assertFalse(self.disassociated)
-
- def test_add_floating_ip_to_instance(self):
- self.stubs.Set(network.api.API, "associate_floating_ip",
- network_api_associate)
- body = dict(addFloatingIp=dict(address='11.0.0.1'))
- req = webob.Request.blank('/v1.1/123/servers/test_inst/action')
- req.method = "POST"
- req.body = json.dumps(body)
- req.headers["content-type"] = "application/json"
-
- resp = req.get_response(fakes.wsgi_app())
- self.assertEqual(resp.status_int, 202)
-
- def test_associate_floating_ip_to_instance_wrong_project_id(self):
- def fake_fixed_ip_get_by_address(ctx, address, session=None):
- return {'address': address, 'network': {'multi_host': None,
- 'host': 'fake'}}
- self.stubs.Set(db.api, "fixed_ip_get_by_address",
- fake_fixed_ip_get_by_address)
- db.floating_ip_update(self.context, self.address, {'project_id': 'bad',
- 'fixed_ip_id': 1})
- body = dict(addFloatingIp=dict(address=self.address))
- req = webob.Request.blank('/v1.1/123/servers/test_inst/action')
- req.method = "POST"
- req.body = json.dumps(body)
- req.headers["content-type"] = "application/json"
- resp = req.get_response(fakes.wsgi_app())
- self.assertEqual(resp.status_int, 401)
-
- def test_associate_floating_ip_to_instance_no_project_id(self):
- def fake_fixed_ip_get_by_address(ctx, address, session=None):
- return {'address': address, 'network': {'multi_host': None,
- 'host': 'fake'}}
- self.stubs.Set(db.api, "fixed_ip_get_by_address",
- fake_fixed_ip_get_by_address)
- db.floating_ip_update(self.context, self.address, {'project_id': None,
- 'fixed_ip_id': 1})
- body = dict(addFloatingIp=dict(address=self.address))
- req = webob.Request.blank('/v1.1/123/servers/test_inst/action')
- req.method = "POST"
- req.body = json.dumps(body)
- req.headers["content-type"] = "application/json"
- resp = req.get_response(fakes.wsgi_app())
- self.assertEqual(resp.status_int, 400)
-
- def test_add_associated_floating_ip_to_instance(self):
- def fake_fixed_ip_get_by_address(ctx, address, session=None):
- return {'address': address, 'network': {'multi_host': None,
- 'host': 'fake'}}
-
- self.disassociated = False
-
- def fake_network_api_disassociate(local_self, ctx, floating_address):
- self.disassociated = True
- db.floating_ip_update(self.context, self.address, {'project_id': '123',
- 'fixed_ip_id': 1})
- self.stubs.Set(network.api.API, "disassociate_floating_ip",
- fake_network_api_disassociate)
- self.stubs.Set(db.api, "fixed_ip_get_by_address",
- fake_fixed_ip_get_by_address)
+# test floating ip add/remove -> associate/disassociate
+ def test_floating_ip_associate(self):
body = dict(addFloatingIp=dict(address=self.address))
req = webob.Request.blank('/v1.1/123/servers/test_inst/action')
req.method = "POST"
@@ -321,22 +238,8 @@ class FloatingIpTest(test.TestCase):
resp = req.get_response(fakes.wsgi_app())
self.assertEqual(resp.status_int, 202)
- self.assertTrue(self.disassociated)
-
- def test_remove_associated_floating_ip_from_instance(self):
- self.disassociated = False
-
- def fake_get_floating_ip_by_ip(ignore, context, ip):
- return {'id': 1, 'address': '10.10.10.10',
- 'fixed_ip': {'id': 1}}
-
- def fake_disassociate(ignore, context, floating_address):
- self.disassociated = True
- self.stubs.Set(network.api.API, "get_floating_ip_by_ip",
- fake_get_floating_ip_by_ip)
- self.stubs.Set(network.api.API, "disassociate_floating_ip",
- fake_disassociate)
+ def test_floating_ip_disassociate(self):
body = dict(removeFloatingIp=dict(address='10.10.10.10'))
req = webob.Request.blank('/v1.1/123/servers/test_inst/action')
req.method = "POST"
@@ -345,31 +248,8 @@ class FloatingIpTest(test.TestCase):
resp = req.get_response(fakes.wsgi_app())
self.assertEqual(resp.status_int, 202)
- self.assertTrue(self.disassociated)
-
- def test_remove_disassociated_floating_ip_from_instance(self):
- self.disassociated = False
-
- def fake_get_floating_ip_by_ip(ignore, context, ip):
- return {'id': 1, 'address': '10.10.10.10',
- 'fixed_ip': None}
- def fake_disassociate(ignore, context, floating_address):
- self.disassociated = True
-
- self.stubs.Set(network.api.API, "get_floating_ip_by_ip",
- fake_get_floating_ip_by_ip)
- self.stubs.Set(network.api.API, "disassociate_floating_ip",
- fake_disassociate)
- body = dict(removeFloatingIp=dict(address='10.10.10.10'))
- req = webob.Request.blank('/v1.1/123/servers/test_inst/action')
- req.method = "POST"
- req.body = json.dumps(body)
- req.headers["content-type"] = "application/json"
-
- resp = req.get_response(fakes.wsgi_app())
- self.assertEqual(resp.status_int, 202)
- self.assertFalse(self.disassociated)
+# these are a few bad param tests
def test_bad_address_param_in_remove_floating_ip(self):
body = dict(removeFloatingIp=dict(badparam='11.0.0.1'))
@@ -391,16 +271,6 @@ class FloatingIpTest(test.TestCase):
resp = req.get_response(fakes.wsgi_app())
self.assertEqual(resp.status_int, 400)
- def test_bad_address_param_in_add_floating_ip(self):
- body = dict(addFloatingIp=dict(badparam='11.0.0.1'))
- req = webob.Request.blank('/v1.1/123/servers/test_inst/action')
- req.method = "POST"
- req.body = json.dumps(body)
- req.headers["content-type"] = "application/json"
-
- resp = req.get_response(fakes.wsgi_app())
- self.assertEqual(resp.status_int, 400)
-
def test_missing_dict_param_in_add_floating_ip(self):
body = dict(addFloatingIp='11.0.0.1')
req = webob.Request.blank('/v1.1/123/servers/test_inst/action')
diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py
index 6344bc4c6..e3d27e091 100644
--- a/nova/tests/test_network.py
+++ b/nova/tests/test_network.py
@@ -20,6 +20,8 @@ from nova import context
from nova import db
from nova import exception
from nova import log as logging
+from nova import quota
+from nova import rpc
from nova import test
from nova.network import manager as network_manager
from nova.tests import fake_network
@@ -394,22 +396,203 @@ class VlanNetworkTestCase(test.TestCase):
self.mox.ReplayAll()
self.network.validate_networks(self.context, requested_networks)
- def test_cant_associate_associated_floating_ip(self):
+ def test_floating_ip_owned_by_project(self):
ctxt = context.RequestContext('testuser', 'testproject',
is_admin=False)
- def fake_floating_ip_get_by_address(context, address):
- return {'address': '10.10.10.10',
- 'fixed_ip': {'address': '10.0.0.1'}}
- self.stubs.Set(self.network.db, 'floating_ip_get_by_address',
- fake_floating_ip_get_by_address)
+ # raises because floating_ip project_id is None
+ floating_ip = {'address': '10.0.0.1',
+ 'project_id': None}
+ self.assertRaises(exception.NotAuthorized,
+ self.network._floating_ip_owned_by_project,
+ ctxt,
+ floating_ip)
+
+ # raises because floating_ip project_id is not equal to ctxt project_id
+ floating_ip = {'address': '10.0.0.1',
+ 'project_id': ctxt.project_id + '1'}
+ self.assertRaises(exception.NotAuthorized,
+ self.network._floating_ip_owned_by_project,
+ ctxt,
+ floating_ip)
+
+ # does not raise (floating ip is owned by ctxt project)
+ floating_ip = {'address': '10.0.0.1',
+ 'project_id': ctxt.project_id}
+ self.network._floating_ip_owned_by_project(ctxt, floating_ip)
+
+ def test_allocate_floating_ip(self):
+ ctxt = context.RequestContext('testuser', 'testproject',
+ is_admin=False)
+
+ def fake1(*args, **kwargs):
+ return {'address': '10.0.0.1'}
+
+ def fake2(*args, **kwargs):
+ return 25
+
+ def fake3(*args, **kwargs):
+ return 0
+
+ self.stubs.Set(self.network.db, 'floating_ip_allocate_address', fake1)
+
+ # this time should raise
+ self.stubs.Set(self.network.db, 'floating_ip_count_by_project', fake2)
+ self.assertRaises(quota.QuotaError,
+ self.network.allocate_floating_ip,
+ ctxt,
+ ctxt.project_id)
+
+ # this time should not
+ self.stubs.Set(self.network.db, 'floating_ip_count_by_project', fake3)
+ self.network.allocate_floating_ip(ctxt, ctxt.project_id)
+
+ def test_deallocate_floating_ip(self):
+ ctxt = context.RequestContext('testuser', 'testproject',
+ is_admin=False)
+
+ def fake1(*args, **kwargs):
+ pass
+
+ def fake2(*args, **kwargs):
+ return {'address': '10.0.0.1', 'fixed_ip_id': 1}
+
+ def fake3(*args, **kwargs):
+ return {'address': '10.0.0.1', 'fixed_ip_id': None}
+
+ self.stubs.Set(self.network.db, 'floating_ip_deallocate', fake1)
+ self.stubs.Set(self.network, '_floating_ip_owned_by_project', fake1)
+
+ # this time should raise because floating ip is associated to fixed_ip
+ self.stubs.Set(self.network.db, 'floating_ip_get_by_address', fake2)
+ self.assertRaises(exception.FloatingIpAssociated,
+ self.network.deallocate_floating_ip,
+ ctxt,
+ mox.IgnoreArg())
+
+ # this time should not raise
+ self.stubs.Set(self.network.db, 'floating_ip_get_by_address', fake3)
+ self.network.deallocate_floating_ip(ctxt, ctxt.project_id)
- self.assertRaises(exception.FloatingIpAlreadyInUse,
+ def test_associate_floating_ip(self):
+ ctxt = context.RequestContext('testuser', 'testproject',
+ is_admin=False)
+
+ def fake1(*args, **kwargs):
+ pass
+
+ # floating ip that's already associated
+ def fake2(*args, **kwargs):
+ return {'address': '10.0.0.1',
+ 'fixed_ip_id': 1}
+
+ # floating ip that isn't associated
+ def fake3(*args, **kwargs):
+ return {'address': '10.0.0.1',
+ 'fixed_ip_id': None}
+
+ # fixed ip with remote host
+ def fake4(*args, **kwargs):
+ return {'address': '10.0.0.1',
+ 'network': {'multi_host': False, 'host': 'jibberjabber'}}
+
+ # fixed ip with local host
+ def fake5(*args, **kwargs):
+ return {'address': '10.0.0.1',
+ 'network': {'multi_host': False, 'host': 'testhost'}}
+
+ def fake6(*args, **kwargs):
+ self.local = False
+
+ def fake7(*args, **kwargs):
+ self.local = True
+
+ self.stubs.Set(self.network, '_floating_ip_owned_by_project', fake1)
+
+ # raises because floating_ip is already associated to a fixed_ip
+ self.stubs.Set(self.network.db, 'floating_ip_get_by_address', fake2)
+ self.assertRaises(exception.FloatingIpAssociated,
self.network.associate_floating_ip,
ctxt,
mox.IgnoreArg(),
mox.IgnoreArg())
+ self.stubs.Set(self.network.db, 'floating_ip_get_by_address', fake3)
+
+ # does not raise and makes call remotely
+ self.local = True
+ self.stubs.Set(self.network.db, 'fixed_ip_get_by_address', fake4)
+ self.stubs.Set(rpc, 'cast', fake6)
+ self.network.associate_floating_ip(ctxt, mox.IgnoreArg(),
+ mox.IgnoreArg())
+ self.assertFalse(self.local)
+
+ # does not raise and makes call locally
+ self.local = False
+ self.stubs.Set(self.network.db, 'fixed_ip_get_by_address', fake5)
+ self.stubs.Set(self.network, '_associate_floating_ip', fake7)
+ self.network.associate_floating_ip(ctxt, mox.IgnoreArg(),
+ mox.IgnoreArg())
+ self.assertTrue(self.local)
+
+ def test_disassociate_floating_ip(self):
+ ctxt = context.RequestContext('testuser', 'testproject',
+ is_admin=False)
+
+ def fake1(*args, **kwargs):
+ pass
+
+ # floating ip that isn't associated
+ def fake2(*args, **kwargs):
+ return {'address': '10.0.0.1',
+ 'fixed_ip_id': None}
+
+ # floating ip that is associated
+ def fake3(*args, **kwargs):
+ return {'address': '10.0.0.1',
+ 'fixed_ip_id': 1}
+
+ # fixed ip with remote host
+ def fake4(*args, **kwargs):
+ return {'address': '10.0.0.1',
+ 'network': {'multi_host': False, 'host': 'jibberjabber'}}
+
+ # fixed ip with local host
+ def fake5(*args, **kwargs):
+ return {'address': '10.0.0.1',
+ 'network': {'multi_host': False, 'host': 'testhost'}}
+
+ def fake6(*args, **kwargs):
+ self.local = False
+
+ def fake7(*args, **kwargs):
+ self.local = True
+
+ self.stubs.Set(self.network, '_floating_ip_owned_by_project', fake1)
+
+ # raises because floating_ip is not associated to a fixed_ip
+ self.stubs.Set(self.network.db, 'floating_ip_get_by_address', fake2)
+ self.assertRaises(exception.FloatingIpNotAssociated,
+ self.network.disassociate_floating_ip,
+ ctxt,
+ mox.IgnoreArg())
+
+ self.stubs.Set(self.network.db, 'floating_ip_get_by_address', fake3)
+
+ # does not raise and makes call remotely
+ self.local = True
+ self.stubs.Set(self.network.db, 'fixed_ip_get', fake4)
+ self.stubs.Set(rpc, 'cast', fake6)
+ self.network.disassociate_floating_ip(ctxt, mox.IgnoreArg())
+ self.assertFalse(self.local)
+
+ # does not raise and makes call locally
+ self.local = False
+ self.stubs.Set(self.network.db, 'fixed_ip_get', fake5)
+ self.stubs.Set(self.network, '_disassociate_floating_ip', fake7)
+ self.network.disassociate_floating_ip(ctxt, mox.IgnoreArg())
+ self.assertTrue(self.local)
+
def test_add_fixed_ip_instance_without_vpn_requested_networks(self):
self.mox.StubOutWithMock(db, 'network_get')
self.mox.StubOutWithMock(db, 'fixed_ip_associate_pool')