Adds support for floating ip pools

* Implements blueprint multiple-floating-ip-ranges
 * Adds pool and interface fields to floating ip tables
 * Adds extension to get a list of available pools
 * Optionally allows a pool to be specified when allocating
 * Changes nova-manage command to allow pool and interface
 * Ip binding uses the interface from table instead of flag
 * Adds default pool flag to use when pool is not specified
 * updates test to work with new fields
 * adds tests for extension

Change-Id: Ieb4cbbf07b211697d08178b1cf2252caf75049a2
This commit is contained in:
Vishvananda Ishaya 2012-01-04 12:47:40 -08:00
parent c27e1ccd3f
commit 26b24b7e23
16 changed files with 371 additions and 45 deletions

View File

@ -1,6 +1,7 @@
#!/usr/bin/env python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
@ -103,6 +104,8 @@ flags.DECLARE('multi_host', 'nova.network.manager')
flags.DECLARE('network_size', 'nova.network.manager')
flags.DECLARE('vlan_start', 'nova.network.manager')
flags.DECLARE('vpn_start', 'nova.network.manager')
flags.DECLARE('default_floating_pool', 'nova.network.manager')
flags.DECLARE('public_interface', 'nova.network.linux_net')
flags.DECLARE('libvirt_type', 'nova.virt.libvirt.connection')
flags.DEFINE_flag(flags.HelpFlag())
flags.DEFINE_flag(flags.HelpshortFlag())
@ -684,14 +687,23 @@ class FixedIpCommands(object):
class FloatingIpCommands(object):
"""Class for managing floating ip."""
@args('--ip_range', dest="range", metavar='<range>', help='IP range')
def create(self, range):
@args('--ip_range', dest="ip_range", metavar='<range>', help='IP range')
@args('--pool', dest="pool", metavar='<pool>', help='Optional pool')
@args('--interface', dest="interface", metavar='<interface>',
help='Optional interface')
def create(self, ip_range, pool=None, interface=None):
"""Creates floating ips for zone by range"""
addresses = netaddr.IPNetwork(range)
addresses = netaddr.IPNetwork(ip_range)
admin_context = context.get_admin_context()
if not pool:
pool = FLAGS.default_floating_pool
if not interface:
interface = FLAGS.public_interface
for address in addresses.iter_hosts():
db.floating_ip_create(admin_context,
{'address': str(address)})
{'address': str(address),
'pool': pool,
'interface': interface})
@args('--ip_range', dest="ip_range", metavar='<range>', help='IP range')
def delete(self, ip_range):

View File

@ -0,0 +1,104 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova.api.openstack.v2 import extensions
from nova import log as logging
from nova import network
LOG = logging.getLogger('nova.api.openstack.v2.contrib.floating_ip_poolss')
def _translate_floating_ip_view(pool):
return {
'name': pool['name'],
}
def _translate_floating_ip_pools_view(pools):
return {
'floating_ip_pools': [_translate_floating_ip_view(pool)
for pool in pools]
}
class FloatingIPPoolsController(object):
"""The Floating IP Pool API controller for the OpenStack API."""
def __init__(self):
self.network_api = network.API()
super(FloatingIPPoolsController, self).__init__()
def index(self, req):
"""Return a list of pools."""
context = req.environ['nova.context']
pools = self.network_api.get_floating_ip_pools(context)
return _translate_floating_ip_pools_view(pools)
def make_float_ip(elem):
elem.set('name')
class FloatingIPPoolTemplate(xmlutil.TemplateBuilder):
def construct(self):
root = xmlutil.TemplateElement('floating_ip_pool',
selector='floating_ip_pool')
make_float_ip(root)
return xmlutil.MasterTemplate(root, 1)
class FloatingIPPoolsTemplate(xmlutil.TemplateBuilder):
def construct(self):
root = xmlutil.TemplateElement('floating_ip_pools')
elem = xmlutil.SubTemplateElement(root, 'floating_ip_pool',
selector='floating_ip_pools')
make_float_ip(elem)
return xmlutil.MasterTemplate(root, 1)
class FloatingIPPoolsSerializer(xmlutil.XMLTemplateSerializer):
def index(self):
return FloatingIPPoolsTemplate()
class Floating_ip_pools(extensions.ExtensionDescriptor):
"""Floating IPs support"""
name = "Floating_ip_pools"
alias = "os-floating-ip-pools"
namespace = \
"http://docs.openstack.org/compute/ext/floating_ip_pools/api/v1.1"
updated = "2012-01-04T00:00:00+00:00"
def get_resources(self):
resources = []
body_serializers = {
'application/xml': FloatingIPPoolsSerializer(),
}
serializer = wsgi.ResponseSerializer(body_serializers)
res = extensions.ResourceExtension('os-floating-ip-pools',
FloatingIPPoolsController(),
serializer=serializer,
member_actions={})
resources.append(res)
return resources

