From 77de9653fd60a802b11f157972f7b3e81497e8a7 Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Wed, 27 Jan 2016 05:18:13 -0800 Subject: [PATCH] Raise RetryRequest on policy parent not found During a port list operation, a port and its parent network may be concurrently deleted from the database after they have been retrieved from the DB but before policy is enforced. Then when the policy engine tries to do a get_network to check network ownership for a port on a network that no longer exists, it will encounter a NetworkNotFound exception from the core plugin. This exception was being propagated all of the way up to the whole API operation as a 404, which made no sense in the context of a port list. This patch adjusts the logic to catch any NotFound exceptions during this processing and convert them into a RetryRequest to trigger the API to restart the operation. At this point the objects will be gone from the database so the problematic items will not be passed to the policy engine for enforcement. Closes-Bug: #1528031 Change-Id: I89d12fe0767e1c7ecb68138b5f6f17aa68a68769 --- neutron/policy.py | 8 ++++++++ neutron/tests/unit/test_policy.py | 13 +++++++++++++ 2 files changed, 21 insertions(+) diff --git a/neutron/policy.py b/neutron/policy.py index ed680566b7d..19db42364aa 100644 --- a/neutron/policy.py +++ b/neutron/policy.py @@ -17,6 +17,7 @@ import collections import re from oslo_config import cfg +from oslo_db import exception as db_exc from oslo_log import log as logging from oslo_policy import policy from oslo_utils import excutils @@ -257,6 +258,13 @@ class OwnerCheck(policy.Check): target[parent_foreign_key], fields=[parent_field]) target[self.target_field] = data[parent_field] + except exceptions.NotFound as e: + # NOTE(kevinbenton): a NotFound exception can occur if a + # list operation is happening at the same time as one of + # the parents and its children being deleted. So we issue + # a RetryRequest so the API will redo the lookup and the + # problem items will be gone. + raise db_exc.RetryRequest(e) except Exception: with excutils.save_and_reraise_exception(): LOG.exception(_LE('Policy check error while calling %s!'), diff --git a/neutron/tests/unit/test_policy.py b/neutron/tests/unit/test_policy.py index 23ad4d9e7bc..88bf6e2d66c 100644 --- a/neutron/tests/unit/test_policy.py +++ b/neutron/tests/unit/test_policy.py @@ -16,6 +16,7 @@ """Test of Policy Engine For Neutron""" import mock +from oslo_db import exception as db_exc from oslo_policy import fixture as op_fixture from oslo_policy import policy as oslo_policy from oslo_serialization import jsonutils @@ -537,6 +538,18 @@ class NeutronPolicyTestCase(base.BaseTestCase): action, target) + def test_retryrequest_on_notfound(self): + failure = exceptions.NetworkNotFound(net_id='whatever') + action = "create_port:mac" + with mock.patch.object(manager.NeutronManager.get_instance().plugin, + 'get_network', side_effect=failure): + target = {'network_id': 'whatever'} + try: + policy.enforce(self.context, action, target) + self.fail("Did not raise RetryRequest") + except db_exc.RetryRequest as e: + self.assertEqual(failure, e.inner_exc) + def test_enforce_tenant_id_check_parent_resource_bw_compatibility(self): def fakegetnetwork(*args, **kwargs):