Ansible for Dummies
Trying to learn something new again, might as well write it down for future reference.
$ python3 -m venv venv
$ echo "venv/" >> .gitignore
$ source venv/bin/activate
$ pip install ansible
$ mkdir -p group_vars roles
$ touch group_vars/common.yml production site.yml
# I don't need this yet BUT THE BOOK SAID SO
$ ansible-galaxy init roles/common
$ ansible-galaxy init roles/updating
# Add whatever hostnames you'd like to mess around with here
# Relevant: https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html
$ echo "localhost" >> production
$ ansible all -i production -m ping
Wow, now I’ve successfully abstracted away ping
from my ping
! Yay.
What I really wanted, tho:
$ ansible all -i production -m setup -a "filter=ansible_interfaces"
box67 | SUCCESS => {
"ansible_facts": {
"ansible_interfaces": [
"lo0",
"pflog0",
"re0"
],
"discovered_interpreter_python": "/usr/local/bin/python3.7"
},
"changed": false
}
OK, cool. Now you know of my currently defined network interfaces. I started out doing these runs on my firewall, but I quickly setup a VM to not mess anything up.
So I tried to use the syspatch
module for ansible 2.9.9, and ran into an error immediately.
$ ansible-playbook -i production site.yml
PLAY [test] ************************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************
ok: [box67]
TASK [roles/updating : Check for updates] ******************************************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: UnboundLocalError: local variable 'check_flag' referenced before assignment
fatal: [box67]: FAILED! => {"changed": false, "module_stderr": "Shared connection to 192.168.122.120 closed.\r\n", "module_stdout": "Traceback (most recent call last):\r\n File \"/root/.ansible/tmp/ansible-tmp-1591220008.182737-58160-140628976587457/AnsiballZ_syspatch.py\", line 102, in <module>\r\n _ansiballz_main()\r\n File \"/root/.ansible/tmp/ansible-tmp-1591220008.182737-58160-140628976587457/AnsiballZ_syspatch.py\", line 94, in _ansiballz_main\r\n invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\r\n File \"/root/.ansible/tmp/ansible-tmp-1591220008.182737-58160-140628976587457/AnsiballZ_syspatch.py\", line 40, in invoke_module\r\n runpy.run_module(mod_name='ansible.modules.system.syspatch', init_globals=None, run_name='__main__', alter_sys=True)\r\n File \"/usr/local/lib/python3.7/runpy.py\", line 205, in run_module\r\n return _run_module_code(code, init_globals, run_name, mod_spec)\r\n File \"/usr/local/lib/python3.7/runpy.py\", line 96, in _run_module_code\r\n mod_name, mod_spec, pkg_name, script_name)\r\n File \"/usr/local/lib/python3.7/runpy.py\", line 85, in _run_code\r\n exec(code, run_globals)\r\n File \"/tmp/ansible_syspatch_payload_wdtkmxc5/ansible_syspatch_payload.zip/ansible/modules/system/syspatch.py\", line 169, in <module>\r\n File \"/tmp/ansible_syspatch_payload_wdtkmxc5/ansible_syspatch_payload.zip/ansible/modules/system/syspatch.py\", line 165, in main\r\n File \"/tmp/ansible_syspatch_payload_wdtkmxc5/ansible_syspatch_payload.zip/ansible/modules/system/syspatch.py\", line 95, in run_module\r\n File \"/tmp/ansible_syspatch_payload_wdtkmxc5/ansible_syspatch_payload.zip/ansible/modules/system/syspatch.py\", line 119, in syspatch_run\r\nUnboundLocalError: local variable 'check_flag' referenced before assignment\r\n", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}
PLAY RECAP *************************************************************************************************************
box67 : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
UnboundLocalError
, wait what? Some googling and I found an issue on GitHub that was relevant. Tried the patch found there:
diff --git a/lib/ansible/modules/system/syspatch.py b/lib/ansible/modules/system/syspatch.py
index 3c75f2f2c0..2c808851cb 100644
--- a/lib/ansible/modules/system/syspatch.py
+++ b/lib/ansible/modules/system/syspatch.py
@@ -113,6 +113,8 @@ def syspatch_run(module):
reboot_needed = False
warnings = []
+ run_flag = []
+ check_flag = []
# Setup command flags
if module.params['revert']:
check_flag = ['-l']
@@ -123,7 +125,7 @@ def syspatch_run(module):
run_flag = ['-r']
elif module.params['apply']:
check_flag = ['-c']
- run_flag = []
+
I have to admit that I just edited the damn file by hand, as I’ve never learned how to properly use patch
. I then tested it:
$ ansible-playbook -i production site.yml
PLAY [test] ************************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************
ok: [box67]
TASK [roles/updating : Check for updates] ******************************************************************************
changed: [box67]
PLAY RECAP *************************************************************************************************************
box67 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
WTH? I tried asking it not to apply any updates?
# roles/updating/tasks/main.yml
---
- name: Check for updates
syspatch:
apply: no
OK, this module probably isn’t the best module available. But my virtual machine is now (accidentally) updated.
I tried to do it myself, but since syspatch
doesn’t seem to use return codes much this was another learning experience - but I found some inspiration on an old github gist.
# roles/updating/tasks/main.yml
---
- name: Check for updates manually
shell: "syspatch -c"
register: syspatch_updates
changed_when: "syspatch_updates.stdout|length > 0"
- name: Apply updates conditionally
shell: "syspatch"
when: "syspatch_updates.stdout|length > 0 and really_apply_syspatch"
# roles/updating/vars/main.yml
---
really_apply_syspatch: false
We can pretend that figuring out the above yaml
didn’t take a hour or so. I’m still not sure why I have to check the length of the output twice? Mentally marked as a future improvement.
ansible-playbook -i production site.yml
PLAY [test] ************************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************
ok: [box67]
TASK [roles/updating : Check for updates manually] *********************************************************************
ok: [box67]
TASK [roles/updating : Apply updates conditionally] ********************************************************************
skipping: [box67]
PLAY RECAP *************************************************************************************************************
box67 : ok=2 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
OK, lets override the default value before running the playbook:
$ ansible-playbook -i production site.yml --extra-vars "really_apply_syspatch=true"
PLAY [test] ************************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************
ok: [box67]
TASK [roles/updating : Check for updates manually] *********************************************************************
changed: [box67]
TASK [roles/updating : Apply updates conditionally] ********************************************************************
changed: [box67]
PLAY RECAP *************************************************************************************************************
box67 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
OK. This is not terrible, I think I’m getting a basic understanding here, and I even ran it on my firewall to update it without having anything exploding.
Enough for today!