【问题标题】:How to gather localhost facts in Ansible while gather_facts is set to "no" for all hosts in Ansible?当 Ansible 中的所有主机的gather_facts 设置为“no”时,如何在 Ansible 中收集 localhost 事实?
【发布时间】:2021-07-08 16:31:00
【问题描述】:

我的剧本使用路由器作为主机来执行任务。我已禁用主机的事实,但我需要从运行剧本的本地主机访问ansible_date_time。本地主机是 Ubuntu 虚拟机。

这就是我的剧本的样子:

---
- hosts: lab
  gather_facts: no

  tasks:   
    - name: Run block tasks
      delegate_to: 127.0.0.1
      block:
        - name: Get cert serial number using OpenSSL
          shell: |
            openssl s_client -connect {{ inventory_hostname }}:50051 2>/dev/null  | sed -n -e '/BEGIN\ CERTIFICATE/,/END\ CERTIFICATE/ p' |openssl x509 -noout -serial  | cut -d'=' -f2 | sed -e 's/\(.*\)/\L\1/'
          register: serialNum
      
        - name: Print Serial Numbers
          debug:
            msg: "{{ serialNum.stdout_lines }}"

    - name: Ansible fact - ansible_date_time
      # gather_facts: yes
      delegate_to: 127.0.0.1
      debug:
        var: ansible_date_time.date

我不能把 gather_facts: yes 放在最后一个任务中,因为那个错误。

如果我在播放级别启用gather_facts: yes,那么我会得到路由器的事实,这不是我想要的。

运行上面的剧本会给我以下信息:

TASK [Ansible fact - ansible_date_time] ***************************************************************************************************************************************************
ok: [router1.mgt.net] => {
    "ansible_date_time.date": "VARIABLE IS NOT DEFINED!"
}

这可能与 Ansible 有关吗?

【问题讨论】:

  • 这里的问题是delegate_to 没有委托事实。因此,必须为运行任务的lab 组中的当前主机定义ansible_date_time.date。一旦你知道这一点,请注意:委派调试任务是完全没用的:它会在你委派给的任何主机上运行,​​并在播放循环中显示来自当前主机的变量。

标签: ansible


【解决方案1】:

任务setup 可以用于此。这实际上是 Ansible 在后台调用的任务,当您拥有 gather_facts: yes 时。

为此,您甚至不需要收集所有事实,只需使用参数gather_subset 的最小子集即可。

鉴于剧本:

- hosts: localhost
  gather_facts: no

  tasks:
    - setup:
        gather_subset:
          - 'min'
    - debug:
        var: ansible_date_time.date

这将产生回顾:

PLAY [localhost] *************************************************************************************************

TASK [setup] *****************************************************************************************************
ok: [localhost]

TASK [debug] *****************************************************************************************************
ok: [localhost] => 
  ansible_date_time.date: '2021-07-08'

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

【讨论】:

    【解决方案2】:

    如果您只需要日期,则不必运行安装程序。只需格式化strftime。这将为您提供当前日期(和时间)而无需设置,例如

        - debug:
            msg: "{{ '%Y-%m-%d'|strftime }}"
    

    给予

      msg: '2021-07-08'
    

    为什么有效?总是在需要当前时间时运行设置会很尴尬,例如

        - debug:
            msg: "{{ '%H:%M:%S'|strftime }}"
        - wait_for:
            timeout: 3
        - debug:
            msg: "{{ '%H:%M:%S'|strftime }}"
    

    给予

      msg: '22:27:07'
      msg: '22:27:11'
    

    【讨论】:

      【解决方案3】:

      我不能 100% 确定我得到了你的用例,但这是我的看法。

      在继续之前,请注意几点。

      • 委派到 127.0.0.1 在全球范围内是一种不好的做法,因为它可能使用 ssh 而不是 local 连接插件,并强制您在清单中定义主机而不是使用 implicit localhost
      • 我们需要从 localhost 收集事实以获取其ansible_date_time 信息。我们将在一个单独的播放中执行此操作,并在下一个播放中将该值分配为播放变量以方便使用。有很多其他方法可以解决这个问题,但最重要的是使用 hostvars magic variable 从特定主机获取事实。
      • 如果您发现从 localhost 收集全套事实过于耗费资源,您可以将我的提议与来自 @β.εηοιτ.βε 的提议混合使用

      这是我将如何修复您的剧本(未经过全面测试)

      ---
      - name: Gather facts from localhost for later use
        hosts: localhost
        # If facts gathering is disabled in ansible.cfg you will
        # have to turn it on explicitly (i.e. `gather_facts: true`)
        
      
      - name: Do the actual work on lab routers
        hosts: lab
        gather_facts: no
      
        vars:
          # Get current date from localhost in a play var
          current_date: "{{ hostvars['localhost'].ansible_date_time.date }}"
      
        tasks:   
          - name: Get cert serial number using OpenSSL
            shell: |
              openssl s_client -connect {{ inventory_hostname }}:50051 2>/dev/null  | sed -n -e '/BEGIN\ CERTIFICATE/,/END\ CERTIFICATE/ p' |openssl x509 -noout -serial  | cut -d'=' -f2 | sed -e 's/\(.*\)/\L\1/'
            register: serialNum
            delegate_to: localhost
            
          - name: Print Serial Numbers
            debug:
              msg: "{{ serialNum.stdout_lines }}"
      
          - name: Show date from localhost gathered at very beginning
            debug:
              var: current_date
      

      【讨论】:

      • 用例是我需要获取路由器内文件的时间戳,并将其与运行 playbook 的主机的当前时间戳进行比较。按照您的建议添加第一个任务不起作用。我仍然得到“变量未定义!”尝试打印“current_date”变量时的消息。
      • 我上面的解决方案有效(刚刚测试收集和显示来自其他主机的信息)。您确定您更改了变量以从本地主机获取信息(即hostvars['localhost'].ansible_date_time.date)吗?同时,这将返回,正如我所说的来自 localhost 的时间戳。现在,如果您想要播放循环中目标路由器的时间戳,您别无选择,只能收集事实(至少部分)或在 shell 会话中发送原始命令以自行获取。
      • 抱歉,我有一个错字。是的,您的解决方案确实有效。
      • 随意接受(绿色勾号)任何最适合您需要的有用答案,这会让其他人知道有一个可行的解决方案。
      【解决方案4】:

      也许您可以结合使用内置的date 命令和ansible.builtin.command 模块?例如,我能够使用以下 playbook 以 YYYY-mm-dd 格式获取日期:

      - connection: local
        hosts: all
        gather_facts: false
        tasks:
          - name: Get the current date
            ansible.builtin.command: date +%Y-%m-%d
            changed_when: false
            register: date
          - debug:
              var: date.stdout
      

      当然,您可以随时更改date 命令的输出格式。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-09-25
        • 2018-01-16
        • 1970-01-01
        • 2015-04-26
        • 2022-01-12
        • 1970-01-01
        • 2015-12-05
        相关资源
        最近更新 更多