Ansible Palybook 变量

一、变量(一)

在ansible中使用变量,能让我们的工作变得更加灵活,在ansible中,变量的使用方式有很多种,我们慢慢聊!

先说说怎样定义变量,变量名应该由字母、数字、下划线组成,变量名需要以字母开头,ansible内置的关键字不能作为变量名。

由于之前的几篇文章都是在通过剧本举例,所以我们先聊聊怎样在playbook中使用变量。

如果我们想要在某个play中定义变量,可以借助vars关键字,示例如下:

---
- hosts: test181
  vars:
    testvar1: testfile
  remote_user: root
  tasks:
  - name: task1
    file:
      path: /testdir/{{ testvar1 }}
      state: touch

上例中,先使用vars关键字,表示在当前play中进行变量的相关设置。

vars关键字的下一级定义了一个变量,变量名为testvar1,变量值为testfile!当我们需要使用testvar1的变量值时,则需要引用这个变量,如你所见,使用{{ 变量名 }}可以引用对应的变量。

也可以定义多个变量,示例如下:

vars:
  testvar1: testfile
  testvar2: testfile2

除了使用上述语法,使用YAML的块序列语法也可以定义变量,示例如下:

vars:
  - testvar1: testfile
  - testvar2: testfile2

在定义变量时,还能够以类似属性的方式定义变量,示例如下:

---
- hosts: test181
  remote_user: root
  vars:
    nginx:
      conf80: /etc/nginx/conf.d/80.conf
      conf8080: /etc/nginx/conf.d/8080.conf
  tasks:
  - name: task1
    file:
      path: "{{ nginx.conf80 }}"
      state: touch
  - name: task2
    file:
      path: "{{ nginx.conf8080 }}"
      state: touch

如上例所示,我定义了两个变量,两个变量的值对应两个nginx配置文件路径!

  vars:
    nginx:
      conf80: /etc/nginx/conf.d/80.conf
      conf8080: /etc/nginx/conf.d/8080.conf

当我们需要引用这两个变量时,有两种语法可用:

  • 语法一:
"{{ nginx.conf80 }}"
  • 语法二:
"{{ nginx['conf8080'] }}"

这样使用变量在逻辑上比较清晰,可以看出conf80与conf8080都属于nginx相关的配置。

细心如你一定发现了,上例中,我在引用变量时使用了双引号,而在本文的第一个示例中引用变量时却没有使用双引号,这是因为,第一个示例中的变量在被引用时,并没有处于开头的位置,第一个示例中变量被引用时如下:

path: /testdir/{{ testvar1 }}

当file模块的path参数引用对应的变量时,先写入了/testdir/,然后才引用了testvar1变量,{{ testvar1 }}并没有处于开头的位置,换句话说就是,{{ testvar1 }}前面还有字符串/testdir/

而在上述后面的示例中引用变量时,变量被引用时如下,处于开头的位置

path: "{{ nginx.conf80 }}"

这种情况下,我们引用变量时必须使用双引号引起被引用的变量,否则会报语法错误。

其实,上述情况也有例外!

前文中有描述过,当在playbook中为模块的参数赋值时,可以使用冒号,也可以使用等号,当使用等号为模块的参数赋值时,则不用考虑引用变量时是否使用引号的问题,示例如下:

---
- hosts: test181
  remote_user: root
  vars:
    nginx:
      conf80: /etc/nginx/conf.d/80.conf
      conf8080: /etc/nginx/conf.d/8080.conf
  tasks:
  - name: task1
    file:
      path={{ nginx.conf80 }}
      state=touch
  - name: task2
    file:
      path={{ nginx['conf8080'] }}
      state=touch

除了能够在playbook中直接定义变量,我们还可以在某个文件中定义变量,然后再在playbook中引入对应的文件,引入文件后,playbook!

即可使用文件中定义的变量,你可能会问,为什么要多此一举呢?这是因为在某些工作场景中这样做很有用,比如,你想要让别人阅读你的playbook,却不想让别人看到某些值,可以使用这种办法,因为别人在阅读playbook时,只能看到引入的变量名,但是看不到变量对应的值,这种将变量分离到某个文件中的做法叫做变量文件分离变量文件分离除了能够隐藏某些值,还能够让你将不同类的信息放在不同的文件中,并且让这些信息与剧本主体分开。

先来看看变量文件分离的一些小例子:

首先,我们来定义一个专门用来存放nginx相关变量的文件(文件名为nginx_vars.yml),在文件中定义变量时,不要使用vars关键字,直接定义变量即可,定义变量的语法与在playbook中定义变量的几种语法相同:

# 语法一示例:
  testvar1: testfile
  testvar2: testfile2

# 语法二示例:
  - testvar1: testfile
  - testvar2: testfile2

# 语法三示例:
nginx:
  conf80: /etc/nginx/conf.d/80.conf
  conf8080: /etc/nginx/conf.d/8080.conf

你可以选择你觉得较为舒适的语法定义变量,如下所示,直接在nginx_vars.yml文件中定义变量即可。

$ cat nginx_vars.yml
nginx:
  conf80: /etc/nginx/conf.d/80.conf
  conf8080: /etc/nginx/conf.d/8080.conf

nginx_vars.yml中定义完相关变量后,即可在playbook中引入文件中的变量,在playbook中引入包含变量的文件时,需要使用vars_files关键字,被引入的文件需要以-开头,以YAML中块序列的语法引入,示例如下:

---
- hosts: test181
  remote_user: root
  vars_files:
  - /root/nginx_vars.yml
  tasks:
  - name: task1
    file:
      path={{ nginx.conf80 }}
      state=touch
  - name: task2
    file:
      path={{ nginx['conf8080'] }}
      state=touch

上例中使用vars_files关键字引入了对应的变量文件,然后使用了文件中定义的变量。

上例中vars_files关键字只引入了一个变量文件,也可以引入多个变量文件,每个被引入的文件都需要以-开头,示例如下:

  vars_files:
  - /root/nginx_vars.yml
  - /root/other_vars.yml

vars关键字和vars_files关键字可以同时使用,如下:

  vars:
  - conf90: /etc/nginx/conf.d/90.conf
  vars_files:
  - /root/nginx_vars.yml

二、变量(二)

当我们运行一个playbook时,默认都会运行一个名为[Gathering Facts]的任务,前文中已经大致的介绍过这个默认的任务,ansible通过[Gathering Facts]这个默认任务收集远程主机的相关信息(例如远程主机的IP地址,主机名,系统版本,硬件配置等信息),其实,这些被收集到的远程主机信息会保存在对应的变量中,当我们想要使用这些信息时,我们可以获取对应的变量,从而使用这些信息。

如果想要查看[Gathering Facts]任务收集的信息内容,我们可以借助一个模块:setup模块

当执行playbook时,playbook其实就是自动调用了setup模块从而执行了[Gathering Facts]任务,所以我们可以通过手动执行setup模块查看[Gathering Facts]任务收集到的信息,示例如下:

$ ansible test181 -m setup

上述ad-hoc命令表示收集test181主机的相关信息,执行上述命令后,远程主机test181的相关信息将会输出到ansible主机的控制台上,返回的信息的格式是json格式,我的返回信息如下。

注:由于返回的信息比较多,此处为了方便示例,我将部分内容删除(或折叠省略)了,所以如下返回信息并不完全,只用于示意。

