Implemented the floating ip create api method
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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')
|
||||
@@ -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',
|
||||
|
||||
@@ -1 +1 @@
|
||||
356d6c0623c8
|
||||
33e9e23ba761
|
||||
@@ -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,
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user