DHCP Auto Scheduling for routed provider networks

In routed provider networks, a network can be divided by segments and
each segment will have its own DHCP agent. AutoScheduling for networks
exists. This patchset is for enabling auto scheduling for routed
networks.

Change-Id: I7d9f01dbd6d9c216474ee47c968919c1826787f7
Partially-implements: blueprint routed-networks
This commit is contained in:
Aradhana Singh 2016-06-28 00:07:06 +00:00
parent f545a4e9b0
commit 996b84540b
3 changed files with 137 additions and 10 deletions

View File

@ -30,6 +30,7 @@ from neutron.db.network_dhcp_agent_binding import models as ndab_model
from neutron.extensions import availability_zone as az_ext
from neutron.scheduler import base_resource_filter
from neutron.scheduler import base_scheduler
from neutron.services.segments import db as segments_db
LOG = logging.getLogger(__name__)
@ -44,10 +45,16 @@ class AutoScheduler(object):
# a list of (agent, net_ids) tuples
bindings_to_add = []
with context.session.begin(subtransactions=True):
fields = ['network_id', 'enable_dhcp']
fields = ['network_id', 'enable_dhcp', 'segment_id']
subnets = plugin.get_subnets(context, fields=fields)
net_ids = set(s['network_id'] for s in subnets
if s['enable_dhcp'])
net_ids = {}
net_segment_ids = collections.defaultdict(set)
for s in subnets:
if s['enable_dhcp']:
net_segment_ids[s['network_id']].add(s.get('segment_id'))
for network_id, segment_ids in net_segment_ids.items():
is_routed_network = any(segment_ids)
net_ids[network_id] = is_routed_network
if not net_ids:
LOG.debug('No non-hosted networks')
return False
@ -57,17 +64,28 @@ class AutoScheduler(object):
agents_db.Agent.host == host,
agents_db.Agent.admin_state_up == sql.true())
dhcp_agents = query.all()
query = context.session.query(
segments_db.SegmentHostMapping.segment_id)
query = query.filter(segments_db.SegmentHostMapping.host == host)
segments_on_host = {s.segment_id for s in query}
for dhcp_agent in dhcp_agents:
if agents_db.AgentDbMixin.is_agent_down(
dhcp_agent.heartbeat_timestamp):
LOG.warning(_LW('DHCP agent %s is not active'),
dhcp_agent.id)
continue
for net_id in net_ids:
for net_id, is_routed_network in net_ids.items():
agents = plugin.get_dhcp_agents_hosting_networks(
context, [net_id])
if len(agents) >= agents_per_network:
continue
segments_on_network = net_segment_ids[net_id]
if is_routed_network:
if len(segments_on_network & segments_on_host) == 0:
continue
else:
if len(agents) >= agents_per_network:
continue
if any(dhcp_agent.id == agent.id for agent in agents):
continue
net = plugin.get_network(context, net_id)

View File

@ -364,7 +364,8 @@ class TestAutoSchedule(test_dhcp_sch.TestDhcpSchedulerBaseTestCase,
enable_dhcp = (not self._strip_host_index(net_id) in
self.networks_with_dhcp_disabled)
subnets.append({'network_id': net_id,
'enable_dhcp': enable_dhcp})
'enable_dhcp': enable_dhcp,
'segment_id': None})
return subnets
def get_network(self, context, net_id):

View File

@ -143,7 +143,8 @@ class TestDhcpScheduler(TestDhcpSchedulerBaseTestCase):
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}]
"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 = []
@ -264,8 +265,8 @@ class TestAutoScheduleNetworks(TestDhcpSchedulerBaseTestCase):
def test_auto_schedule_network(self):
plugin = mock.MagicMock()
plugin.get_subnets.return_value = (
[{"network_id": self.network_id, "enable_dhcp": self.enable_dhcp}]
if self.network_present else [])
[{"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()
@ -291,6 +292,113 @@ class TestAutoScheduleNetworks(TestDhcpSchedulerBaseTestCase):
self.assertEqual(expected_hosted_agents, len(hosted_agents))
class TestAutoScheduleSegments(test_plugin.Ml2PluginV2TestCase,
TestDhcpSchedulerBaseTestCase):
"""Unit test scenarios for ChanceScheduler"""
def setUp(self):
super(TestAutoScheduleSegments, self).setUp()
self.plugin = importutils.import_object('neutron.plugins.ml2.plugin.'
'Ml2Plugin')
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,
'physical_network': constants.ATTR_NOT_SPECIFIED,
'network_type': 'meh',
'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):