5cbb6f7731
Cleaning up a little technical debt by adding docstrings for our public APIs. Note: I plan to submit a patch per file as part of this bug work. These patches won't depend on each other. The last patch of the series will close the bug. Change-Id: I1490e6b69e62617d3d29bd9e0eb8eb0f76ab5cc6 Partial-Bug: #1614594
107 lines
4.1 KiB
Python
107 lines
4.1 KiB
Python
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import six
|
|
|
|
from neutron_lib import exceptions as n_exc
|
|
from sqlalchemy.orm import properties
|
|
|
|
from neutron_lib._i18n import _
|
|
from oslo_db import exception as db_exc
|
|
from oslo_utils import excutils
|
|
from sqlalchemy.orm import exc
|
|
|
|
|
|
def get_and_validate_sort_keys(sorts, model):
|
|
"""Extract sort keys from sorts and ensure they are valid for the model.
|
|
|
|
:param sorts: A list of (key, direction) tuples.
|
|
:param model: A sqlalchemy ORM model class.
|
|
:returns: A list of the extracted sort keys.
|
|
:raises BadRequest: If a sort key attribute references another resource
|
|
and cannot be used in the sort.
|
|
"""
|
|
|
|
sort_keys = [s[0] for s in sorts]
|
|
for sort_key in sort_keys:
|
|
try:
|
|
sort_key_attr = getattr(model, sort_key)
|
|
except AttributeError:
|
|
# Extension attributes don't support sorting. Because it
|
|
# existed in attr_info, it will be caught here.
|
|
msg = _("'%s' is an invalid attribute for sort key") % sort_key
|
|
raise n_exc.BadRequest(resource=model.__tablename__, msg=msg)
|
|
if isinstance(sort_key_attr.property,
|
|
properties.RelationshipProperty):
|
|
msg = _("Attribute '%(attr)s' references another resource and "
|
|
"cannot be used to sort '%(resource)s' resources"
|
|
) % {'attr': sort_key, 'resource': model.__tablename__}
|
|
raise n_exc.BadRequest(resource=model.__tablename__, msg=msg)
|
|
|
|
return sort_keys
|
|
|
|
|
|
def get_sort_dirs(sorts, page_reverse=False):
|
|
"""Extract sort directions from sorts, possibly reversed.
|
|
|
|
:param sorts: A list of (key, direction) tuples.
|
|
:param page_reverse: True if sort direction is reversed.
|
|
:returns: The list of extracted sort directions optionally reversed.
|
|
"""
|
|
if page_reverse:
|
|
return ['desc' if s[1] else 'asc' for s in sorts]
|
|
return ['asc' if s[1] else 'desc' for s in sorts]
|
|
|
|
|
|
def _is_nested_instance(exception, etypes):
|
|
"""Check if exception or its inner excepts are an instance of etypes."""
|
|
return (isinstance(exception, etypes) or
|
|
isinstance(exception, n_exc.MultipleExceptions) and
|
|
any(_is_nested_instance(i, etypes)
|
|
for i in exception.inner_exceptions))
|
|
|
|
|
|
def is_retriable(exception):
|
|
"""Determine if the said exception is retriable.
|
|
|
|
:param exception: The exception to check.
|
|
:returns: True if 'exception' is retriable, otherwise False.
|
|
"""
|
|
if _is_nested_instance(exception,
|
|
(db_exc.DBDeadlock, exc.StaleDataError,
|
|
db_exc.DBConnectionError,
|
|
db_exc.DBDuplicateEntry, db_exc.RetryRequest)):
|
|
return True
|
|
# Look for savepoints mangled by deadlocks. See bug/1590298 for details.
|
|
return (_is_nested_instance(exception, db_exc.DBError) and
|
|
'1305' in str(exception))
|
|
|
|
|
|
def reraise_as_retryrequest(function):
|
|
"""Wrap the said function with a RetryRequest upon error.
|
|
|
|
:param function: The function to wrap/decorate.
|
|
:returns: The 'function' wrapped in a try block that will reraise any
|
|
Exception's as a RetryRequest.
|
|
:raises RetryRequest: If the wrapped function raises retriable exception.
|
|
"""
|
|
@six.wraps(function)
|
|
def _wrapped(*args, **kwargs):
|
|
try:
|
|
return function(*args, **kwargs)
|
|
except Exception as e:
|
|
with excutils.save_and_reraise_exception() as ctx:
|
|
if is_retriable(e):
|
|
ctx.reraise = False
|
|
raise db_exc.RetryRequest(e)
|
|
return _wrapped
|