diff options
| author | Trey Morris <trey.morris@rackspace.com> | 2011-09-16 14:07:41 -0500 |
|---|---|---|
| committer | Trey Morris <trey.morris@rackspace.com> | 2011-09-30 15:39:02 -0500 |
| commit | 15b2a3b85b157e4a032d1fbb68bd3d7a509ed765 (patch) | |
| tree | 6a4fde9971a746d4903e0dbf3d1e9023a36ff93a | |
| parent | 070e60d21776ea2b32ac557a0661d2025ef111d8 (diff) | |
| download | nova-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.py | 9 | ||||
| -rw-r--r-- | nova/compute/api.py | 4 | ||||
| -rw-r--r-- | nova/db/api.py | 5 | ||||
| -rw-r--r-- | nova/db/sqlalchemy/api.py | 19 | ||||
| -rw-r--r-- | nova/exception.py | 8 | ||||
| -rw-r--r-- | nova/network/api.py | 113 | ||||
| -rw-r--r-- | nova/network/manager.py | 210 | ||||
| -rw-r--r-- | nova/test.py | 1 | ||||
| -rw-r--r-- | nova/tests/api/ec2/test_cloud.py | 18 | ||||
| -rw-r--r-- | nova/tests/api/openstack/contrib/test_floating_ips.py | 178 | ||||
| -rw-r--r-- | nova/tests/test_network.py | 197 |
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') |
