OnMetal Driver Can't Handle 404s on Port Delete
When quark tries to delete an onmetal port that no longer exists downstream, it should happily ignore the 404 and move on.Instead, it complains and retries before eventually giving up. This is due to not catching the correctexception from the neutron client.This doesn't present as a 500 to any clients, as the exception is caught and ignored, but it gums up the logs and wastes time making requests multiple times. Implements: Exception handling Closes-Bug: #1738277 Change-Id: I4ccd9be891e6c533ceb68427d47ed658bf27f4a8
This commit is contained in:
parent
92fa456c71
commit
43c8359db5
@ -20,6 +20,7 @@ import json
|
|||||||
import netaddr
|
import netaddr
|
||||||
|
|
||||||
from neutron_lib import exceptions as n_exc
|
from neutron_lib import exceptions as n_exc
|
||||||
|
from neutronclient.neutron import client as neutron_client
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_utils import importutils
|
from oslo_utils import importutils
|
||||||
@ -397,13 +398,13 @@ class IronicDriver(base.BaseDriver):
|
|||||||
def _delete_port(self, context, port_id):
|
def _delete_port(self, context, port_id):
|
||||||
try:
|
try:
|
||||||
return self._client.delete_port(port_id)
|
return self._client.delete_port(port_id)
|
||||||
|
except neutron_client.utils.exceptions.PortNotFoundClient:
|
||||||
|
# Port not found exception encountered, such as port deleted
|
||||||
|
LOG.error("port %s not found downstream."
|
||||||
|
"ignoring delete." % (port_id))
|
||||||
|
return None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# This doesn't get wrapped by the client unfortunately.
|
# Any generic exception (say 500 or others)
|
||||||
if "404 not found" in str(e).lower():
|
|
||||||
LOG.error("port %s not found downstream. ignoring delete."
|
|
||||||
% (port_id))
|
|
||||||
return
|
|
||||||
|
|
||||||
msg = ("failed to delete downstream port. "
|
msg = ("failed to delete downstream port. "
|
||||||
"exception: %s" % (e))
|
"exception: %s" % (e))
|
||||||
LOG.exception(msg)
|
LOG.exception(msg)
|
||||||
|
@ -14,18 +14,17 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
|
|
||||||
import netaddr
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
import netaddr
|
||||||
|
from neutronclient.neutron import client as neutron_client
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
|
||||||
import quark.drivers.ironic_driver
|
import quark.drivers.ironic_driver
|
||||||
from quark import network_strategy
|
from quark import network_strategy
|
||||||
from quark.tests import test_base
|
from quark.tests import test_base
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
|
||||||
class TestIronicDriverBase(test_base.TestBase):
|
class TestIronicDriverBase(test_base.TestBase):
|
||||||
|
|
||||||
@ -569,6 +568,29 @@ class TestIronicDriverCreatePort(TestIronicDriverBase):
|
|||||||
|
|
||||||
class TestIronicDriverDeletePort(TestIronicDriverBase):
|
class TestIronicDriverDeletePort(TestIronicDriverBase):
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def test_port_not_found_exception_no_retry(self):
|
||||||
|
'''When port not found we must only call delete once.'''
|
||||||
|
ironic_driver = quark.drivers.ironic_driver.IronicDriver()
|
||||||
|
ironic_driver._client = mock.MagicMock()
|
||||||
|
ironic_driver._client.delete_port.return_value = None
|
||||||
|
side_effect = neutron_client.utils.exceptions.PortNotFoundClient
|
||||||
|
ironic_driver._client.delete_port.side_effect = side_effect
|
||||||
|
port_id = 'very_fake_port_id'
|
||||||
|
ironic_driver.delete_port(self.context, port_id)
|
||||||
|
ironic_driver._client.delete_port.assert_called_once_with(port_id)
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def test_port_not_found_exception_with_retry(self):
|
||||||
|
'''When port delete fails for unknown reasons, retry.'''
|
||||||
|
ironic_driver = quark.drivers.ironic_driver.IronicDriver()
|
||||||
|
ironic_driver._client = mock.MagicMock()
|
||||||
|
ironic_driver._client.delete_port.side_effect = Exception
|
||||||
|
port_id = 'very_fake_port_id'
|
||||||
|
ironic_driver.delete_port(self.context, port_id)
|
||||||
|
self.assertEqual(ironic_driver._client.delete_port.call_count,
|
||||||
|
CONF.IRONIC.operation_retries)
|
||||||
|
|
||||||
def test_delete_port(self):
|
def test_delete_port(self):
|
||||||
with self._stubs(delete_port=[None]) as (driver, client,
|
with self._stubs(delete_port=[None]) as (driver, client,
|
||||||
_, delete):
|
_, delete):
|
||||||
@ -576,6 +598,7 @@ class TestIronicDriverDeletePort(TestIronicDriverBase):
|
|||||||
delete.assert_called_once_with("foo")
|
delete.assert_called_once_with("foo")
|
||||||
|
|
||||||
def test_delete_port_retries(self):
|
def test_delete_port_retries(self):
|
||||||
|
|
||||||
delete_port = [Exception("uhoh"), None]
|
delete_port = [Exception("uhoh"), None]
|
||||||
with self._stubs(delete_port=delete_port) as (driver,
|
with self._stubs(delete_port=delete_port) as (driver,
|
||||||
client, _,
|
client, _,
|
||||||
@ -601,12 +624,18 @@ class TestIronicDriverDeletePort(TestIronicDriverBase):
|
|||||||
mock.call("foo")])
|
mock.call("foo")])
|
||||||
|
|
||||||
def test_delete_port_ignores_404(self):
|
def test_delete_port_ignores_404(self):
|
||||||
|
|
||||||
|
'''Any exception other than neutronclient.PortNotFound would be
|
||||||
|
|
||||||
|
considered as generic exception, one retry for generic exception
|
||||||
|
'''
|
||||||
|
|
||||||
delete_port = [Exception("404 not found"), None]
|
delete_port = [Exception("404 not found"), None]
|
||||||
with self._stubs(delete_port=delete_port) as (driver,
|
with self._stubs(delete_port=delete_port) as (driver,
|
||||||
client, _,
|
client, _,
|
||||||
delete):
|
delete):
|
||||||
driver.delete_port(self.context, "foo")
|
driver.delete_port(self.context, "foo")
|
||||||
delete.assert_called_once_with("foo")
|
self.assertEqual(delete.call_count, 2)
|
||||||
|
|
||||||
|
|
||||||
class TestIronicDriverUpdatePort(TestIronicDriverBase):
|
class TestIronicDriverUpdatePort(TestIronicDriverBase):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user