Merge "DHCP notification optimization" into stable/ussuri
This commit is contained in:
commit
49eb7b0449
|
@ -143,11 +143,11 @@ class DhcpAgentNotifyAPI(object):
|
||||||
network['id'])
|
network['id'])
|
||||||
return new_agents + existing_agents
|
return new_agents + existing_agents
|
||||||
|
|
||||||
def _get_enabled_agents(self, context, network, agents, method, payload):
|
def _get_enabled_agents(
|
||||||
|
self, context, network_id, network, agents, method, payload):
|
||||||
"""Get the list of agents who can provide services."""
|
"""Get the list of agents who can provide services."""
|
||||||
if not agents:
|
if not agents:
|
||||||
return []
|
return []
|
||||||
network_id = network['id']
|
|
||||||
enabled_agents = agents
|
enabled_agents = agents
|
||||||
if not cfg.CONF.enable_services_on_agents_with_admin_state_down:
|
if not cfg.CONF.enable_services_on_agents_with_admin_state_down:
|
||||||
enabled_agents = [x for x in agents if x.admin_state_up]
|
enabled_agents = [x for x in agents if x.admin_state_up]
|
||||||
|
@ -165,6 +165,10 @@ class DhcpAgentNotifyAPI(object):
|
||||||
if not enabled_agents:
|
if not enabled_agents:
|
||||||
num_ports = self.plugin.get_ports_count(
|
num_ports = self.plugin.get_ports_count(
|
||||||
context, {'network_id': [network_id]})
|
context, {'network_id': [network_id]})
|
||||||
|
if not network:
|
||||||
|
admin_ctx = (context if context.is_admin else
|
||||||
|
context.elevated())
|
||||||
|
network = self.plugin.get_network(admin_ctx, network_id)
|
||||||
notification_required = (
|
notification_required = (
|
||||||
num_ports > 0 and len(network['subnets']) >= 1)
|
num_ports > 0 and len(network['subnets']) >= 1)
|
||||||
if notification_required:
|
if notification_required:
|
||||||
|
@ -179,7 +183,8 @@ class DhcpAgentNotifyAPI(object):
|
||||||
def _is_reserved_dhcp_port(self, port):
|
def _is_reserved_dhcp_port(self, port):
|
||||||
return port.get('device_id') == constants.DEVICE_ID_RESERVED_DHCP_PORT
|
return port.get('device_id') == constants.DEVICE_ID_RESERVED_DHCP_PORT
|
||||||
|
|
||||||
def _notify_agents(self, context, method, payload, network_id):
|
def _notify_agents(
|
||||||
|
self, context, method, payload, network_id, network=None):
|
||||||
"""Notify all the agents that are hosting the network."""
|
"""Notify all the agents that are hosting the network."""
|
||||||
payload['priority'] = METHOD_PRIORITY_MAP.get(method)
|
payload['priority'] = METHOD_PRIORITY_MAP.get(method)
|
||||||
# fanout is required as we do not know who is "listening"
|
# fanout is required as we do not know who is "listening"
|
||||||
|
@ -194,31 +199,35 @@ class DhcpAgentNotifyAPI(object):
|
||||||
if fanout_required:
|
if fanout_required:
|
||||||
self._fanout_message(context, method, payload)
|
self._fanout_message(context, method, payload)
|
||||||
elif cast_required:
|
elif cast_required:
|
||||||
admin_ctx = (context if context.is_admin else context.elevated())
|
candidate_hosts = None
|
||||||
network = self.plugin.get_network(admin_ctx, network_id)
|
|
||||||
if 'subnet' in payload and payload['subnet'].get('segment_id'):
|
if 'subnet' in payload and payload['subnet'].get('segment_id'):
|
||||||
# if segment_id exists then the segment service plugin
|
# if segment_id exists then the segment service plugin
|
||||||
# must be loaded
|
# must be loaded
|
||||||
segment_plugin = directory.get_plugin('segments')
|
segment_plugin = directory.get_plugin('segments')
|
||||||
segment = segment_plugin.get_segment(
|
segment = segment_plugin.get_segment(
|
||||||
context, payload['subnet']['segment_id'])
|
context, payload['subnet']['segment_id'])
|
||||||
network['candidate_hosts'] = segment['hosts']
|
candidate_hosts = segment['hosts']
|
||||||
|
|
||||||
agents = self.plugin.get_dhcp_agents_hosting_networks(
|
agents = self.plugin.get_dhcp_agents_hosting_networks(
|
||||||
context, [network_id], hosts=network.get('candidate_hosts'))
|
context, [network_id], hosts=candidate_hosts)
|
||||||
# schedule the network first, if needed
|
# schedule the network first, if needed
|
||||||
schedule_required = (
|
schedule_required = (
|
||||||
method == 'subnet_create_end' or
|
method == 'subnet_create_end' or
|
||||||
method == 'port_create_end' and
|
method == 'port_create_end' and
|
||||||
not self._is_reserved_dhcp_port(payload['port']))
|
not self._is_reserved_dhcp_port(payload['port']))
|
||||||
if schedule_required:
|
if schedule_required:
|
||||||
|
admin_ctx = context if context.is_admin else context.elevated()
|
||||||
|
network = network or self.plugin.get_network(
|
||||||
|
admin_ctx, network_id)
|
||||||
|
if candidate_hosts:
|
||||||
|
network['candidate_hosts'] = candidate_hosts
|
||||||
agents = self._schedule_network(admin_ctx, network, agents)
|
agents = self._schedule_network(admin_ctx, network, agents)
|
||||||
if not agents:
|
if not agents:
|
||||||
LOG.debug("Network %s is not hosted by any dhcp agent",
|
LOG.debug("Network %s is not hosted by any dhcp agent",
|
||||||
network_id)
|
network_id)
|
||||||
return
|
return
|
||||||
enabled_agents = self._get_enabled_agents(
|
enabled_agents = self._get_enabled_agents(
|
||||||
context, network, agents, method, payload)
|
context, network_id, network, agents, method, payload)
|
||||||
|
|
||||||
if method == 'port_create_end':
|
if method == 'port_create_end':
|
||||||
high_agent = enabled_agents.pop(
|
high_agent = enabled_agents.pop(
|
||||||
|
@ -346,6 +355,8 @@ class DhcpAgentNotifyAPI(object):
|
||||||
payload['network_id'] = network_id
|
payload['network_id'] = network_id
|
||||||
if obj_type == 'port':
|
if obj_type == 'port':
|
||||||
payload['fixed_ips'] = obj_value['fixed_ips']
|
payload['fixed_ips'] = obj_value['fixed_ips']
|
||||||
self._notify_agents(context, method_name, payload, network_id)
|
self._notify_agents(context, method_name, payload, network_id,
|
||||||
|
obj_value.get('network'))
|
||||||
else:
|
else:
|
||||||
self._notify_agents(context, method_name, data, network_id)
|
self._notify_agents(context, method_name, data, network_id,
|
||||||
|
obj_value.get('network'))
|
||||||
|
|
|
@ -1247,6 +1247,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||||
# db base plugin post commit ops
|
# db base plugin post commit ops
|
||||||
self._create_subnet_postcommit(context, result)
|
self._create_subnet_postcommit(context, result)
|
||||||
|
|
||||||
|
# add network to subnet dict to save a DB call on dhcp notification
|
||||||
|
result['network'] = mech_context.network.current
|
||||||
kwargs = {'context': context, 'subnet': result}
|
kwargs = {'context': context, 'subnet': result}
|
||||||
registry.notify(resources.SUBNET, events.AFTER_CREATE, self, **kwargs)
|
registry.notify(resources.SUBNET, events.AFTER_CREATE, self, **kwargs)
|
||||||
try:
|
try:
|
||||||
|
@ -1407,6 +1409,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||||
return self._after_create_port(context, result, mech_context)
|
return self._after_create_port(context, result, mech_context)
|
||||||
|
|
||||||
def _after_create_port(self, context, result, mech_context):
|
def _after_create_port(self, context, result, mech_context):
|
||||||
|
# add network to port dict to save a DB call on dhcp notification
|
||||||
|
result['network'] = mech_context.network.current
|
||||||
# notify any plugin that is interested in port create events
|
# notify any plugin that is interested in port create events
|
||||||
kwargs = {'context': context, 'port': result}
|
kwargs = {'context': context, 'port': result}
|
||||||
registry.notify(resources.PORT, events.AFTER_CREATE, self, **kwargs)
|
registry.notify(resources.PORT, events.AFTER_CREATE, self, **kwargs)
|
||||||
|
|
|
@ -85,12 +85,12 @@ class TestDhcpAgentNotifyAPI(base.BaseTestCase):
|
||||||
new_agents=None, existing_agents=[],
|
new_agents=None, existing_agents=[],
|
||||||
expected_casts=0, expected_warnings=1)
|
expected_casts=0, expected_warnings=1)
|
||||||
|
|
||||||
def _test__get_enabled_agents(self, network,
|
def _test__get_enabled_agents(self, network_id,
|
||||||
agents=None, port_count=0,
|
agents=None, port_count=0,
|
||||||
expected_warnings=0, expected_errors=0):
|
expected_warnings=0, expected_errors=0):
|
||||||
self.notifier.plugin.get_ports_count.return_value = port_count
|
self.notifier.plugin.get_ports_count.return_value = port_count
|
||||||
enabled_agents = self.notifier._get_enabled_agents(
|
enabled_agents = self.notifier._get_enabled_agents(
|
||||||
mock.ANY, network, agents, mock.ANY, mock.ANY)
|
mock.Mock(), network_id, None, agents, mock.ANY, mock.ANY)
|
||||||
if not cfg.CONF.enable_services_on_agents_with_admin_state_down:
|
if not cfg.CONF.enable_services_on_agents_with_admin_state_down:
|
||||||
agents = [x for x in agents if x.admin_state_up]
|
agents = [x for x in agents if x.admin_state_up]
|
||||||
self.assertEqual(agents, enabled_agents)
|
self.assertEqual(agents, enabled_agents)
|
||||||
|
@ -104,8 +104,8 @@ class TestDhcpAgentNotifyAPI(base.BaseTestCase):
|
||||||
agent2 = agent_obj.Agent(mock.ANY, id=uuidutils.generate_uuid())
|
agent2 = agent_obj.Agent(mock.ANY, id=uuidutils.generate_uuid())
|
||||||
agent2.admin_state_up = False
|
agent2.admin_state_up = False
|
||||||
agent2.heartbeat_timestamp = timeutils.utcnow()
|
agent2.heartbeat_timestamp = timeutils.utcnow()
|
||||||
network = {'id': 'foo_network_id'}
|
self._test__get_enabled_agents(network_id='foo_network_id',
|
||||||
self._test__get_enabled_agents(network, agents=[agent1])
|
agents=[agent1])
|
||||||
|
|
||||||
def test__get_enabled_agents_with_inactive_ones(self):
|
def test__get_enabled_agents_with_inactive_ones(self):
|
||||||
agent1 = agent_obj.Agent(mock.ANY, id=uuidutils.generate_uuid())
|
agent1 = agent_obj.Agent(mock.ANY, id=uuidutils.generate_uuid())
|
||||||
|
@ -115,17 +115,18 @@ class TestDhcpAgentNotifyAPI(base.BaseTestCase):
|
||||||
agent2.admin_state_up = True
|
agent2.admin_state_up = True
|
||||||
# This is effectively an inactive agent
|
# This is effectively an inactive agent
|
||||||
agent2.heartbeat_timestamp = datetime.datetime(2000, 1, 1, 0, 0)
|
agent2.heartbeat_timestamp = datetime.datetime(2000, 1, 1, 0, 0)
|
||||||
network = {'id': 'foo_network_id'}
|
self._test__get_enabled_agents(network_id='foo_network_id',
|
||||||
self._test__get_enabled_agents(network,
|
|
||||||
agents=[agent1, agent2],
|
agents=[agent1, agent2],
|
||||||
expected_warnings=1, expected_errors=0)
|
expected_warnings=1, expected_errors=0)
|
||||||
|
|
||||||
def test__get_enabled_agents_with_notification_required(self):
|
def test__get_enabled_agents_with_notification_required(self):
|
||||||
network = {'id': 'foo_network_id', 'subnets': ['foo_subnet_id']}
|
network = {'id': 'foo_network_id', 'subnets': ['foo_subnet_id']}
|
||||||
|
self.notifier.plugin.get_network.return_value = network
|
||||||
agent = agent_obj.Agent(mock.ANY, id=uuidutils.generate_uuid())
|
agent = agent_obj.Agent(mock.ANY, id=uuidutils.generate_uuid())
|
||||||
agent.admin_state_up = False
|
agent.admin_state_up = False
|
||||||
agent.heartbeat_timestamp = timeutils.utcnow()
|
agent.heartbeat_timestamp = timeutils.utcnow()
|
||||||
self._test__get_enabled_agents(network, [agent], port_count=20,
|
self._test__get_enabled_agents('foo_network_id',
|
||||||
|
[agent], port_count=20,
|
||||||
expected_warnings=0, expected_errors=1)
|
expected_warnings=0, expected_errors=1)
|
||||||
|
|
||||||
def test__get_enabled_agents_with_admin_state_down(self):
|
def test__get_enabled_agents_with_admin_state_down(self):
|
||||||
|
@ -137,8 +138,8 @@ class TestDhcpAgentNotifyAPI(base.BaseTestCase):
|
||||||
agent2 = agent_obj.Agent(mock.ANY, id=uuidutils.generate_uuid())
|
agent2 = agent_obj.Agent(mock.ANY, id=uuidutils.generate_uuid())
|
||||||
agent2.admin_state_up = False
|
agent2.admin_state_up = False
|
||||||
agent2.heartbeat_timestamp = timeutils.utcnow()
|
agent2.heartbeat_timestamp = timeutils.utcnow()
|
||||||
network = {'id': 'foo_network_id'}
|
self._test__get_enabled_agents(network_id='foo_network_id',
|
||||||
self._test__get_enabled_agents(network, agents=[agent1, agent2])
|
agents=[agent1, agent2])
|
||||||
|
|
||||||
def test__notify_agents_allocate_priority(self):
|
def test__notify_agents_allocate_priority(self):
|
||||||
mock_context = mock.MagicMock()
|
mock_context = mock.MagicMock()
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import copy
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
@ -1523,6 +1524,11 @@ class OvsDhcpAgentNotifierTestCase(test_agent.AgentDBTestMixIn,
|
||||||
return net, sub, port
|
return net, sub, port
|
||||||
|
|
||||||
def _notification_mocks(self, hosts, net, subnet, port, port_priority):
|
def _notification_mocks(self, hosts, net, subnet, port, port_priority):
|
||||||
|
subnet['subnet']['network'] = copy.deepcopy(net['network'])
|
||||||
|
# 'availability_zones' is empty at the time subnet_create_end
|
||||||
|
# notification is sent
|
||||||
|
subnet['subnet']['network']['availability_zones'] = []
|
||||||
|
port['port']['network'] = net['network']
|
||||||
host_calls = {}
|
host_calls = {}
|
||||||
for host in hosts:
|
for host in hosts:
|
||||||
expected_calls = [
|
expected_calls = [
|
||||||
|
|
Loading…
Reference in New Issue