253f0ce48c
Currently when we create a Bay, magnum is not enough to prevent bay creation failure, due to Invalid discovery url can be referenced and there're no check toward it. We should check the discovery url before creating bay, a 400 Invalid will be raised if the discovery url is invalid: 1.the discovery url should be a correct url of etcd cluster. 2.the discovery url should have suitable cluster size. Change-Id: Ib75f9f2aade18b16dd46035efe139311faf93e1e
445 lines
12 KiB
Python
445 lines
12 KiB
Python
# 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 json
|
|
import sys
|
|
|
|
from keystoneclient import exceptions as keystone_exceptions
|
|
from oslo_config import cfg
|
|
from oslo_log import log as logging
|
|
import six
|
|
|
|
from magnum.i18n import _
|
|
from magnum.i18n import _LE
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
CONF = cfg.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 as e:
|
|
# kwargs doesn't match a variable in the message
|
|
# log the issue and the kwargs
|
|
LOG.exception(_LE('Exception in string format operation, '
|
|
'kwargs: %s') % kwargs)
|
|
try:
|
|
if CONF.fatal_exception_format_errors:
|
|
raise e
|
|
except cfg.NoSuchOptError:
|
|
# Note: work around for Bug: #1447873
|
|
if CONF.oslo_versionedobjects.fatal_exception_format_errors:
|
|
raise e
|
|
|
|
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.")
|
|
|
|
|
|
class ResourceNotFound(ObjectNotFound):
|
|
message = _("The %(name)s resource %(id)s could not be found.")
|
|
code = 404
|
|
|
|
|
|
class AuthorizationFailure(MagnumException):
|
|
message = _("%(client)s connection failed. %(message)s")
|
|
|
|
|
|
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 InvalidBayDiscoveryURL(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 HTTPNotFound(ResourceNotFound):
|
|
pass
|
|
|
|
|
|
class Conflict(MagnumException):
|
|
message = _('Conflict.')
|
|
code = 409
|
|
|
|
|
|
# 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(MagnumException):
|
|
message = _("Invalid configuration file. %(error_msg)s")
|
|
|
|
|
|
class SSHConnectFailed(MagnumException):
|
|
message = _("Failed to establish SSH connection to host %(host)s.")
|
|
|
|
|
|
class FileSystemNotSupported(MagnumException):
|
|
message = _("Failed to create a file system. "
|
|
"File system %(fs)s is not supported.")
|
|
|
|
|
|
class BayModelNotFound(ResourceNotFound):
|
|
message = _("Baymodel %(baymodel)s could not be found.")
|
|
|
|
|
|
class BayModelAlreadyExists(Conflict):
|
|
message = _("A baymodel with UUID %(uuid)s already exists.")
|
|
|
|
|
|
class BayModelReferenced(Invalid):
|
|
message = _("Baymodel %(baymodel)s is referenced by one or multiple bays.")
|
|
|
|
|
|
class BaymodelPublishDenied(NotAuthorized):
|
|
message = _("Not authorized to set public flag for baymodel.")
|
|
|
|
|
|
class BayNotFound(ResourceNotFound):
|
|
message = _("Bay %(bay)s could not be found.")
|
|
|
|
|
|
class BayAlreadyExists(Conflict):
|
|
message = _("A bay with UUID %(uuid)s already exists.")
|
|
|
|
|
|
class ContainerNotFound(ResourceNotFound):
|
|
message = _("Container %(container)s could not be found.")
|
|
|
|
|
|
class ContainerAlreadyExists(Conflict):
|
|
message = _("A container with UUID %(uuid)s already exists.")
|
|
|
|
|
|
class PodNotFound(ResourceNotFound):
|
|
message = _("Pod %(pod)s could not be found.")
|
|
|
|
|
|
class PodAlreadyExists(Conflict):
|
|
message = _("A pod with UUID %(uuid)s already exists.")
|
|
|
|
|
|
class PodListNotFound(ResourceNotFound):
|
|
message = _("Pod list could not be found for Bay %(bay_uuid)s.")
|
|
|
|
|
|
class PodCreationFailed(Invalid):
|
|
message = _("Pod creation failed in Bay %(bay_uuid)s.")
|
|
|
|
|
|
class ReplicationControllerNotFound(ResourceNotFound):
|
|
message = _("ReplicationController %(rc)s could not be found.")
|
|
|
|
|
|
class ReplicationControllerAlreadyExists(Conflict):
|
|
message = _("A ReplicationController with UUID %(uuid)s already exists.")
|
|
|
|
|
|
class ReplicationControllerListNotFound(ResourceNotFound):
|
|
message = _("ReplicationController list could not be found"
|
|
" for Bay %(bay_uuid)s.")
|
|
|
|
|
|
class ReplicationControllerCreationFailed(Invalid):
|
|
message = _("ReplicationController creation failed"
|
|
" for Bay %(bay_uuid)s.")
|
|
|
|
|
|
class ServiceNotFound(ResourceNotFound):
|
|
message = _("Service %(service)s could not be found.")
|
|
|
|
|
|
class ServiceAlreadyExists(Conflict):
|
|
message = _("A service with UUID %(uuid)s already exists.")
|
|
|
|
|
|
class ServiceListNotFound(ResourceNotFound):
|
|
message = _("Service list could not be found for Bay %(bay_uuid)s.")
|
|
|
|
|
|
class ServiceCreationFailed(Invalid):
|
|
message = _("Service creation failed for Bay %(bay_uuid)s.")
|
|
|
|
|
|
class ContainerException(Exception):
|
|
pass
|
|
|
|
|
|
class NotSupported(MagnumException):
|
|
message = _("%(operation)s is not supported.")
|
|
code = 400
|
|
|
|
|
|
class BayTypeNotSupported(MagnumException):
|
|
message = _("Bay type (%(server_type)s, %(os)s, %(coe)s)"
|
|
" not supported.")
|
|
|
|
|
|
class BayTypeNotEnabled(MagnumException):
|
|
message = _("Bay type (%(server_type)s, %(os)s, %(coe)s)"
|
|
" not enabled.")
|
|
|
|
|
|
class RequiredParameterNotProvided(MagnumException):
|
|
message = _("Required parameter %(heat_param)s not provided.")
|
|
|
|
|
|
class Urllib2InvalidScheme(MagnumException):
|
|
message = _("The urllib2 URL %(url)s has an invalid scheme.")
|
|
|
|
|
|
class OperationInProgress(Invalid):
|
|
message = _("Bay %(bay_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(MagnumException):
|
|
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 KubernetesAPIFailed(MagnumException):
|
|
def __init__(self, message=None, err=None, **kwargs):
|
|
if err:
|
|
if err.body:
|
|
message = json.loads(err.body)['message']
|
|
else:
|
|
message = err.reason
|
|
self.__class__.code = err.status
|
|
else:
|
|
self.__class__.code = kwargs.get('code')
|
|
super(KubernetesAPIFailed, self).__init__(message, **kwargs)
|
|
|
|
|
|
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(MagnumException):
|
|
message = _("Unsupported quantity format for k8s bay.")
|
|
|
|
|
|
class UnsupportedDockerQuantityFormat(MagnumException):
|
|
message = _("Unsupported quantity format for Swarm bay.")
|
|
|
|
|
|
class FlavorNotFound(ResourceNotFound):
|
|
"""The code here changed to 400 according to the latest document."""
|
|
message = _("Unable to find flavor %(flavor)s.")
|
|
code = 400
|
|
|
|
|
|
class NetworkNotFound(ResourceNotFound):
|
|
"""The code here changed to 400 according to the latest document."""
|
|
message = _("Unable to find 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 RegionsListFailed(MagnumException):
|
|
message = _("Failed to list regions.")
|
|
|
|
|
|
class TrusteeOrTrustToBayFailed(MagnumException):
|
|
message = _("Failed to create trustee or trust for Bay: %(bay_uuid)s")
|
|
|
|
|
|
class CertificatesToBayFailed(MagnumException):
|
|
message = _("Failed to create certificates for Bay: %(bay_uuid)s")
|