ansible debug msg failed_when and changed_when - automation

y code -
# Update apt cache reposoitary
- name: Update apt cache
command: apt-get update
register: return
# Debug msg
- debug:
msg: '{{ return }}'
result.stdout_lines
"stdout_lines": [
"Hit:1 http://server/pub/mirrors/ubuntu bionic InRelease",
"Get:2 http://server/pub/mirrors/ubuntu bionic-updates InRelease [88.7 kB]",
"Get:3 http://server/pub/mirrors/ubuntu bionic-backports InRelease [74.6 kB]",
"Hit:4 https://download.package.com/infrastructure_agent/linux/apt bionic InRelease",
"Hit:5 https://archive.repo.package.com/apt/ubuntu/18.04/amd64/2018.3 bionic InRelease",
"Get:6 http://server/pub/mirrors/ubuntu bionic-security InRelease [88.7 kB]",
"Hit:7 http://apt.package123.org/pub/repos/apt bionic-pgdg InRelease",
"Fetched 252 kB in 1s (222 kB/s)",
"Reading package lists..."
I would like consider if any one line has 2 string "Err" & "package" that means it has failed to update apt cache from website - https://download.package.com/
I was thinking of something like below:
changed_when: >
(("Get" in return.stdout_lines) and
("package" in ret.stdout_lines)) or
(("Hit" in return.stdout_lines) and
("package" in ret.stdout_lines))
failed_when: >
("Err" in return.stdout_lines) and
("package" in ret.stdout_lines)
Question here is does 2 strings looks for all of the lines or line by line ?
if so how to get this worked on line by line check.

When APT fails to download the package, it will return an error. In that case, stdout isn't used anymore, but stderr. So you most likely need to change it to:
# Update apt cache reposoitary
- name: Update apt cache
command: apt-get update
ignore_errors: true
register: return
# Debug msg
- debug:
msg: '{{ return }}'
changed_when: >
(("Get" in return.stderr_lines) and
("newrelic" in ret.stderr_lines)) or
(("Hit" in return.stderr_lines) and
("newrelic" in ret.stdout_lines))
failed_when: >
("Err" in return.stderr_lines) and
("newrelic" in ret.stderr_lines)
Question here is does 2 strings looks for all of the lines or line by line ?
A: It looks for all the 'text' in the stderr_lines output of that task.
If so how to get this worked on line by line check.
A: Your code looks sane.
Perhaps you want to test this specific use case yourself. What you could do is append the following to /etc/hosts on your target system: 127.0.0.1 download.newrelic.com
This way, APT looks on the target machine for the repository. Since it isn't there, the download will fail.
Also note that Ansible has update cache built-in the APT module.

Here you go working code -
-----------------
# Update apt cache reposoitary
- name: Update apt cache
command: apt-get update
register: return
failed_when: return.stdout_lines is search("Hit:* https://download.package.com")
changed_when: return.stdout_lines is search("Err:* https://download.package.com")
# Debug msg
- debug:
msg: '{{ return }}'
-----------------

Related

Ansible - use registered variable that contains semicolon to install exact apt package

I want to create a role to install latest "xx.yy" or "xx" version of a package on multiple Debian distributions.
Starting with a more generic version I use apt-cache madison, filter the results to get latest version available for that machine an register the value. I am then trying to use this value (that contains special characters) to install exact package version.
For example to install docker I use the following:
vars/main.yaml
docker_engine:
version: 20.10
tasks/main.yaml
- name: Get latest docker version
shell: "apt-cache madison docker-ce | grep {{ docker_engine.version }} | head -1 | awk '{ print $3 }'"
register: docker_exact_version
- name: Display docker_exact_version value
debug:
var: docker_exact_version
tags: debug_info
- name: Install Docker Engine
apt:
update_cache: yes
name: docker-ce= {{ docker_exact_version }}
This fails with
Output:
ok: [...] => {
"docker_exact_version": {
...
"cmd": "apt-cache madison docker-ce | grep 20.1 | head -1 | awk '{ print $3 }'",
"stdout": "5:20.10.18~3-0~debian-bullseye",
...
}
}
...
fatal: [...]: FAILED! =>
File \"/usr/lib/python3.9/sre_parse.py\", line 598, in _parse\r\n raise source.error(msg, len(this) + 1 + len(that))\r\nre.error: bad character range 3-0 at position 35\r\n", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}
If I manually introduce the version and escape the semicolon it's working fine.
- name: Install Docker Engine
apt:
update_cache: yes
name: docker-ce=5\:20.10.18~3-0~debian-bullseye
How can I escape semicolon when using the registered variable?
Thanks a lot

