Ansible Custom Facts

When an Ansible playbook is executed, the first task that gets executed is the setup task. setup task gathers information about the remote system like  OS information, IP information, memory etc. These information are called facts.

We can create custom facts which can be available thru Ansible. In any Ansible controlled host, we can place one or more fact files under /etc/ansible/facts.d. These are files/scripts that returns JSON formatted data.

The following steps shows how we can create a custom facts file and deploy it to Ansible controlled hosts via an Ansible playbook.

Let’s create a sample fact file which returns the host serial number using dmidecode. This should show the same value as ansible_product_serial.

On our Ansible server, create a file /etc/ansible/facts.d/getSerial.fact with the following entries

#!/bin/bash

SERIAL_NUM=`dmidecode | grep "Serial Number" | head -n1 | sed -e 's/\tSerial Number: //g'`
cat <<EOF
{
 "serial_number" : "$SERIAL_NUM"
}
EOF

Let’s create a new playbook which will deploy the facts file and make it executable.

publish_custom_facts.yml

tasks:
 - name: "Create custom fact directory"
 file:
 path: "/etc/ansible/facts.d"
 state: "directory"

- name: "Insert custom fact file"
 copy:
 src: /etc/ansible/facts.d/getSerial.fact 
 dest: /etc/ansible/facts.d/getSerial.fact
 mode: 0755

- name: reload ansible_local
 setup: filter=ansible_local

Let’s run the playbook

[root@ansible GitTraining]# ansible-playbook publish_custom_facts.yml

PLAY [web] *********************************************************************

TASK [setup] *******************************************************************
ok: [192.168.0.53]

TASK [Create custom fact directory] ********************************************
changed: [192.168.0.53]

TASK [Insert custom fact file] *************************************************
changed: [192.168.0.53]

PLAY RECAP *********************************************************************
192.168.0.53 : ok=3 changed=2 unreachable=0 failed=0

[root@ansible GitTraining]#

On our target system, we can check that the new facts file was deployed.

[root@website ~]# ls -ltrh /etc/ansible/facts.d/getSerial.fact
-rwxr-xr-x. 1 root root 158 Mar 28 15:51 /etc/ansible/facts.d/getSerial.fact
[root@website ~]#

The fact can be found under the ansible_local namespace

[root@ansible GitTraining]# ansible -m setup web


