Fix issues with rollback on quota failures
Change-Id: Icf62d1a78f2e5474fbf8a5dd68132a9ea7636698
This commit is contained in:
parent
583584cdf7
commit
36a9f1af25
|
@ -18,6 +18,7 @@ from gbpservice.neutron.plugins.ml2plus import driver_api
|
|||
from neutron.db import api as db_api
|
||||
from neutron.plugins.ml2.common import exceptions as ml2_exc
|
||||
from neutron.plugins.ml2 import managers
|
||||
from neutron.quota import resource_registry
|
||||
from oslo_log import log
|
||||
from oslo_utils import excutils
|
||||
|
||||
|
@ -29,6 +30,25 @@ class MechanismManager(managers.MechanismManager):
|
|||
def __init__(self):
|
||||
super(MechanismManager, self).__init__()
|
||||
|
||||
def _call_on_drivers(self, method_name, context,
|
||||
continue_on_failure=False, raise_db_retriable=False):
|
||||
super(MechanismManager, self)._call_on_drivers(
|
||||
method_name, context, continue_on_failure=False,
|
||||
raise_db_retriable=False)
|
||||
if method_name.endswith('_precommit'):
|
||||
# This does the same thing as:
|
||||
# https://github.com/openstack/neutron/blob/newton-eol/neutron/
|
||||
# api/v2/base.py#L489
|
||||
# but from within the scope of the plugin's transaction, such
|
||||
# that if it fails, everything that happened prior to this in
|
||||
# precommit phase can also be rolled back.
|
||||
resource_name = method_name.replace('_precommit', '').replace(
|
||||
'create_', '').replace('update_', '').replace(
|
||||
'delete_', '')
|
||||
tracked_resource = resource_registry.get_resource(resource_name)
|
||||
tracked_resource._dirty_tenants.add(context.current['tenant_id'])
|
||||
resource_registry.set_resources_dirty(context._plugin_context)
|
||||
|
||||
def _call_on_extended_drivers(self, method_name, context,
|
||||
continue_on_failure=False,
|
||||
raise_db_retriable=False):
|
||||
|
|
|
@ -191,10 +191,13 @@ def undecorated(o):
|
|||
else:
|
||||
return o
|
||||
|
||||
|
||||
from neutron.db import common_db_mixin as common_db_api
|
||||
from neutron.db.quota import api as quota_api
|
||||
from neutron.db.quota import driver # noqa
|
||||
from neutron.db.quota import models as quota_models
|
||||
from neutron import quota
|
||||
from neutron.quota import resource_registry as res_reg
|
||||
from oslo_config import cfg
|
||||
|
||||
|
||||
f = quota_api.remove_reservation
|
||||
|
@ -208,6 +211,37 @@ def commit_reservation(context, reservation_id):
|
|||
quota.QUOTAS.get_driver().commit_reservation = commit_reservation
|
||||
|
||||
|
||||
def patched_set_resources_dirty(context):
|
||||
if not cfg.CONF.QUOTAS.track_quota_usage:
|
||||
return
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
for res in res_reg.get_all_resources().values():
|
||||
if res_reg.is_tracked(res.name) and res.dirty:
|
||||
dirty_tenants_snap = res._dirty_tenants.copy()
|
||||
for tenant_id in dirty_tenants_snap:
|
||||
query = common_db_api.model_query(
|
||||
context, quota_models.QuotaUsage)
|
||||
query = query.filter_by(resource=res.name).filter_by(
|
||||
tenant_id=tenant_id)
|
||||
usage_data = query.first()
|
||||
# Set dirty if not set already. This effectively
|
||||
# patches the inner notify method:
|
||||
# https://github.com/openstack/neutron/blob/newton-eol/
|
||||
# neutron/api/v2/base.py#L481
|
||||
# to avoid updating the QuotaUsages table outside
|
||||
# from that method (which starts a new transaction).
|
||||
# The dirty marking would have been already done
|
||||
# in the ml2plus manager at the end of the pre_commit
|
||||
# stage (and prior to the plugin initiated transaction
|
||||
# completing).
|
||||
if usage_data and not usage_data.dirty:
|
||||
res.mark_dirty(context)
|
||||
|
||||
|
||||
quota.resource_registry.set_resources_dirty = patched_set_resources_dirty
|
||||
|
||||
|
||||
from oslo_db.sqlalchemy import exc_filters
|
||||
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ from neutron.tests.unit.db import test_db_base_plugin_v2 as test_plugin
|
|||
from neutron.tests.unit.extensions import test_address_scope
|
||||
from neutron.tests.unit.extensions import test_l3
|
||||
from neutron.tests.unit.extensions import test_securitygroup
|
||||
from neutron.tests.unit.plugins.ml2 import test_tracked_resources as tr_res
|
||||
from neutron.tests.unit import testlib_api
|
||||
from neutron_lib.callbacks import registry
|
||||
from neutron_lib import constants as n_constants
|
||||
|
@ -238,7 +239,8 @@ class ApicAimTestCase(test_address_scope.AddressScopeTestCase,
|
|||
test_l3.L3NatTestCaseMixin, ApicAimTestMixin,
|
||||
test_securitygroup.SecurityGroupsTestCase):
|
||||
|
||||
def setUp(self, mechanism_drivers=None, tenant_network_types=None):
|
||||
def setUp(self, mechanism_drivers=None, tenant_network_types=None,
|
||||
plugin=None, ext_mgr=None):
|
||||
# Enable the test mechanism driver to ensure that
|
||||
# we can successfully call through to all mechanism
|
||||
# driver apis.
|
||||
|
@ -3061,6 +3063,15 @@ class TestAimMapping(ApicAimTestCase):
|
|||
dhcp_agt_mock.stop()
|
||||
|
||||
|
||||
class TestTrackedResources(tr_res.TestTrackedResources, ApicAimTestCase):
|
||||
|
||||
def setUp(self, **kwargs):
|
||||
super(TestTrackedResources, self).setUp(**kwargs)
|
||||
for patch in mock.mock._patch._active_patches:
|
||||
if patch.attribute == '_ensure_default_security_group':
|
||||
patch.stop()
|
||||
|
||||
|
||||
class TestSyncState(ApicAimTestCase):
|
||||
@staticmethod
|
||||
def _get_synced_status(self, context, resource, **kwargs):
|
||||
|
|
Loading…
Reference in New Issue