Help Using an .stdout as a WHEN Condition

Hello. New to this wonderful Ansible thing. I am a database guy. I have an Oracle process that spits out a file that looks like this (but the value could be different at each run):

[oracle@lnx0016 scripts]$ more /tmp/impact.txt
  -12268.06

Then, within the same playbook, Ansible playbook parses it, and if the value is a negative (less than 0), it is supposed to kick off a whole another block.

    - name: opatch_direct | Export the Post-change Impact Score for for {{ db_name }}
      shell: 'cat /tmp/impact.txt'
      register: impact_score_literal

    - name: opatch_direct | GO / NOGO decision for {{ oracle_home }} and {{ db_name }}
      block:
...
      when: "'-' not in impact_score_literal.stdout"

I also tried a less than zero condition, but ignores it.
I think there are two problems:

  1. The number " -12268.06" has a heading empty space
  2. The data type may be wrong? May be I should use something other than β€œcat”?

Here is the error

TASK [opatch_direct | Stop database ORCLCDB before rolling back 35740258 for /opt/oracle/product/21c/dbhome_1] ************************************************************************************************************************
task path: /home/oracle/ansible/playbooks/opatch_direct.yml:185
fatal: [lnx006]: FAILED! => 
  msg: |-
    The conditional check ''-' not in impact_score_literal.stdout' failed. The error was: error while evaluating conditional ('-' not in impact_score_literal.stdout): 'dict object' has no attribute 'stdout'
  
    The error appears to be in '/home/oracle/ansible/playbooks/opatch_direct.yml': line 185, column 11, but may
    be elsewhere in the file depending on the exact syntax problem.
  
    The offending line appears to be:
  
  
            - name: opatch_direct | Stop database {{db_name}} before rolling back {{ patch_id }} for {{ oracle_home }}
              ^ here
    We could be wrong, but this one looks like it might be an issue with
    missing quotes. Always quote template expression brackets when they
    start a value. For instance:
  
        with_items:
          - {{ foo }}
  
    Should be written as:
  
        with_items:
          - "{{ foo }}"

PLAY RECAP ****************************************************************************************************************************************************************************************************************************
lnx006                     : ok=17   changed=12   unreachable=0    failed=1    skipped=1    rescued=0    ignored=0   

Here is the value of the .stdout for the condition check:

changed: [lnx006] => changed=true 
  cmd: cat /tmp/impact.txt
  delta: '0:00:00.004104'
  end: '2023-12-26 15:38:15.595984'
  invocation:
    module_args:
      _raw_params: cat /tmp/impact.txt
      _uses_shell: true
      argv: null
      chdir: null
      creates: null
      executable: null
      removes: null
      stdin: null
      stdin_add_newline: true
      strip_empty_ends: true
      warn: true
  rc: 0
  start: '2023-12-26 15:38:15.591880'
  stderr: ''
  stderr_lines: <omitted>
  stdout: '  -12268.06'
  stdout_lines: <omitted>

What I find interesting is that the condition works ONLY with an == sign, not <>. That value , if it matches the /tmp/impact.txt contents, works, meaning Ansible keeps rolling, follows the condition as it should. But that is not what I need. I need it to kick off the block only if the number is negative.

Please help
Thanks
Symian

Replying to self.
This value in /tmp/impact.txt added by keyboard:

-6

  when: impact_score_literal.stdout > 0

It reads the -6, understands it will cause a performance degradation and then kicks off the correct block to rollback the Oracle database patch.

Rather than cat you could use the ansible.builtin.slurp module, to read the contents of the file, for example:

- name: Slurp a base64 encoded version of the file
  ansible.builtin.slurp:
    src: /tmp/impact.txt
  register: file_b64encoded

- name: Decode the base64 encoded version of the file and set a variable
  ansible.builtin.set_fact:
    file_contents: "{{ file_b64encoded['content'] | ansible.builtin.b64decode | trim }}"

The b64decode filter is an Ansible filter, the trim filter comes from jinja2.

You can test the type using the ansible.builtin.type_debug filter:

- name: Print the type of the file_contents variable
  ansible.builtin.debug:
    msg: "{{ file_contents | ansible.builtin.type_debug }}"
3 Likes

In addition to @chris’s comment, to be more programatic way, I think a float filter would be helpful: Template Designer Documentation β€” Jinja Documentation (3.0.x)
This allows for a simple comparison of scores to 0, rather than looking for a - in a string.

Example tasks:

- name: opatch_direct | Export the Post-change Impact Score for for {{ db_name }}
  ansible.builtin.slurp:
    src: /tmp/impact.txt
  register: impact_score_b64encoded

