一、变量(一)
在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控制台中输出我们指定的信息,那么我们运行一下这个测试剧本,看一下效果,如下:
如图所示,自定义信息已经输出在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的执行效果如下:
变量的名称以及变量的值都输出到了屏幕上,这个功能可以帮助我们调试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}}
变量前包含冒号,如果不使用引号会报语法错误。
上例输出效果如下:
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}}"
上例执行效果如下:
如图所示,我们自定义的信息中包含了远程主机的内存信息,同时被输出了,只是格式上没有手动执行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
中其实包含了 nocache
、real
、 swap
三个部分的信息,如果我们只想获得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]
的任务中已经显示了第一个任务的返回值的信息,返回信息如下:
从上述返回信息可以看出,返回值是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_name
和your_age
,当运行上例playbook时,会出现What is your name
的提示信息,然后用户输入的信息会存入到your_name
变量中,之后,会出现How old are you
的提示信息,用户输入的信息会存入到your_age
变量中,上例中的output vars
任务会输出一句话,这句话中包含了上述两个变量的值,我们来看一下上例的执行效果。
如上图所示,运行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}}"
命令执行效果,如下图:
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命令即可测试出效果:
如上图所示,操作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
根据前文的总结,聪明如你一定已经想到了,我们可以使用如下两种方法引用变量中的值!
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_var1
和test_group_var2
!
组变量的使用范围为组中的所有主机,上例中,无论test181还是test182,都可以使用到上述两个变量,效果如下:
上例为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的下一级,调用组变量的效果如下:
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_hostname
和play_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中还有其他的一些变量的使用方法,但是需要结合其他的一些知识点,所以之后遇到了实际的使用场景,我们再进行介绍!