test181 | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.99.181"
        ], 
        "ansible_all_ipv6_addresses": [
            "fe80::20c:29ff:fe8b:45a8"
        ], 
        "ansible_apparmor": {
            "status": "disabled"
        }, 
        "ansible_architecture": "x86_64", 
        "ansible_bios_date": "07/22/2020", 
        "ansible_bios_version": "6.00", 
        "ansible_cmdline": {
            "BOOT_IMAGE": "/vmlinuz-5.13.7-1.el7.elrepo.x86_64", 
            "LANG": "zh_CN.UTF-8", 
            "crashkernel": "auto", 
            "quiet": true, 
            "rd.lvm.lv": "centos/swap", 
            "rhgb": true, 
            "ro": true, 
            "root": "/dev/mapper/centos-root"
        }, 
        "ansible_date_time": {
            "date": "2022-04-05", 
            "day": "05", 
            "epoch": "1649132678", 
            "hour": "12", 
            "iso8601": "2022-04-05T04:24:38Z", 
            "iso8601_basic": "20220405T122438810519", 
            "iso8601_basic_short": "20220405T122438", 
            "iso8601_micro": "2022-04-05T04:24:38.810519Z", 
            "minute": "24", 
            "month": "04", 
            "second": "38", 
            "time": "12:24:38", 
            "tz": "CST", 
            "tz_offset": "+0800", 
            "weekday": "星期二", 
            "weekday_number": "2", 
            "weeknumber": "14", 
            "year": "2022"
        }, 
        "ansible_default_ipv4": {
            "address": "192.168.99.181", 
            "alias": "ens33", 
            "broadcast": "192.168.99.255", 
            "gateway": "192.168.99.254", 
            "interface": "ens33", 
            "macaddress": "00:0c:29:8b:45:a8", 
            "mtu": 1500, 
            "netmask": "255.255.255.0", 
            "network": "192.168.99.0", 
            "type": "ether"
        }, 
        "ansible_default_ipv6": {}, 
        "ansible_device_links": {
            "ids": {
                "dm-0": [
                    "dm-name-centos-root", 
                    "dm-uuid-LVM-wOun44dnxFqEsQqsPSIZ3cIqUg2iSDEg0hC7eR0wzR607i2QnueukIcdRuX3uPBX"
                ], 
                "dm-1": [
                    "dm-name-centos-swap", 
                    "dm-uuid-LVM-wOun44dnxFqEsQqsPSIZ3cIqUg2iSDEggIs0ZBctDIBCLPFYn2q2SwO92R6GVmWA"
                ], 
                "dm-2": [
                    "dm-name-centos-home", 
                    "dm-uuid-LVM-wOun44dnxFqEsQqsPSIZ3cIqUg2iSDEgBMraKBcZ33ePHaDtJrRQ81Q8lL0rQzBF"
                ], 
                "sda2": [
                    "lvm-pv-uuid-aocuZD-rpcj-kmjn-pDUw-m6hv-Oy3Q-YMDh9J"
                ], 
                "sr0": [
                    "ata-VMware_Virtual_IDE_CDROM_Drive_10000000000000000001"
                ]
            }, 
            "labels": {}, 
            "masters": {
                "sda2": [
                    "dm-0", 
                    "dm-1", 
                    "dm-2"
                ]
            }, 
            "uuids": {
                "dm-0": [
                    "7d61c1ab-6cf7-459b-92ce-582e6136e4b3"
                ], 
                "dm-1": [
                    "696deb08-5a25-4bac-85ad-31481c56e8c4"
                ], 
                "dm-2": [
                    "a82b2e1e-3eb4-4cab-b669-db0267f7c0a2"
                ], 
                "sda1": [
                    "4ac7250c-c049-4828-baf7-4f831c3eed10"
                ]
            }
        }, 
        "ansible_devices": {
            "dm-0": {
                "holders": [], 
                "host": "", 
                "links": {
                    "ids": [
                        "dm-name-centos-root", 
                        "dm-uuid-LVM-wOun44dnxFqEsQqsPSIZ3cIqUg2iSDEg0hC7eR0wzR607i2QnueukIcdRuX3uPBX"
                    ], 
                    "labels": [], 
                    "masters": [], 
                    "uuids": [
                        "7d61c1ab-6cf7-459b-92ce-582e6136e4b3"
                    ]
                }, 
                "model": null, 
                "partitions": {}, 
                "removable": "0", 
                "rotational": "1", 
                "sas_address": null, 
                "sas_device_handle": null, 
                "scheduler_mode": "", 
                "sectors": "104857600", 
                "sectorsize": "512", 
                "size": "50.00 GB", 
                "support_discard": "0", 
                "vendor": null, 
                "virtual": 1
            }, 
            "dm-1": {
                "holders": [], 
                "host": "", 
                "links": {
                    "ids": [
                        "dm-name-centos-swap", 
                        "dm-uuid-LVM-wOun44dnxFqEsQqsPSIZ3cIqUg2iSDEggIs0ZBctDIBCLPFYn2q2SwO92R6GVmWA"
                    ], 
                    "labels": [], 
                    "masters": [], 
                    "uuids": [
                        "696deb08-5a25-4bac-85ad-31481c56e8c4"
                    ]
                }, 
                "model": null, 
                "partitions": {}, 
                "removable": "0", 
                "rotational": "1", 
                "sas_address": null, 
                "sas_device_handle": null, 
                "scheduler_mode": "", 
                "sectors": "4194304", 
                "sectorsize": "512", 
                "size": "2.00 GB", 
                "support_discard": "0", 
                "vendor": null, 
                "virtual": 1
            }, 
            "dm-2": {
                "holders": [], 
                "host": "", 
                "links": {
                    "ids": [
                        "dm-name-centos-home", 
                        "dm-uuid-LVM-wOun44dnxFqEsQqsPSIZ3cIqUg2iSDEgBMraKBcZ33ePHaDtJrRQ81Q8lL0rQzBF"
                    ], 
                    "labels": [], 
                    "masters": [], 
                    "uuids": [
                        "a82b2e1e-3eb4-4cab-b669-db0267f7c0a2"
                    ]
                }, 
                "model": null, 
                "partitions": {}, 
                "removable": "0", 
                "rotational": "1", 
                "sas_address": null, 
                "sas_device_handle": null, 
                "scheduler_mode": "", 
                "sectors": "98549760", 
                "sectorsize": "512", 
                "size": "46.99 GB", 
                "support_discard": "0", 
                "vendor": null, 
                "virtual": 1
            }, 
            "sda": {
                "holders": [], 
                "host": "SCSI storage controller: LSI Logic / Symbios Logic 53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (rev 01)", 
                "links": {
                    "ids": [], 
                    "labels": [], 
                    "masters": [], 
                    "uuids": []
                }, 
                "model": "VMware Virtual S", 
                "partitions": {
                    "sda1": {
                        "holders": [], 
                        "links": {
                            "ids": [], 
                            "labels": [], 
                            "masters": [], 
                            "uuids": [
                                "4ac7250c-c049-4828-baf7-4f831c3eed10"
                            ]
                        }, 
                        "sectors": "2097152", 
                        "sectorsize": 512, 
                        "size": "1.00 GB", 
                        "start": "2048", 
                        "uuid": "4ac7250c-c049-4828-baf7-4f831c3eed10"
                    }, 
                    "sda2": {
                        "holders": [
                            "centos-swap", 
                            "centos-home", 
                            "centos-root"
                        ], 
                        "links": {
                            "ids": [
                                "lvm-pv-uuid-aocuZD-rpcj-kmjn-pDUw-m6hv-Oy3Q-YMDh9J"
                            ], 
                            "labels": [], 
                            "masters": [
                                "dm-0", 
                                "dm-1", 
                                "dm-2"
                            ], 
                            "uuids": []
                        }, 
                        "sectors": "207616000", 
                        "sectorsize": 512, 
                        "size": "99.00 GB", 
                        "start": "2099200", 
                        "uuid": null
                    }
                }, 
                "removable": "0", 
                "rotational": "1", 
                "sas_address": null, 
                "sas_device_handle": null, 
                "scheduler_mode": "mq-deadline", 
                "sectors": "209715200", 
                "sectorsize": "512", 
                "size": "100.00 GB", 
                "support_discard": "0", 
                "vendor": "VMware,", 
                "virtual": 1
            }, 
            "sr0": {
                "holders": [], 
                "host": "IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)", 
                "links": {
                    "ids": [
                        "ata-VMware_Virtual_IDE_CDROM_Drive_10000000000000000001"
                    ], 
                    "labels": [], 
                    "masters": [], 
                    "uuids": []
                }, 
                "model": "VMware IDE CDR10", 
                "partitions": {}, 
                "removable": "1", 
                "rotational": "1", 
                "sas_address": null, 
                "sas_device_handle": null, 
                "scheduler_mode": "mq-deadline", 
                "sectors": "2097151", 
                "sectorsize": "512", 
                "size": "1024.00 MB", 
                "support_discard": "0", 
                "vendor": "NECVMWar", 
                "virtual": 1
            }
        }, 
        "ansible_distribution": "CentOS", 
        "ansible_distribution_file_parsed": true, 
        "ansible_distribution_file_path": "/etc/redhat-release", 
        "ansible_distribution_file_variety": "RedHat", 
        "ansible_distribution_major_version": "7", 
        "ansible_distribution_release": "Core", 
        "ansible_distribution_version": "7.6", 
        "ansible_dns": {
            "nameservers": [
                "8.8.8.8", 
                "114.114.114.114"
            ]
        }, 
        "ansible_domain": "", 
        "ansible_effective_group_id": 0, 
        "ansible_effective_user_id": 0, 
        "ansible_ens33": {
            "active": true, 
            "device": "ens33", 
            "features": {
                "esp_hw_offload": "off [fixed]", 
                "esp_tx_csum_hw_offload": "off [fixed]", 
                "fcoe_mtu": "off [fixed]", 
                "generic_receive_offload": "on", 
                "generic_segmentation_offload": "on", 
                "highdma": "off [fixed]", 
                "hsr_dup_offload": "off [fixed]", 
                "hsr_fwd_offload": "off [fixed]", 
                "hsr_tag_ins_offload": "off [fixed]", 
                "hsr_tag_rm_offload": "off [fixed]", 
                "hw_tc_offload": "off [fixed]", 
                "l2_fwd_offload": "off [fixed]", 
                "large_receive_offload": "off [fixed]", 
                "loopback": "off [fixed]", 
                "macsec_hw_offload": "off [fixed]", 
                "netns_local": "off [fixed]", 
                "ntuple_filters": "off [fixed]", 
                "receive_hashing": "off [fixed]", 
                "rx_all": "off", 
                "rx_checksumming": "off", 
                "rx_fcs": "off", 
                "rx_gro_hw": "off [fixed]", 
                "rx_gro_list": "off", 
                "rx_udp_gro_forwarding": "off", 
                "rx_udp_tunnel_port_offload": "off [fixed]", 
                "rx_vlan_filter": "on [fixed]", 
                "rx_vlan_offload": "on", 
                "rx_vlan_stag_filter": "off [fixed]", 
                "rx_vlan_stag_hw_parse": "off [fixed]", 
                "scatter_gather": "on", 
                "tcp_segmentation_offload": "on", 
                "tls_hw_record": "off [fixed]", 
                "tls_hw_rx_offload": "off [fixed]", 
                "tls_hw_tx_offload": "off [fixed]", 
                "tx_checksum_fcoe_crc": "off [fixed]", 
                "tx_checksum_ip_generic": "on", 
                "tx_checksum_ipv4": "off [fixed]", 
                "tx_checksum_ipv6": "off [fixed]", 
                "tx_checksum_sctp": "off [fixed]", 
                "tx_checksumming": "on", 
                "tx_esp_segmentation": "off [fixed]", 
                "tx_fcoe_segmentation": "off [fixed]", 
                "tx_gre_csum_segmentation": "off [fixed]", 
                "tx_gre_segmentation": "off [fixed]", 
                "tx_gso_list": "off [fixed]", 
                "tx_gso_partial": "off [fixed]", 
                "tx_gso_robust": "off [fixed]", 
                "tx_ipxip4_segmentation": "off [fixed]", 
                "tx_ipxip6_segmentation": "off [fixed]", 
                "tx_lockless": "off [fixed]", 
                "tx_nocache_copy": "off", 
                "tx_scatter_gather": "on", 
                "tx_scatter_gather_fraglist": "off [fixed]", 
                "tx_sctp_segmentation": "off [fixed]", 
                "tx_tcp6_segmentation": "off [fixed]", 
                "tx_tcp_ecn_segmentation": "off [fixed]", 
                "tx_tcp_mangleid_segmentation": "off", 
                "tx_tcp_segmentation": "on", 
                "tx_tunnel_remcsum_segmentation": "off [fixed]", 
                "tx_udp_segmentation": "off [fixed]", 
                "tx_udp_tnl_csum_segmentation": "off [fixed]", 
                "tx_udp_tnl_segmentation": "off [fixed]", 
                "tx_vlan_offload": "on [fixed]", 
                "tx_vlan_stag_hw_insert": "off [fixed]", 
                "udp_fragmentation_offload": "off", 
                "vlan_challenged": "off [fixed]"
            }, 
            "hw_timestamp_filters": [], 
            "ipv4": {
                "address": "192.168.99.181", 
                "broadcast": "192.168.99.255", 
                "netmask": "255.255.255.0", 
                "network": "192.168.99.0"
            }, 
            "ipv6": [
                {
                    "address": "fe80::20c:29ff:fe8b:45a8", 
                    "prefix": "64", 
                    "scope": "link"
                }
            ], 
            "macaddress": "00:0c:29:8b:45:a8", 
            "module": "e1000", 
            "mtu": 1500, 
            "pciid": "0000:02:01.0", 
            "promisc": false, 
            "speed": 1000, 
            "timestamping": [
                "tx_software", 
                "rx_software", 
                "software"
            ], 
            "type": "ether"
        }, 
        "ansible_env": {
            "HISTTIMEFORMAT": "root %F %T ", 
            "HOME": "/root", 
            "LANG": "zh_CN.UTF-8", 
            "LESSOPEN": "||/usr/bin/lesspipe.sh %s", 
            "LOGNAME": "root", 
            "LS_COLORS": "rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:", 
            "MAIL": "/var/mail/root", 
            "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin", 
            "PROMPT_COMMAND": "{ msg=$(history 1 | { read x y; echo $y; });user=$(whoami); echo [$(date \"+%F %T\")] $(who am i) [$user]:[`pwd`]:$msg; } >> /tmp/`hostname`.`whoami`.history", 
            "PWD": "/root", 
            "SHELL": "/bin/bash", 
            "SHLVL": "2", 
            "SSH_CLIENT": "192.168.99.180 39734 22", 
            "SSH_CONNECTION": "192.168.99.180 39734 192.168.99.181 22", 
            "SSH_TTY": "/dev/pts/1", 
            "TERM": "xterm", 
            "USER": "root", 
            "XDG_RUNTIME_DIR": "/run/user/0", 
            "XDG_SESSION_ID": "2", 
            "_": "/usr/bin/python"
        }, 
        "ansible_fibre_channel_wwn": [], 
        "ansible_fips": false, 
        "ansible_form_factor": "Other", 
        "ansible_fqdn": "test181", 
        "ansible_hostname": "test181", 
        "ansible_hostnqn": "", 
        "ansible_interfaces": [
            "lo", 
            "ens33"
        ], 
        "ansible_is_chroot": false, 
        "ansible_iscsi_iqn": "", 
        "ansible_kernel": "5.13.7-1.el7.elrepo.x86_64", 
        "ansible_kernel_version": "#1 SMP Fri Jul 30 10:08:55 EDT 2021", 
        "ansible_lo": {
            "active": true, 
            "device": "lo", 
            "features": {
                "esp_hw_offload": "off [fixed]", 
                "esp_tx_csum_hw_offload": "off [fixed]", 
                "fcoe_mtu": "off [fixed]", 
                "generic_receive_offload": "on", 
                "generic_segmentation_offload": "on", 
                "highdma": "on [fixed]", 
                "hsr_dup_offload": "off [fixed]", 
                "hsr_fwd_offload": "off [fixed]", 
                "hsr_tag_ins_offload": "off [fixed]", 
                "hsr_tag_rm_offload": "off [fixed]", 
                "hw_tc_offload": "off [fixed]", 
                "l2_fwd_offload": "off [fixed]", 
                "large_receive_offload": "off [fixed]", 
                "loopback": "on [fixed]", 
                "macsec_hw_offload": "off [fixed]", 
                "netns_local": "on [fixed]", 
                "ntuple_filters": "off [fixed]", 
                "receive_hashing": "off [fixed]", 
                "rx_all": "off [fixed]", 
                "rx_checksumming": "on [fixed]", 
                "rx_fcs": "off [fixed]", 
                "rx_gro_hw": "off [fixed]", 
                "rx_gro_list": "off", 
                "rx_udp_gro_forwarding": "off", 
                "rx_udp_tunnel_port_offload": "off [fixed]", 
                "rx_vlan_filter": "off [fixed]", 
                "rx_vlan_offload": "off [fixed]", 
                "rx_vlan_stag_filter": "off [fixed]", 
                "rx_vlan_stag_hw_parse": "off [fixed]", 
                "scatter_gather": "on", 
                "tcp_segmentation_offload": "on", 
                "tls_hw_record": "off [fixed]", 
                "tls_hw_rx_offload": "off [fixed]", 
                "tls_hw_tx_offload": "off [fixed]", 
                "tx_checksum_fcoe_crc": "off [fixed]", 
                "tx_checksum_ip_generic": "on [fixed]", 
                "tx_checksum_ipv4": "off [fixed]", 
                "tx_checksum_ipv6": "off [fixed]", 
                "tx_checksum_sctp": "on [fixed]", 
                "tx_checksumming": "on", 
                "tx_esp_segmentation": "off [fixed]", 
                "tx_fcoe_segmentation": "off [fixed]", 
                "tx_gre_csum_segmentation": "off [fixed]", 
                "tx_gre_segmentation": "off [fixed]", 
                "tx_gso_list": "on", 
                "tx_gso_partial": "off [fixed]", 
                "tx_gso_robust": "off [fixed]", 
                "tx_ipxip4_segmentation": "off [fixed]", 
                "tx_ipxip6_segmentation": "off [fixed]", 
                "tx_lockless": "on [fixed]", 
                "tx_nocache_copy": "off [fixed]", 
                "tx_scatter_gather": "on [fixed]", 
                "tx_scatter_gather_fraglist": "on [fixed]", 
                "tx_sctp_segmentation": "on", 
                "tx_tcp6_segmentation": "on", 
                "tx_tcp_ecn_segmentation": "on", 
                "tx_tcp_mangleid_segmentation": "on", 
                "tx_tcp_segmentation": "on", 
                "tx_tunnel_remcsum_segmentation": "off [fixed]", 
                "tx_udp_segmentation": "on", 
                "tx_udp_tnl_csum_segmentation": "off [fixed]", 
                "tx_udp_tnl_segmentation": "off [fixed]", 
                "tx_vlan_offload": "off [fixed]", 
                "tx_vlan_stag_hw_insert": "off [fixed]", 
                "udp_fragmentation_offload": "off", 
                "vlan_challenged": "on [fixed]"
            }, 
            "hw_timestamp_filters": [], 
            "ipv4": {
                "address": "127.0.0.1", 
                "broadcast": "", 
                "netmask": "255.0.0.0", 
                "network": "127.0.0.0"
            }, 
            "ipv6": [
                {
                    "address": "::1", 
                    "prefix": "128", 
                    "scope": "host"
                }
            ], 
            "mtu": 65536, 
            "promisc": false, 
            "timestamping": [
                "tx_software", 
                "rx_software", 
                "software"
            ], 
            "type": "loopback"
        }, 
        "ansible_local": {}, 
        "ansible_lsb": {}, 
        "ansible_lvm": {
            "lvs": {
                "home": {
                    "size_g": "46.99", 
                    "vg": "centos"
                }, 
                "root": {
                    "size_g": "50.00", 
                    "vg": "centos"
                }, 
                "swap": {
                    "size_g": "2.00", 
                    "vg": "centos"
                }
            }, 
            "pvs": {
                "/dev/sda2": {
                    "free_g": "0.00", 
                    "size_g": "99.00", 
                    "vg": "centos"
                }
            }, 
            "vgs": {
                "centos": {
                    "free_g": "0.00", 
                    "num_lvs": "3", 
                    "num_pvs": "1", 
                    "size_g": "99.00"
                }
            }
        }, 
        "ansible_machine": "x86_64", 
        "ansible_machine_id": "f36646e20ef14cf7b7c3d921839dd2b5", 
        "ansible_memfree_mb": 684, 
        "ansible_memory_mb": {
            "nocache": {
                "free": 773, 
                "used": 176
            }, 
            "real": {
                "free": 684, 
                "total": 949, 
                "used": 265
            }, 
            "swap": {
                "cached": 0, 
                "free": 2047, 
                "total": 2047, 
                "used": 0
            }
        }, 
        "ansible_memtotal_mb": 949, 
        "ansible_mounts": [
            {
                "block_available": 213336, 
                "block_size": 4096, 
                "block_total": 259584, 
                "block_used": 46248, 
                "device": "/dev/sda1", 
                "fstype": "xfs", 
                "inode_available": 523956, 
                "inode_total": 524288, 
                "inode_used": 332, 
                "mount": "/boot", 
                "options": "rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota", 
                "size_available": 873824256, 
                "size_total": 1063256064, 
                "uuid": "4ac7250c-c049-4828-baf7-4f831c3eed10"
            }, 
            {
                "block_available": 12643463, 
                "block_size": 4096, 
                "block_total": 13100800, 
                "block_used": 457337, 
                "device": "/dev/mapper/centos-root", 
                "fstype": "xfs", 
                "inode_available": 26173336, 
                "inode_total": 26214400, 
                "inode_used": 41064, 
                "mount": "/", 
                "options": "rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota", 
                "size_available": 51787624448, 
                "size_total": 53660876800, 
                "uuid": "7d61c1ab-6cf7-459b-92ce-582e6136e4b3"
            }, 
            {
                "block_available": 12304453, 
                "block_size": 4096, 
                "block_total": 12312705, 
                "block_used": 8252, 
                "device": "/dev/mapper/centos-home", 
                "fstype": "xfs", 
                "inode_available": 24637432, 
                "inode_total": 24637440, 
                "inode_used": 8, 
                "mount": "/home", 
                "options": "rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota", 
                "size_available": 50399039488, 
                "size_total": 50432839680, 
                "uuid": "a82b2e1e-3eb4-4cab-b669-db0267f7c0a2"
            }
        ], 
        "ansible_nodename": "test181", 
        "ansible_os_family": "RedHat", 
        "ansible_pkg_mgr": "yum", 
        "ansible_proc_cmdline": {
            "BOOT_IMAGE": "/vmlinuz-5.13.7-1.el7.elrepo.x86_64", 
            "LANG": "zh_CN.UTF-8", 
            "crashkernel": "auto", 
            "quiet": true, 
            "rd.lvm.lv": [
                "centos/root", 
                "centos/swap"
            ], 
            "rhgb": true, 
            "ro": true, 
            "root": "/dev/mapper/centos-root"
        }, 
        "ansible_processor": [
            "0", 
            "GenuineIntel", 
            "Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz"
        ], 
        "ansible_processor_cores": 1, 
        "ansible_processor_count": 1, 
        "ansible_processor_threads_per_core": 1, 
        "ansible_processor_vcpus": 1, 
        "ansible_product_name": "VMware Virtual Platform", 
        "ansible_product_serial": "VMware-56 4d 81 b3 71 50 69 7c-a9 e8 7b 18 16 8b 45 a8", 
        "ansible_product_uuid": "b3814d56-5071-7c69-a9e8-7b18168b45a8", 
        "ansible_product_version": "None", 
        "ansible_python": {
            "executable": "/usr/bin/python", 
            "has_sslcontext": true, 
            "type": "CPython", 
            "version": {
                "major": 2, 
                "micro": 5, 
                "minor": 7, 
                "releaselevel": "final", 
                "serial": 0
            }, 
            "version_info": [
                2, 
                7, 
                5, 
                "final", 
                0
            ]
        }, 
        "ansible_python_version": "2.7.5", 
        "ansible_real_group_id": 0, 
        "ansible_real_user_id": 0, 
        "ansible_selinux": {
            "status": "disabled"
        }, 
        "ansible_selinux_python_present": true, 
        "ansible_service_mgr": "systemd", 
        "ansible_ssh_host_key_ecdsa_public": "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFo/KrBhN3lK3CkykLVGHcTC96dvoUR5Kd40/1aCvesta0IPE2GyahRqMlJVajquSNdGec8gUvyvtCs4fxkJrng=", 
        "ansible_ssh_host_key_ed25519_public": "AAAAC3NzaC1lZDI1NTE5AAAAID+EUCU/ZBz1voXqafEa56pgbFIe2Zrm77kZv+t9Mwvb", 
        "ansible_ssh_host_key_rsa_public": "AAAAB3NzaC1yc2EAAAADAQABAAABAQDROMxdwPEjkzarHzMNEqeVhbpkUKkKCTBmAaPNlmJejAM3u4sEkubIVnlGC38Fu7h9+n30LDV47PjOOq3bO1wf3Kva3eDL5bEFiDKCfOFs98TSUBsFTvPTL9JQXCLFndXz4NDXZDBVD0tCNYbQhhkD45bJTSQbo4Z2pHw002OOVvdaa5mPP8IBCNMhw3th5nQwFSEL2+8TxV1CBm7rfu0l+oB0k2hK0s/4YpsKBLiPjoXbBIBT+4NwTl66vmZMDh75LYObdmIyXTagrj6Z6ELLvFpfR5YRfO1kpnnvh5KbcwTHHMaYtCisGtlnVeOOrhKdiRRrSB74BoCyR0fUVxGp", 
        "ansible_swapfree_mb": 2047, 
        "ansible_swaptotal_mb": 2047, 
        "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", 
            "35", 
            "36", 
            "37", 
            "38", 
            "39", 
            "40+ep"
        ], 
        "ansible_system_capabilities_enforced": "True", 
        "ansible_system_vendor": "VMware, Inc.", 
        "ansible_uptime_seconds": 107, 
        "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": "guest", 
        "ansible_virtualization_type": "VMware", 
        "discovered_interpreter_python": "/usr/bin/python", 
        "gather_subset": [
            "all"
        ], 
        "module_setup": true
    }, 
    "changed": false
}

