From a5765143f1b172396749f8ab54a466b8f3e2be74 Mon Sep 17 00:00:00 2001 From: Jonathan Kamens Date: Thu, 13 Apr 2023 00:52:14 -0400 Subject: [PATCH] dconf: three minor but useful fixes (#6206) * dconf: Correctly handle setting a key that has no value in DB We need to check if the value in the database is None before we try to parse it, because the GVariant parser won't accept None as an input value. By definition if the value is None, i.e., there's no value in the database, than any value the user is trying to set is a change, so just indicate that it's a change without trying to compare the None to whatever the user specified as the value.x * dconf: Give a more useful error when writing a key fails if writing a key fails, then include in the error that is returned the exact key and value aguments that were given to the dconf command, to assist in diagnosing failures caused by providing the key or value in the wrong format.x * dconf: Convert boolean values into the format that dconf expects Even though we warn users to be careful to specify GVariant strings for values, a common error is to be trying to specify a boolean string which ends up getting converted into a boolean by the YAML parser or Ansible. Then it gets converted to "True" or "False", the string representations of Python booleans, which are not valid GVariants. Rather than just failing with an obscure error when this happens, let's be more user-friendly and detect when the user has specified a boolean and convert it into the correct GVariant forms, "true" or "false", so it just works. There's no good reason to be more pedantic than that. --- changelogs/fragments/6206-dconf-booleans.yml | 2 ++ plugins/modules/dconf.py | 29 ++++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 changelogs/fragments/6206-dconf-booleans.yml diff --git a/changelogs/fragments/6206-dconf-booleans.yml b/changelogs/fragments/6206-dconf-booleans.yml new file mode 100644 index 0000000000..92c4c930cb --- /dev/null +++ b/changelogs/fragments/6206-dconf-booleans.yml @@ -0,0 +1,2 @@ +minor_changes: + - "dconf - be forgiving about boolean values: convert them to GVariant booleans automatically (https://github.com/ansible-collections/community.general/pull/6206)." diff --git a/plugins/modules/dconf.py b/plugins/modules/dconf.py index b5ece96ec1..9777102b0e 100644 --- a/plugins/modules/dconf.py +++ b/plugins/modules/dconf.py @@ -70,13 +70,18 @@ options: description: - A dconf key to modify or read from the dconf database. value: - type: str + type: raw required: false description: - Value to set for the specified dconf key. Value should be specified in GVariant format. Due to complexity of this format, it is best to have a look at existing values in the dconf database. - Required for I(state=present). + - Although the type is specified as "raw", it should typically be + specified as a string. However, boolean values in particular are + handled properly even when specified as booleans rather than strings + (in fact, handling booleans properly is why the type of this parameter + is "raw"). state: type: str required: false @@ -155,6 +160,7 @@ except ImportError: HAS_PSUTIL = False from ansible.module_utils.basic import AnsibleModule, missing_required_lib +from ansible.module_utils.common.text.converters import to_native class DBusWrapper(object): @@ -288,6 +294,10 @@ class DconfPreference(object): Returns True if the two values are equal. """ + if canonical_value is None: + # It's unset in dconf database, so anything the user is trying to + # set is a change. + return False try: variant1 = Variant.parse(None, canonical_value) variant2 = Variant.parse(variant1.get_type(), user_value) @@ -349,7 +359,7 @@ class DconfPreference(object): rc, out, err = dbus_wrapper.run_command(command) if rc != 0: - self.module.fail_json(msg='dconf failed while write the value with error: %s' % err, + self.module.fail_json(msg='dconf failed while writing key %s, value %s with error: %s' % (key, value, err), out=out, err=err) @@ -401,11 +411,24 @@ def main(): argument_spec=dict( state=dict(default='present', choices=['present', 'absent', 'read']), key=dict(required=True, type='str', no_log=False), - value=dict(required=False, default=None, type='str'), + # Converted to str below after special handling of bool. + value=dict(required=False, default=None, type='raw'), ), supports_check_mode=True ) + # Try to be forgiving about the user specifying a boolean as the value, or + # more accurately about the fact that YAML and Ansible are quite insistent + # about converting strings that look like booleans into booleans. Convert + # the boolean into a string of the type dconf will understand. Any type for + # the value other than boolean is just converted into a string directly. + if module.params['value'] is not None: + if isinstance(module.params['value'], bool): + module.params['value'] = 'true' if module.params['value'] else 'false' + else: + module.params['value'] = to_native( + module.params['value'], errors='surrogate_or_strict') + if Variant is None: module.warn( 'WARNING: The gi.repository Python library is not available; '