Skip to content

Instantly share code, notes, and snippets.

@skywalker-nick
Last active August 29, 2015 13:55
Show Gist options
  • Save skywalker-nick/8726625 to your computer and use it in GitHub Desktop.
Save skywalker-nick/8726625 to your computer and use it in GitHub Desktop.
Least Utilized Router Scheduler for OpenStack Neutron Havana Release
class LeastUtilizedScheduler(object):
"""Allocate a L3 agent for a new router that is the least utilized
in terms of router count
"""
def auto_schedule_routers(self, plugin, context, host, router_ids):
"""Schedule non-hosted routers to L3 Agent running on host.
If router_ids is given, each router in router_ids is scheduled
if it is not scheduled yet. Otherwise all unscheduled routers
are scheduled.
Don't schedule the routers which are hosted already
by active l3 agents.
"""
with context.session.begin(subtransactions=True):
# query if we have valid l3 agent on the host
query = context.session.query(agents_db.Agent)
query = query.filter(agents_db.Agent.agent_type ==
constants.AGENT_TYPE_L3,
agents_db.Agent.admin_state_up == True)
agents = []
for agent in query.all():
router_query = context.session.query(l3_agentschedulers_db.RouterL3AgentBinding)
router_count = router_query.filter(l3_agentschedulers_db.RouterL3AgentBinding.l3_agent_id == agent['id']).count()
agent['router_count'] = router_count
agents.append(agent)
LOG.debug(_('Router Scheduler found router hosted count='
'%(router_count)s s for agent_id=%(agent_id)s'),
{'router_count': agent['router_count'],
'agent_id': agent['id']})
if not agents:
LOG.debug(_('No enabled L3 agents available to schedule to'))
return False
ordered_agents = sorted(agents, key=lambda k: k['router_count'])
l3_agent = None
for chosen_agent in ordered_agents:
if agents_db.AgentDbMixin.is_agent_down(chosen_agent.heartbeat_timestamp):
LOG.debug(_('Router Skipping Preferred agent_id=%s which is down'), chosen_agent['id'])
continue
else:
l3_agent = chosen_agent
# make sure the agent with the least hosted routers is chosen
break
LOG.debug(_('Router Scheduler choosing agent_id=%s'), l3_agent['id'])
if agents_db.AgentDbMixin.is_agent_down(
l3_agent.heartbeat_timestamp):
LOG.warn(_('L3 agent %s is not active'), l3_agent.id)
# check if each of the specified routers is hosted
if router_ids:
if not isinstance(router_ids, (list, tuple)):
router_ids = [router_ids]
unscheduled_router_ids = []
for router_id in router_ids:
l3_agents = plugin.get_l3_agents_hosting_routers(
context, [router_id], admin_state_up=True)
if l3_agents:
LOG.debug(_('Router %(router_id)s has already been'
' hosted by L3 agent %(agent_id)s'),
{'router_id': router_id,
'agent_id': l3_agents[0]['id']})
else:
unscheduled_router_ids.append(router_id)
LOG.info(_("Router is appending router_id=%r to unscheduled_router_ids"), router_id)
if not unscheduled_router_ids:
# all (specified) routers are already scheduled
return False
else:
# get all routers that are not hosted
#TODO(gongysh) consider the disabled agent's router
stmt = ~exists().where(
l3_db.Router.id ==
l3_agentschedulers_db.RouterL3AgentBinding.router_id)
unscheduled_router_ids = [router_id_[0] for router_id_ in
context.session.query(
l3_db.Router.id).filter(stmt)]
if not unscheduled_router_ids:
LOG.debug(_('No non-hosted routers'))
return False
else:
LOG.info(_("Router RouterL3AgentBinding returned unscheduled_router_ids=%r"), unscheduled_router_ids)
# check if the configuration of l3 agent is compatible
# with the router
routers = plugin.get_routers(
context, filters={'id': unscheduled_router_ids})
to_removed_ids = []
for router in routers:
candidates = plugin.get_l3_agent_candidates(router, [l3_agent])
if not candidates:
to_removed_ids.append(router['id'])
router_ids = set(unscheduled_router_ids) - set(to_removed_ids)
if not router_ids:
LOG.warn(_('No routers compatible with L3 agent configuration'
' on host %s'), host)
return False
# binding
for router_id in router_ids:
LOG.info(_("Router iterating over router_ids=%r and got router_id=%s" % (router_ids, router_id)))
binding = l3_agentschedulers_db.RouterL3AgentBinding()
binding.l3_agent = l3_agent
binding.router_id = router_id
binding.default = True
context.session.add(binding)
return True
def schedule(self, plugin, context, router_id):
"""Schedule the router to the least utilized active L3 agent
if there is no enable L3 agent hosting it.
"""
with context.session.begin(subtransactions=True):
# allow one router is hosted by just
# one enabled l3 agent hosting since active is just a
# timing problem. Non-active l3 agent can return to
# active any time
# get router object from router_id
sync_router = plugin.get_router(context, router_id)
l3_agents = plugin.get_l3_agents_hosting_routers(
context, [sync_router['id']], admin_state_up=True)
if l3_agents:
LOG.debug(_('Router %(router_id)s has already been hosted'
' by L3 agent %(agent_id)s'),
{'router_id': sync_router['id'],
'agent_id': l3_agents[0]['id']})
return
active_l3_agents = plugin.get_l3_agents(context, active=True)
if not active_l3_agents:
LOG.warn(_('No active L3 agents'))
return
candidates = plugin.get_l3_agent_candidates(sync_router,
active_l3_agents)
if not candidates:
LOG.warn(_('No L3 agents can host the router %s'),
sync_router['id'])
return
for candidate in candidates:
routers = plugin.list_routers_on_l3_agent(context,
candidate['id'])
LOG.debug(_('Router Scheduler found router hosted count='
'%(router_count)s s for agent_id=%(agent_id)s'),
{'router_count': len(routers['routers']),
'agent_id': candidate['id']})
candidate['router_count'] = len(routers['routers'])
ordered_agents = sorted(candidates, key=lambda k: k['router_count'])
chosen_agent = None
for l3_agent in ordered_agents:
if agents_db.AgentDbMixin.is_agent_down(l3_agent.heartbeat_timestamp):
LOG.debug(_('Router Skipping Preferred agent_id=%s which is down'), l3_agent['id'])
continue
else:
chosen_agent = l3_agent
# make sure the agent with the least hosted routers is chosen
break
LOG.debug(_('Router Scheduler choosing agent=%r'), chosen_agent)
binding = l3_agentschedulers_db.RouterL3AgentBinding()
binding.l3_agent = chosen_agent
binding.router_id = sync_router['id']
context.session.add(binding)
LOG.debug(_('Router %(router_id)s is scheduled to '
'L3 agent %(agent_id)s'),
{'router_id': sync_router['id'],
'agent_id': chosen_agent['id']})
return chosen_agent
@skywalker-nick
Copy link
Author

Add the code at the end of the file: l3_agent_scheduler.py

@skywalker-nick
Copy link
Author

neutron.conf:
router_scheduler_driver = neutron.scheduler.l3_agent_scheduler.LeastUtilizedScheduler

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment