I am a novice to Ansible. I am trying to ping a set of remote hosts and get its status. The ping status I need to print in one neat tabular column format. The below code is not showing the exact output that I am looking for. Could you please share the sample Ansible code which does this?
- name: check reachable hosts
hosts: protex
gather_facts: false
tasks:
- command: ping -c1 {{ inventory_hostname }}
delegate_to: localhost
register: ping_result
#ignore_errors: yes
- debug: msg:"{{ping_result.rc}}"
I am expecting the output in this format.
Hostname
Ping Status
10.0.0.1
Reachable
10.0.0.2
Not Reachable
Related
So, I have a playbook using a hosts file template to update or revert hosts files on 18 specific Linux VMs. The entry which goes at the end of the file looks like:
10.x.x.66 fooconnect
This above example would be on the 1st of 18 VMs, the 18th VM would look like:
10.x.x.83 fooconnect
Normally, that hostname resolves to a VIP. However, we found during some load testing that it may be beneficial to point each front-end VM to a back-end VM directly. So, my goal is to have a playbook that can update what the hostname resolves to with the above mentioned range, or revert it back to the VIP (reverting back is done using a template only--this part works fine).
What I am unsure about is how to implement this in Ansible. Is there a way to loop through the IPs using jinja2 template "for loops?" Or maybe using lineinfile with some loop magic?
Here is my Ansible role example. For the moment I am using a dirty shell command to create my IP list...open to suggestions for a better way to implement this.
- name: Add a line to a hosts file using a template
template:
src: "{{ srcfile }}"
dest: "{{ destfile }}"
owner: "{{ own_var }}"
group: "{{ grp_var }}"
mode: "{{ mode_var }}"
backup: yes
- name: Get the IPs
shell: "COUNTER=66;for i in {66..83};do echo 10.x.x.$i;((COUNTER++));done"
register: pobs_ip
- name: Add a line
lineinfile:
path: /etc/hosts
line: "{{item}} fooconnect" #Ideally would want "item" to just be one IP and not
insertafter: EOF #the entire list as it would be like this.
loop: "{{pobsips}}"
VARs file:
pobsips:
- "{{pobs_ip.stdout}}"
Instead of using a shell task, we can improvise it and create the range of IP addresses using set_fact with range. Once we have the range of IP addresses in a "list", we can loop lineinfile with that and achieve this.
Example:
- name: create a range of IP addresses in a variable my_range
set_fact:
my_range: "{{ my_range|default([]) + [ '10.1.1.' ~ item ] }}"
loop: "{{ range(66, 84)|list }}"
- name: Add a line to /etc/hosts
lineinfile:
path: /etc/hosts
line: "{{ item }} fooconnect"
insertafter: EOF
loop: "{{ my_range }}"
Updated answer:
There is another approach if we want to append only 1 line into the /etc/hosts file of each host with incrementing IP addresses.
For this we can use the ipmath of ipaddr filter to get the next IP address for given IP address.
Use ansible_play_hosts to get the list of hosts on which play is running
Set an index variable index_var and when condition to update file only when the ansible_hostname or inventory_hostname matches.
Run playbook serially and only once on a host per run using serial and run_once flags.
Let's consider an example inventory file like:
[group_1]
host1
host2
host3
host4
...
Then in playbook:
- hosts: group_1
serial: 1
vars:
start_ip: 10.1.1.66
tasks:
- name: Add a line to /etc/hosts
lineinfile:
path: "/tmp/hosts"
line: "{{ start_ip|ipmath(my_idx) }} fooserver"
insertafter: EOF
loop: "{{ ansible_play_hosts }}"
loop_control:
index_var: my_idx
run_once: true
when: item == inventory_hostname
I'm working on a playbook that does the following:
Goes into a specified path on each Windows server
Slurps text from a file and adds it to a variable
Performs a check on the variable to see if a string of text exists
Writes the results to a file based on the outcome.
Here is the code I have for this:
---
- name: Slurps text from file on Windows server
hosts: win
gather_facts: false
tasks:
- name: Get text
slurp:
src: D:\testsearch.ini
register: norequest
- name: Check for norequest=false in variable
lineinfile:
dest: ./norequest.csv
line: "{{ inventory_hostname }} There is a false value"
state: present
create: true
insertafter: EOF
when: '"''NoRequest = False'' in norequest.content|b64decode"|lower'
delegate_to: localhost
- name: Check for norequest=true in variable
lineinfile:
dest: ./norequest.csv
line: "{{ inventory_hostname }} There is a true value."
state: present
create: true
insertafter: EOF
when: '"''NoRequest = True'' in norequest.content|b64decode"|lower'
delegate_to: localhost
Based on my results, it looks like the playbook slurps the text from the files on both test servers and adds it all to the variable, then performs the conditional check against one of the servers (since the task itself is being delegated to localhost) and outputs the results to the file as though they all came from SERVER1 (the last part seems to be due to the delegation).
PLAY [Slurps text from file on Windows server] *******************************
TASK [Delete previous norequest file] *******************************
changed: [SERVER1 -> localhost]
TASK [Get text] ***************************************
ok: [SERVER2]
ok: [SERVER1]
TASK [Check for norequest=false in variable] ********************************
changed: [SERVER1 -> localhost]
TASK [Check for norequest=true in variable] *******************************
changed: [SERVER1 -> localhost]
PLAY RECAP *******************************
SERVER1 : ok=4 changed=3 unreachable=0 failed=0
SERVER2 : ok=1 changed=0 unreachable=0 failed=0
Here are the contents of the file after the playbook is run:
SERVER1 There is a false value
SERVER1 There is a true value.
This is what the outcome should be if the playbook worked as I want it to:
SERVER1 There is a false value
SERVER2 There is a true value.
I feel like part (or all) of my issue might be that I'm looking at this through a PowerShell lens; as in, "FOR EACH server, get the text from the file, perform a conditional check, write the output to the outfile, then move on to the next server." Is something like that possible in an Ansible playbook? I've looked into dictionaries as a way to solve this, but the only good examples I could find used pre-existing dictionaries or dictionaries populated at runtime with basic server info.
Seems to me, that the when: condition was wrong. Matching is now done via a regexp. Tested it with this playbook:
---
- name: Slurps text from file on Windows server
hosts:
- SERVER1
- SERVER2
gather_facts: false
tasks:
- name: Get text
slurp:
src: D:\testsearch.ini
register: norequest
- name: Check for norequest=false in variable
lineinfile:
dest: ./norequest.csv
line: "{{ inventory_hostname }} There is a false value"
state: present
create: true
insertafter: EOF
when: 'norequest["content"] | b64decode | lower | regex_search("norequest *= *false")'
delegate_to: localhost
- name: Check for norequest=true in variable
lineinfile:
dest: ./norequest.csv
line: "{{ inventory_hostname }} There is a true value"
state: present
create: true
insertafter: EOF
when: 'norequest["content"] | b64decode | lower | regex_search("norequest *= *true")'
delegate_to: localhost
The file testsearch.ini has the following contents on the systems:
SERVER1
NoRequest = False
SERVER2
NoRequest = True
Executing the playbook with ansible-playbook -i hosts play.yml gives the following output:
PLAY [SERVER1,SERVER2] *********************************************************
TASK [Get text] ****************************************************************
ok: [SERVER1]
ok: [SERVER2]
TASK [Check for norequest=false in variable] ***********************************
skipping: [SERVER2]
ok: [SERVER1 -> localhost]
TASK [Check for norequest=true in variable] ************************************
skipping: [SERVER1]
ok: [SERVER2 -> localhost]
PLAY RECAP *********************************************************************
SERVER1 : ok=2 changed=0 unreachable=0 failed=0
SERVER2 : ok=2 changed=0 unreachable=0 failed=0
The contents of norequest.csv after the run is
SERVER1 There is a false value
SERVER2 There is a true value
I have a playbook which has multiple roles and serial setup so that fist it's running on one machine then on the rest of them. In one of the roles I have the following tasks:
- name: getting dbnodes IP addresses
local_action: shell echo "{% for host in groups['dbnodes'] %}{{ hostvars[host]['ansible_eth0']['ipv4']['address'] }},{% endfor %}"
run_once: true
register: IPS
Basically what I want to do is to gather the IP addresses of all the hosts and register it with IPS for further usage. But the task is failing because of the serial (I think) with the following error.
TASK [dbcluster : getting dbnodes IP addresses] ********************************
fatal: [162.220.52.190]: FAILED! => {"failed": true, "msg": "the field 'action' has an invalid value, which appears to include a variable that is undefined. The error was: 'dict object' has no attribute 'ansible_eth0'\n\nThe error appears to have been in '/root/tenon-delivery/ansible/roles/dbcluster/tasks/main.yml': line 52, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- name: getting dbnodes IP addresses\n ^ here\n"}
While running ansible dbnode -s setup I can see that the ansible_eth0 has a proper value. I don't understand why it is saying that it's undefined.
Any idea how to gather the facts on all machines in the same time while still having the option several tasks/handlers still being done serialized.
ansible_eth0 fact may be unknown at the time of your task run.
You may want to add fact gathering play at the very top of your playbook:
- hosts: dbnodes
gather_facts: yes
tasks:
- debug: msg="facts gathering"
- hosts: othernodes
tasks:
- name: getting dbnodes IP addresses
...
Env is: Ansible 1.9.4 or 1.9.2, Linux CentOS 6.5
I have a role build where:
$ cat roles/build/defaults/main.yml:
---
build_user: confman
build_group: confman
tools_dir: ~/tools
$ cat roles/build/tasks/main.yml
- debug: msg="User is = {{ build_user }} -- {{ tools_dir }}"
tags:
- koba
- name: Set directory ownership
file: path="{{ tools_dir }}" owner={{ build_user }} group={{ build_group }} mode=0755 state=directory recurse=yes
become_user: "{{ build_user }}"
tags:
- koba
- name: Set private key file access
file: path="{{ item }}" owner={{ build_user }} group={{ build_group }} mode=0600 state=touch
with_fileglob:
- "{{ tools_dir }}/vmwaretools-lib-*/lib/insecure_private_key"
# with_items:
# - ~/tools/vmwaretools/lib/insecure_private_key
become_user: "{{ build_user }}"
tags:
- koba
In my workspace: hosts file (inventory) contains:
[ansible_servers]
server01.project.jenkins
site.yml (playbook) contains:
---
- hosts: ansible_servers
sudo: yes
roles:
- build
I'm running the following command:
$ ansible-playbook site.yml -i hosts -u confman --private-key ${DEPLOYER_KEY_FILE} -t koba
I'm getting the following error and for some reason, become_user in Ansible while using Ansible loop: with_fileglob is NOT using ~ (home directory) of confman user (which is set in variable {{ build_user }}, instead of that, it's picking my own user ID (c123456).
In the console output for debug action, it's clear that the user (due to become_user) is confman and value of tools_dir variable is ~/tools.
PLAY [ansible_servers] ********************************************************
GATHERING FACTS ***************************************************************
ok: [server01.project.jenkins]
TASK: [build | debug msg="User is = {{ build_user }} -- {{ tools_dir }}"] *****
ok: [server01.project.jenkins] => {
"msg": "User is = confman -- ~/tools"
}
TASK: [build | Set directory ownership] ***************************************
changed: [server01.project.jenkins]
TASK: [build | Set private key file access] ***********************************
failed: [server01.project.jenkins] => (item=/user/home/c123456/tools/vmwaretools-lib-1.0.8-SNAPSHOT/lib/insecure_private_key) => {"failed": true, "item": "/user/home/c123456/tools/vmwaretools-lib-1.0.8-SNAPSHOT/lib/insecure_private_key", "parsed": false}
BECOME-SUCCESS-ajtxlfymjcquzuolgfrrxbssfolqgrsg
Traceback (most recent call last):
File "/tmp/ansible-tmp-1449615824.69-82085663620220/file", line 1994, in <module>
main()
File "/tmp/ansible-tmp-1449615824.69-82085663620220/file", line 372, in main
open(path, 'w').close()
IOError: [Errno 2] No such file or directory: '/user/home/c123456/tools/vmwaretools-lib-1.0.8-SNAPSHOT/lib/insecure_private_key'
OpenSSH_5.3p1, OpenSSL 1.0.1e-fips 11 Feb 2013
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: Applying options for *
debug1: auto-mux: Trying existing master
debug1: mux_client_request_session: master session id: 2
debug1: mux_client_request_session: master session id: 2
Shared connection to server01.project.jenkins closed.
As per the error above, the file it's trying for variable item is /user/home/c123456/tools/vmwaretools-lib-1.0.8-SNAPSHOT/lib/insecure_private_key but there's no such file inside my user ID's home directory. But, this file does exist for user confman's home directory.
i.e. the following file exists.
/user/home/confman/tools/vmwaretools-lib-1.0.7-SNAPSHOT/lib/insecure_private_key
/user/home/confman/tools/vmwaretools-lib-1.0.7/lib/insecure_private_key
/user/home/confman/tools/vmwaretools-lib-1.0.8-SNAPSHOT/lib/insecure_private_key
All, I want is to iterate of these files in ~confman/tools/vmwaretools-lib-*/.. location containing the private key file and change the permission but using "with_fileglob" become_user to set the user during an action is NOT working.
If I comment out the with_fileglob section and use/uncomment with_items section in the tasks/main.yml, then it (become_user) works fine and picks ~confman (instead of ~c123456) and gives the following output:
TASK: [build | Set private key file access] ***********************************
changed: [server01.project.jenkins] => (item=~/tools/vmwaretools/lib/insecure_private_key)
One strange thing I found is, there is no user c123456 on the target machine (server01.project.jenkins) and that's telling me that with_fileglob is using the source/local/master Ansible machine (where I'm running ansible-playbook command) to find the GLOB Pattern (instead of finding / running it over SSH on server01.project.jenkins server), It's true that on local/source Ansible machine, I'm logged in as c123456. Strange thing is, in the OUTPUT, it still shows the target machine but pattern path is coming from source machine as per the output above.
failed: [server01.project.jenkins]
Any idea! what I'm missing here? Thanks.
PS:
- I don't want to set tools_dir: "~{{ build_user }}/tools" or hardcode it as a user can pass tools_dir variable at command line (while running ansible-playbook command using -e / --extra-vars "tools_dir=/production/slave/tools"
Further researching it, I found with_fileglob is for List of local files to iterate over, described using shell fileglob notation (e.g., /playbooks/files/fooapp/*) then, what should I use to iterate over on target/remote server (server01.project.jenkins in my case) using pattern match (fileglob)?
Using with_fileglob, it'll always run on the local/source/master machine where you are running ansible-playbook/ansible. Ansible docs for Loops doesn't clarifies this info (http://docs.ansible.com/ansible/playbooks_loops.html#id4) but I found this clarification here: https://github.com/lorin/ansible-quickref
Thus, while looking for the pattern, it's picking the ~ for user c123456.
Console output is showing [server01.project.jenkins] as it's a different processing/step to read what's there in the inventory/hosts file.
I tried to use with_lines as well as per this post: ansible: Is there something like with_fileglobs for files on remote machine?
But, when I tried the following, it still didn't work i.e. read the pattern on local machine instead of target machine (Ansible docs tells with_items doesn't run on local machine but on the controlling machine):
file: path="{{ item }}" ....
with_items: ls -1 {{ tools_dir }}/vmwaretools-lib-*/lib/insecure_private_key
become_user: {{ build_user }}
Finally to solve the issue, I just went on the plain OS command round using shell (again, this might not be a very good solution if the target env is not a Linux type OS) but for now I'm good.
- name: Set private key file access
shell: "chmod 0400 {{ tools_dir }}/vmtools-lib-*/lib/insecure_private_key"
become_user: "{{ build_user }}"
tags:
- koba
I am working on a project using Ansible which requires me to write some data to a file using one playbook and then read the data from the same file using another playbook.
The playbook will be something like this
test1.yml
---
- hosts: localhost
connection: local
gather_facts: no
tasks:
- name: Writing data to test file
local_action: shell echo "data:" {{ 100 |random(step=10) }} > test.txt
- include: test2.yml
and would need to read it using test2.yml
---
- hosts: localhost
connection: local
gather_facts: no
vars_files:
- test.txt
tasks:
- name: Writing data to test file
local_action: shell echo "{{ data }}" > result.txt
However,
The second playbook is not able to read the latest data being posted by the first playbook.
If I view the data written in test.txt and result.txt they both are different. Is there a way to achieve consistency between the results of playbook calls ????
Are those two playbooks called separately? If they are included inside a master playbook, then this would explain it. All includes in the master playbook are resolved before execution, so Ansible would already have read both playbooks and the vars_file before any of them gets executed. You should be able to solve this by dynamically including the vars file during play with the include_vars module.
If I was wrong with my assumption and you're not including the playbooks in a parent playbook: What exactly do you mean by "different"? Is it completely different data or is it a formatting issue? I'm puzzled how data in general could not be consistent between calls. There is no magic in writing to and reading from a file. That should theoretically work.