Merge "Integration of Floating IP OVO"

This commit is contained in:
Zuul 2017-12-07 08:56:59 +00:00 committed by Gerrit Code Review
commit 635d256a30
12 changed files with 238 additions and 211 deletions

View File

@ -207,9 +207,10 @@ class DNSDbMixin(object):
def _delete_floatingip_from_external_dns_service(self, context, dns_domain,
dns_name, records):
ips = [str(r) for r in records]
try:
self.dns_driver.delete_record_set(context, dns_domain, dns_name,
records)
ips)
except (dns_exc.DNSDomainNotFound, dns_exc.DuplicateRecordSet) as e:
LOG.exception("Error deleting Floating IP data from external "
"DNS service. Name: '%(name)s'. Domain: "
@ -218,7 +219,7 @@ class DNSDbMixin(object):
{"name": dns_name,
"domain": dns_domain,
"message": e.msg,
"ips": ', '.join(records)})
"ips": ', '.join(ips)})
def _get_requested_state_for_external_dns_service_create(self, context,
floatingip_data,
@ -238,9 +239,10 @@ class DNSDbMixin(object):
def _add_ips_to_external_dns_service(self, context, dns_domain, dns_name,
records):
ips = [str(r) for r in records]
try:
self.dns_driver.create_record_set(context, dns_domain, dns_name,
records)
ips)
except (dns_exc.DNSDomainNotFound, dns_exc.DuplicateRecordSet) as e:
LOG.exception("Error publishing floating IP data in external "
"DNS service. Name: '%(name)s'. Domain: "

View File

@ -13,7 +13,6 @@
# under the License.
import functools
import itertools
import random
import netaddr
@ -31,7 +30,6 @@ from neutron_lib.plugins import directory
from neutron_lib.services import base as base_services
from oslo_log import log as logging
from oslo_utils import uuidutils
import six
from sqlalchemy import orm
from sqlalchemy.orm import exc
@ -50,6 +48,7 @@ from neutron.db import models_v2
from neutron.db import standardattrdescription_db as st_attr
from neutron.extensions import l3
from neutron.extensions import qos_fip
from neutron.objects import base as base_obj
from neutron.objects import ports as port_obj
from neutron.objects import router as l3_obj
from neutron.plugins.common import utils as p_utils
@ -158,14 +157,15 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
port_id)
def _fix_or_kill_floating_port(self, context, port_id):
fip = (context.session.query(l3_models.FloatingIP).
filter_by(floating_port_id=port_id).first())
if fip:
pager = base_obj.Pager(limit=1)
fips = l3_obj.FloatingIP.get_objects(
context, _pager=pager, floating_port_id=port_id)
if fips:
LOG.warning("Found incorrect device_id on floating port "
"%(pid)s, correcting to %(fip)s.",
{'pid': port_id, 'fip': fip.id})
{'pid': port_id, 'fip': fips[0].id})
self._core_plugin.update_port(
context, port_id, {'port': {'device_id': fip.id}})
context, port_id, {'port': {'device_id': fips[0].id}})
else:
LOG.warning("Found floating IP port %s without floating IP, "
"deleting.", port_id)
@ -912,7 +912,6 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
subnet_id):
subnet = self._core_plugin.get_subnet(context, subnet_id)
subnet_cidr = netaddr.IPNetwork(subnet['cidr'])
fip_qry = context.session.query(l3_models.FloatingIP)
try:
kwargs = {'context': context, 'router_id': router_id,
'subnet_id': subnet_id}
@ -924,8 +923,9 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
if len(e.errors) == 1:
raise e.errors[0].error
raise l3.RouterInUse(router_id=router_id, reason=e)
for fip_db in fip_qry.filter_by(router_id=router_id):
if netaddr.IPAddress(fip_db['fixed_ip_address']) in subnet_cidr:
fip_objs = l3_obj.FloatingIP.get_objects(context, router_id=router_id)
for fip_obj in fip_objs:
if fip_obj.fixed_ip_address in subnet_cidr:
raise l3.RouterInterfaceInUseByFloatingIP(
router_id=router_id, subnet_id=subnet_id)
@ -1039,28 +1039,32 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
subnets])
def _get_floatingip(self, context, id):
try:
floatingip = model_query.get_by_id(
context, l3_models.FloatingIP, id)
except exc.NoResultFound:
floatingip = l3_obj.FloatingIP.get_object(context, id=id)
if not floatingip:
raise l3.FloatingIPNotFound(floatingip_id=id)
return floatingip
def _make_floatingip_dict(self, floatingip, fields=None,
process_extensions=True):
res = {'id': floatingip['id'],
'tenant_id': floatingip['tenant_id'],
'floating_ip_address': floatingip['floating_ip_address'],
'floating_network_id': floatingip['floating_network_id'],
'router_id': floatingip['router_id'],
'port_id': floatingip['fixed_port_id'],
'fixed_ip_address': floatingip['fixed_ip_address'],
'status': floatingip['status']}
floating_ip_address = (str(floatingip.floating_ip_address)
if floatingip.floating_ip_address else None)
fixed_ip_address = (str(floatingip.fixed_ip_address)
if floatingip.fixed_ip_address else None)
res = {'id': floatingip.id,
'tenant_id': floatingip.project_id,
'floating_ip_address': floating_ip_address,
'floating_network_id': floatingip.floating_network_id,
'router_id': floatingip.router_id,
'port_id': floatingip.fixed_port_id,
'fixed_ip_address': fixed_ip_address,
'status': floatingip.status}
# NOTE(mlavalle): The following assumes this mixin is used in a
# class inheriting from CommonDbMixin, which is true for all existing
# plugins.
# TODO(lujinluo): Change floatingip.db_obj to floatingip once all
# codes are migrated to use Floating IP OVO object.
if process_extensions:
resource_extend.apply_funcs(l3.FLOATINGIPS, res, floatingip)
resource_extend.apply_funcs(l3.FLOATINGIPS, res, floatingip.db_obj)
return db_utils.resource_fields(res, fields)
def _get_router_for_floatingip(self, context, internal_port,
@ -1166,7 +1170,7 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
internal_subnet_id = ipv4_fixed_ips[0]['subnet_id']
return internal_port, internal_subnet_id, internal_ip_address
def _get_assoc_data(self, context, fip, floatingip_db):
def _get_assoc_data(self, context, fip, floatingip_obj):
"""Determine/extract data associated with the internal port.
When a floating IP is associated with an internal port,
@ -1177,14 +1181,14 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
"""
(internal_port, internal_subnet_id,
internal_ip_address) = self._internal_fip_assoc_data(
context, fip, floatingip_db['tenant_id'])
context, fip, floatingip_obj.project_id)
router_id = self._get_router_for_floatingip(
context, internal_port,
internal_subnet_id, floatingip_db['floating_network_id'])
internal_subnet_id, floatingip_obj.floating_network_id)
return (fip['port_id'], internal_ip_address, router_id)
def _check_and_get_fip_assoc(self, context, fip, floatingip_db):
def _check_and_get_fip_assoc(self, context, fip, floatingip_obj):
port_id = internal_ip_address = router_id = None
if fip.get('fixed_ip_address') and not fip.get('port_id'):
msg = _("fixed_ip_address cannot be specified without a port_id")
@ -1193,57 +1197,60 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
port_id, internal_ip_address, router_id = self._get_assoc_data(
context,
fip,
floatingip_db)
floatingip_obj)
if port_id == floatingip_db.fixed_port_id:
if port_id == floatingip_obj.fixed_port_id:
# Floating IP association is not changed.
return port_id, internal_ip_address, router_id
fip_qry = context.session.query(l3_models.FloatingIP)
try:
fip_qry.filter_by(
fip_exists = l3_obj.FloatingIP.objects_exist(
context,
fixed_port_id=fip['port_id'],
floating_network_id=floatingip_db['floating_network_id'],
fixed_ip_address=internal_ip_address).one()
floating_network_id=floatingip_obj.floating_network_id,
fixed_ip_address=netaddr.IPAddress(internal_ip_address))
if fip_exists:
floating_ip_address = (str(floatingip_obj.floating_ip_address)
if floatingip_obj.floating_ip_address else None)
raise l3.FloatingIPPortAlreadyAssociated(
port_id=fip['port_id'],
fip_id=floatingip_db['id'],
floating_ip_address=floatingip_db['floating_ip_address'],
fip_id=floatingip_obj.id,
floating_ip_address=floating_ip_address,
fixed_ip=internal_ip_address,
net_id=floatingip_db['floating_network_id'])
except exc.NoResultFound:
pass
net_id=floatingip_obj.floating_network_id)
if fip and 'port_id' not in fip and floatingip_db.fixed_port_id:
if fip and 'port_id' not in fip and floatingip_obj.fixed_port_id:
# NOTE(liuyulong): without the fix of bug #1610045 here could
# also let floating IP can be dissociated with an empty
# updating dict.
fip['port_id'] = floatingip_db.fixed_port_id
fip['port_id'] = floatingip_obj.fixed_port_id
port_id, internal_ip_address, router_id = self._get_assoc_data(
context, fip, floatingip_db)
context, fip, floatingip_obj)
# After all upper conditions, if updating API dict is submitted with
# {'port_id': null}, then the floating IP cloud also be dissociated.
return port_id, internal_ip_address, router_id
def _update_fip_assoc(self, context, fip, floatingip_db, external_port):
previous_router_id = floatingip_db.router_id
def _update_fip_assoc(self, context, fip, floatingip_obj, external_port):
previous_router_id = floatingip_obj.router_id
port_id, internal_ip_address, router_id = (
self._check_and_get_fip_assoc(context, fip, floatingip_db))
update = {'fixed_ip_address': internal_ip_address,
'fixed_port_id': port_id,
'router_id': router_id,
'last_known_router_id': previous_router_id}
self._check_and_get_fip_assoc(context, fip, floatingip_obj))
floatingip_obj.fixed_ip_address = (
netaddr.IPAddress(internal_ip_address)
if internal_ip_address else None)
floatingip_obj.fixed_port_id = port_id
floatingip_obj.router_id = router_id
floatingip_obj.last_known_router_id = previous_router_id
if 'description' in fip:
update['description'] = fip['description']
floatingip_db.update(update)
floatingip_obj.description = fip['description']
floating_ip_address = (str(floatingip_obj.floating_ip_address)
if floatingip_obj.floating_ip_address else None)
return {'fixed_ip_address': internal_ip_address,
'fixed_port_id': port_id,
'router_id': router_id,
'last_known_router_id': previous_router_id,
'floating_ip_address': floatingip_db.floating_ip_address,
'floating_network_id': floatingip_db.floating_network_id,
'floating_ip_id': floatingip_db.id,
'floating_ip_address': floating_ip_address,
'floating_network_id': floatingip_obj.floating_network_id,
'floating_ip_id': floatingip_obj.id,
'context': context}
def _is_ipv4_network(self, context, net_id):
@ -1301,9 +1308,10 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
floating_fixed_ip = external_ipv4_ips[0]
floating_ip_address = floating_fixed_ip['ip_address']
floatingip_db = l3_models.FloatingIP(
floatingip_obj = l3_obj.FloatingIP(
context,
id=fip_id,
tenant_id=fip['tenant_id'],
project_id=fip['tenant_id'],
status=initial_status,
floating_network_id=fip['floating_network_id'],
floating_ip_address=floating_ip_address,
@ -1311,16 +1319,19 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
description=fip.get('description'))
# Update association with internal port
# and define external IP address
assoc_result = self._update_fip_assoc(context, fip,
floatingip_db, external_port)
context.session.add(floatingip_db)
assoc_result = self._update_fip_assoc(
context, fip, floatingip_obj, external_port)
floatingip_obj.create()
floatingip_dict = self._make_floatingip_dict(
floatingip_db, process_extensions=False)
floatingip_obj, process_extensions=False)
if self._is_dns_integration_supported:
dns_data = self._process_dns_floatingip_create_precommit(
context, floatingip_dict, fip)
if self._is_fip_qos_supported:
self._process_extra_fip_qos_create(context, fip_id, fip)
floatingip_obj = l3_obj.FloatingIP.get_object(
context, id=floatingip_obj.id)
floatingip_db = floatingip_obj.db_obj
registry.notify(resources.FLOATING_IP, events.PRECOMMIT_CREATE,
self, context=context, floatingip=fip,
@ -1338,6 +1349,8 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
self._process_dns_floatingip_create_postcommit(context,
floatingip_dict,
dns_data)
# TODO(lujinluo): Change floatingip_db to floatingip_obj once all
# codes are migrated to use Floating IP OVO object.
resource_extend.apply_funcs(l3.FLOATINGIPS, floatingip_dict,
floatingip_db)
return floatingip_dict
@ -1350,21 +1363,26 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
def _update_floatingip(self, context, id, floatingip):
fip = floatingip['floatingip']
with context.session.begin(subtransactions=True):
floatingip_db = self._get_floatingip(context, id)
old_floatingip = self._make_floatingip_dict(floatingip_db)
fip_port_id = floatingip_db['floating_port_id']
floatingip_obj = self._get_floatingip(context, id)
old_floatingip = self._make_floatingip_dict(floatingip_obj)
fip_port_id = floatingip_obj.floating_port_id
assoc_result = self._update_fip_assoc(
context, fip, floatingip_db,
context, fip, floatingip_obj,
self._core_plugin.get_port(context.elevated(), fip_port_id))
floatingip_dict = self._make_floatingip_dict(floatingip_db)
floatingip_obj.update()
floatingip_dict = self._make_floatingip_dict(floatingip_obj)
if self._is_dns_integration_supported:
dns_data = self._process_dns_floatingip_update_precommit(
context, floatingip_dict)
if self._is_fip_qos_supported:
self._process_extra_fip_qos_update(context,
floatingip_db,
floatingip_obj,
fip,
old_floatingip)
floatingip_obj = l3_obj.FloatingIP.get_object(
context, id=floatingip_obj.id)
floatingip_db = floatingip_obj.db_obj
registry.notify(resources.FLOATING_IP,
events.AFTER_UPDATE,
self._update_fip_assoc,
@ -1374,6 +1392,8 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
self._process_dns_floatingip_update_postcommit(context,
floatingip_dict,
dns_data)
# TODO(lujinluo): Change floatingip_db to floatingip_obj once all
# codes are migrated to use Floating IP OVO object.
resource_extend.apply_funcs(l3.FLOATINGIPS, floatingip_dict,
floatingip_db)
return old_floatingip, floatingip_dict
@ -1392,10 +1412,8 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
@db_api.retry_if_session_inactive()
def update_floatingip_status(self, context, floatingip_id, status):
"""Update operational status for floating IP in neutron DB."""
fip_query = model_query.query_with_hooks(
context, l3_models.FloatingIP).filter(
l3_models.FloatingIP.id == floatingip_id)
fip_query.update({'status': status}, synchronize_session=False)
l3_obj.FloatingIP.update_objects(
context, {'status': status}, id=floatingip_id)
def _delete_floatingip(self, context, id):
floatingip = self._get_floatingip(context, id)
@ -1407,7 +1425,7 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
# a transaction first to remove it ourselves because the delete_port
# method will yield in its post-commit activities.
self._core_plugin.delete_port(context.elevated(),
floatingip['floating_port_id'],
floatingip.floating_port_id,
l3_port_check=False)
return floatingip_dict
@ -1424,34 +1442,32 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
def get_floatingips(self, context, filters=None, fields=None,
sorts=None, limit=None, marker=None,
page_reverse=False):
marker_obj = db_utils.get_marker_obj(self, context, 'floatingip',
limit, marker)
if filters is not None:
for key, val in API_TO_DB_COLUMN_MAP.items():
if key in filters:
filters[val] = filters.pop(key)
return model_query.get_collection(context, l3_models.FloatingIP,
self._make_floatingip_dict,
filters=filters, fields=fields,
sorts=sorts,
limit=limit,
marker_obj=marker_obj,
page_reverse=page_reverse)
pager = base_obj.Pager(sorts, limit, page_reverse, marker)
filters = filters or {}
for key, val in API_TO_DB_COLUMN_MAP.items():
if key in filters:
filters[val] = filters.pop(key)
floatingip_objs = l3_obj.FloatingIP.get_objects(
context, _pager=pager, validate_filters=False, **filters)
floatingip_dicts = [
self._make_floatingip_dict(floatingip_obj, fields)
for floatingip_obj in floatingip_objs
]
return floatingip_dicts
@db_api.retry_if_session_inactive()
def delete_disassociated_floatingips(self, context, network_id):
query = model_query.query_with_hooks(context, l3_models.FloatingIP)
query = query.filter_by(floating_network_id=network_id,
fixed_port_id=None,
router_id=None)
for fip in query:
fip_objs = l3_obj.FloatingIP.get_objects(
context,
floating_network_id=network_id, router_id=None, fixed_port_id=None)
for fip in fip_objs:
self.delete_floatingip(context, fip.id)
@db_api.retry_if_session_inactive()
def get_floatingips_count(self, context, filters=None):
return model_query.get_collection_count(context, l3_models.FloatingIP,
filters=filters)
filters = filters or {}
return l3_obj.FloatingIP.count(context, **filters)
def _router_exists(self, context, router_id):
try:
@ -1460,13 +1476,6 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
except l3.RouterNotFound:
return False
def _floating_ip_exists(self, context, floating_ip_id):
try:
self.get_floatingip(context, floating_ip_id)
return True
except l3.FloatingIPNotFound:
return False
def prevent_l3_port_deletion(self, context, port_id):
"""Checks to make sure a port is allowed to be deleted.
@ -1498,7 +1507,8 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
# error during deletion.
# Elevated context in case router is owned by another tenant
if port['device_owner'] == DEVICE_OWNER_FLOATINGIP:
if not self._floating_ip_exists(context, port['device_id']):
if not l3_obj.FloatingIP.objects_exist(
context, id=port['device_id']):
LOG.debug("Floating IP %(f_id)s corresponding to port "
"%(port_id)s no longer exists, allowing deletion.",
{'f_id': port['device_id'], 'port_id': port['id']})
@ -1523,21 +1533,20 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
This parameter is ignored.
@return: set of router-ids that require notification updates
"""
router_ids = set()
with context.session.begin(subtransactions=True):
for floating_ip in self._get_floatingips_by_port_id(
context, port_id):
router_ids.add(floating_ip['router_id'])
floating_ip.update({'fixed_port_id': None,
'fixed_ip_address': None,
'router_id': None})
return router_ids
floating_ip_objs = l3_obj.FloatingIP.get_objects(
context, fixed_port_id=port_id)
router_ids = {fip.router_id for fip in floating_ip_objs}
values = {'fixed_port_id': None,
'fixed_ip_address': None,
'router_id': None}
l3_obj.FloatingIP.update_objects(
context, values, fixed_port_id=port_id)
return router_ids
def _get_floatingips_by_port_id(self, context, port_id):
"""Helper function to retrieve the fips associated with a port_id."""
fip_qry = context.session.query(l3_models.FloatingIP)
return fip_qry.filter_by(fixed_port_id=port_id).all()
return l3_obj.FloatingIP.get_objects(context, fixed_port_id=port_id)
def _build_routers_list(self, context, routers, gw_ports):
"""Subclasses can override this to add extra gateway info"""
@ -1576,20 +1585,8 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
if r.get('gw_port'))
return self._build_routers_list(context, router_dicts, gw_ports)
@staticmethod
def _unique_floatingip_iterator(query):
"""Iterates over only one row per floating ip. Ignores others."""
# Group rows by fip id. They must be sorted by same.
q = query.order_by(l3_models.FloatingIP.id)
keyfunc = lambda row: row[0]['id']
group_iterator = itertools.groupby(q, keyfunc)
# Just hit the first row of each group
for key, value in group_iterator:
yield six.next(value)
def _make_floatingip_dict_with_scope(self, floatingip_db, scope_id):
d = self._make_floatingip_dict(floatingip_db)
def _make_floatingip_dict_with_scope(self, floatingip_obj, scope_id):
d = self._make_floatingip_dict(floatingip_obj)
d['fixed_ip_address_scope'] = scope_id
return d
@ -1606,22 +1603,11 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
if not router_ids:
return []
query = context.session.query(l3_models.FloatingIP,
models_v2.SubnetPool.address_scope_id)
query = query.join(models_v2.Port,
l3_models.FloatingIP.fixed_port_id == models_v2.Port.id)
# Outer join of Subnet can cause each ip to have more than one row.
query = query.outerjoin(models_v2.Subnet,
models_v2.Subnet.network_id == models_v2.Port.network_id)
query = query.filter(models_v2.Subnet.ip_version == 4)
query = query.outerjoin(models_v2.SubnetPool,
models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id)
# Filter out on router_ids
query = query.filter(l3_models.FloatingIP.router_id.in_(router_ids))
return [self._make_floatingip_dict_with_scope(*row)
for row in self._unique_floatingip_iterator(query)]
return [
self._make_floatingip_dict_with_scope(*scoped_fip)
for scoped_fip in l3_obj.FloatingIP.get_scoped_floating_ips(
context, router_ids)
]
def _get_sync_interfaces(self, context, router_ids, device_owners=None):
"""Query router interfaces that relate to list of router_ids."""

View File

@ -13,6 +13,7 @@
# under the License.
import collections
import netaddr
from neutron_lib.api.definitions import portbindings
from neutron_lib.api import validators
from neutron_lib.callbacks import events
@ -37,11 +38,11 @@ from neutron.db import api as db_api
from neutron.db import l3_attrs_db
from neutron.db import l3_db
from neutron.db.models import allowed_address_pair as aap_models
from neutron.db.models import l3 as l3_models
from neutron.db import models_v2
from neutron.extensions import l3
from neutron.ipam import utils as ipam_utils
from neutron.objects import agent as ag_obj
from neutron.objects import base as base_obj
from neutron.objects import l3agent as rb_obj
from neutron.objects import router as l3_obj
from neutron.plugins.common import utils as p_utils
@ -959,11 +960,11 @@ class _DVRAgentInterfaceMixin(object):
(port_dict['status'] == const.PORT_STATUS_ACTIVE))
if not port_valid_state:
return
query = context.session.query(l3_models.FloatingIP).filter(
l3_models.FloatingIP.fixed_ip_address == port_addr_pair_ip)
fip = query.first()
fips = l3_obj.FloatingIP.get_objects(
context, _pager=base_obj.Pager(limit=1),
fixed_ip_address=netaddr.IPAddress(port_addr_pair_ip))
return self._core_plugin.get_port(
context, fip.fixed_port_id) if fip else None
context, fips[0].fixed_port_id) if fips else None
class L3_NAT_with_dvr_db_mixin(_DVRAgentInterfaceMixin,

View File

@ -59,7 +59,7 @@ class FloatingQoSDbMixin(object):
self._create_fip_qos_db(context, fip_id, qos_policy_id)
def _process_extra_fip_qos_update(
self, context, floatingip_db, fip, old_floatingip):
self, context, floatingip_obj, fip, old_floatingip):
if qos_consts.QOS_POLICY_ID not in fip:
# No qos_policy_id in API input, do nothing
return
@ -71,15 +71,16 @@ class FloatingQoSDbMixin(object):
return
if old_qos_policy_id:
self._delete_fip_qos_db(context,
floatingip_db['id'],
floatingip_obj['id'],
old_qos_policy_id)
if floatingip_db.qos_policy_binding:
floatingip_db.qos_policy_binding['policy_id'] = new_qos_policy_id
if floatingip_obj.db_obj.qos_policy_binding:
floatingip_obj.db_obj.qos_policy_binding['policy_id'] = (
new_qos_policy_id)
if not new_qos_policy_id:
return
qos_policy_binding = self._create_fip_qos_db(
context,
floatingip_db['id'],
floatingip_obj['id'],
new_qos_policy_id)
if not floatingip_db.qos_policy_binding:
floatingip_db.qos_policy_binding = qos_policy_binding
if not floatingip_obj.db_obj.qos_policy_binding:
floatingip_obj.db_obj.qos_policy_binding = qos_policy_binding

