Ansible_Grammar - QLGQ/learning-python GitHub Wiki

获取远程主机系统信息

Facts是一个非常有用的组件,实现获取远程主机的系统信息,包括主机名、IP地址、操作系统、分区信息、硬件信息等,可以配合playbook实现更加个性化、灵活的功能需求,比如在httpd.conf模板中引用Facts的主机名信息作为ServerName参数的值。通过运行ansible hostname -m setup可获取Facts信息,例如,获取192.168.1.21的Facts信息需运行:ansible 192.168.1.21 -m setup。

在模板文件中这样引用Facts信息:

{{ ansible_devices.sda.model }}
{{ ansible_hostname }}

变量

在实际应用场景中,我们希望一些任务、配置根据设备性能的不同而产生差异,比如使用本机CPU核数动态配置Nginx的worker_processes参数,可能有一组主机的应用配置文件几乎相同,但略有不同的配置项可以引用变量。在ansible中使用变量的目的是方便处理系统之间的差异。

变量名的命名规则由字母、数字和下划线组合而成,变量必须以字母开头。

Jinja2过滤器

Jinja2是Python下一个广泛应用的模板引擎,它的设计思想类似于Django的模板引擎,并扩展了其语法和一系列强大的功能,官网地址:http://jinja.pocoo.org/。下面介绍一下ansible使用Jinja2强大的过滤(Filter)功能。

使用格式:{{ 变量名|过滤方法 }}
下面是实现获取一个文件路径变量过滤出文件名的一个示例:{{ path|basename }}。获取文件所处的目录名:{{ path|dirname }}。
下面为一个完整的示例,实现从“/etc/profile”中过滤出文件名“profile”,并输出重定向到/tmp/testshell文件中。

---
- hosts: 192.168.1.21
  vars:
    filename: /etc/profile
  tasks:
    - name: "shell1"
      shell: echo {{ filename|basename }} >> /tmp/testshell

本地Facts

我们可以通过Facts来获取远程目标主机的系统信息,当这些信息还不能满足我们的功能需求时,可以通过编写自定义的Facts模块来实现。当然,还有一个更简单的实现方法,就是通过本地Facts来实现。只需在目标设备/etc/ansible/facts.d目录定义JSON、INI或可执行文件的JSON输出,文件扩展名要求使用“.fact”,这些文件都可以作为ansible的本地Facts,例如,在目标设备192.168.1.21定义三个变量,供以后playbook进行引用。

【/etc/ansible/facts.d/preferences.fact】

[general]
max_memory_size=32
max_user_processes=3730
open_files=65535

在主控端运行ansible 192.168.1.21 -m setup -a "filter=ansible_local"可看到定义的结果。 注意返回JSON的层次结构,preferences(facts文件名前缀) --> general(INI的节名) --> key:value(INI的键与值),最后就可以在我们的模板或playbook中通过以下方式进行调用:{{ ansible_local.preferences.general.open_files }}`。

注册变量

变量的另一个用途是将一条命令的运行结果保存到变量中,供后面的playbook使用。下面是一个简单的示例:

- hosts: web_servers
  tasks:
    - shell: /usr/bin/foo
      register: foo_result
      ignore_errors: True
    - shell: /usr/bin/bar
      when: foo_result.rc == 5

上面示例注册了一个foo_result变量,变量值为shell:/usr/bin/foo的运行结果,ignore_errors:True为忽略错误。变量注册完成后,就可以在后面playbook中使用了,当条件语句when: foo_result.rc == 5成立时,shell: /usr/bin/bar命令才会运行,其中foo_result.rc为返回/usr/bin/foo的resultcode(返回码)。

条件语句

有时候我们想跳过某些主机的执行步骤,比如符合特定版本的操作系统将不安装某个软件包,或者磁盘空间爆满了将进行清理的步骤。在ansible中很容易做到这一点,通过when子句实现,其中将引用Jinja2表达式。下面是一个示例:

tasks:
  - command: /bin/false
    register: result
    ignore_errors: True
  - command: /bin/something
    when: result|failed
  - command: /bin/something_else
    when: result|success
  - command: /bin/still/something_else
    when: result|skipped

“when: result|success”的意思为当变量result执行结果为成功状态时,将执行/bin/something_else命令,其他同理,其中success为ansible内部过滤器方法,返回True代表命令运行成功。

循环

通常一个任务会做很多事情,如创建大量的用户、安装很多包,或重复轮询特定的步骤,直到某种条件结果为止,ansible为我们提供了此支持。下面是一个简单的示例:

- name: add several users
  user: name={{ item }} state=present groups=wheel
  with_items:
    - testuser1
    - testuser2

这个示例实现了一个批量创建系统用户的功能,with_items会自动循环执行上面的语句“user: name={{ item }} state=present groups=wheel”,循环的次数为with_items的元素个数,这里有两个元素,分别为testuser1、testuser2,会分别替换{{ item }}项。这个示例与下面的示例是等价的:

- name: add user testuser1
  user: name=testuser1 state=present groups=wheel
- name: add user testuser2
  user: name=testuser2 state=present groups=wheel

当然,元素也支持字典的形式,如下:

- name: add several users
  user: name={{ item.name }} state=present groups={{ item.groups }}
  with_items:
    - { name: 'testuser1', groups: 'wheel' }
    - { name: 'testuser2', groups: 'wheel' }

循环也支持列表(List)的形式,不过是通过with_flattened语句来实现的,例如:

----
# file: roles/foo/vars/main.yml
packages_base:
  - [ 'foo-package', 'bar-package' ]
packages_apps:
  - [ ['one-package', 'two-package'] ]
  - [ ['red-package', 'blue-package'] ]

以上定义了两个列表变量,分别是需要安装的软件包名,以便后面进行如下引用:

- name: flattened loop demo
  yum: name={{ item }} state=installed
  with_flattened:
    - packages_base
    - packages_apps