View File

@ -1,6 +1,7 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 OpenStack LLC.
# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
# Copyright 2011 Grid Dynamics
# Copyright 2011 Eldar Nugaev, Kirill Shileev, Ilya Alekseyev
#
@ -34,6 +35,7 @@ LOG = logging.getLogger('nova.api.openstack.v2.contrib.floating_ips')
def make_float_ip(elem):
elem.set('id')
elem.set('ip')
elem.set('pool')
elem.set('fixed_ip')
elem.set('instance_id')
@ -56,8 +58,11 @@ class FloatingIPsTemplate(xmlutil.TemplateBuilder):
def _translate_floating_ip_view(floating_ip):
result = {'id': floating_ip['id'],
'ip': floating_ip['address']}
result = {
'id': floating_ip['id'],
'ip': floating_ip['address'],
'pool': floating_ip['pool'],
}
try:
result['fixed_ip'] = floating_ip['fixed_ip']['address']
except (TypeError, KeyError):
@ -106,13 +111,19 @@ class FloatingIPController(object):
def create(self, req, body=None):
context = req.environ['nova.context']
pool = None
if body and 'pool' in body:
pool = body['pool']
try:
address = self.network_api.allocate_floating_ip(context)
address = self.network_api.allocate_floating_ip(context, pool)
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':
msg = _("No more floating ips available.")
if pool:
msg = _("No more floating ips in pool %s.") % pool
else:
msg = _("No more floating ips available.")
raise webob.exc.HTTPBadRequest(explanation=msg)
else:
raise

View File

@ -1,5 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
@ -237,13 +238,18 @@ def floating_ip_get(context, id):
return IMPL.floating_ip_get(context, id)
def floating_ip_allocate_address(context, project_id):
"""Allocate free floating ip and return the address.
def floating_ip_get_pools(context):
"""Returns a list of floating ip pools"""
return IMPL.floating_ip_get_pools(context)
def floating_ip_allocate_address(context, project_id, pool):
"""Allocate free floating ip from specified pool and return the address.
Raises if one is not available.
"""
return IMPL.floating_ip_allocate_address(context, project_id)
return IMPL.floating_ip_allocate_address(context, project_id, pool)
def floating_ip_create(context, values):

View File

@ -1,5 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
@ -489,7 +490,16 @@ def floating_ip_get(context, id):
@require_context
def floating_ip_allocate_address(context, project_id):
def floating_ip_get_pools(context):
session = get_session()
pools = []
for result in session.query(models.FloatingIp.pool).distinct():
pools.append({'name': result[0]})
return pools
@require_context
def floating_ip_allocate_address(context, project_id, pool):
authorize_project_context(context, project_id)
session = get_session()
with session.begin():
@ -497,6 +507,7 @@ def floating_ip_allocate_address(context, project_id):
session=session, read_deleted="no").\
filter_by(fixed_ip_id=None).\
filter_by(project_id=None).\
filter_by(pool=pool).\
with_lockmode('update').\
first()
# NOTE(vish): if with_lockmode isn't supported, as in sqlite,

View File

@ -0,0 +1,46 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.from sqlalchemy import *
from sqlalchemy import Column, MetaData, Table, String
from nova import flags
flags.DECLARE('default_floating_pool', 'nova.network.manager')
flags.DECLARE('public_interface', 'nova.network.linux_net')
FLAGS = flags.FLAGS
meta = MetaData()
pool_column = Column('pool', String(255))
interface_column = Column('interface', String(255))
def upgrade(migrate_engine):
meta.bind = migrate_engine
table = Table('floating_ips', meta, autoload=True)
table.create_column(pool_column)
table.create_column(interface_column)
table.update().values(pool=FLAGS.default_floating_pool,
interface=FLAGS.public_interface).execute()
def downgrade(migrate_engine):
meta.bind = migrate_engine
table = Table('floating_ips', meta, autoload=True)
table.c.pool.drop()
table.c.interface.drop()

View File

@ -1,5 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# Copyright 2011 Piston Cloud Computing, Inc.
@ -711,6 +712,8 @@ class FloatingIp(BASE, NovaBase):
project_id = Column(String(255))
host = Column(String(255)) # , ForeignKey('hosts.id'))
auto_assigned = Column(Boolean, default=False, nullable=False)
pool = Column(String(255))
interface = Column(String(255))
class AuthToken(BASE, NovaBase):

View File

@ -1,5 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
@ -39,6 +40,11 @@ class API(base.Base):
{'method': 'get_floating_ip',
'args': {'id': id}})
def get_floating_ip_pools(self, context):
return rpc.call(context,
FLAGS.network_topic,
{'method': 'get_floating_pools'})
def get_floating_ip_by_address(self, context, address):
return rpc.call(context,
FLAGS.network_topic,
@ -62,8 +68,8 @@ class API(base.Base):
{'method': 'get_vifs_by_instance',
'args': {'instance_id': instance_id}})
def allocate_floating_ip(self, context):
"""Adds a floating ip to a project. (allocates)"""
def allocate_floating_ip(self, context, pool=None):
"""Adds a floating ip to a project from a pool. (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
@ -71,7 +77,8 @@ class API(base.Base):
return rpc.call(context,
FLAGS.network_topic,
{'method': 'allocate_floating_ip',
'args': {'project_id': context.project_id}})
'args': {'project_id': context.project_id,
'pool': pool}})
def release_floating_ip(self, context, address,
affect_auto_assigned=False):

