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:
parent
f545a4e9b0
commit
996b84540b
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue