From 29b11f0fa550610d4db45659960816c0e92f6634 Mon Sep 17 00:00:00 2001 From: sheidan Date: Tue, 15 Jul 2025 18:23:08 -0400 Subject: [PATCH 01/21] feat: populate a subpath from stdin using load command --- plugins/modules/dconf.py | 97 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 5 deletions(-) diff --git a/plugins/modules/dconf.py b/plugins/modules/dconf.py index 8bb9650a87..372b441253 100644 --- a/plugins/modules/dconf.py +++ b/plugins/modules/dconf.py @@ -65,11 +65,18 @@ options: - 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"). + remote_config: + type: str + required: false + description: + - Remote path to the configuration to apply. + - Required for O(state=load). + state: type: str required: false default: present - choices: ['read', 'present', 'absent'] + choices: ['read', 'load', 'present', 'absent'] description: - The action to take upon the key/value. """ @@ -122,12 +129,20 @@ EXAMPLES = r""" key: "/org/cinnamon/desktop-effects" value: "false" state: present + +- name: Load terminal profile in Gnome + community.general.dconf: + key: "/org/gnome/terminal/legacy/profiles/:" + remote_config: "/tmp/solarized_dark.dump" + state: load """ import os import sys +from configparser import ConfigParser + from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.common.respawn import ( has_respawned, @@ -224,7 +239,7 @@ class DBusWrapper(object): return None - def run_command(self, command): + def run_command(self, command, data=None): """ Runs the specified command within a functional D-Bus session. Command is effectively passed-on to AnsibleModule.run_command() method, with @@ -233,19 +248,21 @@ class DBusWrapper(object): :param command: Command to run, including parameters. Each element of the list should be a string. :type module: list + :kw data: If given, information to write to the stdin of the command + :returns: tuple(result_code, standard_output, standard_error) -- Result code, standard output, and standard error from running the command. """ if self.dbus_session_bus_address is None: self.module.debug("Using dbus-run-session wrapper for running commands.") command = [self.dbus_run_session_cmd] + command - rc, out, err = self.module.run_command(command) + rc, out, err = self.module.run_command(command, data=data) if self.dbus_session_bus_address is None and rc == 127: self.module.fail_json(msg="Failed to run passed-in command, dbus-run-session faced an internal error: %s" % err) else: extra_environment = {'DBUS_SESSION_BUS_ADDRESS': self.dbus_session_bus_address} - rc, out, err = self.module.run_command(command, environ_update=extra_environment) + rc, out, err = self.module.run_command(command, data=data, environ_update=extra_environment) return rc, out, err @@ -390,19 +407,86 @@ class DconfPreference(object): # Value was changed. return True + def load(self, key, remote_config): + """ + Load the config file in specified path. + + if an error occurs, a call will be made to AnsibleModule.fail_json. + + :param key: dconf directory for which the config should be set. + :type key: str + + :param remote_config: Remote configuration path to set for the specified dconf path. + :type value: str + + :returns: bool -- True if a change was made, False if no change was required. + """ + # Ensure key refers to a directory, as required by dconf + root_dir = key + if not root_dir.endswith('/'): + root_dir += '/' + + # Read config to check if change is needed and passing to command line + try: + with open(remote_config, 'r') as fd: + raw_config = fd.read() + except FileNotFoundError as ex: + self.module.fail_json(msg='dconf failed while reading configuration file with error: %s' % ex) + + # Parse configuratoin file + config = ConfigParser() + try: + config.read_string(raw_config) + except Exception as e: + self.module.fail_json(msg='dconf failed while reading config with error: %s' % e) + + # For each sub-directory, check if at least on change is needed + for sub_dir in config.sections(): + for sub_key, new_value in config[sub_dir].items(): + absolute_key = '%s%s/%s' % (root_dir, sub_dir, sub_key) + if not self.variants_are_equal(self.read(absolute_key), new_value): + # if at least one change is needed, load the whole config + break + else: + # No change in the sub-directory, check the next one + continue + break + else: + # No change is needed + return False + + if self.check_mode: + return True + + # Set-up command to run. Since DBus is needed for write operation, wrap + # dconf command dbus-launch. + command = [self.dconf_bin, 'load', root_dir] + + # Run the command and fetch standard return code, stdout, and stderr. + dbus_wrapper = DBusWrapper(self.module) + rc, out, err = dbus_wrapper.run_command(command, data=raw_config) + + if rc != 0: + self.module.fail_json(msg='dconf failed while load config %s, root dir %s with error: %s' % (remote_config, root_dir, err), + out=out, + err=err) + # Value was changed. + return True def main(): # Setup the Ansible module module = AnsibleModule( argument_spec=dict( - state=dict(default='present', choices=['present', 'absent', 'read']), + state=dict(default='present', choices=['present', 'absent', 'read', 'load']), key=dict(required=True, type='str', no_log=False), # Converted to str below after special handling of bool. value=dict(required=False, default=None, type='raw'), + remote_config=dict(required=False, default=None, type='str'), ), supports_check_mode=True, required_if=[ ('state', 'present', ['value']), + ('state', 'load', ['remote_config']), ], ) @@ -467,6 +551,9 @@ def main(): elif module.params['state'] == 'absent': changed = dconf.reset(module.params['key']) module.exit_json(changed=changed) + elif module.params['state'] == 'load': + changed = dconf.load(module.params['key'], module.params['remote_config']) + module.exit_json(changed=changed) if __name__ == '__main__': From 7443fe41e3dd67cb650b51cca68cf5f90140f537 Mon Sep 17 00:00:00 2001 From: sheidan Date: Thu, 17 Jul 2025 18:55:30 -0400 Subject: [PATCH 02/21] * Use 'path' convention * Simplify check of changes --- plugins/modules/dconf.py | 42 ++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/plugins/modules/dconf.py b/plugins/modules/dconf.py index 372b441253..627f6c5b7b 100644 --- a/plugins/modules/dconf.py +++ b/plugins/modules/dconf.py @@ -65,7 +65,7 @@ options: - 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"). - remote_config: + path: type: str required: false description: @@ -133,7 +133,7 @@ EXAMPLES = r""" - name: Load terminal profile in Gnome community.general.dconf: key: "/org/gnome/terminal/legacy/profiles/:" - remote_config: "/tmp/solarized_dark.dump" + path: "/tmp/solarized_dark.dump" state: load """ @@ -407,7 +407,7 @@ class DconfPreference(object): # Value was changed. return True - def load(self, key, remote_config): + def load(self, key, path): """ Load the config file in specified path. @@ -416,7 +416,7 @@ class DconfPreference(object): :param key: dconf directory for which the config should be set. :type key: str - :param remote_config: Remote configuration path to set for the specified dconf path. + :param path: Remote configuration path to set for the specified dconf path. :type value: str :returns: bool -- True if a change was made, False if no change was required. @@ -428,7 +428,7 @@ class DconfPreference(object): # Read config to check if change is needed and passing to command line try: - with open(remote_config, 'r') as fd: + with open(path, 'r') as fd: raw_config = fd.read() except FileNotFoundError as ex: self.module.fail_json(msg='dconf failed while reading configuration file with error: %s' % ex) @@ -441,22 +441,14 @@ class DconfPreference(object): self.module.fail_json(msg='dconf failed while reading config with error: %s' % e) # For each sub-directory, check if at least on change is needed - for sub_dir in config.sections(): - for sub_key, new_value in config[sub_dir].items(): - absolute_key = '%s%s/%s' % (root_dir, sub_dir, sub_key) - if not self.variants_are_equal(self.read(absolute_key), new_value): - # if at least one change is needed, load the whole config - break - else: - # No change in the sub-directory, check the next one - continue - break - else: - # No change is needed - return False + changed = any( + not self.variants_are_equal(self.read("%s%s/%s" % (root_dir, sub_dir, k)), v) + for sub_dir in config.sections() + for k, v in config[sub_dir].items() + ) - if self.check_mode: - return True + if self.check_mode or not changed: + return changed # Set-up command to run. Since DBus is needed for write operation, wrap # dconf command dbus-launch. @@ -467,11 +459,11 @@ class DconfPreference(object): rc, out, err = dbus_wrapper.run_command(command, data=raw_config) if rc != 0: - self.module.fail_json(msg='dconf failed while load config %s, root dir %s with error: %s' % (remote_config, root_dir, err), + self.module.fail_json(msg='dconf failed while load config %s, root dir %s with error: %s' % (path, root_dir, err), out=out, err=err) # Value was changed. - return True + return changed def main(): # Setup the Ansible module @@ -481,12 +473,12 @@ def main(): key=dict(required=True, type='str', no_log=False), # Converted to str below after special handling of bool. value=dict(required=False, default=None, type='raw'), - remote_config=dict(required=False, default=None, type='str'), + path=dict(required=False, default=None, type='str'), ), supports_check_mode=True, required_if=[ ('state', 'present', ['value']), - ('state', 'load', ['remote_config']), + ('state', 'load', ['path']), ], ) @@ -552,7 +544,7 @@ def main(): changed = dconf.reset(module.params['key']) module.exit_json(changed=changed) elif module.params['state'] == 'load': - changed = dconf.load(module.params['key'], module.params['remote_config']) + changed = dconf.load(module.params['key'], module.params['path']) module.exit_json(changed=changed) From b840120de04e50dad0ae296ee3197e1bd6ad1833 Mon Sep 17 00:00:00 2001 From: sheidan Date: Sat, 19 Jul 2025 04:05:19 -0400 Subject: [PATCH 03/21] Fix typo --- plugins/modules/dconf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/dconf.py b/plugins/modules/dconf.py index 627f6c5b7b..a20298ed18 100644 --- a/plugins/modules/dconf.py +++ b/plugins/modules/dconf.py @@ -440,7 +440,7 @@ class DconfPreference(object): except Exception as e: self.module.fail_json(msg='dconf failed while reading config with error: %s' % e) - # For each sub-directory, check if at least on change is needed + # For each sub-directory, check if at least one change is needed changed = any( not self.variants_are_equal(self.read("%s%s/%s" % (root_dir, sub_dir, k)), v) for sub_dir in config.sections() From 63f22d3a2fb4c7b06e8699c65bdb02565b45517b Mon Sep 17 00:00:00 2001 From: sheidan Date: Sat, 19 Jul 2025 04:27:50 -0400 Subject: [PATCH 04/21] Add changelog fragment --- changelogs/fragments/10432-dconf-load-subpath.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 changelogs/fragments/10432-dconf-load-subpath.yml diff --git a/changelogs/fragments/10432-dconf-load-subpath.yml b/changelogs/fragments/10432-dconf-load-subpath.yml new file mode 100644 index 0000000000..1a43005d90 --- /dev/null +++ b/changelogs/fragments/10432-dconf-load-subpath.yml @@ -0,0 +1,2 @@ +minor_changes: + - dconf - adds support of `load` to populate a subpath from file From 6f8ef185415ec6a15a7e90b80f6eb9fad5833c3d Mon Sep 17 00:00:00 2001 From: sheidan Date: Sat, 19 Jul 2025 04:38:12 -0400 Subject: [PATCH 05/21] Fix pycodestyle --- plugins/modules/dconf.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/plugins/modules/dconf.py b/plugins/modules/dconf.py index a20298ed18..9e6dfc874e 100644 --- a/plugins/modules/dconf.py +++ b/plugins/modules/dconf.py @@ -444,11 +444,11 @@ class DconfPreference(object): changed = any( not self.variants_are_equal(self.read("%s%s/%s" % (root_dir, sub_dir, k)), v) for sub_dir in config.sections() - for k, v in config[sub_dir].items() + for k, v in config[sub_dir].items() ) if self.check_mode or not changed: - return changed + return changed # Set-up command to run. Since DBus is needed for write operation, wrap # dconf command dbus-launch. @@ -460,10 +460,11 @@ class DconfPreference(object): if rc != 0: self.module.fail_json(msg='dconf failed while load config %s, root dir %s with error: %s' % (path, root_dir, err), - out=out, - err=err) + out=out, + err=err) # Value was changed. - return changed + return changed + def main(): # Setup the Ansible module From 266b62b90d70b9ebdec758d037ad8c87677ef126 Mon Sep 17 00:00:00 2001 From: Sheidan <37596668+Sh3idan@users.noreply.github.com> Date: Sat, 19 Jul 2025 13:28:30 +0200 Subject: [PATCH 06/21] Fix path type in doc Co-authored-by: Felix Fontein --- plugins/modules/dconf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/dconf.py b/plugins/modules/dconf.py index 9e6dfc874e..a7f067fd85 100644 --- a/plugins/modules/dconf.py +++ b/plugins/modules/dconf.py @@ -66,7 +66,7 @@ options: 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"). path: - type: str + type: path required: false description: - Remote path to the configuration to apply. From 84df105a3d350002c5d02c3c3b3d4033fa940d3d Mon Sep 17 00:00:00 2001 From: sheidan Date: Sat, 19 Jul 2025 07:32:59 -0400 Subject: [PATCH 07/21] Fix path and value are mutually exclusive --- plugins/modules/dconf.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/modules/dconf.py b/plugins/modules/dconf.py index a7f067fd85..9d6ff79f2f 100644 --- a/plugins/modules/dconf.py +++ b/plugins/modules/dconf.py @@ -481,6 +481,9 @@ def main(): ('state', 'present', ['value']), ('state', 'load', ['path']), ], + mutually_exclusive=[ + ['value', 'path'] + ], ) if Variant is None: From 3f373704cc0b775662aae87fbfc36d4b4001a5a7 Mon Sep 17 00:00:00 2001 From: sheidan Date: Sat, 19 Jul 2025 09:58:54 -0400 Subject: [PATCH 08/21] Fix trailing spaces --- changelogs/fragments/10432-dconf-load-subpath.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/fragments/10432-dconf-load-subpath.yml b/changelogs/fragments/10432-dconf-load-subpath.yml index 1a43005d90..bba52dbbe7 100644 --- a/changelogs/fragments/10432-dconf-load-subpath.yml +++ b/changelogs/fragments/10432-dconf-load-subpath.yml @@ -1,2 +1,2 @@ minor_changes: - - dconf - adds support of `load` to populate a subpath from file + - dconf - adds support of `load` to populate a subpath from file From 840fbf8c2733dff012e29f6eacaf1d11998aca22 Mon Sep 17 00:00:00 2001 From: sheidan Date: Sat, 19 Jul 2025 10:10:12 -0400 Subject: [PATCH 09/21] * Use state=present instead of state=load * Mutual exclusion of 'path' and 'value' --- plugins/modules/dconf.py | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/plugins/modules/dconf.py b/plugins/modules/dconf.py index 9d6ff79f2f..b57e8b8b2f 100644 --- a/plugins/modules/dconf.py +++ b/plugins/modules/dconf.py @@ -61,7 +61,7 @@ options: 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 O(state=present). + - Required for O(state=present). If provided, O(path) is not required. - 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"). @@ -70,13 +70,13 @@ options: required: false description: - Remote path to the configuration to apply. - - Required for O(state=load). + - Required for O(state=present). If provided, O(value) is not required. state: type: str required: false default: present - choices: ['read', 'load', 'present', 'absent'] + choices: ['read', 'present', 'absent'] description: - The action to take upon the key/value. """ @@ -134,7 +134,7 @@ EXAMPLES = r""" community.general.dconf: key: "/org/gnome/terminal/legacy/profiles/:" path: "/tmp/solarized_dark.dump" - state: load + state: present """ @@ -470,16 +470,15 @@ def main(): # Setup the Ansible module module = AnsibleModule( argument_spec=dict( - state=dict(default='present', choices=['present', 'absent', 'read', 'load']), + state=dict(default='present', choices=['present', 'absent', 'read']), key=dict(required=True, type='str', no_log=False), # Converted to str below after special handling of bool. value=dict(required=False, default=None, type='raw'), - path=dict(required=False, default=None, type='str'), + path=dict(required=False, default=None, type='path'), ), supports_check_mode=True, required_if=[ - ('state', 'present', ['value']), - ('state', 'load', ['path']), + ('state', 'present', ['value', 'path'], True), ], mutually_exclusive=[ ['value', 'path'] @@ -539,17 +538,27 @@ def main(): # Process based on different states. if module.params['state'] == 'read': + # TODO: Handle this case when 'state=present' and 'key' is the only one value = dconf.read(module.params['key']) module.exit_json(changed=False, value=value) elif module.params['state'] == 'present': - changed = dconf.write(module.params['key'], module.params['value']) - module.exit_json(changed=changed) + if module.params['path']: + # Use 'dconf load' to propagate multiple entries from the root given by 'key' + changed = dconf.load(module.params['key'], module.params['path']) + module.exit_json(changed=changed) + elif module.params['value']: + # Use 'dconf write' to modify the key + changed = dconf.write(module.params['key'], module.params['value']) + module.exit_json(changed=changed) + else: + # NOTE: This case shouldn't happen yet as 'key' and 'path' are + # required with 'state=present' + # TODO: if 'key' ends with '/' then 'dconf list' should be used + # else, 'dconf read' + module.fail_json(msg="'key' or 'path' must be defined.") elif module.params['state'] == 'absent': changed = dconf.reset(module.params['key']) module.exit_json(changed=changed) - elif module.params['state'] == 'load': - changed = dconf.load(module.params['key'], module.params['path']) - module.exit_json(changed=changed) if __name__ == '__main__': From a7dd03ffc57c816f8092dcca8e52d83599b4b2a0 Mon Sep 17 00:00:00 2001 From: sheidan Date: Sun, 20 Jul 2025 13:10:41 -0400 Subject: [PATCH 10/21] Fix issue when creating dconf key --- plugins/modules/dconf.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/modules/dconf.py b/plugins/modules/dconf.py index b57e8b8b2f..7fe71c0b50 100644 --- a/plugins/modules/dconf.py +++ b/plugins/modules/dconf.py @@ -421,6 +421,13 @@ class DconfPreference(object): :returns: bool -- True if a change was made, False if no change was required. """ + def _create_dconf_key(root, sub_dir, key): + # Root should end with '/' + if sub_dir == "/": + # No sub directory + return "%s%s" % (root, key) + return "%s%s/%s" % (root, sub_dir, key) + # Ensure key refers to a directory, as required by dconf root_dir = key if not root_dir.endswith('/'): @@ -442,7 +449,7 @@ class DconfPreference(object): # For each sub-directory, check if at least one change is needed changed = any( - not self.variants_are_equal(self.read("%s%s/%s" % (root_dir, sub_dir, k)), v) + not self.variants_are_equal(self.read(_create_dconf_key(root_dir, sub_dir, k)), v) for sub_dir in config.sections() for k, v in config[sub_dir].items() ) From fffdfb48903270390e3d9bc65b5c9c8a2756d381 Mon Sep 17 00:00:00 2001 From: Sheidan <37596668+Sh3idan@users.noreply.github.com> Date: Sun, 20 Jul 2025 21:49:22 +0200 Subject: [PATCH 11/21] Update 'value' description Co-authored-by: Felix Fontein --- plugins/modules/dconf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/dconf.py b/plugins/modules/dconf.py index 7fe71c0b50..02a34e7703 100644 --- a/plugins/modules/dconf.py +++ b/plugins/modules/dconf.py @@ -61,7 +61,7 @@ options: 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 O(state=present). If provided, O(path) is not required. + - One of O(value) and O(path) are required for O(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"). From 8bdcdc8328f48bf92a5745029e09e5ef2ab47624 Mon Sep 17 00:00:00 2001 From: Sheidan <37596668+Sh3idan@users.noreply.github.com> Date: Sun, 20 Jul 2025 21:49:50 +0200 Subject: [PATCH 12/21] Update 'path' description Co-authored-by: Felix Fontein --- plugins/modules/dconf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/dconf.py b/plugins/modules/dconf.py index 02a34e7703..e7417071e8 100644 --- a/plugins/modules/dconf.py +++ b/plugins/modules/dconf.py @@ -70,7 +70,7 @@ options: required: false description: - Remote path to the configuration to apply. - - Required for O(state=present). If provided, O(value) is not required. + - One of O(value) and O(path) are required for O(state=present). state: type: str From d86098674badbce7646b72e5879e0ab42b3f0df0 Mon Sep 17 00:00:00 2001 From: Sheidan <37596668+Sh3idan@users.noreply.github.com> Date: Sun, 20 Jul 2025 21:53:36 +0200 Subject: [PATCH 13/21] Add trailing comma to avoid future diff Co-authored-by: Felix Fontein --- plugins/modules/dconf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/dconf.py b/plugins/modules/dconf.py index e7417071e8..b6fb2c7c80 100644 --- a/plugins/modules/dconf.py +++ b/plugins/modules/dconf.py @@ -488,7 +488,7 @@ def main(): ('state', 'present', ['value', 'path'], True), ], mutually_exclusive=[ - ['value', 'path'] + ['value', 'path'], ], ) From bc32bd4ce120c862e0cbe33ba91203d981288299 Mon Sep 17 00:00:00 2001 From: Sheidan <37596668+Sh3idan@users.noreply.github.com> Date: Sun, 20 Jul 2025 22:21:07 +0200 Subject: [PATCH 14/21] Update: handling IO operation errors This update allows: * Compatibility between python2 and python3 * To handle IO operation errors Co-authored-by: Felix Fontein --- plugins/modules/dconf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/dconf.py b/plugins/modules/dconf.py index b6fb2c7c80..08860a1ace 100644 --- a/plugins/modules/dconf.py +++ b/plugins/modules/dconf.py @@ -437,7 +437,7 @@ class DconfPreference(object): try: with open(path, 'r') as fd: raw_config = fd.read() - except FileNotFoundError as ex: + except IOError as ex: self.module.fail_json(msg='dconf failed while reading configuration file with error: %s' % ex) # Parse configuratoin file From 236edae0714189fd127eb6b00e6e06d01f842cf3 Mon Sep 17 00:00:00 2001 From: Sheidan <37596668+Sh3idan@users.noreply.github.com> Date: Sun, 20 Jul 2025 22:32:09 +0200 Subject: [PATCH 15/21] Fix error message when reading config file failed Co-authored-by: Felix Fontein --- plugins/modules/dconf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/dconf.py b/plugins/modules/dconf.py index 08860a1ace..bad3d48d83 100644 --- a/plugins/modules/dconf.py +++ b/plugins/modules/dconf.py @@ -438,7 +438,7 @@ class DconfPreference(object): with open(path, 'r') as fd: raw_config = fd.read() except IOError as ex: - self.module.fail_json(msg='dconf failed while reading configuration file with error: %s' % ex) + self.module.fail_json(msg='Failed while reading configuration file %s with error: %s' % (path, ex)) # Parse configuratoin file config = ConfigParser() From 7e3f6756be61d60f9be06747e19f10bd9fe76142 Mon Sep 17 00:00:00 2001 From: Sheidan <37596668+Sh3idan@users.noreply.github.com> Date: Sun, 20 Jul 2025 22:35:55 +0200 Subject: [PATCH 16/21] Fix typo in a comment Co-authored-by: Felix Fontein --- plugins/modules/dconf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/dconf.py b/plugins/modules/dconf.py index bad3d48d83..ea2d140704 100644 --- a/plugins/modules/dconf.py +++ b/plugins/modules/dconf.py @@ -440,7 +440,7 @@ class DconfPreference(object): except IOError as ex: self.module.fail_json(msg='Failed while reading configuration file %s with error: %s' % (path, ex)) - # Parse configuratoin file + # Parse configuration file config = ConfigParser() try: config.read_string(raw_config) From abba3026304db5146905385be2100f3a6bcb545b Mon Sep 17 00:00:00 2001 From: Sheidan <37596668+Sh3idan@users.noreply.github.com> Date: Sun, 20 Jul 2025 22:38:21 +0200 Subject: [PATCH 17/21] Fix configparser package to support py2 and py3 Co-authored-by: Felix Fontein --- plugins/modules/dconf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/dconf.py b/plugins/modules/dconf.py index ea2d140704..a28db55e33 100644 --- a/plugins/modules/dconf.py +++ b/plugins/modules/dconf.py @@ -141,7 +141,7 @@ EXAMPLES = r""" import os import sys -from configparser import ConfigParser +from ansible.module_utils.six.moves.configparser import ConfigParser from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.common.respawn import ( From 6e1013568b4ed60d6183c57ca132a4f6333b6d8d Mon Sep 17 00:00:00 2001 From: Sheidan <37596668+Sh3idan@users.noreply.github.com> Date: Sun, 20 Jul 2025 22:38:51 +0200 Subject: [PATCH 18/21] Fix error message when parsing config failed Co-authored-by: Felix Fontein --- plugins/modules/dconf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/dconf.py b/plugins/modules/dconf.py index a28db55e33..c1c4512dc4 100644 --- a/plugins/modules/dconf.py +++ b/plugins/modules/dconf.py @@ -445,7 +445,7 @@ class DconfPreference(object): try: config.read_string(raw_config) except Exception as e: - self.module.fail_json(msg='dconf failed while reading config with error: %s' % e) + self.module.fail_json(msg='Failed while parsing config with error: %s' % e) # For each sub-directory, check if at least one change is needed changed = any( From 8177c0ae98fdfcc4cc6c35a8e9a8960ddeea3b0a Mon Sep 17 00:00:00 2001 From: Sheidan <37596668+Sh3idan@users.noreply.github.com> Date: Tue, 22 Jul 2025 21:47:44 +0200 Subject: [PATCH 19/21] Fix redundant args for 'value' and 'path' Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> --- plugins/modules/dconf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/modules/dconf.py b/plugins/modules/dconf.py index c1c4512dc4..be18f37d2c 100644 --- a/plugins/modules/dconf.py +++ b/plugins/modules/dconf.py @@ -480,8 +480,8 @@ def main(): state=dict(default='present', choices=['present', 'absent', 'read']), key=dict(required=True, type='str', no_log=False), # Converted to str below after special handling of bool. - value=dict(required=False, default=None, type='raw'), - path=dict(required=False, default=None, type='path'), + value=dict(type='raw'), + path=dict(type='path'), ), supports_check_mode=True, required_if=[ From 07a6c799ddec855c09502149974299758fb3cb70 Mon Sep 17 00:00:00 2001 From: Sheidan <37596668+Sh3idan@users.noreply.github.com> Date: Tue, 22 Jul 2025 21:54:29 +0200 Subject: [PATCH 20/21] Fix fragment format Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> --- changelogs/fragments/10432-dconf-load-subpath.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/fragments/10432-dconf-load-subpath.yml b/changelogs/fragments/10432-dconf-load-subpath.yml index bba52dbbe7..33bd4c41ab 100644 --- a/changelogs/fragments/10432-dconf-load-subpath.yml +++ b/changelogs/fragments/10432-dconf-load-subpath.yml @@ -1,2 +1,2 @@ minor_changes: - - dconf - adds support of `load` to populate a subpath from file + - dconf - adds support of ``load`` to populate a subpath from file (https://github.com/ansible-collections/community.general/pull/10432). From 588eb3c52bb025f4cba10ad0787ecbac72252367 Mon Sep 17 00:00:00 2001 From: sheidan Date: Tue, 22 Jul 2025 15:56:03 -0400 Subject: [PATCH 21/21] * Remove redundant type in the 'path' documentation * Remove unreachable code --- plugins/modules/dconf.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/plugins/modules/dconf.py b/plugins/modules/dconf.py index be18f37d2c..4910fbc88e 100644 --- a/plugins/modules/dconf.py +++ b/plugins/modules/dconf.py @@ -67,7 +67,6 @@ options: is why the type of this parameter is "raw"). path: type: path - required: false description: - Remote path to the configuration to apply. - One of O(value) and O(path) are required for O(state=present). @@ -557,12 +556,6 @@ def main(): # Use 'dconf write' to modify the key changed = dconf.write(module.params['key'], module.params['value']) module.exit_json(changed=changed) - else: - # NOTE: This case shouldn't happen yet as 'key' and 'path' are - # required with 'state=present' - # TODO: if 'key' ends with '/' then 'dconf list' should be used - # else, 'dconf read' - module.fail_json(msg="'key' or 'path' must be defined.") elif module.params['state'] == 'absent': changed = dconf.reset(module.params['key']) module.exit_json(changed=changed)