Merge "Integration of Floating IP OVO"
This commit is contained in:
commit
635d256a30
|
@ -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: "
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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',
|
||||
|
|
Loading…
Reference in New Issue