Add 'discover' command for Keystone discovery and version listing

Added @unauthenticated decorator to mark subcommands that do not
need authentication. And checks to skip authentication for these
commands.
Added novaclient.keystone to setup.py

Change-Id: Id2fd60af305c30a950bdbae8f897192bfae4d797
This commit is contained in:
Ziad Sawalha 2011-11-17 05:12:46 -06:00
parent e20dcd8cd7
commit 49284dc5cd
4 changed files with 156 additions and 20 deletions

View File

@ -0,0 +1,99 @@
# Copyright 2010 Jacob Kaplan-Moss
# Copyright 2011 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 httplib2
import urllib
import urlparse
try:
import json
except ImportError:
import simplejson as json
# Python 2.5 compat fix
if not hasattr(urlparse, 'parse_qsl'):
import cgi
urlparse.parse_qsl = cgi.parse_qsl
from novaclient import exceptions
from novaclient import utils
from novaclient import client
@utils.unauthenticated
def do_discover(cs, args):
"""
Discover Keystone servers and show authentication protocols supported.
Usage:
$ nova discover
Keystone found at http://localhost:35357
- supports version v2.0 (beta) here http://localhost:35357/v2.0
Keystone found at https://openstack.org/
- supports version v1.0 (DEPRECATED) here https://openstack.org/v1.0
- supports version v1.1 (CURRENT) here https://openstack.org/v1.1
- supports version v2.0 (BETA) here https://openstack.org/v2.0
"""
_local_keystone_exists()
_check_keystone_versions(cs.client.auth_url)
def _local_keystone_exists():
return _check_keystone_versions("http://localhost:35357")
def _check_keystone_versions(url):
try:
httpclient = client.HTTPClient(user=None, password=None,
projectid=None, auth_url=None)
resp, body = httpclient.request(url, "GET",
headers={'Accept': 'application/json'})
if resp.status in (200, 204): # in some cases we get No Content
try:
print "Keystone found at %s" % url
if 'version' in body:
version = body['version']
# Stable/diablo incorrect format
_display_version_info(version, url)
return True
if 'versions' in body:
# Correct format
for version in body['versions']['values']:
_display_version_info(version, url)
return True
print "Unrecognized response from %s" % url
except KeyError:
raise exceptions.AuthorizationFailure()
elif resp.status == 305:
return _check_keystone_versions(resp['location'])
else:
raise exceptions.from_response(resp, body)
except:
return False
def _display_version_info(version, url):
id = version['id']
status = version['status']
ref = urlparse.urljoin(url, id)
if 'links' in version:
for link in version['links']:
if link['rel'] == 'self':
ref = link['href']
break
print " - supports version %s (%s) here %s" % (id, status, ref)

View File

@ -29,6 +29,7 @@ from novaclient import base
from novaclient import exceptions as exc
from novaclient import utils
from novaclient.v1_1 import shell as shell_v1_1
from novaclient.keystone import shell as shell_keystone
def env(*vars):
@ -119,6 +120,7 @@ class OpenStackComputeShell(object):
actions_module = shell_v1_1
self._find_actions(subparsers, actions_module)
self._find_actions(subparsers, shell_keystone)
self._find_actions(subparsers, self)
for _, _, ext_module in extensions:
@ -229,15 +231,16 @@ class OpenStackComputeShell(object):
#FIXME(usrleon): Here should be restrict for project id same as
# for username or password but for compatibility it is not.
if not utils.isunauthenticated(args.func):
if not user:
raise exc.CommandError("You must provide a username, either "
"via --username or via "
"env[OS_USER_NAME]")
"env[NOVA_USERNAME]")
if not password:
if not apikey:
raise exc.CommandError("You must provide a password, either "
"via --password or via env[OS_PASSWORD]")
raise exc.CommandError("You must provide a password, "
"either via --password or via env[NOVA_PASSWORD]")
else:
password = apikey
@ -251,6 +254,17 @@ class OpenStackComputeShell(object):
"via --url or via "
"env[OS_AUTH_URL]")
if options.version and options.version != '1.0':
if not projectid:
raise exc.CommandError("You must provide an projectid, "
"either via --projectid or via "
"env[NOVA_PROJECT_ID")
if not url:
raise exc.CommandError("You must provide a auth url,"
" either via --url or via "
"env[NOVA_URL")
self.cs = self.get_api_class(options.version)(user, password,
projectid, url, insecure,
region_name=region_name,
@ -258,6 +272,7 @@ class OpenStackComputeShell(object):
extensions=extensions)
try:
if not utils.isunauthenticated(args.func):
self.cs.authenticate()
except exc.Unauthorized:
raise exc.CommandError("Invalid OpenStack Nova credentials.")

View File

@ -15,6 +15,27 @@ def arg(*args, **kwargs):
return _decorator
def unauthenticated(f):
"""
Adds 'unauthenticated' attribute to decorated function.
Usage:
@unauthenticated
def mymethod(f):
...
"""
f.unauthenticated = True
return f
def isunauthenticated(f):
"""
Checks to see if the function is marked as not requiring authentication
with the @unauthenticated decorator. Returns True if decorator is
set to True, False otherwise.
"""
return getattr(f, 'unauthenticated', False)
def pretty_choice_list(l):
return ', '.join("'%s'" % i for i in l)

View File

@ -37,7 +37,8 @@ setuptools.setup(
long_description=read_file("README.rst"),
license="Apache License, Version 2.0",
url="https://github.com/openstack/python-novaclient",
packages=["novaclient", "novaclient.v1_1", "novaclient.v1_1.contrib"],
packages=["novaclient", "novaclient.v1_1", "novaclient.v1_1.contrib",
"novaclient.keystone"],
install_requires=requirements,
tests_require=["nose", "mock"],
test_suite="nose.collector",