Add configuration show command

Create a `configuration show` command that displays the current
configuration of the CLI.  Different configurations can be
displayed using options such as --os-cloud.  Passwords and
tokens are redacted by default unless the --unmask
option is specified.

Closes-Bug: #1476729

Change-Id: I0792365d0c5fa526cd09c0ed88c6bb1e2cb813a7
This commit is contained in:
TerryHowe 2015-07-19 12:15:04 -06:00
parent 64334c0dc9
commit 43942871a9
7 changed files with 178 additions and 0 deletions

View File

@ -0,0 +1,18 @@
=============
configuration
=============
Available for all services
configuration show
------------------
Show the current openstack client configuration. This command is a little
different from other show commands because it does not take a resource name
or id to show. The command line options, such as --os-cloud, can be used to
show different configurations.
.. program:: configuration show
.. code:: bash
os configuration show

View File

@ -137,3 +137,9 @@ that appears in :file:`clouds.yaml`
rackspace: rackspace:
auth: auth:
auth_url: 'https://identity.api.rackspacecloud.com/v2.0/' auth_url: 'https://identity.api.rackspacecloud.com/v2.0/'
Debugging
~~~~~~~~~
You may find the :doc:`config show <command-objects/config>`
helpful to debug configuration issues. It will display your current
configuration.

View File

@ -15,6 +15,7 @@
"""Manage access to the clients, including authenticating when needed.""" """Manage access to the clients, including authenticating when needed."""
import copy
import logging import logging
import pkg_resources import pkg_resources
import sys import sys
@ -203,6 +204,9 @@ class ClientManager(object):
interface=interface) interface=interface)
return endpoint return endpoint
def get_configuration(self):
return copy.deepcopy(self._cli_options.config)
# Plugin Support # Plugin Support

View File

@ -0,0 +1,58 @@
# 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.
#
"""Configuration action implementations"""
import logging
from cliff import show
import six
REDACTED = "<redacted>"
class ShowConfiguration(show.ShowOne):
"""Display configuration details"""
log = logging.getLogger(__name__ + '.ShowConfiguration')
def get_parser(self, prog_name):
parser = super(ShowConfiguration, self).get_parser(prog_name)
mask_group = parser.add_mutually_exclusive_group()
mask_group.add_argument(
"--mask",
dest="mask",
action="store_true",
default=True,
help="Attempt to mask passwords (default)",
)
mask_group.add_argument(
"--unmask",
dest="mask",
action="store_false",
help="Show password in clear text",
)
return parser
def take_action(self, parsed_args):
self.log.debug('take_action(%s)', parsed_args)
info = self.app.client_manager.get_configuration()
for key, value in six.iteritems(info.pop('auth', {})):
if parsed_args.mask:
if 'password' in key.lower():
value = REDACTED
if 'token' in key.lower():
value = REDACTED
info['auth.' + key] = value
return zip(*sorted(six.iteritems(info)))

View File

@ -0,0 +1,79 @@
# 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.
#
from openstackclient.common import configuration
from openstackclient.tests import fakes
from openstackclient.tests import utils
class TestConfiguration(utils.TestCommand):
def test_show(self):
arglist = []
verifylist = [('mask', True)]
cmd = configuration.ShowConfiguration(self.app, None)
parsed_args = self.check_parser(cmd, arglist, verifylist)
columns, data = cmd.take_action(parsed_args)
collist = ('auth.password', 'auth.token', 'auth.username',
'identity_api_version', 'region')
self.assertEqual(collist, columns)
datalist = (
configuration.REDACTED,
configuration.REDACTED,
fakes.USERNAME,
fakes.VERSION,
fakes.REGION_NAME,
)
self.assertEqual(datalist, tuple(data))
def test_show_unmask(self):
arglist = ['--unmask']
verifylist = [('mask', False)]
cmd = configuration.ShowConfiguration(self.app, None)
parsed_args = self.check_parser(cmd, arglist, verifylist)
columns, data = cmd.take_action(parsed_args)
collist = ('auth.password', 'auth.token', 'auth.username',
'identity_api_version', 'region')
self.assertEqual(collist, columns)
datalist = (
fakes.PASSWORD,
fakes.AUTH_TOKEN,
fakes.USERNAME,
fakes.VERSION,
fakes.REGION_NAME,
)
self.assertEqual(datalist, tuple(data))
def test_show_mask(self):
arglist = ['--mask']
verifylist = [('mask', True)]
cmd = configuration.ShowConfiguration(self.app, None)
parsed_args = self.check_parser(cmd, arglist, verifylist)
columns, data = cmd.take_action(parsed_args)
collist = ('auth.password', 'auth.token', 'auth.username',
'identity_api_version', 'region')
self.assertEqual(collist, columns)
datalist = (
configuration.REDACTED,
configuration.REDACTED,
fakes.USERNAME,
fakes.VERSION,
fakes.REGION_NAME,
)
self.assertEqual(datalist, tuple(data))

View File

@ -28,6 +28,7 @@ PASSWORD = "scratchy"
PROJECT_NAME = "poochie" PROJECT_NAME = "poochie"
REGION_NAME = "richie" REGION_NAME = "richie"
INTERFACE = "catchy" INTERFACE = "catchy"
VERSION = "3"
TEST_RESPONSE_DICT = fixture.V2Token(token_id=AUTH_TOKEN, TEST_RESPONSE_DICT = fixture.V2Token(token_id=AUTH_TOKEN,
user_name=USERNAME) user_name=USERNAME)
@ -102,6 +103,17 @@ class FakeClientManager(object):
self.auth_ref = None self.auth_ref = None
self.auth_plugin_name = None self.auth_plugin_name = None
def get_configuration(self):
return {
'auth': {
'username': USERNAME,
'password': PASSWORD,
'token': AUTH_TOKEN,
},
'region': REGION_NAME,
'identity_api_version': VERSION,
}
class FakeModule(object): class FakeModule(object):
def __init__(self, name, version): def __init__(self, name, version):

View File

@ -44,6 +44,7 @@ openstack.cli.base =
volume = openstackclient.volume.client volume = openstackclient.volume.client
openstack.common = openstack.common =
configuration_show = openstackclient.common.configuration:ShowConfiguration
extension_list = openstackclient.common.extension:ListExtension extension_list = openstackclient.common.extension:ListExtension
limits_show = openstackclient.common.limits:ShowLimits limits_show = openstackclient.common.limits:ShowLimits
quota_set = openstackclient.common.quota:SetQuota quota_set = openstackclient.common.quota:SetQuota