@@ -103,3 +103,216 @@ ok: [localhost] => {
103
103
104
104
# ## 找出挂载点
105
105
106
+
107
+ 在这个示例中,我们打算找出所有机器上,某个指定路径的挂载点,由于我们已经收集了挂载事实,因此我们可使用下面的方法:
108
+
109
+
110
+ ` ` ` yaml
111
+ ---
112
+ - hosts: all:!win10-133
113
+ gather_facts: yes
114
+ vars:
115
+ path: /boot
116
+
117
+ tasks:
118
+ - name: The mount point for {{path}}, found using the Ansible mount facts, [-1] is the same as the 'last' filter
119
+ ansible.builtin.debug:
120
+ msg: "{{(ansible_facts.mounts | selectattr('mount', 'in', path) | list | sort(attribute='mount'))[-1]['mount']}}"
121
+ ` ` `
122
+
123
+ > **译注**:可以看到,在任务的 `name` 字段中,也可以使用模板插值语法。
124
+
125
+
126
+ # ## 略去列表中的一些元素
127
+
128
+
129
+ 这个特殊的 `omit` 变量,只适用于模组的选项,但咱们仍可以其他方式,将其用作其他裁剪某个元素清单的标识符:
130
+
131
+
132
+ *在喂给某个模组选项数据时的内联列表过滤器*
133
+
134
+
135
+ ` ` ` yaml
136
+ - name: Enable a list of Windows features, by name
137
+ ansible.builtin.set_fact:
138
+ win_feature_list: "{{ namestuff | reject('equalto', omit) | list }}"
139
+ vars:
140
+ namestuff:
141
+ - "{{ (fs_installed_smb_v1 | default(False)) | ternary(omit, 'FS-SMB1') }}"
142
+ - "foo"
143
+ - "bar"
144
+ ` ` `
145
+
146
+ 另一种方法是避免在列表开头添加元素,这样咱们就可以直接使用,another way is to avoid adding elements to the list in the first place, so you can just use it directly:
147
+
148
+
149
+ *在循环中使用 `set_fact` 有条件地递增某个列表*
150
+
151
+
152
+ ` ` ` yaml
153
+ - name: Build unique list with some items conditionally omitted
154
+ ansible.builtin.debug:
155
+ msg: ' {{ (namestuff | default([])) | union([item]) }}'
156
+ when: item != omit
157
+ loop:
158
+ - "{{ (fs_installed_smb_v1 | default(False)) | ternary(omit, 'FS-SMB1') }}"
159
+ - "foo"
160
+ - "bar"
161
+ ` ` `
162
+
163
+
164
+ # ## 合并同一字典列表中的值
165
+
166
+
167
+ 结合上述示例中的正向与负向过滤器,咱们就可以得到 “确实存在的值”,以及当不存在时的 “回退值”。
168
+
169
+
170
+ *使用 `selectattr` 和 `rejectattr` 获取获取所需的 `ansible_host` 或 `inventory_hostname`*
171
+
172
+ ` ` ` yaml
173
+ - hosts: localhost
174
+
175
+ tasks:
176
+ - name: Check hosts in inventory that respond to ssh port
177
+ wait_for:
178
+ host: "{{ item }}"
179
+ port: 22
180
+ loop: '{{ has_ah + no_ah }}'
181
+ vars:
182
+ has_ah: '{{ hostvars|dictsort|selectattr("1.ansible_host", "defined")|map(attribute="1.ansible_host")|list }}'
183
+ no_ah: '{{ hostvars|dictsort|rejectattr("1.ansible_host", "defined")|map(attribute="0")|list }}'
184
+ ` ` `
185
+
186
+ # ## 根据某个变量定制 `fileglob`
187
+
188
+ 下面这个示例使用了 [Python 参数列表解包语法](https://docs.python.org/3/tutorial/controlflow.html#unpacking-argument-lists),以根据某个变量,创建出一个自定义的 `fileglob` 列表。
189
+
190
+
191
+ ` ` ` yaml
192
+ - hosts: webservers
193
+ gather_facts: no
194
+
195
+ vars:
196
+ mygroups:
197
+ - prod
198
+ - web
199
+
200
+ tasks:
201
+ - name: Copy a glob of files based on a list of groups
202
+ copy:
203
+ src: "{{ item }}"
204
+ dest: "/tmp/{{ item }}"
205
+ loop: '{{ q("fileglob", *globlist) }}'
206
+ vars:
207
+ globlist: '{{ mygroups | map("regex_replace", "^(.*)$", "files/\1 /*.conf") | list }}'
208
+ ` ` `
209
+
210
+ > **译注**:这个示例中,`globlist` 的值如下:
211
+
212
+ ` ` ` json
213
+ [
214
+ "files/prod/*.conf",
215
+ "files/web/*.conf"
216
+ ]
217
+ ` ` `
218
+
219
+ > `q("fileglob", *globlist)` 将查找出 playbook 所在目录下,与 `globlist` 匹配的文件。
220
+
221
+
222
+ # # 复杂的类型转换
223
+
224
+
225
+ Jinja 提供了简单数据类型转换(`int`、`bool` 等)的过滤器,但当咱们想转换某些数据结构时,事情就没那么简单了。咱们可以使用循环和列表综合(如上示)来帮忙,也可使用一些其他可以链接起来的过滤器和查找,实现更复杂的转换。
226
+
227
+
228
+ # ## 从列表创建字典
229
+
230
+ 在大多数语言中,从成对的列表,创建出字典(也称为映射/关联数组/哈希表,map/associative array/hash,等)都不难。在 Ansible 中,有几种方法可以做到这点,而最适合咱们的方法,则可能取决于咱们的数据源。
231
+
232
+
233
+ 下面这两个示例,会产生出 `{"a" : " b" , "c": "d"}` 这个字典。
234
+
235
+
236
+ *假定列表为 `[key, value, key, value, ...]` 形式下的简单列表到字典转换*
237
+
238
+
239
+ ` ` ` yaml
240
+ vars:
241
+ single_list: [ 'a', 'b', 'c', 'd' ]
242
+ mydict: "{{ dict(single_list[::2] | zip_longest(single_list[1::2])) }}"
243
+ ` ` `
244
+
245
+ *当咱们有个成对的列表时,就更简单*:
246
+
247
+
248
+ ` ` ` yaml
249
+ vars:
250
+ list_of_pairs: [ ['a', 'b'], ['c', 'd'] ]
251
+ mydict: "{{ dict(list_of_pairs) }}"
252
+ ` ` `
253
+
254
+
255
+ 两者的最终结果一样,`zip_longest` 将 `single_list`,转换为一个 `list_of_pairs` 的生成器。
256
+
257
+
258
+ 稍微复杂一些,使用 `set_fact` 和 `loop`,从来自 2 个列表的键值对创建/更新出一个字典:
259
+
260
+ *使用 `set_fact` 从列表集合创建出一个字典*
261
+
262
+ ` ` ` yaml
263
+ - name: Uses 'combine' to update the dictionary and 'zip' to make pairs of both lists
264
+ ansible.builtin.set_fact:
265
+ mydict: "{{ mydict | default({}) | combine({item[0]: item[1]}) }}"
266
+ loop: "{{ (keys | zip(values)) | list }}"
267
+ vars:
268
+ keys:
269
+ - foo
270
+ - var
271
+ - bar
272
+ values:
273
+ - a
274
+ - b
275
+ - c
276
+ ` ` `
277
+
278
+ 这会得到 `{"foo" : " a" , "var": "b", "bar": "c"}`。
279
+
280
+ 咱们甚至可以将这些简单示例与其他筛选器和查找插件结合,通过变量名模式匹配,动态地创建出字典:
281
+
282
+
283
+ ` ` ` yaml
284
+ vars:
285
+ xyz_stuff: 1234
286
+ xyz_morestuff: 567
287
+ abc_stuff: 890
288
+ myvarnames: "{{ q('varnames', '^xyz_') }}"
289
+ mydict: "{{ dict(myvarnames|map('regex_replace', '^xyz_', '')|list | zip(q('vars', *myvarnames))) }}"
290
+ ` ` `
291
+
292
+ 简单解释一下,因为从这两行中可以看出很多东西:
293
+
294
+ - 其中的 `varnames` 查找,会返回一个匹配 “以 `xyz_` 开头” 的变量列表;
295
+ - 随后将上一步的列表,喂给其中的 `vars` 查找,以获得值的列表。`*` 用于 “解引用列表”(一种在 Jinja 中有效的 python 机制),否则他将把该列表作为单一参数;
296
+ - 两个列表都会传递给 `zip` 过滤器,将他们配对成一个统一列表`(key、value、key2、value2...)`;
297
+ - 随后 `dict` 函数会利用这个 “配对列表”,创建出字典。
298
+
299
+ 下面是个如何使用事实,找出某个满足条件 X 的主机数据:
300
+
301
+ ` ` ` yaml
302
+ vars:
303
+ uptime_of_host_most_recently_rebooted: "{{ansible_play_hosts_all | map('extract', hostvars, 'ansible_uptime_seconds') | sort | first}}"
304
+ ` ` `
305
+
306
+
307
+ 下面是个以 天/小时/分钟/秒 为单位,显示某个主机运行时间的示例(假设事实已收集)。
308
+
309
+
310
+ ` ` ` yaml
311
+ - name: Show the uptime in days/hours/minutes/seconds
312
+ ansible.builtin.debug:
313
+ msg: Uptime {{ now().replace(microsecond=0) - now().fromtimestamp(now(fmt='%s') | int - ansible_uptime_seconds) }}
314
+ ` ` `
315
+
316
+ (End)
317
+
318
+
0 commit comments