From 0b08d88366f6c463d11b817fc0ca9259e255bbc5 Mon Sep 17 00:00:00 2001 From: Max Rasskazov Date: Thu, 6 Feb 2014 18:09:53 +0400 Subject: [PATCH] Add patch MIRA001-Send-DHCP-notifications-regardless-of-agent-status.patch for deb; OSCI-1004 Change-Id: I8751352c85df04973811e72bb8dc4655ec207199 --- ...fications-regardless-of-agent-status.patch | 201 ++++++++++++++++++ debian/patches/series | 1 + 2 files changed, 202 insertions(+) create mode 100644 debian/patches/MIRA001-Send-DHCP-notifications-regardless-of-agent-status.patch diff --git a/debian/patches/MIRA001-Send-DHCP-notifications-regardless-of-agent-status.patch b/debian/patches/MIRA001-Send-DHCP-notifications-regardless-of-agent-status.patch new file mode 100644 index 000000000..ff0e9c8c2 --- /dev/null +++ b/debian/patches/MIRA001-Send-DHCP-notifications-regardless-of-agent-status.patch @@ -0,0 +1,201 @@ +From 7f1af65475d5c3c1ea5440116ed1f03a186663ff Mon Sep 17 00:00:00 2001 +From: Maru Newby +Date: Tue, 10 Dec 2013 16:10:42 +0000 +Subject: [PATCH 1/1] Send DHCP notifications regardless of agent status + +The Neutron service, when under load, may not be able to process +agent heartbeats in a timely fashion. This can result in +agents being erroneously considered inactive. Previously, DHCP +notifications for which active agents could not be found were +silently dropped. This change ensures that notifications for +a given network are sent to agents even if those agents do not +appear to be active. + +Additionally, if no enabled dhcp agents can be found for a given +network, an error will be logged. Raising an exception might be +preferable, but has such a large testing impact that it will be +submitted as a separate patch if deemed necessary. + +Closes-bug: #1192381 +(cherry picked from commit 522f9f94681de5903422cfde11b93f5c0e71e532) + +Change-Id: Id3e639d9cf3d16708fd66a4baebd3fbeeed3dde8 +--- + .../api/rpc/agentnotifiers/dhcp_rpc_agent_api.py | 35 ++++++++-- + neutron/db/agents_db.py | 4 ++ + neutron/tests/unit/api/__init__.py | 0 + neutron/tests/unit/api/rpc/__init__.py | 0 + .../tests/unit/api/rpc/agentnotifiers/__init__.py | 0 + .../rpc/agentnotifiers/test_dhcp_rpc_agent_api.py | 76 ++++++++++++++++++++++ + 6 files changed, 108 insertions(+), 7 deletions(-) + create mode 100644 neutron/tests/unit/api/__init__.py + create mode 100644 neutron/tests/unit/api/rpc/__init__.py + create mode 100644 neutron/tests/unit/api/rpc/agentnotifiers/__init__.py + create mode 100644 neutron/tests/unit/api/rpc/agentnotifiers/test_dhcp_rpc_agent_api.py + +diff --git a/neutron/api/rpc/agentnotifiers/dhcp_rpc_agent_api.py b/neutron/api/rpc/agentnotifiers/dhcp_rpc_agent_api.py +index 1086a9e..4ed724d 100644 +--- a/neutron/api/rpc/agentnotifiers/dhcp_rpc_agent_api.py ++++ b/neutron/api/rpc/agentnotifiers/dhcp_rpc_agent_api.py +@@ -43,12 +43,11 @@ class DhcpAgentNotifyAPI(proxy.RpcProxy): + super(DhcpAgentNotifyAPI, self).__init__( + topic=topic, default_version=self.BASE_RPC_API_VERSION) + +- def _get_dhcp_agents(self, context, network_id): ++ def _get_enabled_dhcp_agents(self, context, network_id): ++ """Return enabled dhcp agents associated with the given network.""" + plugin = manager.NeutronManager.get_plugin() +- dhcp_agents = plugin.get_dhcp_agents_hosting_networks( +- context, [network_id], active=True) +- return [(dhcp_agent.host, dhcp_agent.topic) for +- dhcp_agent in dhcp_agents] ++ agents = plugin.get_dhcp_agents_hosting_networks(context, [network_id]) ++ return [x for x in agents if x.admin_state_up] + + def _notification_host(self, context, method, payload, host): + """Notify the agent on host.""" +@@ -76,11 +75,33 @@ class DhcpAgentNotifyAPI(proxy.RpcProxy): + context, 'network_create_end', + {'network': {'id': network_id}}, + agent['host']) +- for (host, topic) in self._get_dhcp_agents(context, network_id): ++ agents = self._get_enabled_dhcp_agents(context, network_id) ++ if not agents: ++ LOG.error(_("No DHCP agents are associated with network " ++ "'%(net_id)s'. Unable to send notification " ++ "for '%(method)s' with payload: %(payload)s"), ++ { ++ 'net_id': network_id, ++ 'method': method, ++ 'payload': payload, ++ }) ++ return ++ active_agents = [x for x in agents if x.is_active] ++ if active_agents != agents: ++ LOG.warning(_("Only %(active)d of %(total)d DHCP agents " ++ "associated with network '%(net_id)s' are " ++ "marked as active, so notifications may " ++ "be sent to inactive agents."), ++ { ++ 'active': len(active_agents), ++ 'total': len(agents), ++ 'net_id': network_id, ++ }) ++ for agent in agents: + self.cast( + context, self.make_msg(method, + payload=payload), +- topic='%s.%s' % (topic, host)) ++ topic='%s.%s' % (agent.topic, agent.host)) + else: + # besides the non-agentscheduler plugin, + # There is no way to query who is hosting the network +diff --git a/neutron/db/agents_db.py b/neutron/db/agents_db.py +index e095a4c..fdcc6d3 100644 +--- a/neutron/db/agents_db.py ++++ b/neutron/db/agents_db.py +@@ -60,6 +60,10 @@ class Agent(model_base.BASEV2, models_v2.HasId): + # configurations: a json dict string, I think 4095 is enough + configurations = sa.Column(sa.String(4095), nullable=False) + ++ @property ++ def is_active(self): ++ return not AgentDbMixin.is_agent_down(self.heartbeat_timestamp) ++ + + class AgentDbMixin(ext_agent.AgentPluginBase): + """Mixin class to add agent extension to db_plugin_base_v2.""" +diff --git a/neutron/tests/unit/api/__init__.py b/neutron/tests/unit/api/__init__.py +new file mode 100644 +index 0000000..e69de29 +diff --git a/neutron/tests/unit/api/rpc/__init__.py b/neutron/tests/unit/api/rpc/__init__.py +new file mode 100644 +index 0000000..e69de29 +diff --git a/neutron/tests/unit/api/rpc/agentnotifiers/__init__.py b/neutron/tests/unit/api/rpc/agentnotifiers/__init__.py +new file mode 100644 +index 0000000..e69de29 +diff --git a/neutron/tests/unit/api/rpc/agentnotifiers/test_dhcp_rpc_agent_api.py b/neutron/tests/unit/api/rpc/agentnotifiers/test_dhcp_rpc_agent_api.py +new file mode 100644 +index 0000000..b175d34 +--- /dev/null ++++ b/neutron/tests/unit/api/rpc/agentnotifiers/test_dhcp_rpc_agent_api.py +@@ -0,0 +1,76 @@ ++# Copyright (c) 2013 Red Hat, Inc. ++# ++# Licensed under the Apache License, Version 2.0 (the "License"); ++# you may not use this file except in compliance with the License. ++# You may obtain a copy of the License at ++# ++# http://www.apache.org/licenses/LICENSE-2.0 ++# ++# Unless required by applicable law or agreed to in writing, software ++# distributed under the License is distributed on an "AS IS" BASIS, ++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or ++# implied. ++# See the License for the specific language governing permissions and ++# limitations under the License. ++ ++import contextlib ++ ++import mock ++ ++from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api ++from neutron.common import utils ++from neutron import manager ++from neutron.tests import base ++ ++ ++class TestDhcpAgentNotifyAPI(base.BaseTestCase): ++ ++ def setUp(self): ++ super(TestDhcpAgentNotifyAPI, self).setUp() ++ self.notify = dhcp_rpc_agent_api.DhcpAgentNotifyAPI() ++ ++ def test_get_enabled_dhcp_agents_filters_disabled_agents(self): ++ disabled_agent = mock.Mock() ++ disabled_agent.admin_state_up = False ++ enabled_agent = mock.Mock() ++ with mock.patch.object(manager.NeutronManager, ++ 'get_plugin') as mock_get_plugin: ++ mock_get_plugin.return_value = mock_plugin = mock.Mock() ++ with mock.patch.object( ++ mock_plugin, 'get_dhcp_agents_hosting_networks' ++ ) as mock_get_agents: ++ mock_get_agents.return_value = [disabled_agent, enabled_agent] ++ result = self.notify._get_enabled_dhcp_agents('ctx', 'net_id') ++ self.assertEqual(result, [enabled_agent]) ++ ++ def _test_notification(self, agents): ++ with contextlib.nested( ++ mock.patch.object(manager.NeutronManager, 'get_plugin'), ++ mock.patch.object(utils, 'is_extension_supported'), ++ mock.patch.object(self.notify, '_get_enabled_dhcp_agents') ++ ) as (m1, m2, mock_get_agents): ++ mock_get_agents.return_value = agents ++ self.notify._notification(mock.Mock(), 'foo', {}, 'net_id') ++ ++ def test_notification_sends_cast_for_enabled_agent(self): ++ with mock.patch.object(self.notify, 'cast') as mock_cast: ++ self._test_notification([mock.Mock()]) ++ self.assertEqual(mock_cast.call_count, 1) ++ ++ def test_notification_logs_error_for_no_enabled_agents(self): ++ with mock.patch.object(self.notify, 'cast') as mock_cast: ++ with mock.patch.object(dhcp_rpc_agent_api.LOG, ++ 'error') as mock_log: ++ self._test_notification([]) ++ self.assertEqual(mock_cast.call_count, 0) ++ self.assertEqual(mock_log.call_count, 1) ++ ++ def test_notification_logs_warning_for_inactive_agents(self): ++ agent = mock.Mock() ++ agent.is_active = False ++ with mock.patch.object(self.notify, 'cast') as mock_cast: ++ with mock.patch.object(dhcp_rpc_agent_api.LOG, ++ 'warning') as mock_log: ++ self._test_notification([agent]) ++ self.assertEqual(mock_cast.call_count, 1) ++ self.assertEqual(mock_log.call_count, 1) +-- +1.8.3.1 + diff --git a/debian/patches/series b/debian/patches/series index aa0010058..1e91a8c70 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -3,3 +3,4 @@ disable-udev-tests.patch bump-sqlalchemy-version.patch disable-ml2-notification-tests.patch remove-jsonrpclib.patch +MIRA001-Send-DHCP-notifications-regardless-of-agent-status.patch -- 2.32.3