ansible shell command syntax error in using colon - ansible

Team,
I have a task finding mounts and counting them if they are 0 or greater than 64 in count.
I tried double quoting colon and also single quote whole command but no luck. Can anyone hint how to resolve this?
- name: "Mounts count on GPU Nodes should not be 64+"
shell: 'mount | grep -Ec '/dev/sd.*\<csi' | awk '{ print $0,"mounts found on hostname"($0>64? " that are more than 64." : ".") }''
register: mounts_count
changed_when: false
failed_when:
delegate_to: "{{ item }}"
with_items: "{{ groups['kube-gpu-node'] }}"
- name: Check if csi related mounts are present on gpu nodes
assert:
that:
- item.stdout is search('0')
fail_msg: " mounts are present on this node"
success_msg: "mounts are not present on this node"
loop: "{{ mounts_count.results }}"
loop_control:
label: "{{ item.item }}"
ignore_errors: yes
output:
ERROR! Syntax Error while loading YAML.
expected <block end>, but found '<scalar>'
The offending line appears to be:
- name: "Mounts count on GPU Nodes should not be 64+"
shell: 'mount | grep -Ec '/dev/sd.*\<csi' | awk '{ print $0,"mounts found on hostname"($0>64? " that are more than 64." : ".") }''
^ here
This one looks easy to fix. It seems that there is a value started
with a quote, and the YAML parser is expecting to see the line ended
with the same kind of quote. For instance:
when: "ok" in result.stdout
Could be written as:
when: '"ok" in result.stdout'
Or equivalently:
when: "'ok' in result.stdout"
We could be wrong, but this one looks like it might be an issue with
unbalanced quotes. If starting a value with a quote, make sure the
line ends with the same set of quotes. For instance this arbitrary
example:
foo: "bad" "wolf"
Could be written as:
foo: '"bad" "wolf"'

Resolved it by espacing < and double quotes inside expression and double quoting whole command
shell: "mount | grep -Ec '/dev/sd.*\\<csi' | awk '{ print $0,\"mounts found on hostname\"($0>64? \" that are more than 64.\" : \".\") }'"

Related

The condition in my playbook is not working

I have this simple playbook:
---
- name: JVM Status
shell: "ps -ef | grep {{item}} | grep -v grep | grep {{wasUser}} | wc -l"
loop: "{{jvmStatusList}}"
become: yes
become_user: "{{wasUser}}"
register: statusResult
- debug:
msg: "{{items.item}} is started"
loop: "{{statusResult.results}}"
when: item.stdout != 0
- debug:
msg: "{{item.item}} is stopped"
loop: "{{ statusResult.results }}"
when: item.stdout == 0
...
and when i run it, it doesnt take into account the condition.
I have verified what my item.stdout returns and 2 items are started and 1 is stopped but all three are returned in the first debug while the second one is being skipped by all.
Can anyone help?
thought maybe the problem was because my stdout result was not trimmed. so i tried different syntaxes including item.stdout | trim but no change. Also tried using item.stdout is 1 instead of != 0.

Newline('\n') is replaced by space(' ') in ansible shell module

