Move the object client to tempest.lib

Move the object client to tempest.lib and cleanup the last bits
of code that were required to manage internal-only tempest service
clients.

Change-Id: Ia9aeab78e530c798dfa7b7d6f71e4b3ea3a84b7f
This commit is contained in:
Andrea Frittoli 2017-10-11 10:23:17 +00:00 committed by Ghanshyam Mann
parent f36476e65a
commit 986407ddd3
13 changed files with 149 additions and 73 deletions

View File

@ -8,3 +8,4 @@ features:
* account_client * account_client
* container_client * container_client
* object_client

View File

@ -17,7 +17,6 @@ from tempest import config
from tempest.lib import auth from tempest.lib import auth
from tempest.lib import exceptions as lib_exc from tempest.lib import exceptions as lib_exc
from tempest.lib.services import clients from tempest.lib.services import clients
from tempest.services import object_storage
CONF = config.CONF CONF = config.CONF
@ -281,21 +280,11 @@ class Manager(clients.ServiceClients):
self.snapshots_client_latest = self.snapshots_v3_client self.snapshots_client_latest = self.snapshots_v3_client
def _set_object_storage_clients(self): def _set_object_storage_clients(self):
# NOTE(andreaf) Load configuration from config. Once object storage self.account_client = self.object_storage.AccountClient()
# is in lib, configuration will be pulled directly from the registry self.bulk_client = self.object_storage.BulkMiddlewareClient()
# and this will not be required anymore. self.capabilities_client = self.object_storage.CapabilitiesClient()
params = config.service_client_config('object-storage') self.container_client = self.object_storage.ContainerClient()
self.object_client = self.object_storage.ObjectClient()
self.account_client = object_storage.AccountClient(self.auth_provider,
**params)
self.bulk_client = object_storage.BulkMiddlewareClient(
self.auth_provider, **params)
self.capabilities_client = object_storage.CapabilitiesClient(
self.auth_provider, **params)
self.container_client = object_storage.ContainerClient(
self.auth_provider, **params)
self.object_client = object_storage.ObjectClient(self.auth_provider,
**params)
def get_auth_provider_class(credentials): def get_auth_provider_class(credentials):

View File

@ -76,7 +76,6 @@ from tempest.common import credentials_factory as credentials
from tempest import config from tempest import config
import tempest.lib.common.http import tempest.lib.common.http
from tempest.lib import exceptions as lib_exc from tempest.lib import exceptions as lib_exc
from tempest.services import object_storage
CONF = config.CONF CONF = config.CONF
@ -236,11 +235,10 @@ def verify_api_versions(os, service, update):
def get_extension_client(os, service): def get_extension_client(os, service):
params = config.service_client_config('object-storage')
extensions_client = { extensions_client = {
'nova': os.compute.ExtensionsClient(), 'nova': os.compute.ExtensionsClient(),
'neutron': os.network.ExtensionsClient(), 'neutron': os.network.ExtensionsClient(),
'swift': object_storage.CapabilitiesClient(os.auth_provider, **params), 'swift': os.object_storage.CapabilitiesClient(),
# NOTE: Cinder v3 API is current and v2 and v1 are deprecated. # NOTE: Cinder v3 API is current and v2 and v1 are deprecated.
# V3 extension API is the same as v2, so we reuse the v2 client # V3 extension API is the same as v2, so we reuse the v2 client
# for v3 API also. # for v3 API also.

View File

@ -1384,7 +1384,7 @@ def _register_tempest_service_clients():
module = service_clients[service_client] module = service_clients[service_client]
configs = service_client.split('.')[0] configs = service_client.split('.')[0]
service_client_data = dict( service_client_data = dict(
name=service_client.replace('.', '_'), name=service_client.replace('.', '_').replace('-', '_'),
service_version=service_client, service_version=service_client,
module_path=module.__name__, module_path=module.__name__,
client_names=module.__all__, client_names=module.__all__,

View File

@ -31,6 +31,7 @@ from tempest.lib.services import compute
from tempest.lib.services import identity from tempest.lib.services import identity
from tempest.lib.services import image from tempest.lib.services import image
from tempest.lib.services import network from tempest.lib.services import network
from tempest.lib.services import object_storage
from tempest.lib.services import volume from tempest.lib.services import volume
warnings.simplefilter("once") warnings.simplefilter("once")
@ -50,20 +51,13 @@ def tempest_modules():
'image.v1': image.v1, 'image.v1': image.v1,
'image.v2': image.v2, 'image.v2': image.v2,
'network': network, 'network': network,
'object-storage': object_storage,
'volume.v1': volume.v1, 'volume.v1': volume.v1,
'volume.v2': volume.v2, 'volume.v2': volume.v2,
'volume.v3': volume.v3 'volume.v3': volume.v3
} }
def _tempest_internal_modules():
# Set of unstable service clients available in Tempest
# NOTE(andreaf) This list will exists only as long the remain clients
# are migrated to tempest.lib, and it will then be deleted without
# deprecation or advance notice
return set(['object-storage'])
def available_modules(): def available_modules():
"""Set of service client modules available in Tempest and plugins """Set of service client modules available in Tempest and plugins
@ -101,17 +95,6 @@ def available_modules():
plug_service_versions)) plug_service_versions))
name_conflicts.append(exceptions.PluginRegistrationException( name_conflicts.append(exceptions.PluginRegistrationException(
name=plugin_name, detailed_error=detailed_error)) name=plugin_name, detailed_error=detailed_error))
# NOTE(andreaf) Once all tempest clients are stable, the following
# if will have to be removed.
if not plug_service_versions.isdisjoint(
_tempest_internal_modules()):
detailed_error = (
'Plugin %s is trying to register a service %s already '
'claimed by a Tempest one' % (plugin_name,
_tempest_internal_modules() &
plug_service_versions))
name_conflicts.append(exceptions.PluginRegistrationException(
name=plugin_name, detailed_error=detailed_error))
extra_service_versions |= plug_service_versions extra_service_versions |= plug_service_versions
if name_conflicts: if name_conflicts:
LOG.error( LOG.error(
@ -369,7 +352,7 @@ class ServiceClients(object):
client_parameters = client_parameters or {} client_parameters = client_parameters or {}
self.parameters = {} self.parameters = {}
# Parameters are provided for unversioned services # Parameters are provided for unversioned services
all_modules = available_modules() | _tempest_internal_modules() all_modules = available_modules()
unversioned_services = set( unversioned_services = set(
[x.split('.')[0] for x in all_modules]) [x.split('.')[0] for x in all_modules])
for service in unversioned_services: for service in unversioned_services:
@ -456,9 +439,7 @@ class ServiceClients(object):
@property @property
def registered_services(self): def registered_services(self):
# NOTE(andreaf) Once all tempest modules are stable this needs to return self._registered_services
# be updated to remove _tempest_internal_modules
return self._registered_services | _tempest_internal_modules()
def _setup_parameters(self, parameters): def _setup_parameters(self, parameters):
"""Setup default values for client parameters """Setup default values for client parameters

View File

@ -0,0 +1,25 @@
# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
#
# 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.
from tempest.lib.services.object_storage.account_client import AccountClient
from tempest.lib.services.object_storage.bulk_middleware_client import \
BulkMiddlewareClient
from tempest.lib.services.object_storage.capabilities_client import \
CapabilitiesClient
from tempest.lib.services.object_storage.container_client import \
ContainerClient
from tempest.lib.services.object_storage.object_client import ObjectClient
__all__ = ['AccountClient', 'BulkMiddlewareClient', 'CapabilitiesClient',
'ContainerClient', 'ObjectClient']

View File

@ -1,25 +0,0 @@
# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
#
# 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.
from tempest.lib.services.object_storage.account_client import AccountClient
from tempest.lib.services.object_storage.bulk_middleware_client import \
BulkMiddlewareClient
from tempest.lib.services.object_storage.capabilities_client import \
CapabilitiesClient
from tempest.lib.services.object_storage.container_client import \
ContainerClient
from tempest.services.object_storage.object_client import ObjectClient
__all__ = ['AccountClient', 'BulkMiddlewareClient', 'CapabilitiesClient',
'ContainerClient', 'ObjectClient']

View File

@ -0,0 +1,108 @@
# Copyright 2016 IBM Corp.
# 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 mock
from tempest.lib import exceptions
from tempest.lib.services.object_storage import object_client
from tempest.tests import base
from tempest.tests.lib import fake_auth_provider
class TestObjectClient(base.TestCase):
def setUp(self):
super(TestObjectClient, self).setUp()
self.fake_auth = fake_auth_provider.FakeAuthProvider()
self.url = self.fake_auth.base_url(None)
self.object_client = object_client.ObjectClient(self.fake_auth,
'swift', 'region1')
@mock.patch.object(object_client, '_create_connection')
def test_create_object_continue_no_data(self, mock_poc):
self._validate_create_object_continue(None, mock_poc)
@mock.patch.object(object_client, '_create_connection')
def test_create_object_continue_with_data(self, mock_poc):
self._validate_create_object_continue('hello', mock_poc)
@mock.patch.object(object_client, '_create_connection')
def test_create_continue_with_no_continue_received(self, mock_poc):
self._validate_create_object_continue('hello', mock_poc,
initial_status=201)
def _validate_create_object_continue(self, req_data,
mock_poc, initial_status=100):
expected_hdrs = {
'X-Auth-Token': self.fake_auth.get_token(),
'content-length': 0 if req_data is None else len(req_data),
'Expect': '100-continue'}
# Setup the Mocks prior to invoking the object creation
mock_resp_cls = mock.Mock()
mock_resp_cls._read_status.return_value = ("1", initial_status, "OK")
mock_poc.return_value.response_class.return_value = mock_resp_cls
# This is the final expected return value
mock_poc.return_value.getresponse.return_value.status = 201
mock_poc.return_value.getresponse.return_value.reason = 'OK'
# Call method to PUT object using expect:100-continue
cnt = "container1"
obj = "object1"
path = "/%s/%s" % (cnt, obj)
# If the expected initial status is not 100, then an exception
# should be thrown and the connection closed
if initial_status is 100:
status, reason = \
self.object_client.create_object_continue(cnt, obj, req_data)
else:
self.assertRaises(exceptions.UnexpectedResponseCode,
self.object_client.create_object_continue, cnt,
obj, req_data)
mock_poc.return_value.close.assert_called_once_with()
# Verify that putrequest is called 1 time with the appropriate values
mock_poc.return_value.putrequest.assert_called_once_with('PUT', path)
# Verify that headers were written, including "Expect:100-continue"
calls = []
for header, value in expected_hdrs.items():
calls.append(mock.call(header, value))
mock_poc.return_value.putheader.assert_has_calls(calls, False)
mock_poc.return_value.endheaders.assert_called_once_with()
# The following steps are only taken if the initial status is 100
if initial_status is 100:
# Verify that the method returned what it was supposed to
self.assertEqual(status, 201)
# Verify that _safe_read was called once to remove the CRLF
# after the 100 response
mock_rc = mock_poc.return_value.response_class.return_value
mock_rc._safe_read.assert_called_once_with(2)
# Verify the actual data was written via send
mock_poc.return_value.send.assert_called_once_with(req_data)
# Verify that the getresponse method was called to receive
# the final
mock_poc.return_value.getresponse.assert_called_once_with()

View File

@ -38,7 +38,7 @@ class RegistryFixture(fixtures.Fixture):
"""Initialise the registry fixture""" """Initialise the registry fixture"""
self.services = set(['compute', 'identity.v2', 'identity.v3', self.services = set(['compute', 'identity.v2', 'identity.v3',
'image.v1', 'image.v2', 'network', 'volume.v1', 'image.v1', 'image.v2', 'network', 'volume.v1',
'volume.v2', 'volume.v3']) 'volume.v2', 'volume.v3', 'object-storage'])
def _setUp(self): def _setUp(self):
# Cleanup the registry # Cleanup the registry
@ -50,7 +50,7 @@ class RegistryFixture(fixtures.Fixture):
for sc in self.services: for sc in self.services:
sc_module = service_clients[sc] sc_module = service_clients[sc]
sc_unversioned = sc.split('.')[0] sc_unversioned = sc.split('.')[0]
sc_name = sc.replace('.', '_') sc_name = sc.replace('.', '_').replace('-', '_')
# Pass the bare minimum params to satisfy the clients interface # Pass the bare minimum params to satisfy the clients interface
service_client_data = dict( service_client_data = dict(
name=sc_name, service_version=sc, service=sc_unversioned, name=sc_name, service_version=sc, service=sc_unversioned,

View File

@ -189,9 +189,7 @@ class TestServiceClients(base.TestCase):
def setUp(self): def setUp(self):
super(TestServiceClients, self).setUp() super(TestServiceClients, self).setUp()
self.useFixture(fixtures.MockPatch( self.useFixture(fixtures.MockPatch(
'tempest.lib.services.clients.tempest_modules', return_value={})) 'tempest.lib.services.clients.tempest_modules',
self.useFixture(fixtures.MockPatch(
'tempest.lib.services.clients._tempest_internal_modules',
return_value=set(['fake_service1']))) return_value=set(['fake_service1'])))
def test___init___creds_v2_uri(self): def test___init___creds_v2_uri(self):
@ -416,6 +414,7 @@ class TestServiceClients(base.TestCase):
_manager = self._get_manager() _manager = self._get_manager()
duplicate_service = 'fake_service1' duplicate_service = 'fake_service1'
expected_error = '.*' + duplicate_service expected_error = '.*' + duplicate_service
_manager._registered_services = [duplicate_service]
with testtools.ExpectedException( with testtools.ExpectedException(
exceptions.ServiceClientRegistrationException, expected_error): exceptions.ServiceClientRegistrationException, expected_error):
_manager.register_service_client_module( _manager.register_service_client_module(