Skip to content

Commit

Permalink
identity v3 token
Browse files Browse the repository at this point in the history
Adds a test where get a token using identity api v3,
and use that to perform an operation using compute api.

Fixes bug 1161633

Change-Id: I5d8121d1734ec7f1991758360b829c7889540c71
  • Loading branch information
Brant Knudson committed May 29, 2013
1 parent c730be6 commit c7ca334
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 12 deletions.
2 changes: 2 additions & 0 deletions etc/tempest.conf.sample
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ catalog_type = identity
disable_ssl_certificate_validation = False
# URL for where to find the OpenStack Identity API endpoint (Keystone)
uri = http://127.0.0.1:5000/v2.0/
# URL for where to find the OpenStack V3 Identity API endpoint (Keystone)
uri_v3 = http://127.0.0.1:5000/v3/
# Should typically be left as keystone unless you have a non-Keystone
# authentication API service
strategy = keystone
Expand Down
2 changes: 2 additions & 0 deletions tempest/api/compute/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ def setUpClass(cls):
cls.flavor_ref_alt = cls.config.compute.flavor_ref_alt
cls.servers = []

cls.servers_client_v3_auth = os.servers_client_v3_auth

@classmethod
def _get_identity_admin_client(cls):
"""
Expand Down
19 changes: 19 additions & 0 deletions tempest/clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,14 +264,26 @@ def __init__(self, username=None, password=None, tenant_name=None,
raise exceptions.InvalidConfiguration(msg)

self.auth_url = self.config.identity.uri
self.auth_url_v3 = self.config.identity.uri_v3

if self.config.identity.strategy == 'keystone':
client_args = (self.config, self.username, self.password,
self.auth_url, self.tenant_name)

if self.auth_url_v3:
auth_version = 'v3'
client_args_v3_auth = (self.config, self.username,
self.password, self.auth_url_v3,
self.tenant_name, auth_version)
else:
client_args_v3_auth = None

else:
client_args = (self.config, self.username, self.password,
self.auth_url)

client_args_v3_auth = None

try:
self.servers_client = SERVERS_CLIENTS[interface](*client_args)
self.limits_client = LIMITS_CLIENTS[interface](*client_args)
Expand Down Expand Up @@ -305,6 +317,13 @@ def __init__(self, username=None, password=None, tenant_name=None,
self.tenant_usages_client = \
TENANT_USAGES_CLIENT[interface](*client_args)
self.policy_client = POLICY_CLIENT[interface](*client_args)

if client_args_v3_auth:
self.servers_client_v3_auth = SERVERS_CLIENTS[interface](
*client_args_v3_auth)
else:
self.servers_client_v3_auth = None

except KeyError:
msg = "Unsupported interface type `%s'" % interface
raise exceptions.InvalidConfiguration(msg)
Expand Down
106 changes: 99 additions & 7 deletions tempest/common/rest_client.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright 2012 OpenStack, LLC
# Copyright 2013 IBM Corp.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
Expand Down Expand Up @@ -36,12 +37,14 @@ class RestClient(object):
TYPE = "json"
LOG = logging.getLogger(__name__)

def __init__(self, config, user, password, auth_url, tenant_name=None):
def __init__(self, config, user, password, auth_url, tenant_name=None,
auth_version='v2'):
self.config = config
self.user = user
self.password = password
self.auth_url = auth_url
self.tenant_name = tenant_name
self.auth_version = auth_version

self.service = None
self.token = None
Expand Down Expand Up @@ -70,11 +73,16 @@ def _set_auth(self):
"""

if self.strategy == 'keystone':
self.token, self.base_url = self.keystone_auth(self.user,
self.password,
self.auth_url,
self.service,
self.tenant_name)

if self.auth_version == 'v3':
auth_func = self.identity_auth_v3
else:
auth_func = self.keystone_auth

self.token, self.base_url = (
auth_func(self.user, self.password, self.auth_url,
self.service, self.tenant_name))

