ac527e2429
In the unit test logs there is a MissingAuthPlugin exception printed several times. This patch aims to mock the notifier when it's not being tested. When it is being tested, like in the NovaSegmentNotifierTestCase, mocking the keystone auth prevents the exception from being thrown. Closes-Bug: #1683606 Change-Id: I63a1c887f6a7a43f2bddd48b86078e5836cbf438
872 lines
39 KiB
Python
872 lines
39 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 random
|
|
|
|
import mock
|
|
from neutron_lib import constants
|
|
from neutron_lib import context
|
|
from oslo_config import cfg
|
|
from oslo_utils import importutils
|
|
import testscenarios
|
|
|
|
from neutron.db import agentschedulers_db as sched_db
|
|
from neutron.db import common_db_mixin
|
|
from neutron.db import models_v2
|
|
from neutron.db.network_dhcp_agent_binding import models as ndab_model
|
|
from neutron.extensions import dhcpagentscheduler
|
|
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': 'foo_network_id'}
|
|
self.network_id = 'foo_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:
|
|
with self.ctx.session.begin(subtransactions=True):
|
|
self.ctx.session.add(models_v2.Network(id=network_id))
|
|
|
|
def _test_schedule_bind_network(self, agents, network_id):
|
|
scheduler = dhcp_agent_scheduler.ChanceScheduler()
|
|
scheduler.resource_filter.bind(self.ctx, agents, network_id)
|
|
results = self.ctx.session.query(
|
|
ndab_model.NetworkDhcpAgentBinding).filter_by(
|
|
network_id=network_id).all()
|
|
self.assertEqual(len(agents), len(results))
|
|
for result in results:
|
|
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_agents_db.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 self.ctx.session.query(
|
|
ndab_model.NetworkDhcpAgentBinding
|
|
).filter_by(dhcp_agent_id=agent[0].id).all()
|
|
|
|
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)
|
|
hosted_agents = self.ctx.session.query(
|
|
ndab_model.NetworkDhcpAgentBinding).all()
|
|
self.assertEqual(expected_hosted_agents, len(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': 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,
|
|
common_db_mixin.CommonDbMixin):
|
|
def test_reschedule_network_from_down_agent(self):
|
|
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(["foo-network-2"])
|
|
self._test_schedule_bind_network([agents[1]], "foo-network-2")
|
|
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=dhcpagentscheduler.NetworkNotHostedByDhcpAgent(
|
|
network_id='foo', agent_id='bar'))
|
|
|
|
def test_filter_bindings(self):
|
|
bindings = [
|
|
ndab_model.NetworkDhcpAgentBinding(network_id='foo1',
|
|
dhcp_agent={'id': 'id1'}),
|
|
ndab_model.NetworkDhcpAgentBinding(network_id='foo2',
|
|
dhcp_agent={'id': 'id1'}),
|
|
ndab_model.NetworkDhcpAgentBinding(network_id='foo3',
|
|
dhcp_agent={'id': 'id2'}),
|
|
ndab_model.NetworkDhcpAgentBinding(network_id='foo4',
|
|
dhcp_agent={'id': 'id2'})]
|
|
with mock.patch.object(self, 'agent_starting_up',
|
|
side_effect=[True, False]):
|
|
res = [b for b in self._filter_bindings(None, bindings)]
|
|
# once per each agent id1 and id2
|
|
self.assertEqual(2, len(res))
|
|
res_ids = [b.network_id for b in res]
|
|
self.assertIn('foo3', res_ids)
|
|
self.assertIn('foo4', 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 = 'another-net-id'
|
|
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):
|
|
self._save_networks(['1111'])
|
|
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': '1111', 'availability_zone_hints': ['az2']})
|
|
agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx,
|
|
['1111'])
|
|
self.assertEqual(1, len(agents))
|
|
self.assertEqual('az2-host1', agents[0]['host'])
|
|
|
|
def test_az_scheduler_default_az_hints(self):
|
|
cfg.CONF.set_override('default_availability_zones', ['az1'])
|
|
self._save_networks(['1111'])
|
|
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': '1111', 'availability_zone_hints': []})
|
|
agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx,
|
|
['1111'])
|
|
self.assertEqual(1, len(agents))
|
|
self.assertEqual('az1-host1', agents[0]['host'])
|
|
|
|
def test_az_scheduler_two_az_hints(self):
|
|
cfg.CONF.set_override('dhcp_agents_per_network', 2)
|
|
self._save_networks(['1111'])
|
|
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': '1111', 'availability_zone_hints': ['az1', 'az3']})
|
|
agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx,
|
|
['1111'])
|
|
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):
|
|
cfg.CONF.set_override('dhcp_agents_per_network', 2)
|
|
self._save_networks(['1111'])
|
|
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': '1111', 'availability_zone_hints': ['az1', 'az2']})
|
|
agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx,
|
|
['1111'])
|
|
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):
|
|
num_agent = 2 if multiple_agent else 1
|
|
cfg.CONF.set_override('dhcp_agents_per_network', num_agent)
|
|
self._save_networks(['1111'])
|
|
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': '1111', 'availability_zone_hints': []})
|
|
agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx,
|
|
['1111'])
|
|
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(['1111'])
|
|
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'])
|