Skip to content

Commit a6253c0

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Improve scheduling L3/DHCP agents, missing lower binding indexes" into stable/yoga
2 parents 9df4a7e + 7dcf8be commit a6253c0

File tree

9 files changed

+75
-30
lines changed

9 files changed

+75
-30
lines changed

neutron/common/_constants.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,6 @@
7878
AUTO_DELETE_PORT_OWNERS = [constants.DEVICE_OWNER_DHCP,
7979
constants.DEVICE_OWNER_DISTRIBUTED,
8080
constants.DEVICE_OWNER_AGENT_GW]
81+
82+
# The lowest binding index for L3 agents and DHCP agents.
83+
LOWEST_AGENT_BINDING_INDEX = 1

neutron/db/l3_agentschedulers_db.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@
2525
import oslo_messaging
2626

2727
from neutron.agent.common import utils as agent_utils
28+
from neutron.common import _constants as n_const
2829
from neutron.conf.db import l3_agentschedulers_db
2930
from neutron.db import agentschedulers_db
30-
from neutron.db.models import l3agent as rb_model
3131
from neutron.extensions import l3agentscheduler
3232
from neutron.extensions import router_availability_zone as router_az
3333
from neutron.objects import agent as ag_obj
@@ -522,7 +522,7 @@ def get_vacant_binding_index(self, context, router_id,
522522
bindings = rb_obj.RouterL3AgentBinding.get_objects(
523523
context, _pager=pager, router_id=router_id)
524524
return base_scheduler.get_vacant_binding_index(
525-
num_agents, bindings, rb_model.LOWEST_BINDING_INDEX,
525+
num_agents, bindings, n_const.LOWEST_AGENT_BINDING_INDEX,
526526
force_scheduling=is_manual_scheduling)
527527

528528

neutron/db/models/l3agent.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,9 @@
1515
import sqlalchemy as sa
1616
from sqlalchemy import orm
1717

18+
from neutron.common import _constants as n_const
1819
from neutron.db.models import agent as agent_model
1920

20-
LOWEST_BINDING_INDEX = 1
21-
2221

2322
class RouterL3AgentBinding(model_base.BASEV2):
2423
"""Represents binding between neutron routers and L3 agents."""
@@ -37,5 +36,6 @@ class RouterL3AgentBinding(model_base.BASEV2):
3736
l3_agent_id = sa.Column(sa.String(36),
3837
sa.ForeignKey("agents.id", ondelete='CASCADE'),
3938
primary_key=True)
40-
binding_index = sa.Column(sa.Integer, nullable=False,
41-
server_default=str(LOWEST_BINDING_INDEX))
39+
binding_index = sa.Column(
40+
sa.Integer, nullable=False,
41+
server_default=str(n_const.LOWEST_AGENT_BINDING_INDEX))

neutron/db/network_dhcp_agent_binding/models.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,10 @@
1414
import sqlalchemy as sa
1515
from sqlalchemy import orm
1616

17+
from neutron.common import _constants as n_const
1718
from neutron.db.models import agent as agent_model
1819

1920

20-
LOWEST_BINDING_INDEX = 1
21-
22-
2321
class NetworkDhcpAgentBinding(model_base.BASEV2):
2422
"""Represents binding between neutron networks and DHCP agents."""
2523

@@ -38,5 +36,6 @@ class NetworkDhcpAgentBinding(model_base.BASEV2):
3836
sa.ForeignKey("agents.id",
3937
ondelete='CASCADE'),
4038
primary_key=True)
41-
binding_index = sa.Column(sa.Integer, nullable=False,
42-
server_default=str(LOWEST_BINDING_INDEX))
39+
binding_index = sa.Column(
40+
sa.Integer, nullable=False,
41+
server_default=str(n_const.LOWEST_AGENT_BINDING_INDEX))

neutron/objects/l3agent.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
from sqlalchemy import sql
1919