View File

@ -1,5 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
@ -432,21 +433,21 @@ def init_host(ip_range=None):
iptables_manager.apply()
def bind_floating_ip(floating_ip, check_exit_code=True):
def bind_floating_ip(floating_ip, device, check_exit_code=True):
"""Bind ip to public interface."""
_execute('ip', 'addr', 'add', str(floating_ip) + '/32',
'dev', FLAGS.public_interface,
'dev', device,
run_as_root=True, check_exit_code=check_exit_code)
if FLAGS.send_arp_for_ha:
_execute('arping', '-U', floating_ip,
'-A', '-I', FLAGS.public_interface,
'-A', '-I', device,
'-c', 1, run_as_root=True, check_exit_code=False)
def unbind_floating_ip(floating_ip):
def unbind_floating_ip(floating_ip, device):
"""Unbind a public ip from public interface."""
_execute('ip', 'addr', 'del', str(floating_ip) + '/32',
'dev', FLAGS.public_interface, run_as_root=True)
'dev', device, run_as_root=True)
def ensure_metadata_ip():

View File

@ -1,5 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
@ -94,6 +95,8 @@ flags.DEFINE_integer('network_size', 256,
'Number of addresses in each private subnet')
flags.DEFINE_string('floating_range', '4.4.4.0/24',
'Floating IP address block')
flags.DEFINE_string('default_floating_pool', 'nova',
'Default pool for floating ips')
flags.DEFINE_string('fixed_range', '10.0.0.0/8', 'Fixed IP address block')
flags.DEFINE_string('fixed_range_v6', 'fd00::/48', 'Fixed IPv6 address block')
flags.DEFINE_string('gateway', None, 'Default IPv4 gateway')
@ -201,7 +204,9 @@ class FloatingIP(object):
fixed_address = floating_ip['fixed_ip']['address']
# NOTE(vish): The False here is because we ignore the case
# that the ip is already bound.
self.driver.bind_floating_ip(floating_ip['address'], False)
self.driver.bind_floating_ip(floating_ip['address'],
floating_ip['interface'],
False)
self.driver.ensure_floating_forward(floating_ip['address'],
fixed_address)
@ -286,8 +291,8 @@ class FloatingIP(object):
'project': context.project_id})
raise exception.NotAuthorized()
def allocate_floating_ip(self, context, project_id):
"""Gets an floating ip from the pool."""
def allocate_floating_ip(self, context, project_id, pool=None):
"""Gets a floating ip from the 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:
@ -296,9 +301,10 @@ class FloatingIP(object):
context.project_id)
raise exception.QuotaError(_('Address quota exceeded. You cannot '
'allocate any more addresses'))
# TODO(vish): add floating ips through manage command
pool = pool or FLAGS.default_floating_pool
return self.db.floating_ip_allocate_address(context,
project_id)
project_id,
pool)
def deallocate_floating_ip(self, context, address,
affect_auto_assigned=False):
@ -337,7 +343,6 @@ class FloatingIP(object):
# 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)
@ -348,20 +353,22 @@ class FloatingIP(object):
host = instance['host']
else:
host = fixed_ip['network']['host']
LOG.info("%s", self.host)
interface = floating_ip['interface']
if host == self.host:
# i'm the correct host
self._associate_floating_ip(context, floating_address,
fixed_address)
fixed_address, interface)
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']}})
'args': {'floating_address': floating_address,
'fixed_address': fixed_address,
'interface': interface}})
def _associate_floating_ip(self, context, floating_address, fixed_address):
def _associate_floating_ip(self, context, floating_address, fixed_address,
interface):
"""Performs db and driver calls to associate floating ip & fixed ip"""
# associate floating ip
self.db.floating_ip_fixed_ip_associate(context,
@ -369,7 +376,7 @@ class FloatingIP(object):
fixed_address,
self.host)
# gogo driver time
self.driver.bind_floating_ip(floating_address)
self.driver.bind_floating_ip(floating_address, interface)
self.driver.ensure_floating_forward(floating_address, fixed_address)
def disassociate_floating_ip(self, context, address,
@ -401,29 +408,36 @@ class FloatingIP(object):
host = instance['host']
else:
host = fixed_ip['network']['host']
interface = floating_ip['interface']
if host == self.host:
# i'm the correct host
self._disassociate_floating_ip(context, address)
self._disassociate_floating_ip(context, address, interface)
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}})
'args': {'address': address,
'interface': interface}})
def _disassociate_floating_ip(self, context, address):
def _disassociate_floating_ip(self, context, address, interface):
"""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.unbind_floating_ip(address, interface)
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_pools(self, context):
"""Returns list of floating pools"""
pools = self.db.floating_ip_get_pools(context)
return [dict(pool.iteritems()) for pool in pools]
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,