View File

@ -26,6 +26,7 @@ class FloatingIPDNS(base.NeutronDbObject):
db_model = models.FloatingIPDNS
primary_keys = ['floatingip_id']
foreign_keys = {'FloatingIP': {'floatingip_id': 'id'}}
fields = {
'floatingip_id': common_types.UUIDField(),

View File

@ -10,11 +10,14 @@
# License for the specific language governing permissions and limitations
# under the License.
import itertools
import netaddr
from neutron_lib.api.definitions import availability_zone as az_def
from neutron_lib.api.validators import availability_zone as az_validator
from oslo_versionedobjects import fields as obj_fields
import six
from sqlalchemy import func
from neutron.common import constants as n_const
@ -222,9 +225,11 @@ class FloatingIP(base.NeutronDbObject):
'router_id': common_types.UUIDField(nullable=True),
'last_known_router_id': common_types.UUIDField(nullable=True),
'status': common_types.FloatingIPStatusEnumField(nullable=True),
'dns': obj_fields.ObjectField('FloatingIPDNS', nullable=True),
}
fields_no_update = ['project_id', 'floating_ip_address',
'floating_network_id', 'floating_port_id']
synthetic_fields = ['dns']
@classmethod
def modify_fields_from_db(cls, db_obj):
@ -241,9 +246,40 @@ class FloatingIP(base.NeutronDbObject):
def modify_fields_to_db(cls, fields):
result = super(FloatingIP, cls).modify_fields_to_db(fields)
if 'fixed_ip_address' in result:
result['fixed_ip_address'] = cls.filter_to_str(
result['fixed_ip_address'])
if result['fixed_ip_address'] is not None:
result['fixed_ip_address'] = cls.filter_to_str(
result['fixed_ip_address'])
if 'floating_ip_address' in result:
result['floating_ip_address'] = cls.filter_to_str(
result['floating_ip_address'])
return result
@classmethod
def get_scoped_floating_ips(cls, context, router_ids):
query = context.session.query(l3.FloatingIP,
models_v2.SubnetPool.address_scope_id)
query = query.join(models_v2.Port,
l3.FloatingIP.fixed_port_id == models_v2.Port.id)
# Outer join of Subnet can cause each ip to have more than one row.
query = query.outerjoin(models_v2.Subnet,
models_v2.Subnet.network_id == models_v2.Port.network_id)
query = query.filter(models_v2.Subnet.ip_version == 4)
query = query.outerjoin(models_v2.SubnetPool,
models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id)
# Filter out on router_ids
query = query.filter(l3.FloatingIP.router_id.in_(router_ids))
return cls._unique_floatingip_iterator(context, query)
@classmethod
def _unique_floatingip_iterator(cls, context, query):
"""Iterates over only one row per floating ip. Ignores others."""
# Group rows by fip id. They must be sorted by same.
q = query.order_by(l3.FloatingIP.id)
keyfunc = lambda row: row[0]['id']
group_iterator = itertools.groupby(q, keyfunc)
# Just hit the first row of each group
for key, value in group_iterator:
row = [r for r in six.next(value)]
yield (cls._load_object(context, row[0]), row[1])