20+
from neutron.common import _constants as n_const
2021
from neutron.db.models import agent as agent_model
2122
from neutron.db.models import l3_attrs
2223
from neutron.db.models import l3agent
@@ -36,7 +37,7 @@ class RouterL3AgentBinding(base.NeutronDbObject):
3637
'router_id': common_types.UUIDField(),
3738
'l3_agent_id': common_types.UUIDField(),
3839
'binding_index': obj_fields.IntegerField(
39-
default=l3agent.LOWEST_BINDING_INDEX),
40+
default=n_const.LOWEST_AGENT_BINDING_INDEX),
4041
}
4142

4243
# TODO(ihrachys) return OVO objects not models

neutron/scheduler/base_scheduler.py

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -99,24 +99,44 @@ def get_vacant_binding_index(num_agents, bindings, lowest_binding_index,
9999
always return an index, even if this number
100100
exceeds the maximum configured number of agents.
101101
"""
102-
binding_indices = [b.binding_index for b in bindings]
103-
all_indices = set(range(lowest_binding_index, num_agents + 1))
104-
open_slots = sorted(list(all_indices - set(binding_indices)))
102+
def get_open_slots(binding_indices, lowest_binding_index, max_number):
103+
"""Returns an ordered list of free slots
104+
105+
This list starts from the lowest available binding index. The number
106+
of open slots and "binding_indices" (those already taken), must be
107+
equal to "max_number". The list returned can be [], if
108+
len(max_number) == len(binding_indices) (that means there are no free
109+
slots).
110+
"""
111+
# NOTE(ralonsoh): check LP#2006496 for more context. The DHCP/router
112+
# binding indexes could not be a sequential list starting from
113+
# lowest_binding_index (that is usually 1).
114+
open_slots = set(binding_indices)
115+
idx = lowest_binding_index
116+
while len(open_slots) < max_number:
117+
# Increase sequentially the "open_slots" set until we have the
118+
# required number of slots, that is "num_agents".
119+
open_slots.add(idx)
120+
idx += 1
121+
122+
# Remove those indices already used.
123+
open_slots -= set(binding_indices)
124+
return sorted(list(open_slots))
105125

126+
binding_indices = [b.binding_index for b in bindings]
127+
open_slots = get_open_slots(binding_indices, lowest_binding_index,
128+
num_agents)
106129
if open_slots:
107130
return open_slots[0]
108131

109132
if not force_scheduling:
110133
return -1
111134

112-
# Last chance: if this is a manual scheduling, we're gonna allow
135+
# Last chance: if this is a manual scheduling, we're going to allow
113136
# creation of a binding_index even if it will exceed
114-
# dhcp_agents_per_network.
115-
if max(binding_indices) == len(binding_indices):
116-
return max(binding_indices) + 1
117-
else:
118-
# Find binding index set gaps and return first free one.
119-
all_indices = set(range(lowest_binding_index,
120-
max(binding_indices) + 1))
121-
open_slots = sorted(list(all_indices - set(binding_indices)))
122-
return open_slots[0]
137+
# dhcp_agents_per_network/max_l3_agents_per_router.
138+
while not open_slots:
139+
num_agents += 1
140+
open_slots = get_open_slots(binding_indices, lowest_binding_index,
141+
num_agents)
142+
return open_slots[0]

neutron/scheduler/dhcp_agent_scheduler.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from oslo_log import log as logging
2626

2727
from neutron.agent.common import utils as agent_utils
28-
from neutron.db.network_dhcp_agent_binding import models as ndab_model
28+
from neutron.common import _constants as n_const
2929
from neutron.objects import agent as agent_obj
3030
from neutron.objects import network
3131
from neutron.scheduler import base_resource_filter
@@ -195,7 +195,7 @@ def get_vacant_network_dhcp_agent_binding_index(
195195
bindings = network.NetworkDhcpAgentBinding.get_objects(
196196
context, network_id=network_id)
197197
return base_scheduler.get_vacant_binding_index(
198-
num_agents, bindings, ndab_model.LOWEST_BINDING_INDEX,
198+
num_agents, bindings, n_const.LOWEST_AGENT_BINDING_INDEX,
199199
force_scheduling=force_scheduling)
200200

201201
def bind(self, context, agents, network_id, force_scheduling=False):
@@ -205,7 +205,7 @@ def bind(self, context, agents, network_id, force_scheduling=False):
205205
for agent in agents:
206206
binding_index = self.get_vacant_network_dhcp_agent_binding_index(
207207
context, network_id, force_scheduling)
208-
if binding_index < ndab_model.LOWEST_BINDING_INDEX:
208+
if binding_index < n_const.LOWEST_AGENT_BINDING_INDEX:
209209
LOG.debug('Unable to find a vacant binding_index for '
210210
'network %(network_id)s and agent %(agent_id)s',
211211
{'network_id': network_id,

neutron/scheduler/l3_agent_scheduler.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from oslo_db import exception as db_exc
2727
from oslo_log import log as logging
2828

29+
from neutron.common import _constants as n_const
2930
from neutron.common import utils
3031
from neutron.conf.db import l3_hamode_db
3132
from neutron.db.models import l3agent as rb_model
@@ -185,7 +186,7 @@ def bind_router(self, plugin, context, router_id, agent_id,
185186
return
186187

187188
if not is_ha:
188-
binding_index = rb_model.LOWEST_BINDING_INDEX
189+
binding_index = n_const.LOWEST_AGENT_BINDING_INDEX
189190
if rb_obj.RouterL3AgentBinding.objects_exist(
190191
context, router_id=router_id, binding_index=binding_index):
191192
LOG.debug('Non-HA router %s has already been scheduled',
@@ -194,7 +195,7 @@ def bind_router(self, plugin, context, router_id, agent_id,
194195
else:
195196
binding_index = plugin.get_vacant_binding_index(
196197
context, router_id, is_manual_scheduling)
197-
if binding_index < rb_model.LOWEST_BINDING_INDEX:
198+
if binding_index < n_const.LOWEST_AGENT_BINDING_INDEX:
198199
LOG.debug('Unable to find a vacant binding_index for '
199200
'router %(router_id)s and agent %(agent_id)s',
200201
{'router_id': router_id,

neutron/tests/unit/scheduler/test_base_scheduler.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,21 @@ def test_get_vacant_binding_index_several_agents(self):
3838
3, [mock.Mock(binding_index=1), mock.Mock(binding_index=3)], 1)
3939
self.assertEqual(2, ret)
4040

41+
# Binding list starting in 2, two elements, required three.
42+
ret = base_scheduler.get_vacant_binding_index(
43+
3, [mock.Mock(binding_index=2), mock.Mock(binding_index=3)], 1)
44+
self.assertEqual(1, ret)
45+
46+
# Binding list starting in 2, two elements, required two.
47+
ret = base_scheduler.get_vacant_binding_index(
48+
2, [mock.Mock(binding_index=2), mock.Mock(binding_index=3)], 1)
49+
self.assertEqual(-1, ret)
50+
51+
# Binding list starting in 2, two elements, required one.
52+
ret = base_scheduler.get_vacant_binding_index(
53+
1, [mock.Mock(binding_index=2), mock.Mock(binding_index=3)], 1)
54+
self.assertEqual(-1, ret)
55+
4156
def test_get_vacant_binding_index_force_scheduling(self):
4257
ret = base_scheduler.get_vacant_binding_index(
4358
3, [mock.Mock(binding_index=1), mock.Mock(binding_index=2),
@@ -50,3 +65,9 @@ def test_get_vacant_binding_index_force_scheduling(self):
5065
mock.Mock(binding_index=3), mock.Mock(binding_index=4),
5166
mock.Mock(binding_index=5)], 1, force_scheduling=True)
5267
self.assertEqual(6, ret)
68+
69+
ret = base_scheduler.get_vacant_binding_index(
70+
3, [mock.Mock(binding_index=2), mock.Mock(binding_index=3),
71+
mock.Mock(binding_index=4), mock.Mock(binding_index=5),
72+
mock.Mock(binding_index=6)], 1, force_scheduling=True)
73+
self.assertEqual(1, ret)

0 commit comments

Comments
 (0)