diff --git a/rally-jobs/cinder.yaml b/rally-jobs/cinder.yaml index 62168db2..0a207d43 100644 --- a/rally-jobs/cinder.yaml +++ b/rally-jobs/cinder.yaml @@ -92,6 +92,10 @@ users: tenants: 2 users_per_tenant: 2 + api_versions: + cinder: + version: 2 + service_name: cinderv2 sla: failure_rate: max: 0 diff --git a/rally/plugins/openstack/scenarios/cinder/utils.py b/rally/plugins/openstack/scenarios/cinder/utils.py index 32fb1952..b18d109c 100644 --- a/rally/plugins/openstack/scenarios/cinder/utils.py +++ b/rally/plugins/openstack/scenarios/cinder/utils.py @@ -20,6 +20,7 @@ from oslo_config import cfg from rally import exceptions from rally.plugins.openstack import scenario +from rally.plugins.openstack.wrappers import cinder as cinder_wrapper from rally.task import atomic from rally.task import utils as bench_utils @@ -127,13 +128,12 @@ class CinderScenario(scenario.OpenStackScenario): :param kwargs: Other optional parameters to initialize the volume :returns: Created volume object """ - kwargs["display_name"] = kwargs.get("display_name", - self.generate_random_name()) - if isinstance(size, dict): size = random.randint(size["min"], size["max"]) - volume = self.clients("cinder").volumes.create(size, **kwargs) + client = cinder_wrapper.wrap(self._clients.cinder) + volume = client.create_volume(size, **kwargs) + # NOTE(msdubov): It is reasonable to wait 5 secs before starting to # check whether the volume is ready => less API calls. time.sleep(CONF.benchmark.cinder_volume_create_prepoll_delay) @@ -157,8 +157,8 @@ class CinderScenario(scenario.OpenStackScenario): :param volume: volume object :param update_volume_args: dict, contains values to be updated. """ - update_volume_args["display_name"] = self.generate_random_name() - self.clients("cinder").volumes.update(volume, **update_volume_args) + client = cinder_wrapper.wrap(self._clients.cinder) + client.update_volume(volume, **update_volume_args) @atomic.action_timer("cinder.delete_volume") def _delete_volume(self, volume): @@ -256,11 +256,11 @@ class CinderScenario(scenario.OpenStackScenario): :param kwargs: Other optional parameters to initialize the volume :returns: Created snapshot object """ - kwargs["display_name"] = kwargs.get("display_name", - self.generate_random_name()) kwargs["force"] = force - snapshot = self.clients("cinder").volume_snapshots.create(volume_id, - **kwargs) + + client = cinder_wrapper.wrap(self._clients.cinder) + snapshot = client.create_snapshot(volume_id, **kwargs) + time.sleep(CONF.benchmark.cinder_volume_create_prepoll_delay) snapshot = bench_utils.wait_for( snapshot, diff --git a/rally/plugins/openstack/wrappers/cinder.py b/rally/plugins/openstack/wrappers/cinder.py new file mode 100644 index 00000000..a3c4dad3 --- /dev/null +++ b/rally/plugins/openstack/wrappers/cinder.py @@ -0,0 +1,101 @@ +# Copyright 2014: Mirantis Inc. +# 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 abc + +from rally.common import log as logging +from rally.common import utils +from rally import exceptions + +import six + +LOG = logging.getLogger(__name__) + + +@six.add_metaclass(abc.ABCMeta) +class CinderWrapper(object): + def __init__(self, client): + self.client = client + + @abc.abstractmethod + def create_volume(self, volume): + """Creates new volume.""" + + @abc.abstractmethod + def update_volume(self, volume): + """Updates name and description for this volume.""" + + @abc.abstractmethod + def create_snapshot(self, volume_id): + """Creates a volume snapshot.""" + + +class CinderV1Wrapper(CinderWrapper): + def __init__(self, *args, **kwargs): + super(CinderV1Wrapper, self).__init__(*args, **kwargs) + + def create_volume(self, size, **kwargs): + kwargs["display_name"] = kwargs.get("display_name", + utils.generate_random_name()) + volume = self.client.volumes.create(size, **kwargs) + return volume + + def update_volume(self, volume, **update_args): + update_args["display_name"] = ( + update_args.get("display_name", utils.generate_random_name())) + update_args["display_description"] = ( + update_args.get("display_description")) + self.client.volumes.update(volume, **update_args) + + def create_snapshot(self, volume_id, **kwargs): + kwargs["display_name"] = kwargs.get("display_name", + utils.generate_random_name()) + snapshot = self.client.volume_snapshots.create(volume_id, **kwargs) + return snapshot + + +class CinderV2Wrapper(CinderWrapper): + def __init__(self, *args, **kwargs): + super(CinderV2Wrapper, self).__init__(*args, **kwargs) + + def create_volume(self, size, **kwargs): + kwargs["name"] = kwargs.get("name", utils.generate_random_name()) + + volume = self.client.volumes.create(size, **kwargs) + return volume + + def update_volume(self, volume, **update_args): + update_args["name"] = update_args.get("name", + utils.generate_random_name()) + update_args["description"] = update_args.get("description") + self.client.volumes.update(volume, **update_args) + + def create_snapshot(self, volume_id, **kwargs): + kwargs["name"] = kwargs.get("name", utils.generate_random_name()) + snapshot = self.client.volume_snapshots.create(volume_id, **kwargs) + return snapshot + + +def wrap(client): + """Returns cinderclient wrapper based on cinder client version.""" + version = client.choose_version() + if version == "1": + return CinderV1Wrapper(client()) + elif version == "2": + return CinderV2Wrapper(client()) + else: + msg = "This version of API %s could not be identified." % version + LOG.warning(msg) + raise exceptions.InvalidArgumentsException(msg) diff --git a/tests/unit/plugins/openstack/scenarios/cinder/test_utils.py b/tests/unit/plugins/openstack/scenarios/cinder/test_utils.py index 45f154ce..e5a3f484 100644 --- a/tests/unit/plugins/openstack/scenarios/cinder/test_utils.py +++ b/tests/unit/plugins/openstack/scenarios/cinder/test_utils.py @@ -17,6 +17,7 @@ import mock from oslo_config import cfg from rally import exceptions +from rally import osclients from rally.plugins.openstack.scenarios.cinder import utils from tests.unit import fakes from tests.unit import test @@ -29,7 +30,12 @@ class CinderScenarioTestCase(test.ScenarioTestCase): def setUp(self): super(CinderScenarioTestCase, self).setUp() - self.scenario = utils.CinderScenario(self.context) + wrap = mock.patch("rally.plugins.openstack.wrappers.cinder.wrap") + self.mock_wrap = wrap.start() + self.addCleanup(self.mock_wrap.stop) + self.scenario = utils.CinderScenario( + self.context, + clients=osclients.Clients(fakes.FakeUserContext.user["endpoint"])) def test__list_volumes(self): return_volumes_list = self.scenario._list_volumes() @@ -91,7 +97,7 @@ class CinderScenarioTestCase(test.ScenarioTestCase): def test__create_volume(self): return_volume = self.scenario._create_volume(1) self.mock_wait_for.mock.assert_called_once_with( - self.clients("cinder").volumes.create.return_value, + self.mock_wrap.return_value.create_volume.return_value, is_ready=self.mock_resource_is.mock.return_value, update_resource=self.mock_get_from_manager.mock.return_value, timeout=CONF.benchmark.cinder_volume_create_timeout, @@ -111,11 +117,11 @@ class CinderScenarioTestCase(test.ScenarioTestCase): size={"min": 1, "max": 5}, display_name="TestVolume") - self.clients("cinder").volumes.create.assert_called_once_with( + self.mock_wrap.return_value.create_volume.assert_called_once_with( 3, display_name="TestVolume") self.mock_wait_for.mock.assert_called_once_with( - self.clients("cinder").volumes.create.return_value, + self.mock_wrap.return_value.create_volume.return_value, is_ready=self.mock_resource_is.mock.return_value, update_resource=self.mock_get_from_manager.mock.return_value, timeout=CONF.benchmark.cinder_volume_create_timeout, @@ -131,12 +137,11 @@ class CinderScenarioTestCase(test.ScenarioTestCase): fake_volume = mock.MagicMock() volume_update_args = {"display_name": "_updated", "display_description": "_updated"} - self.scenario.generate_random_name = mock.Mock() self.scenario._update_volume(fake_volume, **volume_update_args) - self.clients("cinder").volumes.update.assert_called_once_with( + self.mock_wrap.return_value.update_volume.assert_called_once_with( fake_volume, - display_name=self.scenario.generate_random_name.return_value, + display_name="_updated", display_description="_updated") self._test_atomic_action_timer(self.scenario.atomic_actions(), "cinder.update_volume") @@ -233,7 +238,7 @@ class CinderScenarioTestCase(test.ScenarioTestCase): return_snapshot = self.scenario._create_snapshot("uuid", False) self.mock_wait_for.mock.assert_called_once_with( - self.clients("cinder").volume_snapshots.create.return_value, + self.mock_wrap.return_value.create_snapshot.return_value, is_ready=self.mock_resource_is.mock.return_value, update_resource=self.mock_get_from_manager.mock.return_value, timeout=cfg.CONF.benchmark.cinder_volume_create_timeout, diff --git a/tests/unit/plugins/openstack/wrappers/test_cinder.py b/tests/unit/plugins/openstack/wrappers/test_cinder.py new file mode 100644 index 00000000..420ae706 --- /dev/null +++ b/tests/unit/plugins/openstack/wrappers/test_cinder.py @@ -0,0 +1,77 @@ +# Copyright 2014: Mirantis Inc. +# 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 rally import exceptions +from rally.plugins.openstack.wrappers import cinder as cinder_wrapper +from tests.unit import test + + +class CinderWrapperTestBase(object): + def test_wrap(self): + client = mock.MagicMock() + client.version = "dummy" + self.assertRaises(exceptions.InvalidArgumentsException, + cinder_wrapper.wrap, client) + + +class CinderV1WrapperTestCase(test.TestCase, CinderWrapperTestBase): + def setUp(self): + super(CinderV1WrapperTestCase, self).setUp() + self.client = mock.MagicMock() + self.client.choose_version.return_value = "1" + self.wrapped_client = cinder_wrapper.wrap(self.client) + + def test_create_volume(self): + self.wrapped_client.create_volume(1, display_name="fake_vol") + self.client.return_value.volumes.create.assert_called_once_with( + 1, display_name="fake_vol") + + def test_update_volume(self): + self.wrapped_client.update_volume("fake_id", display_name="fake_vol", + display_description="_updated") + self.client.return_value.volumes.update.assert_called_once_with( + "fake_id", display_name="fake_vol", display_description="_updated") + + def test_create_snapshot(self): + self.wrapped_client.create_snapshot("fake_id", + display_name="fake_snap") + (self.client.return_value.volume_snapshots.create. + assert_called_once_with("fake_id", display_name="fake_snap")) + + +class CinderV2WrapperTestCase(test.TestCase, CinderWrapperTestBase): + def setUp(self): + super(CinderV2WrapperTestCase, self).setUp() + self.client = mock.MagicMock() + self.client.choose_version.return_value = "2" + self.wrapped_client = cinder_wrapper.wrap(self.client) + + def test_create_volume(self): + self.wrapped_client.create_volume(1, name="fake_vol") + self.client.return_value.volumes.create.assert_called_once_with( + 1, name="fake_vol") + + def test_create_snapshot(self): + self.wrapped_client.create_snapshot("fake_id", name="fake_snap") + (self.client.return_value.volume_snapshots.create. + assert_called_once_with("fake_id", name="fake_snap")) + + def test_update_volume(self): + self.wrapped_client.update_volume("fake_id", name="fake_vol", + description="_updated") + self.client.return_value.volumes.update.assert_called_once_with( + "fake_id", name="fake_vol", description="_updated")