Ansible如何使用lookup插件模板化外部数据

Posted 山河已无恙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Ansible如何使用lookup插件模板化外部数据相关的知识,希望对你有一定的参考价值。

写在前面


  • 今天和小伙伴分享使用lookup插件模板化外部数据
  • 博文内容比较简单
  • 主要介绍的常用lookup插件和对应的Demo
  • 外部数据如何代替cat等通过lookup插件读取
  • 理解不足小伙伴帮忙指正
  • 食用方式:了解Ansible基础语法

运维箴言:重启试试


lookup插件

lookup 插件是 Jinja2 模板化语言的 Ansible 扩展。这些插件使 Ansible 能够使用外部来源的数据,如文件和Shell 环境

默认的Ansible安装中有几十个可用的插件。ansible-doc-t lookup -l,获取可用查找插件的完整列表。

[root@vms81.liruilongs.github.io]$ ansible-doc -t lookup -l
aws_account_attribute Look up AWS account attributes
aws_secret            Look up secrets stored in AWS Secrets Manager
aws_service_ip_ranges Look up the IP ranges for services provided in AWS such as EC2 and S3
aws_ssm               Get the value for a SSM parameter or all parameters under a path
cartesian             returns the cartesian product of lists

可以运行 ansible-doc -t lookup PLUGIN_NAME 命令。我们随便看一个模块

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ansible-doc -t lookup vars
> VARS    (/usr/lib/python2.7/site-packages/ansible/plugins/lookup/vars.py)

        Retrieves the value of an Ansible variable.

  * This module is maintained by The Ansible Community
OPTIONS (= is mandatory):

= _terms
        The variable names to look up.
        ......

嗯,获取一个Ansible变量的值,顺便研究下代码怎么写

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$cat /usr/lib/python2.7/site-packages/ansible/plugins/lookup/vars.py
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

## 一些模块的说明
DOCUMENTATION = """
    lookup: vars
    author: Ansible Core
    version_added: "2.5"
    short_description: Lookup templated value of variables
    description:
      - Retrieves the value of an Ansible variable.
    options:
      _terms:
        description: The variable names to look up.
        required: True
      default:
        description:
            - What to return if a variable is undefined.
            - If no default is set, it will result in an error if any of the variables is undefined.
"""
## 模块使用方法
。。。。

RETURN = """
_value:
  description:
    - value of the variables requested.
"""

from ansible.errors import AnsibleError, AnsibleUndefinedVariable
from ansible.module_utils.six import string_types
from ansible.plugins.lookup import LookupBase


class LookupModule(LookupBase):
    ##只有一个方法,接收所有参数
    def run(self, terms, variables=None, **kwargs):
        # variables不为none的话
        if variables is not None:
            self._templar.available_variables = variables
        myvars = getattr(self._templar, '_available_variables', )

        self.set_options(direct=kwargs)
        default = self.get_option('default')

        ret = []
        for term in terms:
            if not isinstance(term, string_types):
                raise AnsibleError('Invalid setting identifier, "%s" is not a string, its a %s' % (term, type(term)))

            try:
                try:
                    value = myvars[term]
                except KeyError:
                    try:
                        value = myvars['hostvars'][myvars['inventory_hostname']][term]
                    except KeyError:
                        raise AnsibleUndefinedVariable('No variable found with this name: %s' % term)

                ret.append(self._templar.template(value, fail_on_undefined=True))
            except AnsibleUndefinedVariable:
                if default is not None:
                    ret.append(default)
                else:
                    raise

        return ret

可以看到,和回调插件的编写方式类似,继承基类,重写方法。主要用于根据变量名获取当前剧本中的变量,变量名可以是经过运行的变量,我编写一个Demo来测试下