View File

@ -1,5 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
@ -153,7 +154,7 @@ class CloudTestCase(test.TestCase):
address = "10.10.10.10"
db.floating_ip_create(self.context,
{'address': address,
'host': self.network.host})
'pool': 'nova'})
self.cloud.allocate_address(self.context)
self.cloud.describe_addresses(self.context)
self.cloud.release_address(self.context,
@ -165,7 +166,7 @@ class CloudTestCase(test.TestCase):
allocate = self.cloud.allocate_address
db.floating_ip_create(self.context,
{'address': address,
'host': self.network.host})
'pool': 'nova'})
self.assertEqual(allocate(self.context)['publicIp'], address)
db.floating_ip_destroy(self.context, address)
self.assertRaises(exception.NoMoreFloatingIps,
@ -177,7 +178,7 @@ class CloudTestCase(test.TestCase):
allocate = self.cloud.allocate_address
db.floating_ip_create(self.context,
{'address': address,
'host': self.network.host,
'pool': 'nova',
'project_id': self.project_id})
result = self.cloud.release_address(self.context, address)
self.assertEqual(result['releaseResponse'], ['Address released.'])
@ -185,7 +186,9 @@ class CloudTestCase(test.TestCase):
def test_associate_disassociate_address(self):
"""Verifies associate runs cleanly without raising an exception"""
address = "10.10.10.10"
db.floating_ip_create(self.context, {'address': address})
db.floating_ip_create(self.context,
{'address': address,
'pool': 'nova'})
self.cloud.allocate_address(self.context)
# TODO(jkoelker) Probably need to query for instance_type_id and
# make sure we get a valid one

View File

@ -0,0 +1,73 @@
# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from lxml import etree
from nova.api.openstack.v2.contrib import floating_ip_pools
from nova import context
from nova import network
from nova import test
from nova.tests.api.openstack import fakes
def fake_get_floating_ip_pools(self, context):
return [{'name': 'nova'},
{'name': 'other'}]
class FloatingIpPoolTest(test.TestCase):
def setUp(self):
super(FloatingIpPoolTest, self).setUp()
self.stubs.Set(network.api.API, "get_floating_ip_pools",
fake_get_floating_ip_pools)
self.context = context.RequestContext('fake', 'fake')
self.controller = floating_ip_pools.FloatingIPPoolsController()
def test_translate_floating_ip_pools_view(self):
pools = fake_get_floating_ip_pools(None, self.context)
view = floating_ip_pools._translate_floating_ip_pools_view(pools)
self.assertTrue('floating_ip_pools' in view)
self.assertEqual(view['floating_ip_pools'][0]['name'],
pools[0]['name'])
self.assertEqual(view['floating_ip_pools'][1]['name'],
pools[1]['name'])
def test_floating_ips_pools_list(self):
req = fakes.HTTPRequest.blank('/v2/fake/os-floating-ip-pools')
res_dict = self.controller.index(req)
pools = fake_get_floating_ip_pools(None, self.context)
response = {'floating_ip_pools': pools}
self.assertEqual(res_dict, response)
class FloatingIpPoolSerializerTest(test.TestCase):
def test_index_serializer(self):
serializer = floating_ip_pools.FloatingIPPoolsSerializer()
text = serializer.serialize(dict(
floating_ip_pools=[
dict(name='nova'),
dict(name='other')
]), 'index')
tree = etree.fromstring(text)
self.assertEqual('floating_ip_pools', tree.tag)
self.assertEqual(2, len(tree))
self.assertEqual('floating_ip_pool', tree[0].tag)
self.assertEqual('floating_ip_pool', tree[1].tag)
self.assertEqual('nova', tree[0].get('name'))
self.assertEqual('other', tree[1].get('name'))

View File

@ -1,3 +1,4 @@
# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
# Copyright 2011 Eldar Nugaev
# All Rights Reserved.
#
@ -30,11 +31,13 @@ FAKE_UUID = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
def network_api_get_floating_ip(self, context, id):
return {'id': 1, 'address': '10.10.10.10',
'pool': 'nova',
'fixed_ip': None}
def network_api_get_floating_ip_by_address(self, context, address):
return {'id': 1, 'address': '10.10.10.10',
'pool': 'nova',
'fixed_ip': {'address': '10.0.0.1',
'instance': {'uuid': FAKE_UUID}}}
@ -42,9 +45,11 @@ def network_api_get_floating_ip_by_address(self, context, address):
def network_api_get_floating_ips_by_project(self, context):
return [{'id': 1,
'address': '10.10.10.10',
'pool': 'nova',
'fixed_ip': {'address': '10.0.0.1',
'instance': {'uuid': FAKE_UUID}}},
{'id': 2,
'pool': 'nova', 'interface': 'eth0',
'address': '10.10.10.11'}]
@ -107,6 +112,7 @@ class FloatingIpTest(test.TestCase):
host = "fake_host"
return db.floating_ip_create(self.context,
{'address': self.floating_ip,
'pool': 'nova',
'host': host})
def _delete_floating_ip(self):
@ -151,7 +157,8 @@ class FloatingIpTest(test.TestCase):
self.assertEqual(view['floating_ip']['instance_id'], None)
def test_translate_floating_ip_view_dict(self):
floating_ip = {'id': 0, 'address': '10.0.0.10', 'fixed_ip': None}
floating_ip = {'id': 0, 'address': '10.0.0.10', 'pool': 'nova',
'fixed_ip': None}
view = floating_ips._translate_floating_ip_view(floating_ip)
self.assertTrue('floating_ip' in view)
@ -161,10 +168,12 @@ class FloatingIpTest(test.TestCase):
response = {'floating_ips': [{'instance_id': FAKE_UUID,
'ip': '10.10.10.10',
'pool': 'nova',
'fixed_ip': '10.0.0.1',
'id': 1},
{'instance_id': None,
'ip': '10.10.10.11',
'pool': 'nova',
'fixed_ip': None,
'id': 2}]}
self.assertEqual(res_dict, response)
@ -180,6 +189,7 @@ class FloatingIpTest(test.TestCase):
def test_show_associated_floating_ip(self):
def get_floating_ip(self, context, id):
return {'id': 1, 'address': '10.10.10.10',
'pool': 'nova',
'fixed_ip': {'address': '10.0.0.1',
'instance': {'uuid': FAKE_UUID}}}
self.stubs.Set(network.api.API, "get_floating_ip", get_floating_ip)
@ -207,7 +217,7 @@ class FloatingIpTest(test.TestCase):
pass
def fake2(*args, **kwargs):
return {'id': 1, 'address': '10.10.10.10'}
return {'id': 1, 'address': '10.10.10.10', 'pool': 'nova'}
self.stubs.Set(network.api.API, "allocate_floating_ip",
fake1)
@ -223,7 +233,8 @@ class FloatingIpTest(test.TestCase):
"id": 1,
"instance_id": None,
"ip": "10.10.10.10",
"fixed_ip": None}
"fixed_ip": None,
"pool": 'nova'}
self.assertEqual(ip, expected)
def test_floating_ip_release(self):

View File

@ -1,5 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
# Copyright 2011 OpenStack LLC.
# All Rights Reserved.
#
@ -110,6 +111,7 @@ class ExtensionControllerTest(ExtensionTestCase):
"FlavorExtraData",
"Floating_ips",
"Floating_ip_dns",
"Floating_ip_pools",
"Fox In Socks",
"Hosts",
"Keypairs",

View File

@ -1,5 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
# Copyright 2010 OpenStack, LLC
# All Rights Reserved.
#
@ -88,6 +89,7 @@ def stub_out_db_network_api(stubs):
'fixed_ip_id': None,
'fixed_ip': None,
'project_id': None,
'pool': 'nova',
'auto_assigned': False}
virtual_interface_fields = {'id': 0,
@ -101,9 +103,10 @@ def stub_out_db_network_api(stubs):
virtual_interfacees = [virtual_interface_fields]
networks = [network_fields]
def fake_floating_ip_allocate_address(context, project_id):
def fake_floating_ip_allocate_address(context, project_id, pool):
ips = filter(lambda i: i['fixed_ip_id'] is None \
and i['project_id'] is None,
and i['project_id'] is None \
and i['pool'] == pool,
floating_ips)
if not ips:
raise exception.NoMoreFloatingIps()

View File

@ -1,6 +1,7 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 Rackspace
# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -108,6 +109,8 @@ flavor = {'id': 0,
floating_ip_fields = {'id': 0,
'address': '192.168.10.100',
'pool': 'nova',
'interface': 'eth0',
'fixed_ip_id': 0,
'project_id': None,
'auto_assigned': False}
@ -580,21 +583,29 @@ class VlanNetworkTestCase(test.TestCase):
# floating ip that's already associated
def fake2(*args, **kwargs):
return {'address': '10.0.0.1',
'pool': 'nova',
'interface': 'eth0',
'fixed_ip_id': 1}
# floating ip that isn't associated
def fake3(*args, **kwargs):
return {'address': '10.0.0.1',
'pool': 'nova',
'interface': 'eth0',
'fixed_ip_id': None}
# fixed ip with remote host
def fake4(*args, **kwargs):
return {'address': '10.0.0.1',
'pool': 'nova',
'interface': 'eth0',
'network': {'multi_host': False, 'host': 'jibberjabber'}}
# fixed ip with local host
def fake5(*args, **kwargs):
return {'address': '10.0.0.1',
'pool': 'nova',
'interface': 'eth0',
'network': {'multi_host': False, 'host': 'testhost'}}
def fake6(*args, **kwargs):
@ -641,21 +652,29 @@ class VlanNetworkTestCase(test.TestCase):
# floating ip that isn't associated
def fake2(*args, **kwargs):
return {'address': '10.0.0.1',
'pool': 'nova',
'interface': 'eth0',
'fixed_ip_id': None}
# floating ip that is associated
def fake3(*args, **kwargs):
return {'address': '10.0.0.1',
'pool': 'nova',
'interface': 'eth0',
'fixed_ip_id': 1}
# fixed ip with remote host
def fake4(*args, **kwargs):
return {'address': '10.0.0.1',
'pool': 'nova',
'interface': 'eth0',
'network': {'multi_host': False, 'host': 'jibberjabber'}}
# fixed ip with local host
def fake5(*args, **kwargs):
return {'address': '10.0.0.1',
'pool': 'nova',
'interface': 'eth0',
'network': {'multi_host': False, 'host': 'testhost'}}
def fake6(*args, **kwargs):