I have an ansible task where I make use of shell module to run a custom bash script. This bash script expects an optional parameter which could be a multiline string. Example
~/myScript.sh -e "Each \nword \nin \nnew \nline"
This works perfectly fine when I run it in the bash shell. However, I have tried several ways but haven't made it run via the ansible task. The value being a variable itself. The task below -
- name: Sample Task
shell:
cmd: "{{ home_dir }}/myScript.sh -e '{{ string_with_escaped_newlines }}'"
Here, the value of variable string_with_escaped_newlines is set to Each \nword \nin \nnew \nline programmatically by other tasks. The output of the task confirms this.
I had put an echo in my shell script to debug & I observe that the echo would print 2 different sequences in the 2 cases(running directly via shell & running via ansible).
Debug output via shell-
Each
word
in
new
line
Debug output via ansible -
Each word in new line
Notice that there is an extra space introduced in the value in place on \n. I do not understand why this happens and how do I stop this. I have checked/tried shell as well as command modules of ansible.
cmd: "{{ home_dir }}/myScript.sh -e '{{ string_with_escaped_newlines }}'"
is first converted (by Jinja2) to
cmd: "<THE HOME>/myScript.sh -e 'Each \nword \nin \nnew \nline'"
Here \n is within double quotes and in YAML double quoted \ns are NEWLINEs (think "\n" in C or JSON).
For your case you can just write:
cmd: {{ home_dir }}/myScript.sh -e '{{ string_with_escaped_newlines }}'
Or to be more safe:
cmd: |
{{ home_dir }}/myScript.sh -e '{{ string_with_escaped_newlines }}'
YAML syntax is quite "confusing". You can take a look at Ansible's own YAML intro.
Given the script
shell> cat ~/myScript.sh
#!/usr/bin/sh
echo ${1}
shell> ~/myScript.sh "Each \nword \nin \nnew \nline"
Each
word
in
new
line
Q: "Newline '\n' is replaced by space ' ' in Ansible shell module."
A: It depends on how you quote and escape the string. The simplest option is single-quoted style because only ' needs to be escaped
string_with_escaped_newlines1: 'Each \nword \nin \nnew \nline'
You have to escape \n in double-quoted style
string_with_escaped_newlines2: "Each \\nword \\nin \\nnew \\nline"
Both variables expand to the same string
- debug:
var: string_with_escaped_newlines1
- debug:
var: string_with_escaped_newlines2
gives
string_with_escaped_newlines1: Each \nword \nin \nnew \nline
string_with_escaped_newlines2: Each \nword \nin \nnew \nline
Then, the quotation in the command is not significant. All options below give the same result
- command: ~/myScript.sh "{{ string_with_escaped_newlines1 }}"
- command: ~/myScript.sh '{{ string_with_escaped_newlines1 }}'
- command: ~/myScript.sh "{{ string_with_escaped_newlines2 }}"
- command: ~/myScript.sh '{{ string_with_escaped_newlines2 }}'
Example of a complete playbook for testing
- hosts: localhost
vars:
string_with_escaped_newlines1: 'Each \nword \nin \nnew \nline'
string_with_escaped_newlines2: "Each \\nword \\nin \\nnew \\nline"
tasks:
- command: ~/myScript.sh "{{ string_with_escaped_newlines1 }}"
register: out
- debug:
var: out.stdout_lines
- command: ~/myScript.sh '{{ string_with_escaped_newlines1 }}'
register: out
- debug:
var: out.stdout_lines
- command: ~/myScript.sh "{{ string_with_escaped_newlines2 }}"
register: out
- debug:
var: out.stdout_lines
- command: ~/myScript.sh '{{ string_with_escaped_newlines2 }}'
register: out
- debug:
var: out.stdout_lines
gives (abridged) four times the same result
TASK [debug] *********************************************************
ok: [localhost] =>
out.stdout_lines:
- 'Each '
- 'word '
- 'in '
- 'new '
- line
Notes:
Using shell instead of command gives the same results.
The best practice is using the module command unless the ansible.builtin.shell module is explicitly required.
Have you tried \r\n instead of \n
It helped me elsewhere in ansible

ansible playbook access first loop variable in second loop

I have list of applications paths in a file APP_DIR, need to loop through the paths and run start command.
- name: start
command: "{{item[1]}}"
with_nested:
- "{{ lookup('file', 'APP_DIR').splitlines() }}"
- [ "chdir={{item[0]}} ./start",
"ps -aef | grep httpd | grep -v grep"]
ERROR: FAILED! => {"msg": "'item' is undefined"}.
Thanks in Advance for help.
You can't call item variable in a nested list, you can use your command with a simple with_item and a multiline like this :
- name: start
command: |
./start
ps -aef | grep httpd | grep -v grep
args:
chdir: "{{ item[0] }}"
with_items: "{{ lookup('file', 'APP_DIR').splitlines() }}"

ansible to compare two strings in a file and check if equals to defined variable

