add escalator cli framework
[escalator.git] / client / escalatorclient / exc.py
1 # Copyright 2012 OpenStack Foundation
2 # All Rights Reserved.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15
16 import re
17 import sys
18
19
20 class BaseException(Exception):
21     """An error occurred."""
22     def __init__(self, message=None):
23         self.message = message
24
25     def __str__(self):
26         return self.message or self.__class__.__doc__
27
28
29 class CommandError(BaseException):
30     """Invalid usage of CLI."""
31
32
33 class InvalidEndpoint(BaseException):
34     """The provided endpoint is invalid."""
35
36
37 class CommunicationError(BaseException):
38     """Unable to communicate with server."""
39
40
41 class ClientException(Exception):
42     """DEPRECATED!"""
43
44
45 class HTTPException(ClientException):
46     """Base exception for all HTTP-derived exceptions."""
47     code = 'N/A'
48
49     def __init__(self, details=None):
50         self.details = details or self.__class__.__name__
51
52     def __str__(self):
53         return "%s (HTTP %s)" % (self.details, self.code)
54
55
56 class HTTPMultipleChoices(HTTPException):
57     code = 300
58
59     def __str__(self):
60         self.details = ("Requested version of OpenStack Images API is not "
61                         "available.")
62         return "%s (HTTP %s) %s" % (self.__class__.__name__, self.code,
63                                     self.details)
64
65
66 class BadRequest(HTTPException):
67     """DEPRECATED!"""
68     code = 400
69
70
71 class HTTPBadRequest(BadRequest):
72     pass
73
74
75 class Unauthorized(HTTPException):
76     """DEPRECATED!"""
77     code = 401
78
79
80 class HTTPUnauthorized(Unauthorized):
81     pass
82
83
84 class Forbidden(HTTPException):
85     """DEPRECATED!"""
86     code = 403
87
88
89 class HTTPForbidden(Forbidden):
90     pass
91
92
93 class NotFound(HTTPException):
94     """DEPRECATED!"""
95     code = 404
96
97
98 class HTTPNotFound(NotFound):
99     pass
100
101
102 class HTTPMethodNotAllowed(HTTPException):
103     code = 405
104
105
106 class Conflict(HTTPException):
107     """DEPRECATED!"""
108     code = 409
109
110
111 class HTTPConflict(Conflict):
112     pass
113
114
115 class OverLimit(HTTPException):
116     """DEPRECATED!"""
117     code = 413
118
119
120 class HTTPOverLimit(OverLimit):
121     pass
122
123
124 class HTTPInternalServerError(HTTPException):
125     code = 500
126
127
128 class HTTPNotImplemented(HTTPException):
129     code = 501
130
131
132 class HTTPBadGateway(HTTPException):
133     code = 502
134
135
136 class ServiceUnavailable(HTTPException):
137     """DEPRECATED!"""
138     code = 503
139
140
141 class HTTPServiceUnavailable(ServiceUnavailable):
142     pass
143
144
145 # NOTE(bcwaldon): Build a mapping of HTTP codes to corresponding exception
146 # classes
147 _code_map = {}
148 for obj_name in dir(sys.modules[__name__]):
149     if obj_name.startswith('HTTP'):
150         obj = getattr(sys.modules[__name__], obj_name)
151         _code_map[obj.code] = obj
152
153
154 def from_response(response, body=None):
155     """Return an instance of an HTTPException based on httplib response."""
156     cls = _code_map.get(response.status_code, HTTPException)
157     if body and 'json' in response.headers['content-type']:
158         # Iterate over the nested objects and retreive the "message" attribute.
159         messages = [obj.get('message') for obj in response.json().values()]
160         # Join all of the messages together nicely and filter out any objects
161         # that don't have a "message" attr.
162         details = '\n'.join(i for i in messages if i is not None)
163         return cls(details=details)
164     elif body and 'html' in response.headers['content-type']:
165         # Split the lines, strip whitespace and inline HTML from the response.
166         details = [re.sub(r'<.+?>', '', i.strip())
167                    for i in response.text.splitlines()]
168         details = [i for i in details if i]
169         # Remove duplicates from the list.
170         details_seen = set()
171         details_temp = []
172         for i in details:
173             if i not in details_seen:
174                 details_temp.append(i)
175                 details_seen.add(i)
176         # Return joined string separated by colons.
177         details = ': '.join(details_temp)
178         return cls(details=details)
179     elif body:
180         details = body.replace('\n\n', '\n')
181         return cls(details=details)
182
183     return cls()
184
185
186 class NoTokenLookupException(Exception):
187     """DEPRECATED!"""
188     pass
189
190
191 class EndpointNotFound(Exception):
192     """DEPRECATED!"""
193     pass
194
195
196 class SSLConfigurationError(BaseException):
197     pass
198
199
200 class SSLCertificateError(BaseException):
201     pass