Skip to content

Commit 646270d

Browse files
antonkurbatovralonsoh
authored andcommitted
Fixing the 500 HTTP code in the metadata service if Nova is down
If the Nova metadata service is unavailable, the requests.request() function may raise a ConnectionError. This results in the upper code returning a 500 HTTP status code to the user along with a traceback. Let's handle this scenario and instead return a 503 HTTP status code (service unavailable). If the Nova service is down and is behind another proxy (such as Nginx), then instead of a ConnectionError, the request may result in receiving a 502 or 503 HTTP status code. Let's also consider this situation and add support for an additional 504 code. Closes-Bug: #2059032 Change-Id: I16be18c46a6796224b0793dc385b0ddec01739c4 (cherry picked from commit 6395b4f)
1 parent d683804 commit 646270d

File tree

5 files changed

+78
-24
lines changed

5 files changed

+78
-24
lines changed

neutron/agent/metadata/agent.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -246,12 +246,18 @@ def _proxy_request(self, instance_id, tenant_id, req):
246246
client_cert = (self.conf.nova_client_cert,
247247
self.conf.nova_client_priv_key)
248248

249-
resp = requests.request(method=req.method, url=url,
250-
headers=headers,
251-
data=req.body,
252-
cert=client_cert,
253-
verify=verify_cert,
254-
timeout=60)
249+
try:
250+
resp = requests.request(method=req.method, url=url,
251+
headers=headers,
252+
data=req.body,
253+
cert=client_cert,
254+
verify=verify_cert,
255+
timeout=60)
256+
except requests.ConnectionError:
257+
msg = _('The remote metadata server is temporarily unavailable. '
258+
'Please try again later.')
259+
explanation = str(msg)
260+
return webob.exc.HTTPServiceUnavailable(explanation=explanation)
255261

256262
if resp.status_code == 200:
257263
req.response.content_type = resp.headers['content-type']
@@ -264,19 +270,16 @@ def _proxy_request(self, instance_id, tenant_id, req):
264270
'response usually occurs when shared secrets do not match.'
265271
)
266272
return webob.exc.HTTPForbidden()
267-
elif resp.status_code == 400:
268-
return webob.exc.HTTPBadRequest()
269-
elif resp.status_code == 404:
270-
return webob.exc.HTTPNotFound()
271-
elif resp.status_code == 409:
272-
return webob.exc.HTTPConflict()
273273
elif resp.status_code == 500:
274274
msg = _(
275275
'Remote metadata server experienced an internal server error.'
276276
)
277277
LOG.warning(msg)
278278
explanation = str(msg)
279279
return webob.exc.HTTPInternalServerError(explanation=explanation)
280+
elif resp.status_code in (400, 404, 409, 502, 503, 504):
281+
webob_exc_cls = webob.exc.status_map.get(resp.status_code)
282+
return webob_exc_cls()
280283
else:
281284
raise Exception(_('Unexpected response code: %s') %
282285
resp.status_code)

neutron/agent/ovn/metadata/server.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -168,12 +168,18 @@ def _proxy_request(self, instance_id, tenant_id, req):
168168
client_cert = (self.conf.nova_client_cert,
169169
self.conf.nova_client_priv_key)
170170

171-
resp = requests.request(method=req.method, url=url,
172-
headers=headers,
173-
data=req.body,
174-
cert=client_cert,
175-
verify=verify_cert,
176-
timeout=60)
171+
try:
172+
resp = requests.request(method=req.method, url=url,
173+
headers=headers,
174+
data=req.body,
175+
cert=client_cert,
176+
verify=verify_cert,
177+
timeout=60)
178+
except requests.ConnectionError:
179+
msg = _('The remote metadata server is temporarily unavailable. '
180+
'Please try again later.')
181+
explanation = str(msg)
182+
return webob.exc.HTTPServiceUnavailable(explanation=explanation)
177183

