Created
November 28, 2018 19:27
-
-
Save dustymabe/926c0d9bb998b1404b9bc55021977fb7 to your computer and use it in GitHub Desktop.
ansible module for rpm-ostree package layering
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# https://github.com/ansible/ansible/issues/21185 | |
# Copyright: (c) 2018, Dusty Mabe <[email protected]> | |
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) | |
# Make coding more python3-ish | |
from __future__ import (absolute_import, division, print_function) | |
__metaclass__ = type | |
# Most of this module was inspired by the command module | |
# ./lib/ansible/modules/commands/command.py | |
import shlex | |
from ansible.module_utils.basic import AnsibleModule | |
from ansible.module_utils.six import b | |
ANSIBLE_METADATA = { | |
'metadata_version': '1.1', | |
'status': ['preview'], | |
'supported_by': 'community' | |
} | |
DOCUMENTATION = ''' | |
--- | |
module: rpm_ostree | |
short_description: A module for a few rpm-ostree operations | |
version_added: "2.X" | |
description: | |
- "A module for a few rpm-ostree operations" | |
options: | |
name: | |
description: | |
- This is the message to send to the sample module | |
required: true | |
state: | |
install: | |
description: | |
- Packages to install on the system | |
new: | |
description: | |
- Control to demo if the result of this module is changed or not | |
required: false | |
extends_documentation_fragment: | |
- azure | |
author: | |
- Your Name (@yourhandle) | |
''' | |
EXAMPLES = ''' | |
# Pass in a message | |
- name: Test with a message | |
my_new_test_module: | |
name: hello world | |
# pass in a message and have changed true | |
- name: Test with a message and changed output | |
rpm-ostree: | |
name: hello world | |
new: true | |
# fail the module | |
- name: Test failure of the module | |
my_new_test_module: | |
name: fail me | |
''' | |
RETURN = ''' | |
original_message: | |
description: The original name param that was passed in | |
type: str | |
message: | |
description: The output message that the sample module generates | |
''' | |
from ansible.module_utils.basic import AnsibleModule | |
def run_module(): | |
# define available arguments/parameters a user can pass to the module | |
module_args = dict( | |
reboot=dict(type='bool', required=False, default=False), | |
name=dict(aliases=['pkg'], type='list', required=True), | |
state=dict(default='present', | |
choices=['absent', 'present', | |
'installed', 'removed', 'latest']), | |
) | |
# seed the result dict in the object | |
# we primarily care about changed and state | |
# change is if this module effectively modified the target | |
# state will include any data that you want your module to pass back | |
# for consumption, for example, in a subsequent task | |
result = dict( | |
changed=False, | |
original_message='', | |
message='' | |
) | |
# the AnsibleModule object will be our abstraction working with Ansible | |
# this includes instantiation, a couple of common attr would be the | |
# args/params passed to the execution, as well as if the module | |
# supports check mode | |
module = AnsibleModule( | |
argument_spec=module_args, | |
supports_check_mode=True | |
) | |
# if the user is working with this module in only check mode we do not | |
# want to make any changes to the environment, just return the current | |
# state with no modifications | |
if module.check_mode: | |
return result | |
# manipulate or modify the state as needed (this is going to be the | |
# part where your module will do what it needs to do) | |
# result['original_message'] = module.params['name'] | |
# result['message'] = 'goodbye' | |
if module.params['state'] in ['installed', 'present']: | |
action = 'install' | |
elif module.params['state'] in ['absent', 'removed']: | |
action = 'uninstall' | |
cmd = "rpm-ostree {} --allow-inactive --idempotent --unchanged-exit-77 {}" | |
cmd = cmd.format(action, ' '.join(module.params['name'])) | |
cmd = shlex.split(cmd) | |
rc, out, err = module.run_command(cmd, encoding=None) | |
if out is None: | |
out = b('') | |
if err is None: | |
err = b('') | |
result.update(dict( | |
rc = rc, | |
cmd = cmd, | |
stdout = out.rstrip(b("\r\n")), | |
stderr = err.rstrip(b("\r\n")), | |
)) | |
# A few possible options: | |
# - rc=0 - succeeded in making a change | |
# - rc=77 - no change was needed | |
# - rc=? - error | |
if rc == 0: | |
result['changed'] = True | |
elif rc == 77: | |
result['changed'] = False | |
result['rc'] = 0 | |
else: | |
module.fail_json(msg='non-zero return code', **result) | |
# in the event of a successful module execution, you will want to | |
# simple AnsibleModule.exit_json(), passing the key/value results | |
module.exit_json(**result) | |
def main(): | |
run_module() | |
if __name__ == '__main__': | |
main() |
@dustymabe are you planning on submitting this to Ansible?
@fabianvf - yeah, at some point
Would also be great to integrate it with the generic
package
module
yeah the code works like that today if you insert it into the list of modules to "switch" on.
Thank you for this module! However, I tried this with Ansible v2.8 and get the following error. Would you like to fix this, please?
FAILED! => {
"changed": false,
"module_stderr": "Shared connection to <HOST-FQDN> closed.\r\n",
"module_stdout": "
/home/frank/.ansible/tmp/ansible-tmp-1558824059.0516987-80466753735172/AnsiballZ_rpm_ostree_pkg.py:18:
DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
import imp
Traceback (most recent call last):
File \"/home/frank/.ansible/tmp/ansible-tmp-1558824059.0516987-80466753735172/AnsiballZ_rpm_ostree_pkg.py\", line 114, in <module>
_ansiballz_main()
File \"/home/frank/.ansible/tmp/ansible-tmp-1558824059.0516987-80466753735172/AnsiballZ_rpm_ostree_pkg.py\", line 106, in _ansiballz_main
invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
File \"/home/frank/.ansible/tmp/ansible-tmp-1558824059.0516987-80466753735172/AnsiballZ_rpm_ostree_pkg.py\", line 49, in invoke_module
imp.load_module('__main__', mod, module, MOD_DESC)
File \"/usr/lib64/python3.7/imp.py\", line 234, in load_module
return load_source(name, filename, file)
File \"/usr/lib64/python3.7/imp.py\", line 169, in load_source
module = _exec(spec, sys.modules[name])
File \"<frozen importlib._bootstrap>\", line 630, in _exec
File \"<frozen importlib._bootstrap_external>\", line 728, in exec_module
File \"<frozen importlib._bootstrap>\", line 219, in _call_with_frames_removed
File \"/tmp/ansible_rpm_ostree_pkg_payload_8pmby7pg/__main__.py\", line 169, in <module>
File \"/tmp/ansible_rpm_ostree_pkg_payload_8pmby7pg/__main__.py\", line 166, in main
File \"/tmp/ansible_rpm_ostree_pkg_payload_8pmby7pg/__main__.py\", line 135, in run_module
UnboundLocalError: local variable 'action' referenced before assignment\r\n",
"msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}
And in check mode, Ansible displayed this error:
fatal: [<HOST>]: FAILED! => {"changed": false, "msg": "New-style module did not handle its own exit"}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Would also be great to integrate it with the generic
package
module