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:
parent
c27e1ccd3f
commit
26b24b7e23
|
@ -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):
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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():
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'))
|
|
@ -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):
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue