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:
parent
27d54b3c46
commit
fe527999aa
@ -18,6 +18,7 @@ from urllib.parse import urlparse
|
|||||||
|
|
||||||
import click
|
import click
|
||||||
from drydock_provisioner.drydock_client.session import DrydockSession
|
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 drydock_provisioner.drydock_client.client import DrydockClient
|
||||||
from .design import commands as design
|
from .design import commands as design
|
||||||
from .part import commands as part
|
from .part import commands as part
|
||||||
@ -27,32 +28,43 @@ from .node import commands as node
|
|||||||
@click.group()
|
@click.group()
|
||||||
@click.option(
|
@click.option(
|
||||||
'--debug/--no-debug', help='Enable or disable debugging', default=False)
|
'--debug/--no-debug', help='Enable or disable debugging', default=False)
|
||||||
|
# Supported Environment Variables
|
||||||
@click.option(
|
@click.option(
|
||||||
'--token',
|
'--os_project_domain_name',
|
||||||
'-t',
|
envvar='OS_PROJECT_DOMAIN_NAME',
|
||||||
help='The auth token to be used',
|
required=False)
|
||||||
default=lambda: os.environ.get('DD_TOKEN', ''))
|
@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(
|
@click.option(
|
||||||
'--url',
|
'--url',
|
||||||
'-u',
|
'-u',
|
||||||
help='The url of the running drydock instance',
|
help='The url of the running drydock instance',
|
||||||
default=lambda: os.environ.get('DD_URL', ''))
|
default=lambda: os.environ.get('DD_URL', ''))
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def drydock(ctx, debug, token, url):
|
def drydock(ctx, debug, url, os_project_domain_name, os_user_domain_name, os_project_name,
|
||||||
""" Drydock CLI to invoke the running instance of the drydock API
|
os_username, os_password, os_auth_url, os_token):
|
||||||
"""
|
"""Drydock CLI to invoke the running instance of the drydock API."""
|
||||||
if not ctx.obj:
|
if not ctx.obj:
|
||||||
ctx.obj = {}
|
ctx.obj = {}
|
||||||
|
|
||||||
ctx.obj['DEBUG'] = debug
|
ctx.obj['DEBUG'] = debug
|
||||||
|
|
||||||
if not token:
|
keystone_env = {
|
||||||
ctx.fail('Error: Token must be specified either by '
|
'project_domain_name': os_project_domain_name,
|
||||||
'--token or DD_TOKEN from the environment')
|
'user_domain_name': os_user_domain_name,
|
||||||
|
'project_name': os_project_name,
|
||||||
if not url:
|
'username': os_username,
|
||||||
ctx.fail('Error: URL must be specified either by '
|
'password': os_password,
|
||||||
'--url or DD_URL from the environment')
|
'auth_url': os_auth_url,
|
||||||
|
}
|
||||||
|
|
||||||
# setup logging for the CLI
|
# setup logging for the CLI
|
||||||
# Setup root logger
|
# Setup root logger
|
||||||
@ -66,18 +78,44 @@ def drydock(ctx, debug, token, url):
|
|||||||
logger.addHandler(logging_handler)
|
logger.addHandler(logging_handler)
|
||||||
logger.debug('logging for cli initialized')
|
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.
|
# setup the drydock client using the passed parameters.
|
||||||
url_parse_result = urlparse(url)
|
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:
|
if not url_parse_result.scheme:
|
||||||
ctx.fail('URL must specify a scheme and hostname, optionally a port')
|
ctx.fail('URL must specify a scheme and hostname, optionally a port')
|
||||||
ctx.obj['CLIENT'] = DrydockClient(
|
ctx.obj['CLIENT'] = DrydockClient(
|
||||||
DrydockSession(
|
DrydockSession(
|
||||||
scheme=url_parse_result.scheme,
|
scheme=url_parse_result.scheme,
|
||||||
host=url_parse_result.netloc,
|
host=url_parse_result.hostname,
|
||||||
|
port=url_parse_result.port,
|
||||||
token=token))
|
token=token))
|
||||||
|
|
||||||
|
|
||||||
drydock.add_command(design.design)
|
drydock.add_command(design.design)
|
||||||
drydock.add_command(part.part)
|
drydock.add_command(part.part)
|
||||||
drydock.add_command(task.task)
|
drydock.add_command(task.task)
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
import requests
|
import requests
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from keystoneauth1 import session
|
||||||
|
from keystoneauth1.identity import v3
|
||||||
|
|
||||||
class DrydockSession(object):
|
class DrydockSession(object):
|
||||||
"""
|
"""
|
||||||
@ -40,7 +42,7 @@ class DrydockSession(object):
|
|||||||
self.base_url = "%s://%s:%s/api/" % (self.scheme, self.host,
|
self.base_url = "%s://%s:%s/api/" % (self.scheme, self.host,
|
||||||
self.port)
|
self.port)
|
||||||
else:
|
else:
|
||||||
#assume default port for scheme
|
# assume default port for scheme
|
||||||
self.base_url = "%s://%s/api/" % (self.scheme, self.host)
|
self.base_url = "%s://%s/api/" % (self.scheme, self.host)
|
||||||
|
|
||||||
self.token = token
|
self.token = token
|
||||||
@ -48,7 +50,6 @@ class DrydockSession(object):
|
|||||||
|
|
||||||
self.logger = logging.getLogger(__name__)
|
self.logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# TODO Add keystone authentication to produce a token for this session
|
|
||||||
def get(self, endpoint, query=None):
|
def get(self, endpoint, query=None):
|
||||||
"""
|
"""
|
||||||
Send a GET request to Drydock.
|
Send a GET request to Drydock.
|
||||||
@ -85,3 +86,43 @@ class DrydockSession(object):
|
|||||||
self.base_url + endpoint, params=query, json=data, timeout=10)
|
self.base_url + endpoint, params=query, json=data, timeout=10)
|
||||||
|
|
||||||
return resp
|
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)
|
||||||
|
Loading…
Reference in New Issue
Block a user