diff --git a/openstackclient/common/envvars.py b/openstackclient/common/envvars.py new file mode 100644 index 0000000000..5f702ff3d4 --- /dev/null +++ b/openstackclient/common/envvars.py @@ -0,0 +1,57 @@ +# 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. + +import os + +from openstackclient.i18n import _ + + +def bool_from_str(value, strict=False): + true_strings = ('1', 't', 'true', 'on', 'y', 'yes') + false_strings = ('0', 'f', 'false', 'off', 'n', 'no') + + if isinstance(value, bool): + return value + + lowered = value.strip().lower() + if lowered in true_strings: + return True + elif lowered in false_strings or not strict: + return False + + msg = _( + "Unrecognized value '%(value)s'; acceptable values are: %(valid)s" + ) % { + 'value': value, + 'valid': ', '.join( + f"'{s}'" for s in sorted(true_strings + false_strings) + ), + } + raise ValueError(msg) + + +def boolenv(*vars, default=False): + """Search for the first defined of possibly many bool-like env vars. + + Returns the first environment variable defined in vars, or returns the + default. + + :param vars: Arbitrary strings to search for. Case sensitive. + :param default: The default to return if no value found. + :returns: A boolean corresponding to the value found, else the default if + no value found. + """ + for v in vars: + value = os.environ.get(v, None) + if value: + return bool_from_str(value) + return default diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 8e88d8e90c..d415a84538 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -34,6 +34,7 @@ from osc_lib import exceptions from osc_lib import utils from openstackclient.api import compute_v2 +from openstackclient.common import envvars from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -324,48 +325,6 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True): return info -def bool_from_str(value, strict=False): - true_strings = ('1', 't', 'true', 'on', 'y', 'yes') - false_strings = ('0', 'f', 'false', 'off', 'n', 'no') - - if isinstance(value, bool): - return value - - lowered = value.strip().lower() - if lowered in true_strings: - return True - elif lowered in false_strings or not strict: - return False - - msg = _( - "Unrecognized value '%(value)s'; acceptable values are: %(valid)s" - ) % { - 'value': value, - 'valid': ', '.join( - f"'{s}'" for s in sorted(true_strings + false_strings) - ), - } - raise ValueError(msg) - - -def boolenv(*vars, default=False): - """Search for the first defined of possibly many bool-like env vars. - - Returns the first environment variable defined in vars, or returns the - default. - - :param vars: Arbitrary strings to search for. Case sensitive. - :param default: The default to return if no value found. - :returns: A boolean corresponding to the value found, else the default if - no value found. - """ - for v in vars: - value = os.environ.get(v, None) - if value: - return bool_from_str(value) - return default - - class AddFixedIP(command.ShowOne): _description = _("Add fixed IP address to server") @@ -1876,7 +1835,7 @@ class CreateServer(command.ShowOne): if 'delete_on_termination' in mapping: try: - value = bool_from_str( + value = envvars.bool_from_str( mapping['delete_on_termination'], strict=True, ) @@ -2223,7 +2182,7 @@ class DeleteServer(command.Command): parser.add_argument( '--all-projects', action='store_true', - default=boolenv('ALL_PROJECTS'), + default=envvars.boolenv('ALL_PROJECTS'), help=_( 'Delete server(s) in another project by name (admin only)' '(can be specified using the ALL_PROJECTS envvar)' @@ -2389,7 +2348,7 @@ class ListServer(command.Lister): parser.add_argument( '--all-projects', action='store_true', - default=boolenv('ALL_PROJECTS'), + default=envvars.boolenv('ALL_PROJECTS'), help=_( 'Include all projects (admin only) ' '(can be specified using the ALL_PROJECTS envvar)' @@ -4967,7 +4926,7 @@ class StartServer(command.Command): parser.add_argument( '--all-projects', action='store_true', - default=boolenv('ALL_PROJECTS'), + default=envvars.boolenv('ALL_PROJECTS'), help=_( 'Start server(s) in another project by name (admin only) ' '(can be specified using the ALL_PROJECTS envvar)' @@ -5002,7 +4961,7 @@ class StopServer(command.Command): parser.add_argument( '--all-projects', action='store_true', - default=boolenv('ALL_PROJECTS'), + default=envvars.boolenv('ALL_PROJECTS'), help=_( 'Stop server(s) in another project by name (admin only) ' '(can be specified using the ALL_PROJECTS envvar)' diff --git a/openstackclient/tests/unit/test_shell.py b/openstackclient/tests/unit/test_shell.py index fd95fce615..628e362bba 100644 --- a/openstackclient/tests/unit/test_shell.py +++ b/openstackclient/tests/unit/test_shell.py @@ -183,14 +183,14 @@ class TestShell(osc_lib_test_utils.TestShell): osc_lib_test_utils.fake_execute(_shell, _cmd) self.app.assert_called_with(["list", "role"]) - self.assertEqual( - default_args.get("token", ''), _shell.options.token, "token" - ) - self.assertEqual( - default_args.get("auth_url", ''), - _shell.options.auth_url, - "auth_url", - ) + + if default_args.get('token'): + self.assertEqual(default_args['token'], _shell.options.token) + + if default_args.get('auth_url'): + self.assertEqual( + default_args['auth_url'], _shell.options.auth_url + ) def _assert_cli(self, cmd_options, default_args): with mock.patch( @@ -204,25 +204,28 @@ class TestShell(osc_lib_test_utils.TestShell): osc_lib_test_utils.fake_execute(_shell, _cmd) self.app.assert_called_with(["list", "server"]) + + # TODO(stephenfin): Remove "or ''" when we bump osc-lib minimum to + # a version that includes I1d26133c9d9ed299d1035f207059aa8fe463a001 self.assertEqual( default_args["compute_api_version"], - _shell.options.os_compute_api_version, + _shell.options.os_compute_api_version or '', ) self.assertEqual( default_args["identity_api_version"], - _shell.options.os_identity_api_version, + _shell.options.os_identity_api_version or '', ) self.assertEqual( default_args["image_api_version"], - _shell.options.os_image_api_version, + _shell.options.os_image_api_version or '', ) self.assertEqual( default_args["volume_api_version"], - _shell.options.os_volume_api_version, + _shell.options.os_volume_api_version or '', ) self.assertEqual( default_args["network_api_version"], - _shell.options.os_network_api_version, + _shell.options.os_network_api_version or '', ) diff --git a/openstackclient/volume/v3/volume_attachment.py b/openstackclient/volume/v3/volume_attachment.py index fe4765c7b4..b24c96b83a 100644 --- a/openstackclient/volume/v3/volume_attachment.py +++ b/openstackclient/volume/v3/volume_attachment.py @@ -18,6 +18,7 @@ from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient.common import envvars from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -399,7 +400,7 @@ class ListVolumeAttachment(command.Lister): '--all-projects', dest='all_projects', action='store_true', - default=utils.env('ALL_PROJECTS', default=False), + default=envvars.boolenv('ALL_PROJECTS'), help=_('Shows details for all projects (admin only).'), ) parser.add_argument( diff --git a/openstackclient/volume/v3/volume_group.py b/openstackclient/volume/v3/volume_group.py index 201e7a6d7e..f943b80302 100644 --- a/openstackclient/volume/v3/volume_group.py +++ b/openstackclient/volume/v3/volume_group.py @@ -17,6 +17,7 @@ from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient.common import envvars from openstackclient.i18n import _ @@ -410,7 +411,7 @@ class ListVolumeGroup(command.Lister): '--all-projects', dest='all_projects', action='store_true', - default=utils.env('ALL_PROJECTS', default=False), + default=envvars.boolenv('ALL_PROJECTS'), help=_('Shows details for all projects (admin only).'), ) # TODO(stephenfin): Add once we have an equivalent command for diff --git a/openstackclient/volume/v3/volume_group_snapshot.py b/openstackclient/volume/v3/volume_group_snapshot.py index eabf700288..5a760ac9bf 100644 --- a/openstackclient/volume/v3/volume_group_snapshot.py +++ b/openstackclient/volume/v3/volume_group_snapshot.py @@ -17,6 +17,7 @@ from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient.common import envvars from openstackclient.i18n import _ LOG = logging.getLogger(__name__) @@ -145,7 +146,7 @@ class ListVolumeGroupSnapshot(command.Lister): '--all-projects', dest='all_projects', action='store_true', - default=utils.env('ALL_PROJECTS', default=False), + default=envvars.boolenv('ALL_PROJECTS'), help=_('Shows details for all projects (admin only).'), ) # TODO(stephenfin): Add once we have an equivalent command for