summaryrefslogtreecommitdiff
path: root/keystoneclient/generic
diff options
context:
space:
mode:
authorZiad Sawalha <github@highbridgellc.com>2011-12-28 00:23:31 -0600
committerZiad Sawalha <github@highbridgellc.com>2012-01-20 12:14:02 -0600
commit8db366c448d4074c044f596a8e4271befdb797cb (patch)
treeff128968469d602e00ee41cfe09e12ba31a6d6b0 /keystoneclient/generic
parentcbe1f82931002e3562ab41582ff86470681b889b (diff)
downloadpython-keystoneclient-8db366c448d4074c044f596a8e4271befdb797cb.tar.gz
Support for version and extension discoveryessex-3
- Supports unauthenticated call to Keystone to discover supported API versions - Added command-line support (usage: keystone discover) - Added client support (keystoneclient.genenric client). Client returns dicts, whereas shell command prints formated output. - Added tests for genenric client - Replicates 'nove discover' in python-novaclient - Starts to address blueprint keystone-client - keystone discover output looks like this: $ keystone discover Keystone found at http://localhost:35357 - supports version v1.0 (DEPRECATED) here http://localhost:35357/v1.0 - supports version v1.1 (CURRENT) here http://localhost:35357/v1.1 - supports version v2.0 (BETA) here http://localhost:35357/v2.0 - and HP-IDM: HP Token Validation Extension - and OS-KSADM: Openstack Keystone Admin - and OS-KSCATALOG: Openstack Keystone Catalog Change-Id: Id16d34dac094c780d36afb3e31c98c318b6071ac
Diffstat (limited to 'keystoneclient/generic')
-rw-r--r--keystoneclient/generic/__init__.py0
-rw-r--r--keystoneclient/generic/client.py205
-rw-r--r--keystoneclient/generic/shell.py59
3 files changed, 264 insertions, 0 deletions
diff --git a/keystoneclient/generic/__init__.py b/keystoneclient/generic/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/keystoneclient/generic/__init__.py
diff --git a/keystoneclient/generic/client.py b/keystoneclient/generic/client.py
new file mode 100644
index 0000000..724c05b
--- /dev/null
+++ b/keystoneclient/generic/client.py
@@ -0,0 +1,205 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 OpenStack LLC.
+# 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 logging
+import urlparse
+
+from keystoneclient import client
+from keystoneclient import exceptions
+
+_logger = logging.getLogger(__name__)
+
+
+class Client(client.HTTPClient):
+ """Client for the OpenStack Keystone pre-version calls API.
+
+ :param string endpoint: A user-supplied endpoint URL for the keystone
+ service.
+ :param integer timeout: Allows customization of the timeout for client
+ http requests. (optional)
+
+ Example::
+
+ >>> from keystoneclient.generic import client
+ >>> root = client.Client(auth_url=KEYSTONE_URL)
+ >>> versions = root.discover()
+ ...
+ >>> from keystoneclient.v2_0 import client as v2client
+ >>> keystone = v2client.Client(auth_url=versions['v2.0']['url'])
+ ...
+ >>> user = keystone.users.get(USER_ID)
+ >>> user.delete()
+
+ """
+
+ def __init__(self, endpoint=None, **kwargs):
+ """ Initialize a new client for the Keystone v2.0 API. """
+ super(Client, self).__init__(endpoint=endpoint, **kwargs)
+ self.endpoint = endpoint
+
+ def discover(self, url=None):
+ """ Discover Keystone servers and return API versions supported.
+
+ :param url: optional url to test (without version)
+
+ Returns::
+
+ {
+ 'message': 'Keystone found at http://127.0.0.1:5000/',
+ 'v2.0': {
+ 'status': 'beta',
+ 'url': 'http://127.0.0.1:5000/v2.0/',
+ 'id': 'v2.0'
+ },
+ }
+
+ """
+ if url:
+ return self._check_keystone_versions(url)
+ else:
+ return self._local_keystone_exists()
+
+ def _local_keystone_exists(self):
+ """ Checks if Keystone is available on default local port 35357 """
+ return self._check_keystone_versions("http://localhost:35357")
+
+ def _check_keystone_versions(self, url):
+ """ Calls Keystone URL and detects the available API versions """
+ try:
+ httpclient = client.HTTPClient()
+ resp, body = httpclient.request(url, "GET",
+ headers={'Accept': 'application/json'})
+ if resp.status in (200, 204): # in some cases we get No Content
+ try:
+ results = {}
+ if 'version' in body:
+ results['message'] = "Keystone found at %s" % url
+ version = body['version']
+ # Stable/diablo incorrect format
+ id, status, version_url = self._get_version_info(
+ version, url)
+ results[str(id)] = {"id": id,
+ "status": status,
+ "url": version_url}
+ return results
+ elif 'versions' in body:
+ # Correct format
+ results['message'] = "Keystone found at %s" % url
+ for version in body['versions']['values']:
+ id, status, version_url = self._get_version_info(
+ version, url)
+ results[str(id)] = {"id": id,
+ "status": status,
+ "url": version_url}
+ return results
+ else:
+ results['message'] = "Unrecognized response from %s" \
+ % url
+ return results
+ except KeyError:
+ raise exceptions.AuthorizationFailure()
+ elif resp.status == 305:
+ return self._check_keystone_versions(resp['location'])
+ else:
+ raise exceptions.from_response(resp, body)
+ except Exception as e:
+ _logger.exception(e)
+
+ def discover_extensions(self, url=None):
+ """ Discover Keystone extensions supported.
+
+ :param url: optional url to test (should have a version in it)
+
+ Returns::
+
+ {
+ 'message': 'Keystone extensions at http://127.0.0.1:35357/v2',
+ 'OS-KSEC2': 'OpenStack EC2 Credentials Extension',
+ }
+
+ """
+ if url:
+ return self._check_keystone_extensions(url)
+
+ def _check_keystone_extensions(self, url):
+ """ Calls Keystone URL and detects the available extensions """
+ try:
+ httpclient = client.HTTPClient()
+ if not url.endswith("/"):
+ url += '/'
+ resp, body = httpclient.request("%sextensions" % url, "GET",
+ headers={'Accept': 'application/json'})
+ if resp.status in (200, 204): # in some cases we get No Content
+ try:
+ results = {}
+ if 'extensions' in body:
+ if 'values' in body['extensions']:
+ # Parse correct format (per contract)
+ for extension in body['extensions']['values']:
+ alias, name = self._get_extension_info(
+ extension['extension'])
+ results[alias] = name
+ return results
+ else:
+ # Support incorrect, but prevalent format
+ for extension in body['extensions']:
+ alias, name = self._get_extension_info(
+ extension)
+ results[alias] = name
+ return results
+ else:
+ results['message'] = "Unrecognized extensions" \
+ " response from %s" % url
+ return results
+ except KeyError:
+ raise exceptions.AuthorizationFailure()
+ elif resp.status == 305:
+ return self._check_keystone_extensions(resp['location'])
+ else:
+ raise exceptions.from_response(resp, body)
+ except Exception as e:
+ _logger.exception(e)
+
+ @staticmethod
+ def _get_version_info(version, root_url):
+ """ Parses version information
+
+ :param version: a dict of a Keystone version response
+ :param root_url: string url used to construct
+ the version if no URL is provided.
+ :returns: tuple - (verionId, versionStatus, versionUrl)
+ """
+ id = version['id']
+ status = version['status']
+ ref = urlparse.urljoin(root_url, id)
+ if 'links' in version:
+ for link in version['links']:
+ if link['rel'] == 'self':
+ ref = link['href']
+ break
+ return (id, status, ref)
+
+ @staticmethod
+ def _get_extension_info(extension):
+ """ Parses extension information
+
+ :param extension: a dict of a Keystone extension response
+ :returns: tuple - (alias, name)
+ """
+ alias = extension['alias']
+ name = extension['name']
+ return (alias, name)
diff --git a/keystoneclient/generic/shell.py b/keystoneclient/generic/shell.py
new file mode 100644
index 0000000..52d7d1f
--- /dev/null
+++ b/keystoneclient/generic/shell.py
@@ -0,0 +1,59 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 OpenStack LLC.
+# 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.
+
+from keystoneclient import utils
+from keystoneclient.generic import client
+
+CLIENT_CLASS = client.Client
+
+
+@utils.unauthenticated
+def do_discover(cs, args):
+ """
+ Discover Keystone servers and show authentication protocols and
+ extensions supported.
+
+ Usage::
+ $ keystone discover
+ Keystone found at http://localhost:35357
+ - supports version v1.0 (DEPRECATED) here http://localhost:35357/v1.0
+ - supports version v1.1 (CURRENT) here http://localhost:35357/v1.1
+ - supports version v2.0 (BETA) here http://localhost:35357/v2.0
+ - and RAX-KSKEY: Rackspace API Key Authentication Admin Extension
+ - and RAX-KSGRP: Rackspace Keystone Group Extensions
+ """
+ if cs.endpoint:
+ versions = cs.discover(cs.endpoint)
+ elif cs.auth_url:
+ versions = cs.discover(cs.auth_url)
+ else:
+ versions = cs.discover()
+ if versions:
+ if 'message' in versions:
+ print versions['message']
+ for key, version in versions.iteritems():
+ if key != 'message':
+ print " - supports version %s (%s) here %s" % \
+ (version['id'], version['status'], version['url'])
+ extensions = cs.discover_extensions(version['url'])
+ if extensions:
+ for key, extension in extensions.iteritems():
+ if key != 'message':
+ print " - and %s: %s" % \
+ (key, extension)
+ else:
+ print "No Keystone-compatible endpoint found"