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:
parent
64334c0dc9
commit
43942871a9
18
doc/source/command-objects/configuration.rst
Normal file
18
doc/source/command-objects/configuration.rst
Normal 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
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
58
openstackclient/common/configuration.py
Normal file
58
openstackclient/common/configuration.py
Normal 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)))
|
79
openstackclient/tests/common/test_configuration.py
Normal file
79
openstackclient/tests/common/test_configuration.py
Normal 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))
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user