blob: 6d656ec57a018031965900346f096a4d236607ea [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 Treinish6421af82015-04-23 09:47:50 -040016from six.moves import http_client as httplib
Matthew Treinishf077dd22015-04-23 09:37:41 -040017from six.moves.urllib import parse as urlparse
Daisuke Morita2c87d372013-10-31 19:39:19 +090018
Ken'ichi Ohmichi19f68812016-03-02 14:09:17 +090019from tempest.lib.common import rest_client
Brian Oberf2deb182016-04-12 19:28:04 +000020from tempest.lib import exceptions
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
Daisuke Morita2c87d372013-10-31 19:39:19 +090044 def delete_object(self, container, object_name, params=None):
Sean Daguef237ccb2013-01-04 15:19:14 -050045 """Delete storage object."""
dwalleck5d734432012-10-04 01:11:47 -050046 url = "%s/%s" % (str(container), str(object_name))
Daisuke Morita2c87d372013-10-31 19:39:19 +090047 if params:
Matthew Treinish89128142015-04-23 10:44:30 -040048 url += '?%s' % urlparse.urlencode(params)
vponomaryov67b58fe2014-02-06 19:05:41 +020049 resp, body = self.delete(url, headers={})
JordanPa84dde32014-11-14 15:47:42 +010050 self.expected_success([200, 204], resp.status)
dwalleck5d734432012-10-04 01:11:47 -050051 return resp, body
52
53 def update_object_metadata(self, container, object_name, metadata,
54 metadata_prefix='X-Object-Meta-'):
Sean Daguef237ccb2013-01-04 15:19:14 -050055 """Add, remove, or change X-Object-Meta metadata for storage object."""
dwalleck5d734432012-10-04 01:11:47 -050056
57 headers = {}
58 for key in metadata:
59 headers["%s%s" % (str(metadata_prefix), str(key))] = metadata[key]
60
61 url = "%s/%s" % (str(container), str(object_name))
62 resp, body = self.post(url, None, headers=headers)
JordanPa84dde32014-11-14 15:47:42 +010063 self.expected_success(202, resp.status)
dwalleck5d734432012-10-04 01:11:47 -050064 return resp, body
65
66 def list_object_metadata(self, container, object_name):
Sean Daguef237ccb2013-01-04 15:19:14 -050067 """List all storage object X-Object-Meta- metadata."""
dwalleck5d734432012-10-04 01:11:47 -050068
69 url = "%s/%s" % (str(container), str(object_name))
70 resp, body = self.head(url)
JordanPa84dde32014-11-14 15:47:42 +010071 self.expected_success(200, resp.status)
dwalleck5d734432012-10-04 01:11:47 -050072 return resp, body
Larisa Ustalov6c3c7802012-11-05 12:25:19 +020073
Daisuke Morita6d502682014-03-19 21:08:54 +090074 def get_object(self, container, object_name, metadata=None):
Larisa Ustalov6c3c7802012-11-05 12:25:19 +020075 """Retrieve object's data."""
76
Daisuke Morita6d502682014-03-19 21:08:54 +090077 headers = {}
78 if metadata:
79 for key in metadata:
80 headers[str(key)] = metadata[key]
81
Larisa Ustalov6c3c7802012-11-05 12:25:19 +020082 url = "{0}/{1}".format(container, object_name)
Daisuke Morita6d502682014-03-19 21:08:54 +090083 resp, body = self.get(url, headers=headers)
JordanPa84dde32014-11-14 15:47:42 +010084 self.expected_success([200, 206], resp.status)
Larisa Ustalov6c3c7802012-11-05 12:25:19 +020085 return resp, body
86
rajalakshmi-ganesan925e2392012-11-30 19:17:24 +053087 def copy_object_in_same_container(self, container, src_object_name,
88 dest_object_name, metadata=None):
Sean Daguef237ccb2013-01-04 15:19:14 -050089 """Copy storage object's data to the new object using PUT."""
Larisa Ustalov6c3c7802012-11-05 12:25:19 +020090
91 url = "{0}/{1}".format(container, dest_object_name)
92 headers = {}
93 headers['X-Copy-From'] = "%s/%s" % (str(container),
94 str(src_object_name))
95 headers['content-length'] = '0'
96 if metadata:
97 for key in metadata:
98 headers[str(key)] = metadata[key]
99
100 resp, body = self.put(url, None, headers=headers)
JordanPa84dde32014-11-14 15:47:42 +0100101 self.expected_success(201, resp.status)
Larisa Ustalov6c3c7802012-11-05 12:25:19 +0200102 return resp, body
103
rajalakshmi-ganesan925e2392012-11-30 19:17:24 +0530104 def copy_object_across_containers(self, src_container, src_object_name,
105 dst_container, dst_object_name,
106 metadata=None):
Sean Daguef237ccb2013-01-04 15:19:14 -0500107 """Copy storage object's data to the new object using PUT."""
rajalakshmi-ganesan925e2392012-11-30 19:17:24 +0530108
109 url = "{0}/{1}".format(dst_container, dst_object_name)
110 headers = {}
111 headers['X-Copy-From'] = "%s/%s" % (str(src_container),
112 str(src_object_name))
113 headers['content-length'] = '0'
114 if metadata:
115 for key in metadata:
116 headers[str(key)] = metadata[key]
117
118 resp, body = self.put(url, None, headers=headers)
JordanPa84dde32014-11-14 15:47:42 +0100119 self.expected_success(201, resp.status)
rajalakshmi-ganesan925e2392012-11-30 19:17:24 +0530120 return resp, body
121
Larisa Ustalov6c3c7802012-11-05 12:25:19 +0200122 def copy_object_2d_way(self, container, src_object_name, dest_object_name,
123 metadata=None):
Sean Daguef237ccb2013-01-04 15:19:14 -0500124 """Copy storage object's data to the new object using COPY."""
Larisa Ustalov6c3c7802012-11-05 12:25:19 +0200125
126 url = "{0}/{1}".format(container, src_object_name)
127 headers = {}
128 headers['Destination'] = "%s/%s" % (str(container),
129 str(dest_object_name))
130 if metadata:
131 for key in metadata:
132 headers[str(key)] = metadata[key]
133
134 resp, body = self.copy(url, headers=headers)
JordanPa84dde32014-11-14 15:47:42 +0100135 self.expected_success(201, resp.status)
Larisa Ustalov6c3c7802012-11-05 12:25:19 +0200136 return resp, body
harika-vakadi1a9ad612012-12-14 19:12:08 +0530137
harika-vakadi7cfc5182013-01-16 13:59:25 +0530138 def create_object_segments(self, container, object_name, segment, data):
139 """Creates object segments."""
140 url = "{0}/{1}/{2}".format(container, object_name, segment)
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200141 resp, body = self.put(url, data)
JordanPa84dde32014-11-14 15:47:42 +0100142 self.expected_success(201, resp.status)
ravikumar-venkatesane43b77a2013-01-09 07:27:13 +0000143 return resp, body
144
Jordan Pittier4408c4a2016-04-29 15:05:09 +0200145 def put_object_with_chunk(self, container, name, contents):
146 """Put an object with Transfer-Encoding header
147
148 :param container: name of the container
149 :type container: string
150 :param name: name of the object
151 :type name: string
152 :param contents: object data
153 :type contents: iterable
154 """
Daisuke Morita02f840b2014-03-19 20:51:01 +0900155 headers = {'Transfer-Encoding': 'chunked'}
156 if self.token:
157 headers['X-Auth-Token'] = self.token
158
Jordan Pittier4408c4a2016-04-29 15:05:09 +0200159 url = "%s/%s" % (container, name)
160 resp, body = self.put(
161 url, headers=headers,
162 body=contents,
163 chunked=True
164 )
Daisuke Morita02f840b2014-03-19 20:51:01 +0900165
Ken'ichi Ohmichi17051e82016-10-20 11:46:06 -0700166 self._error_checker(resp, body)
JordanPa84dde32014-11-14 15:47:42 +0100167 self.expected_success(201, resp.status)
Jordan Pittier4408c4a2016-04-29 15:05:09 +0200168 return resp.status, resp.reason, resp
Daisuke Morita02f840b2014-03-19 20:51:01 +0900169
Daisuke Morita02f840b2014-03-19 20:51:01 +0900170 def create_object_continue(self, container, object_name,
171 data, metadata=None):
Brian Oberf2deb182016-04-12 19:28:04 +0000172 """Put an object using Expect:100-continue"""
Daisuke Morita02f840b2014-03-19 20:51:01 +0900173 headers = {}
174 if metadata:
175 for key in metadata:
176 headers[str(key)] = metadata[key]
177
Daisuke Morita02f840b2014-03-19 20:51:01 +0900178 headers['X-Auth-Token'] = self.token
Brian Oberf2deb182016-04-12 19:28:04 +0000179 headers['content-length'] = 0 if data is None else len(data)
180 headers['Expect'] = '100-continue'
Daisuke Morita02f840b2014-03-19 20:51:01 +0900181
Brian Oberf2deb182016-04-12 19:28:04 +0000182 parsed = urlparse.urlparse(self.base_url)
183 path = str(parsed.path) + "/"
184 path += "%s/%s" % (str(container), str(object_name))
Daisuke Morita02f840b2014-03-19 20:51:01 +0900185
Brian Oberf2deb182016-04-12 19:28:04 +0000186 conn = create_connection(parsed)
187
188 # Send the PUT request and the headers including the "Expect" header
189 conn.putrequest('PUT', path)
190
guo yunxian7bbbec12016-08-21 20:03:10 +0800191 for header, value in headers.items():
Brian Oberf2deb182016-04-12 19:28:04 +0000192 conn.putheader(header, value)
193 conn.endheaders()
194
195 # Read the 100 status prior to sending the data
Daisuke Morita02f840b2014-03-19 20:51:01 +0900196 response = conn.response_class(conn.sock,
Daisuke Morita02f840b2014-03-19 20:51:01 +0900197 method=conn._method)
Brian Oberf2deb182016-04-12 19:28:04 +0000198 _, status, _ = response._read_status()
Daisuke Morita02f840b2014-03-19 20:51:01 +0900199
Brian Oberf2deb182016-04-12 19:28:04 +0000200 # toss the CRLF at the end of the response
201 response._safe_read(2)
202
203 # Expecting a 100 here, if not close and throw an exception
204 if status != 100:
205 conn.close()
206 pattern = "%s %s" % (
207 """Unexpected http success status code {0}.""",
208 """The expected status code is {1}""")
209 details = pattern.format(status, 100)
210 raise exceptions.UnexpectedResponseCode(details)
211
212 # If a continue was received go ahead and send the data
213 # and get the final response
214 conn.send(data)
215
216 resp = conn.getresponse()
217
218 return resp.status, resp.reason
219
220
221def create_connection(parsed_url):
222 """Helper function to create connection with httplib
223
224 :param parsed_url: parsed url of the remote location
225 """
226 if parsed_url.scheme == 'https':
227 conn = httplib.HTTPSConnection(parsed_url.netloc)
228 else:
229 conn = httplib.HTTPConnection(parsed_url.netloc)
230
231 return conn