Add deployment check subcommand

* Check keystone endpoints and print user-friendly messages so the
user can easily troubleshoot issues,
* List all available services of your deployed OpenStack,
* Refactoring of benchmark.engine.bind in order to avoid redundancy.

Implements blueprint deployment-check-subcommand

Change-Id: Iecfc2e4c093f567b56c3990c598326942c50e34a
This commit is contained in:
Pierre Padrixe
2014-01-24 18:32:01 +01:00
parent e4f58bf63b
commit 00310442a2
6 changed files with 81 additions and 44 deletions

View File

@@ -16,8 +16,6 @@
import json import json
import jsonschema import jsonschema
from keystoneclient import exceptions as keystone_exceptions
from rally.benchmark import base from rally.benchmark import base
from rally.benchmark import runner from rally.benchmark import runner
from rally import consts from rally import consts
@@ -161,18 +159,7 @@ class TestEngine(object):
password=self.endpoint['password'], password=self.endpoint['password'],
tenant_name=self.endpoint['tenant_name'], tenant_name=self.endpoint['tenant_name'],
auth_url=self.endpoint['auth_url']) auth_url=self.endpoint['auth_url'])
clients.get_verified_keystone_client()
try:
# Ensure that user is admin
roles = clients.get_keystone_client().auth_ref['user']['roles']
if not any("admin" == role['name'] for role in roles):
raise exceptions.InvalidAdminException(
username=self.endpoint["username"])
except keystone_exceptions.Unauthorized:
raise exceptions.InvalidEndpointsException()
except keystone_exceptions.AuthorizationFailure:
raise exceptions.HostUnreachableException(
url=self.endpoint['auth_url'])
return self return self
def __enter__(self): def __enter__(self):

View File

@@ -31,6 +31,7 @@ from rally import exceptions
from rally import fileutils from rally import fileutils
from rally.openstack.common.gettextutils import _ from rally.openstack.common.gettextutils import _
from rally.orchestrator import api from rally.orchestrator import api
from rally import osclients
from rally import processing from rally import processing
@@ -145,6 +146,35 @@ class DeploymentCommands(object):
table.add_row([endpoint.get(m, '') for m in headers]) table.add_row([endpoint.get(m, '') for m in headers])
print(table) print(table)
@cliutils.args('--deploy-id', dest='deploy_id', type=str, required=False,
help='UUID of a deployment.')
def check(self, deploy_id=None):
"""Check the deployment.
Check keystone authentication and list all available services.
:param deploy_id: a UUID of the deployment
"""
deploy_id = deploy_id or envutils.default_deployment_id()
headers = ['services', 'type', 'status']
table = prettytable.PrettyTable(headers)
try:
endpoint = db.deployment_get(deploy_id)['endpoint']
clients = osclients.Clients(username=endpoint['username'],
password=endpoint['password'],
tenant_name=endpoint['tenant_name'],
auth_url=endpoint['auth_url'])
client = clients.get_verified_keystone_client()
print("keystone endpoints are valid and following services are "
"available:")
for service in client.service_catalog.get_data():
table.add_row([service['name'], service['type'], 'Available'])
except exceptions.InvalidArgumentsException:
table.add_row(['keystone', 'identity', 'Error'])
print(_("Authentication Issues: %s.")
% sys.exc_info()[1])
print(table)
class TaskCommands(object): class TaskCommands(object):

View File

@@ -17,9 +17,12 @@ import urlparse
from cinderclient import client as cinder from cinderclient import client as cinder
import glanceclient as glance import glanceclient as glance
from keystoneclient import exceptions as keystone_exceptions
from keystoneclient.v2_0 import client as keystone from keystoneclient.v2_0 import client as keystone
from novaclient import client as nova from novaclient import client as nova
from rally import exceptions
class Clients(object): class Clients(object):
"""This class simplify and unify work with openstack python clients.""" """This class simplify and unify work with openstack python clients."""
@@ -42,6 +45,25 @@ class Clients(object):
self.cache["keystone"] = client self.cache["keystone"] = client
return client return client
def get_verified_keystone_client(self):
"""Ensure keystone endpoints are valid and then authenticate
:returns: Keystone Client
"""
try:
# Ensure that user is admin
client = self.get_keystone_client()
roles = client.auth_ref['user']['roles']
if not any('admin' == role['name'] for role in roles):
raise exceptions.InvalidAdminException(
username=self.kw['username'])
except keystone_exceptions.Unauthorized:
raise exceptions.InvalidEndpointsException()
except keystone_exceptions.AuthorizationFailure:
raise exceptions.HostUnreachableException(
url=self.kw['auth_url'])
return client
def get_nova_client(self, version='2'): def get_nova_client(self, version='2'):
"""Returns nova client.""" """Returns nova client."""
if "nova" in self.cache: if "nova" in self.cache:

