| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585 |
- # Copyright (c) 2006-2010 Mitch Garnaat http://garnaat.org/
- # Copyright (c) 2010, Eucalyptus Systems, Inc.
- # All rights reserved.
- #
- # Permission is hereby granted, free of charge, to any person obtaining a
- # copy of this software and associated documentation files (the
- # "Software"), to deal in the Software without restriction, including
- # without limitation the rights to use, copy, modify, merge, publish, dis-
- # tribute, sublicense, and/or sell copies of the Software, and to permit
- # persons to whom the Software is furnished to do so, subject to the fol-
- # lowing conditions:
- #
- # The above copyright notice and this permission notice shall be included
- # in all copies or substantial portions of the Software.
- #
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
- # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
- # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- # IN THE SOFTWARE.
- """
- Exception classes - Subclassing allows you to check for specific errors
- """
- import base64
- import xml.sax
- import boto
- from boto import handler
- from boto.compat import json, StandardError
- from boto.resultset import ResultSet
- class BotoClientError(StandardError):
- """
- General Boto Client error (error accessing AWS)
- """
- def __init__(self, reason, *args):
- super(BotoClientError, self).__init__(reason, *args)
- self.reason = reason
- def __repr__(self):
- return 'BotoClientError: %s' % self.reason
- def __str__(self):
- return 'BotoClientError: %s' % self.reason
- class SDBPersistenceError(StandardError):
- pass
- class StoragePermissionsError(BotoClientError):
- """
- Permissions error when accessing a bucket or key on a storage service.
- """
- pass
- class S3PermissionsError(StoragePermissionsError):
- """
- Permissions error when accessing a bucket or key on S3.
- """
- pass
- class GSPermissionsError(StoragePermissionsError):
- """
- Permissions error when accessing a bucket or key on GS.
- """
- pass
- class BotoServerError(StandardError):
- def __init__(self, status, reason, body=None, *args):
- super(BotoServerError, self).__init__(status, reason, body, *args)
- self.status = status
- self.reason = reason
- self.body = body or ''
- self.request_id = None
- self.error_code = None
- self._error_message = None
- self.message = ''
- self.box_usage = None
- if isinstance(self.body, bytes):
- try:
- self.body = self.body.decode('utf-8')
- except UnicodeDecodeError:
- boto.log.debug('Unable to decode body from bytes!')
- # Attempt to parse the error response. If body isn't present,
- # then just ignore the error response.
- if self.body:
- # Check if it looks like a ``dict``.
- if hasattr(self.body, 'items'):
- # It's not a string, so trying to parse it will fail.
- # But since it's data, we can work with that.
- self.request_id = self.body.get('RequestId', None)
- if 'Error' in self.body:
- # XML-style
- error = self.body.get('Error', {})
- self.error_code = error.get('Code', None)
- self.message = error.get('Message', None)
- else:
- # JSON-style.
- self.message = self.body.get('message', None)
- else:
- try:
- h = handler.XmlHandlerWrapper(self, self)
- h.parseString(self.body)
- except (TypeError, xml.sax.SAXParseException):
- # What if it's JSON? Let's try that.
- try:
- parsed = json.loads(self.body)
- if 'RequestId' in parsed:
- self.request_id = parsed['RequestId']
- if 'Error' in parsed:
- if 'Code' in parsed['Error']:
- self.error_code = parsed['Error']['Code']
- if 'Message' in parsed['Error']:
- self.message = parsed['Error']['Message']
- except (TypeError, ValueError):
- # Remove unparsable message body so we don't include garbage
- # in exception. But first, save self.body in self.error_message
- # because occasionally we get error messages from Eucalyptus
- # that are just text strings that we want to preserve.
- self.message = self.body
- self.body = None
- def __getattr__(self, name):
- if name == 'error_message':
- return self.message
- if name == 'code':
- return self.error_code
- raise AttributeError
- def __setattr__(self, name, value):
- if name == 'error_message':
- self.message = value
- else:
- super(BotoServerError, self).__setattr__(name, value)
- def __repr__(self):
- return '%s: %s %s\n%s' % (self.__class__.__name__,
- self.status, self.reason, self.body)
- def __str__(self):
- return '%s: %s %s\n%s' % (self.__class__.__name__,
- self.status, self.reason, self.body)
- def startElement(self, name, attrs, connection):
- pass
- def endElement(self, name, value, connection):
- if name in ('RequestId', 'RequestID'):
- self.request_id = value
- elif name == 'Code':
- self.error_code = value
- elif name == 'Message':
- self.message = value
- elif name == 'BoxUsage':
- self.box_usage = value
- return None
- def _cleanupParsedProperties(self):
- self.request_id = None
- self.error_code = None
- self.message = None
- self.box_usage = None
- class ConsoleOutput(object):
- def __init__(self, parent=None):
- self.parent = parent
- self.instance_id = None
- self.timestamp = None
- self.comment = None
- self.output = None
- def startElement(self, name, attrs, connection):
- return None
- def endElement(self, name, value, connection):
- if name == 'instanceId':
- self.instance_id = value
- elif name == 'output':
- self.output = base64.b64decode(value)
- else:
- setattr(self, name, value)
- class StorageCreateError(BotoServerError):
- """
- Error creating a bucket or key on a storage service.
- """
- def __init__(self, status, reason, body=None):
- self.bucket = None
- super(StorageCreateError, self).__init__(status, reason, body)
- def endElement(self, name, value, connection):
- if name == 'BucketName':
- self.bucket = value
- else:
- return super(StorageCreateError, self).endElement(name, value, connection)
- class S3CreateError(StorageCreateError):
- """
- Error creating a bucket or key on S3.
- """
- pass
- class GSCreateError(StorageCreateError):
- """
- Error creating a bucket or key on GS.
- """
- pass
- class StorageCopyError(BotoServerError):
- """
- Error copying a key on a storage service.
- """
- pass
- class S3CopyError(StorageCopyError):
- """
- Error copying a key on S3.
- """
- pass
- class GSCopyError(StorageCopyError):
- """
- Error copying a key on GS.
- """
- pass
- class SQSError(BotoServerError):
- """
- General Error on Simple Queue Service.
- """
- def __init__(self, status, reason, body=None):
- self.detail = None
- self.type = None
- super(SQSError, self).__init__(status, reason, body)
- def startElement(self, name, attrs, connection):
- return super(SQSError, self).startElement(name, attrs, connection)
- def endElement(self, name, value, connection):
- if name == 'Detail':
- self.detail = value
- elif name == 'Type':
- self.type = value
- else:
- return super(SQSError, self).endElement(name, value, connection)
- def _cleanupParsedProperties(self):
- super(SQSError, self)._cleanupParsedProperties()
- for p in ('detail', 'type'):
- setattr(self, p, None)
- class SQSDecodeError(BotoClientError):
- """
- Error when decoding an SQS message.
- """
- def __init__(self, reason, message):
- super(SQSDecodeError, self).__init__(reason, message)
- self.message = message
- def __repr__(self):
- return 'SQSDecodeError: %s' % self.reason
- def __str__(self):
- return 'SQSDecodeError: %s' % self.reason
- class StorageResponseError(BotoServerError):
- """
- Error in response from a storage service.
- """
- def __init__(self, status, reason, body=None):
- self.resource = None
- super(StorageResponseError, self).__init__(status, reason, body)
- def startElement(self, name, attrs, connection):
- return super(StorageResponseError, self).startElement(
- name, attrs, connection)
- def endElement(self, name, value, connection):
- if name == 'Resource':
- self.resource = value
- else:
- return super(StorageResponseError, self).endElement(
- name, value, connection)
- def _cleanupParsedProperties(self):
- super(StorageResponseError, self)._cleanupParsedProperties()
- for p in ('resource'):
- setattr(self, p, None)
- class S3ResponseError(StorageResponseError):
- """
- Error in response from S3.
- """
- pass
- class GSResponseError(StorageResponseError):
- """
- Error in response from GS.
- """
- pass
- class EC2ResponseError(BotoServerError):
- """
- Error in response from EC2.
- """
- def __init__(self, status, reason, body=None):
- self.errors = None
- self._errorResultSet = []
- super(EC2ResponseError, self).__init__(status, reason, body)
- self.errors = [
- (e.error_code, e.error_message) for e in self._errorResultSet]
- if len(self.errors):
- self.error_code, self.error_message = self.errors[0]
- def startElement(self, name, attrs, connection):
- if name == 'Errors':
- self._errorResultSet = ResultSet([('Error', _EC2Error)])
- return self._errorResultSet
- else:
- return None
- def endElement(self, name, value, connection):
- if name == 'RequestID':
- self.request_id = value
- else:
- return None # don't call subclass here
- def _cleanupParsedProperties(self):
- super(EC2ResponseError, self)._cleanupParsedProperties()
- self._errorResultSet = []
- for p in ('errors'):
- setattr(self, p, None)
- class JSONResponseError(BotoServerError):
- """
- This exception expects the fully parsed and decoded JSON response
- body to be passed as the body parameter.
- :ivar status: The HTTP status code.
- :ivar reason: The HTTP reason message.
- :ivar body: The Python dict that represents the decoded JSON
- response body.
- :ivar error_message: The full description of the AWS error encountered.
- :ivar error_code: A short string that identifies the AWS error
- (e.g. ConditionalCheckFailedException)
- """
- def __init__(self, status, reason, body=None, *args):
- self.status = status
- self.reason = reason
- self.body = body
- if self.body:
- self.error_message = self.body.get('message', None)
- self.error_code = self.body.get('__type', None)
- if self.error_code:
- self.error_code = self.error_code.split('#')[-1]
- class DynamoDBResponseError(JSONResponseError):
- pass
- class SWFResponseError(JSONResponseError):
- pass
- class EmrResponseError(BotoServerError):
- """
- Error in response from EMR
- """
- pass
- class _EC2Error(object):
- def __init__(self, connection=None):
- self.connection = connection
- self.error_code = None
- self.error_message = None
- def startElement(self, name, attrs, connection):
- return None
- def endElement(self, name, value, connection):
- if name == 'Code':
- self.error_code = value
- elif name == 'Message':
- self.error_message = value
- else:
- return None
- class SDBResponseError(BotoServerError):
- """
- Error in responses from SDB.
- """
- pass
- class AWSConnectionError(BotoClientError):
- """
- General error connecting to Amazon Web Services.
- """
- pass
- class StorageDataError(BotoClientError):
- """
- Error receiving data from a storage service.
- """
- pass
- class S3DataError(StorageDataError):
- """
- Error receiving data from S3.
- """
- pass
- class GSDataError(StorageDataError):
- """
- Error receiving data from GS.
- """
- pass
- class InvalidUriError(Exception):
- """Exception raised when URI is invalid."""
- def __init__(self, message):
- super(InvalidUriError, self).__init__(message)
- self.message = message
- class InvalidAclError(Exception):
- """Exception raised when ACL XML is invalid."""
- def __init__(self, message):
- super(InvalidAclError, self).__init__(message)
- self.message = message
- class InvalidCorsError(Exception):
- """Exception raised when CORS XML is invalid."""
- def __init__(self, message):
- super(InvalidCorsError, self).__init__(message)
- self.message = message
- class NoAuthHandlerFound(Exception):
- """Is raised when no auth handlers were found ready to authenticate."""
- pass
- class InvalidLifecycleConfigError(Exception):
- """Exception raised when GCS lifecycle configuration XML is invalid."""
- def __init__(self, message):
- super(InvalidLifecycleConfigError, self).__init__(message)
- self.message = message
- # Enum class for resumable upload failure disposition.
- class ResumableTransferDisposition(object):
- # START_OVER means an attempt to resume an existing transfer failed,
- # and a new resumable upload should be attempted (without delay).
- START_OVER = 'START_OVER'
- # WAIT_BEFORE_RETRY means the resumable transfer failed but that it can
- # be retried after a time delay within the current process.
- WAIT_BEFORE_RETRY = 'WAIT_BEFORE_RETRY'
- # ABORT_CUR_PROCESS means the resumable transfer failed and that
- # delaying/retrying within the current process will not help. If
- # resumable transfer included a state tracker file the upload can be
- # retried again later, in another process (e.g., a later run of gsutil).
- ABORT_CUR_PROCESS = 'ABORT_CUR_PROCESS'
- # ABORT means the resumable transfer failed in a way that it does not
- # make sense to continue in the current process, and further that the
- # current tracker ID should not be preserved (in a tracker file if one
- # was specified at resumable upload start time). If the user tries again
- # later (e.g., a separate run of gsutil) it will get a new resumable
- # upload ID.
- ABORT = 'ABORT'
- class ResumableUploadException(Exception):
- """
- Exception raised for various resumable upload problems.
- self.disposition is of type ResumableTransferDisposition.
- """
- def __init__(self, message, disposition):
- super(ResumableUploadException, self).__init__(message, disposition)
- self.message = message
- self.disposition = disposition
- def __repr__(self):
- return 'ResumableUploadException("%s", %s)' % (
- self.message, self.disposition)
- class ResumableDownloadException(Exception):
- """
- Exception raised for various resumable download problems.
- self.disposition is of type ResumableTransferDisposition.
- """
- def __init__(self, message, disposition):
- super(ResumableDownloadException, self).__init__(message, disposition)
- self.message = message
- self.disposition = disposition
- def __repr__(self):
- return 'ResumableDownloadException("%s", %s)' % (
- self.message, self.disposition)
- class TooManyRecordsException(Exception):
- """
- Exception raised when a search of Route53 records returns more
- records than requested.
- """
- def __init__(self, message):
- super(TooManyRecordsException, self).__init__(message)
- self.message = message
- class PleaseRetryException(Exception):
- """
- Indicates a request should be retried.
- """
- def __init__(self, message, response=None):
- self.message = message
- self.response = response
- def __repr__(self):
- return 'PleaseRetryException("%s", %s)' % (
- self.message,
- self.response
- )
- class InvalidInstanceMetadataError(Exception):
- MSG = (
- "You can set the 'metadata_service_num_attempts' "
- "in your boto config file to increase the number "
- "of times boto will attempt to retrieve "
- "credentials from the instance metadata service."
- )
- def __init__(self, msg):
- final_msg = msg + '\n' + self.MSG
- super(InvalidInstanceMetadataError, self).__init__(final_msg)
|