美文网首页
第4章 Ansible Playbook杂谈

第4章 Ansible Playbook杂谈

作者: super_pcm | 来源:发表于2021-06-14 22:05 被阅读0次

    4.1 再谈Ansible变量

    4.1.1 变量的作用域

    • Global,作用域为全局

      • Ansibl配置文件中定义的变量
      • 环境变量
      • ansible/ansible-playbook 命令行中传进来的变量
    • Play,作用域为Play(一个Playbook由多个Play构成)

      • Play中vaars关键字下的定义饿变量
      • role在文件default/main.yml 和 vars/main.yml 中定义的变量
    • Host,作用域为某个主机

      • 定义在主机清单中变量
      • 主机的系统变量
      • 注册变量

    4.1.2 变量的优先级

    变量优先级由低到高排序:

    • role defaults
    • dynamic inventory variables
    • inventory variables
    • inventory group_vars
    • inventory host_vars
    • playbook group_vars
    • playbook host_vars
    • host facts
    • registered variables
    • set_facts
    • paly vars_prompt
    • play vars_files
    • role variable and include variables
    • block variables
    • tasks variables
    • extra variables

    从上面的排序可以看出,除了role defaults 变量外,其他变量的作用域越小越精确,变量的优先级越高。优先级高的变量能覆盖优先级的变量。

    4.2 使用lookup访问外部文件或者数据库中的数据

    Ansible Playbook允许用户使用自定义的变量,不过当变量过大,或者太复杂时,无论是Playbook中通过vars定义,还是在单独的变量文件中定义,可读性都比较差,而且不够灵活。

    有了lookup就能解决这类难题,lookup既能够读取Ansibe管理节点上文件系统的文件内容到Ansible变量中,也可以读取配置的数据库中的内容。
    下面是lookup的基本使用方法,将Ansible管理节点上文件data/plain.txt的内容读取出来,并赋值给contents。file高速lookup读取对象的类型是File,直接读取文件内容,无需特别的处理。

    ---
    - hosts: all
      vars:
        contents: "{{ lookup('file', 'data/plain.txt')}}"
      tasks:
      - name: show vars
        debug: msg="the value of data/plain.txt is {{ contents }}"
    

    下面具体介绍下lookup的功能。

    4.2.1 lookup 读取文件

    上面的例子使用了file类型的lookup,是最简单的lookup用法。

    4.2.2 lookup生成随机密码

    第一次执行时,如果密码文件不存在,那么lookup会创建一个,如果已经存在了,那就直接使用。

    ---
    - hosts: localhost
      vars:
        password: "{{ lookup('password', '/tmp/password/pcm length=8')}}"
      tasks:
      - name: show password
        debug: var=password
    

    4.2.3 lookup 读取环境变量

    env类型的lookup可以读取Linux上的环境变量,如下:

    - hosts: localhost
      tasks:
      - name: show env vars
        debug: msg="{{ lookup('env', 'HOME')}} is an env variables."
    

    4.2.4 lookup 读取Linux命令行的执行结果

    pipe类型的lookup可以将Linux的执行结果读取到Ansible中:

    ---
    - hosts: localhost
      tasks:
      - name: show env vars
        debug: msg="{{ lookup('pipe', 'data')}} is an env variables."
    

    4.2.5 lookup 读取template变量替换后的文件

    template类型的lookup可以将一个template文件经过变量替换后的文件内容读取到Ansible中。当然,如果template文件中有未定义的变量,则会报错。

    ---
    - hosts: localhost
      vars:
        name: pcm
      tasks:
      - name: show env vars
        debug: msg="{{ lookup('template', 'some_template.j2')}} is an env variables."
    

    4.2.6 lookup 读取配置文件

    lookup 支持读取两种类型的配置文件:ini和Java的properties。

    ini 类型的lookup默认读取配置文件的类型是ini。
    假设我们有个 users.ini 的配置文件,内容如下

    [intergration]
    user=pcm
    

    playbook如下:

    ---
    - hosts: localhost
      tasks:
      - name: show ini file
        debug: msg="User is intergration is {{ lookup('ini', 'user section=intergration file=users.ini') }}"
    

    输出结果:

    TASK [show ini file] ***********************************************************************************************************************************************************************
    ok: [localhost] => {
        "msg": "User is intergrate is pcm"
    }
    

    读取properties类型文件时,需要加一个额外的参数来告诉lookup,这是一个properties类型的文件。

    - hosts: localhost
      tasks:
      - name: show ini file
        debug: msg="user.name is {{ lookup('ini', 'user.name type=properties file=users.properties') }}"
    

    实际上,每个参数都有默认值的,所以在使用 ini类型的lookup是,每个参数都是可选的。默认的参数如下:

    参数名 默认值 参数含义
    type ini 文件的类型
    file ansible.ini 加载文件的名字
    section global 默认在哪个section里面查找key
    re False key的正则表达式
    default empty string key不存在时的返回值

    4.27 lookup 读取 CSV文件的指定单元

    使用的是csvfile类型,貌似没什么用途,略过。

    4.2.8 lookup 读取DNS解析的值

    dig 类型的lookup可以向DNS服务器查询指定域名的DNS记录,它可以查询任何的DNS记录,包括正向查询和方向查询。

    ---
    - hosts: localhost
      tasks:
      - name: show baidu ip
        debug: msg="The IPv4 address for baidu.com. is {{ lookup('dig','baidu.com.') }}"
    

    4.2.9 更多的lookup功能

    参考Ansible的官方文档,有些lookup的功能需要额外的Python包支持。

    4.3 过滤器

    过滤器(filter)是Python模版语言Jinja2提供的模版,可以用来操作数据。它在Ansible的管理节点上执行并操作数据,而不是在远程的目标主机上。

    过滤器和lookup类似,都是在 {{}} 中使用。不同类型的过滤器功能差距很大。过滤器是Ansible使用模版语言Jinja2的内置共。在Ansible中,不仅可以使用jinja2自带的过滤器,还可以使用Ansible提供的过滤器,以及用户自定义的过滤器。

    本节重点介绍Ansible提供的过滤器,Jinja2自带的过滤器请参考jinja2的官方文档。

    4.3.1 过滤器使用的位置

    下面用一个最简单的过滤器来说明过滤器的语法,quote过滤器的功能是给字符串加上引号。

    ---
    - hosts: localhost
      vars:
        test_var: "Test string"
      tasks:
      - name: "quote {{ test_var }}"
        debug: msg="echo {{ test_var |quote }}"
    

    输出的结果如下:

    TASK [quote Test string] ********************************************************************************************************************************************************************
    ok: [localhost] => {
        "msg": "echo 'Test string'"
    }
    
    

    4.3.2 过滤器对普通变量的操作

    1. default: 为没有定义的变量提供默认值
    - name: "变量没有定义的话,输出默认值Default"
      debug: msg="{{ some_undefined_variable | default("Default") }}"
    
    1. omit: 忽略变量的占位符
      与default一起使用时,如果某个变量没有定义,那么使用omit占位符,Ansible就会把这个参数按照没有传这个参数的值来处理。

    文件 /tmp/foo 没有定义参数mode,所以default(moit)会在没有定义mode时忽略mode变量,Ansible的file模块会按照没有传入mode这个参数来创建文件 /tmp/foo. 文件/tmp/baz 定义了mode为0444,所以文件的权限是0444。

    - name: touch files with an optional mode
      file: dest={{ item.path }} state=touch mode={{ item.mode|default(omit) }}
      with_items:
        - path: /tmp/foo
        - path: /tmp/baz
          mode: "0444"
    
    1. mandatory: 强制变量必须定义,否则拋错。
      在Ansible默认的配置中,如果变量没有定义,在直接使用变量的时候会报错。但是如果Ansible配置文件中使用了下面的配置,那么遇到未定义的变量时,Ansible就不会拋错。
    error_on_undefined_vars = False
    

    而这时候如果想要约束某个变量必须定义,就可以使用mandatory。

    --- 
    - hosts: localhost
      tasks:
      - name: show vars
        debug: msg="{{ some_undefined_variable|mandatory }}"
    
    1. bool: 判断变量是否为布尔类型
    --- 
    - hosts: localhost
      vars:
        test_var = True
      tasks:
      - name: show vars
        debug: msg=test
        when: test_var | bool
    
    1. ternary: Playbook的条件表达式
      ternary类似于编程语言中的类型表达式,("A?B:C")当条件为真的时候返回B,为假时返回C。如下:
    --- 
    - hosts: localhost
      vars:
        name = 'pcm'
      tasks:
      - name: "当变量name为pcm时,返回 Mr"
        debug: msg="{{ (name == "pcm" ) |termary('Mr','Ms') }}"
    

    4.3.3 过滤器对文件路径的操作

    Ansible为了方便对文件及其路径进行操作,提供了一系列的文件目录的操作,包含获取文件名、路径名等等。在Linux下,常用的过滤器有:

    • basename: 获取路径中的文件名
    • dirname: 获取文件的目录
    • expanduser: 拓展为实际的目录
    • realpath: 获取链接文件所指文件的真实路径
    • relpath 获取相当于某一目录的相对路径
    • splitext: 把文件用点号分割成多个部分

    使用示例如下:

    - hosts: localhost
      vars:
        linux_path: "/etc/hosts"
      tasks:
      - name: get basename
        debug: msg="{{ linux_path | basename }}"
    

    windows文件路径的操作的过滤器有:

    • win_basename: 获取文件名
    • win_dirname: 获取文件目录
    • win_splitdrive: 把路径分割为多个部分

    示例略过。

    4.3.4 过滤器对字符串变量的操作

    1. quote: 给字符串加引号
      略过,前面已经演示过了

    2. base64: 得到字符串的Base64编码。

    vars:
      name: pcm
    tasks:
    - name: get base64
      debug: msg="{{ pcm | b64encode }}"
    
    1. hash: 获取字符串的哈希值
      计算哈希值的算法很多,如sha1、md5、checksum等。
    vars:
      my_passwd= "pass123"
    tasks:
    - name: "get hash"
      debug: msg="{{ my_passwd |hash('sha1') }}"
    
    1. comment: 把字符串变成代码注释的一部分
      comment有很多风格和格式,在下面的例子中,展示了将字符串转化为不同风格和格式注释的使用方法。
    ---
    - hosts: localhost
      vars:
        my_comment: "This is  a test comment"
      tasks:
      - name: "Simplte comment"
        debug: msg="{{ my_comment | comment }}"
    
      - name: "C style comment"
        debug: msg="{{ my_comment | comment('c') }}"
    

    运行结果如下:

    TASK [Simplte comment] **********************************************************************************************************************************************************************
    ok: [localhost] => {
        "msg": "#\n# This is  a test comment\n#"
    }
    
    TASK [C style comment] **********************************************************************************************************************************************************************
    ok: [localhost] => {
        "msg": "//\n// This is  a test comment\n//"
    }
    
    
    1. regex: 利用正则表达式对字符串进行替换
    ---
    - hosts: localhost
      tasks:
      - name: "convert ansible to able"
        debug: msg="{{ 'ansible' | regex_replace('^a.*i(.*)$','a\\1') }}"
    
    

    运行结果如下:

    TASK [convert ansible to able] **************************************************************************************************************************************************************
    ok: [localhost] => {
        "msg": "able"
    }
    
    6. ip: 判断字符串是否是合法的IP地址
    
    ```yml
    ---
    - hosts: localhost
      tasks:
      - name: "convert ansible to able"
        debug: msg="{{ '192.168.1111.1' |ipaddr }}"
    
    
    1. datetime 将字符串类型的时间转换为时间戳
    tasks:
    - name: "datetime filter"
      debug: msg="{{ ('2021-01-01 11:11:11' | to_datetime) }}"
    

    4.3.5 过滤器对JSON的操作

    1. format: 将变量的值按照JSON/YAML格式输出
    ---
    - hosts: localhost
      tasks:
      - name:  json
        blockinfile: 
          desk: /tmp/test_ansible_filter_formated
          block: {{ some_variable | to_json }}
    

    ...还有一些用法不大清楚,后面再去了解

    4.3.6 过滤器对数据结构的操作

    Ansible中的过滤器支持以下几种类型的数据结构的操作。

    1. random: 取随机数
      取随机数的操作比较常见,random既支持从List中取随机的元素,也支持生成一个随机的数字。
    ---
    - hosts: localhost
      tasks:
      - name: "List with random"
        debug: msg="{{ ['a','b','c'] | random }}"
      
      - name: "number with random"
        debug: msg="{{  59 |random}}"
      
      - name: "random with step"
        debug: msg="{{ 100 |random(step=10) }}"
    

    执行的结果如下:

    TASK [List with random] *********************************************************************************************************************************************************************
    ok: [localhost] => {
        "msg": "b"
    }
    
    TASK [number with random] *******************************************************************************************************************************************************************
    ok: [localhost] => {
        "msg": "9"
    }
    
    TASK [random with step] *********************************************************************************************************************************************************************
    ok: [localhost] => {
        "msg": "50"
    }
    
    1. 对List的操作有:
    • min: 取最小值
    • max: 取最大值
    • join: 将List的所有元素连接成一个新的字符串
    • shuffle: 将List做顺序打乱成一个新的List
    • map: 实现对List的映射操作
    1. 对Set的操作有如下的过滤器
    • unique: 去重复的元素
    • union: 交集
    • differentce: 差集
    • symmetric_difference: 取对称差

    4.3.7 过滤器的链式/连续使用

    Ansible的过滤器是支持链式使用的,即在一个{{}} 中使用多个过滤器,就像下面的这样。

    - name: "Use multiple filter in chain"
      debug: msg="{ [0,2}]| map('extract', ['x','y','z'])|join('|') }}"
    

    在上面的例子中,先用map操作得到['x','z'],再用join得到字符串'x|z'。

    4.4 测试变量或者表达式是否符合条件

    4.4.1 测试字符串

    match 和 search 是用于测试字符串是否符合某一个正则表达式的测试。其中 match 是完全匹配,search 只需要部分匹配。

    ---
    - hosts: localhost
      vars:
        url: "http://pangcm.com/users/foo/resources/bar"
      tasks:
      - name: show debug
        debug: "msg='matched pattern 1'"
        when: url is match("http://pangcm.com/users/.*/resources/.*")
    
      - name: show search debug
        debug: "msg='matched pattern 2'"
        when: url is search("/users/.*/resources/.*")
    

    4.4.2 比较版本

    使用 version (旧版本使用version_compare) 类型的测试来实现版本的比较

    ---
    - hosts: localhost
      vars:
        version1: "7.8.0"
      tasks:
      - name: echo the version when it newer than 7.8.0
        debug: msg=" {{ version1 is version('7.8.0', '>=') }}"
    

    4.4.3 测试List的包含关系

    issuperset 和 issubset 可以分别测试List是否被包含或者包含另外一个List

    ---
    - hosts: localhost
      vars: 
        a: [1,2,3]
        b: [1,2]
      tasks:
      - name: show debug
        debug: msg='A include B'
        when: a is superset(b)
      - name: show debug
        debug: msg='A include B'
        when: b is subset(a)
    

    4.4.4 测试文件路径

    ---
    - hosts: localhost
      vars:
        mypath: "/etc"
      tasks:
        - debug: msg="path is a directory"
          when: mypath is directory
      
        - debug: msg="path is a fle"
          when: mypath  is file
      
        - debug: msg="path is a symlink"
          when: mypath is link
      
        - debug: msg="path is exists"
          when: mypath is exists
    

    4.4.5 测试任务的执行结果

    Ansible提供了一系列的测试,可以用来判断任务的执行结果

    ---
    - hosts: localhost
      tasks:
      - name: exec shell
        shell: /usr/bin/false
        register: result
        ignore_errors: True
    
      - name: show exec result
        debug: msg="it failed"
        when: result is failed
    
      - name: show exec result
        debug: msg="it changed"
        when: result is changed
    
      - name: show exec result
        debug: msg="it succeed"
        when: result is succeeded
    
      - name: show exec result
        debug: msg="it skip"
        when: result is skipped
    

    执行结果:

    TASK [exec shell] ***************************************************************************************************************************************************************************
    fatal: [localhost]: FAILED! => {"changed": true, "cmd": "/usr/bin/false", "delta": "0:00:00.008272", "end": "2021-06-13 10:44:14.449719", "msg": "non-zero return code", "rc": 1, "start": "2021-06-13 10:44:14.441447", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
    ...ignoring
    
    TASK [show exec result] *********************************************************************************************************************************************************************
    ok: [localhost] => {
        "msg": "it failed"
    }
    
    TASK [show exec result] *********************************************************************************************************************************************************************
    ok: [localhost] => {
        "msg": "it changed"
    }
    
    TASK [show exec result] *********************************************************************************************************************************************************************
    skipping: [localhost]
    
    TASK [show exec result] *********************************************************************************************************************************************************************
    skipping: [localhost]
    
    PLAY RECAP **********************************************************************************************************************************************************************************
    localhost                  : ok=4    changed=1    unreachable=0    failed=0    skipped=2    rescued=0    ignored=1   
    
    

    4.5 认识插件

    Ansible插件(plugin)并不像模块一样有统一出现的位置和相似的使用方法。它只是对Ansible功能的补充。Ansible插件会因类型的不同而使用不同的方法。如果想对上面提到的lookup写更多的插件,使其功能更加丰富,那么应该使用lookup插件,语法结构如下。

    {{ lookup('new_lookup_plugin', "paramters") }}
    

    这部分还是略过吧,感觉用处不大。

    相关文章

      网友评论

          本文标题:第4章 Ansible Playbook杂谈

          本文链接:https://www.haomeiwen.com/subject/qkzgsltx.html