View File

@@ -17,8 +17,6 @@
import mock import mock
from keystoneclient import exceptions as keystone_exceptions
from rally.benchmark import engine from rally.benchmark import engine
from rally import consts from rally import consts
from rally import exceptions from rally import exceptions
@@ -122,34 +120,6 @@ class TestEngineTestCase(test.TestCase):
with tester.bind(self.valid_endpoint): with tester.bind(self.valid_endpoint):
self.assertEqual(tester.endpoint, self.valid_endpoint) self.assertEqual(tester.endpoint, self.valid_endpoint)
@mock.patch("rally.benchmark.engine.osclients")
def test_bind_user_not_admin(self, mock_osclients):
mock_osclients.Clients.return_value = fakes.FakeClients()
mock_osclients.Clients.return_value.get_keystone_client(). \
auth_ref['user']['roles'] = [{'name': 'notadmin'}]
tester = engine.TestEngine(self.valid_test_config_continuous_times,
mock.MagicMock())
self.assertRaises(exceptions.InvalidAdminException,
tester.bind, self.valid_endpoint)
@mock.patch("rally.cmd.main.api.engine.osclients.Clients"
".get_keystone_client")
def test_bind_unauthorized_keystone(self, mock_osclients):
mock_osclients.side_effect = keystone_exceptions.Unauthorized
tester = engine.TestEngine(self.valid_test_config_continuous_times,
mock.MagicMock())
self.assertRaises(exceptions.InvalidEndpointsException,
tester.bind, self.valid_endpoint)
@mock.patch("rally.cmd.main.api.engine.osclients.Clients"
".get_keystone_client")
def test_bind_keystone_host_unreachable(self, mock_osclients):
mock_osclients.side_effect = keystone_exceptions.AuthorizationFailure
tester = engine.TestEngine(self.valid_test_config_continuous_times,
mock.MagicMock())
self.assertRaises(exceptions.HostUnreachableException,
tester.bind, self.valid_endpoint)
@mock.patch("rally.benchmark.runner.ScenarioRunner.run") @mock.patch("rally.benchmark.runner.ScenarioRunner.run")
@mock.patch("rally.benchmark.utils.osclients") @mock.patch("rally.benchmark.utils.osclients")
@mock.patch("rally.benchmark.engine.osclients") @mock.patch("rally.benchmark.engine.osclients")

View File

@@ -374,6 +374,9 @@ class FakeClients(object):
self.keystone = FakeKeystoneClient() self.keystone = FakeKeystoneClient()
return self.keystone return self.keystone
def get_verified_keystone_client(self):
return self.get_keystone_client()
def get_nova_client(self): def get_nova_client(self):
if self.nova is not None: if self.nova is not None:
return self.nova return self.nova

View File

@@ -15,6 +15,9 @@
import mock import mock
from keystoneclient import exceptions as keystone_exceptions
from rally import exceptions
from rally import osclients from rally import osclients
from tests import fakes from tests import fakes
from tests import test from tests import test
@@ -47,6 +50,28 @@ class OSClientsTestCase(test.TestCase):
mock_keystone.Client.assert_called_once_with(**kwargs) mock_keystone.Client.assert_called_once_with(**kwargs)
self.assertEqual(self.clients.cache["keystone"], fake_keystone) self.assertEqual(self.clients.cache["keystone"], fake_keystone)
@mock.patch('rally.osclients.Clients.get_keystone_client')
def test_get_verified_keystone_client_user_not_admin(self, mock_keystone):
mock_keystone.return_value = fakes.FakeKeystoneClient()
mock_keystone.return_value.auth_ref['user']['roles'] = \
[{'name': 'notadmin'}]
self.assertRaises(exceptions.InvalidAdminException,
self.clients.get_verified_keystone_client)
@mock.patch('rally.osclients.Clients.get_keystone_client')
def test_get_verified_keystone_client_unauthorized(self, mock_keystone):
mock_keystone.return_value = fakes.FakeKeystoneClient()
mock_keystone.side_effect = keystone_exceptions.Unauthorized
self.assertRaises(exceptions.InvalidEndpointsException,
self.clients.get_verified_keystone_client)
@mock.patch('rally.osclients.Clients.get_keystone_client')
def test_get_verified_keystone_client_unreachable(self, mock_keystone):
mock_keystone.return_value = fakes.FakeKeystoneClient()
mock_keystone.side_effect = keystone_exceptions.AuthorizationFailure
self.assertRaises(exceptions.HostUnreachableException,
self.clients.get_verified_keystone_client)
def test_get_nova_client(self): def test_get_nova_client(self):
with mock.patch('rally.osclients.nova') as mock_nova: with mock.patch('rally.osclients.nova') as mock_nova:
fake_nova = fakes.FakeNovaClient() fake_nova = fakes.FakeNovaClient()