store output of command in variable in ansible

Concerning: Ansible-Playbooks
Is it possible to run a command on the remote machine and store the resulting output into a variable?
I am trying to get the kernel version and install the matching headers like this:
---
- hosts: all
tasks:
- name: install headers
become: yes
become_method: sudo
apt:
name: "{{ packages }}"
vars:
packages:
- linux-headers-`uname -r`
#- a lot of other packages here
Unfortunately uname -r is not executed here.
I am aware of this question: Ansible: Store command's stdout in new variable?
But it looks like this is another topic.
By definition:
Ansible facts are data related to your remote systems, including
operating systems, IP addresses, attached filesystems, and more.
In this link you can see all ansible facts that you can use.
https://docs.ansible.com/ansible/latest/user_guide/playbooks_vars_facts.html
One of those variables is ansible_kernel, this is the version of the kernel of your remote system. By default ansible gets this variables, but if you want to be sure that ansible will get this variables you have to use gather_facts: yes.
---
- hosts: all
gather_facts: yes
tasks:
- name: install
become: yes
become_method: sudo
apt:
name: "{{ packages }}"
vars:
packages:
- linux-headers-{{ ansible_kernel }}
I found a solution but I am not sure if this is really elegant
---
- hosts: all
tasks:
- name: Getting Kernel Version
command: "uname -r"
register: kernel_version
- name: install
become: yes
become_method: sudo
apt:
name: "{{ packages }}"
vars:
packages:
- linux-headers-{{ kernel_version.stdout }}

ansible installing npm using nvm but returning npm command not found on npm install

