blob: 0acd4ad48666c9edc846392fd4022366033eb66a [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 Ohmichi19f68812016-03-02 14:09:17 +090020from tempest.lib.common import rest_client
Matthew Treinish684d8992014-01-30 16:27:40 +000021
dwalleck5d734432012-10-04 01:11:47 -050022
Ken'ichi Ohmichi19f68812016-03-02 14:09:17 +090023class ObjectClient(rest_client.RestClient):
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):
Ken'ichi Ohmichib2790842015-11-17 11:46:13 +0000152 """Put an object with Transfer-Encoding header"""
Daisuke Morita02f840b2014-03-19 20:51:01 +0900153 headers = {'Transfer-Encoding': 'chunked'}
154 if self.token:
155 headers['X-Auth-Token'] = self.token
156
157 conn = put_object_connection(self.base_url, container, name, contents,
158 chunk_size, headers)
159
160 resp = conn.getresponse()
161 body = resp.read()
162
163 resp_headers = {}
164 for header, value in resp.getheaders():
165 resp_headers[header.lower()] = value
166
167 self._error_checker('PUT', None, headers, contents, resp, body)
JordanPa84dde32014-11-14 15:47:42 +0100168 self.expected_success(201, resp.status)
Daisuke Morita02f840b2014-03-19 20:51:01 +0900169 return resp.status, resp.reason, resp_headers
170
Daisuke Morita02f840b2014-03-19 20:51:01 +0900171 def create_object_continue(self, container, object_name,
172 data, metadata=None):
173 """Create storage object."""
174 headers = {}
175 if metadata:
176 for key in metadata:
177 headers[str(key)] = metadata[key]
178
179 if not data:
180 headers['content-length'] = '0'
181
Daisuke Morita02f840b2014-03-19 20:51:01 +0900182 headers['X-Auth-Token'] = self.token
183
184 conn = put_object_connection(self.base_url, str(container),
185 str(object_name), data, None, headers)
186
187 response = conn.response_class(conn.sock,
188 strict=conn.strict,
189 method=conn._method)
190 version, status, reason = response._read_status()
191 resp = {'version': version,
192 'status': str(status),
193 'reason': reason}
194
195 return resp
196
197
198def put_object_connection(base_url, container, name, contents=None,
199 chunk_size=65536, headers=None, query_string=None):
Ken'ichi Ohmichib2790842015-11-17 11:46:13 +0000200 """Helper function to make connection to put object with httplib
201
Daisuke Morita02f840b2014-03-19 20:51:01 +0900202 :param base_url: base_url of an object client
203 :param container: container name that the object is in
204 :param name: object name to put
205 :param contents: a string or a file like object to read object data
206 from; if None, a zero-byte put will be done
207 :param chunk_size: chunk size of data to write; it defaults to 65536;
Anh Trand44a8be2016-03-25 09:49:14 +0700208 used only if the contents object has a 'read'
Daisuke Morita02f840b2014-03-19 20:51:01 +0900209 method, eg. file-like objects, ignored otherwise
210 :param headers: additional headers to include in the request, if any
211 :param query_string: if set will be appended with '?' to generated path
212 """
213 parsed = urlparse.urlparse(base_url)
214 if parsed.scheme == 'https':
215 conn = httplib.HTTPSConnection(parsed.netloc)
216 else:
217 conn = httplib.HTTPConnection(parsed.netloc)
218 path = str(parsed.path) + "/"
219 path += "%s/%s" % (str(container), str(name))
220
221 if query_string:
222 path += '?' + query_string
223 if headers:
224 headers = dict(headers)
225 else:
226 headers = {}
227 if hasattr(contents, 'read'):
228 conn.putrequest('PUT', path)
Matthew Treinish71426682015-04-23 11:19:38 -0400229 for header, value in six.iteritems(headers):
Daisuke Morita02f840b2014-03-19 20:51:01 +0900230 conn.putheader(header, value)
231 if 'Content-Length' not in headers:
232 if 'Transfer-Encoding' not in headers:
233 conn.putheader('Transfer-Encoding', 'chunked')
234 conn.endheaders()
235 chunk = contents.read(chunk_size)
236 while chunk:
237 conn.send('%x\r\n%s\r\n' % (len(chunk), chunk))
238 chunk = contents.read(chunk_size)
239 conn.send('0\r\n\r\n')
240 else:
241 conn.endheaders()
242 left = headers['Content-Length']
243 while left > 0:
244 size = chunk_size
245 if size > left:
246 size = left
247 chunk = contents.read(size)
248 conn.send(chunk)
249 left -= len(chunk)
250 else:
251 conn.request('PUT', path, contents, headers)
252
253 return conn