summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Authors2
-rw-r--r--nova/api/ec2/cloud.py45
-rw-r--r--nova/api/ec2/ec2utils.py20
-rw-r--r--nova/api/openstack/compute/contrib/floating_ips.py75
-rw-r--r--nova/compute/api.py38
-rw-r--r--nova/network/api.py67
-rw-r--r--nova/network/manager.py18
-rw-r--r--nova/tests/api/ec2/test_cloud.py9
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_floating_ips.py16
-rw-r--r--nova/tests/compute/test_compute.py43
-rw-r--r--nova/tests/network/test_manager.py4
11 files changed, 198 insertions, 139 deletions
diff --git a/Authors b/Authors
index 489122bc2..ab8223a3e 100644
--- a/Authors
+++ b/Authors
@@ -51,6 +51,7 @@ David Pravec <David.Pravec@danix.org>
David Subiros <david.perez5@hp.com>
Dean Troyer <dtroyer@gmail.com>
Deepak Garg <deepak.garg@citrix.com>
+Deevi Rani <deevi_rani@persistent.co.in>
Derek Higgins <derekh@redhat.com>
Devananda van der Veen <devananda.vdv@gmail.com>
Devdeep Singh <devdeep.singh@citrix.com>
@@ -222,4 +223,3 @@ Zhiteng Huang <zhiteng.huang@intel.com>
Zhixue Wu <Zhixue.Wu@citrix.com>
Zhongyue Luo <lzyeval@gmail.com>
Ziad Sawalha <github@highbridgellc.com>
-Deevi Rani <deevi_rani@persistent.co.in>
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 037a84783..fb6768d4c 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -1111,17 +1111,48 @@ class CloudController(object):
" instance %(instance_id)s") % locals(), context=context)
instance_id = ec2utils.ec2_id_to_id(instance_id)
instance = self.compute_api.get(context, instance_id)
+
+ cached_ipinfo = ec2utils.get_ip_info_for_instance(context, instance)
+ fixed_ips = cached_ipinfo['fixed_ips'] + cached_ipinfo['fixed_ip6s']
+ if not fixed_ips:
+ msg = _('Unable to associate IP Address, no fixed_ips.')
+ raise exception.EC2APIError(msg)
+
+ # TODO(tr3buchet): this will associate the floating IP with the
+ # first fixed_ip an instance has. This should be
+ # changed to support specifying a particular fixed_ip if
+ # multiple exist but this may not apply to ec2..
+ if len(fixed_ips) > 1:
+ msg = _('multiple fixed_ips exist, using the first: %s')
+ LOG.warning(msg, fixed_ips[0])
+
try:
- self.compute_api.associate_floating_ip(context,
- instance,
- address=public_ip)
- return {'return': "true"}
- except exception.FloatingIpNotFound:
- raise exception.EC2APIError(_('Unable to associate IP Address.'))
+ self.network_api.associate_floating_ip(context, instance,
+ floating_address=public_ip,
+ fixed_address=fixed_ips[0])
+ return {'return': 'true'}
+ except exception.FloatingIpAssociated:
+ msg = _('Floating ip is already associated.')
+ raise exception.EC2APIError(msg)
+ except exception.NoFloatingIpInterface:
+ msg = _('l3driver call to add floating ip failed.')
+ raise exception.EC2APIError(msg)
+ except:
+ msg = _('Error, unable to associate floating ip.')
+ raise exception.EC2APIError(msg)
def disassociate_address(self, context, public_ip, **kwargs):
+ instance_id = self.network_api.get_instance_id_by_floating_address(
+ context, public_ip)
+ instance = self.compute_api.get(context, instance_id)
LOG.audit(_("Disassociate address %s"), public_ip, context=context)
- self.network_api.disassociate_floating_ip(context, address=public_ip)
+ try:
+ self.network_api.disassociate_floating_ip(context, instance,
+ address=public_ip)
+ except exception.FloatingIpNotAssociated:
+ msg = _('Floating ip is not associated.')
+ raise exception.EC2APIError(msg)
+
return {'return': "true"}
def run_instances(self, context, **kwargs):
diff --git a/nova/api/ec2/ec2utils.py b/nova/api/ec2/ec2utils.py
index ceb695a5e..c12e65dd3 100644
--- a/nova/api/ec2/ec2utils.py
+++ b/nova/api/ec2/ec2utils.py
@@ -93,19 +93,13 @@ def image_ec2_id(image_id, image_type='ami'):
def get_ip_info_for_instance_from_nw_info(nw_info):
- ip_info = dict(fixed_ips=[], fixed_ip6s=[], floating_ips=[])
- for vif in nw_info:
- vif_fixed_ips = vif.fixed_ips()
-
- fixed_ips = [ip['address']
- for ip in vif_fixed_ips if ip['version'] == 4]
- fixed_ip6s = [ip['address']
- for ip in vif_fixed_ips if ip['version'] == 6]
- floating_ips = [ip['address']
- for ip in vif.floating_ips()]
- ip_info['fixed_ips'].extend(fixed_ips)
- ip_info['fixed_ip6s'].extend(fixed_ip6s)
- ip_info['floating_ips'].extend(floating_ips)
+ ip_info = {}
+ fixed_ips = nw_info.fixed_ips()
+ ip_info['fixed_ips'] = [ip['address'] for ip in fixed_ips
+ if ip['version'] == 4]
+ ip_info['fixed_ip6s'] = [ip['address'] for ip in fixed_ips
+ if ip['version'] == 6]
+ ip_info['floating_ips'] = [ip['address'] for ip in nw_info.floating_ips()]
return ip_info
diff --git a/nova/api/openstack/compute/contrib/floating_ips.py b/nova/api/openstack/compute/contrib/floating_ips.py
index 11da0d770..a3dac71ae 100644
--- a/nova/api/openstack/compute/contrib/floating_ips.py
+++ b/nova/api/openstack/compute/contrib/floating_ips.py
@@ -23,6 +23,7 @@ from nova.api.openstack import extensions
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova import compute
+from nova.compute import utils as compute_utils
from nova import exception
from nova import log as logging
from nova import network
@@ -79,6 +80,23 @@ def _translate_floating_ips_view(floating_ips):
for ip in floating_ips]}
+def get_instance_by_floating_ip_addr(self, context, address):
+ snagiibfa = self.network_api.get_instance_id_by_floating_address
+ instance_id = snagiibfa(context, address)
+ if instance_id:
+ return self.compute_api.get(context, instance_id)
+
+
+def disassociate_floating_ip(self, context, instance, address):
+ try:
+ self.network_api.disassociate_floating_ip(context, instance, address)
+ except exception.NotAuthorized:
+ raise webob.exc.HTTPUnauthorized()
+ except exception.FloatingIpNotAssociated:
+ msg = _('Floating ip is not associated')
+ raise webob.exc.HTTPBadRequest(explanation=msg)
+
+
class FloatingIPController(object):
"""The Floating IPs API controller for the OpenStack API."""
@@ -163,14 +181,20 @@ class FloatingIPController(object):
def delete(self, req, id):
context = req.environ['nova.context']
authorize(context)
+
+ # get the floating ip object
floating_ip = self.network_api.get_floating_ip(context, id)
+ address = floating_ip['address']
+
+ # get the associated instance object (if any)
+ instance = get_instance_by_floating_ip_addr(self, context, address)
+ # disassociate if associated
if floating_ip.get('fixed_ip_id'):
- self.network_api.disassociate_floating_ip(context,
- floating_ip['address'])
+ disassociate_floating_ip(self, context, instance, address)
- self.network_api.release_floating_ip(context,
- address=floating_ip['address'])
+ # release ip from project
+ self.network_api.release_floating_ip(context, address)
return webob.Response(status_int=202)
def _get_ip_by_id(self, context, value):
@@ -201,11 +225,36 @@ class FloatingIPActionController(wsgi.Controller):
instance = self.compute_api.get(context, id)
+ cached_nwinfo = compute_utils.get_nw_info_for_instance(instance)
+ if not cached_nwinfo:
+ msg = _('No nw_info cache associated with instance')
+ raise webob.exc.HTTPBadRequest(explanation=msg)
+
+ fixed_ips = cached_nwinfo.fixed_ips()
+ if not fixed_ips:
+ msg = _('No fixed ips associated to instance')
+ raise webob.exc.HTTPBadRequest(explanation=msg)
+
+ # TODO(tr3buchet): this will associate the floating IP with the
+ # first fixed_ip an instance has. This should be
+ # changed to support specifying a particular fixed_ip if
+ # multiple exist.
+ if len(fixed_ips) > 1:
+ msg = _('multiple fixed_ips exist, using the first: %s')
+ LOG.warning(msg, fixed_ips[0]['address'])
+
try:
- self.compute_api.associate_floating_ip(context, instance,
- address)
- except exception.FixedIpNotFoundForInstance:
- msg = _("No fixed ips associated to instance")
+ self.network_api.associate_floating_ip(context, instance,
+ floating_address=address,
+ fixed_address=fixed_ips[0]['address'])
+ except exception.FloatingIpAssociated:
+ msg = _('floating ip is already associated')
+ raise webob.exc.HTTPBadRequest(explanation=msg)
+ except exception.NoFloatingIpInterface:
+ msg = _('l3driver call to add floating ip failed')
+ raise webob.exc.HTTPBadRequest(explanation=msg)
+ except:
+ msg = _('Error. Unable to associate floating ip')
raise webob.exc.HTTPBadRequest(explanation=msg)
return webob.Response(status_int=202)
@@ -225,13 +274,15 @@ class FloatingIPActionController(wsgi.Controller):
msg = _("Address not specified")
raise webob.exc.HTTPBadRequest(explanation=msg)
+ # get the floating ip object
floating_ip = self.network_api.get_floating_ip_by_address(context,
address)
+ # get the associated instance object (if any)
+ instance = get_instance_by_floating_ip_addr(self, context, address)
+
+ # disassociate if associated
if floating_ip.get('fixed_ip_id'):
- try:
- self.network_api.disassociate_floating_ip(context, address)
- except exception.NotAuthorized:
- raise webob.exc.HTTPUnauthorized()
+ disassociate_floating_ip(self, context, instance, address)
return webob.Response(status_int=202)
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 1e28f0190..fc6a48725 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -1589,44 +1589,6 @@ class API(base.Base):
return instance
@wrap_check_policy
- def associate_floating_ip(self, context, instance, address):
- """Makes calls to network_api to associate_floating_ip.
-
- :param address: is a string floating ip address
- """
- instance_uuid = instance['uuid']
-
- # TODO(tr3buchet): currently network_info doesn't contain floating IPs
- # in its info, if this changes, the next few lines will need to
- # accommodate the info containing floating as well as fixed ip
- # addresses
- nw_info = self.network_api.get_instance_nw_info(context.elevated(),
- instance)
-
- if not nw_info:
- raise exception.FixedIpNotFoundForInstance(
- instance_id=instance_uuid)
-
- ips = [ip for ip in nw_info[0].fixed_ips()]
-
- if not ips:
- raise exception.FixedIpNotFoundForInstance(
- instance_id=instance_uuid)
-
- # TODO(tr3buchet): this will associate the floating IP with the
- # first fixed_ip (lowest id) an instance has. This should be
- # changed to support specifying a particular fixed_ip if
- # multiple exist.
- if len(ips) > 1:
- msg = _('multiple fixedips exist, using the first: %s')
- LOG.warning(msg, ips[0]['address'])
-
- self.network_api.associate_floating_ip(context,
- floating_address=address, fixed_address=ips[0]['address'])
- self.network_api.invalidate_instance_cache(context.elevated(),
- instance)
-
- @wrap_check_policy
def get_instance_metadata(self, context, instance):
"""Get all metadata associated with an instance."""
rv = self.db.instance_metadata_get(context, instance['uuid'])
diff --git a/nova/network/api.py b/nova/network/api.py
index 715d054d5..70ad5d4c2 100644
--- a/nova/network/api.py
+++ b/nova/network/api.py
@@ -17,6 +17,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import functools
+
from nova.db import base
from nova import flags
from nova import log as logging
@@ -28,6 +30,43 @@ FLAGS = flags.FLAGS
LOG = logging.getLogger(__name__)
+def refresh_cache(f):
+ """
+ Decorator to update the instance_info_cache
+ """
+ @functools.wraps(f)
+ def wrapper(self, context, *args, **kwargs):
+ res = f(self, context, *args, **kwargs)
+ try:
+ # get the instance from arguments
+ instance = kwargs.get('instance')
+ if not instance and len(args) > 0:
+ instance = args[0]
+
+ # if no instance, nothing to do
+ if instance is None:
+ return res
+
+ # get nw_info from return if possible, otherwise call for it
+ nw_info = res
+ if not nw_info or \
+ not isinstance(nw_info, network_model.NetworkInfo):
+ nw_info = self.get_instance_nw_info(context, instance)
+
+ # update cache
+ cache = {'network_info': nw_info.json()}
+ self.db.instance_info_cache_update(context, instance['uuid'],
+ cache)
+ except Exception:
+ LOG.exception('Failed storing info cache', instance=instance)
+ LOG.debug(_('args: %s') % args)
+ LOG.debug(_('kwargs: %s') % kwargs)
+
+ # return the original function's return value
+ return res
+ return wrapper
+
+
class API(base.Base):
"""API for interacting with the network manager."""
@@ -95,6 +134,13 @@ class API(base.Base):
{'method': 'get_floating_ips_by_fixed_address',
'args': {'fixed_address': fixed_address}})
+ def get_instance_id_by_floating_address(self, context, address):
+ # NOTE(tr3buchet): i hate this
+ return rpc.call(context,
+ FLAGS.network_topic,
+ {'method': 'get_instance_id_by_floating_address',
+ 'args': {'address': address}})
+
def get_vifs_by_instance(self, context, instance):
# NOTE(vish): When the db calls are converted to store network
# data by instance_uuid, this should pass uuid instead.
@@ -130,8 +176,10 @@ class API(base.Base):
'args': {'address': address,
'affect_auto_assigned': affect_auto_assigned}})
- def associate_floating_ip(self, context, floating_address, fixed_address,
- affect_auto_assigned=False):
+ @refresh_cache
+ def associate_floating_ip(self, context, instance,
+ 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
@@ -143,22 +191,16 @@ class API(base.Base):
'fixed_address': fixed_address,
'affect_auto_assigned': affect_auto_assigned}})
- def disassociate_floating_ip(self, context, address,
+ @refresh_cache
+ def disassociate_floating_ip(self, context, instance, 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)
- fixed_ip = self.db.fixed_ip_get(context, floating_ip['fixed_ip_id'])
- instance = self.db.instance_get(context, fixed_ip['instance_id'])
- rpc.cast(context,
+ rpc.call(context,
FLAGS.network_topic,
{'method': 'disassociate_floating_ip',
'args': {'address': address}})
- self.invalidate_instance_cache(context, instance)
-
- def invalidate_instance_cache(self, context, instance):
- # NOTE(vish): get_instance_nw_info will recreate the cache for us
- self.get_instance_nw_info(context, instance)
+ @refresh_cache
def allocate_for_instance(self, context, instance, **kwargs):
"""Allocates all network structures for an instance.
@@ -222,6 +264,7 @@ class API(base.Base):
nw_info = rpc.call(context, FLAGS.network_topic,
{'method': 'get_instance_nw_info',
'args': args})
+
return network_model.NetworkInfo.hydrate(nw_info)
def validate_networks(self, context, requested_networks):
diff --git a/nova/network/manager.py b/nova/network/manager.py
index 72b41b81f..b46bcd648 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -510,7 +510,7 @@ class FloatingIP(object):
fixed_address, interface)
else:
# send to correct host
- rpc.cast(context,
+ rpc.call(context,
rpc.queue_get_for(context, FLAGS.network_topic, host),
{'method': '_associate_floating_ip',
'args': {'floating_address': floating_address,
@@ -580,7 +580,7 @@ class FloatingIP(object):
self._disassociate_floating_ip(context, address, interface)
else:
# send to correct host
- rpc.cast(context,
+ rpc.call(context,
rpc.queue_get_for(context, FLAGS.network_topic, host),
{'method': '_disassociate_floating_ip',
'args': {'address': address,
@@ -1008,11 +1008,8 @@ class NetworkManager(manager.SchedulerDependentManager):
network = self._get_network_by_id(context, vif['network_id'])
networks[vif['uuid']] = network
- # update instance network cache and return network_info
nw_info = self.build_network_info_model(context, vifs, networks,
rxtx_factor, host)
- self.db.instance_info_cache_update(context, instance_uuid,
- {'network_info': nw_info.json()})
return nw_info
def build_network_info_model(self, context, vifs, networks,
@@ -1613,6 +1610,17 @@ class NetworkManager(manager.SchedulerDependentManager):
vifs = self.db.virtual_interface_get_by_instance(context, instance_id)
return [dict(vif.iteritems()) for vif in vifs]
+ def get_instance_id_by_floating_address(self, context, address):
+ """Returns the instance id a floating ip's fixed ip is allocated to"""
+ floating_ip = self.db.floating_ip_get_by_address(context, address)
+ if floating_ip['fixed_ip_id'] is None:
+ return None
+
+ fixed_ip = self.db.fixed_ip_get(context, floating_ip['fixed_ip_id'])
+
+ # NOTE(tr3buchet): this can be None
+ return fixed_ip['instance_id']
+
@wrap_check_policy
def get_network(self, context, network_uuid):
network = self.db.network_get_by_uuid(context.elevated(), network_uuid)
diff --git a/nova/tests/api/ec2/test_cloud.py b/nova/tests/api/ec2/test_cloud.py
index afb906f45..41818de1b 100644
--- a/nova/tests/api/ec2/test_cloud.py
+++ b/nova/tests/api/ec2/test_cloud.py
@@ -39,6 +39,7 @@ from nova import flags
from nova.image import fake
from nova.image import s3
from nova import log as logging
+from nova.network import api as network_api
from nova import rpc
from nova import test
from nova import utils
@@ -233,8 +234,14 @@ class CloudTestCase(test.TestCase):
project_id=project_id)
fixed_ips = nw_info.fixed_ips()
-
ec2_id = ec2utils.id_to_ec2_id(inst['id'])
+
+ self.stubs.Set(ec2utils, 'get_ip_info_for_instance',
+ lambda *args: {'fixed_ips': ['10.0.0.1'],
+ 'fixed_ip6s': [],
+ 'floating_ips': []})
+ self.stubs.Set(network_api.API, 'get_instance_id_by_floating_address',
+ lambda *args: 1)
self.cloud.associate_address(self.context,
instance_id=ec2_id,
public_ip=address)
diff --git a/nova/tests/api/openstack/compute/contrib/test_floating_ips.py b/nova/tests/api/openstack/compute/contrib/test_floating_ips.py
index 9a3a72d55..b1f87c6df 100644
--- a/nova/tests/api/openstack/compute/contrib/test_floating_ips.py
+++ b/nova/tests/api/openstack/compute/contrib/test_floating_ips.py
@@ -19,6 +19,7 @@ import webob
from nova.api.openstack.compute.contrib import floating_ips
from nova import compute
+from nova.compute import utils as compute_utils
from nova import context
from nova import db
from nova import exception
@@ -56,7 +57,7 @@ def network_api_get_floating_ips_by_project(self, context):
{'id': 2,
'pool': 'nova', 'interface': 'eth0',
'address': '10.10.10.11',
- 'fixed_ip_id': None}]
+ 'fixed_ip_id': None}]
def compute_api_get(self, context, instance_id):
@@ -79,7 +80,7 @@ def network_api_associate(self, context, floating_address, fixed_address):
pass
-def network_api_disassociate(self, context, floating_address):
+def network_api_disassociate(self, context, instance, floating_address):
pass
@@ -92,6 +93,12 @@ def fake_instance_get(context, instance_id):
"project_id": '123'}
+def stub_nw_info(stubs):
+ def get_nw_info_for_instance(instance):
+ return fake_network.fake_get_instance_nw_info(stubs, spectacular=True)
+ return get_nw_info_for_instance
+
+
class FloatingIpTest(test.TestCase):
floating_ip = "10.10.10.10"
@@ -122,9 +129,8 @@ class FloatingIpTest(test.TestCase):
network_api_release)
self.stubs.Set(network.api.API, "disassociate_floating_ip",
network_api_disassociate)
-
- fake_network.fake_get_instance_nw_info(self.stubs, 1, 1,
- spectacular=True)
+ self.stubs.Set(compute_utils, "get_nw_info_for_instance",
+ stub_nw_info(self.stubs))
fake_network.stub_out_nw_api_get_instance_nw_info(self.stubs,
spectacular=True)
diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py
index 92e5d193c..35fa6de23 100644
--- a/nova/tests/compute/test_compute.py
+++ b/nova/tests/compute/test_compute.py
@@ -2875,49 +2875,6 @@ class ComputeAPITestCase(BaseTestCase):
finally:
self.compute.terminate_instance(context, instance['uuid'])
- def test_associate_floating_ip(self):
- """Ensure we can associate a floating ip with an instance"""
- called = {'associate': False}
-
- def fake_associate_ip_network_api(self, ctxt, floating_address,
- fixed_address):
- called['associate'] = True
-
- self.stubs.Set(nova.network.API, 'associate_floating_ip',
- fake_associate_ip_network_api)
-
- instance = self._create_fake_instance()
- instance_uuid = instance['uuid']
- address = '0.1.2.3'
-
- self.compute.run_instance(self.context, instance_uuid)
- self.compute_api.associate_floating_ip(self.context,
- instance,
- address)
- self.assertTrue(called['associate'])
- self.compute.terminate_instance(self.context, instance_uuid)
-
- def test_associate_floating_ip_no_fixed_ip(self):
- """Should fail if instance has no fixed ip."""
-
- def fake_get_nw_info(self, ctxt, instance):
- return []
-
- self.stubs.Set(nova.network.API, 'get_instance_nw_info',
- fake_get_nw_info)
-
- instance = self._create_fake_instance()
- instance_uuid = instance['uuid']
- address = '0.1.2.3'
-
- self.compute.run_instance(self.context, instance_uuid)
- self.assertRaises(exception.FixedIpNotFoundForInstance,
- self.compute_api.associate_floating_ip,
- self.context,
- instance,
- address)
- self.compute.terminate_instance(self.context, instance_uuid)
-
def test_get(self):
"""Test get instance"""
c = context.get_admin_context()
diff --git a/nova/tests/network/test_manager.py b/nova/tests/network/test_manager.py
index 15dc68396..817b7271d 100644
--- a/nova/tests/network/test_manager.py
+++ b/nova/tests/network/test_manager.py
@@ -699,7 +699,7 @@ class VlanNetworkTestCase(test.TestCase):
self.local = True
self.stubs.Set(self.network.db, 'fixed_ip_get_by_address', fake4)
self.stubs.Set(self.network.db, 'network_get', fake4_network)
- self.stubs.Set(rpc, 'cast', fake6)
+ self.stubs.Set(rpc, 'call', fake6)
self.network.associate_floating_ip(ctxt, mox.IgnoreArg(),
mox.IgnoreArg())
self.assertFalse(self.local)
@@ -817,7 +817,7 @@ class VlanNetworkTestCase(test.TestCase):
self.local = True
self.stubs.Set(self.network.db, 'fixed_ip_get', fake4)
self.stubs.Set(self.network.db, 'network_get', fake4_network)
- self.stubs.Set(rpc, 'cast', fake6)
+ self.stubs.Set(rpc, 'call', fake6)
self.network.disassociate_floating_ip(ctxt, mox.IgnoreArg())
self.assertFalse(self.local)