From 36ab77d4541c4aecba80655e3a00396b4ac153d5 Mon Sep 17 00:00:00 2001 From: Joseph Benden Date: Wed, 19 Sep 2018 15:10:22 -0700 Subject: [PATCH] New module: Xfconf-based management of the Xfce 4 DE (#28112) --- lib/ansible/modules/system/xfconf.py | 215 +++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 lib/ansible/modules/system/xfconf.py diff --git a/lib/ansible/modules/system/xfconf.py b/lib/ansible/modules/system/xfconf.py new file mode 100644 index 00000000000..66c44ba0e4f --- /dev/null +++ b/lib/ansible/modules/system/xfconf.py @@ -0,0 +1,215 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# (c) 2017, Joseph Benden +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = """ +module: xfconf +author: + - "Joseph Benden (@jbenden)" +short_description: Edit XFCE4 Configurations +description: + - This module allows for the manipulation of Xfce 4 Configuration via + xfconf-query. Please see the xfconf-query(1) man pages for more details. +version_added: "2.8" +options: + channel: + description: + - A Xfconf preference channel is a top-level tree key, inside of the + Xfconf repository that corresponds to the location for which all + application properties/keys are stored. See man xfconf-query(1) + required: yes + property: + description: + - A Xfce preference key is an element in the Xfconf repository + that corresponds to an application preference. See man xfconf-query(1) + required: yes + value: + description: + - Preference properties typically have simple values such as strings, + integers, or lists of strings and integers. This is ignored if the state + is "get". See man xfconf-query(1) + value_type: + description: + - The type of value being set. This is ignored if the state is "get". + choices: [ int, bool, float, string ] + state: + description: + - The action to take upon the property/value. + choices: [ get, present, absent ] + default: "present" +""" + +EXAMPLES = """ +- name: Change the DPI to "192" + xfconf: + channel: "xsettings" + property: "/Xft/DPI" + value_type: "int" + value: "192" + become: True + become_user: johnsmith + +""" + +RETURN = ''' + channel: + description: The channel specified in the module parameters + returned: success + type: string + sample: "xsettings" + property: + description: The property specified in the module parameters + returned: success + type: string + sample: "/Xft/DPI" + value_type: + description: The type of the value that was changed + returned: success + type: string + sample: "int" + value: + description: The value of the preference key after executing the module + returned: success + type: string + sample: "192" +... +''' + +import pipes +import sys + +from ansible.module_utils.basic import AnsibleModule + + +class XfconfPreference(object): + def __init__(self, module, channel, property, value_type, value): + self.module = module + self.channel = channel + self.property = property + self.value_type = value_type + self.value = value + + def call(self, call_type, fail_onerr=True): + """ Helper function to perform xfconf-query operations """ + changed = False + out = '' + + # Execute the call + cmd = "{0} --channel {1} --property {2}".format(self.module.get_bin_path('xfconf-query', True), + pipes.quote(self.channel), + pipes.quote(self.property)) + try: + if call_type == 'set': + cmd += " --type {0} --create --set {1}".format(pipes.quote(self.value_type), + pipes.quote(self.value)) + elif call_type == 'unset': + cmd += " --reset" + + # Start external command + rc, out, err = self.module.run_command(cmd, check_rc=False) + + if rc != 0 or len(err) > 0: + if fail_onerr: + self.module.fail_json(msg='xfconf-query failed with error: %s' % (str(err))) + else: + changed = True + + except OSError as exception: + self.module.fail_json(msg='xfconf-query failed with exception: %s' % exception) + return changed, out.rstrip() + + +def main(): + # Setup the Ansible module + module = AnsibleModule( + argument_spec=dict( + channel=dict(required=True, type='str'), + property=dict(required=True, type='str'), + value_type=dict(required=False, + choices=['int', 'bool', 'float', 'string'], + type='str'), + value=dict(required=False, default=None, type='str'), + state=dict(default='present', + choices=['present', 'get', 'absent'], + type='str') + ), + supports_check_mode=True + ) + + state_values = {"present": "set", "absent": "unset", "get": "get"} + + # Assign module values to dictionary values + channel = module.params['channel'] + property = module.params['property'] + value_type = module.params['value_type'] + if module.params['value'].lower() == "true": + value = "true" + elif module.params['value'] == "false": + value = "false" + else: + value = module.params['value'] + + state = state_values[module.params['state']] + + # Initialize some variables for later + change = False + new_value = '' + + if state != "get": + if value is None or value == "": + module.fail_json(msg='State %s requires "value" to be set' + % str(state)) + elif value_type is None or value_type == "": + module.fail_json(msg='State %s requires "value_type" to be set' + % str(state)) + + # Create a Xfconf preference + xfconf = XfconfPreference(module, + channel, + property, + value_type, + value) + # Now we get the current value, if not found don't fail + dummy, current_value = xfconf.call("get", fail_onerr=False) + + # Check if the current value equals the value we want to set. If not, make + # a change + if current_value != value: + # If check mode, we know a change would have occurred. + if module.check_mode: + # So we will set the change to True + change = True + # And set the new_value to the value that would have been set + new_value = value + # If not check mode make the change. + else: + change, new_value = xfconf.call(state) + # If the value we want to set is the same as the current_value, we will + # set the new_value to the current_value for reporting + else: + new_value = current_value + + facts = dict(xfconf={'changed': change, + 'channel': channel, + 'property': property, + 'value_type': value_type, + 'new_value': new_value, + 'previous_value': current_value, + 'playbook_value': module.params['value']}) + + module.exit_json(changed=change, ansible_facts=facts) + + +if __name__ == '__main__': + main()