diff --git a/rally-jobs/fuel.yaml b/rally-jobs/fuel.yaml index de7bc409c0..c4afe8b78e 100644 --- a/rally-jobs/fuel.yaml +++ b/rally-jobs/fuel.yaml @@ -1,6 +1,5 @@ --- - - FuelEnvironments.list_environments: + FuelEnvironments.create_and_list_environments: - runner: type: "constant" @@ -9,3 +8,15 @@ sla: failure_rate: max: 0 + + FuelEnvironments.create_and_delete_environment: + - + args: + delete_retries: 10 + runner: + type: "constant" + times: 200 + concurrency: 10 + sla: + failure_rate: + max: 0 diff --git a/rally/plugins/openstack/context/cleanup/resources.py b/rally/plugins/openstack/context/cleanup/resources.py index 83fe458d88..07f945cb65 100644 --- a/rally/plugins/openstack/context/cleanup/resources.py +++ b/rally/plugins/openstack/context/cleanup/resources.py @@ -19,6 +19,7 @@ from saharaclient.api import base as saharaclient_base from rally.common import log as logging from rally.plugins.openstack.context.cleanup import base +from rally.plugins.openstack import scenario from rally.plugins.openstack.scenarios.keystone import utils as kutils from rally.plugins.openstack.wrappers import keystone as keystone_wrapper @@ -486,6 +487,28 @@ class IronicNodes(base.ResourceManager): return self.raw_resource.uuid +# FUEL + +@base.resource("fuel", "environment", order=1400, + admin_required=True, perform_for_admin_only=True) +class FuelEnvironment(base.ResourceManager): + """Fuel environment. + + That is the only resource that can be deleted by fuelclient explicitly. + """ + + def id(self): + return self.raw_resource["id"] + + def is_deleted(self): + return not self._manager().get(self.id()) + + def list(self): + return [env for env in self._manager().list() + if env["name"].startswith( + scenario.OpenStackScenario.RESOURCE_NAME_PREFIX)] + + # KEYSTONE _keystone_order = get_order(9000) diff --git a/rally/plugins/openstack/scenarios/fuel/environments.py b/rally/plugins/openstack/scenarios/fuel/environments.py index 8c0ea4b82c..fe5035c589 100644 --- a/rally/plugins/openstack/scenarios/fuel/environments.py +++ b/rally/plugins/openstack/scenarios/fuel/environments.py @@ -18,11 +18,55 @@ from rally.task import validation class FuelEnvironments(utils.FuelScenario): - """Benchmark scenarios for Fuel environments.""" + """Benchmark scenarios for Fuel environments. + + Scenarios take Fuel related parameters: + release_id: OpenStack release available in Fuel + deployment_mode: accepts 'ha_compact' or 'multinode' + network_provider: accepts 'nova' or 'neutron' + net_segment_type: accepts 'gre' or 'vlan' + """ @validation.required_clients("fuel", admin=True) @validation.required_openstack(admin=True) - @scenario.configure() - def list_environments(self): - """List Fuel environments.""" + @scenario.configure(context={"admin_cleanup": ["fuel"]}) + def create_and_delete_environment(self, release_id=1, + network_provider="neutron", + deployment_mode="ha_compact", + net_segment_type="vlan", + delete_retries=5): + """Create and delete Fuel environments. + + :param release_id: release id (default 1) + :param network_provider: network provider (default 'neutron') + :param deployment_mode: deployment mode (default 'ha_compact') + :param net_segment_type: net segment type (default 'vlan') + :param delete_retries: retries count on delete oprations (default 5) + """ + + env_id = self._create_environment(release_id=release_id, + network_provider=network_provider, + deployment_mode=deployment_mode, + net_segment_type=net_segment_type) + self._delete_environment(env_id, delete_retries) + + @validation.required_clients("fuel", admin=True) + @validation.required_openstack(admin=True) + @scenario.configure(context={"admin_cleanup": ["fuel"]}) + def create_and_list_environments(self, release_id=1, + network_provider="neutron", + deployment_mode="ha_compact", + net_segment_type="vlan"): + """Create and list Fuel environments + + :param release_id: release id (default 1) + :param network_provider: network provider (default 'neutron') + :param deployment_mode: deployment mode (default 'ha_compact') + :param net_segment_type: net segment type (default 'vlan') + """ + + self._create_environment(release_id=release_id, + network_provider=network_provider, + deployment_mode=deployment_mode, + net_segment_type=net_segment_type) self._list_environments() diff --git a/rally/plugins/openstack/scenarios/fuel/utils.py b/rally/plugins/openstack/scenarios/fuel/utils.py index b02de0e0ac..545bed537e 100644 --- a/rally/plugins/openstack/scenarios/fuel/utils.py +++ b/rally/plugins/openstack/scenarios/fuel/utils.py @@ -14,14 +14,68 @@ import os +import time import six +from rally.common.i18n import _ from rally import osclients from rally.plugins.openstack import scenario from rally.task import atomic +class FuelEnvManager(object): + + def __init__(self, client): + self.client = client + + def get(self, env_id): + try: + return self.client.get_by_id(env_id) + except BaseException: + return None + + def list(self): + """List Fuel environments.""" + try: + return self.client.get_all() + except SystemExit: + raise RuntimeError(_("Can't list envitonments. " + "Please check server availability.")) + + def create(self, name, release_id=1, + network_provider="neutron", + deployment_mode="ha_compact", + net_segment_type="vlan"): + try: + env = self.client.create(name, release_id, network_provider, + deployment_mode, net_segment_type) + except SystemExit: + raise RuntimeError(_("Something went wrong while creating an " + "environment. This can happen when " + "environment with name %s already exists.") + % name) + + if env: + return env + raise RuntimeError(_("Environment was not created or was " + "created but not returned by server.")) + + def delete(self, env_id, retries=5, retry_pause=0.5): + env = self.get(env_id) + retry_number = 0 + while env: + if retry_number > retries: + raise RuntimeError(_("Can't delete environment " + "id: %s ") % env_id) + try: + self.client.delete_by_id(env_id) + except BaseException: + time.sleep(retry_pause) + env = self.get(env_id) + retry_number += 1 + + class FuelClient(object): """Thin facade over `fuelclient.get_client'.""" @@ -39,8 +93,12 @@ class FuelClient(object): os.environ["KEYSTONE_PASS"] = password import fuelclient + FuelClient.fuelclient_module = fuelclient + get_client = fuelclient.get_client - self.environment = get_client("environment", version=version) + + self.environment = FuelEnvManager(get_client( + "environment", version=version)) self.node = get_client("node", version=version) self.task = get_client("task", version=version) @@ -61,5 +119,24 @@ class FuelScenario(scenario.OpenStackScenario): @atomic.action_timer("fuel.list_environments") def _list_environments(self): - """List Fuel environments.""" - return self.admin_clients("fuel").environment.get_all() + return [env for env in self.admin_clients("fuel").environment.list() + if env["name"].startswith( + scenario.OpenStackScenario.RESOURCE_NAME_PREFIX)] + + @atomic.action_timer("fuel.create_environment") + def _create_environment(self, release_id=1, + network_provider="neutron", + deployment_mode="ha_compact", + net_segment_type="vlan"): + + name = self._generate_random_name( + prefix=scenario.OpenStackScenario.RESOURCE_NAME_PREFIX) + env = self.admin_clients("fuel").environment.create( + name, release_id, network_provider, deployment_mode, + net_segment_type) + return env["id"] + + @atomic.action_timer("fuel.delete_environment") + def _delete_environment(self, env_id, retries=5): + self.admin_clients("fuel").environment.delete( + env_id, retries) diff --git a/samples/tasks/scenarios/fuel/create_and_delete_environments.json b/samples/tasks/scenarios/fuel/create_and_delete_environments.json new file mode 100644 index 0000000000..81f461331b --- /dev/null +++ b/samples/tasks/scenarios/fuel/create_and_delete_environments.json @@ -0,0 +1,19 @@ +{ + "FuelEnvironments.create_and_delete_environment": [ + { + "args": { + "release_id": 1, + "network_provider": "neutron", + "deployment_mode": "ha_compact", + "net_segment_type": "vlan", + "delete_retries": 5 + }, + + "runner": { + "type": "constant", + "times": 200, + "concurrency": 10 + } + } + ] +} diff --git a/samples/tasks/scenarios/fuel/create_and_delete_environments.yaml b/samples/tasks/scenarios/fuel/create_and_delete_environments.yaml new file mode 100644 index 0000000000..0f67ea29df --- /dev/null +++ b/samples/tasks/scenarios/fuel/create_and_delete_environments.yaml @@ -0,0 +1,13 @@ +--- + FuelEnvironments.create_and_delete_environment: + - + args: + release_id: 1 + network_provider: "neutron" + deployment_mode: "ha_compact" + net_segment_type: "vlan" + delete_retries: 5 + runner: + type: "constant" + times: 200 + concurrency: 10 diff --git a/samples/tasks/scenarios/fuel/create_and_list_environments.json b/samples/tasks/scenarios/fuel/create_and_list_environments.json new file mode 100644 index 0000000000..1d182bd8f7 --- /dev/null +++ b/samples/tasks/scenarios/fuel/create_and_list_environments.json @@ -0,0 +1,18 @@ +{ + "FuelEnvironments.create_and_list_environments": [ + { + "args": { + "release_id": 1, + "network_provider": "neutron", + "deployment_mode": "ha_compact", + "net_segment_type": "vlan" + }, + + "runner": { + "type": "constant", + "times": 200, + "concurrency": 10 + } + } + ] +} diff --git a/samples/tasks/scenarios/fuel/create_and_list_environments.yaml b/samples/tasks/scenarios/fuel/create_and_list_environments.yaml new file mode 100644 index 0000000000..84433df662 --- /dev/null +++ b/samples/tasks/scenarios/fuel/create_and_list_environments.yaml @@ -0,0 +1,12 @@ +--- + FuelEnvironments.create_and_list_environments: + - + args: + release_id: 1 + network_provider: "neutron" + deployment_mode: "ha_compact" + net_segment_type: "vlan" + runner: + type: "constant" + times: 200 + concurrency: 10 diff --git a/samples/tasks/scenarios/fuel/list-environments.json b/samples/tasks/scenarios/fuel/list-environments.json deleted file mode 100644 index e746161f45..0000000000 --- a/samples/tasks/scenarios/fuel/list-environments.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "FuelEnvironments.list_environments": [ - { - "runner": { - "type": "constant", - "times": 200, - "concurrency": 10 - } - } - ] -} diff --git a/samples/tasks/scenarios/fuel/list-environments.yaml b/samples/tasks/scenarios/fuel/list-environments.yaml deleted file mode 100644 index 1d71ca5fb4..0000000000 --- a/samples/tasks/scenarios/fuel/list-environments.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- - FuelEnvironments.list_environments: - - - runner: - type: "constant" - times: 200 - concurrency: 10 diff --git a/tests/unit/plugins/openstack/context/cleanup/test_resources.py b/tests/unit/plugins/openstack/context/cleanup/test_resources.py index 45515fc3be..6786b12d91 100644 --- a/tests/unit/plugins/openstack/context/cleanup/test_resources.py +++ b/tests/unit/plugins/openstack/context/cleanup/test_resources.py @@ -523,3 +523,28 @@ class ManilaSecurityServiceTestCase(test.TestCase): self.assertEqual("security_services", ss_resource._resource) ss_resource._manager.return_value.delete.assert_called_once_with( "fake_id") + + +class FuelEnvironmentTestCase(test.TestCase): + + def test_id(self): + fres = resources.FuelEnvironment() + fres.raw_resource = {"id": 42, "name": "chavez"} + self.assertEqual(42, fres.id()) + + @mock.patch("%s.FuelEnvironment._manager" % BASE) + def test_is_deleted(self, mock__manager): + mock__manager.return_value.get.return_value = [] + fres = resources.FuelEnvironment() + fres.id = mock.Mock() + self.assertTrue(fres.is_deleted()) + mock__manager.return_value.get.return_value = ["env"] + self.assertFalse(fres.is_deleted()) + + @mock.patch("%s.FuelEnvironment._manager" % BASE) + def test_list(self, mock__manager): + envs = [{"name": "rally_one"}, {"name": "rally_two"}, + {"name": "three"}] + mock__manager.return_value.list.return_value = envs + fres = resources.FuelEnvironment() + self.assertEqual(envs[:-1], fres.list()) diff --git a/tests/unit/plugins/openstack/scenarios/fuel/test_environments.py b/tests/unit/plugins/openstack/scenarios/fuel/test_environments.py index 059bb8c458..5cca4724cf 100644 --- a/tests/unit/plugins/openstack/scenarios/fuel/test_environments.py +++ b/tests/unit/plugins/openstack/scenarios/fuel/test_environments.py @@ -20,8 +20,31 @@ from tests.unit import test class FuelEnvironmentsTestCase(test.TestCase): - def test_list_environments(self): + def test_create_and_list_environments(self): scenario = environments.FuelEnvironments() + + scenario._create_environment = mock.Mock() scenario._list_environments = mock.Mock() - self.assertIsNone(scenario.list_environments()) + + scenario.create_and_list_environments( + release_id=2, network_provider="test_neutron", + deployment_mode="test_mode", net_segment_type="test_type") + scenario._create_environment.assert_called_once_with( + release_id=2, network_provider="test_neutron", + deployment_mode="test_mode", net_segment_type="test_type") scenario._list_environments.assert_called_once_with() + + def test_create_and_delete_environments(self): + scenario = environments.FuelEnvironments() + + scenario._create_environment = mock.Mock(return_value=42) + scenario._delete_environment = mock.Mock() + + scenario.create_and_delete_environment( + release_id=2, network_provider="test_neutron", + deployment_mode="test_mode", net_segment_type="test_type") + + scenario._create_environment.assert_called_once_with( + release_id=2, network_provider="test_neutron", + deployment_mode="test_mode", net_segment_type="test_type") + scenario._delete_environment.assert_called_once_with(42, 5) diff --git a/tests/unit/plugins/openstack/scenarios/fuel/test_utils.py b/tests/unit/plugins/openstack/scenarios/fuel/test_utils.py index 22e395bb28..b0dd531d24 100644 --- a/tests/unit/plugins/openstack/scenarios/fuel/test_utils.py +++ b/tests/unit/plugins/openstack/scenarios/fuel/test_utils.py @@ -52,10 +52,87 @@ class ModuleTestCase(test.TestCase): self.assertEqual("fuel_client", clients.fuel()) +class FuelEnvTestCase(test.TestCase): + + def test___init__(self): + env = utils.FuelEnvManager("some_client") + self.assertEqual("some_client", env.client) + + def test_get(self): + client = mock.Mock() + fenv = utils.FuelEnvManager(client) + result = fenv.get("some_id") + client.get_by_id.assert_called_once_with("some_id") + self.assertEqual(result, client.get_by_id("some_id")) + client.get_by_id.side_effect = BaseException + self.assertIsNone(fenv.get("some_id")) + + def test_list(self): + client = mock.Mock() + envs = [ + {"name": "one"}, + {"name": "two"}, + {"name": "three"}] + client.get_all.return_value = envs + fenv = utils.FuelEnvManager(client) + self.assertEqual(envs, fenv.list()) + + def test_list_exception(self): + client = mock.Mock() + client.get_all = mock.Mock(side_effect=SystemExit) + fenv = utils.FuelEnvManager(client) + self.assertRaises(RuntimeError, fenv.list) + + def test_create(self): + client = mock.Mock() + client.create.return_value = "env" + fenv = utils.FuelEnvManager(client) + kwargs = {"release_id": 42, "network_provider": "testprov", + "deployment_mode": "some_mode", "net_segment_type": "bar"} + self.assertEqual("env", fenv.create("some_env", **kwargs)) + client.create.assert_called_once_with("some_env", 42, "testprov", + "some_mode", "bar") + client.create.side_effect = SystemExit + self.assertRaises(RuntimeError, fenv.create, "some_env", **kwargs) + + def test_create_env_not_returned(self): + client = mock.Mock() + client.create.return_value = None + kwargs = {"release_id": 42, "network_provider": "testprov", + "deployment_mode": "some_mode", "net_segment_type": "bar"} + fenv = utils.FuelEnvManager(client) + self.assertRaises(RuntimeError, fenv.create, "some_env", **kwargs) + + @mock.patch(UTILS + "scenario.OpenStackScenario") + def test_delete(self, mock_open_stack_scenario): + mock_open_stack_scenario.RESOURCE_NAME_PREFIX = "" + envs = [{"id": "some_one", "name": "one"}] + client = mock.Mock() + client.get_all.return_value = envs + client.delete_by_id.side_effect = SystemExit + + fenv = utils.FuelEnvManager(client) + self.assertRaises(RuntimeError, fenv.delete, "some_one", retries=2) + self.assertEqual(3, len(client.delete_by_id.mock_calls)) + + @mock.patch(UTILS + "scenario.OpenStackScenario") + def test_delete_error(self, mock_open_stack_scenario): + mock_open_stack_scenario.RESOURCE_NAME_PREFIX = "" + envs = [{"id": "some_one", "name": "one"}] + client = mock.Mock() + client.delete_by_id.side_effect = SystemExit + client.get_all.return_value = envs + + fenv = utils.FuelEnvManager(client) + self.assertRaises(RuntimeError, fenv.delete, "some_one", retries=1) + self.assertEqual(2, len(client.delete_by_id.mock_calls)) + + class FuelClientTestCase(test.TestCase): + @mock.patch(UTILS + "FuelEnvManager") @mock.patch(UTILS + "os") - def test___init__(self, mock_os): + def test___init__(self, mock_os, mock_fuel_env_manager): mock_os.environ = {} mock_fuelclient = mock.Mock(get_client=lambda *args, **kw: [args, kw]) with mock.patch.dict("sys.modules", {"fuelclient": mock_fuelclient}): @@ -69,22 +146,51 @@ class FuelClientTestCase(test.TestCase): "LISTEN_PORT": "1234", "SERVER_ADDRESS": "foo_address"} self.assertEqual(expected_environ, mock_os.environ) - self.assertEqual([("environment",), {"version": "foo_version"}], + self.assertEqual(mock_fuel_env_manager.return_value, client.environment) self.assertEqual([("node",), {"version": "foo_version"}], client.node) self.assertEqual([("task",), {"version": "foo_version"}], client.task) + mock_fuel_env_manager.assert_called_once_with( + [("environment",), + {"version": "foo_version"}]) class FuelScenarioTestCase(test.ScenarioTestCase): def test__list_environments(self): + self.admin_clients("fuel").environment.list.return_value = [ + {"name": "some_name1"}, {"name": "rally_name2"}] scenario = utils.FuelScenario() - self.assertEqual( - self.admin_clients("fuel").environment.get_all.return_value, - scenario._list_environments()) - self.admin_clients( - "fuel").environment.get_all.assert_called_once_with() + self.assertEqual([{"name": "rally_name2"}], + scenario._list_environments()) + self.admin_clients("fuel").environment.list.assert_called_once_with() self._test_atomic_action_timer(scenario.atomic_actions(), "fuel.list_environments") + + def test__create_environment(self): + self.admin_clients("fuel").environment.create.return_value = {"id": 42} + + fuel_scenario = utils.FuelScenario() + fuel_scenario.admin_clients = self.admin_clients + + fuel_scenario._generate_random_name = mock.Mock( + return_value="random_name") + result = fuel_scenario._create_environment() + self.assertEqual( + self.admin_clients("fuel").environment.create.return_value["id"], + result) + fuel_scenario._generate_random_name.assert_called_once_with( + prefix=fuel_scenario.RESOURCE_NAME_PREFIX) + tmp_mck = self.admin_clients("fuel").environment.create + tmp_mck.assert_called_once_with( + fuel_scenario._generate_random_name.return_value, 1, "neutron", + "ha_compact", "vlan") + + def test__delete_environment(self): + fuel_scenario = utils.FuelScenario() + fuel_scenario.admin_clients = self.admin_clients + fuel_scenario._delete_environment(42, 33) + tmp_mock = fuel_scenario.admin_clients("fuel") + tmp_mock.environment.delete.assert_called_once_with(42, 33)