Merge "Improve scheduling L3/DHCP agents, missing lower binding indexes" into stable/wallaby
This commit is contained in:
commit
94c2c92f8c
|
@ -78,3 +78,6 @@ IDPOOL_SELECT_SIZE = 100
|
||||||
AUTO_DELETE_PORT_OWNERS = [constants.DEVICE_OWNER_DHCP,
|
AUTO_DELETE_PORT_OWNERS = [constants.DEVICE_OWNER_DHCP,
|
||||||
constants.DEVICE_OWNER_DISTRIBUTED,
|
constants.DEVICE_OWNER_DISTRIBUTED,
|
||||||
constants.DEVICE_OWNER_AGENT_GW]
|
constants.DEVICE_OWNER_AGENT_GW]
|
||||||
|
|
||||||
|
# The lowest binding index for L3 agents and DHCP agents.
|
||||||
|
LOWEST_AGENT_BINDING_INDEX = 1
|
||||||
|
|
|
@ -25,9 +25,9 @@ from oslo_log import log as logging
|
||||||
import oslo_messaging
|
import oslo_messaging
|
||||||
|
|
||||||
from neutron.agent.common import utils as agent_utils
|
from neutron.agent.common import utils as agent_utils
|
||||||
|
from neutron.common import _constants as n_const
|
||||||
from neutron.conf.db import l3_agentschedulers_db
|
from neutron.conf.db import l3_agentschedulers_db
|
||||||
from neutron.db import agentschedulers_db
|
from neutron.db import agentschedulers_db
|
||||||
from neutron.db.models import l3agent as rb_model
|
|
||||||
from neutron.extensions import l3agentscheduler
|
from neutron.extensions import l3agentscheduler
|
||||||
from neutron.extensions import router_availability_zone as router_az
|
from neutron.extensions import router_availability_zone as router_az
|
||||||
from neutron.objects import agent as ag_obj
|
from neutron.objects import agent as ag_obj
|
||||||
|
@ -522,7 +522,7 @@ class L3AgentSchedulerDbMixin(l3agentscheduler.L3AgentSchedulerPluginBase,
|
||||||
bindings = rb_obj.RouterL3AgentBinding.get_objects(
|
bindings = rb_obj.RouterL3AgentBinding.get_objects(
|
||||||
context, _pager=pager, router_id=router_id)
|
context, _pager=pager, router_id=router_id)
|
||||||
return base_scheduler.get_vacant_binding_index(
|
return base_scheduler.get_vacant_binding_index(
|
||||||
num_agents, bindings, rb_model.LOWEST_BINDING_INDEX,
|
num_agents, bindings, n_const.LOWEST_AGENT_BINDING_INDEX,
|
||||||
force_scheduling=is_manual_scheduling)
|
force_scheduling=is_manual_scheduling)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,10 +15,9 @@ from neutron_lib.db import model_base
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy import orm
|
from sqlalchemy import orm
|
||||||
|
|
||||||
|
from neutron.common import _constants as n_const
|
||||||
from neutron.db.models import agent as agent_model
|
from neutron.db.models import agent as agent_model
|
||||||
|
|
||||||
LOWEST_BINDING_INDEX = 1
|
|
||||||
|
|
||||||
|
|
||||||
class RouterL3AgentBinding(model_base.BASEV2):
|
class RouterL3AgentBinding(model_base.BASEV2):
|
||||||
"""Represents binding between neutron routers and L3 agents."""
|
"""Represents binding between neutron routers and L3 agents."""
|
||||||
|
@ -37,5 +36,6 @@ class RouterL3AgentBinding(model_base.BASEV2):
|
||||||
l3_agent_id = sa.Column(sa.String(36),
|
l3_agent_id = sa.Column(sa.String(36),
|
||||||
sa.ForeignKey("agents.id", ondelete='CASCADE'),
|
sa.ForeignKey("agents.id", ondelete='CASCADE'),
|
||||||
primary_key=True)
|
primary_key=True)
|
||||||
binding_index = sa.Column(sa.Integer, nullable=False,
|
binding_index = sa.Column(
|
||||||
server_default=str(LOWEST_BINDING_INDEX))
|
sa.Integer, nullable=False,
|
||||||
|
server_default=str(n_const.LOWEST_AGENT_BINDING_INDEX))
|
||||||
|
|
|
@ -14,12 +14,10 @@ from neutron_lib.db import model_base
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy import orm
|
from sqlalchemy import orm
|
||||||
|
|
||||||
|
from neutron.common import _constants as n_const
|
||||||
from neutron.db.models import agent as agent_model
|
from neutron.db.models import agent as agent_model
|
||||||
|
|
||||||
|
|
||||||
LOWEST_BINDING_INDEX = 1
|
|
||||||
|
|
||||||
|
|
||||||
class NetworkDhcpAgentBinding(model_base.BASEV2):
|
class NetworkDhcpAgentBinding(model_base.BASEV2):
|
||||||
"""Represents binding between neutron networks and DHCP agents."""
|
"""Represents binding between neutron networks and DHCP agents."""
|
||||||
|
|
||||||
|
@ -38,5 +36,6 @@ class NetworkDhcpAgentBinding(model_base.BASEV2):
|
||||||
sa.ForeignKey("agents.id",
|
sa.ForeignKey("agents.id",
|
||||||
ondelete='CASCADE'),
|
ondelete='CASCADE'),
|
||||||
primary_key=True)
|
primary_key=True)
|
||||||
binding_index = sa.Column(sa.Integer, nullable=False,
|
binding_index = sa.Column(
|
||||||
server_default=str(LOWEST_BINDING_INDEX))
|
sa.Integer, nullable=False,
|
||||||
|
server_default=str(n_const.LOWEST_AGENT_BINDING_INDEX))
|
||||||
|
|
|
@ -17,6 +17,7 @@ from sqlalchemy.orm import joinedload
|
||||||
|
|
||||||
from sqlalchemy import sql
|
from sqlalchemy import sql
|
||||||
|
|
||||||
|
from neutron.common import _constants as n_const
|
||||||
from neutron.db.models import agent as agent_model
|
from neutron.db.models import agent as agent_model
|
||||||
from neutron.db.models import l3_attrs
|
from neutron.db.models import l3_attrs
|
||||||
from neutron.db.models import l3agent
|
from neutron.db.models import l3agent
|
||||||
|
@ -36,7 +37,7 @@ class RouterL3AgentBinding(base.NeutronDbObject):
|
||||||
'router_id': common_types.UUIDField(),
|
'router_id': common_types.UUIDField(),
|
||||||
'l3_agent_id': common_types.UUIDField(),
|
'l3_agent_id': common_types.UUIDField(),
|
||||||
'binding_index': obj_fields.IntegerField(
|
'binding_index': obj_fields.IntegerField(
|
||||||
default=l3agent.LOWEST_BINDING_INDEX),
|
default=n_const.LOWEST_AGENT_BINDING_INDEX),
|
||||||
}
|
}
|
||||||
|
|
||||||
# TODO(ihrachys) return OVO objects not models
|
# TODO(ihrachys) return OVO objects not models
|
||||||
|
|
|
@ -99,24 +99,44 @@ def get_vacant_binding_index(num_agents, bindings, lowest_binding_index,
|
||||||
always return an index, even if this number
|
always return an index, even if this number
|
||||||
exceeds the maximum configured number of agents.
|
exceeds the maximum configured number of agents.
|
||||||
"""
|
"""
|
||||||
binding_indices = [b.binding_index for b in bindings]
|
def get_open_slots(binding_indices, lowest_binding_index, max_number):
|
||||||
all_indices = set(range(lowest_binding_index, num_agents + 1))
|
"""Returns an ordered list of free slots
|
||||||
open_slots = sorted(list(all_indices - set(binding_indices)))
|
|
||||||
|
|
||||||
|
This list starts from the lowest available binding index. The number
|
||||||
|
of open slots and "binding_indices" (those already taken), must be
|
||||||
|
equal to "max_number". The list returned can be [], if
|
||||||
|
len(max_number) == len(binding_indices) (that means there are no free
|
||||||
|
slots).
|
||||||
|
"""
|
||||||
|
# NOTE(ralonsoh): check LP#2006496 for more context. The DHCP/router
|
||||||
|
# binding indexes could not be a sequential list starting from
|
||||||
|
# lowest_binding_index (that is usually 1).
|
||||||
|
open_slots = set(binding_indices)
|
||||||
|
idx = lowest_binding_index
|
||||||
|
while len(open_slots) < max_number:
|
||||||
|
# Increase sequentially the "open_slots" set until we have the
|
||||||
|
# required number of slots, that is "num_agents".
|
||||||
|
open_slots.add(idx)
|
||||||
|
idx += 1
|
||||||
|
|
||||||
|
# Remove those indices already used.
|
||||||
|
open_slots -= set(binding_indices)
|
||||||
|
return sorted(list(open_slots))
|
||||||
|
|
||||||
|
binding_indices = [b.binding_index for b in bindings]
|
||||||
|
open_slots = get_open_slots(binding_indices, lowest_binding_index,
|
||||||
|
num_agents)
|
||||||
if open_slots:
|
if open_slots:
|
||||||
return open_slots[0]
|
return open_slots[0]
|
||||||
|
|
||||||
if not force_scheduling:
|
if not force_scheduling:
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
# Last chance: if this is a manual scheduling, we're gonna allow
|
# Last chance: if this is a manual scheduling, we're going to allow
|
||||||
# creation of a binding_index even if it will exceed
|
# creation of a binding_index even if it will exceed
|
||||||
# dhcp_agents_per_network.
|
# dhcp_agents_per_network/max_l3_agents_per_router.
|
||||||
if max(binding_indices) == len(binding_indices):
|
while not open_slots:
|
||||||
return max(binding_indices) + 1
|
num_agents += 1
|
||||||
else:
|
open_slots = get_open_slots(binding_indices, lowest_binding_index,
|
||||||
# Find binding index set gaps and return first free one.
|
num_agents)
|
||||||
all_indices = set(range(lowest_binding_index,
|
return open_slots[0]
|
||||||
max(binding_indices) + 1))
|
|
||||||
open_slots = sorted(list(all_indices - set(binding_indices)))
|
|
||||||
return open_slots[0]
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
from neutron.agent.common import utils as agent_utils
|
from neutron.agent.common import utils as agent_utils
|
||||||
from neutron.db.network_dhcp_agent_binding import models as ndab_model
|
from neutron.common import _constants as n_const
|
||||||
from neutron.objects import agent as agent_obj
|
from neutron.objects import agent as agent_obj
|
||||||
from neutron.objects import network
|
from neutron.objects import network
|
||||||
from neutron.scheduler import base_resource_filter
|
from neutron.scheduler import base_resource_filter
|
||||||
|
@ -195,7 +195,7 @@ class DhcpFilter(base_resource_filter.BaseResourceFilter):
|
||||||
bindings = network.NetworkDhcpAgentBinding.get_objects(
|
bindings = network.NetworkDhcpAgentBinding.get_objects(
|
||||||
context, network_id=network_id)
|
context, network_id=network_id)
|
||||||
return base_scheduler.get_vacant_binding_index(
|
return base_scheduler.get_vacant_binding_index(
|
||||||
num_agents, bindings, ndab_model.LOWEST_BINDING_INDEX,
|
num_agents, bindings, n_const.LOWEST_AGENT_BINDING_INDEX,
|
||||||
force_scheduling=force_scheduling)
|
force_scheduling=force_scheduling)
|
||||||
|
|
||||||
def bind(self, context, agents, network_id, force_scheduling=False):
|
def bind(self, context, agents, network_id, force_scheduling=False):
|
||||||
|
@ -205,7 +205,7 @@ class DhcpFilter(base_resource_filter.BaseResourceFilter):
|
||||||
for agent in agents:
|
for agent in agents:
|
||||||
binding_index = self.get_vacant_network_dhcp_agent_binding_index(
|
binding_index = self.get_vacant_network_dhcp_agent_binding_index(
|
||||||
context, network_id, force_scheduling)
|
context, network_id, force_scheduling)
|
||||||
if binding_index < ndab_model.LOWEST_BINDING_INDEX:
|
if binding_index < n_const.LOWEST_AGENT_BINDING_INDEX:
|
||||||
LOG.debug('Unable to find a vacant binding_index for '
|
LOG.debug('Unable to find a vacant binding_index for '
|
||||||
'network %(network_id)s and agent %(agent_id)s',
|
'network %(network_id)s and agent %(agent_id)s',
|
||||||
{'network_id': network_id,
|
{'network_id': network_id,
|
||||||
|
|
|
@ -27,6 +27,7 @@ from oslo_config import cfg
|
||||||
from oslo_db import exception as db_exc
|
from oslo_db import exception as db_exc
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
from neutron.common import _constants as n_const
|
||||||
from neutron.common import utils
|
from neutron.common import utils
|
||||||
from neutron.conf.db import l3_hamode_db
|
from neutron.conf.db import l3_hamode_db
|
||||||
from neutron.db.models import l3agent as rb_model
|
from neutron.db.models import l3agent as rb_model
|
||||||
|
@ -186,7 +187,7 @@ class L3Scheduler(object, metaclass=abc.ABCMeta):
|
||||||
return
|
return
|
||||||
|
|
||||||
if not is_ha:
|
if not is_ha:
|
||||||
binding_index = rb_model.LOWEST_BINDING_INDEX
|
binding_index = n_const.LOWEST_AGENT_BINDING_INDEX
|
||||||
if rb_obj.RouterL3AgentBinding.objects_exist(
|
if rb_obj.RouterL3AgentBinding.objects_exist(
|
||||||
context, router_id=router_id, binding_index=binding_index):
|
context, router_id=router_id, binding_index=binding_index):
|
||||||
LOG.debug('Non-HA router %s has already been scheduled',
|
LOG.debug('Non-HA router %s has already been scheduled',
|
||||||
|
@ -195,7 +196,7 @@ class L3Scheduler(object, metaclass=abc.ABCMeta):
|
||||||
else:
|
else:
|
||||||
binding_index = plugin.get_vacant_binding_index(
|
binding_index = plugin.get_vacant_binding_index(
|
||||||
context, router_id, is_manual_scheduling)
|
context, router_id, is_manual_scheduling)
|
||||||
if binding_index < rb_model.LOWEST_BINDING_INDEX:
|
if binding_index < n_const.LOWEST_AGENT_BINDING_INDEX:
|
||||||
LOG.debug('Unable to find a vacant binding_index for '
|
LOG.debug('Unable to find a vacant binding_index for '
|
||||||
'router %(router_id)s and agent %(agent_id)s',
|
'router %(router_id)s and agent %(agent_id)s',
|
||||||
{'router_id': router_id,
|
{'router_id': router_id,
|
||||||
|
|
|
@ -38,6 +38,21 @@ class GetVacantBindingFilterCase(base.BaseTestCase):
|
||||||
3, [mock.Mock(binding_index=1), mock.Mock(binding_index=3)], 1)
|
3, [mock.Mock(binding_index=1), mock.Mock(binding_index=3)], 1)
|
||||||
self.assertEqual(2, ret)
|
self.assertEqual(2, ret)
|
||||||
|
|
||||||
|
# Binding list starting in 2, two elements, required three.
|
||||||
|
ret = base_scheduler.get_vacant_binding_index(
|
||||||
|
3, [mock.Mock(binding_index=2), mock.Mock(binding_index=3)], 1)
|
||||||
|
self.assertEqual(1, ret)
|
||||||
|
|
||||||
|
# Binding list starting in 2, two elements, required two.
|
||||||
|
ret = base_scheduler.get_vacant_binding_index(
|
||||||
|
2, [mock.Mock(binding_index=2), mock.Mock(binding_index=3)], 1)
|
||||||
|
self.assertEqual(-1, ret)
|
||||||
|
|
||||||
|
# Binding list starting in 2, two elements, required one.
|
||||||
|
ret = base_scheduler.get_vacant_binding_index(
|
||||||
|
1, [mock.Mock(binding_index=2), mock.Mock(binding_index=3)], 1)
|
||||||
|
self.assertEqual(-1, ret)
|
||||||
|
|
||||||
def test_get_vacant_binding_index_force_scheduling(self):
|
def test_get_vacant_binding_index_force_scheduling(self):
|
||||||
ret = base_scheduler.get_vacant_binding_index(
|
ret = base_scheduler.get_vacant_binding_index(
|
||||||
3, [mock.Mock(binding_index=1), mock.Mock(binding_index=2),
|
3, [mock.Mock(binding_index=1), mock.Mock(binding_index=2),
|
||||||
|
@ -50,3 +65,9 @@ class GetVacantBindingFilterCase(base.BaseTestCase):
|
||||||
mock.Mock(binding_index=3), mock.Mock(binding_index=4),
|
mock.Mock(binding_index=3), mock.Mock(binding_index=4),
|
||||||
mock.Mock(binding_index=5)], 1, force_scheduling=True)
|
mock.Mock(binding_index=5)], 1, force_scheduling=True)
|
||||||
self.assertEqual(6, ret)
|
self.assertEqual(6, ret)
|
||||||
|
|
||||||
|
ret = base_scheduler.get_vacant_binding_index(
|
||||||
|
3, [mock.Mock(binding_index=2), mock.Mock(binding_index=3),
|
||||||
|
mock.Mock(binding_index=4), mock.Mock(binding_index=5),
|
||||||
|
mock.Mock(binding_index=6)], 1, force_scheduling=True)
|
||||||
|
self.assertEqual(1, ret)
|
||||||
|
|
Loading…
Reference in New Issue