Implemented the floating ip create api method

This commit is contained in:
Alan Quillin
2015-05-11 20:23:01 -05:00
parent 3c0cff591b
commit 0712be6818
12 changed files with 695 additions and 78 deletions

View File

@@ -319,7 +319,7 @@ def ip_address_find(context, lock_mode=False, **filters):
return query.filter(*model_filters)
def ip_address_count_all(context, **filters):
def ip_address_count_all(context, filters):
query = context.session.query(sql_func.count(models.IPAddress.id))
model_filters = _model_query(context, models.IPAddress, filters)
return query.filter(*model_filters).scalar()
@@ -892,3 +892,14 @@ def floating_ip_find(context, lock_mode=False, limit=None, sorts=None,
return paginate_query(query.filter(*model_filters), models.IPAddress,
limit, sorts, marker)
def floating_ip_associate_fixed_ip(context, floating_ip, fixed_ip,
enable=True):
assoc = models.FloatingToFixedIPAssociation()
assoc.floating_ip_address_id = floating_ip.id
assoc.fixed_ip_address_id = fixed_ip.id
assoc.enabled = enable
context.session.add(assoc)
floating_ip.fixed_ip = fixed_ip
return floating_ip

View File

@@ -0,0 +1,35 @@
"""Added Floating IP to Fixed IP mapping table
Revision ID: 33e9e23ba761
Revises: 356d6c0623c8
Create Date: 2015-05-11 14:14:23.619952
"""
# revision identifiers, used by Alembic.
revision = '33e9e23ba761'
down_revision = '356d6c0623c8'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.create_table('quark_floating_to_fixed_ip_address_associations',
sa.Column('floating_ip_address_id', sa.String(length=36),
nullable=False),
sa.Column('fixed_ip_address_id', sa.String(length=36),
nullable=False),
sa.Column('enabled', sa.Boolean(), server_default='1',
nullable=False),
sa.ForeignKeyConstraint(['fixed_ip_address_id'],
['quark_ip_addresses.id'], ),
sa.ForeignKeyConstraint(['floating_ip_address_id'],
['quark_ip_addresses.id'], ),
sa.PrimaryKeyConstraint('floating_ip_address_id',
'fixed_ip_address_id'),
mysql_engine='InnoDB')
def downgrade():
op.drop_table('quark_floating_to_fixed_ip_address_associations')

View File

@@ -50,6 +50,10 @@ def _foreign_keys_dropped(op, table):
def upgrade():
metadata = sa.MetaData(bind=op.get_bind())
table = sa.Table('quark_port_ip_address_associations', metadata,
autoload=True)
with _foreign_keys_dropped(op, table):
op.alter_column('quark_port_ip_address_associations', 'ip_address_id',
existing_type=sa.String(36), nullable=False)
op.alter_column('quark_port_ip_address_associations', 'port_id',

View File

@@ -1 +1 @@
356d6c0623c8
33e9e23ba761

View File

@@ -197,6 +197,40 @@ class IPAddress(BASEV2, models.HasId):
return str(ip.ipv6())
deallocated_at = sa.Column(sa.DateTime(), index=True)
fixed_ip = None
class FloatingToFixedIPAssociation(object):
pass
flip_to_fixed_ip_assoc_tbl = sa.Table(
"quark_floating_to_fixed_ip_address_associations",
BASEV2.metadata,
sa.Column("floating_ip_address_id", sa.String(36),
sa.ForeignKey("quark_ip_addresses.id"), nullable=False,
primary_key=True),
sa.Column("fixed_ip_address_id", sa.String(36),
sa.ForeignKey("quark_ip_addresses.id"), nullable=False,
primary_key=True),
sa.Column("enabled", sa.Boolean(), default=True, nullable=False,
server_default='1'),
**TABLE_KWARGS)
orm.mapper(FloatingToFixedIPAssociation, flip_to_fixed_ip_assoc_tbl)
IPAddress.fixed_ip = orm.relationship("IPAddress",
secondary=flip_to_fixed_ip_assoc_tbl,
primaryjoin=(IPAddress.id ==
flip_to_fixed_ip_assoc_tbl
.c.floating_ip_address_id
and
flip_to_fixed_ip_assoc_tbl
.c.floating_ip_address_id ==
1),
secondaryjoin=(IPAddress.id ==
flip_to_fixed_ip_assoc_tbl
.c.fixed_ip_address_id),
uselist=False)
class Route(BASEV2, models.HasTenant, models.HasId, IsHazTags):
@@ -391,6 +425,7 @@ class Port(BASEV2, models.HasTenant, models.HasId):
secondary=port_group_association_table,
backref="ports")
# Indices tailored specifically to get_instance_nw_info calls from nova
sa.Index("idx_ports_1", Port.__table__.c.device_id, Port.__table__.c.tenant_id)
sa.Index("idx_ports_2", Port.__table__.c.device_owner,

View File

@@ -17,6 +17,8 @@
Unicorn driver for Quark
"""
import json
import netaddr
import requests
from oslo.config import cfg
@@ -44,21 +46,41 @@ class UnicornDriver(object):
def get_name(cls):
return "Unicorn"
def register_floating_ip(self, floating_ip):
pass
def register_floating_ip(self, floating_ip, port, fixed_ip):
url = CONF.QUARK.floating_ip_base_url
req = self._build_request_body(floating_ip, port, fixed_ip)
LOG.info("Calling unicorn to register floating ip: %s %s" % (url, req))
r = requests.post(url, data=json.dumps(req))
if r.status_code != 200:
msg = "Unexpected status from unicorn API: Status Code %s, " \
"Message: %s" % (r.status_code, r.json())
LOG.error("register_floating_ip: %s" % msg)
raise ex.RegisterFloatingIpFailure(id=floating_ip.id)
def update_floating_ip(self, floating_ip):
pass
def remove_floating_ip(self, floating_ip):
url = "%s/%s" % (CONF.QUARK.floating_ip_base_url,
floating_ip.formatted())
floating_ip.address_readable)
LOG.info("Calling unicorn to remove floating ip: %s" % url)
r = requests.delete(url)
if r.status_code != 204:
msg = "Unexpected status from unicorn API: Status Code %s, " \
"Message: %s" % (r.status_code,)
"Message: %s" % (r.status_code, r.json())
LOG.error("remove_floating_ip: %s" % msg)
raise ex.RemoveFloatingIpFailure(id=floating_ip["id"], msg=msg)
raise ex.RemoveFloatingIpFailure(id=floating_ip.id)
@staticmethod
def _build_request_body(floating_ip, port, fixed_ip):
mac_addr = netaddr.EUI(port.mac_address)
content = {"public_ip": floating_ip["address_readable"],
"network_uuid": port.id,
"destinations": [
{"private_ip": fixed_ip.address_readable,
"private_mac": str(mac_addr)}]}
return {"floating_ip": content}

View File

@@ -140,5 +140,26 @@ class FloatingIpNotFound(exceptions.NeutronException):
class RemoveFloatingIpFailure(exceptions.NeutronException):
message = _("An error occured when trying to remove the floating IP %(id)."
" %(msg)")
message = _("An error occurred when trying to remove the "
"floating IP %(id).")
class RegisterFloatingIpFailure(exceptions.NeutronException):
message = _("An error occurred when trying to register the floating IP "
"%(id).")
class PortAlreadyContainsFloatingIp(exceptions.Conflict):
message = _("Port %(port_id) already has an associated floating IP.")
class FixedIpDoesNotExistsForPort(exceptions.BadRequest):
message = _("Fixed IP %(fixed_ip) does not exist on Port %(port_id)")
class NoAvailableFixedIPsForPort(exceptions.Conflict):
message = _("There are no available fixed IPs for port %(port_id)")
class PortDoesNotHaveAGateway(exceptions.Conflict):
message = _("Port %(port_id) does not have a gateway")

View File

@@ -386,7 +386,9 @@ class Plugin(neutron_plugin_base_v2.NeutronPluginBaseV2,
@sessioned
def create_floatingip(self, context, floatingip):
return floating_ips.create_floatingip(context, floatingip)
self._fix_missing_tenant_id(context, floatingip["floatingip"])
return floating_ips.create_floatingip(context,
floatingip["floatingip"])
@sessioned
def update_floatingip(self, context, id, floatingip):
@@ -412,9 +414,8 @@ class Plugin(neutron_plugin_base_v2.NeutronPluginBaseV2,
def get_routers_count(self, context, filters=None):
raise NotImplementedError()
@sessioned
def get_floatingips_count(self, context, filters=None):
raise floating_ips.get_floatingips_count(context, filters)
return floating_ips.get_floatingips_count(context, filters)
def get_ip_availability(self, **kwargs):
return ip_availability.get_ip_availability(**kwargs)

View File

@@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from neutron.common import exceptions
from oslo.config import cfg
from oslo_log import log as logging
@@ -30,34 +31,95 @@ LOG = logging.getLogger(__name__)
quark_router_opts = [
cfg.StrOpt('default_floating_ip_driver',
default='Unicorn',
help=_('Driver for floating IP'))
help=_('Driver for floating IP')),
cfg.StrOpt('floating_ip_segment_name', default='floating_ip',
help=_('Segment name for floating IP subnets'))
]
CONF.register_opts(quark_router_opts, "QUARK")
def create_floatingip(context, body):
def create_floatingip(context, content):
LOG.info("create_floatingip %s for tenant %s and body %s" %
(id, context.tenant_id, body))
(id, context.tenant_id, content))
tenant_id = content.get("tenant_id")
network_id = content.get("floating_network_id")
fixed_ip_address = content.get("fixed_ip_address")
ip_address = content.get("floating_ip_address")
port_id = content.get("port_id")
# floating_ip_dict = body.get("ip_address")
# tenant_id = floating_ip_dict.get("tenant_id")
# network_id = floating_ip_dict.get("floating_network_id")
# # fixed_ip_address = floating_ip_dict.get("fixed_ip_address")
# # ip_address = floating_ip_dict.get("floating_ip_address")
# port_id = floating_ip_dict.get("port_id")
#
# if not tenant_id:
# raise exceptions.BadRequest(resource="floating_ip",
# msg="tenant_id is required.")
# if not network_id:
# raise exceptions.BadRequest(resource="floating_ip",
# msg="floating_network_id is required.")
# if not port_id:
# raise exceptions.BadRequest(resource="floating_ip",
# msg="port_id is required.")
if not tenant_id:
tenant_id = context.tenant_id
raise NotImplementedError()
if not network_id:
raise exceptions.BadRequest(resource="floating_ip",
msg="floating_network_id is required.")
network = db_api.network_find(context, id=network_id, scope=db_api.ONE)
if not network:
raise exceptions.NetworkNotFound(net_id=network_id)
fixed_ip = None
port = None
if port_id:
port = db_api.port_find(context, id=port_id, scope=db_api.ONE)
if not port:
raise exceptions.PortNotFound(port_id=port_id)
if not port.ip_addresses or len(port.ip_addresses) == 0:
raise quark_exceptions.NoAvailableFixedIPsForPort(port_id=port_id)
if not fixed_ip_address:
fixed_ip = _get_next_available_fixed_ip(port)
if not fixed_ip:
raise quark_exceptions.NoAvailableFixedIPsForPort(
port_id=port_id)
else:
fixed_ip = next((ip for ip in port.ip_addresses
if (ip["address_readable"] == fixed_ip_address and
ip.get("address_type") == ip_types.FIXED)),
None)
if not fixed_ip:
raise quark_exceptions.FixedIpDoesNotExistsForPort(
fixed_ip=fixed_ip_address, port_id=port_id)
if any(ip for ip in port.ip_addresses
if (ip.get("address_type") == ip_types.FLOATING and
ip.fixed_ip["address_readable"] == fixed_ip_address)):
raise quark_exceptions.PortAlreadyContainsFloatingIp(
port_id=port_id)
new_addresses = []
ip_addresses = []
if ip_address:
ip_addresses.append(ip_address)
seg_name = CONF.QUARK.floating_ip_segment_name
strategy_name = network.get("ipam_strategy")
ipam_driver = ipam.IPAM_REGISTRY.get_strategy(strategy_name)
ipam_driver.allocate_ip_address(context, new_addresses, network_id,
port_id, CONF.QUARK.ipam_reuse_after,
seg_name, version=4,
ip_addresses=ip_addresses,
address_type=ip_types.FLOATING)
floating_ip = new_addresses[0]
if fixed_ip and port:
with context.session.begin():
floating_ip = db_api.floating_ip_associate_fixed_ip(context,
floating_ip,
fixed_ip)
flip_driver_type = CONF.QUARK.default_floating_ip_driver
flip_driver = registry.DRIVER_REGISTRY.get_driver(flip_driver_type)
flip_driver.register_floating_ip(floating_ip, port, fixed_ip)
return v._make_floating_ip_dict(floating_ip)
def update_floatingip(context, id, body):
@@ -80,18 +142,19 @@ def delete_floatingip(context, id):
filters = {"address_type": ip_types.FLOATING, "_deallocated": False}
addr = db_api.floating_ip_find(context, id=id, scope=db_api.ONE, **filters)
if not addr:
floating_ip = db_api.floating_ip_find(context, id=id, scope=db_api.ONE,
**filters)
if not floating_ip:
raise quark_exceptions.FloatingIpNotFound(id=id)
driver_type = CONF.QUARK.default_floating_ip_driver
driver = registry.DRIVER_REGISTRY.get_driver(driver_type)
driver.remove_floating_ip(addr)
driver.remove_floating_ip(floating_ip)
strategy_name = addr.network["ipam_strategy"]
strategy_name = floating_ip.network.get("ipam_strategy")
ipam_driver = ipam.IPAM_REGISTRY.get_strategy(strategy_name)
ipam_driver.deallocate_ip_address(context, addr)
ipam_driver.deallocate_ip_address(context, floating_ip)
def get_floatingip(context, id, fields=None):
@@ -112,12 +175,13 @@ def get_floatingip(context, id, fields=None):
filters = {"address_type": ip_types.FLOATING, "_deallocated": False}
addr = db_api.floating_ip_find(context, id=id, scope=db_api.ONE, **filters)
floating_ip = db_api.floating_ip_find(context, id=id, scope=db_api.ONE,
**filters)
if not addr:
if not floating_ip:
raise quark_exceptions.FloatingIpNotFound(id=id)
return v._make_floating_ip_dict(addr)
return v._make_floating_ip_dict(floating_ip)
def get_floatingips(context, filters=None, fields=None, sorts=None, limit=None,
@@ -150,9 +214,10 @@ def get_floatingips(context, filters=None, fields=None, sorts=None, limit=None,
filters["_deallocated"] = False
filters["address_type"] = ip_types.FLOATING
addrs = db_api.floating_ip_find(context, scope=db_api.ALL, **filters)
floating_ips = db_api.floating_ip_find(context, scope=db_api.ALL,
**filters)
return [v._make_floating_ip_dict(ip) for ip in addrs]
return [v._make_floating_ip_dict(flip) for flip in floating_ips]
def get_floatingips_count(context, filters=None):
@@ -174,7 +239,7 @@ def get_floatingips_count(context, filters=None):
NOTE: this method is optional, as it was not part of the originally
defined plugin API.
"""
LOG.info("get_floatingips_count for tenant %s filters" %
LOG.info("get_floatingips_count for tenant %s filters %s" %
(context.tenant_id, filters))
if filters is None:
@@ -184,3 +249,20 @@ def get_floatingips_count(context, filters=None):
filters["address_type"] = ip_types.FLOATING
return db_api.ip_address_count_all(context, filters)
def _get_next_available_fixed_ip(port):
floating_ips = [ip for ip in port.ip_addresses
if ip.get("address_type") == ip_types.FLOATING]
fixed_ips = [ip for ip in port.ip_addresses
if ip.get("address_type") == ip_types.FIXED]
if not fixed_ips or len(fixed_ips) == 0:
return None
used = [ip.fixed_ip.address for ip in floating_ips
if ip and ip.fixed_ip]
return next((ip for ip in sorted(fixed_ips,
key=lambda ip: ip.get("allocated_at"))
if ip.address not in used), None)

View File

@@ -188,7 +188,9 @@ def _make_port_address_dict(ip, port, fields=None):
def _make_port_dict(port, fields=None):
res = _port_dict(port)
res["fixed_ips"] = [_make_port_address_dict(ip, port, fields)
for ip in port.ip_addresses]
for ip in port.ip_addresses
if (not ip.get("address_type") or
ip.get("address_type") == ip_types.FIXED)]
return res
@@ -196,8 +198,11 @@ def _make_ports_list(query, fields=None):
ports = []
for port in query:
port_dict = _port_dict(port, fields)
port_dict["fixed_ips"] = [_make_port_address_dict(addr, port, fields)
for addr in port.ip_addresses]
port_dict["fixed_ips"] = [_make_port_address_dict(ip, port, fields)
for ip in port.ip_addresses
if (not ip.get("address_type") or
ip.get("address_type") == ip_types.FIXED)
]
ports.append(port_dict)
return ports
@@ -245,27 +250,16 @@ def _make_ip_policy_dict(ipp):
def _make_floating_ip_dict(flip):
# Note(Alan Quillin) A floating IP should only be associated with a single
# port so we need to get the first port from the list if any exist.
ports = flip.ports
port_id = None
fixed_ip = None
if flip.ports and len(flip.ports) > 0:
port = flip.ports[0]
port_id = flip.ports[0].id
if port.ip_addresses and len(port.ip_addresses) > 0:
# Note(Alan Quillin) If the associated port has multiple fixed IP
# addresses, the first one found is used (based on allocated_at)
fixed_ip = next(ip.formatted() if ip else None
for ip in sorted(port.ip_addresses,
key=lambda ip:
ip.get("allocated_at"))
if ip.get("address_type") == ip_types.FIXED)
if ports and len(ports) > 0:
port_id = None if not ports[0] else ports[0].id
fixed_ip = flip.fixed_ip
return {"id": flip.get("id"),
"floating_network_id": flip.get("network_id"),
"router_id": CONF.QUARK.floating_ip_router_id,
"fixed_ip_address": fixed_ip,
"fixed_ip_address": None if not fixed_ip else fixed_ip.formatted(),
"floating_ip_address": flip.formatted(),
"tenant_id": flip.get("tenant_id"),
"status": flip.get("status"),

View File

@@ -15,20 +15,24 @@
import contextlib
import datetime
import mock
import netaddr
from neutron.common import exceptions
from quark.db import models
from quark import exceptions as quark_exceptions
from quark.plugin_modules import floating_ips
from quark.tests import test_quark_plugin
class TestRemoveFloatingIPs(test_quark_plugin.TestQuarkPlugin):
@contextlib.contextmanager
def _stubs(self, addr):
addr_model = None
if addr:
addr_model = models.IPAddress()
addr_model.update(addr)
def _stubs(self, flip=None):
flip_model = None
if flip:
flip_model = models.IPAddress()
flip_model.update(flip)
with contextlib.nested(
mock.patch("quark.db.api.floating_ip_find"),
@@ -36,17 +40,412 @@ class TestRemoveFloatingIPs(test_quark_plugin.TestQuarkPlugin):
mock.patch("quark.drivers.unicorn_driver.UnicornDriver"
".remove_floating_ip")
) as (flip_find, mock_dealloc, mock_remove_flip):
flip_find.return_value = addr_model
flip_find.return_value = flip_model
yield
def test_delete_floating_by_ip_address_id(self):
addr = dict(id=1, address=3232235876, address_readable="192.168.1.100",
flip = dict(id=1, address=3232235876, address_readable="192.168.1.100",
subnet_id=1, network_id=2, version=4, used_by_tenant_id=1,
network=dict(ipam_strategy="ANY"))
with self._stubs(addr=addr):
with self._stubs(flip=flip):
self.plugin.delete_floatingip(self.context, 1)
def test_delete_floating_by_when_ip_address_does_not_exists_fails(self):
with self._stubs(addr=None):
with self._stubs():
with self.assertRaises(quark_exceptions.FloatingIpNotFound):
self.plugin.delete_floatingip(self.context, 1)
class TestFloatingIPUtilityMethods(test_quark_plugin.TestQuarkPlugin):
def test_get_next_available_fixed_ip_with_single_fixed_ip(self):
port = models.Port()
port.update(dict(id=1))
fixed_ip_addr = netaddr.IPAddress('192.168.0.1')
fixed_ip = models.IPAddress()
fixed_ip.update(dict(address_type="fixed", address=int(fixed_ip_addr),
version=4, address_readable=str(fixed_ip_addr),
allocated_at=datetime.datetime.now()))
port.ip_addresses.append(fixed_ip)
next_fixed_ip = floating_ips._get_next_available_fixed_ip(port)
self.assertEqual(next_fixed_ip["address_readable"], '192.168.0.1')
def test_get_next_available_fixed_ip_with_mult_fixed_ips(self):
port = models.Port()
port.update(dict(id=1))
for ip_addr in ["192.168.0.1", "192.168.0.2", "192.168.0.3"]:
fixed_ip_addr = netaddr.IPAddress(ip_addr)
fixed_ip = models.IPAddress()
fixed_ip.update(dict(address_type="fixed",
address=int(fixed_ip_addr),
version=4,
address_readable=str(fixed_ip_addr),
allocated_at=datetime.datetime.now()))
port.ip_addresses.append(fixed_ip)
next_fixed_ip = floating_ips._get_next_available_fixed_ip(port)
self.assertEqual(next_fixed_ip["address_readable"], '192.168.0.1')
def test_get_next_available_fixed_ip_with_no_avail_fixed_ips(self):
port = models.Port()
port.update(dict(id=1))
fixed_ip_addr = netaddr.IPAddress("192.168.0.1")
fixed_ip = models.IPAddress()
fixed_ip.update(dict(address_type="fixed",
address=int(fixed_ip_addr),
version=4,
address_readable=str(fixed_ip_addr),
allocated_at=datetime.datetime.now()))
flip_addr = netaddr.IPAddress("10.0.0.1")
flip = models.IPAddress()
flip.update(dict(address_type="floating",
address=int(flip_addr),
version=4,
address_readable=str(flip_addr),
allocated_at=datetime.datetime.now()))
flip.fixed_ip = fixed_ip
port.ip_addresses.append(fixed_ip)
port.ip_addresses.append(flip)
fixed_ip_addr = netaddr.IPAddress("192.168.0.2")
fixed_ip = models.IPAddress()
fixed_ip.update(dict(address_type="fixed",
address=int(fixed_ip_addr),
version=4,
address_readable=str(fixed_ip_addr),
allocated_at=datetime.datetime.now()))
flip_addr = netaddr.IPAddress("10.0.0.2")
flip = models.IPAddress()
flip.update(dict(address_type="floating",
address=int(flip_addr),
version=4,
address_readable=str(flip_addr),
allocated_at=datetime.datetime.now()))
flip.fixed_ip = fixed_ip
port.ip_addresses.append(fixed_ip)
port.ip_addresses.append(flip)
next_fixed_ip = floating_ips._get_next_available_fixed_ip(port)
self.assertEqual(next_fixed_ip, None)
def test_get_next_available_fixed_ip_with_avail_fixed_ips(self):
port = models.Port()
port.update(dict(id=1))
fixed_ip_addr = netaddr.IPAddress("192.168.0.1")
fixed_ip = models.IPAddress()
fixed_ip.update(dict(address_type="fixed",
address=int(fixed_ip_addr),
version=4,
address_readable=str(fixed_ip_addr),
allocated_at=datetime.datetime.now()))
flip_addr = netaddr.IPAddress("10.0.0.1")
flip = models.IPAddress()
flip.update(dict(address_type="floating",
address=int(flip_addr),
version=4,
address_readable=str(flip_addr),
allocated_at=datetime.datetime.now()))
flip.fixed_ip = fixed_ip
port.ip_addresses.append(fixed_ip)
port.ip_addresses.append(flip)
fixed_ip_addr = netaddr.IPAddress("192.168.0.2")
fixed_ip = models.IPAddress()
fixed_ip.update(dict(address_type="fixed",
address=int(fixed_ip_addr),
version=4,
address_readable=str(fixed_ip_addr),
allocated_at=datetime.datetime.now()))
port.ip_addresses.append(fixed_ip)
port.ip_addresses.append(flip)
next_fixed_ip = floating_ips._get_next_available_fixed_ip(port)
self.assertEqual(next_fixed_ip["address_readable"], "192.168.0.2")
class TestCreateFloatingIPs(test_quark_plugin.TestQuarkPlugin):
@contextlib.contextmanager
def _stubs(self, flip=None, port=None, ips=None, network=None):
port_model = None
if port:
port_model = models.Port()
port_model.update(dict(port=port))
if ips:
for ip in ips:
ip_model = models.IPAddress()
ip_model.update(ip)
if (ip["address_type"] == "floating"
and "fixed_ip_addr" in ip):
fixed_ip = models.IPAddress()
fixed_ip.update(next(ip_addr for ip_addr in ips
if (ip_addr["address_readable"] ==
ip["fixed_ip_addr"])))
ip_model.fixed_ip = fixed_ip
port_model.ip_addresses.append(ip_model)
flip_model = None
if flip:
flip_model = models.IPAddress()
flip_model.update(flip)
net_model = None
if network:
net_model = models.Network()
net_model.update(network)
def _alloc_ip(context, new_addr, net_id, port_m, *args, **kwargs):
new_addr.append(flip_model)
def _flip_fixed_ip_assoc(context, addr, fixed_ip):
addr.fixed_ip = fixed_ip
return addr
with contextlib.nested(
mock.patch("quark.db.api.floating_ip_find"),
mock.patch("quark.db.api.network_find"),
mock.patch("quark.db.api.port_find"),
mock.patch("quark.ipam.QuarkIpam.allocate_ip_address"),
mock.patch("quark.drivers.unicorn_driver.UnicornDriver"
".register_floating_ip"),
mock.patch("quark.db.api.floating_ip_associate_fixed_ip")
) as (flip_find, net_find, port_find, alloc_ip, mock_reg_flip, assoc):
flip_find.return_value = flip_model
net_find.return_value = net_model
port_find.return_value = port_model
alloc_ip.side_effect = _alloc_ip
assoc.side_effect = _flip_fixed_ip_assoc
yield
def test_create_with_a_port(self):
floating_ip_addr = netaddr.IPAddress("10.0.0.1")
floating_ip = dict(id=1, address=int(floating_ip_addr), version=4,
address_readable=str(floating_ip_addr), subnet_id=1,
network_id=2, used_by_tenant_id=1)
network = dict(id="00000000-0000-0000-0000-000000000000",
ipam_strategy="ANY")
fixed_ip_addr = netaddr.IPAddress("192.168.0.1")
fixed_ips = [dict(address_type="fixed", address=int(fixed_ip_addr),
version=4, address_readable=str(fixed_ip_addr),
allocated_at=datetime.datetime.now())]
port = dict(id="abcdefgh-1111-2222-3333-1234567890ab")
with self._stubs(flip=floating_ip, port=port,
ips=fixed_ips, network=network):
request = dict(floating_network_id=network["id"],
port_id=port["id"])
flip = self.plugin.create_floatingip(self.context,
dict(floatingip=request))
self.assertEqual(flip["floating_ip_address"], "10.0.0.1")
self.assertEqual(flip["fixed_ip_address"], "192.168.0.1")
def test_create_without_a_port(self):
floating_ip_addr = netaddr.IPAddress("10.0.0.1")
floating_ip = dict(id=1, address=int(floating_ip_addr), version=4,
address_readable=str(floating_ip_addr), subnet_id=1,
network_id=2, used_by_tenant_id=1)
network = dict(id="00000000-0000-0000-0000-000000000000",
ipam_strategy="ANY")
fixed_ip_addr = netaddr.IPAddress("192.168.0.1")
fixed_ips = [dict(address_type="fixed", address=int(fixed_ip_addr),
version=4, address_readable=str(fixed_ip_addr),
allocated_at=datetime.datetime.now())]
with self._stubs(flip=floating_ip, port=None,
ips=fixed_ips, network=network):
request = dict(floating_network_id=network["id"], port_id=None)
flip = self.plugin.create_floatingip(self.context,
dict(floatingip=request))
self.assertEqual(flip["floating_ip_address"], "10.0.0.1")
self.assertEqual(flip.get("fixed_ip_address"), None)
def test_create_with_fixed_ip_specified(self):
floating_ip_addr = netaddr.IPAddress("10.0.0.1")
floating_ip = dict(id=1, address=int(floating_ip_addr), version=4,
address_readable=str(floating_ip_addr), subnet_id=1,
network_id=2, used_by_tenant_id=1)
network = dict(id="00000000-0000-0000-0000-000000000000",
ipam_strategy="ANY")
fixed_ips = []
for ip_addr in ["192.168.0.1", "192.168.0.2"]:
fixed_ip_addr = netaddr.IPAddress(ip_addr)
fixed_ips.append(dict(address_type="fixed", version=4,
address=int(fixed_ip_addr),
address_readable=str(fixed_ip_addr),
allocated_at=datetime.datetime.now()))
port = dict(id="abcdefgh-1111-2222-3333-1234567890ab")
with self._stubs(flip=floating_ip, port=port,
ips=fixed_ips, network=network):
request = dict(floating_network_id=network["id"],
port_id=port["id"], fixed_ip_address="192.168.0.2")
flip = self.plugin.create_floatingip(self.context,
dict(floatingip=request))
self.assertEqual(flip["floating_ip_address"], "10.0.0.1")
self.assertEqual(flip["fixed_ip_address"], "192.168.0.2")
def test_create_with_floating_ip_specified(self):
floating_ip_addr = netaddr.IPAddress("10.0.0.1")
floating_ip = dict(id=1, address=int(floating_ip_addr), version=4,
address_readable=str(floating_ip_addr), subnet_id=1,
network_id=2, used_by_tenant_id=1)
network = dict(id="00000000-0000-0000-0000-000000000000",
ipam_strategy="ANY")
fixed_ip_addr = netaddr.IPAddress("192.168.0.1")
fixed_ips = [dict(address_type="fixed", address=int(fixed_ip_addr),
version=4, address_readable=str(fixed_ip_addr),
allocated_at=datetime.datetime.now())]
port = dict(id=2)
with self._stubs(flip=floating_ip, port=port,
ips=fixed_ips, network=network):
request = dict(floating_network_id=network["id"],
port_id=port["id"], floating_ip_address="10.0.0.1")
flip = self.plugin.create_floatingip(self.context,
dict(floatingip=request))
self.assertEqual(flip["floating_ip_address"], "10.0.0.1")
self.assertEqual(flip["fixed_ip_address"], "192.168.0.1")
def test_create_without_network_id_fails(self):
with self._stubs():
with self.assertRaises(exceptions.BadRequest):
request = dict(port_id=2, floating_ip_address="10.0.0.1")
self.plugin.create_floatingip(self.context,
dict(floatingip=request))
def test_create_with_invalid_network_fails(self):
with self._stubs():
with self.assertRaises(exceptions.NetworkNotFound):
request = dict(floating_network_id=123,
port_id=2, floating_ip_address="10.0.0.1")
self.plugin.create_floatingip(self.context,
dict(floatingip=request))
def test_create_with_invalid_port_fails(self):
network = dict(id="00000000-0000-0000-0000-000000000000",
ipam_strategy="ANY")
with self._stubs(network=network):
with self.assertRaises(exceptions.PortNotFound):
request = dict(floating_network_id=network["id"],
port_id=2, floating_ip_address="10.0.0.1")
self.plugin.create_floatingip(self.context,
dict(floatingip=request))
def test_create_with_invalid_fixed_ip_for_port_fails(self):
network = dict(id="00000000-0000-0000-0000-000000000000",
ipam_strategy="ANY")
fixed_ip_addr = netaddr.IPAddress("192.168.0.1")
fixed_ips = [dict(address_type="fixed", version=4,
address=int(fixed_ip_addr),
address_readable=str(fixed_ip_addr),
allocated_at=datetime.datetime.now())]
port = dict(id="abcdefgh-1111-2222-3333-1234567890ab")
with self._stubs(port=port, ips=fixed_ips, network=network):
with self.assertRaises(
quark_exceptions.FixedIpDoesNotExistsForPort):
request = dict(floating_network_id=network["id"],
port_id=port["id"],
fixed_ip_address="192.168.0.2")
flip = self.plugin.create_floatingip(self.context,
dict(floatingip=request))
self.assertEqual(flip["address_readable"], "10.0.0.1")
self.assertEqual(flip.fixed_ip["address_readable"],
"192.168.0.2")
def test_create_with_port_and_fixed_ip_with_existing_flip_fails(self):
network = dict(id="00000000-0000-0000-0000-000000000000",
ipam_strategy="ANY")
fixed_ip_addr = netaddr.IPAddress("192.168.0.1")
fixed_ip = dict(address_type="fixed", version=4,
address=int(fixed_ip_addr),
address_readable=str(fixed_ip_addr),
allocated_at=datetime.datetime.now())
floating_ip_addr = netaddr.IPAddress("10.0.0.1")
floating_ip = dict(address_type="floating", version=4,
address=int(floating_ip_addr),
address_readable=str(floating_ip_addr),
allocated_at=datetime.datetime.now(),
fixed_ip_addr="192.168.0.1")
ips = [fixed_ip, floating_ip]
port = dict(id="abcdefgh-1111-2222-3333-1234567890ab")
with self._stubs(port=port, ips=ips, network=network):
with self.assertRaises(
quark_exceptions.PortAlreadyContainsFloatingIp):
request = dict(floating_network_id=network["id"],
port_id=port["id"],
fixed_ip_address="192.168.0.1")
self.plugin.create_floatingip(self.context,
dict(floatingip=request))
def test_create_when_port_has_no_fixed_ips_fails(self):
network = dict(id="00000000-0000-0000-0000-000000000000",
ipam_strategy="ANY")
port = dict(id="abcdefgh-1111-2222-3333-1234567890ab")
with self._stubs(port=port, network=network):
with self.assertRaises(
quark_exceptions.NoAvailableFixedIPsForPort):
request = dict(floating_network_id=network["id"],
port_id=port["id"])
self.plugin.create_floatingip(self.context,
dict(floatingip=request))
def test_create_when_port_has_no_available_fixed_ips_fails(self):
network = dict(id="00000000-0000-0000-0000-000000000000",
ipam_strategy="ANY")
fixed_ip_addr = netaddr.IPAddress("192.168.0.1")
fixed_ip = dict(address_type="fixed", version=4,
address=int(fixed_ip_addr),
address_readable=str(fixed_ip_addr),
allocated_at=datetime.datetime.now())
floating_ip_addr = netaddr.IPAddress("10.0.0.1")
floating_ip = dict(address_type="floating", version=4,
address=int(floating_ip_addr),
address_readable=str(floating_ip_addr),
allocated_at=datetime.datetime.now(),
fixed_ip_addr="192.168.0.1")
ips = [fixed_ip, floating_ip]
port = dict(id="abcdefgh-1111-2222-3333-1234567890ab")
with self._stubs(port=port, ips=ips, network=network):
with self.assertRaises(
quark_exceptions.NoAvailableFixedIPsForPort):
request = dict(floating_network_id=network["id"],
port_id=port["id"])
self.plugin.create_floatingip(self.context,
dict(floatingip=request))

View File

@@ -1557,6 +1557,19 @@ class QuarkNewIPAddressAllocation(QuarkIpamBaseTest):
self.ipam.allocate_ip_address(
self.context, [], 0, 0, 0, ip_addresses=["0.0.0.240"])
def test_allocate_new_ip_address_with_floating_address_type(self):
subnet = dict(id=1, first_ip=0, last_ip=255,
cidr="0.0.0.0/24", ip_version=4,
next_auto_assign_ip=0,
ip_policy=dict(size=1, exclude=[
models.IPPolicyCIDR(cidr="0.0.0.0/32")]))
with self._stubs(subnets=[(subnet, 0)], addresses=[None, None]):
address = []
self.ipam.allocate_ip_address(self.context, address, 0, 0, 0,
version=4,
address_type='floating')
self.assertEqual(address[0]["address_type"], 'floating')
class QuarkIPAddressAllocationTestRetries(QuarkIpamBaseTest):
@contextlib.contextmanager