I am trying to install npm with nvm using ansible playbook script on Ubuntu 18.04.2 LTS. It is getting installed but on running npm install command it returning an error ["/bin/bash: npm: command not found"]
this is the script
- name: Create destination dir if it does not exist
file:
mode: 0775
path: "/usr/local/nvm"
state: directory
when: "nvm_dir != ''"
- name: Install NVM
shell: "curl https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | NVM_SOURCE="" NVM_DIR=/usr/local/nvm PROFILE=/root/.bashrc bash"
args:
warn: false
register: nvm_result
This is the repository where I get the code (https://github.com/morgangraphics/ansible-role-nvm)
By default shell module uses /bin/sh unless the executable has been explicitly defined in the module using args/keyword.
Seems like /bin/bash(a variation of shell is not is installed on the host) thereby giving error. Script needs bin/bash.
bin/bash is mostly installed on all the operating systems. May be some path issue.
Also updated the code below with condition.
---
- hosts: localhost
tasks:
- name: Create destination dir if it does not exist
file:
mode: 0775
path: "/usr/local/nvm"
state: directory
when: "nvm_dir is not defined"
- name: Install NVM
shell: 'curl https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | NVM_SOURCE="" NVM_DIR=/usr/local/nvmPROFILE=/root/.bashrc bash'
args:
warn: false
register: nvm_result

Ansible Do Task If Apt Package Is Missing

I'm looking to do a series of tasks if a specific apt package is missing.
for example:
if graphite-carbon is NOT installed do:
- apt: name=debconf-utils state=present
- shell: echo 'graphite-carbon/postrm_remove_databases boolean false' | debconf-set-selections
- apt: name=debconf-utils state=absent
another example:
if statsd is NOT installed do:
- file: path=/tmp/build state=directory
- shell: cd /tmp/build ; git clone https://github.com/etsy/statsd.git ; cd statsd ; dpkg-buildpackage
- shell: dpkg -i /tmp/build/statsd*.deb
How would I begin to crack this?
I'm thinking maybe I can do a -shell: dpkg -l|grep <package name> and capture the return code somehow.
You can use the package_facts module (requires Ansible 2.5):
- name: Gather package facts
package_facts:
manager: apt
- name: Install debconf-utils if graphite-carbon is absent
apt:
name: debconf-utils
state: present
when: '"graphite-carbon" not in ansible_facts.packages'
...
It looks like my solution is working.
This is an example of how I have it working:
- shell: dpkg-query -W 'statsd'
ignore_errors: True
register: is_statd
- name: create build dir
file: path=/tmp/build state=directory
when: is_statd|failed
- name: install dev packages for statd build
apt: name={{ item }}
with_items:
- git
- devscripts
- debhelper
when: is_statd|failed
- shell: cd /tmp/build ; git clone https://github.com/etsy/statsd.git ; cd statsd ; dpkg-buildpackage
when: is_statd|failed
....
Here is another example:
- name: test if create_superuser.sh exists
stat: path=/tmp/create_superuser.sh
ignore_errors: True
register: f
- name: create graphite superuser
command: /tmp/create_superuser.sh
when: f.stat.exists == True
...and one more
- stat: path=/tmp/build
ignore_errors: True
register: build_dir
- name: destroy build dir
shell: rm -fvR /tmp/build
when: build_dir.stat.isdir is defined and build_dir.stat.isdir
I think you're on the right track with the dpkg | grep, only that the return code will be 0 in any case. But you can simply check the output.
- shell: dpkg-query -l '<package name>'
register: dpkg_result
- do_something:
when: dpkg_result.stdout != ""
I'm a bit late to this party but here's another example that uses exit codes - ensure you explicitly match the desired status text in the dpkg-query results:
- name: Check if SystemD is installed
command: dpkg-query -s systemd | grep 'install ok installed'
register: dpkg_check
tags: ntp
- name: Update repositories cache & install SystemD if it is not installed
apt:
name: systemd
update_cache: yes
when: dpkg_check.rc == 1
tags: ntp

ansible : how to pass multiple commands

I tried this:
- command: ./configure chdir=/src/package/
- command: /usr/bin/make chdir=/src/package/
- command: /usr/bin/make install chdir=/src/package/
which works, but I was hoping for something neater.
So I tried this:
from: https://stackoverflow.com/questions/24043561/multiple-commands-in-the-same-line-for-bruker-topspin which give me back "no such file or directory"
- command: ./configure;/usr/bin/make;/usr/bin/make install chdir=/src/package/
I tried this too: https://u.osu.edu/hasnan.1/2013/12/16/ansible-run-multiple-commands-using-command-module-and-with-items/
but I couldn't find the right syntax to put:
- command: "{{ item }}" chdir=/src/package/
with_items:
./configure
/usr/bin/make
/usr/bin/make install
That does not work, saying there is a quote issue.
To run multiple shell commands with ansible you can use the shell module with a multi-line string (note the pipe after shell:), as shown in this example:
- name: Build nginx
shell: |
cd nginx-1.11.13
sudo ./configure
sudo make
sudo make install
If a value in YAML begins with a curly brace ({), the YAML parser assumes that it is a dictionary. So, for cases like this where there is a (Jinja2) variable in the value, one of the following two strategies needs to be adopted to avoiding confusing the YAML parser:
Quote the whole command:
- command: "{{ item }} chdir=/src/package/"
with_items:
- ./configure
- /usr/bin/make
- /usr/bin/make install
or change the order of the arguments:
- command: chdir=/src/package/ {{ item }}
with_items:
- ./configure
- /usr/bin/make
- /usr/bin/make install
Thanks for #RamondelaFuente alternative suggestion.
Shell works for me.
Simply to say, Shell is the same as you run a shell script.
Notes:
Make sure use | when running multiple cmds.
Shell won't return errors if the last cmd is success (just like normal shell)
Control it with exit 0/1 if you want to stop ansible when error occurs.
The following example shows an error in shell, but it's success at the end of the execution.
- name: test shell with an error
become: no
shell: |
rm -f /test1 # This should be an error.
echo "test2"
echo "test1"
echo "test3" # success
This example shows stopinng shell with exit 1 error.
- name: test shell with exit 1
become: no
shell: |
rm -f /test1 # This should be an error.
echo "test2"
exit 1 # this stops ansible due to returning an error
echo "test1"
echo "test3" # success
reference:
https://docs.ansible.com/ansible/latest/modules/shell_module.html
You can also do like this:
- command: "{{ item }}"
args:
chdir: "/src/package/"
with_items:
- "./configure"
- "/usr/bin/make"
- "/usr/bin/make install"
Hope that might help other
Here is worker like this. \o/
- name: "Exec items"
shell: "{{ item }}"
with_items:
- echo "hello"
- echo "hello2"
I faced the same issue. In my case, part of my variables were in a dictionary i.e. with_dict variable (looping) and I had to run 3 commands on each item.key. This solution is more relevant where you have to use with_dict dictionary with running multiple commands (without requiring with_items)
Using with_dict and with_items in one task didn't help as it was not resolving the variables.
My task was like:
- name: Make install git source
command: "{{ item }}"
with_items:
- cd {{ tools_dir }}/{{ item.value.artifact_dir }}
- make prefix={{ tools_dir }}/{{ item.value.artifact_dir }} all
- make prefix={{ tools_dir }}/{{ item.value.artifact_dir }} install
with_dict: "{{ git_versions }}"
roles/git/defaults/main.yml was:
---
tool: git
default_git: git_2_6_3
git_versions:
git_2_6_3:
git_tar_name: git-2.6.3.tar.gz
git_tar_dir: git-2.6.3
git_tar_url: https://www.kernel.org/pub/software/scm/git/git-2.6.3.tar.gz
The above resulted in an error similar to the following for each {{ item }} (for 3 commands as mentioned above). As you see, the values of tools_dir is not populated (tools_dir is a variable which is defined in a common role's defaults/main.yml and also item.value.git_tar_dir value was not populated/resolved).
failed: [server01.poc.jenkins] => (item=cd {# tools_dir #}/{# item.value.git_tar_dir #}) => {"cmd": "cd '{#' tools_dir '#}/{#' item.value.git_tar_dir '#}'", "failed": true, "item": "cd {# tools_dir #}/{# item.value.git_tar_dir #}", "rc": 2}
msg: [Errno 2] No such file or directory
Solution was easy. Instead of using "COMMAND" module in Ansible, I used "Shell" module and created a a variable in roles/git/defaults/main.yml
So, now roles/git/defaults/main.yml looks like:
---
tool: git
default_git: git_2_6_3
git_versions:
git_2_6_3:
git_tar_name: git-2.6.3.tar.gz
git_tar_dir: git-2.6.3
git_tar_url: https://www.kernel.org/pub/software/scm/git/git-2.6.3.tar.gz
#git_pre_requisites_install_cmds: "cd {{ tools_dir }}/{{ item.value.git_tar_dir }} && make prefix={{ tools_dir }}/{{ item.value.git_tar_dir }} all && make prefix={{ tools_dir }}/{{ item.value.git_tar_dir }} install"
#or use this if you want git installation to work in ~/tools/git-x.x.x
git_pre_requisites_install_cmds: "cd {{ tools_dir }}/{{ item.value.git_tar_dir }} && make prefix=`pwd` all && make prefix=`pwd` install"
#or use this if you want git installation to use the default prefix during make
#git_pre_requisites_install_cmds: "cd {{ tools_dir }}/{{ item.value.git_tar_dir }} && make all && make install"
and the task roles/git/tasks/main.yml looks like:
- name: Make install from git source
shell: "{{ git_pre_requisites_install_cmds }}"
become_user: "{{ build_user }}"
with_dict: "{{ git_versions }}"
tags:
- koba
This time, the values got successfully substituted as the module was "SHELL" and ansible output echoed the correct values. This didn't require with_items: loop.
"cmd": "cd ~/tools/git-2.6.3 && make prefix=/home/giga/tools/git-2.6.3 all && make prefix=/home/giga/tools/git-2.6.3 install",