Remove already applied patches
[openstack-build/neutron-build.git] / rpm / SOURCES / MIRA001-Send-DHCP-notifications-regardless-of-agent-status.patch
1 From 7f1af65475d5c3c1ea5440116ed1f03a186663ff Mon Sep 17 00:00:00 2001
2 From: Maru Newby <marun@redhat.com>
3 Date: Tue, 10 Dec 2013 16:10:42 +0000
4 Subject: [PATCH 1/1] Send DHCP notifications regardless of agent status
5
6 The Neutron service, when under load, may not be able to process
7 agent heartbeats in a timely fashion.  This can result in
8 agents being erroneously considered inactive.  Previously, DHCP
9 notifications for which active agents could not be found were
10 silently dropped.  This change ensures that notifications for
11 a given network are sent to agents even if those agents do not
12 appear to be active.
13
14 Additionally, if no enabled dhcp agents can be found for a given
15 network, an error will be logged.  Raising an exception might be
16 preferable, but has such a large testing impact that it will be
17 submitted as a separate patch if deemed necessary.
18
19 Closes-bug: #1192381
20 (cherry picked from commit 522f9f94681de5903422cfde11b93f5c0e71e532)
21
22 Change-Id: Id3e639d9cf3d16708fd66a4baebd3fbeeed3dde8
23 ---
24  .../api/rpc/agentnotifiers/dhcp_rpc_agent_api.py   | 35 ++++++++--
25  neutron/db/agents_db.py                            |  4 ++
26  neutron/tests/unit/api/__init__.py                 |  0
27  neutron/tests/unit/api/rpc/__init__.py             |  0
28  .../tests/unit/api/rpc/agentnotifiers/__init__.py  |  0
29  .../rpc/agentnotifiers/test_dhcp_rpc_agent_api.py  | 76 ++++++++++++++++++++++
30  6 files changed, 108 insertions(+), 7 deletions(-)
31  create mode 100644 neutron/tests/unit/api/__init__.py
32  create mode 100644 neutron/tests/unit/api/rpc/__init__.py
33  create mode 100644 neutron/tests/unit/api/rpc/agentnotifiers/__init__.py
34  create mode 100644 neutron/tests/unit/api/rpc/agentnotifiers/test_dhcp_rpc_agent_api.py
35
36 diff --git a/neutron/api/rpc/agentnotifiers/dhcp_rpc_agent_api.py b/neutron/api/rpc/agentnotifiers/dhcp_rpc_agent_api.py
37 index 1086a9e..4ed724d 100644
38 --- a/neutron/api/rpc/agentnotifiers/dhcp_rpc_agent_api.py
39 +++ b/neutron/api/rpc/agentnotifiers/dhcp_rpc_agent_api.py
40 @@ -43,12 +43,11 @@ class DhcpAgentNotifyAPI(proxy.RpcProxy):
41          super(DhcpAgentNotifyAPI, self).__init__(
42              topic=topic, default_version=self.BASE_RPC_API_VERSION)
43  
44 -    def _get_dhcp_agents(self, context, network_id):
45 +    def _get_enabled_dhcp_agents(self, context, network_id):
46 +        """Return enabled dhcp agents associated with the given network."""
47          plugin = manager.NeutronManager.get_plugin()
48 -        dhcp_agents = plugin.get_dhcp_agents_hosting_networks(
49 -            context, [network_id], active=True)
50 -        return [(dhcp_agent.host, dhcp_agent.topic) for
51 -                dhcp_agent in dhcp_agents]
52 +        agents = plugin.get_dhcp_agents_hosting_networks(context, [network_id])
53 +        return [x for x in agents if x.admin_state_up]
54  
55      def _notification_host(self, context, method, payload, host):
56          """Notify the agent on host."""
57 @@ -76,11 +75,33 @@ class DhcpAgentNotifyAPI(proxy.RpcProxy):
58                              context, 'network_create_end',
59                              {'network': {'id': network_id}},
60                              agent['host'])
61 -            for (host, topic) in self._get_dhcp_agents(context, network_id):
62 +            agents = self._get_enabled_dhcp_agents(context, network_id)
63 +            if not agents:
64 +                LOG.error(_("No DHCP agents are associated with network "
65 +                            "'%(net_id)s'. Unable to send notification "
66 +                            "for '%(method)s' with payload: %(payload)s"),
67 +                          {
68 +                              'net_id': network_id,
69 +                              'method': method,
70 +                              'payload': payload,
71 +                          })
72 +                return
73 +            active_agents = [x for x in agents if x.is_active]
74 +            if active_agents != agents:
75 +                LOG.warning(_("Only %(active)d of %(total)d DHCP agents "
76 +                              "associated with network '%(net_id)s' are "
77 +                              "marked as active, so notifications may "
78 +                              "be sent to inactive agents."),
79 +                            {
80 +                                'active': len(active_agents),
81 +                                'total': len(agents),
82 +                                'net_id': network_id,
83 +                            })
84 +            for agent in agents:
85                  self.cast(
86                      context, self.make_msg(method,
87                                             payload=payload),
88 -                    topic='%s.%s' % (topic, host))
89 +                    topic='%s.%s' % (agent.topic, agent.host))
90          else:
91              # besides the non-agentscheduler plugin,
92              # There is no way to query who is hosting the network
93 diff --git a/neutron/db/agents_db.py b/neutron/db/agents_db.py
94 index e095a4c..fdcc6d3 100644
95 --- a/neutron/db/agents_db.py
96 +++ b/neutron/db/agents_db.py
97 @@ -60,6 +60,10 @@ class Agent(model_base.BASEV2, models_v2.HasId):
98      # configurations: a json dict string, I think 4095 is enough
99      configurations = sa.Column(sa.String(4095), nullable=False)
100  
101 +    @property
102 +    def is_active(self):
103 +        return not AgentDbMixin.is_agent_down(self.heartbeat_timestamp)
104 +
105  
106  class AgentDbMixin(ext_agent.AgentPluginBase):
107      """Mixin class to add agent extension to db_plugin_base_v2."""
108 diff --git a/neutron/tests/unit/api/__init__.py b/neutron/tests/unit/api/__init__.py
109 new file mode 100644
110 index 0000000..e69de29
111 diff --git a/neutron/tests/unit/api/rpc/__init__.py b/neutron/tests/unit/api/rpc/__init__.py
112 new file mode 100644
113 index 0000000..e69de29
114 diff --git a/neutron/tests/unit/api/rpc/agentnotifiers/__init__.py b/neutron/tests/unit/api/rpc/agentnotifiers/__init__.py
115 new file mode 100644
116 index 0000000..e69de29
117 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
118 new file mode 100644
119 index 0000000..b175d34
120 --- /dev/null
121 +++ b/neutron/tests/unit/api/rpc/agentnotifiers/test_dhcp_rpc_agent_api.py
122 @@ -0,0 +1,76 @@
123 +# Copyright (c) 2013 Red Hat, Inc.
124 +#
125 +# Licensed under the Apache License, Version 2.0 (the "License");
126 +# you may not use this file except in compliance with the License.
127 +# You may obtain a copy of the License at
128 +#
129 +#    http://www.apache.org/licenses/LICENSE-2.0
130 +#
131 +# Unless required by applicable law or agreed to in writing, software
132 +# distributed under the License is distributed on an "AS IS" BASIS,
133 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
134 +# implied.
135 +# See the License for the specific language governing permissions and
136 +# limitations under the License.
137 +
138 +import contextlib
139 +
140 +import mock
141 +
142 +from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
143 +from neutron.common import utils
144 +from neutron import manager
145 +from neutron.tests import base
146 +
147 +
148 +class TestDhcpAgentNotifyAPI(base.BaseTestCase):
149 +
150 +    def setUp(self):
151 +        super(TestDhcpAgentNotifyAPI, self).setUp()
152 +        self.notify = dhcp_rpc_agent_api.DhcpAgentNotifyAPI()
153 +
154 +    def test_get_enabled_dhcp_agents_filters_disabled_agents(self):
155 +        disabled_agent = mock.Mock()
156 +        disabled_agent.admin_state_up = False
157 +        enabled_agent = mock.Mock()
158 +        with mock.patch.object(manager.NeutronManager,
159 +                               'get_plugin') as mock_get_plugin:
160 +            mock_get_plugin.return_value = mock_plugin = mock.Mock()
161 +            with mock.patch.object(
162 +                mock_plugin, 'get_dhcp_agents_hosting_networks'
163 +            ) as mock_get_agents:
164 +                mock_get_agents.return_value = [disabled_agent, enabled_agent]
165 +                result = self.notify._get_enabled_dhcp_agents('ctx', 'net_id')
166 +        self.assertEqual(result, [enabled_agent])
167 +
168 +    def _test_notification(self, agents):
169 +        with contextlib.nested(
170 +            mock.patch.object(manager.NeutronManager, 'get_plugin'),
171 +            mock.patch.object(utils, 'is_extension_supported'),
172 +            mock.patch.object(self.notify, '_get_enabled_dhcp_agents')
173 +        ) as (m1, m2, mock_get_agents):
174 +            mock_get_agents.return_value = agents
175 +            self.notify._notification(mock.Mock(), 'foo', {}, 'net_id')
176 +
177 +    def test_notification_sends_cast_for_enabled_agent(self):
178 +        with mock.patch.object(self.notify, 'cast') as mock_cast:
179 +            self._test_notification([mock.Mock()])
180 +        self.assertEqual(mock_cast.call_count, 1)
181 +
182 +    def test_notification_logs_error_for_no_enabled_agents(self):
183 +        with mock.patch.object(self.notify, 'cast') as mock_cast:
184 +            with mock.patch.object(dhcp_rpc_agent_api.LOG,
185 +                                   'error') as mock_log:
186 +                self._test_notification([])
187 +        self.assertEqual(mock_cast.call_count, 0)
188 +        self.assertEqual(mock_log.call_count, 1)
189 +
190 +    def test_notification_logs_warning_for_inactive_agents(self):
191 +        agent = mock.Mock()
192 +        agent.is_active = False
193 +        with mock.patch.object(self.notify, 'cast') as mock_cast:
194 +            with mock.patch.object(dhcp_rpc_agent_api.LOG,
195 +                                   'warning') as mock_log:
196 +                self._test_notification([agent])
197 +        self.assertEqual(mock_cast.call_count, 1)
198 +        self.assertEqual(mock_log.call_count, 1)
199 -- 
200 1.8.3.1
201