diff --git a/neutron/db/agentschedulers_db.py b/neutron/db/agentschedulers_db.py index 7ad5bcb37b0..a4f2f514616 100644 --- a/neutron/db/agentschedulers_db.py +++ b/neutron/db/agentschedulers_db.py @@ -17,11 +17,15 @@ import datetime import random import time +from neutron_lib.callbacks import events +from neutron_lib.callbacks import registry +from neutron_lib.callbacks import resources from neutron_lib import constants from neutron_lib import context as ncontext from neutron_lib import exceptions as n_exc from neutron_lib.exceptions import agent as agent_exc from neutron_lib.exceptions import dhcpagentscheduler as das_exc +from neutron_lib.plugins import directory from oslo_config import cfg from oslo_log import log as logging import oslo_messaging @@ -35,6 +39,7 @@ from neutron.db import agents_db from neutron.db.availability_zone import network as network_az from neutron.extensions import dhcpagentscheduler from neutron.objects import network +from neutron.objects import subnet as subnet_obj from neutron import worker as neutron_worker @@ -221,12 +226,15 @@ class DhcpAgentSchedulerDbMixin(dhcpagentscheduler additional_time) return agent_expected_up > timeutils.utcnow() - def _schedule_network(self, context, network_id, dhcp_notifier): + def _schedule_network(self, context, network_id, dhcp_notifier, + candidate_hosts=None): LOG.info("Scheduling unhosted network %s", network_id) try: # TODO(enikanorov): have to issue redundant db query # to satisfy scheduling interface network = self.get_network(context, network_id) + if candidate_hosts: + network['candidate_hosts'] = candidate_hosts agents = self.schedule_network(context, network) if not agents: LOG.info("Failed to schedule network %s, " @@ -477,6 +485,25 @@ class DhcpAgentSchedulerDbMixin(dhcpagentscheduler if self.network_scheduler: self.network_scheduler.auto_schedule_networks(self, context, host) + @registry.receives(resources.SEGMENT_HOST_MAPPING, [events.AFTER_CREATE]) + def auto_schedule_new_network_segments(self, resource, event, trigger, + payload=None): + if not cfg.CONF.network_auto_schedule: + return + segment_plugin = directory.get_plugin('segments') + dhcp_notifier = self.agent_notifiers.get(constants.AGENT_TYPE_DHCP) + segment_ids = payload.metadata.get('current_segment_ids') + segments = segment_plugin.get_segments( + payload.context, filters={'id': segment_ids}) + subnets = subnet_obj.Subnet.get_objects( + payload.context, segment_id=segment_ids) + network_ids = {s.network_id for s in subnets} + for network_id in network_ids: + for segment in segments: + self._schedule_network( + payload.context, network_id, dhcp_notifier, + candidate_hosts=segment['hosts']) + class AZDhcpAgentSchedulerDbMixin(DhcpAgentSchedulerDbMixin, network_az.NetworkAvailabilityZoneMixin): diff --git a/neutron/tests/unit/db/test_agentschedulers_db.py b/neutron/tests/unit/db/test_agentschedulers_db.py index 1dfe4148030..0f061d934b9 100644 --- a/neutron/tests/unit/db/test_agentschedulers_db.py +++ b/neutron/tests/unit/db/test_agentschedulers_db.py @@ -18,6 +18,8 @@ import datetime import mock from neutron_lib.api.definitions import dhcpagentscheduler as das_apidef from neutron_lib.api.definitions import portbindings +from neutron_lib.callbacks import events +from neutron_lib.callbacks import resources from neutron_lib import constants from neutron_lib import context from neutron_lib.plugins import constants as plugin_constants @@ -1560,6 +1562,47 @@ class OvsDhcpAgentNotifierTestCase(test_agent.AgentDBTestMixIn, for expected in low_expecteds: self.assertIn(expected, self.dhcp_notifier_cast.call_args_list) + def _test_auto_schedule_new_network_segments(self, subnet_on_segment): + ctx = mock.Mock() + payload = events.DBEventPayload( + ctx, + metadata={'host': 'HOST A', + 'current_segment_ids': set(['segment-1'])}) + segments_plugin = mock.Mock() + segments_plugin.get_segments.return_value = [ + {'id': 'segment-1', 'hosts': ['HOST A']}] + dhcp_notifier = mock.Mock() + dhcp_mixin = agentschedulers_db.DhcpAgentSchedulerDbMixin() + with mock.patch( + 'neutron_lib.plugins.directory.get_plugin', + return_value=segments_plugin), \ + mock.patch( + 'neutron.objects.subnet.Subnet.get_objects') as get_subnets, \ + mock.patch.object( + dhcp_mixin, '_schedule_network') as schedule_network: + + get_subnets.return_value = ( + [subnet_on_segment] if subnet_on_segment else []) + + dhcp_mixin.agent_notifiers[constants.AGENT_TYPE_DHCP] = ( + dhcp_notifier) + dhcp_mixin.auto_schedule_new_network_segments( + resources.SEGMENT_HOST_MAPPING, events.AFTER_CREATE, + ctx, payload) + if subnet_on_segment: + schedule_network.assert_called_once_with( + ctx, subnet_on_segment.network_id, + dhcp_notifier, candidate_hosts=['HOST A']) + else: + schedule_network.assert_not_called() + + def test_auto_schedule_new_network_segments(self): + self._test_auto_schedule_new_network_segments( + subnet_on_segment=mock.Mock(network_id='net-1')) + + def test_auto_schedule_new_network_segments_no_networks_on_segment(self): + self._test_auto_schedule_new_network_segments(subnet_on_segment=None) + def _is_schedule_network_called(self, device_id): dhcp_notifier_schedule = mock.patch( 'neutron.api.rpc.agentnotifiers.dhcp_rpc_agent_api.'