View File

@ -6276,11 +6276,11 @@ class DbModelMixin(object):
return sg, rule
def _make_floating_ip(self, ctx, port_id):
with db_api.context_manager.writer.using(ctx):
flip = l3_models.FloatingIP(floating_ip_address='1.2.3.4',
floating_network_id='somenet',
floating_port_id=port_id)
ctx.session.add(flip)
flip = l3_obj.FloatingIP(
ctx, floating_ip_address=netaddr.IPAddress('1.2.3.4'),
floating_network_id=uuidutils.generate_uuid(),
floating_port_id=port_id)
flip.create()
return flip
def _make_router(self, ctx):
@ -6343,7 +6343,7 @@ class DbModelMixin(object):
port = self._make_port(ctx, network.id)
flip = self._make_floating_ip(ctx, port.id)
self._test_staledata_error_on_concurrent_object_update(
l3_models.FloatingIP, flip['id'])
flip.db_model, flip.id)
def test_staledata_error_on_concurrent_object_update_sg(self):
ctx = context.get_admin_context()
@ -6434,7 +6434,9 @@ class DbModelMixin(object):
network = self._make_network(ctx)
port = self._make_port(ctx, network.id)
flip = self._make_floating_ip(ctx, port.id)
self._test_standardattr_removed_on_obj_delete(ctx, flip)
# TODO(lujinluo): Change flip.db_obj to flip once all
# codes are migrated to use Floating IP OVO object.
self._test_standardattr_removed_on_obj_delete(ctx, flip.db_obj)
def test_standardattr_removed_on_router_delete(self):
ctx = context.get_admin_context()

