| # Copyright 2012 OpenStack Foundation |
| # All Rights Reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may |
| # not use this file except in compliance with the License. You may obtain |
| # a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations |
| # under the License. |
| |
| import six |
| from six.moves import http_client as httplib |
| from six.moves.urllib import parse as urlparse |
| |
| from tempest.lib.common import rest_client |
| |
| |
| class ObjectClient(rest_client.RestClient): |
| |
| def create_object(self, container, object_name, data, |
| params=None, metadata=None, headers=None): |
| """Create storage object.""" |
| |
| if headers is None: |
| headers = self.get_headers() |
| if not data: |
| headers['content-length'] = '0' |
| if metadata: |
| for key in metadata: |
| headers[str(key)] = metadata[key] |
| url = "%s/%s" % (str(container), str(object_name)) |
| if params: |
| url += '?%s' % urlparse.urlencode(params) |
| |
| resp, body = self.put(url, data, headers) |
| self.expected_success(201, resp.status) |
| return resp, body |
| |
| def update_object(self, container, object_name, data): |
| """Upload data to replace current storage object.""" |
| resp, body = self.create_object(container, object_name, data) |
| self.expected_success(201, resp.status) |
| return resp, body |
| |
| def delete_object(self, container, object_name, params=None): |
| """Delete storage object.""" |
| url = "%s/%s" % (str(container), str(object_name)) |
| if params: |
| url += '?%s' % urlparse.urlencode(params) |
| resp, body = self.delete(url, headers={}) |
| self.expected_success([200, 204], resp.status) |
| return resp, body |
| |
| def update_object_metadata(self, container, object_name, metadata, |
| metadata_prefix='X-Object-Meta-'): |
| """Add, remove, or change X-Object-Meta metadata for storage object.""" |
| |
| headers = {} |
| for key in metadata: |
| headers["%s%s" % (str(metadata_prefix), str(key))] = metadata[key] |
| |
| url = "%s/%s" % (str(container), str(object_name)) |
| resp, body = self.post(url, None, headers=headers) |
| self.expected_success(202, resp.status) |
| return resp, body |
| |
| def list_object_metadata(self, container, object_name): |
| """List all storage object X-Object-Meta- metadata.""" |
| |
| url = "%s/%s" % (str(container), str(object_name)) |
| resp, body = self.head(url) |
| self.expected_success(200, resp.status) |
| return resp, body |
| |
| def get_object(self, container, object_name, metadata=None): |
| """Retrieve object's data.""" |
| |
| headers = {} |
| if metadata: |
| for key in metadata: |
| headers[str(key)] = metadata[key] |
| |
| url = "{0}/{1}".format(container, object_name) |
| resp, body = self.get(url, headers=headers) |
| self.expected_success([200, 206], resp.status) |
| return resp, body |
| |
| def copy_object_in_same_container(self, container, src_object_name, |
| dest_object_name, metadata=None): |
| """Copy storage object's data to the new object using PUT.""" |
| |
| url = "{0}/{1}".format(container, dest_object_name) |
| headers = {} |
| headers['X-Copy-From'] = "%s/%s" % (str(container), |
| str(src_object_name)) |
| headers['content-length'] = '0' |
| if metadata: |
| for key in metadata: |
| headers[str(key)] = metadata[key] |
| |
| resp, body = self.put(url, None, headers=headers) |
| self.expected_success(201, resp.status) |
| return resp, body |
| |
| def copy_object_across_containers(self, src_container, src_object_name, |
| dst_container, dst_object_name, |
| metadata=None): |
| """Copy storage object's data to the new object using PUT.""" |
| |
| url = "{0}/{1}".format(dst_container, dst_object_name) |
| headers = {} |
| headers['X-Copy-From'] = "%s/%s" % (str(src_container), |
| str(src_object_name)) |
| headers['content-length'] = '0' |
| if metadata: |
| for key in metadata: |
| headers[str(key)] = metadata[key] |
| |
| resp, body = self.put(url, None, headers=headers) |
| self.expected_success(201, resp.status) |
| return resp, body |
| |
| def copy_object_2d_way(self, container, src_object_name, dest_object_name, |
| metadata=None): |
| """Copy storage object's data to the new object using COPY.""" |
| |
| url = "{0}/{1}".format(container, src_object_name) |
| headers = {} |
| headers['Destination'] = "%s/%s" % (str(container), |
| str(dest_object_name)) |
| if metadata: |
| for key in metadata: |
| headers[str(key)] = metadata[key] |
| |
| resp, body = self.copy(url, headers=headers) |
| self.expected_success(201, resp.status) |
| return resp, body |
| |
| def create_object_segments(self, container, object_name, segment, data): |
| """Creates object segments.""" |
| url = "{0}/{1}/{2}".format(container, object_name, segment) |
| resp, body = self.put(url, data) |
| self.expected_success(201, resp.status) |
| return resp, body |
| |
| def put_object_with_chunk(self, container, name, contents, chunk_size): |
| """Put an object with Transfer-Encoding header""" |
| headers = {'Transfer-Encoding': 'chunked'} |
| if self.token: |
| headers['X-Auth-Token'] = self.token |
| |
| conn = put_object_connection(self.base_url, container, name, contents, |
| chunk_size, headers) |
| |
| resp = conn.getresponse() |
| body = resp.read() |
| |
| resp_headers = {} |
| for header, value in resp.getheaders(): |
| resp_headers[header.lower()] = value |
| |
| self._error_checker('PUT', None, headers, contents, resp, body) |
| self.expected_success(201, resp.status) |
| return resp.status, resp.reason, resp_headers |
| |
| def create_object_continue(self, container, object_name, |
| data, metadata=None): |
| """Create storage object.""" |
| headers = {} |
| if metadata: |
| for key in metadata: |
| headers[str(key)] = metadata[key] |
| |
| if not data: |
| headers['content-length'] = '0' |
| |
| headers['X-Auth-Token'] = self.token |
| |
| conn = put_object_connection(self.base_url, str(container), |
| str(object_name), data, None, headers) |
| |
| response = conn.response_class(conn.sock, |
| strict=conn.strict, |
| method=conn._method) |
| version, status, reason = response._read_status() |
| resp = {'version': version, |
| 'status': str(status), |
| 'reason': reason} |
| |
| return resp |
| |
| |
| def put_object_connection(base_url, container, name, contents=None, |
| chunk_size=65536, headers=None, query_string=None): |
| """Helper function to make connection to put object with httplib |
| |
| :param base_url: base_url of an object client |
| :param container: container name that the object is in |
| :param name: object name to put |
| :param contents: a string or a file like object to read object data |
| from; if None, a zero-byte put will be done |
| :param chunk_size: chunk size of data to write; it defaults to 65536; |
| used only if the contents object has a 'read' |
| method, eg. file-like objects, ignored otherwise |
| :param headers: additional headers to include in the request, if any |
| :param query_string: if set will be appended with '?' to generated path |
| """ |
| parsed = urlparse.urlparse(base_url) |
| if parsed.scheme == 'https': |
| conn = httplib.HTTPSConnection(parsed.netloc) |
| else: |
| conn = httplib.HTTPConnection(parsed.netloc) |
| path = str(parsed.path) + "/" |
| path += "%s/%s" % (str(container), str(name)) |
| |
| if query_string: |
| path += '?' + query_string |
| if headers: |
| headers = dict(headers) |
| else: |
| headers = {} |
| if hasattr(contents, 'read'): |
| conn.putrequest('PUT', path) |
| for header, value in six.iteritems(headers): |
| conn.putheader(header, value) |
| if 'Content-Length' not in headers: |
| if 'Transfer-Encoding' not in headers: |
| conn.putheader('Transfer-Encoding', 'chunked') |
| conn.endheaders() |
| chunk = contents.read(chunk_size) |
| while chunk: |
| conn.send('%x\r\n%s\r\n' % (len(chunk), chunk)) |
| chunk = contents.read(chunk_size) |
| conn.send('0\r\n\r\n') |
| else: |
| conn.endheaders() |
| left = headers['Content-Length'] |
| while left > 0: |
| size = chunk_size |
| if size > left: |
| size = left |
| chunk = contents.read(size) |
| conn.send(chunk) |
| left -= len(chunk) |
| else: |
| conn.request('PUT', path, contents, headers) |
| |
| return conn |