
Make the list methods naming and doc string consistent for container clients. Partially implements blueprint consistent-service-method-names Change-Id: Ibc770eef75a74880231d18670478e6ec99c90d1f
178 lines
7.0 KiB
Python
178 lines
7.0 KiB
Python
# Copyright 2013 NTT Corporation
|
|
#
|
|
# 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 hashlib
|
|
|
|
from oslo_serialization import jsonutils as json
|
|
|
|
from tempest.api.object_storage import base
|
|
from tempest.common import custom_matchers
|
|
from tempest.common import utils
|
|
from tempest.lib.common.utils import data_utils
|
|
from tempest.lib.common.utils import test_utils
|
|
from tempest.lib import decorators
|
|
|
|
# Each segment, except for the final one, must be at least 1 megabyte
|
|
MIN_SEGMENT_SIZE = 1024 * 1024
|
|
|
|
|
|
class ObjectSloTest(base.BaseObjectTest):
|
|
|
|
def setUp(self):
|
|
super(ObjectSloTest, self).setUp()
|
|
self.container_name = self.create_container()
|
|
self.objects = []
|
|
|
|
def tearDown(self):
|
|
for obj in self.objects:
|
|
test_utils.call_and_ignore_notfound_exc(
|
|
self.object_client.delete_object,
|
|
self.container_name, obj)
|
|
self.container_client.delete_container(self.container_name)
|
|
super(ObjectSloTest, self).tearDown()
|
|
|
|
def _create_object(self, container_name, object_name, data, params=None):
|
|
resp, _ = self.object_client.create_object(container_name,
|
|
object_name,
|
|
data,
|
|
params)
|
|
self.objects.append(object_name)
|
|
|
|
return resp
|
|
|
|
def _create_manifest(self):
|
|
# Create a manifest file for SLO uploading
|
|
object_name = data_utils.rand_name(name='TestObject')
|
|
object_name_base_1 = object_name + '_01'
|
|
object_name_base_2 = object_name + '_02'
|
|
data_size = MIN_SEGMENT_SIZE
|
|
self.content = data_utils.random_bytes(data_size)
|
|
self._create_object(self.container_name,
|
|
object_name_base_1,
|
|
self.content)
|
|
self._create_object(self.container_name,
|
|
object_name_base_2,
|
|
self.content)
|
|
|
|
path_object_1 = '/%s/%s' % (self.container_name,
|
|
object_name_base_1)
|
|
path_object_2 = '/%s/%s' % (self.container_name,
|
|
object_name_base_2)
|
|
data_manifest = [{'path': path_object_1,
|
|
'etag': hashlib.md5(self.content).hexdigest(),
|
|
'size_bytes': data_size},
|
|
{'path': path_object_2,
|
|
'etag': hashlib.md5(self.content).hexdigest(),
|
|
'size_bytes': data_size}]
|
|
|
|
return json.dumps(data_manifest)
|
|
|
|
def _create_large_object(self):
|
|
# Create a large object for preparation of testing various SLO
|
|
# features
|
|
manifest = self._create_manifest()
|
|
|
|
params = {'multipart-manifest': 'put'}
|
|
object_name = data_utils.rand_name(name='TestObject')
|
|
self._create_object(self.container_name,
|
|
object_name,
|
|
manifest,
|
|
params)
|
|
return object_name
|
|
|
|
def _assertHeadersSLO(self, resp, method):
|
|
# When sending GET or HEAD requests to SLO the response contains
|
|
# 'X-Static-Large-Object' header
|
|
if method in ('GET', 'HEAD'):
|
|
self.assertIn('x-static-large-object', resp)
|
|
self.assertEqual(resp['x-static-large-object'], 'True')
|
|
|
|
# Etag value of a large object is enclosed in double-quotations.
|
|
# After etag quotes are checked they are removed and the response is
|
|
# checked if all common headers are present and well formatted
|
|
self.assertTrue(resp['etag'].startswith('\"'))
|
|
self.assertTrue(resp['etag'].endswith('\"'))
|
|
resp['etag'] = resp['etag'].strip('"')
|
|
self.assertHeaders(resp, 'Object', method)
|
|
|
|
@decorators.idempotent_id('2c3f24a6-36e8-4711-9aa2-800ee1fc7b5b')
|
|
@utils.requires_ext(extension='slo', service='object')
|
|
def test_upload_manifest(self):
|
|
# create static large object from multipart manifest
|
|
manifest = self._create_manifest()
|
|
|
|
params = {'multipart-manifest': 'put'}
|
|
object_name = data_utils.rand_name(name='TestObject')
|
|
resp = self._create_object(self.container_name,
|
|
object_name,
|
|
manifest,
|
|
params)
|
|
|
|
self._assertHeadersSLO(resp, 'PUT')
|
|
|
|
@decorators.idempotent_id('e69ad766-e1aa-44a2-bdd2-bf62c09c1456')
|
|
@utils.requires_ext(extension='slo', service='object')
|
|
def test_list_large_object_metadata(self):
|
|
# list static large object metadata using multipart manifest
|
|
object_name = self._create_large_object()
|
|
|
|
resp, _ = self.object_client.list_object_metadata(
|
|
self.container_name,
|
|
object_name)
|
|
|
|
self._assertHeadersSLO(resp, 'HEAD')
|
|
|
|
@decorators.idempotent_id('49bc49bc-dd1b-4c0f-904e-d9f10b830ee8')
|
|
@utils.requires_ext(extension='slo', service='object')
|
|
def test_retrieve_large_object(self):
|
|
# list static large object using multipart manifest
|
|
object_name = self._create_large_object()
|
|
|
|
resp, body = self.object_client.get_object(
|
|
self.container_name,
|
|
object_name)
|
|
|
|
self._assertHeadersSLO(resp, 'GET')
|
|
|
|
sum_data = self.content + self.content
|
|
self.assertEqual(body, sum_data)
|
|
|
|
@decorators.idempotent_id('87b6dfa1-abe9-404d-8bf0-6c3751e6aa77')
|
|
@utils.requires_ext(extension='slo', service='object')
|
|
def test_delete_large_object(self):
|
|
# delete static large object using multipart manifest
|
|
object_name = self._create_large_object()
|
|
|
|
params_del = {'multipart-manifest': 'delete'}
|
|
resp, _ = self.object_client.delete_object(
|
|
self.container_name,
|
|
object_name,
|
|
params=params_del)
|
|
|
|
# When deleting SLO using multipart manifest, the response contains
|
|
# not 'content-length' but 'transfer-encoding' header. This is the
|
|
# special case, therefore the existence of response headers is checked
|
|
# outside of custom matcher.
|
|
self.assertIn('transfer-encoding', resp)
|
|
self.assertIn('content-type', resp)
|
|
self.assertIn('x-trans-id', resp)
|
|
self.assertIn('date', resp)
|
|
|
|
# Check only the format of common headers with custom matcher
|
|
self.assertThat(resp, custom_matchers.AreAllWellFormatted())
|
|
|
|
resp, body = self.container_client.list_container_objects(
|
|
self.container_name)
|
|
self.assertEqual(int(resp['x-container-object-count']), 0)
|