---
- name: vars Demo
  hosts: master
  tasks:
  - name: Show value of 'variablename'
    debug:
      msg: " lookup('vars', 'variabl' + myvar)"
    vars:
      variablename: hello
      myvar: ename

  - name: Show default empty since i dont have 'variablnotename'
    debug:
      msg: " lookup('vars', 'variabl' + myvar, default='变量不存在')"
    vars:
      variablename: hello
      myvar: notename

  - name: Produce an error since i dont have 'variablnotename'
    debug:
        msg: " lookup('vars', 'variabl' + myvar)"
    ignore_errors: True
    vars:
        variablename: hello
        myvar: notename

  - name: find several related variables
    debug:
        msg: " lookup('vars', 'ansible_play_hosts', 'ansible_play_batch', 'ansible_play_hosts_all') "

  - name: alternate way to find some 'prefixed vars' in loop
    debug:
      msg: " lookup('vars', 'ansible_play_' + item) "
    loop:
      - hosts
      - batch
      - hosts_all
┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ansible-playbook vars.yaml

PLAY [vars Demo] ****************************************************************

TASK [Gathering Facts] **********************************************************
ok: [192.168.26.81]

TASK [Show value of 'variablename'] *********************************************
ok: [192.168.26.81] => 
    "msg": "hello"


TASK [Show default empty since i dont have 'variablnotename'] *******************
ok: [192.168.26.81] => 
    "msg": "变量不存在"


TASK [Produce an error since i dont have 'variablnotename'] *********************
fatal: [192.168.26.81]: FAILED! => "msg": "The task includes an option with an undefined variable. The error was: No variable found with this name: variablnotename\\n\\nThe error appears to be in '/root/ansible/vars.yaml': line 19, column 5, but may\\nbe elsewhere in the file depending on the exact syntax problem.\\n\\nThe offending line appears to be:\\n\\n\\n  - name: Produce an error since i dont have 'variablnotename'\\n    ^ here\\n"
...ignoring

TASK [find several related variables] *******************************************
ok: [192.168.26.81] => 
    "msg": [
        [
            "192.168.26.81"
        ],
        [
            "192.168.26.81"
        ],
        [
            "192.168.26.81"
        ]
    ]


TASK [alternate way to find some 'prefixed vars' in loop] ***********************
ok: [192.168.26.81] => (item=hosts) => 
    "msg": [
        "192.168.26.81"
    ]

ok: [192.168.26.81] => (item=batch) => 
    "msg": [
        "192.168.26.81"
    ]

ok: [192.168.26.81] => (item=hosts_all) => 
    "msg": [
        "192.168.26.81"
    ]


PLAY RECAP **********************************************************************
192.168.26.81              : ok=6    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=1

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$

可以使用lookup和query来调用查找插件。使用方法与过滤器相似;指定函数的名称,并在括号中添加要调用的查找插件的名称以及该插件所需的所有参数。

调用lookup插件

可以使用两个 Jinja2 模板函数lookup 或 query)中的一个来调用插件。

这两种方法都具有和过滤器非常相似的语法。指定函数的名称,并在圆括号中指定要调用的lookup插件的名称和插件需要的任何参数。

通过lookup的file插件获取指定文件的内容,编写剧本

- name: lookup Demo
  hosts: master
  vars:
    hosts: " lookup('file', '/etc/hosts')"
  tasks:
    - debug:
        var: hosts

模拟执行剧本

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ansible-playbook jinja2.yaml  -C
PLAY [lookup Demo] **********************************************************************************
TASK [Gathering Facts] ******************************************************************************ok: [192.168.26.81]
TASK [debug] ****************************************************************************************ok: [192.168.26.81] => 
    "hosts": "127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4\\n::1         localhost localhost.localdomain localhost6 localhost6.localdomain6\\n192.168.26.81 vms81.liruilongs.github.io vms81\\n192.168.26.82 vms82.liruilongs.github.io vms82\\n192.168.26.83 vms83.liruilongs.github.io vms83"


PLAY RECAP ******************************************************************************************192.168.26.81              : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

使用逗号分隔,可以在 file 插件中包含多个文件名:

- name: lookup Demo
  hosts: master
  vars:
    issue: " lookup( 'file','/etc/hosts','/etc/issue')"
  tasks:
    - debug:
        var: issue