178184
if resp.status_code == 200:
179185
req.response.content_type = resp.headers['content-type']
@@ -186,19 +192,16 @@ def _proxy_request(self, instance_id, tenant_id, req):
186192
'response usually occurs when shared secrets do not match.'
187193
)
188194
return webob.exc.HTTPForbidden()
189-
elif resp.status_code == 400:
190-
return webob.exc.HTTPBadRequest()
191-
elif resp.status_code == 404:
192-
return webob.exc.HTTPNotFound()
193-
elif resp.status_code == 409:
194-
return webob.exc.HTTPConflict()
195195
elif resp.status_code == 500:
196196
msg = _(
197197
'Remote metadata server experienced an internal server error.'
198198
)
199199
LOG.warning(msg)
200200
explanation = str(msg)
201201
return webob.exc.HTTPInternalServerError(explanation=explanation)
202+
elif resp.status_code in (400, 404, 409, 502, 503, 504):
203+
webob_exc_cls = webob.exc.status_map.get(resp.status_code)
204+
return webob_exc_cls()
202205
else:
203206
raise Exception(_('Unexpected response code: %s') %
204207
resp.status_code)

neutron/tests/unit/agent/metadata/test_agent.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import ddt
1818
import netaddr
1919
from neutron_lib import constants as n_const
20+
import requests
2021
import testtools
2122
import webob
2223

@@ -469,10 +470,30 @@ def test_proxy_request_500(self):
469470
self.assertIsInstance(self._proxy_request_test_helper(500),
470471
webob.exc.HTTPInternalServerError)
471472

473+
def test_proxy_request_502(self):
474+
self.assertIsInstance(self._proxy_request_test_helper(502),
475+
webob.exc.HTTPBadGateway)
476+
477+
def test_proxy_request_503(self):
478+
self.assertIsInstance(self._proxy_request_test_helper(503),
479+
webob.exc.HTTPServiceUnavailable)
480+
481+
def test_proxy_request_504(self):
482+
self.assertIsInstance(self._proxy_request_test_helper(504),
483+
webob.exc.HTTPGatewayTimeout)
484+
472485
def test_proxy_request_other_code(self):
473486
with testtools.ExpectedException(Exception):
474487
self._proxy_request_test_helper(302)
475488

489+
def test_proxy_request_conenction_error(self):
490+
req = mock.Mock(path_info='/the_path', query_string='', headers={},
491+
method='GET', body='')
492+
with mock.patch('requests.request') as mock_request:
493+
mock_request.side_effect = requests.ConnectionError()
494+
retval = self.handler._proxy_request('the_id', 'tenant_id', req)
495+
self.assertIsInstance(retval, webob.exc.HTTPServiceUnavailable)
496+
476497

477498
class TestMetadataProxyHandlerNewCache(TestMetadataProxyHandlerBase,
478499
_TestMetadataProxyHandlerCacheMixin):

neutron/tests/unit/agent/ovn/metadata/test_server.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from oslo_config import cfg
1919
from oslo_config import fixture as config_fixture
2020
from oslo_utils import fileutils
21+
import requests
2122
import testtools
2223
import webob
2324

@@ -232,10 +233,30 @@ def test_proxy_request_500(self):
232233
self.assertIsInstance(self._proxy_request_test_helper(500),
233234
webob.exc.HTTPInternalServerError)
234235

236+
def test_proxy_request_502(self):
237+
self.assertIsInstance(self._proxy_request_test_helper(502),
238+
webob.exc.HTTPBadGateway)
239+
240+
def test_proxy_request_503(self):
241+
self.assertIsInstance(self._proxy_request_test_helper(503),
242+
webob.exc.HTTPServiceUnavailable)
243+
244+
def test_proxy_request_504(self):
245+
self.assertIsInstance(self._proxy_request_test_helper(504),
246+
webob.exc.HTTPGatewayTimeout)
247+
235248
def test_proxy_request_other_code(self):
236249
with testtools.ExpectedException(Exception):
237250
self._proxy_request_test_helper(302)
238251

252+
def test_proxy_request_conenction_error(self):
253+
req = mock.Mock(path_info='/the_path', query_string='', headers={},
254+
method='GET', body='')
255+
with mock.patch('requests.request') as mock_request:
256+
mock_request.side_effect = requests.ConnectionError()
257+
retval = self.handler._proxy_request('the_id', 'tenant_id', req)
258+
self.assertIsInstance(retval, webob.exc.HTTPServiceUnavailable)
259+
239260

240261
class TestUnixDomainMetadataProxy(base.BaseTestCase):
241262
def setUp(self):
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
other:
3+
- |
4+
Enhance error handling in the Neutron metadata service for cases when the
5+
Nova metadata service is unavailable, ensuring correct HTTP status codes
6+
are returned.

0 commit comments

Comments
 (0)