【问题标题】:Ansible include task only if file exists仅当文件存在时才包含 Ansible 任务
【发布时间】:2015-01-23 22:07:09
【问题描述】:

我试图仅在文件存在时才包含它。如果我的角色用户需要,这允许在现有“任务/角色”之间自定义“任务/角色”。我发现了这个:

- include: ...
  when: condition

但 Ansible 文档指出:

“所有任务都得到评估,但条件适用于每个任务” - http://docs.ansible.com/playbooks_conditionals.html#applying-when-to-roles-and-includes

所以

- stat: path=/home/user/optional/file.yml
  register: optional_file
- include: /home/user/optional/file.yml
  when: optional_file.stat.exists

如果包含的文件不存在,将失败。我想可能有另一种机制允许用户向现有配方添加任务。我不能让用户在我之后添加角色,因为他们无法控制顺序:他们的角色将在我之后执行。

【问题讨论】:

  • 我花了一点时间来解决这个问题,看来你是对的,即使when 条件为假,它也肯定会失败。

标签: ansible


【解决方案1】:

with_first_found 条件可以在没有statlocal_action 的情况下完成此操作。此条件将遍历本地文件列表并执行任务,并将item 设置为存在的第一个文件的路径。 在with_first_found 选项中包含skip: true 将防止文件不存在时失败。

例子:

- hosts: localhost
  connection: local
  gather_facts: false

  tasks:
    - include: "{{ item }}"
      with_first_found:
        - files:
            - /home/user/optional/file.yml
          skip: true

【讨论】:

  • 尝试这个,我看到这个:错误! 'with_first_found' 不是 PlaybookInclude 的有效属性
  • 我将include 声明作为不在剧本级别的任务。它以这种方式作为一项任务工作。包含的文件只包含任务。如果您需要包含单独的主机目标剧本,那么您必须在本地统计文件,然后使用该结果作为条件。
  • 这是 Ansible 的一个很好隐藏的功能,但是非常棒,谢谢!请注意,较新版本的 Ansible 已切换到使用 lookupfirst_found 过滤器。见docs.ansible.com/ansible/latest/plugins/lookup/first_found.html
【解决方案2】:

感谢大家的帮助!我终于在今天的 Ansible 中尝试了所有回复和我自己的问题代码后,提出了自己的问题:ansible 2.0.1.0

我的原始代码现在似乎可以工作了,除了我正在寻找的可选文件在我的本地机器中,所以我必须通过 local_action 运行 stat 并为该特定任务设置 become: no,所以ansible 不会尝试在我的本地机器上执行 sudo 并出现错误:"sudo: a password is required\n"

- local_action: stat path=/home/user/optional/file.yml
  register: optional_file
  become: no
- include: /home/user/optional/file.yml
  when: optional_file.stat.exists

【讨论】:

  • 是否可以为角色做类似的事情:仅当角色存在时才“包含”角色?我尝试使用 pre_tast 检查主文件中是否存在角色,保存一个变量,然后在角色包含中使用该变量,但 ansible 失败并显示the role 'debian_stock_config' was not found。谢谢
  • 嗨,Igor,这超出了这个问题的范围,您可能想自己开始,以便更多地关注您的具体问题。
【解决方案3】:

我使用类似的东西,但对于文件模块,我的诀窍是检查变量定义,尝试类似:

when: optional_file.stat.exists is defined and optional_file.stat.exists

只有当变量存在时任务才会运行。

【讨论】:

  • 谢谢,没有optional_file.stat.exists is defined 似乎什么时候可以工作。我仍在尝试确定哪些更改允许这样做。
【解决方案4】:

在 Ansible 2.5 及更高版本中,可以使用如下测试来完成:

- include: /home/user/optional/file.yml
  when: "'/home/user/optional/file.yml' is file"

更多详情:https://docs.ansible.com/ansible/latest/user_guide/playbooks_tests.html#testing-paths

