Skip to content

Commit 14f9b76

Browse files
kajinamitmelwitt
authored andcommitted
Retry attachment delete API call for 504 Gateway Timeout
When cinder-api runs behind a load balancer(eg haproxy), the load balancer can return 504 Gateway Timeout when cinder-api does not respond within timeout. This change ensures nova retries deleting a volume attachment in that case. Also this change makes nova ignore 404 in the API call. This is required because cinder might continue deleting the attachment even if the load balancer returns 504. This also helps us in the situation where the volume attachment was accidentally removed by users. Closes-Bug: #1978444 Change-Id: I593011d9f4c43cdae7a3d53b556c6e2a2b939989 (cherry picked from commit 8f4b740) (cherry picked from commit b94ffb1)
1 parent 513241a commit 14f9b76

File tree

3 files changed

+54
-11
lines changed

3 files changed

+54
-11
lines changed

nova/tests/unit/volume/test_cinder.py

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -520,16 +520,15 @@ def test_attachment_delete(self, mock_cinderclient):
520520
@mock.patch('nova.volume.cinder.cinderclient')
521521
def test_attachment_delete_failed(self, mock_cinderclient, mock_log):
522522
mock_cinderclient.return_value.attachments.delete.side_effect = (
523-
cinder_exception.NotFound(404, '404'))
523+
cinder_exception.BadRequest(400, '400'))
524524

525525
attachment_id = uuids.attachment
526-
ex = self.assertRaises(exception.VolumeAttachmentNotFound,
526+
ex = self.assertRaises(exception.InvalidInput,
527527
self.api.attachment_delete,
528528
self.ctx,
529529
attachment_id)
530530

531-
self.assertEqual(404, ex.code)
532-
self.assertIn(attachment_id, str(ex))
531+
self.assertEqual(400, ex.code)
533532

534533
@mock.patch('nova.volume.cinder.cinderclient',
535534
side_effect=exception.CinderAPIVersionNotAvailable(
@@ -545,6 +544,16 @@ def test_attachment_delete_unsupported_api_version(self,
545544
mock_cinderclient.assert_called_once_with(self.ctx, '3.44',
546545
skip_version_check=True)
547546

547+
@mock.patch('nova.volume.cinder.cinderclient')
548+
def test_attachment_delete_not_found(self, mock_cinderclient):
549+
mock_cinderclient.return_value.attachments.delete.side_effect = (
550+
cinder_exception.ClientException(404))
551+
552+
attachment_id = uuids.attachment
553+
self.api.attachment_delete(self.ctx, attachment_id)
554+
555+
self.assertEqual(1, mock_cinderclient.call_count)
556+
548557
@mock.patch('nova.volume.cinder.cinderclient')
549558
def test_attachment_delete_internal_server_error(self, mock_cinderclient):
550559
mock_cinderclient.return_value.attachments.delete.side_effect = (
@@ -568,6 +577,29 @@ def test_attachment_delete_internal_server_error_do_not_raise(
568577

569578
self.assertEqual(2, mock_cinderclient.call_count)
570579

580+
@mock.patch('nova.volume.cinder.cinderclient')
581+
def test_attachment_delete_gateway_timeout(self, mock_cinderclient):
582+
mock_cinderclient.return_value.attachments.delete.side_effect = (
583+
cinder_exception.ClientException(504))
584+
585+
self.assertRaises(cinder_exception.ClientException,
586+
self.api.attachment_delete,
587+
self.ctx, uuids.attachment_id)
588+
589+
self.assertEqual(5, mock_cinderclient.call_count)
590+
591+
@mock.patch('nova.volume.cinder.cinderclient')
592+
def test_attachment_delete_gateway_timeout_do_not_raise(
593+
self, mock_cinderclient):
594+
# generate exception, and then have a normal return on the next retry
595+
mock_cinderclient.return_value.attachments.delete.side_effect = [
596+
cinder_exception.ClientException(504), None]
597+
598+
attachment_id = uuids.attachment
599+
self.api.attachment_delete(self.ctx, attachment_id)
600+
601+
self.assertEqual(2, mock_cinderclient.call_count)
602+
571603
@mock.patch('nova.volume.cinder.cinderclient')
572604
def test_attachment_delete_bad_request_exception(self, mock_cinderclient):
573605
mock_cinderclient.return_value.attachments.delete.side_effect = (

nova/volume/cinder.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -883,19 +883,23 @@ def attachment_update(self, context, attachment_id, connector,
883883
@retrying.retry(stop_max_attempt_number=5,
884884
retry_on_exception=lambda e:
885885
(isinstance(e, cinder_exception.ClientException) and
886-
e.code == 500))
886+
e.code in (500, 504)))
887887
def attachment_delete(self, context, attachment_id):
888888
try:
889889
cinderclient(
890890
context, '3.44', skip_version_check=True).attachments.delete(
891891
attachment_id)
892892
except cinder_exception.ClientException as ex:
893-
with excutils.save_and_reraise_exception():
894-
LOG.error('Delete attachment failed for attachment '
895-
'%(id)s. Error: %(msg)s Code: %(code)s',
896-
{'id': attachment_id,
897-
'msg': str(ex),
898-
'code': getattr(ex, 'code', None)})
893+
if ex.code == 404:
894+
LOG.warning('Attachment %(id)s does not exist. Ignoring.',
895+
{'id': attachment_id})
896+
else:
897+
with excutils.save_and_reraise_exception():
898+
LOG.error('Delete attachment failed for attachment '
899+
'%(id)s. Error: %(msg)s Code: %(code)s',
900+
{'id': attachment_id,
901+
'msg': str(ex),
902+
'code': getattr(ex, 'code', None)})
899903

900904
@translate_attachment_exception
901905
def attachment_complete(self, context, attachment_id):
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
fixes:
3+
- |
4+
`Bug #1978444 <https://bugs.launchpad.net/nova/+bug/1978444>`_: Now nova
5+
retries deleting a volume attachment in case Cinder API returns
6+
``504 Gateway Timeout``. Also, ``404 Not Found`` is now ignored and
7+
leaves only a warning message.

0 commit comments

Comments
 (0)