Merge "[OVO] Integration of RouterL3AgentBinding"

This commit is contained in:
Jenkins 2017-05-27 05:04:19 +00:00 committed by Gerrit Code Review
commit 09fe4aa92d
10 changed files with 154 additions and 127 deletions

View File

@ -19,22 +19,19 @@ from oslo_config import cfg
from oslo_db import exception as db_exc
from oslo_log import log as logging
import oslo_messaging
import sqlalchemy as sa
from sqlalchemy import func
from sqlalchemy import or_
from sqlalchemy import orm
from sqlalchemy.orm import joinedload
from sqlalchemy import sql
from neutron._i18n import _, _LI
from neutron.agent.common import utils as agent_utils
from neutron.common import utils as n_utils
from neutron.db import agentschedulers_db
from neutron.db.models import agent as agent_model
from neutron.db.models import l3_attrs
from neutron.db.models import l3agent as rb_model
from neutron.extensions import l3agentscheduler
from neutron.extensions import router_availability_zone as router_az
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_objs
@ -86,20 +83,9 @@ class L3AgentSchedulerDbMixin(l3agentscheduler.L3AgentSchedulerPluginBase,
rescheduling_failed=l3agentscheduler.RouterReschedulingFailed)
def get_down_router_bindings(self, context, agent_dead_limit):
# TODO(sshank): This portion is done in seperate patch: [1]
# [1] Change-Id: I0af665a97087ad72431d58f04089a804088ef005
cutoff = self.get_cutoff_time(agent_dead_limit)
return (context.session.query(
rb_model.RouterL3AgentBinding).
join(agent_model.Agent).
filter(agent_model.Agent.heartbeat_timestamp < cutoff,
agent_model.Agent.admin_state_up).outerjoin(
l3_attrs.RouterExtraAttributes,
l3_attrs.RouterExtraAttributes.router_id ==
rb_model.RouterL3AgentBinding.router_id).filter(
sa.or_(
l3_attrs.RouterExtraAttributes.ha == sql.false(),
l3_attrs.RouterExtraAttributes.ha == sql.null())))
cutoff = self.get_cutoff_time(agent_dead_limit)
return rb_obj.RouterL3AgentBinding.get_down_router_bindings(
context, cutoff)
def _get_agent_mode(self, agent_db):
agent_conf = self.get_configuration_dict(agent_db)
@ -146,8 +132,8 @@ class L3AgentSchedulerDbMixin(l3agentscheduler.L3AgentSchedulerPluginBase,
"""
router_id = router['id']
agent_id = agent['id']
query = context.session.query(rb_model.RouterL3AgentBinding)
bindings = query.filter_by(router_id=router_id).all()
bindings = rb_obj.RouterL3AgentBinding.get_objects(context,
router_id=router_id)
if not bindings:
return True
for binding in bindings:
@ -235,12 +221,8 @@ class L3AgentSchedulerDbMixin(l3agentscheduler.L3AgentSchedulerPluginBase,
context, router_id, agent.host)
def _unbind_router(self, context, router_id, agent_id):
with context.session.begin(subtransactions=True):
query = context.session.query(rb_model.RouterL3AgentBinding)
query = query.filter(
rb_model.RouterL3AgentBinding.router_id == router_id,
rb_model.RouterL3AgentBinding.l3_agent_id == agent_id)
query.delete()
rb_obj.RouterL3AgentBinding.delete_objects(
context, router_id=router_id, l3_agent_id=agent_id)
def _unschedule_router(self, context, router_id, agents_ids):
with context.session.begin(subtransactions=True):
@ -291,11 +273,10 @@ class L3AgentSchedulerDbMixin(l3agentscheduler.L3AgentSchedulerPluginBase,
router_id=router_id)
def list_routers_on_l3_agent(self, context, agent_id):
query = context.session.query(rb_model.RouterL3AgentBinding.router_id)
query = query.filter(
rb_model.RouterL3AgentBinding.l3_agent_id == agent_id)
binding_objs = rb_obj.RouterL3AgentBinding.get_objects(
context, l3_agent_id=agent_id)
router_ids = [item[0] for item in query]
router_ids = [item.router_id for item in binding_objs]
if router_ids:
return {'routers':
self.get_routers(context, filters={'id': router_ids})}
@ -327,15 +308,11 @@ class L3AgentSchedulerDbMixin(l3agentscheduler.L3AgentSchedulerPluginBase,
Overridden for DVR to handle agents in 'dvr' mode which have
no explicit bindings with routers
"""
query = context.session.query(rb_model.RouterL3AgentBinding.router_id)
query = query.filter(
rb_model.RouterL3AgentBinding.l3_agent_id == agent.id)
filters = {'l3_agent_id': agent.id}
if router_ids:
query = query.filter(
rb_model.RouterL3AgentBinding.router_id.in_(router_ids))
return [item[0] for item in query]
filters['router_id'] = router_ids
bindings = rb_obj.RouterL3AgentBinding.get_objects(context, **filters)
return [item.router_id for item in bindings]
def list_active_sync_routers_on_active_l3_agent(
self, context, host, router_ids):
@ -364,16 +341,17 @@ class L3AgentSchedulerDbMixin(l3agentscheduler.L3AgentSchedulerPluginBase,
active=None):
if not router_ids:
return []
query = context.session.query(rb_model.RouterL3AgentBinding)
query = query.options(orm.contains_eager(
rb_model.RouterL3AgentBinding.l3_agent))
query = query.join(rb_model.RouterL3AgentBinding.l3_agent)
query = query.filter(
rb_model.RouterL3AgentBinding.router_id.in_(router_ids))
record_objs = rb_obj.RouterL3AgentBinding.get_objects(
context, router_id=router_ids)
if admin_state_up is not None:
query = (query.filter(agent_model.Agent.admin_state_up ==
admin_state_up))
l3_agents = [binding.l3_agent for binding in query]
l3_agents = ag_obj.Agent.get_objects(context,
id=[obj.l3_agent_id for obj in record_objs],
admin_state_up=admin_state_up)
else:
l3_agents = [
ag_obj.Agent.get_object(context, id=obj.l3_agent_id)
for obj in record_objs
]
if active is not None:
l3_agents = [l3_agent for l3_agent in
l3_agents if not
@ -381,21 +359,19 @@ class L3AgentSchedulerDbMixin(l3agentscheduler.L3AgentSchedulerPluginBase,
l3_agent['heartbeat_timestamp'])]
return l3_agents
def _get_l3_bindings_hosting_routers(self, context, router_ids):
def _get_l3_agents_hosting_routers(self, context, router_ids):
if not router_ids:
return []
query = context.session.query(rb_model.RouterL3AgentBinding)
query = query.options(joinedload('l3_agent')).filter(
rb_model.RouterL3AgentBinding.router_id.in_(router_ids))
return query.all()
return (
rb_obj.RouterL3AgentBinding.get_l3_agents_by_router_ids(
context, router_ids))
def list_l3_agents_hosting_router(self, context, router_id):
with context.session.begin(subtransactions=True):
bindings = self._get_l3_bindings_hosting_routers(
agents = self._get_l3_agents_hosting_routers(
context, [router_id])
return {'agents': [self._make_agent_dict(binding.l3_agent) for
binding in bindings]}
return {'agents': [self._make_agent_dict(agent)
for agent in agents]}
def get_routers_l3_agents_count(self, context):
"""Return a map between routers and agent counts for all routers."""
@ -493,20 +469,11 @@ class L3AgentSchedulerDbMixin(l3agentscheduler.L3AgentSchedulerPluginBase,
self.schedule_router(context, router, candidates=None)
def get_l3_agent_with_min_routers(self, context, agent_ids):
"""Return l3 agent with the least number of routers."""
if not agent_ids:
return None
query = context.session.query(
agent_model.Agent,
func.count(
rb_model.RouterL3AgentBinding.router_id
).label('count')).outerjoin(
rb_model.RouterL3AgentBinding).group_by(
agent_model.Agent.id,
rb_model.RouterL3AgentBinding
.l3_agent_id).order_by('count')
res = query.filter(agent_model.Agent.id.in_(agent_ids)).first()
return res[0]
agents = ag_obj.Agent.get_l3_agent_with_min_routers(
context, agent_ids)
return agents
def get_hosts_to_notify(self, context, router_id):
"""Returns all hosts to send notification about router update"""
@ -528,13 +495,9 @@ class L3AgentSchedulerDbMixin(l3agentscheduler.L3AgentSchedulerPluginBase,
"""
num_agents = self.get_number_of_agents_for_scheduling(context)
query = context.session.query(rb_model.RouterL3AgentBinding)
query = query.filter(
rb_model.RouterL3AgentBinding.router_id == router_id)
query = query.order_by(rb_model.
RouterL3AgentBinding.binding_index.asc())
bindings = query.all()
pager = base_obj.Pager(sorts=[('binding_index', True)])
bindings = rb_obj.RouterL3AgentBinding.get_objects(
context, _pager=pager, router_id=router_id)
binding_indices = [b.binding_index for b in bindings]
all_indicies = set(range(rb_model.LOWEST_BINDING_INDEX,
num_agents + 1))

View File

@ -430,7 +430,8 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
with context.session.begin(subtransactions=True):
gw_port = router.gw_port
router.gw_port = None
context.session.add(router)
if router not in context.session:
context.session.add(router)
context.session.expire(gw_port)
try:
kwargs = {'context': context, 'router_id': router.id}

View File

@ -36,10 +36,11 @@ 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.models import l3agent as rb_model
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 l3agent as rb_obj
from neutron.plugins.common import utils as p_utils
@ -512,9 +513,9 @@ class DVRResourceOperationHandler(object):
if removed_hosts:
agents = plugin.get_l3_agents(context,
filters={'host': removed_hosts})
binding_table = rb_model.RouterL3AgentBinding
snat_binding = context.session.query(binding_table).filter_by(
router_id=router_id).first()
bindings = rb_obj.RouterL3AgentBinding.get_objects(
context, router_id=router_id)
snat_binding = bindings.pop() if bindings else None
for agent in agents:
is_this_snat_agent = (
snat_binding and snat_binding.l3_agent_id == agent['id'])
@ -593,10 +594,9 @@ class _DVRAgentInterfaceMixin(object):
if not routers:
return []
router_ids = [r['id'] for r in routers]
snat_binding = rb_model.RouterL3AgentBinding
query = (context.session.query(snat_binding).
filter(snat_binding.router_id.in_(router_ids))).all()
bindings = dict((b.router_id, b) for b in query)
binding_objs = rb_obj.RouterL3AgentBinding.get_objects(
context, router_id=router_ids)
bindings = dict((b.router_id, b) for b in binding_objs)
for rtr in routers:
gw_port_id = rtr['gw_port_id']
@ -613,7 +613,9 @@ class _DVRAgentInterfaceMixin(object):
LOG.debug('No snat is bound to router %s', rtr['id'])
continue
rtr['gw_port_host'] = binding.l3_agent.host
l3_agent = ag_obj.Agent.get_object(context,
id=binding.l3_agent_id)
rtr['gw_port_host'] = l3_agent.host
return routers

View File

@ -26,8 +26,8 @@ from neutron.common import utils as n_utils
from neutron.db import agentschedulers_db
from neutron.db import l3_agentschedulers_db as l3agent_sch_db
from neutron.db.models import l3agent as rb_model
from neutron.db import models_v2
from neutron.objects import l3agent as rb_obj
from neutron.plugins.ml2 import db as ml2_db
from neutron.plugins.ml2 import models as ml2_models
@ -163,11 +163,9 @@ class L3_DVRsch_db_mixin(l3agent_sch_db.L3AgentSchedulerDbMixin):
context, n_const.AGENT_TYPE_L3, port_host)
removed_router_info = []
for router_id in router_ids:
snat_binding = context.session.query(
rb_model.RouterL3AgentBinding).filter_by(
router_id=router_id).filter_by(
l3_agent_id=agent.id).first()
if snat_binding:
if rb_obj.RouterL3AgentBinding.objects_exist(context,
router_id=router_id,
l3_agent_id=agent.id):
# not removing from the agent hosting SNAT for the router
continue
subnet_ids = self.get_subnet_ids_on_router(admin_context,

View File

@ -18,11 +18,9 @@ from neutron_lib.callbacks import registry
from neutron_lib.callbacks import resources
from neutron_lib import constants
from neutron_lib.plugins import directory
from sqlalchemy import func
from neutron.db import l3_agentschedulers_db as l3_sch_db
from neutron.db.models import agent as agent_model
from neutron.db.models import l3agent as rb_model
from neutron.objects import agent as ag_obj
class L3_HA_scheduler_db_mixin(l3_sch_db.AZL3AgentSchedulerDbMixin):
@ -30,15 +28,8 @@ class L3_HA_scheduler_db_mixin(l3_sch_db.AZL3AgentSchedulerDbMixin):
def get_l3_agents_ordered_by_num_routers(self, context, agent_ids):
if not agent_ids:
return []
query = (context.session.query(agent_model.Agent, func.count(
rb_model.RouterL3AgentBinding.router_id)
.label('count')).
outerjoin(rb_model.RouterL3AgentBinding).
group_by(agent_model.Agent.id).
filter(agent_model.Agent.id.in_(agent_ids)).
order_by('count'))
return [record[0] for record in query]
return ag_obj.Agent.get_l3_agents_ordered_by_num_routers(
context, agent_ids)
def _get_agents_dict_for_router(self, agents_and_states):
agents = []
@ -52,14 +43,14 @@ class L3_HA_scheduler_db_mixin(l3_sch_db.AZL3AgentSchedulerDbMixin):
with context.session.begin(subtransactions=True):
router_db = self._get_router(context, router_id)
if router_db.extra_attributes.ha:
bindings = self.get_l3_bindings_hosting_router_with_ha_states(
agents = self.get_l3_bindings_hosting_router_with_ha_states(
context, router_id)
else:
bindings = self._get_l3_bindings_hosting_routers(
agents = self._get_l3_agents_hosting_routers(
context, [router_id])
bindings = [(binding.l3_agent, None) for binding in bindings]
agents = [(agent, None) for agent in agents]
return self._get_agents_dict_for_router(bindings)
return self._get_agents_dict_for_router(agents)
def _notify_l3_agent_ha_port_update(resource, event, trigger, **kwargs):

View File

@ -14,9 +14,11 @@
from oslo_versionedobjects import base as obj_base
from oslo_versionedobjects import fields as obj_fields
from sqlalchemy import func
from neutron.agent.common import utils
from neutron.db.models import agent as agent_model
from neutron.db.models import l3agent as rb_model
from neutron.objects import base
from neutron.objects import common_types
@ -74,3 +76,36 @@ class Agent(base.NeutronDbObject):
@property
def is_active(self):
return not utils.is_agent_down(self.heartbeat_timestamp)
# TODO(ihrachys) reuse query builder from
# get_l3_agents_ordered_by_num_routers
@classmethod
def get_l3_agent_with_min_routers(cls, context, agent_ids):
"""Return l3 agent with the least number of routers."""
with context.session.begin(subtransactions=True):
query = context.session.query(
agent_model.Agent,
func.count(
rb_model.RouterL3AgentBinding.router_id
).label('count')).outerjoin(
rb_model.RouterL3AgentBinding).group_by(
agent_model.Agent.id,
rb_model.RouterL3AgentBinding
.l3_agent_id).order_by('count')
res = query.filter(agent_model.Agent.id.in_(agent_ids)).first()
agent_obj = cls._load_object(context, res[0])
return agent_obj
@classmethod
def get_l3_agents_ordered_by_num_routers(cls, context, agent_ids):
with context.session.begin(subtransactions=True):
query = (context.session.query(agent_model.Agent, func.count(
rb_model.RouterL3AgentBinding.router_id)
.label('count')).
outerjoin(rb_model.RouterL3AgentBinding).
group_by(agent_model.Agent.id).
filter(agent_model.Agent.id.in_(agent_ids)).
order_by('count'))
agents = [cls._load_object(context, record[0]) for record in query]
return agents

View File

@ -12,7 +12,13 @@
from oslo_versionedobjects import base as obj_base
from oslo_versionedobjects import fields as obj_fields
import sqlalchemy as sa
from sqlalchemy.orm import joinedload
from sqlalchemy import sql
from neutron.db.models import agent as agent_model
from neutron.db.models import l3_attrs
from neutron.db.models import l3agent
from neutron.objects import base
from neutron.objects import common_types
@ -33,3 +39,29 @@ class RouterL3AgentBinding(base.NeutronDbObject):
'binding_index': obj_fields.IntegerField(
default=l3agent.LOWEST_BINDING_INDEX),
}
# TODO(ihrachys) return OVO objects not models
# TODO(ihrachys) move under Agent object class
@classmethod
def get_l3_agents_by_router_ids(cls, context, router_ids):
query = context.session.query(l3agent.RouterL3AgentBinding)
query = query.options(joinedload('l3_agent')).filter(
l3agent.RouterL3AgentBinding.router_id.in_(router_ids))
return [db_obj.l3_agent for db_obj in query.all()]
@classmethod
def get_down_router_bindings(cls, context, cutoff):
query = (context.session.query(
l3agent.RouterL3AgentBinding).
join(agent_model.Agent).
filter(agent_model.Agent.heartbeat_timestamp < cutoff,
agent_model.Agent.admin_state_up).outerjoin(
l3_attrs.RouterExtraAttributes,
l3_attrs.RouterExtraAttributes.router_id ==
l3agent.RouterL3AgentBinding.router_id).filter(
sa.or_(
l3_attrs.RouterExtraAttributes.ha == sql.false(),
l3_attrs.RouterExtraAttributes.ha == sql.null())))
bindings = [cls._load_object(context, db_obj) for db_obj in
query.all()]
return bindings

View File

@ -33,6 +33,7 @@ from neutron.db import l3_hamode_db
from neutron.db.models import l3agent as rb_model
from neutron.extensions import availability_zone as az_ext
from neutron.extensions import l3
from neutron.objects import l3agent as rb_obj
LOG = logging.getLogger(__name__)
@ -188,10 +189,9 @@ class L3Scheduler(object):
one try, regardless of the error preventing the addition of a new
RouterL3AgentBinding object to the database.
"""
bindings = context.session.query(
rb_model.RouterL3AgentBinding).filter_by(router_id=router_id)
if bindings.filter_by(l3_agent_id=agent_id).first():
if rb_obj.RouterL3AgentBinding.objects_exist(
context, router_id=router_id, l3_agent_id=agent_id):
LOG.debug('Router %(router_id)s has already been scheduled '
'to L3 agent %(agent_id)s.',
{'router_id': router_id, 'agent_id': agent_id})
@ -199,7 +199,8 @@ class L3Scheduler(object):
if not is_ha:
binding_index = rb_model.LOWEST_BINDING_INDEX
if bindings.filter_by(binding_index=binding_index).first():
if rb_obj.RouterL3AgentBinding.objects_exist(
context, router_id=router_id, binding_index=binding_index):
LOG.debug('Non-HA router %s has already been scheduled',
router_id)
return
@ -214,12 +215,10 @@ class L3Scheduler(object):
return
try:
with context.session.begin(subtransactions=True):
binding = rb_model.RouterL3AgentBinding()
binding.l3_agent_id = agent_id
binding.router_id = router_id
binding.binding_index = binding_index
context.session.add(binding)
binding = rb_obj.RouterL3AgentBinding(
context, l3_agent_id=agent_id,
router_id=router_id, binding_index=binding_index)
binding.create()
LOG.debug('Router %(router_id)s is scheduled to L3 agent '
'%(agent_id)s with binding_index %(binding_index)d',
{'router_id': router_id,
@ -314,7 +313,9 @@ class L3Scheduler(object):
def _filter_scheduled_agents(self, plugin, context, router_id, candidates):
hosting = plugin.get_l3_agents_hosting_routers(context, [router_id])
return list(set(candidates) - set(hosting))
# convert to comparable types
hosting_list = [tuple(host) for host in hosting]
return list(set(candidates) - set(hosting_list))
def _bind_ha_router(self, plugin, context, router_id,
tenant_id, candidates):

View File

@ -34,10 +34,11 @@ from neutron.common import constants as n_const
from neutron.db import agents_db
from neutron.db import agentschedulers_db
from neutron.db.models import agent as agent_model
from neutron.db.models import l3agent as rb_model
from neutron.extensions import agent
from neutron.extensions import dhcpagentscheduler
from neutron.extensions import l3agentscheduler
from neutron.objects import agent as ag_obj
from neutron.objects import l3agent as rb_obj
from neutron.tests.common import helpers
from neutron.tests import fake_notifier
from neutron.tests import tools
@ -792,10 +793,12 @@ class OvsAgentSchedulerTestCase(OvsAgentSchedulerTestCaseBase):
# A should still have it even though it was inactive due to the
# admin_state being down
rab = rb_model.RouterL3AgentBinding
binding = (self.adminContext.session.query(rab).
filter(rab.router_id == r['router']['id']).first())
self.assertEqual(binding.l3_agent.host, L3_HOSTA)
bindings = rb_obj.RouterL3AgentBinding.get_objects(
self.adminContext, router_id=r['router']['id'])
binding = bindings.pop() if bindings else None
l3_agent = ag_obj.Agent.get_objects(
self.adminContext, id=binding.l3_agent_id)
self.assertEqual(l3_agent[0].host, L3_HOSTA)
# B should not pick up the router
ret_b = l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTB)

View File

@ -42,6 +42,7 @@ from neutron.db.models import l3ha as l3ha_model
from neutron.extensions import l3
from neutron.extensions import l3agentscheduler as l3agent
from neutron import manager
from neutron.objects import l3agent as rb_obj
from neutron.scheduler import l3_agent_scheduler
from neutron.tests import base
from neutron.tests.common import helpers
@ -284,7 +285,7 @@ class L3SchedulerTestBaseMixin(object):
self.plugin._unbind_router(self.adminContext,
router['router']['id'],
agent_id)
bindings = self.plugin._get_l3_bindings_hosting_routers(
bindings = rb_obj.RouterL3AgentBinding.get_l3_agents_by_router_ids(
self.adminContext, [router['router']['id']])
self.assertEqual(0, len(bindings))
@ -493,7 +494,7 @@ class L3SchedulerTestBaseMixin(object):
# checking that bind_router() is not throwing
# when supplied with router_id of non-existing router
scheduler.bind_router(self.plugin, self.adminContext,
"dummyID", self.agent_id1)
uuidutils.generate_uuid(), self.agent_id1)
def test_bind_existing_router(self):
router = self._make_router(self.fmt,