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 cd475f9af8)
(cherry picked from commit b5e9148cc7)
(cherry picked from commit 49f49bc2bf)
This commit is contained in:
Szymon Wroblewski 2022-09-02 11:26:40 +02:00 committed by Slawek Kaplonski
parent c81fb5b394
commit c9dbbd4192
2 changed files with 23 additions and 0 deletions

View File

@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from keystoneauth1 import exceptions as ks_exceptions
from keystoneauth1 import loading as ks_loading from keystoneauth1 import loading as ks_loading
from neutron_lib.callbacks import events from neutron_lib.callbacks import events
from neutron_lib.callbacks import registry from neutron_lib.callbacks import registry
@ -29,6 +31,7 @@ from oslo_context import context as common_context
from oslo_log import log as logging from oslo_log import log as logging
from oslo_utils import uuidutils from oslo_utils import uuidutils
from sqlalchemy.orm import attributes as sql_attr from sqlalchemy.orm import attributes as sql_attr
import tenacity
from neutron.notifiers import batch_notifier from neutron.notifiers import batch_notifier
@ -242,6 +245,12 @@ class Notifier(object):
'tag': port.id}) 'tag': port.id})
self.send_port_status(None, None, port) 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): def send_events(self, batched_events):
LOG.debug("Sending events: %s", batched_events) LOG.debug("Sending events: %s", batched_events)
novaclient = self._get_nova_client() novaclient = self._get_nova_client()
@ -251,6 +260,10 @@ class Notifier(object):
except nova_exceptions.NotFound: except nova_exceptions.NotFound:
LOG.debug("Nova returned NotFound for event: %s", LOG.debug("Nova returned NotFound for event: %s",
batched_events) batched_events)
except ks_exceptions.RetriableConnectionFailure:
raise
# next clause handles all exceptions
# so reraise for retry decorator
except Exception: except Exception:
LOG.exception("Failed to notify nova on events: %s", LOG.exception("Failed to notify nova on events: %s",
batched_events) batched_events)

View File

@ -15,6 +15,8 @@
import mock import mock
from keystoneauth1 import exceptions as ks_exc
from neutron_lib import constants as n_const from neutron_lib import constants as n_const
from neutron_lib import context as n_ctx from neutron_lib import context as n_ctx
from neutron_lib import exceptions as n_exc from neutron_lib import exceptions as n_exc
@ -246,6 +248,14 @@ class TestNovaNotify(base.BaseTestCase):
nova_exceptions.NotFound) nova_exceptions.NotFound)
self.nova_notifier.send_events([]) 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') @mock.patch('novaclient.client.Client')
def test_nova_send_events_raises(self, mock_client): def test_nova_send_events_raises(self, mock_client):
mock_client.server_external_events.create.return_value = Exception mock_client.server_external_events.create.return_value = Exception