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:
parent
e20dcd8cd7
commit
49284dc5cd
99
novaclient/keystone/shell.py
Normal file
99
novaclient/keystone/shell.py
Normal 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)
|
@ -29,6 +29,7 @@ from novaclient import base
|
|||||||
from novaclient import exceptions as exc
|
from novaclient import exceptions as exc
|
||||||
from novaclient import utils
|
from novaclient import utils
|
||||||
from novaclient.v1_1 import shell as shell_v1_1
|
from novaclient.v1_1 import shell as shell_v1_1
|
||||||
|
from novaclient.keystone import shell as shell_keystone
|
||||||
|
|
||||||
|
|
||||||
def env(*vars):
|
def env(*vars):
|
||||||
@ -119,6 +120,7 @@ class OpenStackComputeShell(object):
|
|||||||
actions_module = shell_v1_1
|
actions_module = shell_v1_1
|
||||||
|
|
||||||
self._find_actions(subparsers, actions_module)
|
self._find_actions(subparsers, actions_module)
|
||||||
|
self._find_actions(subparsers, shell_keystone)
|
||||||
self._find_actions(subparsers, self)
|
self._find_actions(subparsers, self)
|
||||||
|
|
||||||
for _, _, ext_module in extensions:
|
for _, _, ext_module in extensions:
|
||||||
@ -229,15 +231,16 @@ class OpenStackComputeShell(object):
|
|||||||
#FIXME(usrleon): Here should be restrict for project id same as
|
#FIXME(usrleon): Here should be restrict for project id same as
|
||||||
# for username or password but for compatibility it is not.
|
# for username or password but for compatibility it is not.
|
||||||
|
|
||||||
|
if not utils.isunauthenticated(args.func):
|
||||||
if not user:
|
if not user:
|
||||||
raise exc.CommandError("You must provide a username, either "
|
raise exc.CommandError("You must provide a username, either "
|
||||||
"via --username or via "
|
"via --username or via "
|
||||||
"env[OS_USER_NAME]")
|
"env[NOVA_USERNAME]")
|
||||||
|
|
||||||
if not password:
|
if not password:
|
||||||
if not apikey:
|
if not apikey:
|
||||||
raise exc.CommandError("You must provide a password, either "
|
raise exc.CommandError("You must provide a password, "
|
||||||
"via --password or via env[OS_PASSWORD]")
|
"either via --password or via env[NOVA_PASSWORD]")
|
||||||
else:
|
else:
|
||||||
password = apikey
|
password = apikey
|
||||||
|
|
||||||
@ -251,6 +254,17 @@ class OpenStackComputeShell(object):
|
|||||||
"via --url or via "
|
"via --url or via "
|
||||||
"env[OS_AUTH_URL]")
|
"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,
|
self.cs = self.get_api_class(options.version)(user, password,
|
||||||
projectid, url, insecure,
|
projectid, url, insecure,
|
||||||
region_name=region_name,
|
region_name=region_name,
|
||||||
@ -258,6 +272,7 @@ class OpenStackComputeShell(object):
|
|||||||
extensions=extensions)
|
extensions=extensions)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
if not utils.isunauthenticated(args.func):
|
||||||
self.cs.authenticate()
|
self.cs.authenticate()
|
||||||
except exc.Unauthorized:
|
except exc.Unauthorized:
|
||||||
raise exc.CommandError("Invalid OpenStack Nova credentials.")
|
raise exc.CommandError("Invalid OpenStack Nova credentials.")
|
||||||
|
@ -15,6 +15,27 @@ def arg(*args, **kwargs):
|
|||||||
return _decorator
|
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):
|
def pretty_choice_list(l):
|
||||||
return ', '.join("'%s'" % i for i in l)
|
return ', '.join("'%s'" % i for i in l)
|
||||||
|
|
||||||
|
3
setup.py
3
setup.py
@ -37,7 +37,8 @@ setuptools.setup(
|
|||||||
long_description=read_file("README.rst"),
|
long_description=read_file("README.rst"),
|
||||||
license="Apache License, Version 2.0",
|
license="Apache License, Version 2.0",
|
||||||
url="https://github.com/openstack/python-novaclient",
|
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,
|
install_requires=requirements,
|
||||||
tests_require=["nose", "mock"],
|
tests_require=["nose", "mock"],
|
||||||
test_suite="nose.collector",
|
test_suite="nose.collector",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user