Team, I have a kubeconfig file and i need to compare if context name and current-context are same, if yes, I want to proceed further else fail. below is my shell which i want to integrate and run with ansible.
cat cluster-user.kubeconfig | yq .contexts[0].name```
"site.test.com"
cat cluster-user.kubeconfig | yq .[\"current-context\"]```
"site.test.com"
- name: "GET API server current-context name with YQ.. "
shell: "cat cluster-user.kubeconfig | yq .[\"current-context\"]"
register: string1
ignore_errors: true
- debug:
var: string1.stdout_lines
when: string1.stdout != ''
- name: "GET API server contexts name with YQ.. "
shell: "cat cluster-user.kubeconfig | yq .contexts[0].name"
register: string2
ignore_errors: true
- debug:
var: string2.stdout_lines
when: string2.stdout != ''
- name: "Validate if both string1 and string2 are same, if yes proceed.. "
failed_when: string2.stdout != string1.stdout
output:
on the exact syntax problem.
The offending line appears to be:
- name: "Validate if both string1 and string2 are same, if yes proceed.. "
^ here```
anyhint what am i missing?
Can you try this :
- fail:
msg: "Validate if both string1 and string2 are same, if yes proceed..."
when: "'{{ string2.stdout }}' != '{{ string1.stdout }}'"
failed_when: This is used to fail the particular task when the condition is met. Failed_When Ansible Documentation
fail: This is used to fail the progress with a custom message. Fail Ansible Documentation

Write to file specified by user from command line argument - Ansible

I am trying to write a line to a file using lineinfile.
The name of the file is to be passed to the playbook at run time by the user as a command line argument.
Here is what the task looks like:
# Check for timezone.
- name: check timezone
tags: timezoneCheck
register: timezoneCheckOut
shell: timedatectl | grep -i "Time Zone" | awk --field-separator=":" '{print $2}' | awk --field-separator=" " '{print $1}'
- lineinfile:
path: {{ output }}
line: "Did not find { DesiredTimeZone }"
create: True
state: present
insertafter: EOF
when: timezoneCheckOut.stdout != DesiredTimezone
- debug: var=timezoneCheckOut.stdout
My questions are:
1. How do I specify the command line argument to be the destination file to write to (path)?
2. How do I append the argument DesiredTimeZone (specified in an external variables file) to the line argument?
My following answer might not be your solutions.
how to specify the command argument for output variable.
ansible-playbook yourplaybook.yml -e output=/path/to/outputfile
how to include DesiredTimeZone variable from external file.
vars_files:
- external.yml
full playbook.yml for testing on local:
yourplaybook.yml
- name: For testing
hosts: localhost
vars_files:
- external.yml
tasks:
- name: check timezone
tags: timezoneCheck
register: timezoneCheckOut
shell: timedatectl | grep -i "Time Zone" | awk -F":" '{print $2}' | awk --field-separator=" " '{print $1}'
- debug: var=timezoneCheckOut.stdout
- lineinfile:
path: "{{ output }}"
line: "Did not find {{ DesiredTimeZone }}"
create: True
state: present
insertafter: EOF
when: timezoneCheckOut.stdout != DesiredTimeZone
external.yml (place the same level with yourplaybook.yml)
---
DesiredTimeZone: "Asia/Tokyo"
With Ansible you should define the desired state. Period.
The correct way of doing this is to just use timezone module:
- name: set timezone
timezone:
name: "{{ DesiredTimeZone }}"
No need to jump through the hoops with shell, register, compare, print...
If you want to put system into the desired state, just run playbook:
ansible-playbook -e DesiredTimeZone=Asia/Tokyo timezone_playbook.yml
Ansible will ensure that all hosts in question will have the DesiredTimeZone.
If you just want to check if you system comply to desired state, use --check switch:
ansible-playbook -e DesiredTimeZone=Asia/Tokyo --check timezone_playbook.yml
In this case Ansible will just print to the log what should be changed in the current state to become desired state and don't make any actual changes.

Resources