fdhfgh 发表于 2018-1-2 23:17:03

ansible 番外篇之变量与fact

变量与 fact
  在 Ansible 中, 变量的作用域是按照主机划分的, 只有针对特定主机讨论变量的值才有意义.

1. 变量

1.1. 定义变量
  vars : 定义变量的列表或字典
  
vars_file : 指定 定义变量的文件列表
  vars 区段的定义, 实际上是在 当前 play 中针对一组主机定义了变量, 但 Ansible 实际做法其实时, 对这个群组的每一个主机创建一个变量的副本.
  ansible 允许定义于主机或群组有关的变量, 这些变量可以定义在 inventory 文件中, 也可以定义在与 inventory 文件放在一起的独立文件中.
  Ansible 变量定义位置

变量标识
描述
vars
playbook 区段, 为字典列表
vars_file
playbook 区段, 为指向文件的列表
host_vars
目录, 主机变量
group_vars
目录, 群组变量
主机变量
inventory中, 单独针对主机的变量
群组变量
inventory中, 单独针对单个群组的变量
1.2. 显示变量: debug 模块
  

- debug: var=myvarname  

1.3. register 注册变量: 基于 task 的执行结果, 设置变量的值.
  示例:
  

- name: Run MyProg  command: /opt/myprog
  register: result
  ignore_errors: True
  

  
- debug: var=result
  

  ignore_errors 语句, 可以实现, 在 task 失败的时候, 是否忽略错误, 继续执行下面的 task, 默认为 False.
  访问变量中字典的key, 有两种方式:


[*]{{ login.stdout }}
[*]{{ ansible_eth1["ipv4"]["address"] }}
  当 task 在目标主机, 没有执行命令时, 即当目标主机已经符合目标结果时, 输出中没有 stdout,stderr,stdout_lines 三个键值.
  如果在 playbook 中使用了注册变量, 那么无论模块是否改变了主机的状态, 请确保你了解变量的内容, 否则, 当你的 playbook 尝试访问注册变量中不存的 key时, 可能会导致失败.

1.4. set_fact 定义新变量
  使用 set_fact 模块在 task 中设置 fact(与定义一个新变量是一样的). 可以在 register 关键字后, 立即使用 set_fact , 这样使得变量引用更简单.
  

- name: get snapshot>shell: >  aws ec2 describe-snapshot --filters Name=tag:Name, Valuse=my-snapshot | jq --raw-outpuy ".Snapshots[].SnapshtId"
  register: snap_result
  
- set_fact: snap={{ snap_result.stdout }}
  

  
- name: delete old snapshot
  command: aws ec2 delete-snapshot --snapshot-id "{{ snap }}"
  

1.5. 内置变量

参数
说明
hostvars
字典, key 为 Ansible 主机的名字, value 为所有变量名与相应变量值映射组成的字典
inventory_hostname
当前主机被 Ansible 识别的名字, 如果定义了别名, 则为别名.
group_names
列表, 由当前主机所属的所有群组组成
groups
字典, key 为 ansible 群组名, value 为群组成员的主机名所组成的列表. 包括 all 分组和 ungrouped 分组
play_hosts
列表, 成员是当前 play 涉及的主机的 inventory 主机名.
ansible_version
字典, 由 Ansible 版本信息组成.

[*]  hostvars :
  在 Ansible 中, 变量的作用域是按照主机划分的, 只有针对特定主机讨论变量的值才有意义.
  有时候 , 针对一组主机定义的变量, 该变量实际始于特定的主机相关联的.
  
例如 vars 区段的定义, 实际上是在 当前 play 中针对一组主机定义了变量, 但 Ansible 实际做法其实时, 对这个群组的每一个主机创建一个变量的副本.
  hostvars变量包含了在所有主机上定义的所有变量, 并以 ansible 识别的主机名作为 key. 如果 Ansible 还未对主机采集 fact, 那么除非启动 fact 缓存, 否则无法使用 hostvars 访问fact.
  有时, 在某一个主机上运行的 task 可能会需要在另一台主机上定义的变量. 例如, web 服务器, 可能需要 数据库服务器的 ansible_eth1.ipv4.address 这个 fact. 如果 数据库服务器为 db.example.com, 那么, 其变量引用为:
  

{{ hostvars['db.example.com'].ansible_eth1.ipv4.address }}  

  - debug: var=hostvars : 输出与当前主机相关联的所有变量.

[*]  groups :
  代表当前 inventory 所定义的所有组的集合, 为一个字典.
  示例: web 负载均衡配置文件
  

backend web-backend  
{% for host in groups.web %}
  server {{ host.inventory_hostname }} {{ host.ansible_default_ipv4.address }}:80
  
{% endfor %}

1.6. 在命令行设置变量
  向 ansible-playbook 传入 -e var=value 参数设置变量或传递参数, 有最高优先级. 可以覆盖已定义的变量值.
  