else:
self.token, self.base_url = self.basic_auth(self.user,
self.password,
Expand Down Expand Up @@ -116,7 +124,7 @@ def basic_auth(self, user, password, auth_url):

def keystone_auth(self, user, password, auth_url, service, tenant_name):
"""
Provides authentication via Keystone.
Provides authentication via Keystone using v2 identity API.
"""

# Normalize URI to ensure /tokens is in it.
Expand Down Expand Up @@ -170,6 +178,90 @@ def keystone_auth(self, user, password, auth_url, service, tenant_name):
raise exceptions.IdentityError('Unexpected status code {0}'.format(
resp.status))

def identity_auth_v3(self, user, password, auth_url, service,
project_name, domain_id='default'):
"""Provides authentication using Identity API v3."""

req_url = auth_url.rstrip('/') + '/auth/tokens'

creds = {
"auth": {
"identity": {
"methods": ["password"],
"password": {
"user": {
"name": user, "password": password,
"domain": {"id": domain_id}
}
}
},
"scope": {
"project": {
"domain": {"id": domain_id},
"name": project_name
}
}
}
}

headers = {'Content-Type': 'application/json'}
body = json.dumps(creds)
resp, body = self.http_obj.request(req_url, 'POST',
headers=headers, body=body)

if resp.status == 201:
try:
token = resp['x-subject-token']
except Exception:
self.LOG.exception("Failed to obtain token using V3"
" authentication (auth URL is '%s')" %
req_url)
raise

catalog = json.loads(body)['token']['catalog']

mgmt_url = None
for service_info in catalog:
if service_info['type'] != service:
continue # this isn't the entry for us.

endpoints = service_info['endpoints']

# Look for an endpoint in the region if configured.
if service in self.region:
region = self.region[service]

for ep in endpoints:
if ep['region'] != region:
continue

mgmt_url = ep['url']
# FIXME(blk-u): this isn't handling endpoint type
# (public, internal, admin).
break

if not mgmt_url:
# Didn't find endpoint for region, use the first.

ep = endpoints[0]
mgmt_url = ep['url']
# FIXME(blk-u): this isn't handling endpoint type
# (public, internal, admin).

break

return token, mgmt_url

elif resp.status == 401:
raise exceptions.AuthenticationFailure(user=user,
password=password)
else:
self.LOG.error("Failed to obtain token using V3 authentication"
" (auth URL is '%s'), the response status is %s" %
(req_url, resp.status))
raise exceptions.AuthenticationFailure(user=user,
password=password)

def post(self, url, body, headers):
return self.request('POST', url, headers, body)

Expand Down
4 changes: 3 additions & 1 deletion tempest/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@
help="Set to True if using self-signed SSL certificates."),
cfg.StrOpt('uri',
default=None,
help="Full URI of the OpenStack Identity API (Keystone)"),
help="Full URI of the OpenStack Identity API (Keystone), v2"),
cfg.StrOpt('uri_v3',
help='Full URI of the OpenStack Identity API (Keystone), v3'),
cfg.StrOpt('strategy',
default='keystone',
help="Which auth method does the environment use? "
Expand Down
6 changes: 4 additions & 2 deletions tempest/services/compute/json/servers_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@

class ServersClientJSON(RestClient):

def __init__(self, config, username, password, auth_url, tenant_name=None):
def __init__(self, config, username, password, auth_url, tenant_name=None,
auth_version='v2'):
super(ServersClientJSON, self).__init__(config, username, password,
auth_url, tenant_name)
auth_url, tenant_name,
auth_version=auth_version)
self.service = self.config.compute.catalog_type

def create_server(self, name, image_ref, flavor_ref, **kwargs):
Expand Down
6 changes: 4 additions & 2 deletions tempest/services/compute/xml/servers_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,11 @@ def _translate_server_xml_to_json(xml_dom):

class ServersClientXML(RestClientXML):

def __init__(self, config, username, password, auth_url, tenant_name=None):
def __init__(self, config, username, password, auth_url, tenant_name=None,
auth_version='v2'):
super(ServersClientXML, self).__init__(config, username, password,
auth_url, tenant_name)
auth_url, tenant_name,
auth_version=auth_version)
self.service = self.config.compute.catalog_type

def _parse_key_value(self, node):
Expand Down
52 changes: 52 additions & 0 deletions tempest/tests/compute/test_auth_token.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Copyright 2013 IBM Corp
#
# 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 testtools

import tempest.config as config
from tempest.tests.compute import base


class AuthTokenTestJSON(base.BaseComputeTest):
_interface = 'json'

@classmethod
def setUpClass(cls):
super(AuthTokenTestJSON, cls).setUpClass()

cls.servers_v2 = cls.os.servers_client
cls.servers_v3 = cls.os.servers_client_v3_auth

def test_v2_token(self):
# Can get a token using v2 of the identity API and use that to perform
# an operation on the compute service.

# Doesn't matter which compute API is used,
# picking list_servers because it's easy.
self.servers_v2.list_servers()

@testtools.skipIf(not config.TempestConfig().identity.uri_v3,
'v3 auth client not configured')
def test_v3_token(self):
# Can get a token using v3 of the identity API and use that to perform
# an operation on the compute service.

# Doesn't matter which compute API is used,
# picking list_servers because it's easy.
self.servers_v3.list_servers()


class AuthTokenTestXML(AuthTokenTestJSON):
_interface = 'xml'

0 comments on commit c7ca334

Please sign in to comment.