Giter Club home page Giter Club logo

ansible-gsetting's Introduction

ansible-gsetting

Ansible module for setting GSettings entries.

See also ansible-dconf.

Installation

curl https://raw.githubusercontent.com/jistr/ansible-gsetting/master/gsetting.py > ~/ansible_dir/library/gsetting

Usage examples

# Embed the schema in the key
- name: turn off hot corners
  gsetting:
    user: jistr
    key: org.gnome.desktop.interface.enable-hot-corners
    value: "{{'false'|string}}"
    
# Specify the schema separately
- name: turn off hot corners
  gsetting:
    user: jistr
    schema: org.gnome.desktop.interface
    key: enable-hot-corners
    value: "{{'false'|string}}"

# Use a relocatable schema
- name: set custom key binding
  gsetting:
    user: jistr
    schema: org.gnome.settings-daemon.plugins.media-keys.custom-keybinding
    path: /org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/
    settings:
      name: Flameshot
      binding: Print
      command: flameshot gui

- name: do not remember mount password
  gsetting:
    user: jistr
    settings:
      org.gnome.shell.remember-mount-password: false
      org.gnome.desktop.wm.keybindings.panel-main-menu: "@as []"
      org.gnome.nautilus.preferences.default-folder-viewer: "'list-view'"

If you want to run Ansible as the user you want to adjust settings for, you should omit the user parameter:

- name: shortcut panel-main-menu
  gsetting:
    settings:
      org.gnome.desktop.wm.keybindings.panel-main-menu: "@as []"

Be careful with string values, which should be passed into GSetting single-quoted. You'll need to quote the value twice in YAML:

- name: nautilus use list view
  gsetting:
    user: jistr
    settings:
      org.gnome.nautilus.preferences.default-folder-viewer: "'list-view'"

- name: nautilus list view columns
  gsetting:
    user: jistr
    settings:
      org.gnome.nautilus.list-view.default-visible-columns: "['name', 'size', 'date_modified', 'permissions', 'owner', 'group']"

ansible-gsetting's People

Contributors

clifford2 avatar ian2020 avatar jikamens avatar jistr avatar ttencate avatar yannik avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

ansible-gsetting's Issues

Setting key fails when fish shell is user's default shell

When I try to run task like this:

- name: new termnial shortcut
  gsetting:
    user: me
    key: "org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/.name"
    value: "New terminal"

I get this error:

TASK [gnome : apply new term] ************************************************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "failed": true, "module_stderr": "Password: su: Authentication failure\nTraceback (most recent call last):\n  File \"/tmp/ansible_g501K0/ansible_module_gsetting.py\", line 81, in <module>\n    main()\n  File \"/tmp/ansible_g501K0/ansible_module_gsetting.py\", line 68, in main\n    old_value = _get_value(user, key)\n  File \"/tmp/ansible_g501K0/ansible_module_gsetting.py\", line 47, in _get_value\n    'su', '-', user , '-c', command\n  File \"/usr/lib64/python2.7/subprocess.py\", line 219, in check_output\n    raise CalledProcessError(retcode, cmd, output=output)\nsubprocess.CalledProcessError: Command '['su', '-', 'me', '-c', 'export `/usr/bin/dbus-launch` ; /usr/bin/gsettings get org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/ name ; kill $DBUS_SESSION_BUS_PID &> /dev/null']' returned non-zero exit status 1\n", "module_stdout": "", "msg": "MODULE FAILURE", "rc": 0}

The important part is this:

subprocess.CalledProcessError:
Command 
'['su', '-', 'me', '-c', 'export `/usr/bin/dbus-launch` ;
/usr/bin/gsettings get org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/ name ;
kill $DBUS_SESSION_BUS_PID &> /dev/null']' returned non-zero exit status 1

and this is because fish shell doesn't support backticks (they come up in export `/usr/bin/dbus-launch` )

Apply multiple settings at once

I would like to apply multiple settings at once. I have up to 60 settings for some users and it can take over 40 seconds to apply via a loop over this module, a significant chunk of my ansible run. The code in the suggested PR brings this down to around 6 seconds as ansible only runs the module once, and we avoid repeated calls to fetch the dbus session.

I would suggest this requires a new and breaking argument spec such that a task would look like this:

- name: apply my GNOME settings
  gsetting:
    user: jistr
    settings:
      org.gnome.shell.remember-mount-password: false
      org.gnome.desktop.wm.keybindings.panel-main-menu: "@as []"
      org.gnome.nautilus.preferences.default-folder-viewer: "'list-view'"

The module will return a status of changed when any one of the given settings has been updated.

Here's a real world example play where we call the module and then debug the results:

