We were recently confronted with one of our playbook constraint while trying to identify which database software could be uninstalled from our servers.
Until now, we ran the playbook to de-install a specific version which were “discovered manually”. However, we even got lazy to run this “discovery phase” manually.
This short post explains how to design a play around the results of a specific task like generating a list of obsolete Oracle RDBMS software.
Indeed, we tried to loop over registered results from a simple SHELL command like fuser; The goal was to build a list with the result of fuser and iterate over the whole list.
Here the task snippet of what we tried achieve:
- name: Find all installed Oracle RDBMS binaries find: paths: /u01/app/oracle/product/ recurse: no file_type: directory register: reg_oracle_home_installed - name: Check if ORACLE_HOME is used shell: "/sbin/fuser {{ ora_home.path }}/bin/oracle" register: reg_oracle_home_status loop: "{{ reg_oracle_home_installed.files }}" loop_control: loop_var: ora_home label: "{{ ora_home.path }}" changed_when: false failed_when: reg_oracle_home_status.rc is not regex('^(1|0)$') - name: Dynamic list of candidate ORACLE_HOME to deinstall debug: msg: " {{ entry.ora_home.path }}" loop: "{{ reg_oracle_home_status.results }}" loop_control: loop_var: entry label: "{{ entry.ora_home.path }}"
According to the Ansible documentation, the return values for registered data structure in loops should return a list per item.However, processing the “results” list is not working as expected which explains why you are still reading my post 😉.
Below, the returned data structure debug output:
ok: [vmoel7] => (item={'stderr_lines': [], u'cmd': u'fuser /u01/app/oracle/product/18_7_0_0_RU190716_v0/bin/oracle', u'stdout': u'', u'msg': u'non-zero return code', u'delta': u'0:00:00.027083', 'stdout_lines': [], 'failed_when_result': False, 'ansible_loop_var': u'item', u'end': u'2020-10-15 22:13:42.461691', 'item': {u'islnk': False, u'uid': 54321, u'rgrp': True, u'xoth': True, u'rusr': True, u'woth': False, u'nlink': 72, u'issock': False, u'mtime': 1563907992.349, u'gr_name': u'oinstall', u'path': u'/u01/app/oracle/product/18_7_0_0_RU190716_v0', u'xusr': True, u'atime': 1593096574.662, u'inode': 132, u'isgid': False, u'size': 4096, u'isdir': True, u'wgrp': False, u'ctime': 1563907992.349, u'isblk': False, u'xgrp': True, u'isuid': False, u'dev': 2065, u'roth': True, u'isreg': False, u'isfifo': False, u'mode': u'0755', u'pw_name': u'oracle', u'gid': 54321, u'ischr': False, u'wusr': True}, u'changed': True, u'failed': False, u'stderr': u'', u'rc': 1, u'invocation': {u'module_args': {u'warn': True, u'executable': None, u'_uses_shell': True, u'strip_empty_ends': True, u'_raw_params': u'fuser /u01/app/oracle/product/18_7_0_0_RU190716_v0/bin/oracle', u'removes': None, u'argv': None, u'creates': None, u'chdir': None, u'stdin_add_newline': True, u'stdin': None}}, u'start': u'2020-10-15 22:13:42.434608'}) => { "msg": "unsed OracleHome={'stderr_lines': [], 'ansible_loop_var': u'item', u'end': u'2020-10-15 22:13:42.461691', u'stdout': u'', 'item': {u'uid': 54321, u'woth': False, u'mtime': 1563907992.349, u'inode': 132, u'isgid': False, u'size': 4096, u'roth': True, u'isuid': False, u'isreg': False, u'pw_name': u'oracle', u'gid': 54321, u'ischr': False, u'wusr': True, u'xoth': True, u'rusr': True, u'nlink': 72, u'issock': False, u'rgrp': True, u'gr_name': u'oinstall', u'path': u'/u01/app/oracle/product/18_7_0_0_RU190716_v0', u'xusr': True, u'atime': 1593096574.662, u'isdir': True, u'ctime': 1563907992.349, u'wgrp': False, u'xgrp': True, u'dev': 2065, u'isblk': False, u'isfifo': False, u'mode': u'0755', u'islnk': False}, u'changed': True, u'rc': 1, u'failed': False, u'cmd': u'fuser /u01/app/oracle/product/18_7_0_0_RU190716_v0/bin/oracle', u'stderr': u'', u'delta': u'0:00:00.027083', u'invocation': {u'module_args': {u'creates': None, u'executable': None, u'_uses_shell': True, u'strip_empty_ends': True, u'_raw_params': u'fuser /u01/app/oracle/product/18_7_0_0_RU190716_v0/bin/oracle', u'removes': None, u'argv': None, u'warn': True, u'chdir': None, u'stdin_add_newline': True, u'stdin': None}}, 'stdout_lines': [], 'failed_when_result': False, u'start': u'2020-10-15 22:13:42.434608', u'msg': u'non-zero return code'}" }
Indeed, it looks like a YAML list but it isn’t … Lets quickly verify the variable type using “type_debug”
Code Snippet:
- debug: msg: "reg_oracle_home_status.results: {{ reg_oracle_home_status.results | type_debug }}"
Output:
ok: [vmoel7] => { "msg": "reg_oracle_home_status.results: list" }
Hum, it’s a list! Hence, It’s must be a bug.
So, let’s try to reformat it using the from_yaml Python method. Follows, the output after python from_yaml conversion:
ok: [vmoel7] => { "msg": [ { "cmd": "fuser /u01/app/oracle/product/18_7_0_0_RU190716_v0/bin/oracle", … "msg": "non-zero return code", "rc": 1, "start": "2020-10-15 22:22:40.857383", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": [] }, { "cmd": "fuser /u01/app/oracle/product/19_8_0_0_RU200714_v0/bin/oracle", "rc": 0, … } …
Better isn’t it?
Now, we are able to process the list in a loop as expected:
- name: Dynamic list of candidate ORACLE_HOME to deinstall debug: msg: "deinstall ORACLE_HOME={{ entry.ora_home.path}}" loop: "{{ reg_oracle_home_status.results | from_yaml | list }}" loop_control: loop_var: entry label: "{{ entry.ora_home.path }}" # ReturnCode=0 - if used when: entry.rc != 0
Sounds obvious? But took a couple of hours to debug.
it’s probably the case for plenty of Ansible nerds all over the world if you look at the number of forum threads.
The proof? You are still reading this post 😊