Ansible fact
Ansible is actually a variable that ansible automatically detects on the managed host. The fact contains host related information that can be used like regular variables, conditions, loops in play, or any other statement that depends on the values collected from the managed host.
Some of the facts collected for managed hosts may include:
- Host name
- Kernel version
- network interface
- IP address
- Operating system version
- Various environmental variables
- Number of CPU s
- Memory provided or available
- disk space available
With the help of facts, the state of the managed host can be easily retrieved and the operation to be performed can be determined according to the state. For example:
- You can restart the server by running the conditional task based on the fact that it contains the current kernel version of the managed host
- MySQL configuration files can be customized based on available memory through fact reporting
- You can set the IPv4 address used in the configuration file based on the value of the fact
Usually, each play will automatically run the setup module to collect facts before performing the first task.
One way to view the facts collected for the managed host is to run a collection of facts and use the debug module to display ansible_ Short playbook of facts variable value.
The following table shows some facts that may be collected from managed nodes and can be used in playbook:
Example of Ansible fact
fact | variable |
---|---|
Short host name | ansible_facts['hostname'] |
Fully qualified domain name | ansible_facts['fqdn'] |
IPv4 address | ansible_facts['default_ipv4']['address'] |
Name list of all network interfaces | ansible_facts['interfaces'] |
/Size of dev/vda1 disk partition | ansible_facts['devices']['vda']['partitions']['vda1']['size'] |
DNS server list | ansible_facts['dns']['nameservers'] |
Currently running kernel version | ansible_facts['kernel'] |
[root@master xm]# cat fact.yml --- - hosts: 192.168.72.137 tasks: - debug: var: ansible_facts [root@master xm]# ansible-playbook fact.yml PLAY [192.168.72.137] ****************************************************************************************************************** TASK [Gathering Facts] ***************************************************************************************************************** Enter passphrase for key '/root/.ssh/id_rsa': ok: [192.168.72.137] TASK [debug] *************************************************************************************************************************** ok: [192.168.72.137] => { "ansible_facts": { "all_ipv4_addresses": [ "192.168.72.137" ], "all_ipv6_addresses": [ "fe80::7014:45ea:bce2:63" ], "ansible_local": {}, ........
Replace facts with dynamic values
[root@master xm]# cat fact.yml --- - hosts: 192.168.72.137 tasks: - debug: msg: > The IPv4 address of {{ ansible_facts['default_ipv4']['address'] }} is {{ ansible_facts['fqdn'] }} [root@master xm]# ansible-playbook fact.yml PLAY [192.168.72.137] ****************************************************************************************************************** TASK [Gathering Facts] ***************************************************************************************************************** Enter passphrase for key '/root/.ssh/id_rsa': ok: [192.168.72.137] TASK [debug] *************************************************************************************************************************** ok: [192.168.72.137] => { "msg": "The IPv4 address of 192.168.72.137 is localhost.localdomain\n" } PLAY RECAP ***************************************************************************************************************************** 192.168.72.137 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 //You can use the setup module to display all the facts [root@master xm]# ansible 192.168.72.137 -m setup Enter passphrase for key '/root/.ssh/id_rsa': 192.168.72.137 | SUCCESS => { "ansible_facts": { "ansible_all_ipv4_addresses": [ "192.168.72.137" ], "ansible_all_ipv6_addresses": [ "fe80::7014:45ea:bce2:63" ], "ansible_apparmor": { "status": "disabled" }, "ansible_architecture": "x86_64", "ansible_bios_date": "07/22/2020", "ansible_bios_version": "6.00", ........
Turn off fact collection
Closing facts can improve the running speed (gather_facts: no), or you can use the setup module to collect facts manually
[root@master xm]# cat fact.yml --- - hosts: 192.168.72.137 gather_facts: no tasks: - setup: - debug: var: ansible_facts [root@master xm]# ansible-playbook fact.yml PLAY [192.168.72.137] ****************************************************************************************************************** TASK [setup] *************************************************************************************************************************** Enter passphrase for key '/root/.ssh/id_rsa': ok: [192.168.72.137] TASK [debug] *************************************************************************************************************************** ok: [192.168.72.137] => { "ansible_facts": { "all_ipv4_addresses": [ "192.168.72.137" ], "all_ipv6_addresses": [ "fe80::7014:45ea:bce2:63" ], .......
Custom facts
In addition to using the facts captured by the system, we can also customize the facts and store them locally on each managed host. These facts are consolidated into a standard list of facts collected when the setup module runs on the managed host. They enable the managed host to provide arbitrary variables to Ansible to adjust the behavior of play.
Custom facts can be defined in static files. The format can be INI files or JSON. They can also be executable scripts that generate JSON output, just like dynamic manifest scripts.
With custom facts, we can define specific values for the managed host for play to populate the configuration file or run tasks conditionally. Dynamic custom facts allow you to programmatically determine the values of these facts at play runtime, and even determine which facts to provide.
By default, the setup module is from / etc / ansible / facts.com of each managed host Load custom facts in the files and scripts in the D directory. The name of each file or script must be in The end of fact can only be used. The dynamic custom fact script must output the fact in JSON format and must be an executable file.
The following is a static custom fact file written in INI format. The custom fact file in INI format contains the top-level value defined by a part, followed by the key value pair for the fact to be defined:
[root@master xm]# cat xxx.fact [bbc] package = httpd service = httpd state = started enabled = yes [root@master xm]# cat fact.yml --- - hosts: 192.168.72.137 vars: dir: /etc/ansible/facts.d file: xxx.fact tasks: - name: create dir file: state: directory recurse: yes path: "{{ dir }}" - name: create file copy: src: "{{ file }}" dest: "{{ dir }}" [root@master xm]# ansible-playbook fact.yml PLAY [192.168.72.137] ****************************************************************************************************************** TASK [Gathering Facts] ***************************************************************************************************************** ok: [192.168.72.137] TASK [create dir] ********************************************************************************************************************** ok: [192.168.72.137] TASK [create file] ********************************************************************************************************************* changed: [192.168.72.137] PLAY RECAP ***************************************************************************************************************************** 192.168.72.137 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Magic variable
Some variables are not facts or configured through the setup module, but are also automatically set by Ansible. These magic variables can also be used to obtain information related to a specific managed host.
There are four most commonly used:
Magic variable | explain |
---|---|
hostvars | A variable that contains a managed host and can be used to get the value of a variable of another managed host. If facts have not been collected for the managed host, it will not contain the facts for that host |
group_names | Lists all groups to which the currently managed host belongs |
groups | Lists all groups and hosts in the list |
inventory_hostname | Contains the host name of the currently managed host configured in the manifest. For various reasons, it may be different from the host name reported in the fact |
There are many other "magic variables". For more information, see the following link: https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable
loop
By using loops, the task of using the first mock exam is not necessary. For example, instead of writing five tasks to ensure that there are five users, they just need to write one task to iterate over a list of five users to ensure that they all exist.
Ansible supports iterative tasks on a set of projects using the loop keyword. Loops can be configured to repeat tasks using items in the list, the contents of files in the list, a generated sequence of numbers, or a more complex structure.
Simple cycle
[root@master xm]# cat sb.yml --- - hosts: 192.168.72.137 tasks: - name: user: name: "{{ item }}" state: present loop: - xxx - zzz [root@master xm]# ansible-playbook sb.yml PLAY [192.168.72.137] ****************************************************************************************************************** TASK [Gathering Facts] ***************************************************************************************************************** Enter passphrase for key '/root/.ssh/id_rsa': ok: [192.168.72.137] TASK [user] **************************************************************************************************************************** changed: [192.168.72.137] => (item=xxx) changed: [192.168.72.137] => (item=zzz) PLAY RECAP ***************************************************************************************************************************** 192.168.72.137 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Circular hash or dictionary list
The loop list does not need to be a simple list of values. In the following example, each item in the list is actually a hash or dictionary. Each hash or dictionary in the example has two keys, name and groups. The value of each key in the current item loop variable can be passed through item Name and item Groups variable.
[root@master xm]# cat sb.yml --- - hosts: 192.168.72.137 tasks: - name: user: name: "{{ item.name }}" groups: "{{ item.groups }}" state: present loop: - name: xm groups: xxx - name: slf groups: zzz [root@master xm]# ansible-playbook sb.yml PLAY [192.168.72.137] ****************************************************************************************************************** TASK [Gathering Facts] ***************************************************************************************************************** Enter passphrase for key '/root/.ssh/id_rsa': ok: [192.168.72.137] TASK [user] **************************************************************************************************************************** changed: [192.168.72.137] => (item={'name': 'xm', 'groups': 'xxx'}) changed: [192.168.72.137] => (item={'name': 'slf', 'groups': 'zzz'}) PLAY RECAP ***************************************************************************************************************************** 192.168.72.137 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Use the Register variable with Loop
[root@master xm]# cat sb.yml --- - hosts: 192.168.72.137 tasks: - name: user: name: "{{ item.name }}" groups: "{{ item.groups }}" state: present loop: - name: xm groups: xxx - name: slf groups: zzz register: result - name: debug: var: result [root@master xm]# ansible-playbook sb.yml PLAY [192.168.72.137] ****************************************************************************************************************** TASK [Gathering Facts] ***************************************************************************************************************** Enter passphrase for key '/root/.ssh/id_rsa': ok: [192.168.72.137] TASK [user] **************************************************************************************************************************** ok: [192.168.72.137] => (item={'name': 'xm', 'groups': 'xxx'}) ok: [192.168.72.137] => (item={'name': 'slf', 'groups': 'zzz'}) TASK [debug] *************************************************************************************************************************** ok: [192.168.72.137] => { "result": { "changed": false, "msg": "All items completed", "results": [ .......
Conditional judgment
Ansible can use conditions to perform tasks or play when specific conditions are met. For example, a condition can be used to determine the available memory on the managed host before ansible installs or configures the service.
We can use conditions to distinguish different managed hosts and assign functional roles according to their conditions. Playbook variables, registered variables, and Ansible facts can all be tested by conditions. You can use operators that compare strings, numeric data, and Boolean values.
The following scenarios illustrate the use of conditions in Ansible:
- You can define a hard limit (such as min_memory) in the variable and compare it with the available memory on the managed host.
- Ansible can capture and evaluate the output of a command to determine whether a task has been completed before performing further operations. For example, if a program fails, it will pass through batch processing.
- The Ansible fact can be used to determine the managed host network configuration and determine the template file to send (such as network binding or relay).
- You can evaluate the number of CPU s to determine how to properly tune a Web server.
- Compare the registered variables with predefined variables to determine whether the service has changed. For example, test MD5 of the service configuration file to verify and see if the service has changed.
Conditional task syntax
The when statement is used to run a task conditionally. It takes the condition to be tested as the value. If the conditions are met, run the task. If the conditions are not met, the task is skipped.
//If the value is true, it will run. If the value is false, it will be skipped [root@master xm]# cat sb.yml --- - hosts: 192.168.72.137 vars: slf: true tasks: - name: user: name: "{{ item.name }}" groups: "{{ item.groups }}" state: present loop: - name: xm groups: xxx - name: slf groups: zzz when: slf
Example conditions
operation | Example |
---|---|
Equal to (value is string) | ansible_machine == "x86_64" |
Equal to (value is numeric) | max_memory == 512 |
less than | min_memory < 128 |
greater than | min_memory > 256 |
Less than or equal to | min_memory <= 256 |
Greater than or equal to | min_memory >= 512 |
Not equal to | min_memory != 512 |
Variable exists | min_memory is defined |
Variable does not exist | min_memory is not defined |
The boolean variable is true. 1. True or yes evaluates to true | memory_available |
The boolean variable is False. 0, False, or no evaluate to False | not memory_available |
The value of the first variable exists as the value in the list of the second variable | ansible_distribution in supported_distros |
Test multiple conditions
A when statement can be used to evaluate multiple conditions. Use the and and or keywords to combine conditions and use parentheses to group conditions.
If the conditional statement is satisfied when any of the conditions is true, the or statement should be used. For example, if the computer is running Red Hat Enterprise linux or Fedora, the following conditions are met:
when: ansible_distribution == "Redhat" or ansible_distribution == "Fedora"
When using the and operation, both conditions must be true to satisfy the entire conditional statement. For example, if the remote host is Red Hat Enterprise Linux 7 5 host, and the installed kernel is the specified version, the following conditions will be met:
when: ansible_distribution_version == "7.5" and ansible_kernel == "3.10.0-327.el7.x86_64"
The when keyword also supports the use of lists to describe condition lists. When you provide a list to the when keyword, all conditions are combined using the and operation. The following example demonstrates another way to combine multiple conditional statements using the and operator:
when: - ansible_distribution_version == "7.5" - ansible_kernel == "3.10.0-327.el7.x86_64"
This format improves readability, which is a key goal of writing Ansible Playbook well.
By grouping conditions with parentheses, you can express more complex conditional statements. For example, if the computer is running Red Hat Enterprise Linux 7 or Fedora 28, the following conditional statements are met. This example uses greater than characters so that the long condition can be divided into multiple lines in the playbook for easy reading.
when: > ( ansible_distribution == "Redhat" and ansible_distribution_major_version == "7" ) or ( ansible_distribution == "Fedora" and ansible_distribution_major_version == "28" )
Combining loops and conditional tasks
--- - hosts: 192.168.72.137 tasks: - name: yum: name: mariadb-server state: latest loop: "{{ ansible_mounts }}" when: item.mount == "/" and item.size_available > 10000000 [root@master xm]# ansible-playbook xx.yml PLAY [192.168.72.137] ****************************************************************************************************************** TASK [Gathering Facts] ***************************************************************************************************************** ok: [192.168.72.137] TASK [yum] ***************************************************************************************************************************** ok: [192.168.72.137] => (item={'mount': '/', 'device': '/dev/mapper/rhel-root', 'fstype': 'xfs', 'options': 'rw,seclabel,relatime,attr2,inode64,noquota', 'size_total': 50390917120, 'size_available': 47464321024, 'block_size': 4096, 'block_total': 12302470, 'block_available': 11587969, 'block_used': 714501, 'inode_total': 24616960, 'inode_available': 24552800, 'inode_used': 64160, 'uuid': '22c13e5c-701e-4552-94a1-520b0b285506'}) skipping: [192.168.72.137] => (item={'mount': '/boot', 'device': '/dev/nvme0n1p1', 'fstype': 'xfs', 'options': 'rw,seclabel,relatime,attr2,inode64,noquota', 'size_total': 1063256064, 'size_available': 876052480, 'block_size': 4096, 'block_total': 259584, 'block_available': 213880, 'block_used': 45704, 'inode_total': 524288, 'inode_available': 523987, 'inode_used': 301, 'uuid': 'cd44aec7-b06c-4df8-9fa6-187c3222f148'}) PLAY RECAP ***************************************************************************************************************************** 192.168.72.137 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 //When vsftpd is running, restart httpd --- - hosts: 192.168.72.137 tasks: - name: shell: systemctl status vsftpd ignore_errors: yes register: result - name: service: name: httpd state: restarted when: result.rc == 0 [root@master xm]# ansible-playbook xx.yml PLAY [192.168.72.137] ****************************************************************************************************************** TASK [Gathering Facts] ***************************************************************************************************************** Enter passphrase for key '/root/.ssh/id_rsa': ok: [192.168.72.137] TASK [shell] *************************************************************************************************************************** fatal: [192.168.72.137]: FAILED! => {"changed": true, "cmd": "systemctl status vsftpd", "delta": "0:00:00.009824", "end": "2021-07-25 06:32:27.841754", "msg": "non-zero return code", "rc": 4, "start": "2021-07-25 06:32:27.831930", "stderr": "Unit vsftpd.service could not be found.", "stderr_lines": ["Unit vsftpd.service could not be found."], "stdout": "", "stdout_lines": []} ...ignoring TASK [service] ************************************************************************************************************************* skipping: [192.168.72.137] PLAY RECAP ***************************************************************************************************************************** 192.168.72.137 : ok=2 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=1