- name: Apply GNOME settings
  become: yes
  gsetting:
    settings:
      # Press power button, should no nothing
      org.gnome.settings-daemon.plugins.power.power-button-action:  "'nothing'"
      # Setup Night Light
      org.gnome.settings-daemon.plugins.color.night-light-schedule-automatic: "false"
      org.gnome.settings-daemon.plugins.color.night-light-schedule-to: "7.0"
      # Extension: Freon
      org.gnome.shell.extensions.freon.show-decimal-value: "false"
      org.gnome.shell.extensions.freon.position-in-panel: "'left'"
  register: gsettings
- debug: var=gsettings

And the corresponding ansible output when two settings are changed and the rest are not:

TASK [Apply GNOME settings] ******************************************************************************
changed: [localhost]

TASK [debug] **********************************************************************************************************
ok: [localhost] => {
    "gsettings": {
        "changed": true,
        "changed_settings": [
            {
                "key": "org.gnome.shell.extensions.freon.show-decimal-value",
                "new_value": "false",
                "value": "true"
            },
            {
                "key": "org.gnome.shell.extensions.freon.position-in-panel",
                "new_value": "'left'",
                "value": "'right'"
            }
        ],
        "failed": false,
        "unchanged_settings": [
            {
                "key": "org.gnome.settings-daemon.plugins.power.power-button-action",
                "value": "'nothing'"
            },
            {
                "key": "org.gnome.settings-daemon.plugins.color.night-light-schedule-automatic",
                "value": "false"
            },
            {
                "key": "org.gnome.settings-daemon.plugins.color.night-light-schedule-to",
                "value": "7.0"
            }
        ]
    }
}

Let me know what you think, it's only a suggestion and I'll understand if it's not the right direction for this module. There aren't many ansible modules that apply multiple changes at once that I've found.

Gsettings fails on exit

When setting a value using ansible on ubuntu 18.04 and 18.10 the exit routine seems to not return pass, but the setting key actually changes
fatal: [localhost]: FAILED! => { "changed": false, "module_stderr": "", "module_stdout": "{\"changed\": false, \"old_value\": \"false\", \"value\": \"false\", \"key\": \"org.gnome.desktop.media-handling.automount-open\"}\n{\"msg\": \"New-style module did not handle its own exit\", \"failed\": true}\n", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1 }

Fails on Fedora 31 when user is specified

This is the error seen:

...subprocess.CalledProcessError: Command '['grep', '-z', '^DBUS_SESSION_BUS_ADDRESS', '/proc/181555\\n181619\\n181622/environ']' returned non-zero exit status 2.

It appears that the work to get the PID of the gnome session on line 29:

    pid = _check_output_strip(['pgrep', '-u', user, 'gnome-session'])

...now returns three processes instead of one on Fedora 31.

I think this is due to a change in GNOME 3.33.90 where new binaries were added/renamed to help with systemd, e.g. this commit which introduced new binary gnome-session-ctl.

I have a suggested fix to get the GNOME version with a new function:

def _get_gnome_version():
    return tuple(map(int, (_check_output_strip(
        ['gnome-shell', '--version']).split(' ')[2].split('.'))))

And change the pgrep cmd depending on it:

    pgrep_cmd = ['pgrep', '-u', user, 'gnome-session']
    gnome_ver = _get_gnome_version()
    if (gnome_ver >= (3, 33, 90)):
        # From GNOME 3.33.90 session process has changed
        # https://github.com/GNOME/gnome-session/releases/tag/3.33.90
        pgrep_cmd = ['pgrep', '-u', user, '-f', 'session=gnome']

I have tested this and it works on Fedora 30 (GNOME 3.32.2) which is prior to the GNOME change and after on Fedora 31 (GNOME 3.34.3).

I can raise a pull request against this issue but please let me know any feedback. Perhaps there is a better way to get the user's GNOME session without pgrep?

cannot change settings with desktop-specific defaults

On a Debian 11 system, I found that the following code had no effect:

- gsetting:
    user: jrandom
    settings:
      org.gnome.mutter.focus-change-on-pointer-rest: "false"

This happens because the default value of some GSettings depends on the environment variable XDG_CURRENT_DESKTOP.

When ansible-gsetting invokes gsettings for another user, XDG_CURRENT_DESKTOP is unset. On my (Debian 11) system, that makes the default value of the focus-change-on-pointer-rest setting false. So ansible-gsetting decides to do nothing.

When my user jrandom is logged in, however, XDG_CURRENT_DESKTOP is set to the value GNOME, which changes the default of focus-change-on-pointer-rest to true. So now the effective setting for jrandom is the opposite of what's in the playbook.

You can see the difference in behaviour of gsettings here:

$ gsettings reset org.gnome.mutter focus-change-on-pointer-rest
$ echo $XDG_CURRENT_DESKTOP
GNOME
$ gsettings get org.gnome.mutter focus-change-on-pointer-rest
true
$ unset XDG_CURRENT_DESKTOP
$ gsettings get org.gnome.mutter focus-change-on-pointer-rest
false
$

(I guess that happens because there is a [org.gnome.mutter:GNOME] section in /usr/share/glib-2.0/schemas/00_org.gnome.shell.gschema.override which modifies the defaults of a handful of settings.)

