How to check if some repos exist in /etc/yum.repos.d - ansible

I have written this playbook, in order to check if some repos exist in /etc/yum.repos.d/, but I am not sure if it is correct. Is it correct?
- name: Check that the repos exists
stat:
path: /etc/yum.repos.d/{{ item }}
with_items:
- "rhel-mc.repo"
- "epel-mc.repo"
- "redhat.repo"
register: stat_result
debug:
msg: "Repo file exists..."
when: stat_result.stat.exists
debug:
msg: "Repo file not found"
when: stat_result.stat.exists == False

It almost is perfect!
Make sure module options are the only things aligned inside the module, everything else should be outside of it. These include:
with_items
when
register
You need to ensure you wrap the {item} in single quotes, wrapping the entire path works fine here.
You forgot the dashes for the debugs.
Finally, ansible lint will fail if you compare to True or False so instead I would not before the conditional.
- name: Check that the repos exists
stat:
path: '/etc/yum.repos.d/{{ item }}'
with_items:
- "rhel-mc.repo"
- "epel-mc.repo"
- "redhat.repo"
register: stat_result
- debug:
msg: "Repo file exists..."
when: stat_result.stat.exists
- debug:
msg: "Repo file not found"
when: not stat_result.stat.exists

You can also use the module assert to check for a condition and based on the result display different messages. Something like this:
- name: Check that the repos exists
stat:
path: '/etc/yum.repos.d/{{ item }}'
with_items:
- "rhel-mc.repo"
- "epel-mc.repo"
- "redhat.repo"
register: stat_result
- assert:
that: stat_result.stat.exists
success_msg: "Repo file exists..."
fail_msg: "Repo file not found"
You don't have to wrap path with quotes but it is a good practice in programming in general, to wrap strings in quotes, especially when (in Ansible) it contains a variable.

Related

Ansible task continuation only when find strings in registred variable

I have Ansible code below
- name: Try to delete file
shell: rm -rf /tmp/"{{ file_Del }}"
register: result
ignore_errors: True
- name: Display result
debug:
var: result.stdout
result.stdout can be either
"result.stderr": "Error from server (NotFound): file /tmp/somefile not found"
OR
"result.stderr": "" <= in success
Both of these are valid but I want to fail Ansible of anything else in the "result.stderr". Ex: "result.stderr": "rm -rf Command not found"
How do I do that with "end_play"
- meta: end_play
when: "*Error from server (NotFound): file.*.*not found" not in result.stderr OR result.stderr ==""
When looking at this question it looks like you're working in a bit of a mess, and trying to continue in that way. I think it's best to not move further in that direction, but rather work in a more professional fashion.
For now; a more elegant solution, with only using Ansible modules;
---
- hosts: local_test
# gather_facts: False
vars:
file_Del: test
tasks:
- name: find some files at a location, recursively
find:
paths: "/tmp/{{ file_Del }}"
recurse: true
register: found_files
- name: display files found, to be deleted, could be empty
debug:
msg: "Files found are {{ item.path }}"
with_items:
- "{{ found_files.files }}"
- name: delete files when found
file:
path: "{{ item.path }}"
state: absent
with_items: "{{ found_files.files | default ([]) }}"
- name: display files found, to be deleted, could be empty
debug:
msg: "{{ found_files }}"
- name: end play when no files are found
meta: end_play
when: found_files.matched == 0
- name: this task is skipped when there are no files found, but executed when files were deleted
shell: echo hi
Regarding your matter with "rm -rf Command not found"; this looks just out of the ordinary, e.g. you're executing a playbook on both a Linux and a Windows host, where the task would fail on a Windows host. Each Linux host I know of is pre-installed with rm, even most Docker containers and OpenBSD hosts.
It could be wise to separate these tasks per environment.
Perhaps a bit of context could help us, and eventually you, out.
rm isn't very chatty. See rm -rf return codes. But it's possible to use find and xargs. For example the play
- hosts: localhost
vars:
file_del: xxx
tasks:
- shell: "find /tmp/{{ file_del }} | xargs /bin/rm"
ignore_errors: True
register: result
- meta: end_play
when: result.rc == 123
- debug:
msg: /tmp/{{ file_del }} found and deleted. Continue play.
gives (when /tmp/{{ file_del }} found and deleted)
"msg": "/tmp/xxx found and deleted. Continue play."
and ends the play when file not found result.rc == 123.
Notes
Make sure rc 123 is reported when file not found
It's possible to test result.stderr
If possible write a custom command. For example
$ cat my_custom_command.sh
#!/bin/sh
if [ "$#" -eq "0" ]; then
echo "[ERR 123] No files found.";
exit 123;
fi
echo "Here I can do whatever I want with $#";
exit 0;

