CLI support of Keystone openrc

Add support of the standard openrc env vars
to the CLI so that a token will be automatically
generated and the Drydock API endpoint will be
sourced from the Keystone catalogue.

- Use click to allow env or cli based keystone env

Change-Id: I10a48d509c0b90670af07870a1ae4d31525b4a3b
This commit is contained in:
Scott Hussey 2017-10-18 14:00:57 -05:00
parent 27d54b3c46
commit fe527999aa
2 changed files with 98 additions and 19 deletions

View File

@ -18,6 +18,7 @@ from urllib.parse import urlparse
import click
from drydock_provisioner.drydock_client.session import DrydockSession
from drydock_provisioner.drydock_client.session import KeystoneClient
from drydock_provisioner.drydock_client.client import DrydockClient
from .design import commands as design
from .part import commands as part
@ -27,32 +28,43 @@ from .node import commands as node
@click.group()
@click.option(
'--debug/--no-debug', help='Enable or disable debugging', default=False)
# Supported Environment Variables
@click.option(
'--token',
'-t',
help='The auth token to be used',
default=lambda: os.environ.get('DD_TOKEN', ''))
'--os_project_domain_name',
envvar='OS_PROJECT_DOMAIN_NAME',
required=False)
@click.option(
'--os_user_domain_name', envvar='OS_USER_DOMAIN_NAME', required=False)
@click.option('--os_project_name', envvar='OS_PROJECT_NAME', required=False)
@click.option('--os_username', envvar='OS_USERNAME', required=False)
@click.option('--os_password', envvar='OS_PASSWORD', required=False)
@click.option('--os_auth_url', envvar='OS_AUTH_URL', required=False)
@click.option(
'--os_token',
help='The Keystone token to be used',
default=lambda: os.environ.get('OS_TOKEN', ''))
@click.option(
'--url',
'-u',
help='The url of the running drydock instance',
default=lambda: os.environ.get('DD_URL', ''))
@click.pass_context
def drydock(ctx, debug, token, url):
""" Drydock CLI to invoke the running instance of the drydock API
"""
def drydock(ctx, debug, url, os_project_domain_name, os_user_domain_name, os_project_name,
os_username, os_password, os_auth_url, os_token):
"""Drydock CLI to invoke the running instance of the drydock API."""
if not ctx.obj:
ctx.obj = {}
ctx.obj['DEBUG'] = debug
if not token:
ctx.fail('Error: Token must be specified either by '
'--token or DD_TOKEN from the environment')
if not url:
ctx.fail('Error: URL must be specified either by '
'--url or DD_URL from the environment')
keystone_env = {
'project_domain_name': os_project_domain_name,
'user_domain_name': os_user_domain_name,
'project_name': os_project_name,
'username': os_username,
'password': os_password,
'auth_url': os_auth_url,
}
# setup logging for the CLI
# Setup root logger
@ -66,18 +78,44 @@ def drydock(ctx, debug, token, url):
logger.addHandler(logging_handler)
logger.debug('logging for cli initialized')
try:
if not os_token:
logger.debug("Generating Keystone session by env vars: %s" % str(keystone_env))
ks_sess = KeystoneClient.get_ks_session(**keystone_env)
else:
logger.debug("Generating Keystone session by explicit token: %s" % os_token)
ks_sess = KeystoneClient.get_ks_session(token=os_token)
KeystoneClient.get_token(ks_sess=ks_sess)
except Exception as ex:
logger.debug("Exception getting Keystone session.", exc_info=ex)
ctx.fail('Error: Unable to authenticate with Keystone')
return
try:
if not url:
url = KeystoneClient.get_endpoint('physicalprovisioner', ks_sess=ks_sess)
except Exception as ex:
logger.debug("Exception getting Drydock endpoint.", exc_info=ex)
ctx.fail('Error: Unable to discover Drydock API URL')
# setup the drydock client using the passed parameters.
url_parse_result = urlparse(url)
logger.debug(url_parse_result)
if not os_token:
token = KeystoneClient.get_token(ks_sess=ks_sess)
logger.debug("Creating Drydock client with token %s." % token)
else:
token = os_token
if not url_parse_result.scheme:
ctx.fail('URL must specify a scheme and hostname, optionally a port')
ctx.obj['CLIENT'] = DrydockClient(
DrydockSession(
scheme=url_parse_result.scheme,
host=url_parse_result.netloc,
host=url_parse_result.hostname,
port=url_parse_result.port,
token=token))
drydock.add_command(design.design)
drydock.add_command(part.part)
drydock.add_command(task.task)

View File

@ -14,6 +14,8 @@
import requests
import logging
from keystoneauth1 import session
from keystoneauth1.identity import v3
class DrydockSession(object):
"""
@ -40,7 +42,7 @@ class DrydockSession(object):
self.base_url = "%s://%s:%s/api/" % (self.scheme, self.host,
self.port)
else:
#assume default port for scheme
# assume default port for scheme
self.base_url = "%s://%s/api/" % (self.scheme, self.host)
self.token = token
@ -48,7 +50,6 @@ class DrydockSession(object):
self.logger = logging.getLogger(__name__)
# TODO Add keystone authentication to produce a token for this session
def get(self, endpoint, query=None):
"""
Send a GET request to Drydock.
@ -85,3 +86,43 @@ class DrydockSession(object):
self.base_url + endpoint, params=query, json=data, timeout=10)
return resp
class KeystoneClient(object):
@staticmethod
def get_endpoint(endpoint, ks_sess=None, auth_info=None):
"""
Wraps calls to keystone for lookup of an endpoint by service type
:param endpoint: The endpoint to look up
:param ks_sess: A keystone session to use for accessing endpoint catalogue
:param auth_info: Authentication info to use for building a token if a ``ks_sess`` is not specified
:returns: The url string of the endpoint
:rtype: str
:raises AppError: if the endpoint cannot be resolved
"""
if ks_sess is None:
ks_sess = KeystoneClient.get_ks_session(**auth_info)
return ks_sess.get_endpoint(interface='internal', service_type=endpoint)
@staticmethod
def get_token(ks_sess=None, auth_info=None):
"""
Returns the simple token string for a token acquired from keystone
:param ks_sess: an existing Keystone session to retrieve a token from
:param auth_info: dictionary of information required to generate a keystone token
"""
if ks_sess is None:
ks_sess = KeystoneClient.get_ks_session(**auth_info)
return ks_sess.get_auth_headers().get('X-Auth-Token')
@staticmethod
def get_ks_session(**kwargs):
# Establishes a keystone session
if 'token' in kwargs:
auth = v3.TokenMethod(token=kwargs.get('token'))
else:
auth = v3.Password(**kwargs)
return session.Session(auth=auth)