I'm trying to figure out how I can extract the value of the "Address" of a nslookup command result in an Ansible task. I will always get back 1 ip address in the result:
nslookup fs-d12345.efs.ap-southeast-2.amazonaws.com 10.75.0.2
Server: 10.75.0.2
Address: 10.75.0.2#53
Non-authoritative answer:
Name: fs-d12345.efs.ap-southeast-2.amazonaws.com
Address: 10.75.21.67
I need the value 10.75.21.67 to be stored as a var that I can use later in the playbook.
My task would look something like:
- shell: "nslookup fs-d12345.efs.ap-southeast-2.amazonaws.com 10.75.0.2"
register: results
How do I extract the value of the Address?
Thanks!
Before reaching for the command or shell module, always have a look around for alternatives as for common tasks, the Ansible community have often done the heavy lifting for you.
If you can accommodate installing an additional Python package to support, there is already an nslookup (well dig) utility built into Ansible:
- set_fact:
target_ip: "{{ lookup('dig', 'fs-d12345.efs.ap-southeast-2.amazonaws.com', '#10.75.0.2') }}"
The pre-requisite to getting this to work, is you need the dnspython library installed on the machine where this task will be run, e.g.
apt-get install python-dnspython
or
yum install python-dns # (I think ...)
If you only want to have to do this on your control machine, but you want to be able to access the looked up data on a remote machine, you could do something like this:
- hosts: localhost
connection: local
tasks:
- set_fact:
target_ip: "{{ lookup('dig', 'fs-d12345.efs.ap-southeast-2.amazonaws.com', '#10.75.0.2') }}"
- hosts: remote_machine
tasks:
- debug:
var: hostvars['localhost'].target_ip
Related
I've written a small playbook to run the sudo /usr/sbin/dmidecode -t1 | grep -i vmware | grep -i product command and write the output in a result file by usign the following code as a .yml:
# Check if server is vmware
---
- name: Check if server is vmware
hosts: all
become: yes
#ignore_errors: yes
gather_facts: False
serial: 50
#become_flags: -i
tasks:
- name: Run uptime command
#become: yes
shell: "sudo /usr/sbin/dmidecode -t1 | grep -i vmware | grep -i product"
register: upcmd
- debug:
msg: "{{ upcmd.stdout }}"
- name: write to file
lineinfile:
path: /home/myuser/ansible/mine/vmware.out
create: yes
line: "{{ inventory_hostname }};{{ upcmd.stdout }}"
delegate_to: localhost
#when: upcmd.stdout != ""
When running the playbook against a list of hosts I get different weird results so even if the debug shows the correct output, when I check the /home/myuser/ansible/mine/vmware.out file I see only part of them being present. Even weirder is that if I run the playbook again, I will correctly populate the whole list but only if I run this twice. I have repeated this several times with some minor tweaks but not getting the expected result. Doing -v or -vv shows nothing unusual.
You are writing to the same file in parallel on localhost. I suspect you're hitting a write concurrency issue. Try the following and see if it fixes your problem:
- name: write to file
lineinfile:
path: /home/myuser/ansible/mine/vmware.out
create: yes
line: "{{ host }};{{ hostvars[host].upcmd.stdout }}"
delegate_to: localhost
run_once: true
loop: "{{ ansible_play_hosts }}"
loop_control:
loop_var: host
From your described case I understand that you like to find out "How to check if a server is virtual?"
The information will already be collected by the setup module.
---
- hosts: linux_host
become: false
gather_facts: true
tasks:
- name: Show Gathered Facts
debug:
msg: "{{ ansible_facts }}"
For an under MS Hyper-V virtualized Linux system, the output could contain
...
bios_version: Hyper-V UEFI Release v1.0
...
system_vendor: Microsoft Corporation
uptime_seconds: 2908494
...
userspace_architecture: x86_64
userspace_bits: '64'
virtualization_role: guest
virtualization_type: VirtualPC
and having already the uptime in seconds included
uptime
... up 33 days ...
For just only a virtual check one could gather_subset resulting into a full output of
gather_subset:
- '!all'
- '!min'
- virtual
module_setup: true
virtualization_role: guest
virtualization_type: VirtualPC
By Caching facts
... you have access to variables and information about all hosts even when you are only managing a small number of servers
on your Ansible Control Node. In ansible.cfg you can configure where and how they are stored and for how long.
fact_caching = yaml
fact_caching_connection = /tmp/ansible/facts_cache
fact_caching_timeout = 86400 # seconds
This would be a minimal and simple solution without re-implementing functionality which is already there.
Further Documentation and Q&A
Ansible facts
What is the exact list of Ansible setup min?
I'm trying to create a playbook which basically consists 2 hosts init; (don't ask why)
---
- hosts: all
tasks:
- name: get the hostname of machine and save it as a variable
shell: hostname
register: host_name
when: ansible_host == "x.x.x.x" *(will be filled by my application)*
- hosts: "{{ host_name.stdout }}"
tasks:
- name: use the variable as hostname
shell: whoami
I don't have any hostname information in my application so I need to trigger my playbook with an IP address, then i should get the hostname of that machine and save it to a variable to use in my other tasks to avoid "when" command for each task.
The problem is that I'm able to use "host_name" variable in all other fields except "hosts", it gives me an Error like this when i try to run;
ERROR! The field 'hosts' has an invalid value, which includes an undefined variable. The error was: 'host_name' is undefined
Screenshot of the error
By default, Ansible itself gathers some information about a host. This happens at the beginning of a playbook's execution right after PLAY in TASK [Gathering Facts].
This automatic gathering of information about a system can be turned off via gather_facts: no, by default this is active.
This collected information is called Ansible Facts. An example of the collected facts is shown in the Ansible Docs, for your host you can print out all Ansible Facts:
either in the playbook as a task:
- name: Print all available facts
debug:
var: ansible_facts
or via CLI as an adhoc command:
ansible <hostname> -m setup
The Ansible Facts contain values like: ansible_hostname, ansible_fqdn, ansible_domain or even ansible_all_ipv4_addresses. This is the simplest way to act with the hostname of the client.
If you want to output the hostname and IP addresses that Ansible has collected, you can do it with the following tasks for example:
- name: Print hostname
debug:
var: ansible_hostname
- name: Print IP addresses
debug:
var: ansible_all_ipv4_addresses
If you start your playbook for all hosts, you can check the IP address and also stop it directly for the "wrong" clients.
---
- hosts: all
tasks:
- name: terminate execution for wrong hosts
assert:
that: '"x.x.x.x" is in ansible_all_ipv4_addresses'
fail_msg: Terminating because IP did not match
success_msg: "Host matched. Hostname: {{ ansible_hostname }}"
# your task for desired host
I have got list of the mac address in the text file mac.txt such as:
[
"08f1.ea6d.033c",
"08f1.ea6d.033d",
"08f1.ea6d.033e",
"08f1.ea6d.033f",
"b883.0381.4b.20",
"b883.0381.4b21",
"b883.0384.d51c",
"b883.0384.d51d"
]
Now I want to check one by one above mac addresses in switch, to verify the connectivity of the server & switch and all these mac addresses may not be exist on the switch. Let's say only two exist and that mac needs to be stored in variable.
Note: these mac addresses may vary.
And here is the switch playbook which I was writing:
- name: Run the show lldp neighbors command & find out the switch port
ios_command:
commands: show mac address-table | in {{ macdress }}
with_item:
- macaddress
What is the correct playbook to achieve the requirement?
Since your text file is a valid json list, you simply have to read its content using the file lookup and load it inside a variable using the from_json filter. You can then use the variable (in a loop or anything else).
---
- name: Load values from json file demo
hosts: localhost
gather_facts: false
vars:
macaddresses: "{{ lookup('file', 'mac.txt') | from_json }}"
tasks:
- name: Show imported macs list
debug:
var: macaddresses
- name: Loop over imported macs
debug:
msg: "I'm looping over mac {{ item }}"
loop: "{{ macaddresses }}"
Note: loop is the recent syntax for looping and is the equivalent of with_list. In this situation you can perfectly replace it with with_list or with_item which are not deprecated and will do the same job. See the ansible loop documentation for more info.
I am trying to use ansible to modify the hostnames of a dozen newly created Virtual Machines, and I am failing to understand how to loop correctly.
Here is the playbook I've written:
---
- hosts: k8s_kvm_vms
tasks:
- name: "update hostnames"
hostname:
name: "{{ item }}"
with_items:
- centos01
- centos02
...
The result is that it updates each host with each hostname. So if I have 12 machines, each hostname would be "centos12" at the end of the playbook.
I expect this behavior to essentially produce the same result as:
num=0
for ip in ${list_of_ips[*]}; do
ssh $ip hostnamectl set-hostname centos${num}
num=$((num+1))
done
If I were to write it myself in bash
The answer on this page leads me to believe I would have to include all of the IP addresses in my playbook. In my opinion, the advantage of scripting would be that I could have the same hostnames even if their IP changes (I just have to copy the ip addresses into /etc/ansible/hosts) which I could reuse with other playbooks. I have read the ansible page on the hostname module, but their example leads me to believe I would, instead of using a loop, have to define a task for each individual IP address. If this is the case, why use ansible over a bash script?
ansible hostname module
You can create a new variable for each of the servers in the inventory like
[k8s_kvm_vms]
server1 new_hostname=centos1
server2 new_hostname=centos2
Playbook:
---
- hosts: k8s_kvm_vms
tasks:
- name: "update hostnames"
hostname:
name: "{{ new_hostname }}"
I think you need to sudo to change the hostname thus you should add "become: yes"
---
- hosts: k8s_kvm_vms
become: yes
tasks:
- name: "update hostnames"
hostname:
name: "{{ new_hostname }}"
In ansible, I do some code on a remote machine, and then I want to use the result from the vars_prompt again on a different host. I've searched around, and the docs make it sound like i should use {{ hostvars.local_server_name_in_vagrant_group.local_username }}, using my example below to set the context. However, it says that the index from the dictionary doesn't exist when referencing hostvars. Instead, as shown below, I simply do a vars_prompt twice. Gross! Any tips?
BTW, there's also discussion on whether or not using vars_prompt is a great idea. I have confirmed that for my usage, indeed, I do want to use vars_prompt. Thanks!
- hosts: vagrant
vars_prompt:
local_username: "enter your desired local username"
... remote task activity using local_username...
- hosts: localhost
connection: local
vars_prompt:
local_username: "enter your desired local username, again (please)
... host task activity, also using local_username ...
As I said in the comment to the question :
You can use set_facts to register your variable as a fact of the current host and access it from a different one. I do not think standard variables are stored after the role/tasks.
Here is an example :
- name: First
connection: local
hosts: host1
tasks:
- name: register real root user to localhost facts
set_fact: fact_for_host1="1"
- name: Second
connection: local
hosts: host2
tasks:
- debug: msg="{{ hostvars['host1']['fact_for_host1'] }}"
Note that the connection: local is present only for local tests purpose.