394 lines
9.2 KiB
Python
394 lines
9.2 KiB
Python
|
|
# -*- coding: utf-8 -*-
|
|||
|
|
|
|||
|
|
"""
|
|||
|
|
oss2.exceptions
|
|||
|
|
~~~~~~~~~~~~~~
|
|||
|
|
|
|||
|
|
异常类。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import re
|
|||
|
|
import base64
|
|||
|
|
import xml.etree.ElementTree as ElementTree
|
|||
|
|
from xml.parsers import expat
|
|||
|
|
|
|||
|
|
|
|||
|
|
from .compat import to_string
|
|||
|
|
from .headers import *
|
|||
|
|
|
|||
|
|
_OSS_ERROR_TO_EXCEPTION = {} # populated at end of module
|
|||
|
|
|
|||
|
|
|
|||
|
|
OSS_CLIENT_ERROR_STATUS = -1
|
|||
|
|
OSS_REQUEST_ERROR_STATUS = -2
|
|||
|
|
OSS_INCONSISTENT_ERROR_STATUS = -3
|
|||
|
|
OSS_FORMAT_ERROR_STATUS = -4
|
|||
|
|
OSS_SELECT_CLIENT_ERROR_STATUS = -5
|
|||
|
|
|
|||
|
|
|
|||
|
|
class OssError(Exception):
|
|||
|
|
def __init__(self, status, headers, body, details):
|
|||
|
|
#: HTTP 状态码
|
|||
|
|
self.status = status
|
|||
|
|
|
|||
|
|
#: 请求ID,用于跟踪一个OSS请求。提交工单时,最好能够提供请求ID
|
|||
|
|
self.request_id = headers.get(OSS_REQUEST_ID, '')
|
|||
|
|
|
|||
|
|
#: HTTP响应体(部分)
|
|||
|
|
self.body = body
|
|||
|
|
|
|||
|
|
#: 详细错误信息,是一个string到string的dict
|
|||
|
|
self.details = details
|
|||
|
|
|
|||
|
|
#: OSS错误码
|
|||
|
|
self.code = self.details.get('Code', '')
|
|||
|
|
|
|||
|
|
#: OSS错误信息
|
|||
|
|
self.message = self.details.get('Message', '')
|
|||
|
|
|
|||
|
|
#: OSS新的错误码
|
|||
|
|
self.ec = self.details.get('EC', '')
|
|||
|
|
|
|||
|
|
#: header信息
|
|||
|
|
self.headers = headers
|
|||
|
|
|
|||
|
|
def __str__(self):
|
|||
|
|
error = {'status': self.status,
|
|||
|
|
OSS_REQUEST_ID : self.request_id,
|
|||
|
|
'details': self.details}
|
|||
|
|
return str(error)
|
|||
|
|
|
|||
|
|
def _str_with_body(self):
|
|||
|
|
error = {'status': self.status,
|
|||
|
|
OSS_REQUEST_ID : self.request_id,
|
|||
|
|
'details': self.body}
|
|||
|
|
return str(error)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ClientError(OssError):
|
|||
|
|
def __init__(self, message):
|
|||
|
|
OssError.__init__(self, OSS_CLIENT_ERROR_STATUS, {}, 'ClientError: ' + message, {})
|
|||
|
|
|
|||
|
|
def __str__(self):
|
|||
|
|
return self._str_with_body()
|
|||
|
|
|
|||
|
|
|
|||
|
|
class RequestError(OssError):
|
|||
|
|
def __init__(self, e):
|
|||
|
|
OssError.__init__(self, OSS_REQUEST_ERROR_STATUS, {}, 'RequestError: ' + str(e), {})
|
|||
|
|
self.exception = e
|
|||
|
|
|
|||
|
|
def __str__(self):
|
|||
|
|
return self._str_with_body()
|
|||
|
|
|
|||
|
|
|
|||
|
|
class InconsistentError(OssError):
|
|||
|
|
def __init__(self, message, request_id=''):
|
|||
|
|
OssError.__init__(self, OSS_INCONSISTENT_ERROR_STATUS, {OSS_REQUEST_ID : request_id}, 'InconsistentError: ' + message, {})
|
|||
|
|
|
|||
|
|
def __str__(self):
|
|||
|
|
return self._str_with_body()
|
|||
|
|
|
|||
|
|
|
|||
|
|
class OpenApiFormatError(OssError):
|
|||
|
|
def __init__(self, message):
|
|||
|
|
OssError.__init__(self, OSS_FORMAT_ERROR_STATUS, {}, message, {})
|
|||
|
|
|
|||
|
|
def __str__(self):
|
|||
|
|
return self._str_with_body()
|
|||
|
|
|
|||
|
|
|
|||
|
|
class OpenApiServerError(OssError):
|
|||
|
|
def __init__(self, status, request_id, message, error_code):
|
|||
|
|
OssError.__init__(self, status, {OSS_REQUEST_ID : request_id}, '', {'Code': error_code, 'Message': message})
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ServerError(OssError):
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
|
|||
|
|
class NotFound(ServerError):
|
|||
|
|
status = 404
|
|||
|
|
code = ''
|
|||
|
|
|
|||
|
|
|
|||
|
|
class MalformedXml(ServerError):
|
|||
|
|
status = 400
|
|||
|
|
code = 'MalformedXML'
|
|||
|
|
|
|||
|
|
|
|||
|
|
class InvalidRequest(ServerError):
|
|||
|
|
status = 400
|
|||
|
|
code = 'InvalidRequest'
|
|||
|
|
|
|||
|
|
|
|||
|
|
class OperationNotSupported(ServerError):
|
|||
|
|
status = 400
|
|||
|
|
code = 'OperationNotSupported'
|
|||
|
|
|
|||
|
|
|
|||
|
|
class RestoreAlreadyInProgress(ServerError):
|
|||
|
|
status = 409
|
|||
|
|
code = 'RestoreAlreadyInProgress'
|
|||
|
|
|
|||
|
|
|
|||
|
|
class InvalidArgument(ServerError):
|
|||
|
|
status = 400
|
|||
|
|
code = 'InvalidArgument'
|
|||
|
|
|
|||
|
|
def __init__(self, status, headers, body, details):
|
|||
|
|
super(InvalidArgument, self).__init__(status, headers, body, details)
|
|||
|
|
self.name = details.get('ArgumentName')
|
|||
|
|
self.value = details.get('ArgumentValue')
|
|||
|
|
|
|||
|
|
|
|||
|
|
class InvalidDigest(ServerError):
|
|||
|
|
status = 400
|
|||
|
|
code = 'InvalidDigest'
|
|||
|
|
|
|||
|
|
|
|||
|
|
class InvalidObjectName(ServerError):
|
|||
|
|
status = 400
|
|||
|
|
code = 'InvalidObjectName'
|
|||
|
|
|
|||
|
|
|
|||
|
|
class NotImplemented(ServerError):
|
|||
|
|
status = 400
|
|||
|
|
code = 'NotImplemented'
|
|||
|
|
|
|||
|
|
|
|||
|
|
class InvalidEncryptionRequest(ServerError):
|
|||
|
|
status = 400
|
|||
|
|
code = 'InvalidEncryptionRequest'
|
|||
|
|
|
|||
|
|
class BucketReplicationAlreadyExist(ServerError):
|
|||
|
|
status = 400
|
|||
|
|
code = 'BucketReplicationAlreadyExist'
|
|||
|
|
|
|||
|
|
class NoSuchBucket(NotFound):
|
|||
|
|
status = 404
|
|||
|
|
code = 'NoSuchBucket'
|
|||
|
|
|
|||
|
|
|
|||
|
|
class NoSuchKey(NotFound):
|
|||
|
|
status = 404
|
|||
|
|
code = 'NoSuchKey'
|
|||
|
|
|
|||
|
|
|
|||
|
|
class NoSuchUpload(NotFound):
|
|||
|
|
status = 404
|
|||
|
|
code = 'NoSuchUpload'
|
|||
|
|
|
|||
|
|
|
|||
|
|
class NoSuchWebsite(NotFound):
|
|||
|
|
status = 404
|
|||
|
|
code = 'NoSuchWebsiteConfiguration'
|
|||
|
|
|
|||
|
|
|
|||
|
|
class NoSuchLifecycle(NotFound):
|
|||
|
|
status = 404
|
|||
|
|
code = 'NoSuchLifecycle'
|
|||
|
|
|
|||
|
|
|
|||
|
|
class NoSuchCors(NotFound):
|
|||
|
|
status = 404
|
|||
|
|
code = 'NoSuchCORSConfiguration'
|
|||
|
|
|
|||
|
|
|
|||
|
|
class NoSuchLiveChannel(NotFound):
|
|||
|
|
status = 404
|
|||
|
|
code = 'NoSuchLiveChannel'
|
|||
|
|
|
|||
|
|
|
|||
|
|
class NoSuchBucketPolicy(NotFound):
|
|||
|
|
status = 404
|
|||
|
|
code = 'NoSuchBucketPolicy'
|
|||
|
|
|
|||
|
|
class NoSuchInventory(NotFound):
|
|||
|
|
status = 404
|
|||
|
|
code = 'NoSuchInventory'
|
|||
|
|
|
|||
|
|
class NoSuchReplicationRule(NotFound):
|
|||
|
|
status = 404
|
|||
|
|
code = 'NoSuchReplicationRule'
|
|||
|
|
|
|||
|
|
class Conflict(ServerError):
|
|||
|
|
status = 409
|
|||
|
|
code = ''
|
|||
|
|
|
|||
|
|
|
|||
|
|
class BucketNotEmpty(Conflict):
|
|||
|
|
status = 409
|
|||
|
|
code = 'BucketNotEmpty'
|
|||
|
|
|
|||
|
|
|
|||
|
|
class PositionNotEqualToLength(Conflict):
|
|||
|
|
status = 409
|
|||
|
|
code = 'PositionNotEqualToLength'
|
|||
|
|
|
|||
|
|
def __init__(self, status, headers, body, details):
|
|||
|
|
super(PositionNotEqualToLength, self).__init__(status, headers, body, details)
|
|||
|
|
self.next_position = int(headers[OSS_NEXT_APPEND_POSITION])
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ObjectNotAppendable(Conflict):
|
|||
|
|
status = 409
|
|||
|
|
code = 'ObjectNotAppendable'
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ChannelStillLive(Conflict):
|
|||
|
|
status = 409
|
|||
|
|
code = 'ChannelStillLive'
|
|||
|
|
|
|||
|
|
|
|||
|
|
class LiveChannelDisabled(Conflict):
|
|||
|
|
status = 409
|
|||
|
|
code = 'LiveChannelDisabled'
|
|||
|
|
|
|||
|
|
|
|||
|
|
class PreconditionFailed(ServerError):
|
|||
|
|
status = 412
|
|||
|
|
code = 'PreconditionFailed'
|
|||
|
|
|
|||
|
|
|
|||
|
|
class NotModified(ServerError):
|
|||
|
|
status = 304
|
|||
|
|
code = ''
|
|||
|
|
|
|||
|
|
|
|||
|
|
class AccessDenied(ServerError):
|
|||
|
|
status = 403
|
|||
|
|
code = 'AccessDenied'
|
|||
|
|
|
|||
|
|
class NoSuchServerSideEncryptionRule(NotFound):
|
|||
|
|
status = 404
|
|||
|
|
code = 'NoSuchServerSideEncryptionRule'
|
|||
|
|
|
|||
|
|
class InvalidEncryptionAlgorithmError(ServerError):
|
|||
|
|
status = 400
|
|||
|
|
code = 'InvalidEncryptionAlgorithmError'
|
|||
|
|
|
|||
|
|
class SelectOperationFailed(ServerError):
|
|||
|
|
code = 'SelectOperationFailed'
|
|||
|
|
def __init__(self, status, code, message):
|
|||
|
|
self.status = status
|
|||
|
|
self.code = code
|
|||
|
|
self.message = message
|
|||
|
|
|
|||
|
|
def __str__(self):
|
|||
|
|
error = {'status': self.status,
|
|||
|
|
'code': self.code,
|
|||
|
|
'details': self.message}
|
|||
|
|
return str(error)
|
|||
|
|
|
|||
|
|
class SelectOperationClientError(OssError):
|
|||
|
|
def __init__(self, message, request_id):
|
|||
|
|
OssError.__init__(self, OSS_SELECT_CLIENT_ERROR_STATUS, {'x-oss-request-id': request_id}, 'SelectOperationClientError: ' + message, {})
|
|||
|
|
|
|||
|
|
def __str__(self):
|
|||
|
|
error = {'x-oss-request-id':self.request_id,
|
|||
|
|
'message': self.message}
|
|||
|
|
return str(error)
|
|||
|
|
|
|||
|
|
class SignatureDoesNotMatch(ServerError):
|
|||
|
|
status = 403
|
|||
|
|
code = 'SignatureDoesNotMatch'
|
|||
|
|
|
|||
|
|
class ObjectAlreadyExists(ServerError):
|
|||
|
|
status = 400
|
|||
|
|
code = 'ObjectAlreadyExists'
|
|||
|
|
|
|||
|
|
class PartNotSequential(ServerError):
|
|||
|
|
status = 400
|
|||
|
|
code = 'PartNotSequential'
|
|||
|
|
|
|||
|
|
class NoSuchWORMConfiguration(ServerError):
|
|||
|
|
status = 404
|
|||
|
|
code = 'NoSuchWORMConfiguration'
|
|||
|
|
|
|||
|
|
class WORMConfigurationLocked(ServerError):
|
|||
|
|
status = 403
|
|||
|
|
code = 'WORMConfigurationLocked'
|
|||
|
|
|
|||
|
|
class InvalidWORMConfiguration(ServerError):
|
|||
|
|
status = 400
|
|||
|
|
code = 'InvalidWORMConfiguration'
|
|||
|
|
|
|||
|
|
class NoSuchTransferAccelerationConfiguration(ServerError):
|
|||
|
|
status = 404
|
|||
|
|
code = 'NoSuchTransferAccelerationConfiguration'
|
|||
|
|
|
|||
|
|
def make_exception(resp):
|
|||
|
|
status = resp.status
|
|||
|
|
headers = resp.headers
|
|||
|
|
body = resp.read(4096)
|
|||
|
|
if not body and headers.get('x-oss-err') is not None:
|
|||
|
|
try:
|
|||
|
|
value = base64.b64decode(to_string(headers.get('x-oss-err')))
|
|||
|
|
except:
|
|||
|
|
value = body
|
|||
|
|
details = _parse_error_body(value)
|
|||
|
|
else:
|
|||
|
|
details = _parse_error_body(body)
|
|||
|
|
code = details.get('Code', '')
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
klass = _OSS_ERROR_TO_EXCEPTION[(status, code)]
|
|||
|
|
return klass(status, headers, body, details)
|
|||
|
|
except KeyError:
|
|||
|
|
return ServerError(status, headers, body, details)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _walk_subclasses(klass):
|
|||
|
|
for sub in klass.__subclasses__():
|
|||
|
|
yield sub
|
|||
|
|
for subsub in _walk_subclasses(sub):
|
|||
|
|
yield subsub
|
|||
|
|
|
|||
|
|
|
|||
|
|
for klass in _walk_subclasses(ServerError):
|
|||
|
|
status = getattr(klass, 'status', None)
|
|||
|
|
code = getattr(klass, 'code', None)
|
|||
|
|
|
|||
|
|
if status is not None and code is not None:
|
|||
|
|
_OSS_ERROR_TO_EXCEPTION[(status, code)] = klass
|
|||
|
|
|
|||
|
|
|
|||
|
|
# XML parsing exceptions have changed in Python2.7 and ElementTree 1.3
|
|||
|
|
if hasattr(ElementTree, 'ParseError'):
|
|||
|
|
ElementTreeParseError = (ElementTree.ParseError, expat.ExpatError)
|
|||
|
|
else:
|
|||
|
|
ElementTreeParseError = (expat.ExpatError)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _parse_error_body(body):
|
|||
|
|
try:
|
|||
|
|
root = ElementTree.fromstring(body)
|
|||
|
|
if root.tag != 'Error':
|
|||
|
|
return {}
|
|||
|
|
|
|||
|
|
details = {}
|
|||
|
|
for child in root:
|
|||
|
|
details[child.tag] = child.text
|
|||
|
|
return details
|
|||
|
|
except ElementTreeParseError:
|
|||
|
|
return _guess_error_details(body)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _guess_error_details(body):
|
|||
|
|
details = {}
|
|||
|
|
body = to_string(body)
|
|||
|
|
|
|||
|
|
if '<Error>' not in body or '</Error>' not in body:
|
|||
|
|
return details
|
|||
|
|
|
|||
|
|
m = re.search('<Code>(.*)</Code>', body)
|
|||
|
|
if m:
|
|||
|
|
details['Code'] = m.group(1)
|
|||
|
|
|
|||
|
|
m = re.search('<Message>(.*)</Message>', body)
|
|||
|
|
if m:
|
|||
|
|
details['Message'] = m.group(1)
|
|||
|
|
|
|||
|
|
return details
|