Ansible 2.5和更高版本中,可以使用query函数,而不是 lookup来调用查找插件。两者之间的区别在于,query 始终会返回⼀个更容易解析和使用的列表,而不是返回逗号分隔的值

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ansible-playbook query.yaml
PLAY [query] **********************************************************************************
TASK [Gathering Facts] ************************************************************************
ok: [192.168.26.81]
TASK [debug] **********************************************************************************
ok: [192.168.26.81] => 
    "param": [
        "\\\\S\\nKernel \\\\r on an \\\\m\\n\\n192.168.26.81",
        "127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4\\n::1         localhost localhost.localdomain localhost6 localhost6.localdomain6\\n192.168.26.81 vms81.liruilongs.github.io vms81\\n192.168.26.82 vms82.liruilongs.github.io vms82\\n192.168.26.83 vms83.liruilongs.github.io vms83"
    ]

PLAY RECAP ************************************************************************************
192.168.26.81              : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$
- name: query
  hosts: master
  vars:
    param: " query('file','/etc/issue','/etc/hosts')"
  tasks:
    - debug:
        var: param

那这里有一个问题,lookup获取文件的内容适控制节点,还是被控节点,实际上是控制节点

读取文件的内容

file 插件允许 Ansible 将本地文件的内容加载到变量。来看一个传递SSH密钥的Demo

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ssh root@vms82.liruilongs.github.io useradd fred
┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ssh root@vms82.liruilongs.github.io useradd naoko
┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$
┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ssh-keygen  -N '' -f fred.key
Generating public/private rsa key pair.
Your identification has been saved in fred.key.
Your public key has been saved in fred.key.pub.
The key fingerprint is:
SHA256:AABygrfjKr2zllYikm0DCbxHaEvt/5fLwN6jY/OaXN8 root@vms81.liruilongs.github.io
The key's randomart image is:
+---[RSA 2048]----+
|*.=..            |
|+B.o .           |
|+o=.  .          |
|oooo   .         |
| =...   S        |
|+ * ...          |
|.= =  .o ..      |
|o *   o=*+. .    |
|.oo+  .*B=o. E   |
+----[SHA256]-----+
┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ssh-keygen  -N '' -f naoko.key
Generating public/private rsa key pair.
Your identification has been saved in naoko.key.
Your public key has been saved in naoko.key.pub.
The key fingerprint is:
SHA256:UDtUESSooboZtIungph4VJoLa3mwVqekwp6wdoExwaI root@vms81.liruilongs.github.io
The key's randomart image is:
+---[RSA 2048]----+
|.      .+o=o     |
|.o  . .o o       |
|o .. o. o        |
|E+. o  . .       |
|..=+    S        |
|++++ .           |
|BOO.+            |
|&@=+             |
|X*o              |
+----[SHA256]-----+
┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$
┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ls | grep key
fred.key
fred.key.pub
naoko.key
naoko.key.pub
- name: Add authorized keys
  hosts: 192.168.26.82
  vars:
    users:
      - fred
      - naoko
  tasks:
    - name: Add keys
      authorized_key:
         user: " item "
         key: " lookup('file',item + '.key.pub')"
      loop: " users "

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ansible-playbook file_vars.yaml

PLAY [Add authorized keys] **************************************************************************

TASK [Gathering Facts] ******************************************************************************
ok: [192.168.26.82]

TASK [Add keys] *************************************************************************************
changed: [192.168.26.82] => (item=fred)
changed: [192.168.26.82] => (item=naoko)

PLAY RECAP ******************************************************************************************
192.168.26.82              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ssh -i fred.key  fred@vms82.liruilongs.github.io id
uid=1001(fred) gid=1001(fred)=1001(fred)
┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ssh -i naoko.key  naoko@vms82.liruilongs.github.io id
uid=1002(naoko) gid=1002(naoko)=1002(naoko)
┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$

对于公钥的获取,也可以直接通过变量拼接,不使用运算符。

 key: " lookup('file', ' item .key.pub')"

