Merge "L3 agent scheduler should return a valid index if manual scheduling" into stable/ussuri
This commit is contained in:
commit
d4f22c6926
|
@ -34,6 +34,7 @@ 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
|
||||
from neutron.scheduler import base_scheduler
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
@ -520,21 +521,9 @@ class L3AgentSchedulerDbMixin(l3agentscheduler.L3AgentSchedulerPluginBase,
|
|||
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))
|
||||
open_slots = sorted(list(all_indicies - set(binding_indices)))
|
||||
|
||||
if open_slots:
|
||||
return open_slots[0]
|
||||
|
||||
# Last chance: if this is a manual scheduling, we're gonna allow
|
||||
# creation of a binding_index even if it will exceed
|
||||
# max_l3_agents_per_router.
|
||||
if is_manual_scheduling:
|
||||
return max(all_indicies) + 1
|
||||
|
||||
return -1
|
||||
return base_scheduler.get_vacant_binding_index(
|
||||
num_agents, bindings, rb_model.LOWEST_BINDING_INDEX,
|
||||
force_scheduling=is_manual_scheduling)
|
||||
|
||||
|
||||
class AZL3AgentSchedulerDbMixin(L3AgentSchedulerDbMixin,
|
||||
|
|
|
@ -85,3 +85,40 @@ class BaseWeightScheduler(BaseScheduler):
|
|||
chosen_agents = sorted(resource_hostable_agents,
|
||||
key=attrgetter('load'))[0:num_agents_needed]
|
||||
return chosen_agents
|
||||
|
||||
|
||||
def get_vacant_binding_index(num_agents, bindings, lowest_binding_index,
|
||||
force_scheduling=False):
|
||||
"""Return a vacant binding_index to use and whether or not it exists.
|
||||
|
||||
This method can be used with DHCP and L3 agent schedulers. It will return
|
||||
the lowest vacant index for one of those agents.
|
||||
:param num_agents: (int) number of agents (DHCP, L3) already scheduled
|
||||
:param bindings: (NetworkDhcpAgentBinding, RouterL3AgentBinding) agent
|
||||
binding object, must have "binding_index" field.
|
||||
:param lowest_binding_index: (int) lowest index number to be scheduled.
|
||||
:param force_scheduling: (optional)(boolean) if enabled, the method will
|
||||
always return an index, even if this number
|
||||
exceeds the maximum configured number of agents.
|
||||
"""
|
||||
binding_indices = [b.binding_index for b in bindings]
|
||||
all_indices = set(range(lowest_binding_index, num_agents + 1))
|
||||
open_slots = sorted(list(all_indices - set(binding_indices)))
|
||||
|
||||
if open_slots:
|
||||
return open_slots[0]
|
||||
|
||||
if not force_scheduling:
|
||||
return -1
|
||||
|
||||
# Last chance: if this is a manual scheduling, we're gonna allow
|
||||
# creation of a binding_index even if it will exceed
|
||||
# dhcp_agents_per_network.
|
||||
if max(binding_indices) == len(binding_indices):
|
||||
return max(binding_indices) + 1
|
||||
else:
|
||||
# Find binding index set gaps and return first free one.
|
||||
all_indices = set(range(lowest_binding_index,
|
||||
max(binding_indices) + 1))
|
||||
open_slots = sorted(list(all_indices - set(binding_indices)))
|
||||
return open_slots[0]
|
||||
|
|
|
@ -192,32 +192,11 @@ class DhcpFilter(base_resource_filter.BaseResourceFilter):
|
|||
num_agents = agent_obj.Agent.count(
|
||||
context, agent_type=constants.AGENT_TYPE_DHCP)
|
||||
num_agents = min(num_agents, cfg.CONF.dhcp_agents_per_network)
|
||||
|
||||
bindings = network.NetworkDhcpAgentBinding.get_objects(
|
||||
context, network_id=network_id)
|
||||
|
||||
binding_indices = [b.binding_index for b in bindings]
|
||||
all_indices = set(range(ndab_model.LOWEST_BINDING_INDEX,
|
||||
num_agents + 1))
|
||||
open_slots = sorted(list(all_indices - set(binding_indices)))
|
||||
|
||||
if open_slots:
|
||||
return open_slots[0]
|
||||
|
||||
if not force_scheduling:
|
||||
return -1
|
||||
|
||||
# Last chance: if this is a manual scheduling, we're gonna allow
|
||||
# creation of a binding_index even if it will exceed
|
||||
# dhcp_agents_per_network.
|
||||
if max(binding_indices) == len(binding_indices):
|
||||
return max(binding_indices) + 1
|
||||
else:
|
||||
# Find binding index set gaps and return first free one.
|
||||
all_indices = set(range(ndab_model.LOWEST_BINDING_INDEX,
|
||||
max(binding_indices) + 1))
|
||||
open_slots = sorted(list(all_indices - set(binding_indices)))
|
||||
return open_slots[0]
|
||||
return base_scheduler.get_vacant_binding_index(
|
||||
num_agents, bindings, ndab_model.LOWEST_BINDING_INDEX,
|
||||
force_scheduling=force_scheduling)
|
||||
|
||||
def bind(self, context, agents, network_id, force_scheduling=False):
|
||||
"""Bind the network to the agents."""
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
# Copyright 2020 Red Hat Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
from unittest import mock
|
||||
|
||||
from neutron.scheduler import base_scheduler
|
||||
|
||||
from neutron.tests import base
|
||||
|
||||
|
||||
class GetVacantBindingFilterCase(base.BaseTestCase):
|
||||
|
||||
def test_get_vacant_binding_index_no_agents(self):
|
||||
ret = base_scheduler.get_vacant_binding_index(0, [], 1)
|
||||
self.assertEqual(-1, ret)
|
||||
|
||||
def test_get_vacant_binding_index_several_agents(self):
|
||||
ret = base_scheduler.get_vacant_binding_index(1, [], 1)
|
||||
self.assertEqual(1, ret)
|
||||
|
||||
ret = base_scheduler.get_vacant_binding_index(
|
||||
1, [mock.Mock(binding_index=1)], 1)
|
||||
self.assertEqual(-1, ret)
|
||||
|
||||
ret = base_scheduler.get_vacant_binding_index(
|
||||
3, [mock.Mock(binding_index=1), mock.Mock(binding_index=3)], 1)
|
||||
self.assertEqual(2, ret)
|
||||
|
||||
def test_get_vacant_binding_index_force_scheduling(self):
|
||||
ret = base_scheduler.get_vacant_binding_index(
|
||||
3, [mock.Mock(binding_index=1), mock.Mock(binding_index=2),
|
||||
mock.Mock(binding_index=3), mock.Mock(binding_index=5),
|
||||
mock.Mock(binding_index=7)], 1, force_scheduling=True)
|
||||
self.assertEqual(4, ret)
|
||||
|
||||
ret = base_scheduler.get_vacant_binding_index(
|
||||
3, [mock.Mock(binding_index=1), mock.Mock(binding_index=2),
|
||||
mock.Mock(binding_index=3), mock.Mock(binding_index=4),
|
||||
mock.Mock(binding_index=5)], 1, force_scheduling=True)
|
||||
self.assertEqual(6, ret)
|
|
@ -31,7 +31,6 @@ from neutron.objects import agent
|
|||
from neutron.objects import network as network_obj
|
||||
from neutron.scheduler import dhcp_agent_scheduler
|
||||
from neutron.services.segments import db as segments_service_db
|
||||
from neutron.tests import base
|
||||
from neutron.tests.common import helpers
|
||||
from neutron.tests.unit.plugins.ml2 import test_plugin
|
||||
from neutron.tests.unit import testlib_api
|
||||
|
@ -931,61 +930,3 @@ class DHCPAgentAZAwareWeightSchedulerTestCase(TestDhcpSchedulerBaseTestCase):
|
|||
# which is also not in the same az as the first selected agent.
|
||||
self.assertEqual('az2-host2', agents_select[1]['host'])
|
||||
self.assertEqual('az2', agents_select[1]['availability_zone'])
|
||||
|
||||
|
||||
class DhcpFilterTestCase(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(DhcpFilterTestCase, self).setUp()
|
||||
self.mock_agent_count = mock.patch.object(agent.Agent, 'count').start()
|
||||
self.mock_dhcpagent_bindings = mock.patch.object(
|
||||
network_obj.NetworkDhcpAgentBinding, 'get_objects').start()
|
||||
cfg.CONF.set_override('dhcp_agents_per_network', 3)
|
||||
self.dhcp_filter = dhcp_agent_scheduler.DhcpFilter()
|
||||
|
||||
def test_get_vacant_network_dhcp_agent_binding_index_no_agents(self):
|
||||
self.mock_agent_count.return_value = 0
|
||||
self.mock_dhcpagent_bindings.return_value = []
|
||||
ret = self.dhcp_filter.get_vacant_network_dhcp_agent_binding_index(
|
||||
mock.ANY, mock.ANY, False)
|
||||
self.assertEqual(-1, ret)
|
||||
|
||||
def test_get_vacant_network_dhcp_agent_binding_index_several_agents(self):
|
||||
self.mock_agent_count.return_value = 1
|
||||
self.mock_dhcpagent_bindings.return_value = []
|
||||
ret = self.dhcp_filter.get_vacant_network_dhcp_agent_binding_index(
|
||||
mock.ANY, mock.ANY, False)
|
||||
self.assertEqual(1, ret)
|
||||
|
||||
self.mock_agent_count.return_value = 1
|
||||
self.mock_dhcpagent_bindings.return_value = [
|
||||
mock.Mock(binding_index=1)]
|
||||
ret = self.dhcp_filter.get_vacant_network_dhcp_agent_binding_index(
|
||||
mock.ANY, mock.ANY, False)
|
||||
self.assertEqual(-1, ret)
|
||||
|
||||
self.mock_agent_count.return_value = 3
|
||||
self.mock_dhcpagent_bindings.return_value = [
|
||||
mock.Mock(binding_index=1), mock.Mock(binding_index=3)]
|
||||
ret = self.dhcp_filter.get_vacant_network_dhcp_agent_binding_index(
|
||||
mock.ANY, mock.ANY, False)
|
||||
self.assertEqual(2, ret)
|
||||
|
||||
def test_get_vacant_network_dhcp_agent_binding_index_force_sched(self):
|
||||
self.mock_agent_count.return_value = 3
|
||||
self.mock_dhcpagent_bindings.return_value = [
|
||||
mock.Mock(binding_index=1), mock.Mock(binding_index=2),
|
||||
mock.Mock(binding_index=3), mock.Mock(binding_index=5),
|
||||
mock.Mock(binding_index=7)]
|
||||
ret = self.dhcp_filter.get_vacant_network_dhcp_agent_binding_index(
|
||||
mock.ANY, mock.ANY, True)
|
||||
self.assertEqual(4, ret)
|
||||
|
||||
self.mock_agent_count.return_value = 3
|
||||
self.mock_dhcpagent_bindings.return_value = [
|
||||
mock.Mock(binding_index=1), mock.Mock(binding_index=2),
|
||||
mock.Mock(binding_index=3), mock.Mock(binding_index=4),
|
||||
mock.Mock(binding_index=5)]
|
||||
ret = self.dhcp_filter.get_vacant_network_dhcp_agent_binding_index(
|
||||
mock.ANY, mock.ANY, True)
|
||||
self.assertEqual(6, ret)
|
||||
|
|
Loading…
Reference in New Issue