Schedule networks to new segments if needed
In case when subnet in segment X was created before hosts from that segment was mapped in the Neutron DB, network wasn't scheduled to the DHCP agents in that new segment. Now DHCP scheduler is triggered in such case. Closes-bug: #1917811 Change-Id: Ic9e64aa4ecdc3d56f00c26204ad931b810db7599
This commit is contained in:
parent
3ce1ca690c
commit
5c931f2913
@ -17,12 +17,16 @@ import datetime
|
|||||||
import random
|
import random
|
||||||
import time
|
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 constants
|
||||||
from neutron_lib import context as ncontext
|
from neutron_lib import context as ncontext
|
||||||
from neutron_lib.db import api as db_api
|
from neutron_lib.db import api as db_api
|
||||||
from neutron_lib import exceptions as n_exc
|
from neutron_lib import exceptions as n_exc
|
||||||
from neutron_lib.exceptions import agent as agent_exc
|
from neutron_lib.exceptions import agent as agent_exc
|
||||||
from neutron_lib.exceptions import dhcpagentscheduler as das_exc
|
from neutron_lib.exceptions import dhcpagentscheduler as das_exc
|
||||||
|
from neutron_lib.plugins import directory
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
import oslo_messaging
|
import oslo_messaging
|
||||||
@ -36,6 +40,7 @@ from neutron.db import agents_db
|
|||||||
from neutron.db.availability_zone import network as network_az
|
from neutron.db.availability_zone import network as network_az
|
||||||
from neutron.extensions import dhcpagentscheduler
|
from neutron.extensions import dhcpagentscheduler
|
||||||
from neutron.objects import network
|
from neutron.objects import network
|
||||||
|
from neutron.objects import subnet as subnet_obj
|
||||||
from neutron import worker as neutron_worker
|
from neutron import worker as neutron_worker
|
||||||
|
|
||||||
|
|
||||||
@ -226,12 +231,15 @@ class DhcpAgentSchedulerDbMixin(dhcpagentscheduler
|
|||||||
additional_time)
|
additional_time)
|
||||||
return agent_expected_up > timeutils.utcnow()
|
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)
|
LOG.info("Scheduling unhosted network %s", network_id)
|
||||||
try:
|
try:
|
||||||
# TODO(enikanorov): have to issue redundant db query
|
# TODO(enikanorov): have to issue redundant db query
|
||||||
# to satisfy scheduling interface
|
# to satisfy scheduling interface
|
||||||
network = self.get_network(context, network_id)
|
network = self.get_network(context, network_id)
|
||||||
|
if candidate_hosts:
|
||||||
|
network['candidate_hosts'] = candidate_hosts
|
||||||
agents = self.schedule_network(context, network)
|
agents = self.schedule_network(context, network)
|
||||||
if not agents:
|
if not agents:
|
||||||
LOG.info("Failed to schedule network %s, "
|
LOG.info("Failed to schedule network %s, "
|
||||||
@ -481,6 +489,25 @@ class DhcpAgentSchedulerDbMixin(dhcpagentscheduler
|
|||||||
if self.network_scheduler:
|
if self.network_scheduler:
|
||||||
self.network_scheduler.auto_schedule_networks(self, context, host)
|
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,
|
class AZDhcpAgentSchedulerDbMixin(DhcpAgentSchedulerDbMixin,
|
||||||
network_az.NetworkAvailabilityZoneMixin):
|
network_az.NetworkAvailabilityZoneMixin):
|
||||||
|
@ -18,6 +18,8 @@ from unittest import mock
|
|||||||
|
|
||||||
from neutron_lib.api.definitions import dhcpagentscheduler as das_apidef
|
from neutron_lib.api.definitions import dhcpagentscheduler as das_apidef
|
||||||
from neutron_lib.api.definitions import portbindings
|
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 constants
|
||||||
from neutron_lib import context
|
from neutron_lib import context
|
||||||
from neutron_lib.db import api as db_api
|
from neutron_lib.db import api as db_api
|
||||||
@ -1588,6 +1590,47 @@ class OvsDhcpAgentNotifierTestCase(test_agent.AgentDBTestMixIn,
|
|||||||
for expected in low_expecteds:
|
for expected in low_expecteds:
|
||||||
self.assertIn(expected, self.dhcp_notifier_cast.call_args_list)
|
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):
|
def _is_schedule_network_called(self, device_id):
|
||||||
dhcp_notifier_schedule = mock.patch(
|
dhcp_notifier_schedule = mock.patch(
|
||||||
'neutron.api.rpc.agentnotifiers.dhcp_rpc_agent_api.'
|
'neutron.api.rpc.agentnotifiers.dhcp_rpc_agent_api.'
|
||||||
|
Loading…
Reference in New Issue
Block a user