19
19
Client classes for callers of a Glance system
24
from glance.api.v1 import images as v1_images
25
from glance.common import client as base_client
26
from glance.common import exception
30
27
from glance import utils
31
from glance.common import exception
33
29
#TODO(jaypipes) Allow a logger param for client classes
36
class ClientConnectionError(Exception):
37
"""Error resulting from a client connecting to a server"""
41
class ImageBodyIterator(object):
44
A class that acts as an iterator over an image file's
45
chunks of data. This is returned as part of the result
46
tuple from `glance.client.Client.get_image`
51
def __init__(self, response):
53
Constructs the object from an HTTPResponse object
55
self.response = response
59
Exposes an iterator over the chunks of data in the
63
chunk = self.response.read(ImageBodyIterator.CHUNKSIZE)
70
class BaseClient(object):
72
"""A base client class"""
76
def __init__(self, host, port, use_ssl):
78
Creates a new client to some service.
80
:param host: The host where service resides
81
:param port: The port where service resides
82
:param use_ssl: Should we use HTTPS?
86
self.use_ssl = use_ssl
87
self.connection = None
89
def get_connection_type(self):
91
Returns the proper connection type
94
return httplib.HTTPSConnection
96
return httplib.HTTPConnection
98
def do_request(self, method, action, body=None, headers=None,
101
Connects to the server and issues a request. Handles converting
102
any returned HTTP error status codes to OpenStack/Glance exceptions
103
and closing the server connection. Returns the result data, or
104
raises an appropriate exception.
106
:param method: HTTP method ("GET", "POST", "PUT", etc...)
107
:param action: part of URL after root netloc
108
:param body: string of data to send, or None (default)
109
:param headers: mapping of key/value pairs to add as headers
110
:param params: dictionary of key/value pairs to add to append
115
If the body param has a read attribute, and method is either
116
POST or PUT, this method will automatically conduct a chunked-transfer
117
encoding and use the body as a file object, transferring chunks
118
of data using the connection's send() method. This allows large
119
objects to be transferred efficiently without buffering the entire
122
if type(params) is dict:
123
action += '?' + urllib.urlencode(params)
126
connection_type = self.get_connection_type()
127
headers = headers or {}
128
c = connection_type(self.host, self.port)
130
# Do a simple request or a chunked request, depending
131
# on whether the body param is a file-like object and
132
# the method is PUT or POST
133
if hasattr(body, 'read') and method.lower() in ('post', 'put'):
135
c.putrequest(method, action)
137
for header, value in headers.items():
138
c.putheader(header, value)
139
c.putheader('Transfer-Encoding', 'chunked')
142
chunk = body.read(self.CHUNKSIZE)
144
c.send('%x\r\n%s\r\n' % (len(chunk), chunk))
145
chunk = body.read(self.CHUNKSIZE)
149
c.request(method, action, body, headers)
150
res = c.getresponse()
151
status_code = self.get_status_code(res)
152
if status_code in (httplib.OK,
157
elif status_code == httplib.UNAUTHORIZED:
158
raise exception.NotAuthorized
159
elif status_code == httplib.FORBIDDEN:
160
raise exception.NotAuthorized
161
elif status_code == httplib.NOT_FOUND:
162
raise exception.NotFound
163
elif status_code == httplib.CONFLICT:
164
raise exception.Duplicate(res.read())
165
elif status_code == httplib.BAD_REQUEST:
166
raise exception.Invalid(res.read())
167
elif status_code == httplib.INTERNAL_SERVER_ERROR:
168
raise Exception("Internal Server error: %s" % res.read())
170
raise Exception("Unknown error occurred! %s" % res.read())
172
except (socket.error, IOError), e:
173
raise ClientConnectionError("Unable to connect to "
174
"server. Got error: %s" % e)
176
def get_status_code(self, response):
178
Returns the integer status code from the response, which
179
can be either a Webob.Response (used in testing) or httplib.Response
181
if hasattr(response, 'status_int'):
182
return response.status_int
184
return response.status
187
class V1Client(BaseClient):
32
class V1Client(base_client.BaseClient):
189
34
"""Main client class for accessing Glance resources"""