bf35cf65c8
This patch implements the last code bits pending to conclude the new DB engine facade migration. Due to the resultant interactions in the modified code, is not possible to submit smaller patches; this code must be migrated at once. Partially-Implements blueprint: enginefacade-switch Signed-off-by: Slawek Kaplonski <skaplons@redhat.com> Co-Authored-By: Rodolfo Alonso Hernandez <ralonsoh@redhat.com> Change-Id: Id3f09b78c8d0a8daa7ec4fa6f5bf79f7d5ab8f8b
373 lines
13 KiB
Python
373 lines
13 KiB
Python
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
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 neutron_lib import constants as n_const
|
|
from neutron_lib.objects import common_types
|
|
from neutron_lib.utils import net as net_utils
|
|
from oslo_utils import versionutils
|
|
from oslo_versionedobjects import fields as obj_fields
|
|
from sqlalchemy import func
|
|
|
|
from neutron.db.models import dvr as dvr_models
|
|
from neutron.db.models import l3
|
|
from neutron.db.models import l3_attrs
|
|
from neutron.db.models import l3agent as rb_model
|
|
from neutron.db import models_v2
|
|
from neutron.objects import base
|
|
from neutron.objects.qos import binding as qos_binding
|
|
|
|
|
|
@base.NeutronObjectRegistry.register
|
|
class RouterRoute(base.NeutronDbObject):
|
|
# Version 1.0: Initial version
|
|
VERSION = '1.0'
|
|
|
|
db_model = l3.RouterRoute
|
|
|
|
fields = {
|
|
'router_id': common_types.UUIDField(),
|
|
'destination': common_types.IPNetworkField(),
|
|
'nexthop': obj_fields.IPAddressField()
|
|
}
|
|
|
|
primary_keys = ['router_id', 'destination', 'nexthop']
|
|
foreign_keys = {'Router': {'router_id': 'id'}}
|
|
|
|
@classmethod
|
|
def modify_fields_from_db(cls, db_obj):
|
|
result = super(RouterRoute, cls).modify_fields_from_db(db_obj)
|
|
if 'destination' in result:
|
|
result['destination'] = net_utils.AuthenticIPNetwork(
|
|
result['destination'])
|
|
if 'nexthop' in result:
|
|
result['nexthop'] = netaddr.IPAddress(result['nexthop'])
|
|
return result
|
|
|
|
@classmethod
|
|
def modify_fields_to_db(cls, fields):
|
|
result = super(RouterRoute, cls).modify_fields_to_db(fields)
|
|
if 'destination' in result:
|
|
result['destination'] = cls.filter_to_str(result['destination'])
|
|
if 'nexthop' in result:
|
|
result['nexthop'] = cls.filter_to_str(result['nexthop'])
|
|
return result
|
|
|
|
|
|
@base.NeutronObjectRegistry.register
|
|
class RouterExtraAttributes(base.NeutronDbObject):
|
|
# Version 1.0: Initial version
|
|
VERSION = '1.0'
|
|
|
|
db_model = l3_attrs.RouterExtraAttributes
|
|
|
|
fields = {
|
|
'router_id': common_types.UUIDField(),
|
|
'distributed': obj_fields.BooleanField(default=False),
|
|
'service_router': obj_fields.BooleanField(default=False),
|
|
'ha': obj_fields.BooleanField(default=False),
|
|
'ha_vr_id': obj_fields.IntegerField(nullable=True),
|
|
'availability_zone_hints': obj_fields.ListOfStringsField(nullable=True)
|
|
}
|
|
|
|
primary_keys = ['router_id']
|
|
|
|
foreign_keys = {'Router': {'router_id': 'id'}}
|
|
|
|
@classmethod
|
|
def modify_fields_from_db(cls, db_obj):
|
|
result = super(RouterExtraAttributes, cls).modify_fields_from_db(
|
|
db_obj)
|
|
if az_def.AZ_HINTS in result:
|
|
result[az_def.AZ_HINTS] = (
|
|
az_validator.convert_az_string_to_list(
|
|
result[az_def.AZ_HINTS]))
|
|
return result
|
|
|
|
@classmethod
|
|
def modify_fields_to_db(cls, fields):
|
|
result = super(RouterExtraAttributes, cls).modify_fields_to_db(fields)
|
|
if az_def.AZ_HINTS in result:
|
|
result[az_def.AZ_HINTS] = (
|
|
az_validator.convert_az_list_to_string(
|
|
result[az_def.AZ_HINTS]))
|
|
return result
|
|
|
|
@classmethod
|
|
def get_router_agents_count(cls, context):
|
|
# TODO(sshank): This is pulled out from l3_agentschedulers_db.py
|
|
# until a way to handle joins is figured out.
|
|
binding_model = rb_model.RouterL3AgentBinding
|
|
sub_query = (context.session.query(
|
|
binding_model.router_id,
|
|
func.count(binding_model.router_id).label('count')).
|
|
join(l3_attrs.RouterExtraAttributes,
|
|
binding_model.router_id ==
|
|
l3_attrs.RouterExtraAttributes.router_id).
|
|
join(l3.Router).
|
|
group_by(binding_model.router_id).subquery())
|
|
|
|
query = (context.session.query(l3.Router, sub_query.c.count).
|
|
outerjoin(sub_query))
|
|
|
|
return list(query)
|
|
|
|
|
|
@base.NeutronObjectRegistry.register
|
|
class RouterPort(base.NeutronDbObject):
|
|
# Version 1.0: Initial version
|
|
VERSION = '1.0'
|
|
|
|
db_model = l3.RouterPort
|
|
|
|
primary_keys = ['router_id', 'port_id']
|
|
|
|
foreign_keys = {'Router': {'router_id': 'id'},
|
|
'Port': {'port_id': 'id'}}
|
|
|
|
fields = {
|
|
'router_id': common_types.UUIDField(),
|
|
'port_id': common_types.UUIDField(),
|
|
'port_type': obj_fields.StringField(nullable=True),
|
|
}
|
|
|
|
@classmethod
|
|
def get_router_ids_by_subnetpool(cls, context, subnetpool_id):
|
|
query = context.session.query(l3.RouterPort.router_id)
|
|
query = query.join(models_v2.Port)
|
|
query = query.join(
|
|
models_v2.Subnet,
|
|
models_v2.Subnet.network_id == models_v2.Port.network_id)
|
|
query = query.filter(
|
|
models_v2.Subnet.subnetpool_id == subnetpool_id,
|
|
l3.RouterPort.port_type.in_(n_const.ROUTER_PORT_OWNERS))
|
|
query = query.distinct()
|
|
return [r[0] for r in query]
|
|
|
|
|
|
@base.NeutronObjectRegistry.register
|
|
class DVRMacAddress(base.NeutronDbObject):
|
|
# Version 1.0: Initial version
|
|
VERSION = '1.0'
|
|
|
|
db_model = dvr_models.DistributedVirtualRouterMacAddress
|
|
|
|
primary_keys = ['host']
|
|
|
|
fields = {
|
|
'host': obj_fields.StringField(),
|
|
'mac_address': common_types.MACAddressField()
|
|
}
|
|
|
|
@classmethod
|
|
def modify_fields_from_db(cls, db_obj):
|
|
fields = super(DVRMacAddress, cls).modify_fields_from_db(db_obj)
|
|
if 'mac_address' in fields:
|
|
# NOTE(tonytan4ever): Here uses AuthenticEUI to retain the format
|
|
# passed from API.
|
|
fields['mac_address'] = net_utils.AuthenticEUI(
|
|
fields['mac_address'])
|
|
return fields
|
|
|
|
@classmethod
|
|
def modify_fields_to_db(cls, fields):
|
|
result = super(DVRMacAddress, cls).modify_fields_to_db(fields)
|
|
if 'mac_address' in fields:
|
|
result['mac_address'] = cls.filter_to_str(result['mac_address'])
|
|
return result
|
|
|
|
|
|
@base.NeutronObjectRegistry.register
|
|
class Router(base.NeutronDbObject):
|
|
# Version 1.0: Initial version
|
|
VERSION = '1.0'
|
|
|
|
db_model = l3.Router
|
|
|
|
fields = {
|
|
'id': common_types.UUIDField(),
|
|
'project_id': obj_fields.StringField(nullable=True),
|
|
'name': obj_fields.StringField(nullable=True),
|
|
'status': common_types.RouterStatusEnumField(nullable=True),
|
|
'admin_state_up': obj_fields.BooleanField(nullable=True),
|
|
'gw_port_id': common_types.UUIDField(nullable=True),
|
|
'enable_snat': obj_fields.BooleanField(default=True),
|
|
'flavor_id': common_types.UUIDField(nullable=True),
|
|
'extra_attributes': obj_fields.ObjectField(
|
|
'RouterExtraAttributes', nullable=True),
|
|
}
|
|
|
|
synthetic_fields = ['extra_attributes']
|
|
|
|
fields_no_update = ['project_id']
|
|
|
|
@classmethod
|
|
def check_routers_not_owned_by_projects(cls, context, gw_ports, projects):
|
|
"""This method is to check whether routers that aren't owned by
|
|
existing projects or not
|
|
"""
|
|
|
|
# TODO(hungpv) We may want to implement NOT semantic in get_object(s)
|
|
query = context.session.query(l3.Router).filter(
|
|
l3.Router.gw_port_id.in_(gw_ports))
|
|
|
|
query = query.filter(
|
|
~l3.Router.project_id.in_(projects))
|
|
|
|
return bool(query.count())
|
|
|
|
|
|
@base.NeutronObjectRegistry.register
|
|
class FloatingIP(base.NeutronDbObject):
|
|
# Version 1.0: Initial version
|
|
# Version 1.1: Added qos_policy_id field
|
|
VERSION = '1.1'
|
|
|
|
db_model = l3.FloatingIP
|
|
|
|
fields = {
|
|
'id': common_types.UUIDField(),
|
|
'project_id': obj_fields.StringField(nullable=True),
|
|
'floating_ip_address': obj_fields.IPAddressField(),
|
|
'floating_network_id': common_types.UUIDField(),
|
|
'floating_port_id': common_types.UUIDField(),
|
|
'fixed_port_id': common_types.UUIDField(nullable=True),
|
|
'fixed_ip_address': obj_fields.IPAddressField(nullable=True),
|
|
'qos_policy_id': common_types.UUIDField(nullable=True, default=None),
|
|
'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',
|
|
'qos_policy_id',
|
|
]
|
|
|
|
@classmethod
|
|
def modify_fields_from_db(cls, db_obj):
|
|
result = super(FloatingIP, cls).modify_fields_from_db(db_obj)
|
|
if 'fixed_ip_address' in result:
|
|
result['fixed_ip_address'] = netaddr.IPAddress(
|
|
result['fixed_ip_address'])
|
|
if 'floating_ip_address' in result:
|
|
result['floating_ip_address'] = netaddr.IPAddress(
|
|
result['floating_ip_address'])
|
|
return result
|
|
|
|
@classmethod
|
|
def modify_fields_to_db(cls, fields):
|
|
result = super(FloatingIP, cls).modify_fields_to_db(fields)
|
|
if 'fixed_ip_address' in result:
|
|
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
|
|
|
|
def _attach_qos_policy(self, qos_policy_id):
|
|
qos_binding.QosPolicyFloatingIPBinding.delete_objects(
|
|
self.obj_context, fip_id=self.id)
|
|
if qos_policy_id:
|
|
fip_binding_obj = qos_binding.QosPolicyFloatingIPBinding(
|
|
self.obj_context, policy_id=qos_policy_id, fip_id=self.id)
|
|
fip_binding_obj.create()
|
|
|
|
self.qos_policy_id = qos_policy_id
|
|
self.obj_reset_changes(['qos_policy_id'])
|
|
|
|
def create(self):
|
|
fields = self.obj_get_changes()
|
|
with self.db_context_writer(self.obj_context):
|
|
qos_policy_id = self.qos_policy_id
|
|
super(FloatingIP, self).create()
|
|
if 'qos_policy_id' in fields:
|
|
self._attach_qos_policy(qos_policy_id)
|
|
|
|
def update(self):
|
|
fields = self.obj_get_changes()
|
|
with self.db_context_writer(self.obj_context):
|
|
super(FloatingIP, self).update()
|
|
if 'qos_policy_id' in fields:
|
|
self._attach_qos_policy(fields['qos_policy_id'])
|
|
|
|
def from_db_object(self, db_obj):
|
|
super(FloatingIP, self).from_db_object(db_obj)
|
|
fields_to_change = []
|
|
if db_obj.get('qos_policy_binding'):
|
|
self.qos_policy_id = db_obj.qos_policy_binding.policy_id
|
|
fields_to_change.append('qos_policy_id')
|
|
|
|
self.obj_reset_changes(fields_to_change)
|
|
|
|
def obj_make_compatible(self, primitive, target_version):
|
|
_target_version = versionutils.convert_version_to_tuple(target_version)
|
|
if _target_version < (1, 1):
|
|
primitive.pop('qos_policy_id', None)
|
|
|
|
@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:
|
|
# pylint: disable=stop-iteration-return
|
|
row = list(next(value))
|
|
yield (cls._load_object(context, row[0]), row[1])
|
|
|
|
|
|
@base.NeutronObjectRegistry.register
|
|
class DvrFipGatewayPortAgentBinding(base.NeutronDbObject):
|
|
# Version 1.0: Initial version
|
|
VERSION = '1.0'
|
|
|
|
db_model = dvr_models.DvrFipGatewayPortAgentBinding
|
|
|
|
primary_keys = ['network_id', 'agent_id']
|
|
|
|
fields = {
|
|
'network_id': common_types.UUIDField(),
|
|
'agent_id': common_types.UUIDField(),
|
|
}
|