View File

@ -14,17 +14,21 @@
# limitations under the License.
import mock
import netaddr
from neutron_lib.callbacks import events
from neutron_lib.callbacks import registry
from neutron_lib.callbacks import resources
from neutron_lib import constants as n_const
from neutron_lib import context
from neutron_lib import exceptions as n_exc
from neutron_lib.plugins import directory
from oslo_utils import uuidutils
import testtools
from neutron.db import l3_db
from neutron.db.models import l3 as l3_models
from neutron.extensions import l3
from neutron.objects import router as l3_obj
from neutron.tests import base
@ -131,6 +135,7 @@ class TestL3_NAT_dbonly_mixin(base.BaseTestCase):
'id': mock.sentinel.fip_ip}, result)
def test__unique_floatingip_iterator(self):
context = mock.MagicMock()
query = mock.MagicMock()
query.order_by().__iter__.return_value = [
({'id': 'id1'}, 'scope1'),
@ -140,12 +145,15 @@ class TestL3_NAT_dbonly_mixin(base.BaseTestCase):
({'id': 'id2'}, 'scope2'),
({'id': 'id3'}, 'scope3')]
query.reset_mock()
result = list(
l3_db.L3_NAT_dbonly_mixin._unique_floatingip_iterator(query))
query.order_by.assert_called_once_with(l3_models.FloatingIP.id)
self.assertEqual([({'id': 'id1'}, 'scope1'),
({'id': 'id2'}, 'scope2'),
({'id': 'id3'}, 'scope3')], result)
with mock.patch.object(
l3_obj.FloatingIP, '_load_object',
side_effect=({'id': 'id1'}, {'id': 'id2'}, {'id': 'id3'})):
result = list(
l3_obj.FloatingIP._unique_floatingip_iterator(context, query))
query.order_by.assert_called_once_with(l3_models.FloatingIP.id)
self.assertEqual([({'id': 'id1'}, 'scope1'),
({'id': 'id2'}, 'scope2'),
({'id': 'id3'}, 'scope3')], result)
@mock.patch.object(directory, 'get_plugin')
def test_prevent_l3_port_deletion_port_not_found(self, gp):
@ -191,13 +199,16 @@ class TestL3_NAT_dbonly_mixin(base.BaseTestCase):
@mock.patch.object(directory, 'get_plugin')
def test_prevent_l3_port_existing_floating_ip(self, gp):
ctx = context.get_admin_context()
gp.return_value.get_port.return_value = {
'device_owner': n_const.DEVICE_OWNER_FLOATINGIP,
'device_id': 'some_flip', 'id': 'f',
'fixed_ips': [{'ip_address': '1.1.1.1', 'subnet_id': '4'}]}
self.db.get_floatingip = mock.Mock()
with testtools.ExpectedException(n_exc.ServicePortInUse):
self.db.prevent_l3_port_deletion(mock.Mock(), None)
with mock.patch.object(l3_obj.FloatingIP, 'objects_exist',
return_value=mock.Mock()),\
testtools.ExpectedException(n_exc.ServicePortInUse):
self.db.prevent_l3_port_deletion(ctx, None)
@mock.patch.object(directory, 'get_plugin')
def test_subscribe_address_scope_of_subnetpool(self, gp):
@ -211,20 +222,21 @@ class TestL3_NAT_dbonly_mixin(base.BaseTestCase):
def test__check_and_get_fip_assoc_with_extra_association_no_change(self):
fip = {'extra_key': 'value'}
context = mock.MagicMock()
floatingip_db = l3_models.FloatingIP(
id='fake_fip_id',
floating_network_id='fake_floating_network_id',
floating_ip_address='8.8.8.8',
fixed_port_id='fake_fixed_port_id',
floating_port_id='fake_floating_port_id')
floatingip_obj = l3_obj.FloatingIP(
context,
id=uuidutils.generate_uuid(),
floating_network_id=uuidutils.generate_uuid(),
floating_ip_address=netaddr.IPAddress('8.8.8.8'),
fixed_port_id=uuidutils.generate_uuid(),
floating_port_id=uuidutils.generate_uuid())
with mock.patch.object(
l3_db.L3_NAT_dbonly_mixin,
'_get_assoc_data',
return_value=('1', '2', '3')) as mock_get_assoc_data:
self.db._check_and_get_fip_assoc(context, fip, floatingip_db)
self.db._check_and_get_fip_assoc(context, fip, floatingip_obj)
context.session.query.assert_not_called()
mock_get_assoc_data.assert_called_once_with(
mock.ANY, fip, floatingip_db)
mock.ANY, fip, floatingip_obj)
def test__notify_attaching_interface(self):
with mock.patch.object(l3_db.registry, 'notify') as mock_notify:

