blob: 09883731c6a1238f91bf829736227ecafc80d975 [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
Brian Oberf2deb182016-04-12 19:28:04 +000021from tempest.lib import exceptions
Matthew Treinish684d8992014-01-30 16:27:40 +000022
dwalleck5d734432012-10-04 01:11:47 -050023
Ken'ichi Ohmichi19f68812016-03-02 14:09:17 +090024class ObjectClient(rest_client.RestClient):
dwalleck5d734432012-10-04 01:11:47 -050025
Daisuke Moritacf6f6952014-03-19 21:25:50 +090026 def create_object(self, container, object_name, data,
Ken'ichi Ohmichi179ea572015-01-01 14:04:49 +000027 params=None, metadata=None, headers=None):
Sean Daguef237ccb2013-01-04 15:19:14 -050028 """Create storage object."""
dwalleck5d734432012-10-04 01:11:47 -050029
Ken'ichi Ohmichi179ea572015-01-01 14:04:49 +000030 if headers is None:
31 headers = self.get_headers()
Martina Kollarova03720a52013-06-18 15:08:46 +020032 if not data:
33 headers['content-length'] = '0'
Daisuke Moritacf6f6952014-03-19 21:25:50 +090034 if metadata:
35 for key in metadata:
36 headers[str(key)] = metadata[key]
dwalleck5d734432012-10-04 01:11:47 -050037 url = "%s/%s" % (str(container), str(object_name))
Daisuke Morita2c87d372013-10-31 19:39:19 +090038 if params:
Matthew Treinish89128142015-04-23 10:44:30 -040039 url += '?%s' % urlparse.urlencode(params)
Daisuke Morita2c87d372013-10-31 19:39:19 +090040
Martina Kollarova03720a52013-06-18 15:08:46 +020041 resp, body = self.put(url, data, headers)
JordanPa84dde32014-11-14 15:47:42 +010042 self.expected_success(201, resp.status)
dwalleck5d734432012-10-04 01:11:47 -050043 return resp, body
44
45 def update_object(self, container, object_name, data):
Sean Daguef237ccb2013-01-04 15:19:14 -050046 """Upload data to replace current storage object."""
JordanPa84dde32014-11-14 15:47:42 +010047 resp, body = self.create_object(container, object_name, data)
48 self.expected_success(201, resp.status)
49 return resp, body
dwalleck5d734432012-10-04 01:11:47 -050050
Daisuke Morita2c87d372013-10-31 19:39:19 +090051 def delete_object(self, container, object_name, params=None):
Sean Daguef237ccb2013-01-04 15:19:14 -050052 """Delete storage object."""
dwalleck5d734432012-10-04 01:11:47 -050053 url = "%s/%s" % (str(container), str(object_name))
Daisuke Morita2c87d372013-10-31 19:39:19 +090054 if params:
Matthew Treinish89128142015-04-23 10:44:30 -040055 url += '?%s' % urlparse.urlencode(params)
vponomaryov67b58fe2014-02-06 19:05:41 +020056 resp, body = self.delete(url, headers={})
JordanPa84dde32014-11-14 15:47:42 +010057 self.expected_success([200, 204], resp.status)
dwalleck5d734432012-10-04 01:11:47 -050058 return resp, body
59
60 def update_object_metadata(self, container, object_name, metadata,
61 metadata_prefix='X-Object-Meta-'):
Sean Daguef237ccb2013-01-04 15:19:14 -050062 """Add, remove, or change X-Object-Meta metadata for storage object."""
dwalleck5d734432012-10-04 01:11:47 -050063
64 headers = {}
65 for key in metadata:
66 headers["%s%s" % (str(metadata_prefix), str(key))] = metadata[key]
67
68 url = "%s/%s" % (str(container), str(object_name))
69 resp, body = self.post(url, None, headers=headers)
JordanPa84dde32014-11-14 15:47:42 +010070 self.expected_success(202, resp.status)
dwalleck5d734432012-10-04 01:11:47 -050071 return resp, body
72
73 def list_object_metadata(self, container, object_name):
Sean Daguef237ccb2013-01-04 15:19:14 -050074 """List all storage object X-Object-Meta- metadata."""
dwalleck5d734432012-10-04 01:11:47 -050075
76 url = "%s/%s" % (str(container), str(object_name))
77 resp, body = self.head(url)
JordanPa84dde32014-11-14 15:47:42 +010078 self.expected_success(200, resp.status)
dwalleck5d734432012-10-04 01:11:47 -050079 return resp, body
Larisa Ustalov6c3c7802012-11-05 12:25:19 +020080
Daisuke Morita6d502682014-03-19 21:08:54 +090081 def get_object(self, container, object_name, metadata=None):
Larisa Ustalov6c3c7802012-11-05 12:25:19 +020082 """Retrieve object's data."""
83
Daisuke Morita6d502682014-03-19 21:08:54 +090084 headers = {}
85 if metadata:
86 for key in metadata:
87 headers[str(key)] = metadata[key]
88
Larisa Ustalov6c3c7802012-11-05 12:25:19 +020089 url = "{0}/{1}".format(container, object_name)
Daisuke Morita6d502682014-03-19 21:08:54 +090090 resp, body = self.get(url, headers=headers)
JordanPa84dde32014-11-14 15:47:42 +010091 self.expected_success([200, 206], resp.status)
Larisa Ustalov6c3c7802012-11-05 12:25:19 +020092 return resp, body
93
rajalakshmi-ganesan925e2392012-11-30 19:17:24 +053094 def copy_object_in_same_container(self, container, src_object_name,
95 dest_object_name, metadata=None):
Sean Daguef237ccb2013-01-04 15:19:14 -050096 """Copy storage object's data to the new object using PUT."""
Larisa Ustalov6c3c7802012-11-05 12:25:19 +020097
98 url = "{0}/{1}".format(container, dest_object_name)
99 headers = {}
100 headers['X-Copy-From'] = "%s/%s" % (str(container),
101 str(src_object_name))
102 headers['content-length'] = '0'
103 if metadata:
104 for key in metadata:
105 headers[str(key)] = metadata[key]
106
107 resp, body = self.put(url, None, headers=headers)
JordanPa84dde32014-11-14 15:47:42 +0100108 self.expected_success(201, resp.status)
Larisa Ustalov6c3c7802012-11-05 12:25:19 +0200109 return resp, body
110
rajalakshmi-ganesan925e2392012-11-30 19:17:24 +0530111 def copy_object_across_containers(self, src_container, src_object_name,
112 dst_container, dst_object_name,
113 metadata=None):
Sean Daguef237ccb2013-01-04 15:19:14 -0500114 """Copy storage object's data to the new object using PUT."""
rajalakshmi-ganesan925e2392012-11-30 19:17:24 +0530115
116 url = "{0}/{1}".format(dst_container, dst_object_name)
117 headers = {}
118 headers['X-Copy-From'] = "%s/%s" % (str(src_container),
119 str(src_object_name))
120 headers['content-length'] = '0'
121 if metadata:
122 for key in metadata:
123 headers[str(key)] = metadata[key]
124
125 resp, body = self.put(url, None, headers=headers)
JordanPa84dde32014-11-14 15:47:42 +0100126 self.expected_success(201, resp.status)
rajalakshmi-ganesan925e2392012-11-30 19:17:24 +0530127 return resp, body
128
Larisa Ustalov6c3c7802012-11-05 12:25:19 +0200129 def copy_object_2d_way(self, container, src_object_name, dest_object_name,
130 metadata=None):
Sean Daguef237ccb2013-01-04 15:19:14 -0500131 """Copy storage object's data to the new object using COPY."""
Larisa Ustalov6c3c7802012-11-05 12:25:19 +0200132
133 url = "{0}/{1}".format(container, src_object_name)
134 headers = {}
135 headers['Destination'] = "%s/%s" % (str(container),
136 str(dest_object_name))
137 if metadata:
138 for key in metadata:
139 headers[str(key)] = metadata[key]
140
141 resp, body = self.copy(url, headers=headers)
JordanPa84dde32014-11-14 15:47:42 +0100142 self.expected_success(201, resp.status)
Larisa Ustalov6c3c7802012-11-05 12:25:19 +0200143 return resp, body
harika-vakadi1a9ad612012-12-14 19:12:08 +0530144
harika-vakadi7cfc5182013-01-16 13:59:25 +0530145 def create_object_segments(self, container, object_name, segment, data):
146 """Creates object segments."""
147 url = "{0}/{1}/{2}".format(container, object_name, segment)
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200148 resp, body = self.put(url, data)
JordanPa84dde32014-11-14 15:47:42 +0100149 self.expected_success(201, resp.status)
ravikumar-venkatesane43b77a2013-01-09 07:27:13 +0000150 return resp, body
151
Jordan Pittier4408c4a2016-04-29 15:05:09 +0200152 def put_object_with_chunk(self, container, name, contents):
153 """Put an object with Transfer-Encoding header
154
155 :param container: name of the container
156 :type container: string
157 :param name: name of the object
158 :type name: string
159 :param contents: object data
160 :type contents: iterable
161 """
Daisuke Morita02f840b2014-03-19 20:51:01 +0900162 headers = {'Transfer-Encoding': 'chunked'}
163 if self.token:
164 headers['X-Auth-Token'] = self.token
165
Jordan Pittier4408c4a2016-04-29 15:05:09 +0200166 url = "%s/%s" % (container, name)
167 resp, body = self.put(
168 url, headers=headers,
169 body=contents,
170 chunked=True
171 )
Daisuke Morita02f840b2014-03-19 20:51:01 +0900172
173 self._error_checker('PUT', None, headers, contents, resp, body)
JordanPa84dde32014-11-14 15:47:42 +0100174 self.expected_success(201, resp.status)
Jordan Pittier4408c4a2016-04-29 15:05:09 +0200175 return resp.status, resp.reason, resp
Daisuke Morita02f840b2014-03-19 20:51:01 +0900176
Daisuke Morita02f840b2014-03-19 20:51:01 +0900177 def create_object_continue(self, container, object_name,
178 data, metadata=None):
Brian Oberf2deb182016-04-12 19:28:04 +0000179 """Put an object using Expect:100-continue"""
Daisuke Morita02f840b2014-03-19 20:51:01 +0900180 headers = {}
181 if metadata:
182 for key in metadata:
183 headers[str(key)] = metadata[key]
184
Daisuke Morita02f840b2014-03-19 20:51:01 +0900185 headers['X-Auth-Token'] = self.token
Brian Oberf2deb182016-04-12 19:28:04 +0000186 headers['content-length'] = 0 if data is None else len(data)
187 headers['Expect'] = '100-continue'
Daisuke Morita02f840b2014-03-19 20:51:01 +0900188
Brian Oberf2deb182016-04-12 19:28:04 +0000189 parsed = urlparse.urlparse(self.base_url)
190 path = str(parsed.path) + "/"
191 path += "%s/%s" % (str(container), str(object_name))
Daisuke Morita02f840b2014-03-19 20:51:01 +0900192
Brian Oberf2deb182016-04-12 19:28:04 +0000193 conn = create_connection(parsed)
194
195 # Send the PUT request and the headers including the "Expect" header
196 conn.putrequest('PUT', path)
197
198 for header, value in six.iteritems(headers):
199 conn.putheader(header, value)
200 conn.endheaders()
201
202 # Read the 100 status prior to sending the data
Daisuke Morita02f840b2014-03-19 20:51:01 +0900203 response = conn.response_class(conn.sock,
204 strict=conn.strict,
205 method=conn._method)
Brian Oberf2deb182016-04-12 19:28:04 +0000206 _, status, _ = response._read_status()
Daisuke Morita02f840b2014-03-19 20:51:01 +0900207
Brian Oberf2deb182016-04-12 19:28:04 +0000208 # toss the CRLF at the end of the response
209 response._safe_read(2)
210
211 # Expecting a 100 here, if not close and throw an exception
212 if status != 100:
213 conn.close()
214 pattern = "%s %s" % (
215 """Unexpected http success status code {0}.""",
216 """The expected status code is {1}""")
217 details = pattern.format(status, 100)
218 raise exceptions.UnexpectedResponseCode(details)
219
220 # If a continue was received go ahead and send the data
221 # and get the final response
222 conn.send(data)
223
224 resp = conn.getresponse()
225
226 return resp.status, resp.reason
227
228
229def create_connection(parsed_url):
230 """Helper function to create connection with httplib
231
232 :param parsed_url: parsed url of the remote location
233 """
234 if parsed_url.scheme == 'https':
235 conn = httplib.HTTPSConnection(parsed_url.netloc)
236 else:
237 conn = httplib.HTTPConnection(parsed_url.netloc)
238
239 return conn