返回信息如上,是一个json格式的字符串,为了方便你阅读,ansible已经将格式化后的json信息返回到了控制台中,返回的信息很全面,比如:

  • ansible_all_ipv4_addresses:表示远程主机中的所有ipv4地址,从其对应的值可以看出,test181主机上一共有1个ipv4地址;
  • ansible_distribution:表示远程主机的系统发行版,从其对应的值可以看出test181主机的系统发行版为centos;
  • ansible_distribution_version:表示远程主机的系统版本号,从其对应的值与 ansible_distribution的值可以看出test181主机的系统版本为centos7.6;
  • ansible_ens33:表示远程主机ens33网卡的相关信息;
  • ansible_memory_mb:表示远程主机的内存配置信息;

返回的信息的确很多,很全面,但是,并不是每一次我们都需要看这么多信息,如果你只是想查看某一类信息,你可以通过关键字对信息进行过滤,比如,我只是想要查看远程主机的内存配置信息,那么我可以使用如下命令:

$ ansible test181 -m setup -a 'filter=ansible_memory_mb'

上述命令表示通过ansible_memory_mb关键字对返回信息进行过滤,如你所见,通过setup模块的filter参数可以指定需要过滤的关键字,这样ansible就只会将ansible_memory_mb的相关信息返回,返回如下:

test181 | SUCCESS => {
    "ansible_facts": {
        "ansible_memory_mb": {
            "nocache": {
                "free": 772, 
                "used": 177
            }, 
            "real": {
                "free": 681, 
                "total": 949, 
                "used": 268
            }, 
            "swap": {
                "cached": 0, 
                "free": 2047, 
                "total": 2047, 
                "used": 0
            }
        }, 
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false
}

这样就精简很多了,因为精准的返回了你需要的信息,我知道,有的朋友可能跟我一样,记性不好,所以通常记不住准确的关键字,所以我们可以使用通配符,进行相对模糊的过滤,示例如下:

$ ansible test181 -m setup -a 'filter=*mb*'

上述命令表示返回所有包含mb的关键字对应的信息,返回信息如下:

test181 | SUCCESS => {
    "ansible_facts": {
        "ansible_memfree_mb": 681, 
        "ansible_memory_mb": {
            "nocache": {
                "free": 772, 
                "used": 177
            }, 
            "real": {
                "free": 681, 
                "total": 949, 
                "used": 268
            }, 
            "swap": {
                "cached": 0, 
                "free": 2047, 
                "total": 2047, 
                "used": 0
            }
        }, 
        "ansible_memtotal_mb": 949, 
        "ansible_swapfree_mb": 2047, 
        "ansible_swaptotal_mb": 2047, 
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false
}

其实,除了这些信息以外,我们还能够在远程主机中写入一些自定义的信息,这些自定义信息也可以被setup模块收集到。

那么,我们应该在哪里定义这些信息呢?该怎样定义这些信息呢?

ansible默认会去目标主机的/etc/ansible/facts.d目录下查找主机中的自定义信息,并且规定,自定义信息需要写在以.fact为后缀的文件中,同时,这些以.fact为后缀的文件中的内容需要是INI格式或者是json格式的。

那么,我们来创建一个测试文件,测试文件路径为test181主机的/etc/ansible/facts.d/testinfo.fact,在文件中写入如下INI格式的信息。

root@test181:~ $ cat /etc/ansible/facts.d/testinfo.fact
[testmsg]
msg1=This is the first custom test message
msg2=This is the second custom test message

如上所示,上述内容是一段INI风格的内容,我在[testmsg]配置段中配置了两条自定义信息,msg1与msg2。当然,我们也可以使用json格式进行配置,比如在/etc/ansible/facts.d/testinfo.fact文件中写入如下配置,如下配置与上述配置的效果是相同的,只是书写格式不同:

{
   "testmsg":{
       "msg1":"This is the first custom test message",
       "msg2":"This is the second custom test message"
   }
}

通过上述方式,我们可以在目标主机的本地自定义信息,这些在远程主机本地自定义的信息被称为local facts,当我们运行setup模块时,远程主机的local facts信息也会被收集,我们可以通过ansible_local关键字过滤远程主机的local facts信息,示例命令如下:

$ ansible test181 -m setup -a "filter=ansible_local"

上述命令返回的信息如下:

test181 | SUCCESS => {
    "ansible_facts": {
        "ansible_local": {
            "testinfo": {
                "testmsg": {
                    "msg1": "This is the first custom test message", 
                    "msg2": "This is the second custom test message"
                }
            }
        }, 
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false
}

之前说过,当setup收集远程主机的local facts时,默认会查找远程主机的/etc/ansible/facts.d目录,如果你把local facts信息文件放在了其他自定义路径,在使用setup模块时,需要使用fact_path参数指定对应的路径,假设,我把.fact文件放在了目标主机的/testdir目录下,示例命令如下:

$ ansible test181 -m setup -a 'fact_path=/testdir'

其实,setup模块返回的这些信息都存在了对应的变量中,我们可以通过引用变量从而使用对应的信息,但是别急,我们先来了解一下另外一个模块,这个模块叫debug模块

见名知义,debug模块的作用就是帮助我们进行调试的,debug模块可以帮助我们把信息输出到ansible控制台上,以便我们能够定位问题。

那么我们先来看一个debug模块的playbook小示例,如下:

---
- hosts: test181
  remote_user: root
  tasks:
  - name: touch testfile
    file:
      path: /testdir/testfile
      state: touch
  - name: debug demo
    debug:
      msg: this is debug info,The test file has been touched

上例中,我们先在test181主机上touch了对应的文件,然后,利用debug模块在控制台中输出了我们想要显示的信息,如你所见,debug模块的msg参数可以指定我们想要输出的信息,上述playbook表示touch完对应的文件以后,在ansible控制台中输出我们指定的信息,那么我们运行一下这个测试剧本,看一下效果,如下:

image

如图所示,自定义信息已经输出在ansible控制台中。

debug模块除了能够使用msg参数输出自定义的信息,还能够直接输出变量中的信息,通过debug模块直接输出变量信息需要使用var参数,示例如下:

---
- hosts: test181
  remote_user: root
  vars:
    testvar: value of test variable
  tasks:
  - name: debug demo
    debug:
      var: testvar

上例虽然连接到了test181远程主机,但是并没有对test181做任何操作,只是在playbook中定义了一个变量,并且通过debug的var参数输出了这个变量的内容,只是为了单纯的演示debug模块的var参数的使用方法,上述playbook的执行效果如下:

image

变量的名称以及变量的值都输出到了屏幕上,这个功能可以帮助我们调试playbook中变量,让我们了解变量的值是否符合我们的要求。

当然,使用debug的msg参数时也可以引用变量的值,这样我们自定义的信息就更加灵活了,示例如下:

---
- hosts: test181
  remote_user: root
  vars:
    testvar: testv
  tasks:
  - name: debug demo
    debug:
      msg: "value of testvar is : {{testvar}}"

上例中的msg自定义信息中引用了testvar变量的值!

注:上例中msg的值需要使用引号引起,因为{{testvar}}变量前包含冒号,如果不使用引号会报语法错误。

上例输出效果如下:

image

setup模块与debug模块了解完了,现在绕回一开始的话题,playbook在运行时默认都会运行[Gathering Facts]任务,[Gathering Facts]任务会收集远程主机的相关信息,这些信息会保存在对应的变量中,我们在playbook中可以使用这些变量,从而利用这些信息,那么我们怎样在playbook获取到这些变量的值呢?在setup模块的示例中,我们可以通过ansible_memory_mb关键字获取远程主机的内存信息,其实,ansible_memory_mb就是一个变量名,换句话说就是,我们可以在playbook中直接引用名为ansible_memory_mb的变量,从而获取到远程主机的内存信息,示例如下:

---
- hosts: test181
  remote_user: root
  tasks:
  - name: debug demo
    debug:
      msg: "Remote host memory information: {{ansible_memory_mb}}"

上例执行效果如下:

image

如图所示,我们自定义的信息中包含了远程主机的内存信息,同时被输出了,只是格式上没有手动执行setup模块返回的信息格式易读,手动执行setup模块获取到的内存信息返回如下:

test181 | SUCCESS => {
    "ansible_facts": {
        "ansible_memory_mb": {
            "nocache": {
                "free": 771, 
                "used": 178
            }, 
            "real": {
                "free": 675, 
                "total": 949, 
                "used": 274
            }, 
            "swap": {
                "cached": 0, 
                "free": 2047, 
                "total": 2047, 
                "used": 0
            }
        }, 
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false
}

如上述返回信息所示,ansible_memory_mb中其实包含了 nocacherealswap三个部分的信息,如果我们只想获得real部分的信息,在playbook中引用变量时可以使用如下两种语法。

# 语法一示例:
debug:
     msg: "Remote host memory information : {{ansible_memory_mb.real}}"

# 语法二示例:
debug:
     msg: "Remote host memory information : {{ansible_memory_mb['real']}}"

# 上述两种语法前文中已经进行过示例,此处不再赘述。

其实,这些远程主机的变量信息不仅仅能够用于输出,我们通常会获取到这些信息以后,对这些信息的值进行判断,判断是否符合我们的要求,然后再执行下一步动作,比如,先获取到远程主机的系统发行版信息,然后判断发行版是centos6还是centos7,如果是centos6,我们就将准备好的A文件拷贝到远程主机中,如果是centos7,我们就将准备好的B文件拷贝到远程主机中,不过由于我们还没有总结条件判断的相关使用方法,所以此处就不进行示例了!

三、变量(三)

3.1 注册变量

ansible的模块在运行之后,其实都会返回一些返回值,只是默认情况下,这些返回值并不会显示而已,我们可以把这些返回值写入到某个变量中,这样我们就能够通过引用对应的变量从而获取到这些返回值了,这种将模块的返回值写入到变量中的方法被称为注册变量,那么怎样将返回值注册到变量中呢?我们来看一个playbook示例:

---
- hosts: test181
  remote_user: root
  tasks:
  - name: test shell
    shell: "echo test > /var/testshellfile"
    register: testvar
  - name: shell module return values
    debug:
      var: testvar

上例中共有两个任务,第一个任务使用shell模块在test181主机中创建了一个测试文件/var/testshellfile,将字符test输入到了测试文件中,然后使用register关键字将当前shell任务的返回值写入了名为testvar的变量中,第二个任务使用debug模块输出了第一个任务中的注册变量的值,没错,注册变量就是这么简单,使用register关键字指定对应的变量名即可。

上述playbook执行后,可以在控制台中看到名为[shell module return values]的任务中已经显示了第一个任务的返回值的信息,返回信息如下:

image

从上述返回信息可以看出,返回值是json格式的,上述返回值中包含一些键值对,比如 changed: true cmd: echo test > /var/testshellfile等, 如果你只是想要获取到返回值中的某一项特定值,只需要指定键值对中的key即可,假设,我只是想要获取到上述返回信息中cmd的值,则可以使用如下两种语法(前文中已经对如下两种语法进行过示例,此处不再赘述)!

# 语法一
  - name: shell module return values
    debug:
      msg: "{{testvar.cmd}}"

# 语法二
  - name: shell module return values
    debug:
      msg: "{{testvar['cmd']}}"

上述示例的返回信息为shell模块的返回值,如果你想要了解返回值中每一项的含义,则可以查看官方手册,我使用的是2.4版本的ansible,所以我可以参考2.4版本的官网文档,找到shell模块的介绍,参考官网链接

不同的模块,返回值也不尽相同,ansible官网对一些常见的返回值进行了总结,链接:https://docs.ansible.com/ansible/2.4/common_return_values.html

如果你想要查看模块对应的返回值,可以先查找官方手册,但是,并不是所有模块的官方手册中都对模块的返回值进行了描述,你可以使用上述示例中的方法,自己查看模块的返回值,这些返回值不仅仅能够用于输出,通常我们会利用到这些返回值,比如,通过模块的返回值决定之后的一些动作,所以,注册变量在playbook中还是会被经常用到的,在之后的文章中我们会给出示例,此处不用纠结。

3.2 提示用户输入信息并写入变量

在运行某些脚本时,有时候脚本会提示用户输入一些信息,脚本需要根据用户输入的信息决定下一步的动作,这种交互有时候是必须的,那么,在playbook中该怎样实现这种交互呢?我们可以这样做,提示用户输入信息,然后将用户输入的信息存入到指定的变量中,当我们需要使用这些输入的信息时,只要引用对应的变量即可。

我们来看一个小示例,如下:

---
- hosts: test181
  remote_user: root
  vars_prompt:
    - name: "your_name"
      prompt: "What is your name"
    - name: "your_age"
      prompt: "How old are you"
  tasks:
   - name: output vars
     debug:
      msg: Your name is {{your_name}},You are {{your_age}} years old.

如上例所示,我们使用vars_prompt关键字创建了两个变量,这两个变量的名称分别为your_nameyour_age,当运行上例playbook时,会出现What is your name的提示信息,然后用户输入的信息会存入到your_name变量中,之后,会出现How old are you的提示信息,用户输入的信息会存入到your_age变量中,上例中的output vars任务会输出一句话,这句话中包含了上述两个变量的值,我们来看一下上例的执行效果。

image

如上图所示,运行playbook时会提示输入你的名字,输入你的年龄,你输入的内容并不会显示在屏幕上,在完成提示输入的内容后,在output vars任务的输出中可以看到用户输入的名字和年龄。

如你所见,当你使用这种方式提示用户时,默认情况下不会显示用户输入的信息,这种方式比较适合用户输入密码时的场景,如果你想要显示用户输入的信息,可以使用如下示例中的方法。

  vars_prompt:
    - name: "your_name"
      prompt: "What is your name"
      private: no
    - name: "your_age"
      prompt: "How old are you"
      private: no

如上例所示,我们在定义vars_prompt中的变量时,使用private关键字,将变量的private属性设置为no即可,private: no表示变量值为非私有的,可见的,默认情况下 private值为yes,表示不可见。

我们还能为提示信息设置默认值,即如果用户不输入任何信息,则将默认值赋予变量,示例playbook如下:

---
- hosts: test181
  remote_user: root
  vars_prompt:
    - name: "solution"
      prompt: "Choose the solution you want \n
      A: solutionA\n
      B: solutionB\n
      C: solutionC\n"
      private: no
      default: A
  tasks:
   - name: output vars
     debug:
      msg: The final solution is {{solution}}.

如上例所示,我们使用了default关键字设置了solution变量的默认值,如果用户没有输入任何值(直接回车),则将solution变量的值设置为A,如果用户输入了值,则solution变量值为用户输入的值。

之前的示例中,我们提到可以利用提示信息让用户设置密码,有了这项功能,我们就可以编写出一个playbook,这个playbook可以让用户手动输入用户名和密码,然后根据用户输入的信息去创建系统用户了,聪明如你一定想到了,创建系统用户可以使用user模块,前文已经总结过user模块,此处不再赘述,那么我们来尝试编写一个可交互创建系统用户的playbook吧,经过思考,我编写了如下playbook,你可以帮我看看如下playbook中存在什么问题。

---
- hosts: test181
  remote_user: root
  vars_prompt:
    - name: "user_name"
      prompt: "Enter user name"
      private: no
    - name: "user_password"
      prompt: "Enter user password"
  tasks:
   - name: create user
     user:
      name: "{{user_name}}"
      password: "{{user_password}}"

上例的playbook似乎没有什么不妥,但是细心如你一定发现了,user模块的password参数虽然可以指定用户的密码,但是password参数对应的值必须是一个明文密码哈希过后的字符串(如果你不明白我在说什么,可以参考之前文章中总结的user模块的使用方法),而上例中,用户经过提示后输入的密码字符串并未经过哈希操作,所以,即使通过上述playbook可以创建用户,创建后的用户也无法通过设置的密码进行登录,因为保存在/etc/shadow文件中的密码字段是一个未哈希的明文的密码字段。那么,我们该怎么办呢?没错,我们需要对用户输入的密码字符串进行哈希,然后将哈希过后的字符串传入user模块的password参数中,ansible已经为我们考虑到了这一点,我们可以使用encrypt关键字,对用户输入的字符串进行哈希,用户输入的信息被哈希以后会存入对应的变量中,示例如下:

---
- hosts: test181
  remote_user: root
  vars_prompt:
    - name: "hash_string"
      prompt: "Enter something"
      private: no
      encrypt: "sha512_crypt"
  tasks:
   - name: Output the string after hash
     debug:
      msg: "{{hash_string}}"

如上例所示(先不要着急运行上述playbook),encrypt关键字表示对用户输入的信息进行哈希,encrypt: sha512_crypt表示使用sha512算法对用户输入的信息进行哈希,哈希后的字符串会存入到上例中的hash_string变量中,利用encrypt关键字,就可以解决之前遇到的创建用户时指定密码字符串的问题,但是需要注意,当使用encrypt关键字对字符串进行哈希时,ansible需要依赖passlib库完成哈希操作,如果未安装passlib库(一个用于哈希明文密码的python库),执行playbook时会报如下错误ERROR! passlib must be installed to encrypt vars_prompt values,我的ansible主机的操作系统为centos7.4,默认自带python2.7.5,为了能够正常执行上述playbook,需要先安装passlib库,命令如下:

$ yum -y install python-pip
$ pip install passlib

passlib库安装完成后,执行上例中的剧本,可以看到,你输入的信息在经过哈希以后被输出了,当然,上例中我们指定了使用sha512算法对字符串进行哈希,你也可以指定其他passlib库支持的算法,算法名称可以参考链接:https://docs.ansible.com/ansible/2.4/playbooks_prompts.html

除了能够使用encrypt关键字对字符串进行哈希加密,还能够使用confirm关键字实现类似确认密码的功能,我们在为用户设置密码时,通常需要输入两次完全相同的密码,才能够设置成功,通过confirm关键字就能实现类似的效果,示例playbook如下:

---
- hosts: test181
  remote_user: root
  vars_prompt:
    - name: "user_name"
      prompt: "Enter user name"
      private: no
    - name: "user_password"
      prompt: "Enter user password"
      encrypt: "sha512_crypt"
      confirm: yes
  tasks:
   - name: create user
     user:
      name: "{{user_name}}"
      password: "{{user_password}}"

命令执行效果,如下图:

image

3.3 通过命令行传入变量

除了之前总结过的定义变量的方法,我们还能够在执行playbook时直接传入需要使用的变量,我们来看一小示例,如下:

---
- hosts: test181
  remote_user: root
  tasks:
  - name: "Passing Variables On The Command Line"
    debug:
      msg: "{{pass_var}}"

上例中的playbook中,并没有定义pass_var变量,而是直接引用了pass_var变量,我们可以在调用上述playbook时直接从命令行传入pass_var变量,方法如下:

$ ansible-playbook cmdvar.yml --extra-vars "pass_var=cmdline pass var"

如上例所示,在调用playbook时使用--extra-vars选项可以传递对应的变量与变量值,--extra-vars是长选项,对应的短选项是-e,我们也可以一次性传入多个变量,变量之间用空格隔开,如下:

$ ansible-playbook cmdvar.yml -e 'pass_var="test" pass_var1="test1"'

上例中的playbook中并没有定义pass_var变量,如果在调用playbook时也没有传入pass_var变量,则会报错,其实,我们也可以先在playbook中定义好变量,然后在执行playbook时,再次传入相同名称的变量,最终还是以传入的变量值为准,示例如下:

---
- hosts: test181
  remote_user: root
  vars:
    pass_var: test_default
  tasks:
  - name: "Passing Variables On The Command Line"
    debug:
      msg: "{{pass_var}}"

上例的playbook中定义了pass_var变量,其值为test_default,在执行上述playbook时,从命令行再次传入pass_var变量,命令如下:

$ ansible-playbook cmdvar.yml -e 'pass_var="test"'

执行上述命令后,你会发现,最终输出的值为test而非test_default,也就是说,命令行传入的变量的优先级要高于playbook中的变量,通过这种方法,我们就能够更加灵活的指定变量的值了。

不仅ansible-playbook命令可以使用-e传递变量,ansible命令也同样可以,所以在执行ad-hoc命令时也可以使用同样的方法传入变量,如下:

$ ansible test181 -e "testvar=test" -m shell -a "echo {{testvar}}"

上述的几个示例从命令行中传递变量时,都是使用了key=value的形式,除了使用key=value的方式传递变量,ansible还支持通过json的格式传入变量,示例如下:

  • 通过json格式传入两个变量:
$ ansible-playbook cmdvar.yml -e '{"testvar":"test","testvar1":"test1"}'

通过json格式传入稍微复杂一点的变量:

$ ansible-playbook cmdvar.yml -e '{"countlist":["one","two","three","four"]}'

在剧本中引用上述命令传入的countlist变量时,如果想要获取到值one,则可以使用如下两种语法引用变量:

{{countlist[0]}} 或者 {{countlist.0}}

命令行不仅能够传入变量,还能传入变量文件,变量文件中的变量都会一并被传入,变量文件可以是json格式的,也可以是YAML格式的,此处使用YAML格式的变量文件进行示例,示例文件内容如下:

$ cat /testdir/ansible/testvar
testvar: testvarinfile
countlist:
- one
- two
- three
- four

测试用playbook内容如下:

---
- hosts: test181
  remote_user: root
  tasks:
  - name: "Passing Variables On The Command Line"
    debug:
      msg: "{{testvar}} {{countlist[0]}}"

如playbook所示,playbook中引用了变量文件中定义的两个变量,那么,我们怎样从命令行中将变量文件中的变量传入playbook呢?示例如下:

$ ansible-playbook cmdvar.yml -e "@/testdir/ansible/testvar"

如上述命令所示,使用@符号加上变量文件的路径,即可在命令行中传入对应的变量文件,变量文件中的所有变量都可以在playbook中引用,还是很方便的吧。

四、变量(四)

4.1 在清单中配置变量

在ansible系列文章的前几篇文章中,我们总结了Ansible清单配置详解方法,在清单中,可以配置需要被管理的远程主机,也可以将部分远程主机分为一组,其实,在配置清单时,还可以为主机或主机组设置变量,具体方法见如下总结:

4.1.1 主机变量

在清单中配置远程主机时,可以同时为主机配置对应的变量,当操作这个主机时,即可直接使用对应的变量。

比如,我在/etc/ansible/hosts中定义test181主机时,可以为test181主机配置一个名为testhostvar的变量,变量值为test181_host_var,示例如下:

test181 ansible_host=192.168.99.181 testhostvar=test181_host_var

如上例所示,只要在定义主机时将变量名和变量值写在主机配置的后面即可,可以为一个主机定义多个主机变量,用空格隔开即可,很方便吧,那么我们来测试一下,看看在操作test181主机时,能否引用到这个变量,为了方便示例就不编写playbook了,输入如下ad-hoc命令即可测试出效果:

image

如上图所示,操作test181主机时,testhostvar已经被引用到了,当然,testhostvar是test181的主机变量,其他主机并不能引用到这个变量,主机变量的生效范围只限于对应的主机。

前文中总结过,配置清单时可以使用INI格式或者YAML格式的语法,刚才的示例为INI风格的语法配置,YAML格式的配置中,可以使用如下方法配置变量:

all:
 hosts:
   test181:
     ansible_host: 192.168.99.181
     ansible_port: 22
     testhostvar: test181_host_var
     testhostvar1: test181_host_var1

如上例所示,我们为test181主机配置了两个变量,testhostvar和testhostvar1,没错,就是这么简单,直接在test181的下一级写明变量与变量值即可。

你也可以使用如下方法配置有层级的变量,如下:

all:
 hosts:
   test181:
     ansible_host: 192.168.99.181
     ansible_port: 22
     testhostvar: test181_host_var
     testhostvar1: test181_host_var1
     testhostvar3:
       thv31: 3.1
       thv32: 3.2

根据前文的总结,聪明如你一定已经想到了,我们可以使用如下两种方法引用变量中的值!

image

4.1.2 主机组变量

在清单中,我们能将多个主机分为一组,这样方便我们成批的操作远程主机。

比如,我在清单中将test181与test182分为一组,组名为testB,INI格式的配置如下:

[testB]
test181 ansible_host=192.168.99.181
test182 anisble_host=192.168.99.182

如果我们想为testB组配置组变量,该怎么办呢?示例如下:

[testB]
test181 ansible_host=192.168.99.181
test182 anisble_host=192.168.99.182
 
[testB:vars]
test_group_var1='group var test'
test_group_var2='group var test2'

如上例所示,[testB:vars]表示为testB组配置变量,上例中,testB组中一共定义了两个组变量,test_group_var1test_group_var2

组变量的使用范围为组中的所有主机,上例中,无论test181还是test182,都可以使用到上述两个变量,效果如下:

image

上例为INI格式中配置组变量的方法,YAML格式中配置组变量的示例如下:

all:
 children:
   testB:
     hosts:
       test181:
         ansible_host: 192.168.99.181
         ansible_port: 22
       test182:
         ansible_host: 192.168.99.182
         ansible_port: 22
     vars:
       test_group_var1: 'group var test1'
       test_group_var2: 'group var test2'

如上例所示,使用vars关键字可以指定组变量,vars关键字位于对应组的下一级,上例中,vars关键字位于testB的下一级,调用组变量的效果如下:

image

4.2 通过set_fact定义变量

set_fact是一个模块,我们可以通过set_fact模块在tasks中定义变量,先来看一个小示例,如下:

---
- hosts: test181
  remote_user: root
  tasks:
  - set_fact:
      testvar: "testtest"
  - debug:
      msg: "{{testvar}}"

如上例所示,我们通过set_fact模块定义了一个名为testvar的变量,变量值为testtest,然后使用debug模块输出了这个变量。

是不是很简单,通过set_fact模块就能够在tasks中定义变量了,我们也可以通过set_fact将一个变量的值赋予另一个变量,示例如下:

---
- hosts: test181
  remote_user: root
  vars:
    testvar1: test1_string
  tasks:
  - shell: "echo test2_string"
    register: shellreturn
  - set_fact:
      testsf1: "{{testvar1}}"
      testsf2: "{{shellreturn.stdout}}"
  - debug:
      msg: "{{testsf1}} {{testsf2}}"

上例中,我们先定义了一个变量testvar1,又使用register将shell模块的返回值注册到了变量shellreturn中,之后,使用set_fact模块将testvar1变量的值赋予了变量testsf1,将shellreturn变量中的stdout信息赋值给了testsf2变量,最后,使用debug模块输出了testsf1与testsf2的值。

如上述示例所示,set_fact模块可以让我们在tasks中创建变量,也可以将一个变量的值赋值给另一个变量。

其实,通过set_fact模块创建的变量还有一个特殊性,通过set_fact创建的变量就像主机上的facts信息一样,可以在之后的play中被引用,什么意思呢?

前文中已经总结过,默认情况下,每个play执行之前都会执行一个名为[Gathering Facts]的默认任务,这个任务会收集对应主机的相关信息,我们可以称这些信息为facts信息,我们已经总结过怎样通过变量引用这些facts信息,此处不再赘述,而通过set_fact模块创建的变量可以在之后play中被引用,就好像主机的facts信息可以在play中引用一样,这样说可能还是不是特别容易理解,不如来看一个小例子,如下:

---
- hosts: test181
  remote_user: root
  vars:
    testvar1: tv1
  tasks:
  - set_fact:
      testvar2: tv2
  - debug:
      msg: "{{testvar1}} ----- {{testvar2}}"
 
- hosts: test181
  remote_user: root
  tasks:
  - name: other play get testvar2
    debug:
      msg: "{{testvar2}}"
  - name: other play get testvar1
    debug:
      msg: "{{testvar1}}"

上例中一共有两个play,第一个play中,我们通过两种方式创建了两个变量,第一个变量testvar1使用vas关键字创建,第二个变量使用set_fact创建。

如果执行上例的playbook,可以发现,这两个变量在第一个play中都可以正常的输出。但是在第二个play中,testvar2可以被正常输出了,testvar1却不能被正常输出,会出现未定义testvar1的错误,因为在第一个play中针对test181主机进行操作时,testvar1是通过vars关键字创建的,而testvar2是通过set_fact创建的,所以testvar2就好像test181的facts信息一样,可以在第二个play中引用到,而创建testvar1变量的方式则不能达到这种效果,虽然testvar2就像facts信息一样能被之后的play引用,但是在facts信息中并不能找到testvar2,只是”效果上”与facts信息相同罢了。

前文已经总结了注册变量的用法,其实注册变量也可以在之后的play操作同一主机时被调用到,示例如下:

---
- hosts: test181
  remote_user: root
  vars:
    testvar3: tv3
  tasks:
  - shell: "echo tv4"
    register: testvar4
  - debug:
      msg: "{{testvar3}} -- {{testvar4.stdout}}"
 
- hosts: test181
  remote_user: root
  tasks:
  - name: other play get testvar4
    debug:
      msg: "{{testvar4.stdout}}"
  - name: other play get testvar3
    debug:
      msg: "{{testvar3}}"

执行上例的playbook时,在第二个play中获取testvar3时会报错,而在第二个play中获取注册变量testvar4时则正常,但是,注册变量中的信息是模块的返回值,这并不是我们自定义的信息,所以,如果想要在tasks中给变量自定义信息,并且在之后的play操作同一个主机时能够使用到之前在tasks中定义的变量时,则可以使用set_facts定义对应的变量。

细心如你一定发现了,上述示例中,即使是跨play获取变量,也都是针对同一台主机,但是某些时候,我们可能想要在操作一台主机时,获取到之前操作的另一台主机中定义的变量,那么该怎样做呢?接着往下看!

五、变量(五)

ansible中还有一些内置变量可供我们使用,当然,这些内置变量的变量名是被ansible保留的,我们定义变量时不能使用这些变量名。

5.1 内置变量ansible_version

先从一个简单的内置变量说起,比如,我们可以通过内置变量ansible_version获取到ansible的版本号,示例命令如下:

$ ansible test181 -m debug -a "msg={{ansible_version}}"

5.2 内置变量hostvars

除了ansible_version,还有一些非常有用的内置变量。比如内置变量hostvars,还可以帮助我们在操作当前主机时获取到其他主机中的信息。

假设,我想要在操作test181主机时获取到test182主机中的facts信息,我该怎么办呢?示例如下:

---
- name: "play 1: Gather facts of test182"
  hosts: test182
  remote_user: root
 
- name: "play 2: Get facts of test182 when operating on test182"
  hosts: test181
  remote_user: root
  tasks:
  - debug:
      msg: "{{hostvars['test182'].ansible_ens33.ipv4}}"

上例中有两个play,第一个play针对test182主机执行,但是第一个play中没有显式指定任何task(后文会解释原因),第二个play针对test181主机执行,在第二个play中只有一个task,即使用debug模块,输出了test182主机中的ens33网卡的IP信息,如你所见,我们可以借助hostvars在操作当前主机时输出其他主机中的facts信息,上例中使用hostvars加上清单中的主机名称再加上facts的key,即可获取到对应的facts信息,有了前文的总结作为基础,你一定想到了,上例中的msg的值改为如下写法也是可以的。

"{{hostvars.test182.ansible_ens33.ipv4}}"

上例中的第一个play中并没有任何的task,为什么还需要第一个play呢?如果你将上例的第一个play删除,只保留第二个play,运行时则会报错,这是因为,虽然第一个play中没有任何task,但是当第一个play执行时,默认会调用[Gathering Facts]任务,也就是说,默认会收集test182主机的facts信息,只有被收集过的facts信息才能被后面的play引用到,如果压根没有收集对应主机的facts信息,即使使用hostvars内置变量,也无法获取到对应主机的facts信息,我们来做个试验,我们可以直接把上例的第一个play从playbook中删除,也可以指明让第一个play不收集对应的facts信息,使用gather_facts关键字可以控制当前play是否收集对应主机的facts信息,示例如下:

---
- name: "play 1: Gather facts of test182"
  hosts: test182
  remote_user: root
  gather_facts: no
 
- name: "play 2: Get facts of test182 when operating on test181"
  hosts: test181
  remote_user: root
  tasks:
  - debug:
      msg: "{{hostvars['test182'].ansible_ens33.ipv4}}"

如上例所示,第一个play中的gather_facts: no表示设置当前play不收集对应主机的信息,运行上例playbook会报错,因为第二个play在操作test181时,无法获取到test182主机中的facts信息,原因是test182的facts信息并未被收集过,所以,调用其他主机的facts信息的前提是对应主机的facts信息已经被收集过。

其实,除了facts信息,我们还能够利用hostvars内置变量从别的主机中获取到其他类型的一些变量信息,比如,其他主机的注册变量、主机变量、组变量等信息,我们先来看一个获取其他主机的注册变量的小示例,如下:

---
- hosts: test182
  remote_user: root
  gather_facts: no
  tasks:
  - shell: "echo register_var_in_play1"
    register: shellreturn
 
- hosts: test181
  remote_user: root
  gather_facts: no
  tasks:
  - debug:
      msg: "{{hostvars.test182.shellreturn.stdout}}"

如上例所示,通过hostvars内置变量可以直接获取到其他主机中的注册变量,你一定发现了,注册变量并不用像facts信息那样需要事先收集,即可直接通过hostvars跨主机被引用到,同理,如果你在清单中为test182主机配置了主机变量,或者为test182主机所在的组配置了组变量,也是可以通过hostvars直接跨主机引用的,这里就不进行示例了,动手试试吧。

你可能会问,如果我直接在play中为当前主机定义一个变量,可以在之后的play中操作其他主机时被引用到吗?那么我们来做个实验,示例如下:

---
- hosts: test182
  remote_user: root
  gather_facts: no
  vars:
    testvar: testvar_in_71
  tasks:
  - debug:
      msg: "{{testvar}}"
 
- hosts: test181
  remote_user: root
  gather_facts: no
  tasks:
  - debug:
      msg: "{{hostvars.test182.testvar}}"

在上例的第一个play中我们为test182主机定义了一个变量,变量名称为testvar,在第二个play中操作test181主机时,使用hostvars尝试引用test182主机中的变量,如果执行上述playbook则会报错,看来通过vars关键字定义的变量使用上例中的方法是无法被跨主机引用的,聪明如你,一定想到了解决方案,我们在上面文章中总结了怎样使用set_fact关键字定义变量,通过set_fact关键字定义的变量拥有类似facts信息的特性,所以,我们可以把vars关键字中定义的变量通过set_fact关键字去定义,这样这些变量就好像facts信息被收集过一样,能被之后的play引用到了,示例如下:

---
- hosts: test182
  remote_user: root
  gather_facts: no
  tasks:
  - set_fact:
      testvar: "testvar_in_71"
  - debug:
      msg: "{{testvar}}"
 
- hosts: test181
  remote_user: root
  gather_facts: no
  tasks:
  - debug:
      msg: "{{hostvars.test182.testvar}}"

上例通过set_fact结合hostvars的方式,实现了跨play获取其他主机中的变量信息的功能,还是很方便的。

5.3 内置变量inventory_hostname

通过inventory_hostname变量可以获取到被操作的当前主机的主机名称,这里所说的主机名称并不是linux系统的主机名,而是对应主机在清单中配置的名称,假设我的清单配置如下:

[test_group]
192.168.99.181
test181.lzj.net ansible_host=192.168.99.181
test182 anisble_host=192.168.99.182

清单中配置了三个主机,第一个主机以IP的形式配置,第二个主机和第三个主机都以别名的方式配置,他们同属于test_group组。

那么我们使用内置变量inventory_hostname获取一下各个主机的对应的主机名,看看会返回什么,示例如下:

$ ansible test_group -m debug -a "msg={{inventory_hostname}}"
192.168.99.181 | SUCCESS => {
    "msg": "192.168.99.181"
}
test181.lzj.net | SUCCESS => {
    "msg": "test181.lzj.net"
}
test182 | SUCCESS => {
    "msg": "test182"
}

从返回信息可以看出,如果使用IP配置主机,inventory_hostname的值就是IP,如果使用别名,inventory_hostname的值就是别名!

5.4 内置变量inventory_hostname_short

与内置变量inventory_hostname类似,通过inventory_hostname_short也可以获取当前play操作的主机在清单中对应的名称,但是这个名称更加简短,假设我的清单配置如下:

[test_group]
192.168.99.181
test181.lzj.net ansible_host=192.168.99.181
test182 anisble_host=192.168.99.182

那么通过内置变量inventory_hostname_short获取到的主机的简短名称如下:

$ ansible test_group -m debug -a "msg={{inventory_hostname_short}}"
192.168.99.181 | SUCCESS => {
    "msg": "192"
}
test181.lzj.net | SUCCESS => {
    "msg": "test181"
}
test182 | SUCCESS => {
    "msg": "test182"
}

可以看到,无论是IP还是别名,如果清单的主机名称中包含.inventory_hostname_short都会取得主机名中第一个.之前的字符作为主机的简短名称。

5.5 内置变量play_hosts

通过内置变量play_hosts可以获取到当前play所操作的所有主机的主机名列表,示例playbook如下:

---
- hosts: test181,test182
  remote_user: root
  gather_facts: no
  tasks:
  - debug:
      msg: "{{play_hosts}}"

执行上例的playbook,返回信息如下:

TASK [debug] *******************************************************************************************************************************************************
ok: [test181] => {
    "msg": [
        "test181", 
        "test182"
    ]
}
ok: [test182] => {
    "msg": [
        "test181", 
        "test182"
    ]
}

可以看到,此play每操作一个主机,都会将当前play操作的所有主机的主机名列表返回。

没错,inventory_hostnameplay_hosts都是返回主机名,只不过,inventory_hostname只返回当前被操作的主机的主机名,而play_hosts则返回当前play中所有被操作主机的主机名列表。

5.6 内置变量groups

通过groups内置变量可以获取到清单中所有分组的分组信息,什么意思呢?我们先来看一个清单配置,假设我的清单配置如下:

192.168.99.181
test181.lzj.net ansible_host=192.168.99.181
test182 anisble_host=192.168.99.182
 
[testA]
test181 ansible_host=192.168.99.181
test182 ansible_host=192.168.99.182
 
[testB]
test181 ansible_host=192.168.99.181
 
[test:children]
testA
testB

上述清单中,显式的指定了三个组,testA组、testB组、test组,其中,testA组与testB组是test组的子组,除了组中的主机,还有三台主机没有任何分组,直接写在了清单中。

现在,我们获取一下groups变量的值,看看会返回哪些信息,随便操作清单中的任意一台主机即可,示例如下:

$ ansible test181 -m debug -a "msg={{groups}}"
test181 | SUCCESS => {
    "msg": {
        "all": [
            "192.168.99.181", 
            "test181.lzj.net", 
            "test181", 
            "test182"
        ], 
        "test": [
            "test181", 
            "test182"
        ], 
        "testA": [
            "test181", 
            "test182"
        ], 
        "testB": [
            "test181"
        ], 
        "ungrouped": [
            "192.168.99.181", 
            "test181.lzj.net"
        ]
    }
}

从上述返回信息可以看出,所有主机默认被分成了组名为all的组,testA组中有两台主机,testB组中有一台主机,由于testA组和testB组都属于test组的子组,所以testA组与testB组中的主机都属于test组,由于有三台主机在清单中并未分组,所以,ansible自动将没有分组的主机分到了名为ungrouped的组中,即组名为未分组的组。

我们还能够通过组名,获取到指定组的分组信息,假设,我想要获取到上例中test组中的主机名称,则可以使用如下方法。

$ ansible test181 -m debug -a "msg={{groups.test}}"

当然,语法也可以改为如下:

$ ansible test181 -m debug -a "msg={{groups['test']}}"

聪明如你一定已经会举一反三了,所以,如果我们想要获取到所有未分组主机的主机名,则可以使用如下方法:

$ ansible test181 -m debug -a "msg={{groups.ungrouped}}"

5.7 内置变量group_names

见名知义,我们可以通过内置变量group_names获取到当前主机所在分组的组名,比如,我的清单配置如下:

192.168.99.181
test181.lzj.net ansible_host=192.168.99.181
test182 anisble_host=192.168.99.182

[testA]
test182 ansible_host=192.168.99.182

[testB]
test181 ansible_host=192.168.99.181

[test:children]
testA
testB

那么,当我操作test181主机时,group_names变量值如下:

$ ansible test181 -m debug -a "msg={{group_names}}"
test181 | SUCCESS => {
    "msg": [
        "test", 
        "testB"
    ]
}

如上例返回值所示,test181主机属于testB组,而testB组又是test组的子组,所以test181主机同时属于testB组和test组,所以,最终返回的信息中包括test与testB!

当我们操作未分组的主机时,group_names的值为ungrouped,示例如下:

$ ansible 192.168.99.181 -m debug -a "msg={{group_names}}"
192.168.99.181 | SUCCESS => {
    "msg": [
        "ungrouped"
    ]
}

5.8 内置变量inventory_dir

我们可以通过inventory_dir变量获取到ansible主机中清单文件的存放路径,我使用的是默认的清单文件/etc/ansible/hosts,所以,inventory_dir变量对应的值为/etc/ansible,如下例所示:

$ ansible test181 -m debug -a "msg={{inventory_dir}}"
test181 | SUCCESS => {
    "msg": "/etc/ansible"
}

ansible中还有其他的一些变量的使用方法,但是需要结合其他的一些知识点,所以之后遇到了实际的使用场景,我们再进行介绍!

posted @ 2022-04-05 15:43  吕振江  阅读(912)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end