Support specifying both schema and path

I'd like to issue basically this command

$ gsettings get org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/ name

But I have to fake this with the setting

- name: new terminal shortcut
  gsetting:
    user: me
    key: "org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/.name"
    value: "New terminal"

The path must be specified in the case, since the schema for custom keyboard shortcuts is relocatable:

$ gsettings get org.gnome.settings-daemon.plugins.media-keys.custom-keybinding name
Schema “org.gnome.settings-daemon.plugins.media-keys.custom-keybinding” is relocatable (path must be specified)

Module Fails on Ubuntu 20.04 Remote

Module fails when deploying to an Ubuntu 20.04 host.

Example Failure Message:
Using module file <redacted>/gsetting.py raise CalledProcessError(retcode, cmd, output=output)\nsubprocess.CalledProcessError: Command '['grep', '-z', '^DBUS_SESSION_BUS_ADDRESS', '/proc/2153\\n2257\\n2276/environ']' returned non-zero exit status 2\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1 }

Upon closer examination of the running processes on Ubuntu 20.04, I found that there are three PIDs that match gnome-session and so the path gets mangled.

Setting values are being interpreted by Python under recent ansible/python

Under Fedora 37 (ansible 7.1, python 3.11.1) if your setting value resembles a Python type, e.g. a list like "[ 'some value' ]" then it gets interpreted as one, leading to an error inside gsetting. This seems quite likely given how many gsettings have the same syntax as Python lists.

This is NOT a problem under Fedora 36 (ansible 5.9, python 3.10.9).

Example task:

- name: set tracker directories
  become: yes
  gsetting:
    user: "me"
    settings:
      "org.freedesktop.Tracker3.Miner.Files.index-recursive-directories": "['&DOCUMENTS', '&DOWNLOAD', '&MUSIC', '&PICTURES']"

Gives:

Traceback (most recent call last):
  File \"/home/me/.ansible/tmp/ansible-tmp-1675943488.5975382-83483-120901875465516/AnsiballZ_gsetting\", line 107, in <module>
    _ansiballz_main()
  File \"/home/me/.ansible/tmp/ansible-tmp-1675943488.5975382-83483-120901875465516/AnsiballZ_gsetting\", line 99, in _ansiballz_main
    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
  File \"/home/me/.ansible/tmp/ansible-tmp-1675943488.5975382-83483-120901875465516/AnsiballZ_gsetting\", line 47, in invoke_module
    runpy.run_module(mod_name='ansible.modules.gsetting', init_globals=dict(_module_fqn='ansible.modules.gsetting', _modlib_path=modlib_path),
  File \"<frozen runpy>\", line 226, in run_module
  File \"<frozen runpy>\", line 98, in _run_module_code
  File \"<frozen runpy>\", line 88, in _run_code
  File \"/tmp/ansible_gsetting_payload_z_wkixsm/ansible_gsetting_payload.zip/ansible/modules/gsetting.py\", line 136, in <module>
  File \"/tmp/ansible_gsetting_payload_z_wkixsm/ansible_gsetting_payload.zip/ansible/modules/gsetting.py\", line 123, in main
  File \"/tmp/ansible_gsetting_payload_z_wkixsm/ansible_gsetting_payload.zip/ansible/modules/gsetting.py\", line 68, in _set_value
  File \"/tmp/ansible_gsetting_payload_z_wkixsm/ansible_gsetting_payload.zip/ansible/modules/gsetting.py\", line 13, in _escape_single_quotes
  File \"/usr/lib64/python3.11/re/__init__.py\", line 185, in sub
    return _compile(pattern, flags).sub(repl, string, count)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: expected string or bytes-like object, got 'list'

I believe this is due to the value: "['&DOCUMENTS', '&DOWNLOAD', '&MUSIC', '&PICTURES']" becoming a Python list by the time it's fed to the gsetting module.

I don't know why this has changed - must be something inside ansible or python.

Looking at the history of the ansible code that launches modules
lib/ansible/executor/module_common.py there has been some changes around dropping python2 support that may have altered the behaviour of how arguments are interpreted/passed but I would be surprised if this hadn't affected loads of other modules too.

This can be 'fixed' in gsetting by forcing the value back to a string:

if type(value) is not str:
    value=str(value)

...but this seems a total hack. A better approach might be to use argument_spec in the module to force values to be strings but this is not supported for the dict type.

Settings changes not are not showing up in graphical Dconf editor, nor are they keeping their value

If I use your module then I see that according to a gsettings get that the value appears set. However the graphical Dconf editor doesn't show the change. Eventually the value reverts to its previous value.

I've tried calling gsettings set from a remote shell, setting the $DISPLAY variable as requested. This method works.

I have the same problem with your ansible-dconf repo.

I'm using Trisquel 8, which is based on Ubuntu 16.04.

Thanks. :-)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.