Skip to content

Instantly share code, notes, and snippets.

@brutus brutus/toast.py
Last active Jul 3, 2017

Embed
What would you like to do?
A very basic Ansible module for toast.
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
Ansible module to interact with `toast`_.
To use this from Ansible, copy it in your `library`_::
cd path/to/library
git clone https://gist.github.com/brutus/ba3b110c3041777e2e976b61e924b18c toast
And use it in your playbook or roles like this::
# install tar
- toast:
name: tar
# update tar
- toast:
name: tar
state: latest
# uninstall tar
- toast:
name: tar
state: absent
It returns some JSON like this (eg: install `tar` while it is already
installed)::
{
"action": "nothing",
"changed": false,
"invocation": {
"module_args": {
"name": "tar",
"state": "present"
},
"module_name": "toast"
},
"status_new": {
"name": "tar",
"state": "present",
"version": "1.29"
},
"status_old": {
"name": "tar",
"state": "present",
"version": "1.29"
}
}
Install new package sources
---------------------------
To add a new URL as package source, use a `add` as *state* and the URL as *name*, e.g. for the `tree`_ utility::
# add package for `tree`
- toast:
name: ftp://mama.indstate.edu/linux/tree/
state: add
.. _toast: http://www.toastball.net/toast/
.. _library: http://docs.ansible.com/ansible/intro_configuration.html#library
.. _tree: http://mama.indstate.edu/users/ice/tree/
"""
from __future__ import absolute_import
from __future__ import unicode_literals
import re
from ansible.module_utils.basic import AnsibleModule
def main():
REGEX_VERSION = re.compile(r'version (?P<version>[0-9\.]+): .+')
def get_status(name):
"""
Returns a dict with toast status information for *name*.
The *name*, *state* and *version* will be returned.
If the return code of the toast command (`rc`) is 0, the *name* is
found in toasts DB and the output should be similar to this::
toast
version 1.486: stored
urls:
http://www.toastball.net/toast/toast-1.486.tar.gz
build 1: armed
"""
state = 'absent'
version = None
err_msg = 'failed to parse output: {0}'
# run command
cmd = ['toast', 'status', name]
(rc, stdout, stderr) = module.run_command(cmd, check_rc=False)
result = [line.strip() for line in stdout.split('\n') if line]
if rc == 0: # means the *name* is in the DB
# verify name
if result[0] != name:
module.fail_json(msg=err_msg.format(result))
# check state
build_string = result[-1]
if (
build_string.startswith('build') and
build_string.endswith('armed')
):
state = 'present'
# check version
match = REGEX_VERSION.match(result[1])
if match:
version = match.group('version')
else:
module.fail_json(msg=err_msg.format(result))
return {
'name': name,
'state': state,
'version': version,
}
def get_package_status(name):
"""
Returns the state for the package with *name*.
The *state* is either `present` or `absent`.
"""
cmd = ['toast', 'status', name]
(rc, stdout, stderr) = module.run_command(cmd, check_rc=False)
if rc == 0:
return 'present'
else:
return 'absent'
def install(name):
"""
Calls toast to install *name*.
"""
cmd = ['toast', 'arm', name]
module.run_command(cmd, check_rc=True)
def upgrade(name):
"""
Calls toast to upgrade *name*.
"""
cmd = ['toast', 'upgrade', name]
module.run_command(cmd, check_rc=True)
def uninstall(name):
"""
Calls toast to uninstall *name*.
"""
cmd = ['toast', 'disarm', name]
module.run_command(cmd, check_rc=True)
def add_package(url):
"""
Calls toast to add a new package for *url*.
"""
cmd = ['toast', 'add', url]
module.run_command(cmd, check_rc=True)
# setup module
args = {
'name': dict(required=True),
'state': dict(
choices=['present', 'absent', 'latest', 'add'],
default='present',
),
}
module = AnsibleModule(
argument_spec=args,
supports_check_mode=True,
)
# interact with toast
name = module.params['name']
req_state = module.params['state']
# add package?
if req_state == 'add':
action = 'add'
status_old = get_package_status(name)
if module.check_mode or status_old == 'present':
status_new = status_old
else:
add_package(name)
status_new = get_package_status(name)
# install / upgrade / uninstall
else:
status_old = get_status(name)
state = status_old['state']
if req_state == 'absent':
action = 'uninstall' if state == 'present' else ''
elif req_state == 'latest':
action = 'upgrade' if state == 'present' else 'install'
else: # present
action = '' if state == 'present' else 'install'
if module.check_mode:
status_new = status_old
else:
if action == 'uninstall':
uninstall(name)
elif action == 'upgrade':
upgrade(name)
else: # install
install(name)
status_new = get_status(name)
# return results as JSON
module.exit_json(
changed=status_old != status_new,
status_old=status_old,
status_new=status_new,
action=action or 'nothing',
)
if __name__ == '__main__':
main()
@brutus

This comment has been minimized.

Copy link
Owner Author

commented Dec 29, 2016

To use this from Ansible, copy it in your library and use it in your playbook or roles like this:

# install tar
- toast:
  name: tar

# update tar
- toast:
  name: tar
  state: latest

# uninstall tar
- toast:
  name: tar
  state: absent

It returns some JSON like this (eg: install tar while it is already installed):

{
    "action": "nothing", 
    "changed": false, 
    "invocation": {
        "module_args": {
            "name": "tar", 
            "state": "present"
        }, 
        "module_name": "toast"
    }, 
    "status_new": {
        "name": "tar", 
        "state": "present", 
        "version": "1.29"
    }, 
    "status_old": {
        "name": "tar", 
        "state": "present", 
        "version": "1.29"
    }
}
@andreasmischke

This comment has been minimized.

Copy link

commented Feb 16, 2017

I guess it has to be name: tar in the update example

# update tar
- toast:
  name: tar    # was "name: toast"
  state: latest
@brutus

This comment has been minimized.

Copy link
Owner Author

commented Jul 3, 2017

I guess it has to be name: tar in the update example

Correct. Fixed it. Thanks for spotting and report!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.