【讨论】:

  • 在我的设置 (Ansible 2.10) 中,需要一个完整路径,所以要让它在角色中工作,我必须使用 when: (role_path, '/templates/', item, '.yml.j2') | join('') is file
  • 原则上看起来不错,但是请注意,测试总是在本地主机(运行 Ansible 的主机)而不是目标主机上进行评估。这对于这个问题的用例来说很好,但可能不适用于其他用例。
【解决方案5】:

如果我没记错的话,即使when语句为假,你还想继续剧本吗?

如果有,请在when:之后添加这一行

ignore_errors: True

所以你的任务将如下所示:

- stat: path=/home/user/optional/file.yml
  register: optional_file
- include: /home/user/optional/file.yml
  when: optional_file.stat.exists
  ignore_errors: True

如果我正确理解您的问题或可以提供进一步帮助,请告诉我。谢谢

【讨论】:

  • 谢谢,您似乎理解了这个问题,但这不起作用。 ignore_errors 不会忽略文件丢失的事实。我认为docs.ansible.com/… 在说:“请注意,上述系统仅管理特定任务的失败,因此如果您使用了未定义的变量,它仍然会引发用户需要解决的错误。 "此错误是会阻止用户的错误之一。
【解决方案6】:

我可以在这里花时间抨击 ansible 的错误处理规定,但简而言之,您是对的,由于上述原因,您不能为此目的使用 stat 模块。

对于大多数 ansible 问题,最简单的解决方案是在 ansible 之外进行。例如

- shell: test -f /home/user/optional/file.yml     # or use -r if you're too particular.
  register: optional_file
  failed_when: False
- include: /home/user/optional/file.yml
  when: optional_file.rc == 0
- debug: msg="/home/user/optional/file.yml did not exist and was not included"
  when: optional_file.rc != 0

* failed_when 添加以避免在文件不存在时主机被排除在进一步的任务之外。

【讨论】:

  • 这是一个很棒的方法。谢谢。我使用 test -d 检查目录,后来意识到 failed_when 标志的值。再次感谢
【解决方案7】:

使用 ansible-2.1.0,我可以在我的剧本中像这样使用 sn-ps:

- hosts: all
  gather_facts: false
  tasks:
    - name: Determine if local playbook exists
      local_action: stat path=local.yml
      register: st

- include: local.yml
  when: st.stat.exists

local.yml 不存在时,我没有收到任何错误/失败,并且该剧本被执行(作为剧本,这意味着它以hosts: 行等开头)

您可以在任务级别执行相同的操作,而不是使用类似的代码。 使用 stat 似乎可以正常工作。

【讨论】:

    【解决方案8】:

    还可以选择使用 Jinja2 过滤器:

     - set_fact: optional_file="/home/user/optional/file.yml"
    
     - include: ....
       when: optional_file|exists
    

    【讨论】:

    • 在 Ansible 2.9.1 上,我收到 no filter named 'exists'
    • 你试过用is exists代替| exists吗?
    • 刚才我尝试了您的建议,但无法成功。你的意思是使用local_action: stat ...,如接受的答案所示?
    • 那么这个过滤器可能已经在 Ansible 的最新版本中被删除了,还没有检查。免费忽略我的答案并使用已接受的答案!
    【解决方案9】:

    到目前为止,我想出的最佳选择是:

    - include: "{{ hook_variable | default(lookup('pipe', 'pwd') ~ '/hooks/empty.yml') }}"
    

    它不完全是“如果存在”,但它为您角色的用户提供了相同的结果。在您的角色中创建一些变量和一个默认的空文件。 Jinja 过滤器“默认”和“查找”负责在未设置变量的情况下回退到空文件。

    为方便起见,用户可以使用{{ playbook_dir }} 变量来设置路径:

    hook_variable: "{{ playbook_dir }}/hooks/tasks-file.yml" 
    

    【讨论】:

    • 这仅适用于 Ansible v1.9 及更低版本。 Ansible v2.0 及更高版本应使用@olluch 建议的when: include_file_variable is defined
    • 谢谢!我在我的真实代码中使用了{{ playbook_dir }},为了简单起见,我只是把它排除在示例之外。
    猜你喜欢
    • 1970-01-01
    • 2016-07-29
    • 2013-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多