diff --git a/rally_openstack/platforms/existing.py b/rally_openstack/platforms/existing.py index 220bdfa4..824e42d5 100644 --- a/rally_openstack/platforms/existing.py +++ b/rally_openstack/platforms/existing.py @@ -181,13 +181,15 @@ class OpenStack(platform.Platform): users_to_check = self.platform_data["users"] if self.platform_data["admin"]: users_to_check.append(self.platform_data["admin"]) - + clients = None for user in users_to_check: + user["api_info"] = self.platform_data.get("api_info", {}) try: + clients = osclients.Clients(user) if self.platform_data["admin"] == user: - osclients.Clients(user).verified_keystone() + clients.verified_keystone() else: - osclients.Clients(user).keystone() + clients.keystone() except osclients.exceptions.RallyException as e: # all rally native exceptions should provide user-friendly # messages @@ -216,6 +218,36 @@ class OpenStack(platform.Platform): "traceback": traceback.format_exc() } + for name in self.platform_data.get("api_info", {}): + if name == "keystone": + continue + if not hasattr(clients, name): + return { + "available": False, + "message": ("There is no OSClient plugin '%s' for" + " communicating with OpenStack API." + % name)} + client = getattr(clients, name) + try: + client.validate_version(client.choose_version()) + client.create_client() + except osclients.exceptions.RallyException as e: + return { + "available": False, + "message": ("Invalid setting for '%(client)s':" + " %(error)s") % { + "client": name, "error": e.format_message()} + } + except Exception: + return { + "available": False, + "message": ("Can not create '%(client)s' with" + " %(version)s version.") % { + "client": name, + "version": client.choose_version()}, + "traceback": traceback.format_exc() + } + return {"available": True} def info(self): diff --git a/tests/functional/extra/fake_dir/fake_plugin.py b/tests/functional/extra/fake_dir/fake_plugin.py index 1995623d..1d69702c 100644 --- a/tests/functional/extra/fake_dir/fake_plugin.py +++ b/tests/functional/extra/fake_dir/fake_plugin.py @@ -26,6 +26,14 @@ class FakeDummy(osclients.OSClient): return {"version": version, "service_type": service_type} +@osclients.configure("faileddummy", default_version="1", + default_service_type="faileddummy", + supported_versions=["1", "2"]) +class FailedDummy(osclients.OSClient): + def create_client(self, version=None, service_type=None): + raise Exception("Failed Dummy") + + @scenario.configure(name="FakeDummy.openstack_api") class FakeDummyOpenstackAPI(scenario.OpenStackScenario): diff --git a/tests/functional/test_cli_env.py b/tests/functional/test_cli_env.py index 083aa11a..3ad42b5c 100644 --- a/tests/functional/test_cli_env.py +++ b/tests/functional/test_cli_env.py @@ -121,3 +121,74 @@ class EnvTestCase(unittest.TestCase): self.assertEqual( TEST_ENV["OS_AUTH_URL"], config["existing@openstack"]["auth_url"]) + + def test_check_api_info_success(self): + rally = utils.Rally() + spec = copy.deepcopy(rally.env_spec) + spec["existing@openstack"]["api_info"] = { + "fakedummy": { + "version": "2", + "service_type": "dummyv2" + } + } + spec = utils.JsonTempFile(spec) + rally("env create --name t_create_env_with_api_info" + " --spec %s" % spec.filename) + plugings = "tests/functional/extra/fake_dir/fake_plugin.py" + rally("--plugin-paths %s env check" % plugings) + + def test_check_api_info_fail_1(self): + rally = utils.Rally() + spec = copy.deepcopy(rally.env_spec) + spec["existing@openstack"]["api_info"] = { + "fakedummy": { + "version": "3", + "service_type": "dummyv2" + } + } + spec = utils.JsonTempFile(spec) + rally("env create --name t_create_env_with_api_info" + " --spec %s" % spec.filename) + try: + plugings = "tests/functional/extra/fake_dir/fake_plugin.py" + rally("--plugin-paths %s env check" % plugings) + except utils.RallyCliError as e: + self.assertIn("Invalid setting for 'fakedummy':", e.output) + + def test_check_api_info_fail_2(self): + rally = utils.Rally() + spec = copy.deepcopy(rally.env_spec) + spec["existing@openstack"]["api_info"] = { + "noneclient": { + "version": "1", + "service_type": "none" + } + } + spec = utils.JsonTempFile(spec) + rally("env create --name t_create_env_with_api_info" + " --spec %s" % spec.filename) + try: + plugings = "tests/functional/extra/fake_dir/fake_plugin.py" + rally("--plugin-paths %s env check" % plugings) + except utils.RallyCliError as e: + self.assertIn("There is no OSClient plugin 'noneclient'", + e.output) + + def test_check_api_info_fail_3(self): + rally = utils.Rally() + spec = copy.deepcopy(rally.env_spec) + spec["existing@openstack"]["api_info"] = { + "faileddummy": { + "version": "2", + "service_type": "dummy" + } + } + spec = utils.JsonTempFile(spec) + rally("env create --name t_create_env_with_api_info" + " --spec %s" % spec.filename) + try: + plugings = "tests/functional/extra/fake_dir/fake_plugin.py" + rally("--plugin-paths %s env check" % plugings) + except utils.RallyCliError as e: + self.assertIn("Can not create 'faileddummy' with 2 version", + e.output) diff --git a/tests/unit/platforms/test_existing.py b/tests/unit/platforms/test_existing.py index 20437d20..05af34a1 100644 --- a/tests/unit/platforms/test_existing.py +++ b/tests/unit/platforms/test_existing.py @@ -298,7 +298,8 @@ class ExistingPlatformTestCase(PlatformBaseTestCase): {"available": False, "message": "Bad admin creds: \n%s" - % json.dumps({"username": "balbab", "password": "***"}, + % json.dumps({"username": "balbab", "password": "***", + "api_info": {}}, indent=2, sort_keys=True), "traceback": mock.ANY}, result) @@ -315,12 +316,65 @@ class ExistingPlatformTestCase(PlatformBaseTestCase): {"available": False, "message": "Bad user creds: \n%s" - % json.dumps({"username": "balbab", "password": "***"}, + % json.dumps({"username": "balbab", "password": "***", + "api_info": {}}, indent=2, sort_keys=True), "traceback": mock.ANY}, result) self.assertIn("Traceback (most recent call last)", result["traceback"]) + @mock.patch("rally_openstack.osclients.Clients") + def test_check_health_with_api_info(self, mock_clients): + pdata = {"admin": mock.MagicMock(), + "users": [], + "api_info": {"fakeclient": "version"}} + result = existing.OpenStack({}, platform_data=pdata).check_health() + self._check_health_schema(result) + self.assertEqual({"available": True}, result) + mock_clients.assert_has_calls( + [mock.call(pdata["admin"]), mock.call().verified_keystone(), + mock.call().fakeclient.choose_version(), + mock.call().fakeclient.validate_version( + mock.call().fakeclient.choose_version.return_value), + mock.call().fakeclient.create_client()]) + + @mock.patch("rally_openstack.osclients.Clients") + def test_check_version_failed_with_api_info(self, mock_clients): + pdata = {"admin": mock.MagicMock(), + "users": [], + "api_info": {"fakeclient": "version"}} + + def validate_version(version): + raise exceptions.RallyException("Version is not supported.") + (mock_clients.return_value.fakeclient + .validate_version) = validate_version + result = existing.OpenStack({}, platform_data=pdata).check_health() + self._check_health_schema(result) + self.assertEqual({"available": False, + "message": ("Invalid setting for 'fakeclient':" + " Version is not supported.")}, + result) + + @mock.patch("rally_openstack.osclients.Clients") + def test_check_unexpected_failed_with_api_info(self, mock_clients): + pdata = {"admin": mock.MagicMock(), + "users": [], + "api_info": {"fakeclient": "version"}} + + def create_client(): + raise Exception("Invalid client.") + + (mock_clients.return_value.fakeclient + .choose_version.return_value) = "1.0" + mock_clients.return_value.fakeclient.create_client = create_client + result = existing.OpenStack({}, platform_data=pdata).check_health() + self._check_health_schema(result) + self.assertEqual({"available": False, + "message": ("Can not create 'fakeclient' with" + " 1.0 version."), + "traceback": mock.ANY}, + result) + @mock.patch("rally_openstack.osclients.Clients") def test_info(self, mock_clients): mock_clients.return_value.services.return_value = {