$ ansible-playbook example.yml -e token=123456  

  希望在变量中出现空格, 需要使用引号:
  

$ ansible-playbook playbooks/greeting.yml -e 'greeting="Oops you have another hello world"'  

  @filename.yml 传递参数:
  

$ cat greetvars.yml  greeting: "ops you have another hello world"
  

  
$ ansible-playbook playbooks/greeting.yml -e @greetvars.yml
  

2. fact
  当 Ansible 采集 fact 的时候, 他会连接到目标主机收集各种详细信息: CPU 架构,操作系统,IP地址,内存信息,磁盘信息等. 这些信息保存在被称为 fact 的变量中. fact 与其他变量的行为一模一样.

2.1. setup 模块
  

实现 fact 收集的模块. 一般无需再 playbook 中调用该模块, Ansible 会在采集 fact 时, 自动调用.  

  
`$ ansible server_name -m setup -a 'filter=ansible_eth*'`
  

  
其返回值为一个字典, 字典的 key 是 ansible_fact, 他的 value 是一个有实际 fact 的名字与值组成的字典.
  

  
setup 模块支持 filter 参数, 可以实现 shell 通配符的匹配过滤.
  

2.2. 模块返回 fact
  

如果一个模块返回一个字典且包含名为 ansible_facts 的key, 那么 ansible 将会根据对应的 value 创建响应的变量, 并分配给相对应的主机. 对于返回 fact 的模块, 并不需要使用注册变量, 因为 ansible 会自动创建.  

  
可以自动返回 fact 的模块: $ ansible-doc --list |grep facts
  
- ec2_facts
  
- docker_image_facts
  

2.3. 本地 fact
  

可将一个或者多个文件放置在目标主机的 /etc/ansible/facts.d/ 目录下, 如果该目录下的文件以 init格式, JSON格式 或者输出JSON格式的可执行文件(无需参数), 以这种形式加载的 fact 是 ansible_local 的特殊变量.  

  
示例:
  

  # 目标主机
  $ /etc/ansible/facts.d/books.fact

  title=Ansible: Up and Running
  author=Lorin Hochstein
  publisher=P'Reilly Media
  

  # ansible 主机
  $ cat playbooks/local.yml
  

  - name: get local variables
  hosts: host_c
  gather_facts: True
  tasks:
  - name: print local variables;
  debug: var=ansible_local

  - name: print book>
  debug: msg="The Book>  

  
注意 ansible_local 变量值的结构, 因为 fact 文件的名称为 books, 所以 ansible_local 变量是一个字典, 且包含一个名为 "books" 的 key.
  

3. 变量优先级:
  以下优先级依次降低


[*]  命令行参数

[*]  其他

[*]  通过 inventory 文件或 YAML 文件定义的主机变量或群组变量

[*]  Fact

[*]  在 role 的 defaults/mail.yml 文件中的变量.

4. 过滤器: 变量加工处理
  Ansible 除了使用 Jinja2 作为模板之外, 还将其用于变量求值. 即, 可以在 playbook 中在 {{}} 内使用过滤器.
  
除了可以用 Jinja2 的内置过滤器外, Ansible 还有一些自己扩展的过滤器.
  有些参数, 需要参数, 有些则不需要.
  Jinja2 内置过滤器
  
Ansible 过滤器

4.1. default : 设置默认值.
  

# 设置 HOST 变量的默认值, 如果 database 没有被定义, 则使用 localhost .  
"HOST": "{{ database | default('localhost') }}"
  

4.2. 用于注册变量的过滤器
  对注册变量状态检查状态的过滤器

名称
描述
failed
如果注册变量的值是任务 failed , 则返回 True
changed
如果注册变量的值是任务 changed , 则返回 True
success
如果注册变量的值是任务 success , 则返回 True
skipped
如果注册变量的值是任务 skipped , 则返回 True  示例:
  

- name: Run myprog  command: /opt/myprog
  register: result
  ignore_errors: True
  
- debug: var=result
  
- debug: msg="Stop Running the playbook if myprog failed"
  failed_when: result|failed
  

4.3. 用于文件路径的过滤器
  用于处理包含控制主机文件系统的路径的变量.

过滤器
描述
basename
文件路径中的目录
dirname
文件路径中的目录
expanduser
将文件路径中的 ~ 替换为用户家目录
realpath
处理符号链接后的文件实际路径  示例:
  

vars:  homepages: /usr/share/nginx/html/index.html
  
tasks:
  - name: copy home page
  copy: src=files/{{ homepages| basename }} desc={{ homepages }}
  

4.4. 自定义过滤器
  Ansible 会在存放 playbook 的目录下的 filter_plugins 目录中寻找自定义过滤器. 也可以放在 /usr/share/ansible_plugins/filter_plugins/ 目录下, 或者 环境变量ANSIBLE_FILTER_PLUGINS 环境变量设置的目录.
  