如果文件是JSON 或 YAML格式,可以使用from_yaml 或 from_json过滤器将其解析为正确结构化的数据:

我们读取一个pod资源文件试试

---
- name: yaml to vars
  hosts: 192.168.26.82
  tasks:
    - name: show yaml
      debug:
         var: " lookup('file', 'static-pod.yaml') | from_yaml"

---
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: pod-static
  name: pod-static
  namespeace: default
spec:
  containers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    name: pod-demo
    resources: 
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: 
┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ansible-playbook file_yaml.yaml

PLAY [yaml to vars] *********************************************************************************

TASK [Gathering Facts] ******************************************************************************
ok: [192.168.26.82]

TASK [show yaml] ************************************************************************************
ok: [192.168.26.82] => 
    " lookup('file', 'static-pod.yaml') | from_yaml": 
        "apiVersion": "v1",
        "kind": "Pod",
        "metadata": 
            "creationTimestamp": null,
            "labels": 
                "run": "pod-static"
            ,
            "name": "pod-static",
            "namespeace": "default"
        ,
        "spec": 
            "containers": [
                
                    "image": "nginx",
                    "imagePullPolicy": "IfNotPresent",
                    "name": "pod-demo",
                    "resources": 
                
            ],
            "dnsPolicy": "ClusterFirst",
            "restartPolicy": "Always"
        ,
        "status": 
    


PLAY RECAP ******************************************************************************************
192.168.26.82              : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

json也是一样的,我们来看一下,这是传递一个docker加速器设置

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$cat daemon.json

  "registry-mirrors": ["https://2tefyfv7.mirror.aliyuncs.com"]

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$cat file_json.yaml
---
- name: json to vars
  hosts: 192.168.26.82
  tasks:
   - debug:
        var: lookup('file', 'daemon.json') | from_json
┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ansible-playbook file_json.yaml

PLAY [json to vars] *********************************************************************************

TASK [Gathering Facts] ******************************************************************************
ok: [192.168.26.82]

TASK [debug] ****************************************************************************************
ok: [192.168.26.82] => 
    "lookup('file', 'daemon.json') | from_json": 
        "registry-mirrors": [
            "https://2tefyfv7.mirror.aliyuncs.com"
        ]
    


PLAY RECAP ******************************************************************************************
192.168.26.82              : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

ini插件查询控制节点ini格式文件特定参数值。

---
- name:  lookup or query Play
  hosts:  192.168.26.82
  gather_facts: false
  tasks:
   - debug:
      msg: >
       first name in file /etc/foo. ini section liruilong is  lookup('ini', 'first_name  lest_name section=liruilong file=/etc/foo.ini') 
┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ansible-playbook file_ini.yaml

PLAY [lookup or query Play] ********************************************************************************************

TASK [debug] ***********************************************************************************************************
ok: [192.168.26.82] => 
    "msg": "first name in file /etc/foo. ini section liruilong is []\\n"


PLAY RECAP *************************************************************************************************************
192.168.26.82              : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

使用模板应用数据

file插件一样,template 插件也会返回文件的内容,不同之处在于,template 插件预期文件内容为 Jinja2 模
板,并在应用之前评估该模板。

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$echo "hello  name " > hello.j2
---
- name: template Demo
  hosts: 192.168.26.82
  vars:
    name: liruilong
  tasks:
    - name: mes deml
      debug:
        var: lookup('template', 'hello.j2')
┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ansible-playbook template_demo.yaml
[WARNING]: Found variable using reserved name: name

PLAY [template Demo] ***********************

以上是关于Ansible如何使用lookup插件模板化外部数据的主要内容,如果未能解决你的问题,请参考以下文章

Ansible自动化运维Ansible变量之lookup生成变量方法

如何从 Ansible 中的 lookup() 模块的结果中删除换行符 '\n'?

3 ansible-playbook 条件语句-外部变量使用

ansible-playbook模板化(jinja2)

Ansible12:lookup

如何防止特定模板的隐式模板实例化?