- name: Debug values and filteres
  ansible.builtin.debug:
    var: debug_scores
  vars:
    debug_scores:
      01_source_____: "{{ impact_score_b64encoded.content }}"
      02_b64decoded_: "{{ impact_score_b64encoded.content | ansible.builtin.b64decode }}"
      03_casted_____: "{{ impact_score_b64encoded.content | ansible.builtin.b64decode | float }}"
      04_is_negative: "{{ impact_score_b64encoded.content | ansible.builtin.b64decode | float < 0 }}"
      05_is_positive: "{{ impact_score_b64encoded.content | ansible.builtin.b64decode | float > 0 }}"

- name: opatch_direct | GO / NOGO decision for {{ oracle_home }} and {{ db_name }}
  block:
    - name: Message if the score is nevative
      ansible.builtin.debug:
        msg: The score is negative
  when: impact_score_b64encoded.content | ansible.builtin.b64decode | float < 0

Result:

TASK [opatch_direct | Export the Post-change Impact Score for for hoge] ************************************
ok: [localhost]

TASK [Debug values and filteres] ***************************************************************************
ok: [localhost] => {
    "debug_scores": {
        "01_source_____": "ICAtMTIyNjguMDY=",
        "02_b64decoded_": "  -12268.06",
        "03_casted_____": "-12268.06",
        "04_is_negative": true,
        "05_is_positive": false
    }
}

TASK [Message if the score is nevative] ********************************************************************
ok: [localhost] => {
    "msg": "The score is negative"
}

PLAY RECAP *************************************************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
4 Likes

cannot thank you enough, both!

1 Like

I put the block in the playbook, thanks. But it fires on both negative and positive numbers. For example, the below is for the value β€œ7”. It does the same for β€œ-7”. But it correctly says β€œ-7” or β€œ7” as below, but rollsback the patch even when the number is positive, which it should not.

Blockquote

TASK [Debug values and filteres] ***********************************************************************************************************************************************************************************
task path: /home/oracle/ansible/playbooks/opatch_direct.yml:214
ok: [lnx006] =>
debug_scores:
01_source_____: IDcK
02_b64decoded_: |2-
7
03_casted_____: β€˜7.0’
04_is_negative: false
05_is_positive: true

TASK [Message if the score is nevative] ****************************************************************************************************************************************************************************
task path: /home/oracle/ansible/playbooks/opatch_direct.yml:227
ok: [lnx006] =>
msg: The post-change improvement score is negative, the change is being rolled back now

Hello @Symian, what both @chris & @kurokobo told you should be enough to achieve your goal. Anyway, I’ll tell you what I use to do on these cases, since I normally prefer the use of lookup plugins instead of modules when I have the chance (just because I’m more used to those, not for nothing in particular). For example, you may combine the use of ansible.builtin.lookup and ansible.builtin.assert, like this:

---
- name: Help Using an .stdout as a WHEN Condition
  hosts: localhost
  gather_facts: false
  tasks:

    - name: Asserting float sign from file lookup
      ansible.builtin.assert:
        that: impact_score | float < 0
        success_msg: NEGATIVE NUMBER
        fail_msg: NOT NEGATIVE NUMBER
      vars:
        impact_score: "{{ lookup('file', 'tmp/impact.txt', lstrim=true) }}"
      register: results

    - name: My Block
      when: not results.failed
      block:

        - name: DEBUG
          ansible.builtin.debug:
            msg: these tasks will run only when the impact_score is negative
...

Hope it helps!

2 Likes

Hmm on my side it works as expected:

TASK [Debug values and filteres] ***************************************************************************
ok: [localhost] => 
  debug_scores:
    01_source_____: IDcK
    02_b64decoded_: |2-
       7
    03_casted_____: '7.0'
    04_is_negative: false
    05_is_positive: true

TASK [Message if the score is nevative] ********************************************************************
skipping: [localhost]

PLAY RECAP *************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

Have you updated when for your block correctly?

when: impact_score_b64encoded.content | ansible.builtin.b64decode | float < 0

For the comment from @jbericat, indeed ansible.builtin.lookup and ansible.builtin.assert are useful modules so familiarity with them will be very helpful in your future playbook development.

Note, however, that lookup('file') can only read local files on control node and cannot be used to read files placed on target (remote) nodes, as in this case.

4 Likes

My mistake, you’re totally right @kurokobo, I did oversee this β€˜small’ detail indeed!

1 Like

That also explains why the /tmp/impact.txt kept reporting a negative even when a positive was present. I missed the same point that it kept reading the file on the admin node which was always negative, even when I change it to a positive on the remote node. Thank you anyway.

1 Like

It all works beautifully, thanks to you all @jbericat @kurokobo

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.