# filter_plugins/surround_by_quotes.py  
def surround_by_quote(a_list):
  return ['"%S"' % an_element for an_element in a_list]
  

  
class FilterModule(object):
  def filters(self):
  return {'surround_by_quote': surround_by_quote}
  

  surround_by_quote 函数定义了 Jinja2 过滤器.
  
FilterModule 类定义了一个 filter 方法, 该方法返回由过滤器名称和函数本身组成的字典. FilterModule 是 Ansible 相关代码, 他使得 Jinja2 过滤器可以再 Ansible 中使用.

5. lookup: 从多种来源读取配置数据.
  lookup 官方文档说明
  
Ansible 所有的 lookup 插件都是在控制主机, 而不是远程主机上执行的
  支持的数据来源表:

名称
描述
file
文件的内容
password
随机生成密码
pipe
本地命令执行的输出
env
环境变量
template
Jinja2 模板渲染的结果
csvfile
.csv 文件中的条目
dnstxt
DNS 的 TXT 记录
redis_ke
对 Redis 的key 进行查询
etcd
对 etcd 中的key 进行查询

[*]  file
  示例: 在 playbook 中调用 lookup
  

- name: Add my public key as an EC2 key  ec2_key: name=mykey key_material="{{ lookup('file', '/home/me/.ssh/id_rsa.pub') }}"
  

  示例: 使用 Jinja2 模板
  

# authorized_keys.j2  
{{ lookup('file', '/home/me/.ssh/id_rsa.pub') }}
  

  
# playbook
  
- name: copy authorized_host file
  template: src=authorized_keys.j2 desc=/home/deploy/.ssh/authorized_keys

[*]  pipe
  在控制主机上调用一个外部程序, 并将这个程序的输出打印到标准输出上.
  示例: 得到最新的 git commit 使用的 SHA-1 算法的值.
  

- name: get SHA of most recent commit  debug: msg="{{ lookup('pipe', 'git rev-parse HEAD') }}"

[*]  env
  获取在控制主机上的某个环境变量的值.
  示例:
  

- name: get the current shell  debug: msg="{{ lookup('env', 'SHELL') }}"

[*]  password
  随机生成一个密码, 并将这个密码写入到参数指定的(控制主机)文件中.
  示例: 生成 deploy 的 Postgre 用户和密码, 并将密码写入到 deploy-password.txt 中:
  

- name: create deploy postgre user  postgresql_user:
  name: deploy
  password: "{{ lookup('password', 'deploy-password.txt') }}"

[*]  template
  指定一个 Jinji2 模板文件, 并返回这个模板渲染的结果.
  

# message.j2  
This host runs {{ ansible_distribution }}
  

  
# task
  
- name: output message from template
  debug: msg="{{ lookup('template', 'message.j2') }}"

[*]  csvfile
  从 csv 文件中读取一个条目.
  

# users.csv  
username, email
  
lorin, lorin@example.com
  
john, john@example.com
  
sue, sue@example.com
  

  
# 调用 : 查看名为 users.csv 的文件, 使用逗号作为分隔符来定位区域, 寻找第一列的值是 sue 的那一行, 返回第二列(索引从 0 开始)的值.
  
lookup('csvfile', 'sue file=users.csv delimiter=, col=1') --> sue@example.com
  

  
# 用户名被存储在 username 变量中, 可以用 "+" 连接其他参数, 构建完整的参数字符串.
  
lookup('csvfile', username + 'file=users.csv delimiter=, col=1')

[*]  dnstxt
  需要安装 dnspython 包, $ pip install dnspython
  TXT 记录是 DNS 中一个可以附加在主机名上的任意字符串, 一旦为主机名关联了一条 TXT 记录, 则任何人都可以使用 DNS 客户端获取这段文本.
  

# 使用 dig 查看 TXT 记录  
$ dig +short ansiblebook.com TXT
  
"isbn=97801491915325"
  

  
# task
  
- name: look up TXT record
  debug: msg="{{ lookup('dnstxt', 'ansiblebook.com') }}"

[*]  redis-kv
  需要安装 redis 包: $ pip install pip
  
可以使用 redis-kv 获取一个 key 的value, key 必须为字符串.
  

# 设置一个值:  
$ redis-cli SET weather sunny
  

  
# task
  
- name: look up value in redis
  debug: msg="{{ lookup('redis_kv', 'redis://localhost:6379,weather') }}"

[*]  etcd
  etcd lookup 默认在 http://127.0.0.1:4001 上查找 etcd 服务器, 可以在执行 ansible-playbook 之前, 通过设置 ANSIBLE_ETCD_URL 改变这个值.
  

# 设置测试值  
$ curl -L http://127.0.0.1:4001/v2/keys/weather -XPUT -d value=cloudy
  

  
# task
  
- name: loop up value in etcd
  debug: msg="{{ lookup('etcd', 'weather') }}"

页: [1]
查看完整版本: ansible 番外篇之变量与fact