192.168.0.53 | SUCCESS => {
 "ansible_facts": {
 "ansible_all_ipv4_addresses": [
 "10.10.1.120"
 ], 
 "ansible_all_ipv6_addresses": [
 "fe80::f816:3eff:fedf:fd6e"
 ], 
 "ansible_architecture": "x86_64", 
 "ansible_bios_date": "04/01/2014", 
 "ansible_bios_version": "seabios-1.7.5-11.el7", 
 "ansible_cmdline": {
 "BOOT_IMAGE": "/boot/vmlinuz-4.0.4-301.fc22.x86_64", 
 "LANG": "en_US.UTF-8", 
 "console": "tty1", 
 "initrd": "/boot/initramfs-4.0.4-301.fc22.x86_64.img", 
 "no_timer_check": true, 
 "quiet": true, 
 "rhgb": true, 
 "ro": true, 
 "root": "UUID=01bc7316-b1f4-45c9-a23a-00c5a2336ef2"
 }, 
 "ansible_date_time": {
 "date": "2017-03-28", 
 "day": "28", 
 "epoch": "1490712946", 
 "hour": "14", 
 "iso8601": "2017-03-28T14:55:46Z", 
 "iso8601_basic": "20170328T145546592104", 
 "iso8601_basic_short": "20170328T145546", 
 "iso8601_micro": "2017-03-28T14:55:46.592468Z", 
 "minute": "55", 
 "month": "03", 
 "second": "46", 
 "time": "14:55:46", 
 "tz": "UTC", 
 "tz_offset": "+0000", 
 "weekday": "Tuesday", 
 "weekday_number": "2", 
 "weeknumber": "13", 
 "year": "2017"
 }, 
 "ansible_default_ipv4": {
 "address": "10.10.1.120", 
 "alias": "eth0", 
 "broadcast": "10.10.1.255", 
 "gateway": "10.10.1.1", 
 "interface": "eth0", 
 "macaddress": "fa:16:3e:df:fd:6e", 
 "mtu": 1400, 
 "netmask": "255.255.255.0", 
 "network": "10.10.1.0", 
 "type": "ether"
 }, 
 "ansible_default_ipv6": {}, 
 "ansible_devices": {
 "vda": {
 "holders": [], 
 "host": "", 
 "model": null, 
 "partitions": {
 "vda1": {
 "sectors": "10483712", 
 "sectorsize": 512, 
 "size": "5.00 GB", 
 "start": "2048"
 }
 }, 
 "removable": "0", 
 "rotational": "1", 
 "sas_address": null, 
 "sas_device_handle": null, 
 "scheduler_mode": "", 
 "sectors": "10485760", 
 "sectorsize": "512", 
 "size": "5.00 GB", 
 "support_discard": "0", 
 "vendor": "0x1af4"
 }
 }, 
 "ansible_distribution": "Fedora", 
 "ansible_distribution_major_version": "22", 
 "ansible_distribution_release": "Twenty Two", 
 "ansible_distribution_version": "22", 
 "ansible_dns": {
 "nameservers": [
 "10.10.1.100", 
 "192.168.0.1", 
 "8.8.8.8"
 ], 
 "search": [
 "openstacklocal"
 ]
 }, 
 "ansible_domain": "", 
 "ansible_env": {
 "HOME": "/root", 
 "LANG": "en_US.UTF-8", 
 "LC_ALL": "en_US.UTF-8", 
 "LC_MESSAGES": "en_US.UTF-8", 
 "LESSOPEN": "||/usr/bin/lesspipe.sh %s", 
 "LOGNAME": "root", 
 "MAIL": "/var/mail/root", 
 "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin", 
 "PWD": "/root", 
 "SELINUX_LEVEL_REQUESTED": "", 
 "SELINUX_ROLE_REQUESTED": "", 
 "SELINUX_USE_CURRENT_RANGE": "", 
 "SHELL": "/bin/bash", 
 "SHLVL": "2", 
 "SSH_CLIENT": "192.168.0.56 53598 22", 
 "SSH_CONNECTION": "192.168.0.56 53598 10.10.1.120 22", 
 "SSH_TTY": "/dev/pts/1", 
 "TERM": "xterm-256color", 
 "USER": "root", 
 "XDG_RUNTIME_DIR": "/run/user/0", 
 "XDG_SESSION_ID": "15", 
 "XMODIFIERS": "@im=ibus", 
 "_": "/usr/bin/python"
 }, 
 "ansible_eth0": {
 "active": true, 
 "device": "eth0", 
 "ipv4": {
 "address": "10.10.1.120", 
 "broadcast": "10.10.1.255", 
 "netmask": "255.255.255.0", 
 "network": "10.10.1.0"
 }, 
 "ipv6": [
 {
 "address": "fe80::f816:3eff:fedf:fd6e", 
 "prefix": "64", 
 "scope": "link"
 }
 ], 
 "macaddress": "fa:16:3e:df:fd:6e", 
 "module": "virtio_net", 
 "mtu": 1400, 
 "pciid": "virtio0", 
 "promisc": false, 
 "type": "ether"
 }, 
 "ansible_fips": false, 
 "ansible_form_factor": "Other", 
 "ansible_fqdn": "website", 
 "ansible_gather_subset": [
 "hardware", 
 "network", 
 "virtual"
 ], 
 "ansible_hostname": "website", 
 "ansible_interfaces": [
 "lo", 
 "eth0"
 ], 
 "ansible_kernel": "4.0.4-301.fc22.x86_64", 
 "ansible_lo": {
 "active": true, 
 "device": "lo", 
 "ipv4": {
 "address": "127.0.0.1", 
 "broadcast": "host", 
 "netmask": "255.0.0.0", 
 "network": "127.0.0.0"
 }, 
 "ipv6": [
 {
 "address": "::1", 
 "prefix": "128", 
 "scope": "host"
 }
 ], 
 "mtu": 65536, 
 "promisc": false, 
 "type": "loopback"
 }, 
 "ansible_local": {
 "getSerial": {
 "serial_number": "cf918881-ad39-4e2d-8cfb-fe21b39b8efb"
 }
 }, 
 "ansible_machine": "x86_64", 
 "ansible_machine_id": "1cb6c3aa84d24db1b0bbb0bcaefc0ebd", 
 "ansible_memfree_mb": 16, 
 "ansible_memory_mb": {
 "nocache": {
 "free": 164, 
 "used": 325
 }, 
 "real": {
 "free": 16, 
 "total": 489, 
 "used": 473
 }, 
 "swap": {
 "cached": 0, 
 "free": 0, 
 "total": 0, 
 "used": 0
 }
 }, 
 "ansible_memtotal_mb": 489, 
 "ansible_mounts": [
 {
 "device": "/dev/vda1", 
 "fstype": "ext4", 
 "mount": "/", 
 "options": "rw,seclabel,relatime,data=ordered", 
 "size_available": 3770216448, 
 "size_total": 5218238464, 
 "uuid": "01bc7316-b1f4-45c9-a23a-00c5a2336ef2"
 }
 ], 
 "ansible_nodename": "website", 
 "ansible_os_family": "RedHat", 
 "ansible_pkg_mgr": "dnf", 
 "ansible_processor": [
 "GenuineIntel", 
 "Westmere E56xx/L56xx/X56xx (Nehalem-C)"
 ], 
 "ansible_processor_cores": 1, 
 "ansible_processor_count": 1, 
 "ansible_processor_threads_per_core": 1, 
 "ansible_processor_vcpus": 1, 
 "ansible_product_name": "OpenStack Nova", 
 "ansible_product_serial": "cf918881-ad39-4e2d-8cfb-fe21b39b8efb", 
 "ansible_product_uuid": "86AB1D13-732D-844A-8987-575B8B22285A", 
 "ansible_product_version": "13.1.0-1.el7", 
 "ansible_python": {
 "executable": "/usr/bin/python", 
 "has_sslcontext": true, 
 "type": "CPython", 
 "version": {
 "major": 2, 
 "micro": 9, 
 "minor": 7, 
 "releaselevel": "final", 
 "serial": 0
 }, 
 "version_info": [
 2, 
 7, 
 9, 
 "final", 
 0
 ]
 }, 
 "ansible_python_version": "2.7.9", 
 "ansible_selinux": {
 "config_mode": "enforcing", 
 "mode": "enforcing", 
 "policyvers": 29, 
 "status": "enabled", 
 "type": "targeted"
 }, 
 "ansible_service_mgr": "systemd", 
 "ansible_ssh_host_key_ecdsa_public": "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBBRc7z7YqkdeEOUh/KZKsqgi35cYtmVfb3LdWyGenZKFRDNNLcjPc30vxiofER2zMjZ4HbmFb8D6RqqRk3hBFLA=", 
 "ansible_ssh_host_key_ed25519_public": "AAAAC3NzaC1lZDI1NTE5AAAAIIa/7/Fw+zKnM3caCzwS54flZcOa0dTVMfuiFf/mg6AF", 
 "ansible_ssh_host_key_rsa_public": "AAAAB3NzaC1yc2EAAAADAQABAAABAQC52hRFlHbugr2Z3iyQAg3cl33SirS/n3gHg+7lJkHHqC9mN2kIVDLEyAwu2UHjXKLOWoCS7+nt1wbZLG/+nYe/OVXjJOvW/9MkjxQEsqFOgzKdE/0RVDx1Lnk3TTS+oublE6V49IF4h2+YTz3DqfXF8geg5+d5uKmcBDwYisDTeumgsRgHOFM3O6xWaJdyN887jsZnfv2DHoU144h/pFeSDLJm+evuxNKbLxp9zw+ypty75Div7jn4Shs25hMmwJhRodYCy2azwJ87ZuH5xRVYJznACmlSenddbKc7BetdeKYCvyrq1yfDPTjngOy1CNfsFiumxyVdUfRYC59kLPOt", 
 "ansible_swapfree_mb": 0, 
 "ansible_swaptotal_mb": 0, 
 "ansible_system": "Linux", 
 "ansible_system_capabilities": [
 "cap_chown", 
 "cap_dac_override", 
 "cap_dac_read_search", 
 "cap_fowner", 
 "cap_fsetid", 
 "cap_kill", 
 "cap_setgid", 
 "cap_setuid", 
 "cap_setpcap", 
 "cap_linux_immutable", 
 "cap_net_bind_service", 
 "cap_net_broadcast", 
 "cap_net_admin", 
 "cap_net_raw", 
 "cap_ipc_lock", 
 "cap_ipc_owner", 
 "cap_sys_module", 
 "cap_sys_rawio", 
 "cap_sys_chroot", 
 "cap_sys_ptrace", 
 "cap_sys_pacct", 
 "cap_sys_admin", 
 "cap_sys_boot", 
 "cap_sys_nice", 
 "cap_sys_resource", 
 "cap_sys_time", 
 "cap_sys_tty_config", 
 "cap_mknod", 
 "cap_lease", 
 "cap_audit_write", 
 "cap_audit_control", 
 "cap_setfcap", 
 "cap_mac_override", 
 "cap_mac_admin", 
 "cap_syslog", 
 "cap_wake_alarm", 
 "cap_block_suspend", 
 "37+ep"
 ], 
 "ansible_system_capabilities_enforced": "True", 
 "ansible_system_vendor": "Fedora Project", 
 "ansible_uptime_seconds": 284442, 
 "ansible_user_dir": "/root", 
 "ansible_user_gecos": "root", 
 "ansible_user_gid": 0, 
 "ansible_user_id": "root", 
 "ansible_user_shell": "/bin/bash", 
 "ansible_user_uid": 0, 
 "ansible_userspace_architecture": "x86_64", 
 "ansible_userspace_bits": "64", 
 "ansible_virtualization_role": "NA", 
 "ansible_virtualization_type": "NA", 
 "module_setup": true
 }, 
 "changed": false
}
[root@ansible GitTraining]#

We can now use/fetch the fact by

[root@ansible GitTraining]# ansible -m setup web | grep -A 4 ansible_local
 "getSerial": {
 "serial_number": "cf918881-ad39-4e2d-8cfb-fe21b39b8efb"
 }
 }, 
[root@ansible GitTraining]#

Or by using the following in our playbook

{{ ansible_local.returnLastOctet }}

 

 

 

Leave a Reply