exception.py 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. # Copyright 2010 United States Government as represented by the
  2. # Administrator of the National Aeronautics and Space Administration.
  3. # All Rights Reserved.
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  6. # not use this file except in compliance with the License. You may obtain
  7. # a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  13. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  14. # License for the specific language governing permissions and limitations
  15. # under the License.
  16. import sys
  17. from oslo_config import cfg
  18. from oslo_log import log as logging
  19. from oslo_versionedobjects import exception as obj_exc
  20. import six
  21. import webob.exc
  22. from webob.util import status_generic_reasons
  23. from webob.util import status_reasons
  24. from coriolis.i18n import _, _LE # noqa
  25. LOG = logging.getLogger(__name__)
  26. CONF = cfg.CONF
  27. class ConvertedException(webob.exc.WSGIHTTPException):
  28. def __init__(self, code=500, title="", explanation=""):
  29. self.code = code
  30. # There is a strict rule about constructing status line for HTTP:
  31. # '...Status-Line, consisting of the protocol version followed by a
  32. # numeric status code and its associated textual phrase, with each
  33. # element separated by SP characters'
  34. # (http://www.faqs.org/rfcs/rfc2616.html)
  35. # 'code' and 'title' can not be empty because they correspond
  36. # to numeric status code and its associated text
  37. if title:
  38. self.title = title
  39. else:
  40. try:
  41. self.title = status_reasons[self.code]
  42. except KeyError:
  43. generic_code = self.code // 100
  44. self.title = status_generic_reasons[generic_code]
  45. self.explanation = explanation
  46. super(ConvertedException, self).__init__()
  47. class Error(Exception):
  48. pass
  49. class CoriolisException(Exception):
  50. """Base Coriolis Exception
  51. To correctly use this class, inherit from it and define
  52. a 'message' property. That message will get printf'd
  53. with the keyword arguments provided to the constructor.
  54. """
  55. message = _("An unknown exception occurred.")
  56. code = 500
  57. headers = {}
  58. safe = False
  59. def __init__(self, message=None, **kwargs):
  60. self.kwargs = kwargs
  61. if 'code' not in self.kwargs:
  62. try:
  63. self.kwargs['code'] = self.code
  64. except AttributeError:
  65. pass
  66. for k, v in self.kwargs.items():
  67. if isinstance(v, Exception):
  68. self.kwargs[k] = six.text_type(v)
  69. if self._should_format(message):
  70. try:
  71. message = self.message % kwargs
  72. except Exception:
  73. exc_info = sys.exc_info()
  74. # kwargs doesn't match a variable in the message
  75. # log the issue and the kwargs
  76. LOG.exception(_LE('Exception in string format operation'))
  77. for name, value in kwargs.items():
  78. LOG.error(_LE("%(name)s: %(value)s"),
  79. {'name': name, 'value': value})
  80. if CONF.fatal_exception_format_errors:
  81. six.reraise(*exc_info)
  82. # at least get the core message out if something happened
  83. message = self.message
  84. elif isinstance(message, Exception):
  85. message = six.text_type(message)
  86. # NOTE(luisg): We put the actual message in 'msg' so that we can access
  87. # it, because if we try to access the message via 'message' it will be
  88. # overshadowed by the class' message attribute
  89. self.msg = message
  90. super(CoriolisException, self).__init__(message)
  91. def _should_format(self, message):
  92. return message is None or '%(message)' in self.message
  93. def __unicode__(self):
  94. return six.text_type(self.msg)
  95. class NotAuthorized(CoriolisException):
  96. message = _("Not authorized.")
  97. code = 403
  98. safe = True
  99. class PolicyNotAuthorized(CoriolisException):
  100. message = _("Not authorized via policy.")
  101. code = 403
  102. safe = True
  103. class Conflict(CoriolisException):
  104. message = _("Conflict")
  105. code = 409
  106. safe = True
  107. class AdminRequired(NotAuthorized):
  108. message = _("User does not have admin privileges")
  109. class PolicyNotAuthorized(NotAuthorized):
  110. message = _("Policy doesn't allow %(action)s to be performed.")
  111. class Invalid(CoriolisException):
  112. message = _("Unacceptable parameters.")
  113. code = 400
  114. safe = True
  115. class InvalidResults(Invalid):
  116. message = _("The results are invalid.")
  117. class InvalidInput(Invalid):
  118. message = _("Invalid input received: %(reason)s")
  119. class InvalidContentType(Invalid):
  120. message = _("Invalid content type %(content_type)s.")
  121. class InvalidHost(Invalid):
  122. message = _("Invalid host: %(reason)s")
  123. class SameDestination(Invalid):
  124. message = _("Origin and destination cannot be the same")
  125. # Cannot be templated as the error syntax varies.
  126. # msg needs to be constructed when raised.
  127. class InvalidParameterValue(Invalid):
  128. message = _("%(err)s")
  129. class InvalidAuthKey(Invalid):
  130. message = _("Invalid auth key: %(reason)s")
  131. class InvalidConfigurationValue(Invalid):
  132. message = _('Value "%(value)s" is not valid for '
  133. 'configuration option "%(option)s"')
  134. class InvalidActionTasksExecutionState(Invalid):
  135. message = _("Invalid tasks execution state: %(reason)s")
  136. class InvalidMigrationState(Invalid):
  137. message = _("Invalid migration state: %(reason)s")
  138. class InvalidReplicaState(Invalid):
  139. message = _("Invalid replica state: %(reason)s")
  140. class ServiceUnavailable(Invalid):
  141. message = _("Service is unavailable at this time.")
  142. class APIException(CoriolisException):
  143. message = _("Error while requesting %(service)s API.")
  144. safe = True
  145. def __init__(self, message=None, **kwargs):
  146. if 'service' not in kwargs:
  147. kwargs['service'] = 'unknown'
  148. super(APIException, self).__init__(message, **kwargs)
  149. class APITimeout(APIException):
  150. message = _("Timeout while requesting %(service)s API.")
  151. class NotFound(CoriolisException):
  152. message = _("Resource could not be found.")
  153. code = 404
  154. safe = True
  155. class OSMorphingToolsNotFound(NotFound):
  156. message = _("Couldn't find any morphing tools for this OS.")
  157. class FileNotFound(NotFound):
  158. message = _("File %(file_path)s could not be found.")
  159. class InstanceNotFound(NotFound):
  160. message = _("Instance \"%(instance_name)s\" could not be found.")
  161. class NetworkNotFound(NotFound):
  162. message = _("Network \"%(network_name)s\" could not be found.")
  163. class DiskStorageMappingNotFound(NotFound):
  164. message = _('No storage mapping for disk with ID "%(id)s" could be found.')
  165. class StorageBackendNotFound(NotFound):
  166. message = _(
  167. 'Storage backend with name "%(storage_name)s" could not be found.')
  168. class ImageNotFound(NotFound):
  169. message = _("Image \"%(image_name)s\" could not be found.")
  170. class FlavorNotFound(NotFound):
  171. message = _("Flavor \"%(flavor_name)s\" could not be found.")
  172. class FloatingIPPoolNotFound(NotFound):
  173. message = _("Floating IP pool \"%(pool_name)s\" could not be found.")
  174. class VolumeNotFound(NotFound):
  175. message = _("Volume \"%(volume_id)s\" could not be found.")
  176. class VolumeSnapshotNotFound(NotFound):
  177. message = _("Volume snapshot \"%(snapshot_id)s\" could not be found.")
  178. class VolumeBackupNotFound(NotFound):
  179. message = _("Volume backup \"%(backup_id)s\" could not be found.")
  180. class Duplicate(CoriolisException):
  181. safe = True
  182. class MalformedRequestBody(CoriolisException):
  183. message = _("Malformed message body: %(reason)s")
  184. code = 400
  185. safe = True
  186. class ConfigNotFound(NotFound):
  187. message = _("Could not find config at %(path)s")
  188. class ParameterNotFound(NotFound):
  189. message = _("Could not find parameter %(param)s")
  190. class PasteAppNotFound(NotFound):
  191. message = _("Could not load paste app '%(name)s' from %(path)s")
  192. class NoValidHost(CoriolisException):
  193. message = _("No valid host was found. %(reason)s")
  194. safe = True
  195. UnsupportedObjectError = obj_exc.UnsupportedObjectError
  196. OrphanedObjectError = obj_exc.OrphanedObjectError
  197. IncompatibleObjectVersion = obj_exc.IncompatibleObjectVersion
  198. ReadOnlyFieldError = obj_exc.ReadOnlyFieldError
  199. ObjectActionError = obj_exc.ObjectActionError
  200. ObjectFieldInvalid = obj_exc.ObjectFieldInvalid
  201. class NotSupportedOperation(Invalid):
  202. message = _("Operation not supported: %(operation)s.")
  203. code = 405
  204. class TaskProcessException(CoriolisException):
  205. safe = True
  206. class OperatingSystemNotFound(NotFound):
  207. pass
  208. class ConnectionValidationException(CoriolisException):
  209. safe = True
  210. class SchemaValidationException(CoriolisException):
  211. safe = True
  212. class QEMUException(Exception):
  213. pass
  214. if six.PY2:
  215. class ConnectionRefusedError(OSError):
  216. pass
  217. else:
  218. ConnectionRefusedError = six.moves.builtins.ConnectionRefusedError