From 49f49bc2bf3b477df00a92e25a8de66221ec8ee6 Mon Sep 17 00:00:00 2001 From: Szymon Wroblewski Date: Fri, 2 Sep 2022 11:26:40 +0200 Subject: [PATCH] Retry connections to Nova Sometimes Neutron is failing to send notification to Nova due to timeout, refused connection or another HTTP error. Retry send in those cases. Conflicts: neutron/notifiers/nova.py neutron/tests/unit/notifiers/test_nova.py Closes-Bug: #1987780 Change-Id: Iaaccec770484234b704f70f3c144efac4d8ffba0 (cherry picked from commit cd475f9af898b81d98b3e0d3f55b94ea653c193c) (cherry picked from commit b5e9148cc7311080ba1b2a410145949c1adaa0ca) --- neutron/notifiers/nova.py | 12 ++++++++++++ neutron/tests/unit/notifiers/test_nova.py | 9 +++++++++ 2 files changed, 21 insertions(+) diff --git a/neutron/notifiers/nova.py b/neutron/notifiers/nova.py index 711999c7d6a..789ad17885c 100644 --- a/neutron/notifiers/nova.py +++ b/neutron/notifiers/nova.py @@ -15,6 +15,7 @@ import contextlib +from keystoneauth1 import exceptions as ks_exceptions from keystoneauth1 import loading as ks_loading from neutron_lib.callbacks import events from neutron_lib.callbacks import registry @@ -31,6 +32,7 @@ from oslo_context import context as common_context from oslo_log import log as logging from oslo_utils import uuidutils from sqlalchemy.orm import attributes as sql_attr +import tenacity from neutron.notifiers import batch_notifier @@ -258,6 +260,12 @@ class Notifier(object): 'tag': port.id}) self.send_port_status(None, None, port) + @tenacity.retry( + retry=tenacity.retry_if_exception_type( + ks_exceptions.RetriableConnectionFailure), + wait=tenacity.wait_exponential(multiplier=0.01, max=1), + stop=tenacity.stop_after_delay(1), + after=tenacity.after_log(LOG, logging.DEBUG)) def send_events(self, batched_events): LOG.debug("Sending events: %s", batched_events) novaclient = self._get_nova_client() @@ -267,6 +275,10 @@ class Notifier(object): except nova_exceptions.NotFound: LOG.debug("Nova returned NotFound for event: %s", batched_events) + except ks_exceptions.RetriableConnectionFailure: + raise + # next clause handles all exceptions + # so reraise for retry decorator except Exception: LOG.exception("Failed to notify nova on events: %s", batched_events) diff --git a/neutron/tests/unit/notifiers/test_nova.py b/neutron/tests/unit/notifiers/test_nova.py index 10b2550857d..6a43f5ee081 100644 --- a/neutron/tests/unit/notifiers/test_nova.py +++ b/neutron/tests/unit/notifiers/test_nova.py @@ -15,6 +15,7 @@ from unittest import mock +from keystoneauth1 import exceptions as ks_exc from neutron_lib import constants as n_const from neutron_lib import context as n_ctx from neutron_lib import exceptions as n_exc @@ -246,6 +247,14 @@ class TestNovaNotify(base.BaseTestCase): nova_exceptions.NotFound) self.nova_notifier.send_events([]) + @mock.patch('novaclient.client.Client') + def test_nova_send_events_raises_connect_exc(self, mock_client): + create = mock_client().server_external_events.create + create.side_effect = ( + ks_exc.ConnectFailure, ks_exc.ConnectTimeout, []) + self.nova_notifier.send_events([]) + self.assertEqual(3, create.call_count) + @mock.patch('novaclient.client.Client') def test_nova_send_events_raises(self, mock_client): mock_client.server_external_events.create.return_value = Exception