neutron/neutron/tests/unit/scheduler/test_dhcp_agent_scheduler.py

921 lines
41 KiB
Python

# Copyright 2014 OpenStack Foundation
#
# 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.
import datetime
import random
import mock
from neutron_lib import constants
from neutron_lib import context
from neutron_lib.exceptions import dhcpagentscheduler as das_exc
from oslo_config import cfg
from oslo_utils import importutils
from oslo_utils import uuidutils
import testscenarios
from neutron.db import agentschedulers_db as sched_db
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.common import helpers
from neutron.tests.unit.plugins.ml2 import test_plugin
from neutron.tests.unit import testlib_api
# Required to generate tests from scenarios. Not compatible with nose.
load_tests = testscenarios.load_tests_apply_scenarios
HOST_C = 'host-c'
HOST_D = 'host-d'
class TestDhcpSchedulerBaseTestCase(testlib_api.SqlTestCase):
CORE_PLUGIN = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2'
def setUp(self):
super(TestDhcpSchedulerBaseTestCase, self).setUp()
self.setup_coreplugin(self.CORE_PLUGIN)
self.ctx = context.get_admin_context()
self.network = {'id': uuidutils.generate_uuid()}
self.network_id = self.network['id']
self._save_networks([self.network_id])
def _create_and_set_agents_down(self, hosts, down_agent_count=0,
admin_state_up=True,
az=helpers.DEFAULT_AZ):
agents = []
for i, host in enumerate(hosts):
is_alive = i >= down_agent_count
agents.append(helpers.register_dhcp_agent(
host,
admin_state_up=admin_state_up,
alive=is_alive,
az=az))
return agents
def _save_networks(self, networks):
for network_id in networks:
network_obj.Network(self.ctx, id=network_id).create()
def _test_schedule_bind_network(self, agents, network_id):
scheduler = dhcp_agent_scheduler.ChanceScheduler()
scheduler.resource_filter.bind(self.ctx, agents, network_id)
binding_objs = network_obj.NetworkDhcpAgentBinding.get_objects(
self.ctx, network_id=network_id)
self.assertEqual(len(agents), len(binding_objs))
for result in binding_objs:
self.assertEqual(network_id, result.network_id)
class TestDhcpScheduler(TestDhcpSchedulerBaseTestCase):
def test_schedule_bind_network_single_agent(self):
agents = self._create_and_set_agents_down(['host-a'])
self._test_schedule_bind_network(agents, self.network_id)
def test_schedule_bind_network_multi_agents(self):
agents = self._create_and_set_agents_down(['host-a', 'host-b'])
self._test_schedule_bind_network(agents, self.network_id)
def test_schedule_bind_network_multi_agent_fail_one(self):
agents = self._create_and_set_agents_down(['host-a'])
self._test_schedule_bind_network(agents, self.network_id)
with mock.patch.object(dhcp_agent_scheduler.LOG, 'info') as fake_log:
self._test_schedule_bind_network(agents, self.network_id)
self.assertEqual(1, fake_log.call_count)
def _test_get_agents_and_scheduler_for_dead_agent(self):
agents = self._create_and_set_agents_down(['dead_host', 'alive_host'],
1)
dead_agent = [agents[0]]
alive_agent = [agents[1]]
self._test_schedule_bind_network(dead_agent, self.network_id)
scheduler = dhcp_agent_scheduler.ChanceScheduler()
return dead_agent, alive_agent, scheduler
def _test_reschedule_vs_network_on_dead_agent(self,
active_hosts_only):
dead_agent, alive_agent, scheduler = (
self._test_get_agents_and_scheduler_for_dead_agent())
network = {'id': self.network_id}
plugin = mock.Mock()
plugin.get_subnets.return_value = [{"network_id": self.network_id,
"enable_dhcp": True}]
plugin.get_agent_objects.return_value = dead_agent + alive_agent
plugin.filter_hosts_with_network_access.side_effect = (
lambda context, network_id, hosts: hosts)
if active_hosts_only:
plugin.get_dhcp_agents_hosting_networks.return_value = []
self.assertTrue(
scheduler.schedule(
plugin, self.ctx, network))
else:
plugin.get_dhcp_agents_hosting_networks.return_value = dead_agent
self.assertFalse(
scheduler.schedule(
plugin, self.ctx, network))
def test_network_rescheduled_when_db_returns_active_hosts(self):
self._test_reschedule_vs_network_on_dead_agent(True)
def test_network_not_rescheduled_when_db_returns_all_hosts(self):
self._test_reschedule_vs_network_on_dead_agent(False)
def _get_agent_binding_from_db(self, agent):
return network_obj.NetworkDhcpAgentBinding.get_objects(
self.ctx, dhcp_agent_id=agent[0].id)
def _test_auto_reschedule_vs_network_on_dead_agent(self,
active_hosts_only):
dead_agent, alive_agent, scheduler = (
self._test_get_agents_and_scheduler_for_dead_agent())
plugin = mock.Mock()
plugin.get_subnets.return_value = [{"network_id": self.network_id,
"enable_dhcp": True,
"segment_id": None}]
plugin.get_network.return_value = self.network
if active_hosts_only:
plugin.get_dhcp_agents_hosting_networks.return_value = []
else:
plugin.get_dhcp_agents_hosting_networks.return_value = dead_agent
network_assigned_to_dead_agent = (
self._get_agent_binding_from_db(dead_agent))
self.assertEqual(1, len(network_assigned_to_dead_agent))
self.assertTrue(
scheduler.auto_schedule_networks(
plugin, self.ctx, "alive_host"))
network_assigned_to_dead_agent = (
self._get_agent_binding_from_db(dead_agent))
network_assigned_to_alive_agent = (
self._get_agent_binding_from_db(alive_agent))
self.assertEqual(1, len(network_assigned_to_dead_agent))
if active_hosts_only:
self.assertEqual(1, len(network_assigned_to_alive_agent))
else:
self.assertEqual(0, len(network_assigned_to_alive_agent))
def test_network_auto_rescheduled_when_db_returns_active_hosts(self):
self._test_auto_reschedule_vs_network_on_dead_agent(True)
def test_network_not_auto_rescheduled_when_db_returns_all_hosts(self):
self._test_auto_reschedule_vs_network_on_dead_agent(False)
class TestAutoScheduleNetworks(TestDhcpSchedulerBaseTestCase):
"""Unit test scenarios for ChanceScheduler.auto_schedule_networks.
network_present
Network is present or not
enable_dhcp
Dhcp is enabled or disabled in the subnet of the network
scheduled_already
Network is already scheduled to the agent or not
agent_down
Dhcp agent is down or alive
valid_host
If true, then an valid host is passed to schedule the network,
else an invalid host is passed.
az_hints
'availability_zone_hints' of the network.
note that default 'availability_zone' of an agent is 'nova'.
"""
scenarios = [
('Network present',
dict(network_present=True,
enable_dhcp=True,
scheduled_already=False,
agent_down=False,
valid_host=True,
az_hints=[])),
('No network',
dict(network_present=False,
enable_dhcp=False,
scheduled_already=False,
agent_down=False,
valid_host=True,
az_hints=[])),
('Network already scheduled',
dict(network_present=True,
enable_dhcp=True,
scheduled_already=True,
agent_down=False,
valid_host=True,
az_hints=[])),
('Agent down',
dict(network_present=True,
enable_dhcp=True,
scheduled_already=False,
agent_down=False,
valid_host=True,
az_hints=[])),
('dhcp disabled',
dict(network_present=True,
enable_dhcp=False,
scheduled_already=False,
agent_down=False,
valid_host=False,
az_hints=[])),
('Invalid host',
dict(network_present=True,
enable_dhcp=True,
scheduled_already=False,
agent_down=False,
valid_host=False,
az_hints=[])),
('Match AZ',
dict(network_present=True,
enable_dhcp=True,
scheduled_already=False,
agent_down=False,
valid_host=True,
az_hints=['nova'])),
('Not match AZ',
dict(network_present=True,
enable_dhcp=True,
scheduled_already=False,
agent_down=False,
valid_host=True,
az_hints=['not-match'])),
]
def test_auto_schedule_network(self):
plugin = mock.MagicMock()
plugin.get_subnets.return_value = (
[{"network_id": self.network_id, "enable_dhcp": self.enable_dhcp,
"segment_id": None}] if self.network_present else [])
plugin.get_network.return_value = {'availability_zone_hints':
self.az_hints}
scheduler = dhcp_agent_scheduler.ChanceScheduler()
if self.network_present:
down_agent_count = 1 if self.agent_down else 0
agents = self._create_and_set_agents_down(
['host-a'], down_agent_count=down_agent_count)
if self.scheduled_already:
self._test_schedule_bind_network(agents, self.network_id)
expected_result = (self.network_present and self.enable_dhcp)
expected_hosted_agents = (1 if expected_result and
self.valid_host else 0)
if (self.az_hints and
agents[0]['availability_zone'] not in self.az_hints):
expected_hosted_agents = 0
host = "host-a" if self.valid_host else "host-b"
observed_ret_value = scheduler.auto_schedule_networks(
plugin, self.ctx, host)
self.assertEqual(expected_result, observed_ret_value)
count_hosted_agents = network_obj.NetworkDhcpAgentBinding.count(
self.ctx)
self.assertEqual(expected_hosted_agents, count_hosted_agents)
class TestAutoScheduleSegments(test_plugin.Ml2PluginV2TestCase,
TestDhcpSchedulerBaseTestCase):
"""Unit test scenarios for ChanceScheduler"""
CORE_PLUGIN = 'neutron.plugins.ml2.plugin.Ml2Plugin'
def setUp(self):
super(TestAutoScheduleSegments, self).setUp()
self.plugin = self.driver
self.segments_plugin = importutils.import_object(
'neutron.services.segments.plugin.Plugin')
self.ctx = context.get_admin_context()
# Remove MissingAuthPlugin exception from logs
mock.patch(
'neutron.notifiers.batch_notifier.BatchNotifier._notify').start()
def _create_network(self):
net = self.plugin.create_network(
self.ctx,
{'network': {'name': 'name',
'tenant_id': 'tenant_one',
'admin_state_up': True,
'shared': True}})
return net['id']
def _create_segment(self, network_id):
seg = self.segments_plugin.create_segment(
self.ctx,
{'segment': {'network_id': network_id,
'name': None, 'description': None,
'physical_network': 'physnet1',
'network_type': 'vlan',
'segmentation_id': constants.ATTR_NOT_SPECIFIED}})
return seg['id']
def _create_subnet(self, segment_id, network_id, cidr='192.168.10.0/24'):
subnet = self.plugin.create_subnet(
self.ctx,
{'subnet': {'name': 'name',
'ip_version': constants.IP_VERSION_4,
'network_id': network_id,
'cidr': cidr,
'gateway_ip': constants.ATTR_NOT_SPECIFIED,
'allocation_pools': constants.ATTR_NOT_SPECIFIED,
'dns_nameservers': constants.ATTR_NOT_SPECIFIED,
'host_routes': constants.ATTR_NOT_SPECIFIED,
'tenant_id': 'tenant_one',
'enable_dhcp': True,
'segment_id': segment_id}})
return subnet['id']
def test_auto_schedule_one_network_one_segment_one_subnet(self):
net_id = self._create_network()
seg_id = self._create_segment(net_id)
self._create_subnet(seg_id, net_id)
helpers.register_dhcp_agent(HOST_C)
segments_service_db.update_segment_host_mapping(
self.ctx, HOST_C, {seg_id})
scheduler = dhcp_agent_scheduler.ChanceScheduler()
observed_return_val = scheduler.auto_schedule_networks(
self.plugin, self.ctx, HOST_C)
self.assertTrue(observed_return_val)
agent1 = self.plugin.get_dhcp_agents_hosting_networks(
self.ctx, [net_id])
self.assertEqual(1, len(agent1))
self.assertEqual('host-c', agent1[0]['host'])
def test_auto_schedule_one_network_one_segment_two_subnet(self):
net_id = self._create_network()
seg_id = self._create_segment(net_id)
self._create_subnet(seg_id, net_id)
self._create_subnet(seg_id, net_id, '192.168.11.0/24')
helpers.register_dhcp_agent(HOST_C)
segments_service_db.update_segment_host_mapping(
self.ctx, HOST_C, {seg_id})
scheduler = dhcp_agent_scheduler.ChanceScheduler()
observed_return_val = scheduler.auto_schedule_networks(
self.plugin, self.ctx, HOST_C)
self.assertTrue(observed_return_val)
agent1 = self.plugin.get_dhcp_agents_hosting_networks(
self.ctx, [net_id])
self.assertEqual(1, len(agent1))
self.assertEqual('host-c', agent1[0]['host'])
def test_auto_schedule_one_network_two_segments_with_one_subnet_each(self):
net_id = self._create_network()
seg1_id = self._create_segment(net_id)
self._create_subnet(seg1_id, net_id)
helpers.register_dhcp_agent(HOST_D)
segments_service_db.update_segment_host_mapping(
self.ctx, HOST_D, {seg1_id})
scheduler = dhcp_agent_scheduler.ChanceScheduler()
observed_val_first_segment = scheduler.auto_schedule_networks(
self.plugin, self.ctx, HOST_D)
self.assertTrue(observed_val_first_segment)
agents = self.plugin.get_dhcp_agents_hosting_networks(
self.ctx, [net_id])
self.assertEqual(1, len(agents))
seg2_id = self._create_segment(net_id)
self._create_subnet(seg2_id, net_id, '192.168.11.0/24')
helpers.register_dhcp_agent(HOST_C)
segments_service_db.update_segment_host_mapping(
self.ctx, HOST_C, {seg2_id})
observed_val_second_segment = scheduler.auto_schedule_networks(
self.plugin, self.ctx, HOST_C)
self.assertTrue(observed_val_second_segment)
agents = self.plugin.get_dhcp_agents_hosting_networks(
self.ctx, [net_id])
self.assertEqual(2, len(agents))
class TestNetworksFailover(TestDhcpSchedulerBaseTestCase,
sched_db.DhcpAgentSchedulerDbMixin):
def test_reschedule_network_from_down_agent(self):
net_id = uuidutils.generate_uuid()
agents = self._create_and_set_agents_down(['host-a', 'host-b'], 1)
self._test_schedule_bind_network([agents[0]], self.network_id)
self._save_networks([net_id])
self._test_schedule_bind_network([agents[1]], net_id)
with mock.patch.object(self, 'remove_network_from_dhcp_agent') as rn,\
mock.patch.object(self,
'schedule_network',
return_value=[agents[1]]) as sch,\
mock.patch.object(self,
'get_network',
create=True,
return_value={'id': self.network_id}):
notifier = mock.MagicMock()
self.agent_notifiers[constants.AGENT_TYPE_DHCP] = notifier
self.remove_networks_from_down_agents()
rn.assert_called_with(mock.ANY, agents[0].id, self.network_id,
notify=False)
sch.assert_called_with(mock.ANY, {'id': self.network_id})
notifier.network_added_to_agent.assert_called_with(
mock.ANY, self.network_id, agents[1].host)
def _test_failed_rescheduling(self, rn_side_effect=None):
agents = self._create_and_set_agents_down(['host-a', 'host-b'], 1)
self._test_schedule_bind_network([agents[0]], self.network_id)
with mock.patch.object(self,
'remove_network_from_dhcp_agent',
side_effect=rn_side_effect) as rn,\
mock.patch.object(self,
'schedule_network',
return_value=None) as sch,\
mock.patch.object(self,
'get_network',
create=True,
return_value={'id': self.network_id}):
notifier = mock.MagicMock()
self.agent_notifiers[constants.AGENT_TYPE_DHCP] = notifier
self.remove_networks_from_down_agents()
rn.assert_called_with(mock.ANY, agents[0].id, self.network_id,
notify=False)
sch.assert_called_with(mock.ANY, {'id': self.network_id})
self.assertFalse(notifier.network_added_to_agent.called)
def test_reschedule_network_from_down_agent_failed(self):
self._test_failed_rescheduling()
def test_reschedule_network_from_down_agent_concurrent_removal(self):
self._test_failed_rescheduling(
rn_side_effect=das_exc.NetworkNotHostedByDhcpAgent(
network_id='foo', agent_id='bar'))
def _create_test_networks(self, num_net=0):
networks = [network_obj.Network(
self.ctx,
id=uuidutils.generate_uuid(),
name='network-%s' % (i))
for i in range(num_net)]
for net in networks:
net.create()
return [net.id for net in networks]
def _create_dhcp_agents(self):
timestamp = datetime.datetime.now()
dhcp_agent_ids = [uuidutils.generate_uuid() for x in range(2)]
dhcp_agent_1 = agent.Agent(self.ctx, id=dhcp_agent_ids[0],
agent_type='DHCP Agent',
topic='fake_topic',
host='fake_host',
binary='fake_binary',
created_at=timestamp,
started_at=timestamp,
heartbeat_timestamp=timestamp,
configurations={},
load=0)
dhcp_agent_1.create()
dhcp_agent_2 = agent.Agent(self.ctx, id=dhcp_agent_ids[1],
agent_type='DHCP Agent',
topic='fake_topic',
host='fake_host_1',
binary='fake_binary',
created_at=timestamp,
started_at=timestamp,
heartbeat_timestamp=timestamp,
configurations={},
load=0)
dhcp_agent_2.create()
return [dhcp_agent_1.id, dhcp_agent_2.id]
def test_filter_bindings(self):
self.ctx = context.get_admin_context()
dhcp_agt_ids = self._create_dhcp_agents()
network_ids = self._create_test_networks(num_net=4)
ndab_obj1 = network_obj.NetworkDhcpAgentBinding(self.ctx,
network_id=network_ids[0], dhcp_agent_id=dhcp_agt_ids[0])
ndab_obj1.create()
ndab_obj2 = network_obj.NetworkDhcpAgentBinding(self.ctx,
network_id=network_ids[1], dhcp_agent_id=dhcp_agt_ids[0])
ndab_obj2.create()
ndab_obj3 = network_obj.NetworkDhcpAgentBinding(self.ctx,
network_id=network_ids[2], dhcp_agent_id=dhcp_agt_ids[1])
ndab_obj3.create()
ndab_obj4 = network_obj.NetworkDhcpAgentBinding(self.ctx,
network_id=network_ids[3], dhcp_agent_id=dhcp_agt_ids[1])
ndab_obj4.create()
bindings_objs = network_obj.NetworkDhcpAgentBinding.get_objects(
self.ctx)
with mock.patch.object(self, 'agent_starting_up',
side_effect=[True, False]):
res = [b for b in self._filter_bindings(None, bindings_objs)]
# once per each agent id1 and id2
self.assertEqual(2, len(res))
res_ids = [b.network_id for b in res]
self.assertIn(network_ids[2], res_ids)
self.assertIn(network_ids[3], res_ids)
def test_reschedule_network_from_down_agent_failed_on_unexpected(self):
agents = self._create_and_set_agents_down(['host-a'], 1)
self._test_schedule_bind_network([agents[0]], self.network_id)
with mock.patch.object(
self, '_filter_bindings',
side_effect=Exception()):
# just make sure that no exception is raised
self.remove_networks_from_down_agents()
def test_reschedule_network_catches_exceptions_on_fetching_bindings(self):
with mock.patch('neutron_lib.context.get_admin_context') as get_ctx:
mock_ctx = mock.Mock()
get_ctx.return_value = mock_ctx
mock_ctx.session.query.side_effect = Exception()
# just make sure that no exception is raised
self.remove_networks_from_down_agents()
def test_reschedule_doesnt_occur_if_no_agents(self):
agents = self._create_and_set_agents_down(['host-a', 'host-b'], 2)
self._test_schedule_bind_network([agents[0]], self.network_id)
with mock.patch.object(
self, 'remove_network_from_dhcp_agent') as rn:
self.remove_networks_from_down_agents()
self.assertFalse(rn.called)
class DHCPAgentWeightSchedulerTestCase(test_plugin.Ml2PluginV2TestCase):
"""Unit test scenarios for WeightScheduler.schedule."""
def setUp(self):
super(DHCPAgentWeightSchedulerTestCase, self).setUp()
weight_scheduler = (
'neutron.scheduler.dhcp_agent_scheduler.WeightScheduler')
cfg.CONF.set_override('network_scheduler_driver', weight_scheduler)
self.plugin = self.driver
mock.patch.object(
self.plugin, 'filter_hosts_with_network_access',
side_effect=lambda context, network_id, hosts: hosts).start()
self.plugin.network_scheduler = importutils.import_object(
weight_scheduler)
cfg.CONF.set_override("dhcp_load_type", "networks")
self.segments_plugin = importutils.import_object(
'neutron.services.segments.plugin.Plugin')
self.ctx = context.get_admin_context()
def _create_network(self):
net = self.plugin.create_network(
self.ctx,
{'network': {'name': 'name',
'tenant_id': 'tenant_one',
'admin_state_up': True,
'shared': True}})
return net['id']
def _create_segment(self, network_id):
seg = self.segments_plugin.create_segment(
self.ctx,
{'segment': {'network_id': network_id,
'name': None, 'description': None,
'physical_network': 'physnet1',
'network_type': 'vlan',
'segmentation_id': constants.ATTR_NOT_SPECIFIED}})
return seg['id']
def test_scheduler_one_agents_per_network(self):
net_id = self._create_network()
helpers.register_dhcp_agent(HOST_C)
self.plugin.network_scheduler.schedule(self.plugin, self.ctx,
{'id': net_id})
agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx,
[net_id])
self.assertEqual(1, len(agents))
def test_scheduler_two_agents_per_network(self):
cfg.CONF.set_override('dhcp_agents_per_network', 2)
net_id = self._create_network()
helpers.register_dhcp_agent(HOST_C)
helpers.register_dhcp_agent(HOST_D)
self.plugin.network_scheduler.schedule(self.plugin, self.ctx,
{'id': net_id})
agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx,
[net_id])
self.assertEqual(2, len(agents))
def test_scheduler_no_active_agents(self):
net_id = self._create_network()
self.plugin.network_scheduler.schedule(self.plugin, self.ctx,
{'id': net_id})
agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx,
[net_id])
self.assertEqual(0, len(agents))
def test_scheduler_equal_distribution(self):
net_id_1 = self._create_network()
net_id_2 = self._create_network()
net_id_3 = self._create_network()
helpers.register_dhcp_agent(HOST_C)
helpers.register_dhcp_agent(HOST_D, networks=1)
self.plugin.network_scheduler.schedule(
self.plugin, context.get_admin_context(), {'id': net_id_1})
helpers.register_dhcp_agent(HOST_D, networks=2)
self.plugin.network_scheduler.schedule(
self.plugin, context.get_admin_context(), {'id': net_id_2})
helpers.register_dhcp_agent(HOST_C, networks=4)
self.plugin.network_scheduler.schedule(
self.plugin, context.get_admin_context(), {'id': net_id_3})
agent1 = self.plugin.get_dhcp_agents_hosting_networks(
self.ctx, [net_id_1])
agent2 = self.plugin.get_dhcp_agents_hosting_networks(
self.ctx, [net_id_2])
agent3 = self.plugin.get_dhcp_agents_hosting_networks(
self.ctx, [net_id_3])
self.assertEqual('host-c', agent1[0]['host'])
self.assertEqual('host-c', agent2[0]['host'])
self.assertEqual('host-d', agent3[0]['host'])
def _get_network_with_candidate_hosts(self, net_id, seg_id):
# expire the session so that the segment is fully reloaded on fetch,
# including its new host mapping
self.ctx.session.expire_all()
net = self.plugin.get_network(self.ctx, net_id)
seg = self.segments_plugin.get_segment(self.ctx, seg_id)
net['candidate_hosts'] = seg['hosts']
return net
def test_schedule_segment_one_hostable_agent(self):
net_id = self._create_network()
seg_id = self._create_segment(net_id)
helpers.register_dhcp_agent(HOST_C)
helpers.register_dhcp_agent(HOST_D)
segments_service_db.update_segment_host_mapping(
self.ctx, HOST_C, {seg_id})
net = self._get_network_with_candidate_hosts(net_id, seg_id)
agents = self.plugin.network_scheduler.schedule(
self.plugin, self.ctx, net)
self.assertEqual(1, len(agents))
self.assertEqual(HOST_C, agents[0].host)
def test_schedule_segment_many_hostable_agents(self):
net_id = self._create_network()
seg_id = self._create_segment(net_id)
helpers.register_dhcp_agent(HOST_C)
helpers.register_dhcp_agent(HOST_D)
segments_service_db.update_segment_host_mapping(
self.ctx, HOST_C, {seg_id})
segments_service_db.update_segment_host_mapping(
self.ctx, HOST_D, {seg_id})
net = self._get_network_with_candidate_hosts(net_id, seg_id)
agents = self.plugin.network_scheduler.schedule(
self.plugin, self.ctx, net)
self.assertEqual(1, len(agents))
self.assertIn(agents[0].host, [HOST_C, HOST_D])
def test_schedule_segment_no_host_mapping(self):
net_id = self._create_network()
seg_id = self._create_segment(net_id)
helpers.register_dhcp_agent(HOST_C)
helpers.register_dhcp_agent(HOST_D)
net = self.plugin.get_network(self.ctx, net_id)
seg = self.segments_plugin.get_segment(self.ctx, seg_id)
net['candidate_hosts'] = seg['hosts']
agents = self.plugin.network_scheduler.schedule(
self.plugin, self.ctx, net)
self.assertEqual(0, len(agents))
def test_schedule_segment_two_agents_per_segment(self):
cfg.CONF.set_override('dhcp_agents_per_network', 2)
net_id = self._create_network()
seg_id = self._create_segment(net_id)
helpers.register_dhcp_agent(HOST_C)
helpers.register_dhcp_agent(HOST_D)
segments_service_db.update_segment_host_mapping(
self.ctx, HOST_C, {seg_id})
segments_service_db.update_segment_host_mapping(
self.ctx, HOST_D, {seg_id})
net = self._get_network_with_candidate_hosts(net_id, seg_id)
agents = self.plugin.network_scheduler.schedule(
self.plugin, self.ctx, net)
self.assertEqual(2, len(agents))
self.assertIn(agents[0].host, [HOST_C, HOST_D])
self.assertIn(agents[1].host, [HOST_C, HOST_D])
def test_schedule_segment_two_agents_per_segment_one_hostable_agent(self):
cfg.CONF.set_override('dhcp_agents_per_network', 2)
net_id = self._create_network()
seg_id = self._create_segment(net_id)
helpers.register_dhcp_agent(HOST_C)
helpers.register_dhcp_agent(HOST_D)
segments_service_db.update_segment_host_mapping(
self.ctx, HOST_C, {seg_id})
net = self._get_network_with_candidate_hosts(net_id, seg_id)
agents = self.plugin.network_scheduler.schedule(
self.plugin, self.ctx, net)
self.assertEqual(1, len(agents))
self.assertEqual(HOST_C, agents[0].host)
class TestDhcpSchedulerFilter(TestDhcpSchedulerBaseTestCase,
sched_db.DhcpAgentSchedulerDbMixin):
def _test_get_dhcp_agents_hosting_networks(self, expected, **kwargs):
agents = self._create_and_set_agents_down(['host-a', 'host-b'], 1)
agents += self._create_and_set_agents_down(['host-c', 'host-d'], 1,
admin_state_up=False)
networks = kwargs.pop('networks', [self.network_id])
for network in networks:
self._test_schedule_bind_network(agents, network)
agents = self.get_dhcp_agents_hosting_networks(self.ctx,
networks,
**kwargs)
host_ids = set(a['host'] for a in agents)
self.assertEqual(expected, host_ids)
def test_get_dhcp_agents_hosting_networks_default(self):
self._test_get_dhcp_agents_hosting_networks({'host-a', 'host-b',
'host-c', 'host-d'})
def test_get_dhcp_agents_hosting_networks_active(self):
self._test_get_dhcp_agents_hosting_networks({'host-b', 'host-d'},
active=True)
def test_get_dhcp_agents_hosting_networks_admin_up(self):
self._test_get_dhcp_agents_hosting_networks({'host-a', 'host-b'},
admin_state_up=True)
def test_get_dhcp_agents_hosting_networks_active_admin_up(self):
self._test_get_dhcp_agents_hosting_networks({'host-b'},
active=True,
admin_state_up=True)
def test_get_dhcp_agents_hosting_networks_admin_down(self):
self._test_get_dhcp_agents_hosting_networks({'host-c', 'host-d'},
admin_state_up=False)
def test_get_dhcp_agents_hosting_networks_active_admin_down(self):
self._test_get_dhcp_agents_hosting_networks({'host-d'},
active=True,
admin_state_up=False)
def test_get_dhcp_agents_hosting_many_networks(self):
net_id = uuidutils.generate_uuid()
self._save_networks([net_id])
networks = [net_id, self.network_id]
self._test_get_dhcp_agents_hosting_networks({'host-a', 'host-b',
'host-c', 'host-d'},
networks=networks)
def test_get_dhcp_agents_host_network_filter_by_hosts(self):
self._test_get_dhcp_agents_hosting_networks({'host-a'},
hosts=['host-a'])
class DHCPAgentAZAwareWeightSchedulerTestCase(TestDhcpSchedulerBaseTestCase):
def setUp(self):
super(DHCPAgentAZAwareWeightSchedulerTestCase, self).setUp()
self.setup_coreplugin('ml2')
cfg.CONF.set_override("network_scheduler_driver",
'neutron.scheduler.dhcp_agent_scheduler.AZAwareWeightScheduler')
self.plugin = importutils.import_object('neutron.plugins.ml2.plugin.'
'Ml2Plugin')
mock.patch.object(
self.plugin, 'filter_hosts_with_network_access',
side_effect=lambda context, network_id, hosts: hosts).start()
cfg.CONF.set_override('dhcp_agents_per_network', 1)
cfg.CONF.set_override("dhcp_load_type", "networks")
def test_az_scheduler_one_az_hints(self):
net_id = uuidutils.generate_uuid()
self._save_networks([net_id])
helpers.register_dhcp_agent('az1-host1', networks=1, az='az1')
helpers.register_dhcp_agent('az1-host2', networks=2, az='az1')
helpers.register_dhcp_agent('az2-host1', networks=3, az='az2')
helpers.register_dhcp_agent('az2-host2', networks=4, az='az2')
self.plugin.network_scheduler.schedule(self.plugin, self.ctx,
{'id': net_id, 'availability_zone_hints': ['az2']})
agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx,
[net_id])
self.assertEqual(1, len(agents))
self.assertEqual('az2-host1', agents[0]['host'])
def test_az_scheduler_default_az_hints(self):
net_id = uuidutils.generate_uuid()
cfg.CONF.set_override('default_availability_zones', ['az1'])
self._save_networks([net_id])
helpers.register_dhcp_agent('az1-host1', networks=1, az='az1')
helpers.register_dhcp_agent('az1-host2', networks=2, az='az1')
helpers.register_dhcp_agent('az2-host1', networks=3, az='az2')
helpers.register_dhcp_agent('az2-host2', networks=4, az='az2')
self.plugin.network_scheduler.schedule(self.plugin, self.ctx,
{'id': net_id, 'availability_zone_hints': []})
agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx,
[net_id])
self.assertEqual(1, len(agents))
self.assertEqual('az1-host1', agents[0]['host'])
def test_az_scheduler_two_az_hints(self):
net_id = uuidutils.generate_uuid()
cfg.CONF.set_override('dhcp_agents_per_network', 2)
self._save_networks([net_id])
helpers.register_dhcp_agent('az1-host1', networks=1, az='az1')
helpers.register_dhcp_agent('az1-host2', networks=2, az='az1')
helpers.register_dhcp_agent('az2-host1', networks=3, az='az2')
helpers.register_dhcp_agent('az2-host2', networks=4, az='az2')
helpers.register_dhcp_agent('az3-host1', networks=5, az='az3')
helpers.register_dhcp_agent('az3-host2', networks=6, az='az3')
self.plugin.network_scheduler.schedule(self.plugin, self.ctx,
{'id': net_id, 'availability_zone_hints': ['az1', 'az3']})
agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx,
[net_id])
self.assertEqual(2, len(agents))
expected_hosts = set(['az1-host1', 'az3-host1'])
hosts = set([a['host'] for a in agents])
self.assertEqual(expected_hosts, hosts)
def test_az_scheduler_two_az_hints_one_available_az(self):
net_id = uuidutils.generate_uuid()
cfg.CONF.set_override('dhcp_agents_per_network', 2)
self._save_networks([net_id])
helpers.register_dhcp_agent('az1-host1', networks=1, az='az1')
helpers.register_dhcp_agent('az1-host2', networks=2, az='az1')
helpers.register_dhcp_agent('az2-host1', networks=3, alive=False,
az='az2')
helpers.register_dhcp_agent('az2-host2', networks=4,
admin_state_up=False, az='az2')
self.plugin.network_scheduler.schedule(self.plugin, self.ctx,
{'id': net_id, 'availability_zone_hints': ['az1', 'az2']})
agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx,
[net_id])
self.assertEqual(2, len(agents))
expected_hosts = set(['az1-host1', 'az1-host2'])
hosts = set([a['host'] for a in agents])
self.assertEqual(expected_hosts, hosts)
def _test_az_scheduler_no_az_hints(self, multiple_agent=False):
net_id = uuidutils.generate_uuid()
num_agent = 2 if multiple_agent else 1
cfg.CONF.set_override('dhcp_agents_per_network', num_agent)
self._save_networks([net_id])
helpers.register_dhcp_agent('az1-host1', networks=2, az='az1')
helpers.register_dhcp_agent('az1-host2', networks=3, az='az1')
helpers.register_dhcp_agent('az2-host1', networks=2, az='az2')
helpers.register_dhcp_agent('az2-host2', networks=1, az='az2')
self.plugin.network_scheduler.schedule(self.plugin, self.ctx,
{'id': net_id, 'availability_zone_hints': []})
agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx,
[net_id])
self.assertEqual(num_agent, len(agents))
if multiple_agent:
expected_hosts = set(['az1-host1', 'az2-host2'])
else:
expected_hosts = set(['az2-host2'])
hosts = {a['host'] for a in agents}
self.assertEqual(expected_hosts, hosts)
def test_az_scheduler_no_az_hints_multiple_agent(self):
self._test_az_scheduler_no_az_hints(multiple_agent=True)
def test_az_scheduler_no_az_hints_one_agent(self):
self._test_az_scheduler_no_az_hints()
def test_az_scheduler_select_az_with_least_weight(self):
self._save_networks([uuidutils.generate_uuid()])
dhcp_agents = []
# Register 6 dhcp agents in 3 AZs, every AZ will have 2 agents.
dhcp_agents.append(
helpers.register_dhcp_agent('az1-host1', networks=6, az='az1'))
dhcp_agents.append(
helpers.register_dhcp_agent('az1-host2', networks=5, az='az1'))
dhcp_agents.append(
helpers.register_dhcp_agent('az2-host1', networks=4, az='az2'))
dhcp_agents.append(
helpers.register_dhcp_agent('az2-host2', networks=3, az='az2'))
dhcp_agents.append(
helpers.register_dhcp_agent('az3-host1', networks=2, az='az3'))
dhcp_agents.append(
helpers.register_dhcp_agent('az3-host2', networks=1, az='az3'))
# Try multiple times to verify that the select of AZ scheduler will
# output stably.
for i in range(3):
# Shuffle the agents
random.shuffle(dhcp_agents)
# Select agents with empty resource_hosted_agents. This means each
# AZ will have same amount of agents scheduled (0 in this case)
agents_select = self.plugin.network_scheduler.select(
self.plugin, self.ctx, dhcp_agents, [], 2)
self.assertEqual(2, len(agents_select))
# The agent and az with least weight should always be selected
# first
self.assertEqual('az3-host2', agents_select[0]['host'])
self.assertEqual('az3', agents_select[0]['availability_zone'])
# The second selected agent should be the agent with least weight,
# 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'])