Merge pull request #3 from anotherjesse/standardize

Standardize OS API variable naming and project cleanup.
This commit is contained in:
Gabriel Hurley
2011-12-21 13:07:32 -08:00
15 changed files with 125 additions and 137 deletions

View File

@@ -27,7 +27,7 @@ By way of a quick-start::
# use v2.0 auth with http://example.com:5000/v2.0")
>>> from keystoneclient.v2_0 import client
>>> keystone = client.Client(username=USERNAME, password=API_KEY, project_id=TENANT, auth_url=KEYSTONE_URL)
>>> keystone = client.Client(username=USERNAME, password=PASSWORD, tenant_name=TENANT, auth_url=KEYSTONE_URL)
>>> keystone.tenants.list()
>>> tenant = keystone.tenants.create(name="test", descrption="My new tenant!", enabled=True)
>>> tenant.delete()
@@ -48,17 +48,16 @@ You'll need to provide your OpenStack username and API key. You can do this
with the ``--username``, ``--apikey`` and ``--projectid`` params, but it's
easier to just set them as environment variables::
export KEYSTONE_USERNAME=openstack
export KEYSTONE_API_KEY=yadayada
export KEYSTONE_PROJECTID=yadayada
export OS_TENANT_NAME=project
export OS_USERNAME=user
export OS_PASSWORD=pass
You will also need to define the authentication url with ``--url`` and the
version of the API with ``--version``. Or set them as an environment
variables as well::
export KEYSTONE_URL=http://example.com:5000/v2.0
export OS_AUTH_URL=http://example.com:5000/v2.0
export KEYSTONE_ADMIN_URL=http://example.com:35357/v2.0
export KEYSTONE_VERSION=2.0
Since Keystone can return multiple regions in the Service Catalog, you
can specify the one you want with ``--region_name`` (or
@@ -67,8 +66,8 @@ can specify the one you want with ``--region_name`` (or
You'll find complete documentation on the shell by running
``keystone help``::
usage: keystone [--username USERNAME] [--apikey APIKEY] [--projectid PROJECTID]
[--url URL] [--version VERSION] [--region_name NAME]
usage: keystone [--username user] [--password password]
[--tenant_name tenant] [--auth_url URL]
<subcommand> ...
Command-line interface to the OpenStack Keystone API.
@@ -79,15 +78,15 @@ You'll find complete documentation on the shell by running
Optional arguments:
--username USERNAME Defaults to env[KEYSTONE_USERNAME].
--apikey APIKEY Defaults to env[KEYSTONE_API_KEY].
--apikey PROJECTID Defaults to env[KEYSTONE_PROJECT_ID].
--url AUTH_URL Defaults to env[KEYSTONE_URL] or
--url ADMIN_URL Defaults to env[KEYSTONE_ADMIN_URL]
--version VERSION Defaults to env[KEYSTONE_VERSION] or 2.0.
--region_name NAME The region name in the Keystone Service Catalog
to use after authentication. Defaults to
env[KEYSTONE_REGION_NAME] or the first item
in the list returned.
--username USER Defaults to env[OS_USERNAME].
--password PASSWORD Defaults to env[OS_PASSWORD].
--tenant_name TENANT_NAME Defaults to env[OS_TENANT_NAME].
--tenant_id TENANT_ID Defaults to env[OS_TENANT_ID].
--url AUTH_URL Defaults to env[OS_AUTH_URL] or
--version VERSION Defaults to env[KEYSTONE_VERSION] or 2.0.
--region_name NAME The region name in the Keystone Service
Catalog to use after authentication.
Defaults to env[KEYSTONE_REGION_NAME] or the
first item in the list returned.
See "keystone help COMMAND" for help on a specific command.

View File

@@ -12,27 +12,33 @@ The :program:`keystone` shell utility
The :program:`keystone` shell utility interacts with OpenStack Keystone API
from the command line. It supports the entirety of the OpenStack Keystone API.
First, you'll need an OpenStack Keystone account and an API key. You get this
by using the `keystone-manage` command in OpenStack Keystone.
First, you'll need an OpenStack Keystone account. You get this by using the
`keystone-manage` command in OpenStack Keystone.
You'll need to provide :program:`keystone` with your OpenStack username and
API key. You can do this with the :option:`--username`, :option:`--apikey`
and :option:`--projectid` options, but it's easier to just set them as
environment variables by setting two environment variables:
password. You can do this with the :option:`--username`, :option:`--password`.
You can optionally specify a :option:`--tenant_id` or :option:`--tenant_name`,
to scope your token to a specific tenant. If you don't specify a tenant, you
will be scoped to your default tenant if you have one. Instead of using
options, it is easier to just set them as environment variables:
.. envvar:: KEYSTONE_USERNAME
.. envvar:: OS_USERNAME
Your Keystone username.
.. envvar:: KEYSTONE_API_KEY
.. envvar:: OS_PASSWORD
Your API key.
Your Keystone password.
.. envvar:: KEYSTONE_PROJECT_ID
.. envvar:: OS_TENANT_NAME
Project for work.
Name of Keystone Tenant.
.. envvar:: KEYSTONE_URL
.. envvar:: OS_TENANT_ID
ID of Keystone Tenant.
.. envvar:: OS_AUTH_URL
The OpenStack API server URL.
@@ -42,10 +48,10 @@ environment variables by setting two environment variables:
For example, in Bash you'd use::
export KEYSTONE_USERNAME=yourname
export KEYSTONE_API_KEY=yadayadayada
export KEYSTONE_PROJECT_ID=myproject
export KEYSTONE_URL=http://...
export OS_USERNAME=yourname
export OS_PASSWORD=yadayadayada
export OS_TENANT_NAME=myproject
export OS_AUTH_URL=http://example.com:5000/v2.0/
export KEYSTONE_VERSION=2.0
From there, all shell commands take the form::

View File

@@ -38,19 +38,20 @@ class HTTPClient(httplib2.Http):
USER_AGENT = 'python-keystoneclient'
def __init__(self, username=None, password=None, token=None,
project_id=None, auth_url=None, region_name=None,
timeout=None, endpoint=None):
def __init__(self, username=None, tenant_id=None, tenant_name=None,
password=None, auth_url=None, region_name=None, timeout=None,
endpoint=None, token=None):
super(HTTPClient, self).__init__(timeout=timeout)
self.user = username
self.username = username
self.tenant_id = tenant_id
self.tenant_name = tenant_name
self.password = password
self.project_id = unicode(project_id)
self.auth_url = auth_url
self.version = 'v2.0'
self.region_name = region_name
self.auth_token = token
self.management_url = endpoint
self.auth_token = token or password
# httplib2 overrides
self.force_exception_to_status_code = True
@@ -140,8 +141,6 @@ class HTTPClient(httplib2.Http):
kwargs.setdefault('headers', {})
if self.auth_token and self.auth_token != self.password:
kwargs['headers']['X-Auth-Token'] = self.auth_token
if self.project_id:
kwargs['headers']['X-Auth-Project-Id'] = self.project_id
# Perform the request once. If we get a 401 back then it
# might be because the auth token expired, so try to

View File

@@ -56,20 +56,24 @@ class OpenStackIdentityShell(object):
help=argparse.SUPPRESS)
parser.add_argument('--username',
default=env('KEYSTONE_USERNAME'),
help='Defaults to env[KEYSTONE_USERNAME].')
default=env('OS_USERNAME'),
help='Defaults to env[OS_USERNAME].')
parser.add_argument('--apikey',
default=env('KEYSTONE_API_KEY'),
help='Defaults to env[KEYSTONE_API_KEY].')
parser.add_argument('--password',
default=env('OS_PASSWORD'),
help='Defaults to env[OS_PASSWORD].')
parser.add_argument('--projectid',
default=env('KEYSTONE_PROJECT_ID'),
help='Defaults to env[KEYSTONE_PROJECT_ID].')
parser.add_argument('--tenant_name',
default=env('OS_TENANT_NAME'),
help='Defaults to env[OS_TENANT_NAME].')
parser.add_argument('--tenant_id',
default=env('OS_TENANT_ID'),
help='Defaults to env[OS_TENANT_ID].')
parser.add_argument('--url',
default=env('KEYSTONE_URL'),
help='Defaults to env[KEYSTONE_URL].')
default=env('OS_AUTH_URL'),
help='Defaults to env[OS_AUTH_URL].')
parser.add_argument('--region_name',
default=env('KEYSTONE_REGION_NAME'),
@@ -144,37 +148,27 @@ class OpenStackIdentityShell(object):
self.do_help(args)
return 0
user, apikey, projectid, url, region_name = \
args.username, args.apikey, args.projectid, args.url, \
args.region_name
#FIXME(usrleon): Here should be restrict for project id same as
# for username or apikey but for compatibility it is not.
if not user:
raise exc.CommandError("You must provide a username, either"
"via --username or via "
"env[KEYSTONE_USERNAME]")
if not apikey:
raise exc.CommandError("You must provide an API key, either"
"via --apikey or via"
"env[KEYSTONE_API_KEY]")
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[KEYSTONE_PROJECT_ID")
if not args.username:
raise exc.CommandError("You must provide a username:"
"via --username or env[OS_USERNAME]")
if not args.password:
raise exc.CommandError("You must provide a password, either"
"via --password or env[OS_PASSWORD]")
if not url:
raise exc.CommandError("You must provide a auth url, either"
"via --url or via"
"env[KEYSTONE_URL")
if not args.url:
raise exc.CommandError("You must provide a auth url, either"
"via --auth_url or via"
"env[OS_AUTH_URL")
self.cs = self.get_api_class(options.version)(user,
apikey,
projectid,
url,
region_name=region_name)
self.cs = self.get_api_class(options.version)(username=args.username,
tenant_name=args.tenant_name,
tenant_id=args.tenant_id,
password=args.password,
auth_url=args.auth_url,
region_name=args.region_name)
try:
self.cs.authenticate()

View File

@@ -12,7 +12,6 @@
# 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 urlparse
import logging
from keystoneclient import client
@@ -34,7 +33,8 @@ class Client(client.HTTPClient):
:param string username: Username for authentication. (optional)
:param string password: Password for authentication. (optional)
:param string token: Token for authentication. (optional)
:param string project_id: Tenant/Project id. (optional)
:param string tenant_name: Tenant id. (optional)
:param string tenant_id: Tenant name. (optional)
:param string auth_url: Keystone service endpoint for authorization.
:param string region_name: Name of a region to select when choosing an
endpoint from the service catalog.
@@ -50,7 +50,7 @@ class Client(client.HTTPClient):
>>> from keystoneclient.v2_0 import client
>>> keystone = client.Client(username=USER,
password=PASS,
project_id=TENANT,
tenant_name=TENANT_NAME,
auth_url=KEYSTONE_URL)
>>> keystone.tenants.list()
...
@@ -87,11 +87,12 @@ class Client(client.HTTPClient):
"""
self.management_url = self.auth_url
try:
raw_token = self.tokens.authenticate(username=self.user,
password=self.password,
tenant=self.project_id,
token=self.auth_token,
return_raw=True)
raw_token = self.tokens.authenticate(username=self.username,
tenant_id=self.tenant_id,
tenant_name=self.tenant_name,
password=self.password,
token=self.auth_token,
return_raw=True)
self._extract_service_catalog(self.auth_url, raw_token)
return True
except (exceptions.AuthorizationFailure, exceptions.Unauthorized):
@@ -108,9 +109,13 @@ class Client(client.HTTPClient):
self.auth_token = self.service_catalog.get_token()
except KeyError:
raise exceptions.AuthorizationFailure()
if self.project_id:
# FIXME(ja): we should be lazy about setting managment_url.
# in fact we should rewrite the client to support the service
# catalog (api calls should be directable to any endpoints)
try:
self.management_url = self.service_catalog.url_for(attr='region',
filter_value=self.region_name, endpoint_type='adminURL')
except:
# Unscoped tokens don't return a service catalog
self.management_url = self.service_catalog.url_for(
attr='region',
filter_value=self.region_name)
return self.service_catalog
_logger.exception("unable to retrieve service catalog with token")

View File

@@ -21,8 +21,8 @@ class Token(base.Resource):
class TokenManager(base.ManagerWithFind):
resource_class = Token
def authenticate(self, username=None, password=None, tenant=None,
token=None, return_raw=False):
def authenticate(self, username=None, tenant_id=None, tenant_name=None,
password=None, token=None, return_raw=False):
if token and token != password:
params = {"auth": {"token": {"id": token}}}
elif username and password:
@@ -30,8 +30,10 @@ class TokenManager(base.ManagerWithFind):
"password": password}}}
else:
raise ValueError('A username and password or token is required.')
if tenant:
params['auth']['tenantId'] = tenant
if tenant_id:
params['auth']['tenantId'] = tenant_id
elif tenant_name:
params['auth']['tenantName'] = tenant_name
return self._create('/tokens', params, "access", return_raw=return_raw)
def endpoints(self, token):

View File

@@ -11,8 +11,8 @@ mock_request = mock.Mock(return_value=(fake_response, fake_body))
def get_client():
cl = client.HTTPClient(username="username", password="apikey",
project_id="project_id", auth_url="auth_test")
cl = client.HTTPClient(username="username", password="password",
tenant_id="tenant", auth_url="auth_test")
return cl
@@ -33,7 +33,6 @@ class ClientTest(utils.TestCase):
def test_get_call():
resp, body = cl.get("/hi")
headers = {"X-Auth-Token": "token",
"X-Auth-Project-Id": "project_id",
"User-Agent": cl.USER_AGENT,
}
mock_request.assert_called_with("http://127.0.0.1:5000/"
@@ -52,7 +51,6 @@ class ClientTest(utils.TestCase):
cl.post("/hi", body=[1, 2, 3])
headers = {
"X-Auth-Token": "token",
"X-Auth-Project-Id": "project_id",
"Content-Type": "application/json",
"User-Agent": cl.USER_AGENT
}

View File

@@ -13,10 +13,10 @@ class ShellTest(utils.TestCase):
def setUp(self):
global _old_env
fake_env = {
'KEYSTONE_USERNAME': 'username',
'KEYSTONE_API_KEY': 'password',
'KEYSTONE_PROJECT_ID': 'project_id',
'KEYSTONE_URL': 'http://127.0.0.1:5000',
'OS_USERNAME': 'username',
'OS_PASSWORD': 'password',
'OS_TENANT_ID': 'tenant_id',
'OS_AUTH_URL': 'http://127.0.0.1:5000/v2.0',
}
_old_env, os.environ = os.environ, fake_env.copy()

View File

@@ -8,7 +8,7 @@ from keystoneclient.v2_0 import client
class TestCase(unittest.TestCase):
TEST_TENANT = '1'
TEST_TENANT_ID = '1'
TEST_TENANT_NAME = 'aTenant'
TEST_TOKEN = 'aToken'
TEST_USER = 'test'
@@ -70,7 +70,7 @@ class TestCase(unittest.TestCase):
httplib2.Http.request = self.mox.CreateMockAnything()
self.client = client.Client(username=self.TEST_USER,
token=self.TEST_TOKEN,
project_id=self.TEST_TENANT,
tenant_name=self.TEST_TENANT_NAME,
auth_url=self.TEST_URL,
endpoint=self.TEST_URL)

View File

@@ -36,7 +36,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
"username": self.TEST_USER,
"password": self.TEST_TOKEN,
},
"tenantId": self.TEST_TENANT
"tenantId": self.TEST_TENANT_ID
}
}
self.TEST_REQUEST_HEADERS = {
@@ -47,7 +47,6 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
def test_authenticate_failure(self):
self.TEST_REQUEST_BODY['auth']['passwordCredentials']['password'] = \
'bad_key'
self.TEST_REQUEST_HEADERS['X-Auth-Project-Id'] = '1'
resp = httplib2.Response({
"status": 401,
"body": json.dumps({"unauthorized": {
@@ -70,11 +69,10 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
with self.assertRaises(exceptions.Unauthorized):
client.Client(username=self.TEST_USER,
password="bad_key",
project_id=self.TEST_TENANT,
tenant_id=self.TEST_TENANT_ID,
auth_url=self.TEST_URL)
def test_auth_redirect(self):
self.TEST_REQUEST_HEADERS['X-Auth-Project-Id'] = '1'
correct_response = json.dumps(self.TEST_RESPONSE_DICT)
dict_responses = [
{"headers": {'location': self.TEST_ADMIN_URL + "/tokens"},
@@ -101,17 +99,16 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
cs = client.Client(username=self.TEST_USER,
password=self.TEST_TOKEN,
project_id=self.TEST_TENANT,
tenant_id=self.TEST_TENANT_ID,
auth_url=self.TEST_URL)
self.assertEqual(cs.management_url,
self.TEST_RESPONSE_DICT["access"]["serviceCatalog"][3]
['endpoints'][0]["publicURL"])
['endpoints'][0]["adminURL"])
self.assertEqual(cs.auth_token,
self.TEST_RESPONSE_DICT["access"]["token"]["id"])
def test_authenticate_success_password_scoped(self):
self.TEST_REQUEST_HEADERS['X-Auth-Project-Id'] = '1'
resp = httplib2.Response({
"status": 200,
"body": json.dumps(self.TEST_RESPONSE_DICT),
@@ -126,11 +123,11 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
cs = client.Client(username=self.TEST_USER,
password=self.TEST_TOKEN,
project_id=self.TEST_TENANT,
tenant_id=self.TEST_TENANT_ID,
auth_url=self.TEST_URL)
self.assertEqual(cs.management_url,
self.TEST_RESPONSE_DICT["access"]["serviceCatalog"][3]
['endpoints'][0]["publicURL"])
['endpoints'][0]["adminURL"])
self.assertEqual(cs.auth_token,
self.TEST_RESPONSE_DICT["access"]["token"]["id"])
@@ -159,7 +156,6 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
def test_authenticate_success_token_scoped(self):
del self.TEST_REQUEST_BODY['auth']['passwordCredentials']
self.TEST_REQUEST_BODY['auth']['token'] = {'id': self.TEST_TOKEN}
self.TEST_REQUEST_HEADERS['X-Auth-Project-Id'] = '1'
self.TEST_REQUEST_HEADERS['X-Auth-Token'] = self.TEST_TOKEN
resp = httplib2.Response({
"status": 200,
@@ -174,11 +170,11 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
self.mox.ReplayAll()
cs = client.Client(token=self.TEST_TOKEN,
project_id=self.TEST_TENANT,
tenant_id=self.TEST_TENANT_ID,
auth_url=self.TEST_URL)
self.assertEqual(cs.management_url,
self.TEST_RESPONSE_DICT["access"]["serviceCatalog"][3]
['endpoints'][0]["publicURL"])
['endpoints'][0]["adminURL"])
self.assertEqual(cs.auth_token,
self.TEST_RESPONSE_DICT["access"]["token"]["id"])

View File

@@ -10,11 +10,9 @@ from tests import utils
class RoleTests(utils.TestCase):
def setUp(self):
super(RoleTests, self).setUp()
self.TEST_REQUEST_HEADERS = {'X-Auth-Project-Id': '1',
'X-Auth-Token': 'aToken',
self.TEST_REQUEST_HEADERS = {'X-Auth-Token': 'aToken',
'User-Agent': 'python-keystoneclient'}
self.TEST_POST_HEADERS = {'X-Auth-Project-Id': '1',
'Content-Type': 'application/json',
self.TEST_POST_HEADERS = {'Content-Type': 'application/json',
'X-Auth-Token': 'aToken',
'User-Agent': 'python-keystoneclient'}
self.TEST_ROLES = {

View File

@@ -10,11 +10,9 @@ from tests import utils
class ServiceTests(utils.TestCase):
def setUp(self):
super(ServiceTests, self).setUp()
self.TEST_REQUEST_HEADERS = {'X-Auth-Project-Id': '1',
'X-Auth-Token': 'aToken',
self.TEST_REQUEST_HEADERS = {'X-Auth-Token': 'aToken',
'User-Agent': 'python-keystoneclient'}
self.TEST_POST_HEADERS = {'X-Auth-Project-Id': '1',
'Content-Type': 'application/json',
self.TEST_POST_HEADERS = {'Content-Type': 'application/json',
'X-Auth-Token': 'aToken',
'User-Agent': 'python-keystoneclient'}
self.TEST_SERVICES = {"OS-KSADM:services": {

View File

@@ -10,11 +10,9 @@ from tests import utils
class TenantTests(utils.TestCase):
def setUp(self):
super(TenantTests, self).setUp()
self.TEST_REQUEST_HEADERS = {'X-Auth-Project-Id': '1',
'X-Auth-Token': 'aToken',
self.TEST_REQUEST_HEADERS = {'X-Auth-Token': 'aToken',
'User-Agent': 'python-keystoneclient'}
self.TEST_POST_HEADERS = {'X-Auth-Project-Id': '1',
'Content-Type': 'application/json',
self.TEST_POST_HEADERS = {'Content-Type': 'application/json',
'X-Auth-Token': 'aToken',
'User-Agent': 'python-keystoneclient'}
self.TEST_TENANTS = {"tenants": {
@@ -144,7 +142,6 @@ class TenantTests(utils.TestCase):
req_body['tenant']['name'],
req_body['tenant']['description'],
req_body['tenant']['enabled'])
print tenant
self.assertTrue(isinstance(tenant, tenants.Tenant))
self.assertEqual(tenant.id, 4)
self.assertEqual(tenant.name, "tenantX")

View File

@@ -10,11 +10,9 @@ from tests import utils
class TokenTests(utils.TestCase):
def setUp(self):
#super(ServiceTests, self).setUp()
self.TEST_REQUEST_HEADERS = {'X-Auth-Project-Id': '1',
'X-Auth-Token': 'aToken',
self.TEST_REQUEST_HEADERS = {'X-Auth-Token': 'aToken',
'User-Agent': 'python-keystoneclient'}
self.TEST_POST_HEADERS = {'X-Auth-Project-Id': '1',
'Content-Type': 'application/json',
self.TEST_POST_HEADERS = {'Content-Type': 'application/json',
'X-Auth-Token': 'aToken',
'User-Agent': 'python-keystoneclient'}
'''

View File

@@ -10,11 +10,9 @@ from tests import utils
class UserTests(utils.TestCase):
def setUp(self):
super(UserTests, self).setUp()
self.TEST_REQUEST_HEADERS = {'X-Auth-Project-Id': '1',
'X-Auth-Token': 'aToken',
self.TEST_REQUEST_HEADERS = {'X-Auth-Token': 'aToken',
'User-Agent': 'python-keystoneclient'}
self.TEST_POST_HEADERS = {'X-Auth-Project-Id': '1',
'Content-Type': 'application/json',
self.TEST_POST_HEADERS = {'Content-Type': 'application/json',
'X-Auth-Token': 'aToken',
'User-Agent': 'python-keystoneclient'}
self.TEST_USERS = {