Compare two files with Ansible

I am struggling to find out how to compare two files. Tried several methods including this one which errors out with:
FAILED! => {"msg": "The module diff was not found in configured module paths. Additionally, core modules are missing. If this is a
checkout, run 'git pull --rebase' to correct this problem."}
Is this the best practice to compare two files and ensure the contents are the same or is there a better way?
Thanks in advance.
My playbook:
- name: Find out if cluster management protocol is in use
ios_command:
commands:
- show running-config | include ^line vty|transport input
register: showcmpstatus
- local_action: copy content="{{ showcmpstatus.stdout_lines[0] }}" dest=/poc/files/{{ inventory_hostname }}.result
- local_action: diff /poc/files/{{ inventory_hostname }}.result /poc/files/transport.results
failed_when: "diff.rc > 1"
register: diff
- name: debug output
debug: msg="{{ diff.stdout }}"
Why not using stat to compare the two files?
Just a simple example:
- name: Get cksum of my First file
stat:
path : "/poc/files/{{ inventory_hostname }}.result"
register: myfirstfile
- name: Current SHA1
set_fact:
mf1sha1: "{{ myfirstfile.stat.checksum }}"
- name: Get cksum of my Second File (If needed you can jump this)
stat:
path : "/poc/files/transport.results"
register: mysecondfile
- name: Current SHA1
set_fact:
mf2sha1: "{{ mysecondfile.stat.checksum }}"
- name: Compilation Changed
debug:
msg: "File Compare"
failed_when: mf2sha1 != mf1sha1
your "diff" task is missing the shell keyword, Ansible thinks you want to use the diff module instead.
also i think diff (as name of the variable to register the tasks result) leads ansible to confusion, change to diff_result or something.
code (example):
tasks:
- local_action: shell diff /etc/hosts /etc/fstab
failed_when: "diff_output.rc > 1"
register: diff_output
- debug:
var: diff_output
hope it helps
From Ansible User Guide: https://docs.ansible.com/ansible/latest/user_guide/playbooks_error_handling.html
- name: Fail task when both files are identical
ansible.builtin.raw: diff foo/file1 bar/file2
register: diff_cmd
failed_when: diff_cmd.rc == 0 or diff_cmd.rc >= 2
A slightly shortened version of 'imjoseangel' answer which avoids setting facts:
vars:
file_1: cats.txt
file_2: dogs.txt
tasks:
- name: register the first file
stat:
path: "{{ file_1 }}"
checksum: sha1
get_checksum: yes
register: file_1_checksum
- name: register the second file
stat:
path: "{{ file_2 }}"
checksum: sha1
get_checksum: yes
register: file_2_checksum
- name: Check if the files are the same
debug: msg="The {{ file_1 }} and {{ file_2 }} are identical"
failed_when: file_1_checksum.stat.checksum != file_2_checksum.stat.checksum
ignore_errors: true

Expecting dict; got: shell:Error

I am getting the below error
ERROR: expecting dict; got: shell:"ls /mule/ansiple/mule-enterprise-standalone-3.4.1/*.txt" register:the_file
I want to check the folder is exist.
if exist I need to specify some path /mule/acc
if the path does not exist need to point /mule/bcc
The following is ansible playbook
---
# get name of the .txt file
- stat: path=/mule/ansiple/mule-enterprise-standalone-3.4.1
register:the_file
when: the_file.stat.exists == True
- shell:"ls /mule/ansiple/mule-enterprise-standalone-3.4.1/*.txt"
register:the_file
- debug: msg="{{ the_file }}"
- set_fact: app_folder="{{ the_file.stdout | replace('-anchor.txt','') }}"
- debug: msg="{{ app_folder }}"
- debug: msg="{{ the_file.stdout }}"
# delete the .txt file
- name: Delete the anchor.txt file
file: path="{{ the_file.stdout }}" state=absent
# wait until the app folder disappears
- name: Wait for the folder to disappear
wait_for: path="{{ app_folder }}" state=absent
# copy the zip file
- name: Copy the zip file
copy: src="../p" dest="/c"
You almost have the YAML right, but there is one YAML error in your file and that is on the line:
register:the_file
because the line preceding it starts the first element of the toplevel sequence as a mapping through defining a key-value pair based on the colon (':') followed by space, and that cannot be followed by a scalar (or a sequence). If that file was your input, you would have gotten a syntax error by the YAML parser with part of the message looking like:
register:the_file
^
could not find expected ':'
This:
- shell:"ls /mule/ansiple/mule-enterprise-standalone-3.4.1/*.txt"
register:the_file
is perfectly fine YAML. It defines the second element of the top-level sequence to be a scalar string:
shell:"ls /mule/ansiple/mule-enterprise-standalone-3.4.1/*.txt" register:the_file
(you can break scalar strings over multiple lines in YAML).
Now ansible doesn't like that and expects that element, and probably all top-level sequence elements, to be mappings. And for a mapping you need to have a key value pair, which is (like with register:the_file) separated/indicated by a colon-space pair of characters in YAML. So ansible (not YAML) probably wants:
shell: ls /mule/ansiple/mule-enterprise-standalone-3.4.1/*.txt
register: the_file
please note that the quotes around the scalar value ls /mule/ansiple/mule-enterprise-standalone-3.4.1/*.txt are unnecessary in YAML.
There might be other things ansible expects from the structure of the file, but I would start with the above change and see if ansible throws further errors on:
# get name of the .txt file
- stat: path=/mule/ansiple/mule-enterprise-standalone-3.4.1
register: the_file
when: the_file.stat.exists == True
- shell: ls /mule/ansiple/mule-enterprise-standalone-3.4.1/*.txt
register: the_file
- debug: msg="{{ the_file }}"
- set_fact: app_folder="{{ the_file.stdout | replace('-anchor.txt','') }}"
- debug: msg="{{ app_folder }}"
- debug: msg="{{ the_file.stdout }}"
# delete the .txt file
- name: Delete the anchor.txt file
file: path="{{ the_file.stdout }}" state=absent
# wait until the app folder disappears
- name: Wait for the folder to disappear
wait_for: path="{{ app_folder }}" state=absent
# copy the zip file
- name: Copy the zip file
copy: src="../analytic-core-services-mule-3.0.0-SNAPSHOT.zip" dest="/mule/ansiple/mule-enterprise-standalone-3.4.1"
You need to learn YAML syntax. After every colon you are required to add a whitespace. The message comes from the YAML parser, telling you it expected a dictionary:
key1: value1
key2: value2
Instead it found no key-value-pair:
key1:value1
Specifically it complains about this line:
- shell:"ls /mule/ansiple/mule-enterprise-standalone-3.4.1/*.txt"
which should be
- shell: "ls /mule/ansiple/mule-enterprise-standalone-3.4.1/*.txt"
But you have the same issue in two more lines which look like:
register:the_file
and should be :
register: the_file
If in doubt an error comes from Ansible tasks or simply from a YAML parsing error, paste your YAML definition into any online YAML parser.
So much for the format. Now to logical problems:
- stat: path=/mule/ansiple/mule-enterprise-standalone-3.4.1
register: the_file
when: the_file.stat.exists == True
Unless you have the_file already registered from a previous task you didn't show, this can't work. when is the condition to decide if the task should be run. You can not execute a task depending on its outcome. It first has to run before the result is available. At the time the condition is evaluated the_file simply won't exist and this should result in an error, complaining that a None object does not have a key stat or something similar.
Then in the next task you again register the result with the same name.
- shell: "ls /mule/ansiple/mule-enterprise-standalone-3.4.1/*.txt"
register: the_file
This simply will override the previous registered result. Maybe you meant to have the condition from the first task on the second. But even that would not work. A result still will be registered from skipped tasks, simply stating the task was skipped. You either need to store the results in unique vars or check all possible file locations in a single task.
Cleaned up your playbook would look like this:
---
# get name of the .txt file
- stat:
path: /mule/ansiple/mule-enterprise-standalone-3.4.1
register: the_file
when: the_file.stat.exists
- shell: ls /mule/ansiple/mule-enterprise-standalone-3.4.1/*.txt
register: the_file
- debug:
msg: "{{ the_file }}"
- set_fact:
app_folder: "{{ the_file.stdout | replace('-anchor.txt','') }}"
- debug:
msg: "{{ app_folder }}"
- debug:
msg: "{{ the_file.stdout }}"
- name: Delete the anchor.txt file
file:
path: "{{ the_file.stdout }}"
state: absent
- name: Wait for the folder to disappear
wait_for:
path: "{{ app_folder }}"
state: absent
- name: Copy the zip file
copy:
src: ../analytic-core-services-mule-3.0.0-SNAPSHOT.zip
dest: /mule/ansiple/mule-enterprise-standalone-3.4.1
...

How to check if a file exists in Ansible?

I have to check whether a file exists in /etc/. If the file exists then I have to skip the task.
Here is the code I am using:
- name: checking the file exists
command: touch file.txt
when: $(! -s /etc/file.txt)
You can first check that the destination file exists or not and then make a decision based on the output of its result:
tasks:
- name: Check that the somefile.conf exists
stat:
path: /etc/file.txt
register: stat_result
- name: Create the file, if it doesnt exist already
file:
path: /etc/file.txt
state: touch
when: not stat_result.stat.exists
The stat module will do this as well as obtain a lot of other information for files. From the example documentation:
- stat: path=/path/to/something
register: p
- debug: msg="Path exists and is a directory"
when: p.stat.isdir is defined and p.stat.isdir
This can be achieved with the stat module to skip the task when file exists.
- hosts: servers
tasks:
- name: Ansible check file exists.
stat:
path: /etc/issue
register: p
- debug:
msg: "File exists..."
when: p.stat.exists
- debug:
msg: "File not found"
when: p.stat.exists == False
In general you would do this with the stat module. But the command module has the creates option which makes this very simple:
- name: touch file
command: touch /etc/file.txt
args:
creates: /etc/file.txt
I guess your touch command is just an example? Best practice would be to not check anything at all and let ansible do its job - with the correct module. So if you want to ensure the file exists you would use the file module:
- name: make sure file exists
file:
path: /etc/file.txt
state: touch
Discovered that calling stat is slow and collects a lot of info that is not required for file existence check.
After spending some time searching for solution, i discovered following solution, which works much faster:
- raw: test -e /path/to/something && echo -n true || echo -n false
register: file_exists
- debug: msg="Path exists"
when: file_exists.stdout == "true"
You can use Ansible stat module to register the file, and when module to apply the condition.
- name: Register file
stat:
path: "/tmp/test_file"
register: file_path
- name: Create file if it doesn't exists
file:
path: "/tmp/test_file"
state: touch
when: file_path.stat.exists == False
Below is the ansible play which i used to remove the file if the file exists on OS end.
- name: find out /etc/init.d/splunk file exists or not'
stat:
path: /etc/init.d/splunk
register: splunkresult
tags:
- always
- name: 'Remove splunk from init.d file if splunk already running'
file:
path: /etc/init.d/splunk
state: absent
when: splunkresult.stat.exists == true
ignore_errors: yes
tags:
- always
I have used play condition as like below
when: splunkresult.stat.exists == true --> Remove the file
you can give true/false based on your requirement
when: splunkresult.stat.exists == false
when: splunkresult.stat.exists == true
I find it can be annoying and error prone to do a lot of these .stat.exists type checks. For example they require extra care to get check mode (--check) working.
Many answers here suggest
get and register
apply when register expression is true
However, sometimes this is a code smell so always look for better ways to use Ansible, specifically there are many advantages to using the correct module. e.g.
- name: install ntpdate
package:
name: ntpdate
or
- file:
path: /etc/file.txt
owner: root
group: root
mode: 0644
But when it is not possible use one module, also investigate if you can register and check the result of a previous task. e.g.
# jmeter_version: 4.0
- name: Download Jmeter archive
get_url:
url: "http://archive.apache.org/dist/jmeter/binaries/apache-jmeter-{{ jmeter_version }}.tgz"
dest: "/opt/jmeter/apache-jmeter-{{ jmeter_version }}.tgz"
checksum: sha512:eee7d68bd1f7e7b269fabaf8f09821697165518b112a979a25c5f128c4de8ca6ad12d3b20cd9380a2b53ca52762b4c4979e564a8c2ff37196692fbd217f1e343
register: download_result
- name: Extract apache-jmeter
unarchive:
src: "/opt/jmeter/apache-jmeter-{{ jmeter_version }}.tgz"
dest: "/opt/jmeter/"
remote_src: yes
creates: "/opt/jmeter/apache-jmeter-{{ jmeter_version }}"
when: download_result.state == 'file'
Note the when: but also the creates: so --check doesn't error out
I mention this because often these less-than-ideal practices come in pairs i.e. no apt/yum package so we have to 1) download and 2) unzip
Hope this helps
I use this code and it works fine for folders and files. Just make sure there is no trailing spaces after the folder name. If folder exists , the file_exists.stdout will be "true" otherwise it will just be an empty string ""
- name: check filesystem existence
shell: if [[ -d "/folder_name" ]]; then echo "true"; fi
register: file_exists
- name: debug data
debug:
msg: "Folder exists"
when: file_exists.stdout == "true"
vars:
mypath: "/etc/file.txt"
tasks:
- name: checking the file exists
command: touch file.txt
when: mypath is not exists
A note on relative paths to complement the other answers.
When doing infrastructure as code I'm usually using roles and tasks that accept relative paths, specially for files defined in those roles.
Special variables like playbook_dir and role_path are very useful to create the absolute paths needed to test for existence.
You can use shell commands to check if file exists
- set_fact:
file: "/tmp/test_file"
- name: check file exists
shell: "ls {{ file }}"
register: file_path
ignore_errors: true
- name: create file if don't exist
shell: "touch {{ file }}"
when: file_path.rc != 0

Move files on remote system, only if destination doesn't exist, with Ansible

I'm trying to write an Ansible role that moves a number of files on the remote system. I found a Stack Overflow post about how to do this, which essentially says "just use the command module with 'mv'". I have a single task defined with a with_items statement like this where each item in dirs is a dictionary with src and dest keys:
- name: Move directories
command: mv {{ item.src }} {{ item.dest }}
with_items: dirs
This is good and it works, but I run into problems if the destination directory already exists. I don't want to overwrite it, so I thought about trying to stat each dest directory first. I wanted to update the dirs variable with the stat info, but as far as I know, there isn't a good way to set or update variables once they're defined. So I used stat to get the info on each directory and then saved the data with register:
- name: Check if directories already exist
stat: path={{ item.dest }}
with_items: dirs
register: dirs_stat
Is there a way to tie the registered stat info to the mv commands? This would be easy if it were a single directory. The looping is what makes this tricky. Is there a way to do this without unrolling this loop into two tasks per directory?
This is not the simplest solution by any means, but if you wanted to use Ansible and not "unroll":
---
- hosts: all
vars:
dirs:
- src: /home/ubuntu/src/test/src1
dest: /home/ubuntu/src/test/dest1
- src: /home/ubuntu/src/test/src2
dest: /home/ubuntu/src/test/dest2
tasks:
- stat:
path: "{{item.dest}}"
with_items: dirs
register: dirs_stat
- debug:
msg: "should not copy {{ item.0.src }}"
with_together:
- dirs
- dirs_stat.results
when: item.1.stat.exists
Simply adapt the debug task to run the appropriate command task instead and when: to when: not ....
You can use stat keyword in your playbook to check if it exists or not if it doesn't then move.
---
- name: Demo Playbook
hosts: all
become: yes
tasks:
- name: check destination
stat:
path: /path/to/dest
register: p
- name: copy file if not exists
command: mv /path/to/src /path/to/src
when: p.stat.exists == False

Resources