magnum/magnum/common/exception.py
coldmoment ba8ad5e37f Add a hacking rule for string interpolation at logging
String interpolation should be delayed to be handled
by the logging code, rather than being done at the point
of the logging call.
See the oslo i18n guideline
* https://docs.openstack.org/oslo.i18n/latest/user/guidelines.html#adding-variables-to-log-messages
and
* https://github.com/openstack-dev/hacking/blob/master/hacking/checks/other.py#L39

Change-Id: I8a4f5f896865aebbff88ee894f0081e58cfce9ef
2017-07-15 14:49:45 +08:00

377 lines
10 KiB
Python
Executable File

# Copyright 2013 - Red Hat, Inc.
#
# 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.
"""Magnum base exception handling.
Includes decorator for re-raising Magnum-type exceptions.
"""
import functools
import sys
from keystoneclient import exceptions as keystone_exceptions
from oslo_config import cfg
from oslo_log import log as logging
import six
import magnum.conf
from magnum.i18n import _
LOG = logging.getLogger(__name__)
CONF = magnum.conf.CONF
try:
CONF.import_opt('fatal_exception_format_errors',
'oslo_versionedobjects.exception')
except cfg.NoSuchOptError as e:
# Note:work around for magnum run against master branch
# in devstack gate job, as magnum not branched yet
# verisonobjects kilo/master different version can
# cause issue here. As it changed import group. So
# add here before branch to prevent gate failure.
# Bug: #1447873
CONF.import_opt('fatal_exception_format_errors',
'oslo_versionedobjects.exception',
group='oslo_versionedobjects')
def wrap_keystone_exception(func):
"""Wrap keystone exceptions and throw Magnum specific exceptions."""
@functools.wraps(func)
def wrapped(*args, **kw):
try:
return func(*args, **kw)
except keystone_exceptions.AuthorizationFailure:
raise AuthorizationFailure(
client=func.__name__, message="reason: %s" % sys.exc_info()[1])
except keystone_exceptions.ClientException:
raise AuthorizationFailure(
client=func.__name__,
message="unexpected keystone client error occurred: %s"
% sys.exc_info()[1])
return wrapped
class MagnumException(Exception):
"""Base Magnum Exception
To correctly use this class, inherit from it and define
a 'message' property. That message will get printf'd
with the keyword arguments provided to the constructor.
"""
message = _("An unknown exception occurred.")
code = 500
def __init__(self, message=None, **kwargs):
self.kwargs = kwargs
if 'code' not in self.kwargs and hasattr(self, 'code'):
self.kwargs['code'] = self.code
if message:
self.message = message
try:
self.message = self.message % kwargs
except Exception:
# kwargs doesn't match a variable in the message
# log the issue and the kwargs
LOG.exception('Exception in string format operation, '
'kwargs: %s', kwargs)
try:
if CONF.fatal_exception_format_errors:
raise
except cfg.NoSuchOptError:
# Note: work around for Bug: #1447873
if CONF.oslo_versionedobjects.fatal_exception_format_errors:
raise
super(MagnumException, self).__init__(self.message)
def __str__(self):
if six.PY3:
return self.message
return self.message.encode('utf-8')
def __unicode__(self):
return self.message
def format_message(self):
if self.__class__.__name__.endswith('_Remote'):
return self.args[0]
else:
return six.text_type(self)
class ObjectNotFound(MagnumException):
message = _("The %(name)s %(id)s could not be found.")
code = 404
class ProjectNotFound(ObjectNotFound):
message = _("The %(name)s %(id)s could not be found.")
class ResourceNotFound(ObjectNotFound):
message = _("The %(name)s resource %(id)s could not be found.")
class AuthorizationFailure(MagnumException):
message = _("%(client)s connection failed. %(message)s")
code = 403
class Invalid(MagnumException):
message = _("Unacceptable parameters.")
code = 400
class InvalidUUID(Invalid):
message = _("Expected a uuid but received %(uuid)s.")
class InvalidName(Invalid):
message = _("Expected a name but received %(name)s.")
class InvalidDiscoveryURL(Invalid):
message = _("Received invalid discovery URL '%(discovery_url)s' for "
"discovery endpoint '%(discovery_endpoint)s'.")
class GetDiscoveryUrlFailed(MagnumException):
message = _("Failed to get discovery url from '%(discovery_endpoint)s'.")
class InvalidClusterDiscoveryURL(Invalid):
message = _("Invalid discovery URL '%(discovery_url)s'.")
class InvalidClusterSize(Invalid):
message = _("Expected cluster size %(expect_size)d but get cluster "
"size %(size)d from '%(discovery_url)s'.")
class GetClusterSizeFailed(MagnumException):
message = _("Failed to get the size of cluster from '%(discovery_url)s'.")
class InvalidIdentity(Invalid):
message = _("Expected an uuid or int but received %(identity)s.")
class InvalidCsr(Invalid):
message = _("Received invalid csr %(csr)s.")
class InvalidSubnet(Invalid):
message = _("Received invalid subnet %(subnet)s.")
class HTTPNotFound(ResourceNotFound):
pass
class Conflict(MagnumException):
message = _('Conflict.')
code = 409
class ApiVersionsIntersect(Invalid):
message = _("Version of %(name)s %(min_ver)s %(max_ver)s intersects "
"with another versions.")
# Cannot be templated as the error syntax varies.
# msg needs to be constructed when raised.
class InvalidParameterValue(Invalid):
message = _("%(err)s")
class PatchError(Invalid):
message = _("Couldn't apply patch '%(patch)s'. Reason: %(reason)s")
class NotAuthorized(MagnumException):
message = _("Not authorized.")
code = 403
class PolicyNotAuthorized(NotAuthorized):
message = _("Policy doesn't allow %(action)s to be performed.")
class InvalidMAC(Invalid):
message = _("Expected a MAC address but received %(mac)s.")
class ConfigInvalid(Invalid):
message = _("Invalid configuration file. %(error_msg)s")
class ClusterTemplateNotFound(ResourceNotFound):
message = _("ClusterTemplate %(clustertemplate)s could not be found.")
class ClusterTemplateAlreadyExists(Conflict):
message = _("A ClusterTemplate with UUID %(uuid)s already exists.")
class ClusterTemplateReferenced(Invalid):
message = _("ClusterTemplate %(clustertemplate)s is referenced by one or"
" multiple clusters.")
class ClusterTemplatePublishDenied(NotAuthorized):
message = _("Not authorized to set public flag for cluster template.")
class ClusterNotFound(ResourceNotFound):
message = _("Cluster %(cluster)s could not be found.")
class ClusterAlreadyExists(Conflict):
message = _("A cluster with UUID %(uuid)s already exists.")
class NotSupported(MagnumException):
message = _("%(operation)s is not supported.")
code = 400
class ClusterTypeNotSupported(NotSupported):
message = _("Cluster type (%(server_type)s, %(os)s, %(coe)s)"
" not supported.")
class RequiredParameterNotProvided(Invalid):
message = _("Required parameter %(heat_param)s not provided.")
class OperationInProgress(Invalid):
message = _("Cluster %(cluster_name)s already has an operation in "
"progress.")
class ImageNotFound(ResourceNotFound):
"""The code here changed to 400 according to the latest document."""
message = _("Image %(image_id)s could not be found.")
code = 400
class ImageNotAuthorized(NotAuthorized):
message = _("Not authorized for image %(image_id)s.")
class OSDistroFieldNotFound(ResourceNotFound):
"""The code here changed to 400 according to the latest document."""
message = _("Image %(image_id)s doesn't contain os_distro field.")
code = 400
class X509KeyPairNotFound(ResourceNotFound):
message = _("A key pair %(x509keypair)s could not be found.")
class X509KeyPairAlreadyExists(Conflict):
message = _("A key pair with UUID %(uuid)s already exists.")
class CertificateStorageException(MagnumException):
message = _("Could not store certificate: %(msg)s")
class CertificateValidationError(Invalid):
message = _("Extension '%(extension)s' not allowed")
class KeyPairNotFound(ResourceNotFound):
message = _("Unable to find keypair %(keypair)s.")
class MagnumServiceNotFound(ResourceNotFound):
message = _("A magnum service %(magnum_service_id)s could not be found.")
class MagnumServiceAlreadyExists(Conflict):
message = _("A magnum service with ID %(id)s already exists.")
class UnsupportedK8sQuantityFormat(Invalid):
message = _("Unsupported quantity format for k8s cluster.")
class UnsupportedDockerQuantityFormat(Invalid):
message = _("Unsupported quantity format for Swarm cluster.")
class FlavorNotFound(ResourceNotFound):
"""The code here changed to 400 according to the latest document."""
message = _("Unable to find flavor %(flavor)s.")
code = 400
class ExternalNetworkNotFound(ResourceNotFound):
"""The code here changed to 400 according to the latest document."""
""""Ensure the network is not private."""
message = _("Unable to find external network %(network)s.")
code = 400
class TrustCreateFailed(MagnumException):
message = _("Failed to create trust for trustee %(trustee_user_id)s.")
class TrustDeleteFailed(MagnumException):
message = _("Failed to delete trust %(trust_id)s.")
class TrusteeCreateFailed(MagnumException):
message = _("Failed to create trustee %(username)s "
"in domain %(domain_id)s")
class TrusteeDeleteFailed(MagnumException):
message = _("Failed to delete trustee %(trustee_id)s")
class QuotaAlreadyExists(Conflict):
message = _("Quota for project %(project_id)s already exists "
"for resource %(resource)s.")
class QuotaNotFound(ResourceNotFound):
message = _("Quota could not be found: %(msg)s")
class ResourceLimitExceeded(NotAuthorized):
message = _('Resource limit exceeded: %(msg)s')
class RegionsListFailed(MagnumException):
message = _("Failed to list regions.")
class TrusteeOrTrustToClusterFailed(MagnumException):
message = _("Failed to create trustee or trust for Cluster: "
"%(cluster_uuid)s")
class CertificatesToClusterFailed(MagnumException):
message = _("Failed to create certificates for Cluster: %(cluster_uuid)s")