blob: 226558726178d5d0765b07dcd0e7cf4bfc89b094 [file] [log] [blame]
ZhiQiang Fan39f97222013-09-20 04:49:44 +08001# Copyright 2012 OpenStack Foundation
dwalleck5d734432012-10-04 01:11:47 -05002# 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
Matthew Treinish71426682015-04-23 11:19:38 -040016import six
Matthew Treinish6421af82015-04-23 09:47:50 -040017from six.moves import http_client as httplib
Matthew Treinishf077dd22015-04-23 09:37:41 -040018from six.moves.urllib import parse as urlparse
Daisuke Morita2c87d372013-10-31 19:39:19 +090019
Ken'ichi Ohmichi564b2ad2015-01-22 02:08:59 +000020from tempest.common import service_client
Matthew Treinish684d8992014-01-30 16:27:40 +000021
dwalleck5d734432012-10-04 01:11:47 -050022
Ken'ichi Ohmichi564b2ad2015-01-22 02:08:59 +000023class ObjectClient(service_client.ServiceClient):
dwalleck5d734432012-10-04 01:11:47 -050024
Daisuke Moritacf6f6952014-03-19 21:25:50 +090025 def create_object(self, container, object_name, data,
Ken'ichi Ohmichi179ea572015-01-01 14:04:49 +000026 params=None, metadata=None, headers=None):
Sean Daguef237ccb2013-01-04 15:19:14 -050027 """Create storage object."""
dwalleck5d734432012-10-04 01:11:47 -050028
Ken'ichi Ohmichi179ea572015-01-01 14:04:49 +000029 if headers is None:
30 headers = self.get_headers()
Martina Kollarova03720a52013-06-18 15:08:46 +020031 if not data:
32 headers['content-length'] = '0'
Daisuke Moritacf6f6952014-03-19 21:25:50 +090033 if metadata:
34 for key in metadata:
35 headers[str(key)] = metadata[key]
dwalleck5d734432012-10-04 01:11:47 -050036 url = "%s/%s" % (str(container), str(object_name))
Daisuke Morita2c87d372013-10-31 19:39:19 +090037 if params:
Matthew Treinish89128142015-04-23 10:44:30 -040038 url += '?%s' % urlparse.urlencode(params)
Daisuke Morita2c87d372013-10-31 19:39:19 +090039
Martina Kollarova03720a52013-06-18 15:08:46 +020040 resp, body = self.put(url, data, headers)
JordanPa84dde32014-11-14 15:47:42 +010041 self.expected_success(201, resp.status)
dwalleck5d734432012-10-04 01:11:47 -050042 return resp, body
43
44 def update_object(self, container, object_name, data):
Sean Daguef237ccb2013-01-04 15:19:14 -050045 """Upload data to replace current storage object."""
JordanPa84dde32014-11-14 15:47:42 +010046 resp, body = self.create_object(container, object_name, data)
47 self.expected_success(201, resp.status)
48 return resp, body
dwalleck5d734432012-10-04 01:11:47 -050049
Daisuke Morita2c87d372013-10-31 19:39:19 +090050 def delete_object(self, container, object_name, params=None):
Sean Daguef237ccb2013-01-04 15:19:14 -050051 """Delete storage object."""
dwalleck5d734432012-10-04 01:11:47 -050052 url = "%s/%s" % (str(container), str(object_name))
Daisuke Morita2c87d372013-10-31 19:39:19 +090053 if params:
Matthew Treinish89128142015-04-23 10:44:30 -040054 url += '?%s' % urlparse.urlencode(params)
vponomaryov67b58fe2014-02-06 19:05:41 +020055 resp, body = self.delete(url, headers={})
JordanPa84dde32014-11-14 15:47:42 +010056 self.expected_success([200, 204], resp.status)
dwalleck5d734432012-10-04 01:11:47 -050057 return resp, body
58
59 def update_object_metadata(self, container, object_name, metadata,
60 metadata_prefix='X-Object-Meta-'):
Sean Daguef237ccb2013-01-04 15:19:14 -050061 """Add, remove, or change X-Object-Meta metadata for storage object."""
dwalleck5d734432012-10-04 01:11:47 -050062
63 headers = {}
64 for key in metadata:
65 headers["%s%s" % (str(metadata_prefix), str(key))] = metadata[key]
66
67 url = "%s/%s" % (str(container), str(object_name))
68 resp, body = self.post(url, None, headers=headers)
JordanPa84dde32014-11-14 15:47:42 +010069 self.expected_success(202, resp.status)
dwalleck5d734432012-10-04 01:11:47 -050070 return resp, body
71
72 def list_object_metadata(self, container, object_name):
Sean Daguef237ccb2013-01-04 15:19:14 -050073 """List all storage object X-Object-Meta- metadata."""
dwalleck5d734432012-10-04 01:11:47 -050074
75 url = "%s/%s" % (str(container), str(object_name))
76 resp, body = self.head(url)
JordanPa84dde32014-11-14 15:47:42 +010077 self.expected_success(200, resp.status)
dwalleck5d734432012-10-04 01:11:47 -050078 return resp, body
Larisa Ustalov6c3c7802012-11-05 12:25:19 +020079
Daisuke Morita6d502682014-03-19 21:08:54 +090080 def get_object(self, container, object_name, metadata=None):
Larisa Ustalov6c3c7802012-11-05 12:25:19 +020081 """Retrieve object's data."""
82
Daisuke Morita6d502682014-03-19 21:08:54 +090083 headers = {}
84 if metadata:
85 for key in metadata:
86 headers[str(key)] = metadata[key]
87
Larisa Ustalov6c3c7802012-11-05 12:25:19 +020088 url = "{0}/{1}".format(container, object_name)
Daisuke Morita6d502682014-03-19 21:08:54 +090089 resp, body = self.get(url, headers=headers)
JordanPa84dde32014-11-14 15:47:42 +010090 self.expected_success([200, 206], resp.status)
Larisa Ustalov6c3c7802012-11-05 12:25:19 +020091 return resp, body
92
rajalakshmi-ganesan925e2392012-11-30 19:17:24 +053093 def copy_object_in_same_container(self, container, src_object_name,
94 dest_object_name, metadata=None):
Sean Daguef237ccb2013-01-04 15:19:14 -050095 """Copy storage object's data to the new object using PUT."""
Larisa Ustalov6c3c7802012-11-05 12:25:19 +020096
97 url = "{0}/{1}".format(container, dest_object_name)
98 headers = {}
99 headers['X-Copy-From'] = "%s/%s" % (str(container),
100 str(src_object_name))
101 headers['content-length'] = '0'
102 if metadata:
103 for key in metadata:
104 headers[str(key)] = metadata[key]
105
106 resp, body = self.put(url, None, headers=headers)
JordanPa84dde32014-11-14 15:47:42 +0100107 self.expected_success(201, resp.status)
Larisa Ustalov6c3c7802012-11-05 12:25:19 +0200108 return resp, body
109
rajalakshmi-ganesan925e2392012-11-30 19:17:24 +0530110 def copy_object_across_containers(self, src_container, src_object_name,
111 dst_container, dst_object_name,
112 metadata=None):
Sean Daguef237ccb2013-01-04 15:19:14 -0500113 """Copy storage object's data to the new object using PUT."""
rajalakshmi-ganesan925e2392012-11-30 19:17:24 +0530114
115 url = "{0}/{1}".format(dst_container, dst_object_name)
116 headers = {}
117 headers['X-Copy-From'] = "%s/%s" % (str(src_container),
118 str(src_object_name))
119 headers['content-length'] = '0'
120 if metadata:
121 for key in metadata:
122 headers[str(key)] = metadata[key]
123
124 resp, body = self.put(url, None, headers=headers)
JordanPa84dde32014-11-14 15:47:42 +0100125 self.expected_success(201, resp.status)
rajalakshmi-ganesan925e2392012-11-30 19:17:24 +0530126 return resp, body
127
Larisa Ustalov6c3c7802012-11-05 12:25:19 +0200128 def copy_object_2d_way(self, container, src_object_name, dest_object_name,
129 metadata=None):
Sean Daguef237ccb2013-01-04 15:19:14 -0500130 """Copy storage object's data to the new object using COPY."""
Larisa Ustalov6c3c7802012-11-05 12:25:19 +0200131
132 url = "{0}/{1}".format(container, src_object_name)
133 headers = {}
134 headers['Destination'] = "%s/%s" % (str(container),
135 str(dest_object_name))
136 if metadata:
137 for key in metadata:
138 headers[str(key)] = metadata[key]
139
140 resp, body = self.copy(url, headers=headers)
JordanPa84dde32014-11-14 15:47:42 +0100141 self.expected_success(201, resp.status)
Larisa Ustalov6c3c7802012-11-05 12:25:19 +0200142 return resp, body
harika-vakadi1a9ad612012-12-14 19:12:08 +0530143
harika-vakadi7cfc5182013-01-16 13:59:25 +0530144 def create_object_segments(self, container, object_name, segment, data):
145 """Creates object segments."""
146 url = "{0}/{1}/{2}".format(container, object_name, segment)
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200147 resp, body = self.put(url, data)
JordanPa84dde32014-11-14 15:47:42 +0100148 self.expected_success(201, resp.status)
ravikumar-venkatesane43b77a2013-01-09 07:27:13 +0000149 return resp, body
150
Daisuke Morita02f840b2014-03-19 20:51:01 +0900151 def put_object_with_chunk(self, container, name, contents, chunk_size):
152 """
153 Put an object with Transfer-Encoding header
154 """
155 if self.base_url is None:
156 self._set_auth()
157
158 headers = {'Transfer-Encoding': 'chunked'}
159 if self.token:
160 headers['X-Auth-Token'] = self.token
161
162 conn = put_object_connection(self.base_url, container, name, contents,
163 chunk_size, headers)
164
165 resp = conn.getresponse()
166 body = resp.read()
167
168 resp_headers = {}
169 for header, value in resp.getheaders():
170 resp_headers[header.lower()] = value
171
172 self._error_checker('PUT', None, headers, contents, resp, body)
JordanPa84dde32014-11-14 15:47:42 +0100173 self.expected_success(201, resp.status)
Daisuke Morita02f840b2014-03-19 20:51:01 +0900174 return resp.status, resp.reason, resp_headers
175
Daisuke Morita02f840b2014-03-19 20:51:01 +0900176 def create_object_continue(self, container, object_name,
177 data, metadata=None):
178 """Create storage object."""
179 headers = {}
180 if metadata:
181 for key in metadata:
182 headers[str(key)] = metadata[key]
183
184 if not data:
185 headers['content-length'] = '0'
186
187 if self.base_url is None:
188 self._set_auth()
189 headers['X-Auth-Token'] = self.token
190
191 conn = put_object_connection(self.base_url, str(container),
192 str(object_name), data, None, headers)
193
194 response = conn.response_class(conn.sock,
195 strict=conn.strict,
196 method=conn._method)
197 version, status, reason = response._read_status()
198 resp = {'version': version,
199 'status': str(status),
200 'reason': reason}
201
202 return resp
203
204
205def put_object_connection(base_url, container, name, contents=None,
206 chunk_size=65536, headers=None, query_string=None):
207 """
208 Helper function to make connection to put object with httplib
209 :param base_url: base_url of an object client
210 :param container: container name that the object is in
211 :param name: object name to put
212 :param contents: a string or a file like object to read object data
213 from; if None, a zero-byte put will be done
214 :param chunk_size: chunk size of data to write; it defaults to 65536;
215 used only if the the contents object has a 'read'
216 method, eg. file-like objects, ignored otherwise
217 :param headers: additional headers to include in the request, if any
218 :param query_string: if set will be appended with '?' to generated path
219 """
220 parsed = urlparse.urlparse(base_url)
221 if parsed.scheme == 'https':
222 conn = httplib.HTTPSConnection(parsed.netloc)
223 else:
224 conn = httplib.HTTPConnection(parsed.netloc)
225 path = str(parsed.path) + "/"
226 path += "%s/%s" % (str(container), str(name))
227
228 if query_string:
229 path += '?' + query_string
230 if headers:
231 headers = dict(headers)
232 else:
233 headers = {}
234 if hasattr(contents, 'read'):
235 conn.putrequest('PUT', path)
Matthew Treinish71426682015-04-23 11:19:38 -0400236 for header, value in six.iteritems(headers):
Daisuke Morita02f840b2014-03-19 20:51:01 +0900237 conn.putheader(header, value)
238 if 'Content-Length' not in headers:
239 if 'Transfer-Encoding' not in headers:
240 conn.putheader('Transfer-Encoding', 'chunked')
241 conn.endheaders()
242 chunk = contents.read(chunk_size)
243 while chunk:
244 conn.send('%x\r\n%s\r\n' % (len(chunk), chunk))
245 chunk = contents.read(chunk_size)
246 conn.send('0\r\n\r\n')
247 else:
248 conn.endheaders()
249 left = headers['Content-Length']
250 while left > 0:
251 size = chunk_size
252 if size > left:
253 size = left
254 chunk = contents.read(size)
255 conn.send(chunk)
256 left -= len(chunk)
257 else:
258 conn.request('PUT', path, contents, headers)
259
260 return conn