1f745f82a5
Implements: blueprint cisco-routing-service-vm Change-Id: Ifd021fa06ce34d622e61734aab94b4da32649c4a
578 lines
27 KiB
Python
578 lines
27 KiB
Python
# Copyright 2014 Cisco Systems, Inc. All rights reserved.
|
|
#
|
|
# 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.
|
|
#
|
|
# @author: Bob Melander, Cisco Systems, Inc.
|
|
|
|
import copy
|
|
|
|
from oslo.config import cfg
|
|
from sqlalchemy.orm import exc
|
|
from sqlalchemy.orm import joinedload
|
|
from sqlalchemy.sql import expression as expr
|
|
|
|
from neutron.common import constants as l3_constants
|
|
from neutron.common import exceptions as n_exc
|
|
from neutron.common import rpc as n_rpc
|
|
from neutron import context as n_context
|
|
from neutron.db import extraroute_db
|
|
from neutron.db import l3_db
|
|
from neutron.db import models_v2
|
|
from neutron.extensions import providernet as pr_net
|
|
from neutron.openstack.common import lockutils
|
|
from neutron.openstack.common import log as logging
|
|
from neutron.openstack.common import loopingcall
|
|
from neutron.plugins.cisco.common import cisco_constants as c_const
|
|
from neutron.plugins.cisco.db.l3 import l3_models
|
|
from neutron.plugins.cisco.l3.rpc import l3_router_rpc_joint_agent_api
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
ROUTER_APPLIANCE_OPTS = [
|
|
cfg.IntOpt('backlog_processing_interval',
|
|
default=10,
|
|
help=_('Time in seconds between renewed scheduling attempts of '
|
|
'non-scheduled routers.')),
|
|
]
|
|
|
|
cfg.CONF.register_opts(ROUTER_APPLIANCE_OPTS, "general")
|
|
|
|
|
|
class RouterCreateInternalError(n_exc.NeutronException):
|
|
message = _("Router could not be created due to internal error.")
|
|
|
|
|
|
class RouterInternalError(n_exc.NeutronException):
|
|
message = _("Internal error during router processing.")
|
|
|
|
|
|
class RouterBindingInfoError(n_exc.NeutronException):
|
|
message = _("Could not get binding information for router %(router_id)s.")
|
|
|
|
|
|
class L3RouterApplianceDBMixin(extraroute_db.ExtraRoute_dbonly_mixin):
|
|
"""Mixin class implementing Neutron's routing service using appliances."""
|
|
|
|
# Dictionary of routers for which new scheduling attempts should
|
|
# be made and the refresh setting and heartbeat for that.
|
|
_backlogged_routers = {}
|
|
_refresh_router_backlog = True
|
|
_heartbeat = None
|
|
|
|
@property
|
|
def l3_cfg_rpc_notifier(self):
|
|
if not hasattr(self, '_l3_cfg_rpc_notifier'):
|
|
self._l3_cfg_rpc_notifier = (l3_router_rpc_joint_agent_api.
|
|
L3RouterJointAgentNotifyAPI(self))
|
|
return self._l3_cfg_rpc_notifier
|
|
|
|
@l3_cfg_rpc_notifier.setter
|
|
def l3_cfg_rpc_notifier(self, value):
|
|
self._l3_cfg_rpc_notifier = value
|
|
|
|
def create_router(self, context, router):
|
|
with context.session.begin(subtransactions=True):
|
|
if self.mgmt_nw_id() is None:
|
|
raise RouterCreateInternalError()
|
|
router_created = (super(L3RouterApplianceDBMixin, self).
|
|
create_router(context, router))
|
|
r_hd_b_db = l3_models.RouterHostingDeviceBinding(
|
|
router_id=router_created['id'],
|
|
auto_schedule=True,
|
|
hosting_device_id=None)
|
|
context.session.add(r_hd_b_db)
|
|
# backlog so this new router gets scheduled asynchronously
|
|
self.backlog_router(r_hd_b_db['router'])
|
|
return router_created
|
|
|
|
def update_router(self, context, id, router):
|
|
r = router['router']
|
|
# Check if external gateway has changed so we may have to
|
|
# update trunking
|
|
o_r_db = self._get_router(context, id)
|
|
old_ext_gw = (o_r_db.gw_port or {}).get('network_id')
|
|
new_ext_gw = (r.get('external_gateway_info', {}) or {}).get(
|
|
'network_id')
|
|
with context.session.begin(subtransactions=True):
|
|
e_context = context.elevated()
|
|
if old_ext_gw is not None and old_ext_gw != new_ext_gw:
|
|
o_r = self._make_router_dict(o_r_db, process_extensions=False)
|
|
# no need to schedule now since we're only doing this to
|
|
# tear-down connectivity and there won't be any if not
|
|
# already scheduled.
|
|
self._add_type_and_hosting_device_info(e_context, o_r,
|
|
schedule=False)
|
|
p_drv = self.get_hosting_device_plugging_driver()
|
|
if p_drv is not None:
|
|
p_drv.teardown_logical_port_connectivity(e_context,
|
|
o_r_db.gw_port)
|
|
router_updated = (
|
|
super(L3RouterApplianceDBMixin, self).update_router(
|
|
context, id, router))
|
|
routers = [copy.deepcopy(router_updated)]
|
|
self._add_type_and_hosting_device_info(e_context, routers[0])
|
|
self.l3_cfg_rpc_notifier.routers_updated(context, routers)
|
|
return router_updated
|
|
|
|
def delete_router(self, context, id):
|
|
router_db = self._get_router(context, id)
|
|
router = self._make_router_dict(router_db)
|
|
with context.session.begin(subtransactions=True):
|
|
e_context = context.elevated()
|
|
r_hd_binding = self._get_router_binding_info(e_context, id)
|
|
self._add_type_and_hosting_device_info(
|
|
e_context, router, binding_info=r_hd_binding, schedule=False)
|
|
if router_db.gw_port is not None:
|
|
p_drv = self.get_hosting_device_plugging_driver()
|
|
if p_drv is not None:
|
|
p_drv.teardown_logical_port_connectivity(e_context,
|
|
router_db.gw_port)
|
|
# conditionally remove router from backlog just to be sure
|
|
self.remove_router_from_backlog(id)
|
|
if router['hosting_device'] is not None:
|
|
self.unschedule_router_from_hosting_device(context,
|
|
r_hd_binding)
|
|
super(L3RouterApplianceDBMixin, self).delete_router(context, id)
|
|
self.l3_cfg_rpc_notifier.router_deleted(context, router)
|
|
|
|
def notify_router_interface_action(
|
|
self, context, router_interface_info, routers, action):
|
|
l3_method = '%s_router_interface' % action
|
|
self.l3_cfg_rpc_notifier.routers_updated(context, routers, l3_method)
|
|
|
|
mapping = {'add': 'create', 'remove': 'delete'}
|
|
notifier = n_rpc.get_notifier('network')
|
|
router_event = 'router.interface.%s' % mapping[action]
|
|
notifier.info(context, router_event,
|
|
{'router_interface': router_interface_info})
|
|
|
|
def add_router_interface(self, context, router_id, interface_info):
|
|
with context.session.begin(subtransactions=True):
|
|
info = (super(L3RouterApplianceDBMixin, self).
|
|
add_router_interface(context, router_id, interface_info))
|
|
routers = [self.get_router(context, router_id)]
|
|
self._add_type_and_hosting_device_info(context.elevated(),
|
|
routers[0])
|
|
self.notify_router_interface_action(context, info, routers, 'add')
|
|
return info
|
|
|
|
def remove_router_interface(self, context, router_id, interface_info):
|
|
if 'port_id' in (interface_info or {}):
|
|
port_db = self._core_plugin._get_port(
|
|
context, interface_info['port_id'])
|
|
elif 'subnet_id' in (interface_info or {}):
|
|
subnet_db = self._core_plugin._get_subnet(
|
|
context, interface_info['subnet_id'])
|
|
port_db = self._get_router_port_db_on_subnet(
|
|
context, router_id, subnet_db)
|
|
else:
|
|
msg = "Either subnet_id or port_id must be specified"
|
|
raise n_exc.BadRequest(resource='router', msg=msg)
|
|
routers = [self.get_router(context, router_id)]
|
|
with context.session.begin(subtransactions=True):
|
|
e_context = context.elevated()
|
|
self._add_type_and_hosting_device_info(e_context, routers[0])
|
|
p_drv = self.get_hosting_device_plugging_driver()
|
|
if p_drv is not None:
|
|
p_drv.teardown_logical_port_connectivity(e_context, port_db)
|
|
info = (super(L3RouterApplianceDBMixin, self).
|
|
remove_router_interface(context, router_id,
|
|
interface_info))
|
|
self.notify_router_interface_action(context, info, routers, 'remove')
|
|
return info
|
|
|
|
def create_floatingip(
|
|
self, context, floatingip,
|
|
initial_status=l3_constants.FLOATINGIP_STATUS_ACTIVE):
|
|
with context.session.begin(subtransactions=True):
|
|
info = super(L3RouterApplianceDBMixin, self).create_floatingip(
|
|
context, floatingip)
|
|
if info['router_id']:
|
|
routers = [self.get_router(context, info['router_id'])]
|
|
self._add_type_and_hosting_device_info(context.elevated(),
|
|
routers[0])
|
|
self.l3_cfg_rpc_notifier.routers_updated(context, routers,
|
|
'create_floatingip')
|
|
return info
|
|
|
|
def update_floatingip(self, context, id, floatingip):
|
|
orig_fl_ip = super(L3RouterApplianceDBMixin, self).get_floatingip(
|
|
context, id)
|
|
before_router_id = orig_fl_ip['router_id']
|
|
with context.session.begin(subtransactions=True):
|
|
info = super(L3RouterApplianceDBMixin, self).update_floatingip(
|
|
context, id, floatingip)
|
|
router_ids = []
|
|
if before_router_id:
|
|
router_ids.append(before_router_id)
|
|
router_id = info['router_id']
|
|
if router_id and router_id != before_router_id:
|
|
router_ids.append(router_id)
|
|
routers = []
|
|
for router_id in router_ids:
|
|
router = self.get_router(context, router_id)
|
|
self._add_type_and_hosting_device_info(context.elevated(),
|
|
router)
|
|
routers.append(router)
|
|
self.l3_cfg_rpc_notifier.routers_updated(context, routers,
|
|
'update_floatingip')
|
|
return info
|
|
|
|
def delete_floatingip(self, context, id):
|
|
floatingip_db = self._get_floatingip(context, id)
|
|
router_id = floatingip_db['router_id']
|
|
with context.session.begin(subtransactions=True):
|
|
super(L3RouterApplianceDBMixin, self).delete_floatingip(
|
|
context, id)
|
|
if router_id:
|
|
routers = [self.get_router(context, router_id)]
|
|
self._add_type_and_hosting_device_info(context.elevated(),
|
|
routers[0])
|
|
self.l3_cfg_rpc_notifier.routers_updated(context, routers,
|
|
'delete_floatingip')
|
|
|
|
def disassociate_floatingips(self, context, port_id, do_notify=True):
|
|
with context.session.begin(subtransactions=True):
|
|
router_ids = super(L3RouterApplianceDBMixin,
|
|
self).disassociate_floatingips(context, port_id)
|
|
if router_ids and do_notify:
|
|
routers = []
|
|
for router_id in router_ids:
|
|
router = self.get_router(context, router_id)
|
|
self._add_type_and_hosting_device_info(context.elevated(),
|
|
router)
|
|
routers.append(router)
|
|
self.l3_cfg_rpc_notifier.routers_updated(
|
|
context, routers, 'disassociate_floatingips')
|
|
# since caller assumes that we handled notifications on its
|
|
# behalf, return nothing
|
|
return
|
|
return router_ids
|
|
|
|
@lockutils.synchronized('routerbacklog', 'neutron-')
|
|
def _handle_non_responding_hosting_devices(self, context, hosting_devices,
|
|
affected_resources):
|
|
"""Handle hosting devices determined to be "dead".
|
|
|
|
This function is called by the hosting device manager.
|
|
Service plugins are supposed to extend the 'affected_resources'
|
|
dictionary. Hence, we add the id of Neutron routers that are
|
|
hosted in <hosting_devices>.
|
|
|
|
param: hosting_devices - list of dead hosting devices
|
|
param: affected_resources - dict with list of affected logical
|
|
resources per hosting device:
|
|
{'hd_id1': {'routers': [id1, id2, ...],
|
|
'fw': [id1, ...],
|
|
...},
|
|
'hd_id2': {'routers': [id3, id4, ...],
|
|
'fw': [id1, ...],
|
|
...},
|
|
...}
|
|
"""
|
|
LOG.debug('Processing affected routers in dead hosting devices')
|
|
with context.session.begin(subtransactions=True):
|
|
for hd in hosting_devices:
|
|
hd_bindings = self._get_hosting_device_bindings(context,
|
|
hd['id'])
|
|
router_ids = []
|
|
for binding in hd_bindings:
|
|
router_ids.append(binding['router_id'])
|
|
if binding['auto_schedule']:
|
|
self.backlog_router(binding['router'])
|
|
try:
|
|
affected_resources[hd['id']].update(
|
|
{'routers': router_ids})
|
|
except KeyError:
|
|
affected_resources[hd['id']] = {'routers': router_ids}
|
|
|
|
def get_sync_data_ext(self, context, router_ids=None, active=None):
|
|
"""Query routers and their related floating_ips, interfaces.
|
|
|
|
Adds information about hosting device as well as trunking.
|
|
"""
|
|
with context.session.begin(subtransactions=True):
|
|
sync_data = (super(L3RouterApplianceDBMixin, self).
|
|
get_sync_data(context, router_ids, active))
|
|
for router in sync_data:
|
|
self._add_type_and_hosting_device_info(context, router)
|
|
plg_drv = self.get_hosting_device_plugging_driver()
|
|
if plg_drv and router['hosting_device']:
|
|
self._add_hosting_port_info(context, router, plg_drv)
|
|
return sync_data
|
|
|
|
def schedule_router_on_hosting_device(self, context, r_hd_binding):
|
|
LOG.info(_('Attempting to schedule router %s.'),
|
|
r_hd_binding['router']['id'])
|
|
result = self._create_csr1kv_vm_hosting_device(context.elevated())
|
|
if result is None:
|
|
# CSR1kv hosting device creation was unsuccessful so backlog
|
|
# it for another scheduling attempt later.
|
|
self.backlog_router(r_hd_binding['router'])
|
|
return False
|
|
with context.session.begin(subtransactions=True):
|
|
router = r_hd_binding['router']
|
|
r_hd_binding.hosting_device = result
|
|
self.remove_router_from_backlog(router['id'])
|
|
LOG.info(_('Successfully scheduled router %(r_id)s to '
|
|
'hosting device %(d_id)s'),
|
|
{'r_id': r_hd_binding['router']['id'],
|
|
'd_id': result['id']})
|
|
return True
|
|
|
|
def unschedule_router_from_hosting_device(self, context, r_hd_binding):
|
|
LOG.info(_('Un-schedule router %s.'),
|
|
r_hd_binding['router']['id'])
|
|
hosting_device = r_hd_binding['hosting_device']
|
|
if r_hd_binding['hosting_device'] is None:
|
|
return False
|
|
self._delete_service_vm_hosting_device(context.elevated(),
|
|
hosting_device)
|
|
|
|
@lockutils.synchronized('routers', 'neutron-')
|
|
def backlog_router(self, router):
|
|
if ((router or {}).get('id') is None or
|
|
router['id'] in self._backlogged_routers):
|
|
return
|
|
LOG.info(_('Backlogging router %s for renewed scheduling attempt '
|
|
'later'), router['id'])
|
|
self._backlogged_routers[router['id']] = router
|
|
|
|
@lockutils.synchronized('routers', 'neutron-')
|
|
def remove_router_from_backlog(self, id):
|
|
self._backlogged_routers.pop(id, None)
|
|
LOG.info(_('Router %s removed from backlog'), id)
|
|
|
|
@lockutils.synchronized('routerbacklog', 'neutron-')
|
|
def _process_backlogged_routers(self):
|
|
if self._refresh_router_backlog:
|
|
self._sync_router_backlog()
|
|
if not self._backlogged_routers:
|
|
return
|
|
context = n_context.get_admin_context()
|
|
scheduled_routers = []
|
|
LOG.info(_('Processing router (scheduling) backlog'))
|
|
# try to reschedule
|
|
for r_id, router in self._backlogged_routers.items():
|
|
self._add_type_and_hosting_device_info(context, router)
|
|
if router.get('hosting_device'):
|
|
# scheduling attempt succeeded
|
|
scheduled_routers.append(router)
|
|
self._backlogged_routers.pop(r_id, None)
|
|
# notify cfg agents so the scheduled routers are instantiated
|
|
if scheduled_routers:
|
|
self.l3_cfg_rpc_notifier.routers_updated(context,
|
|
scheduled_routers)
|
|
|
|
def _setup_backlog_handling(self):
|
|
self._heartbeat = loopingcall.FixedIntervalLoopingCall(
|
|
self._process_backlogged_routers)
|
|
self._heartbeat.start(
|
|
interval=cfg.CONF.general.backlog_processing_interval)
|
|
|
|
def _sync_router_backlog(self):
|
|
LOG.info(_('Synchronizing router (scheduling) backlog'))
|
|
context = n_context.get_admin_context()
|
|
query = context.session.query(l3_models.RouterHostingDeviceBinding)
|
|
query = query.options(joinedload('router'))
|
|
query = query.filter(
|
|
l3_models.RouterHostingDeviceBinding.hosting_device_id ==
|
|
expr.null())
|
|
for binding in query:
|
|
router = self._make_router_dict(binding.router,
|
|
process_extensions=False)
|
|
self._backlogged_routers[binding.router_id] = router
|
|
self._refresh_router_backlog = False
|
|
|
|
def _get_router_binding_info(self, context, id, load_hd_info=True):
|
|
query = context.session.query(l3_models.RouterHostingDeviceBinding)
|
|
if load_hd_info:
|
|
query = query.options(joinedload('hosting_device'))
|
|
query = query.filter(l3_models.RouterHostingDeviceBinding.router_id ==
|
|
id)
|
|
try:
|
|
return query.one()
|
|
except exc.NoResultFound:
|
|
# This should not happen
|
|
LOG.error(_('DB inconsistency: No type and hosting info associated'
|
|
' with router %s'), id)
|
|
raise RouterBindingInfoError(router_id=id)
|
|
except exc.MultipleResultsFound:
|
|
# This should not happen either
|
|
LOG.error(_('DB inconsistency: Multiple type and hosting info'
|
|
' associated with router %s'), id)
|
|
raise RouterBindingInfoError(router_id=id)
|
|
|
|
def _get_hosting_device_bindings(self, context, id, load_routers=False,
|
|
load_hosting_device=False):
|
|
query = context.session.query(l3_models.RouterHostingDeviceBinding)
|
|
if load_routers:
|
|
query = query.options(joinedload('router'))
|
|
if load_hosting_device:
|
|
query = query.options(joinedload('hosting_device'))
|
|
query = query.filter(
|
|
l3_models.RouterHostingDeviceBinding.hosting_device_id == id)
|
|
return query.all()
|
|
|
|
def _add_type_and_hosting_device_info(self, context, router,
|
|
binding_info=None, schedule=True):
|
|
"""Adds type and hosting device information to a router."""
|
|
try:
|
|
if binding_info is None:
|
|
binding_info = self._get_router_binding_info(context,
|
|
router['id'])
|
|
except RouterBindingInfoError:
|
|
LOG.error(_('DB inconsistency: No hosting info associated with '
|
|
'router %s'), router['id'])
|
|
router['hosting_device'] = None
|
|
return
|
|
router['router_type'] = {
|
|
'id': None,
|
|
'name': 'CSR1kv_router',
|
|
'cfg_agent_driver': (cfg.CONF.hosting_devices
|
|
.csr1kv_cfgagent_router_driver)}
|
|
if binding_info.hosting_device is None and schedule:
|
|
# This router has not been scheduled to a hosting device
|
|
# so we try to do it now.
|
|
self.schedule_router_on_hosting_device(context, binding_info)
|
|
context.session.expire(binding_info)
|
|
if binding_info.hosting_device is None:
|
|
router['hosting_device'] = None
|
|
else:
|
|
router['hosting_device'] = self.get_device_info_for_agent(
|
|
binding_info.hosting_device)
|
|
|
|
def _add_hosting_port_info(self, context, router, plugging_driver):
|
|
"""Adds hosting port information to router ports.
|
|
|
|
We only populate hosting port info, i.e., reach here, if the
|
|
router has been scheduled to a hosting device. Hence this
|
|
a good place to allocate hosting ports to the router ports.
|
|
"""
|
|
# cache of hosting port information: {mac_addr: {'name': port_name}}
|
|
hosting_pdata = {}
|
|
if router['external_gateway_info'] is not None:
|
|
h_info, did_allocation = self._populate_hosting_info_for_port(
|
|
context, router['id'], router['gw_port'],
|
|
router['hosting_device'], hosting_pdata, plugging_driver)
|
|
for itfc in router.get(l3_constants.INTERFACE_KEY, []):
|
|
h_info, did_allocation = self._populate_hosting_info_for_port(
|
|
context, router['id'], itfc, router['hosting_device'],
|
|
hosting_pdata, plugging_driver)
|
|
|
|
def _populate_hosting_info_for_port(self, context, router_id, port,
|
|
hosting_device, hosting_pdata,
|
|
plugging_driver):
|
|
port_db = self._core_plugin._get_port(context, port['id'])
|
|
h_info = port_db.hosting_info
|
|
new_allocation = False
|
|
if h_info is None:
|
|
# The port does not yet have a hosting port so allocate one now
|
|
h_info = self._allocate_hosting_port(
|
|
context, router_id, port_db, hosting_device['id'],
|
|
plugging_driver)
|
|
if h_info is None:
|
|
# This should not happen but just in case ...
|
|
port['hosting_info'] = None
|
|
return None, new_allocation
|
|
else:
|
|
new_allocation = True
|
|
if hosting_pdata.get('mac') is None:
|
|
p_data = self._core_plugin.get_port(
|
|
context, h_info.hosting_port_id, ['mac_address', 'name'])
|
|
hosting_pdata['mac'] = p_data['mac_address']
|
|
hosting_pdata['name'] = p_data['name']
|
|
# Including MAC address of hosting port so L3CfgAgent can easily
|
|
# determine which VM VIF to configure VLAN sub-interface on.
|
|
port['hosting_info'] = {'hosting_port_id': h_info.hosting_port_id,
|
|
'hosting_mac': hosting_pdata.get('mac'),
|
|
'hosting_port_name': hosting_pdata.get('name')}
|
|
plugging_driver.extend_hosting_port_info(
|
|
context, port_db, port['hosting_info'])
|
|
return h_info, new_allocation
|
|
|
|
def _allocate_hosting_port(self, context, router_id, port_db,
|
|
hosting_device_id, plugging_driver):
|
|
net_data = self._core_plugin.get_network(
|
|
context, port_db['network_id'], [pr_net.NETWORK_TYPE])
|
|
network_type = net_data.get(pr_net.NETWORK_TYPE)
|
|
alloc = plugging_driver.allocate_hosting_port(
|
|
context, router_id, port_db, network_type, hosting_device_id)
|
|
if alloc is None:
|
|
LOG.error(_('Failed to allocate hosting port for port %s'),
|
|
port_db['id'])
|
|
return
|
|
with context.session.begin(subtransactions=True):
|
|
h_info = l3_models.HostedHostingPortBinding(
|
|
logical_resource_id=router_id,
|
|
logical_port_id=port_db['id'],
|
|
network_type=network_type,
|
|
hosting_port_id=alloc['allocated_port_id'],
|
|
segmentation_id=alloc['allocated_vlan'])
|
|
context.session.add(h_info)
|
|
context.session.expire(port_db)
|
|
# allocation succeeded so establish connectivity for logical port
|
|
context.session.expire(h_info)
|
|
plugging_driver.setup_logical_port_connectivity(context, port_db)
|
|
return h_info
|
|
|
|
def _get_router_port_db_on_subnet(self, context, router_id, subnet):
|
|
try:
|
|
rport_qry = context.session.query(models_v2.Port)
|
|
ports = rport_qry.filter_by(
|
|
device_id=router_id,
|
|
device_owner=l3_db.DEVICE_OWNER_ROUTER_INTF,
|
|
network_id=subnet['network_id'])
|
|
for p in ports:
|
|
if p['fixed_ips'][0]['subnet_id'] == subnet['id']:
|
|
return p
|
|
except exc.NoResultFound:
|
|
return
|
|
|
|
def list_active_sync_routers_on_hosting_devices(self, context, host,
|
|
router_ids=None,
|
|
hosting_device_ids=None):
|
|
agent = self._get_agent_by_type_and_host(
|
|
context, c_const.AGENT_TYPE_CFG, host)
|
|
if not agent.admin_state_up:
|
|
return []
|
|
query = context.session.query(
|
|
l3_models.RouterHostingDeviceBinding.router_id)
|
|
query = query.join(l3_models.HostingDevice)
|
|
query = query.filter(l3_models.HostingDevice.cfg_agent_id == agent.id)
|
|
if router_ids:
|
|
if len(router_ids) == 1:
|
|
query = query.filter(
|
|
l3_models.RouterHostingDeviceBinding.router_id ==
|
|
router_ids[0])
|
|
else:
|
|
query = query.filter(
|
|
l3_models.RouterHostingDeviceBinding.router_id.in_(
|
|
router_ids))
|
|
if hosting_device_ids:
|
|
if len(hosting_device_ids) == 1:
|
|
query = query.filter(
|
|
l3_models.RouterHostingDeviceBinding.hosting_device_id ==
|
|
hosting_device_ids[0])
|
|
elif len(hosting_device_ids) > 1:
|
|
query = query.filter(
|
|
l3_models.RouterHostingDeviceBinding.hosting_device_id.in_(
|
|
hosting_device_ids))
|
|
router_ids = [item[0] for item in query]
|
|
if router_ids:
|
|
return self.get_sync_data_ext(context, router_ids=router_ids,
|
|
active=True)
|
|
else:
|
|
return []
|