View File

@ -3905,22 +3905,6 @@ class TestL3DbOperationBounds(test_db_base_plugin_v2.DbOperationBoundMixin,
self._assert_object_list_queries_constant(router_maker, 'routers')
def test_floatingip_list_queries_constant(self):
with self.floatingip_with_assoc(**self.kwargs) as flip:
internal_port = self._show('ports', flip['floatingip']['port_id'])
internal_net_id = internal_port['port']['network_id']
def float_maker():
port = self._make_port(
self.fmt, internal_net_id, **self.kwargs)
return self._make_floatingip(
self.fmt, flip['floatingip']['floating_network_id'],
port_id=port['port']['id'],
**self.kwargs)
self._assert_object_list_queries_constant(float_maker,
'floatingips')
class TestL3DbOperationBoundsTenant(TestL3DbOperationBounds):
admin = False

View File

@ -37,6 +37,7 @@ from neutron.db.models import l3 as l3_models
from neutron.extensions import l3
from neutron.objects import network as net_obj
from neutron.objects import ports as port_obj
from neutron.objects import router as l3_obj
from neutron.objects import subnet as subnet_obj
from neutron.tests import base
from neutron.tests.unit.db import test_db_base_plugin_v2
@ -226,9 +227,10 @@ class TestL3GwModeMixin(testlib_api.SqlTestCase):
network_id=self.int_net.id,
subnet_id=self.int_sub_id,
ip_address='3.3.3.3')
self.fip = l3_models.FloatingIP(
self.fip = l3_obj.FloatingIP(
self.context,
id=_uuid(),
floating_ip_address='1.1.1.2',
floating_ip_address=netaddr.IPAddress('1.1.1.2'),
floating_network_id=self.ext_net_id,
floating_port_id=FAKE_FIP_EXT_PORT_ID,
fixed_port_id=None,
@ -236,7 +238,7 @@ class TestL3GwModeMixin(testlib_api.SqlTestCase):
router_id=None)
self.fip_int_port.create()
self.fip_int_ip_info.create()
self.context.session.add(self.fip)
self.fip.create()
self.context.session.flush()
self.context.session.expire_all()
self.fip_request = {'port_id': FAKE_FIP_INT_PORT_ID,

View File

@ -32,7 +32,6 @@ from oslo_versionedobjects import fields as obj_fields
import testtools
from neutron.db import _model_query as model_query
from neutron.db.models import l3 as l3_model
from neutron.db import standard_attr
from neutron import objects
from neutron.objects import agent
@ -1448,16 +1447,17 @@ class BaseDbObjectTestCase(_BaseObjectTestCase,
def _create_test_fip_id(self, fip_id=None):
fake_fip = '172.23.3.0'
ext_net_id = self._create_external_network_id()
# TODO(manjeets) replace this with fip ovo
# once it is implemented
values = {'floating_ip_address': fake_fip,
'floating_network_id': ext_net_id,
'floating_port_id': self._create_test_port_id(
network_id=ext_net_id)}
values = {
'floating_ip_address': netaddr.IPAddress(fake_fip),
'floating_network_id': ext_net_id,
'floating_port_id': self._create_test_port_id(
network_id=ext_net_id)
}
if fip_id:
values['id'] = fip_id
return obj_db_api.create_object(
self.context, l3_model.FloatingIP, values).id
fip_obj = router.FloatingIP(self.context, **values)
fip_obj.create()
return fip_obj.id
def _create_test_subnet_id(self, network_id=None):
if not network_id:

View File

@ -39,7 +39,7 @@ object_data = {
'FlatAllocation': '1.0-bf666f24f4642b047eeca62311fbcb41',
'Flavor': '1.0-82194de5c9aafce08e8527bb7977f5c6',
'FlavorServiceProfileBinding': '1.0-a2c8731e16cefdac4571f80abf1f8930',
'FloatingIP': '1.0-ea69515cfe08b5efc0600e6446efe64f',
'FloatingIP': '1.0-0205cc99ec79e8089d641ed1b565ddae',
'FloatingIPDNS': '1.0-ee3db848500fa1825235f701828c06d5',
'GeneveAllocation': '1.0-d5f76e8eac60a778914d61dd8e23e90f',
'GeneveEndpoint': '1.0-040f026996b5952e2ae4ccd40ac61ca6',