English 中文(简体)
How to fallback to a default value when ansible lookup fails?
原标题:

I was a little bit surprised to discover that his piece of code fails with an IOError exception instead of defaulting to omitting the value.

#!/usr/bin/env ansible-playbook -i localhost,
---
- hosts: localhost
  tasks:
    - debug: msg="{{ lookup( ini ,  foo section=DEFAULT file=missing-file.conf ) | default(omit) }}"

How can I load a value without raising an exception?

Please note that the lookup module supports a default value parameter but this one is useless to me because it works only when it can open the file.

I need a default value that works even when the it fails to open the file.

最佳回答

As far as I know Jinja2 unfortunately doesn t support any try/catch mechanism.

So you either patch ini lookup plugin / file issue to Ansible team, or use this ugly workaround:

---
- hosts: localhost
  gather_facts: no
  tasks:
    - debug: msg="{{ lookup( first_found , dict(files=[ test-ini.conf ], skip=true)) | ternary(lookup( ini ,  foo section=DEFAULT file=test-ini.conf ), omit) }}"

In this example first_found lookup return file name if file exists or empty list otherwise. If file exists, ternary filter calls ini lookup, otherwise omit placeholder is returned.

问题回答

In case people like me stumble upon this question in 2022, Ansible now supports rescue blocks, which is similar to try-catch-finally in programming languages.

Examples can be found in the official documentation Error handling with blocks.

You can use block/rescue as follows:

- hosts: localhost
  tasks:
    - block:
        - debug: msg="{{ lookup( ini ,  foo section=DEFAULT file=missing-file.conf ) }}"
      rescue:
        - debug: msg="omit"

You can also convert your input file with a from_yaml filter before using the default filter

- name: "load a yaml file or a default value"
  set_fact:
    myvar: "{{ lookup( file ,  myfile.yml , errors= ignore ) | from_yaml | default(mydefaultObject, true) }}"

To avoid the error when the path doesn t exist, use a condition to check for the path before attempting the lookup:

---

- hosts: localhost
  tasks:

    - debug: msg="{{ lookup( ini ,  foo section=DEFAULT file=missing-file.conf ) }}"
      when: missing-file.conf | exists

You can use this with set_fact as well, then omit the undefined var when using it if required:

- hosts: localhost
  tasks:

    - set_fact:
        foo: "{{ lookup( ini ,  foo section=DEFAULT file=missing-file.conf ) }}"
      when: missing-file.conf | exists

    - debug:
        var: foo  # undefined
        msg: "{{ foo | default(omit) }}"  # omitted

Note that lookups and Jinja2 tests run on the controller. If you need to check the path on the host, use the stat and either slurp or fetch modules:

- stat:
    file: missing-remote-file-with-text-i-want
  register: file

- slurp:
    src: missing-remote-file-with-text-i-want
  register: slurp
  when: file.stat.exists

- set_fact:
    foo: "{{ slurp.content | b64decode }}"
  when: file.stat.exists

- fetch:
    src: missing-file.conf
    dest: /tmp/fetched
    fail_on_missing: False

- set_fact:
    bar: "{{ lookup( ini ,  foo section=DEFAULT file=/tmp/fetched/  + inventory_hostname +  /missing-file.conf ) }}"
  when: ( /tmp/fetched/  + inventory_hostname +  /missing-file.conf ) | exists
  

Second note, in Ansible v2.5 the grammar for using the path tests was changed, the format is now:

- set_fact:
    foo: "{{ lookup( ini ,  foo section=DEFAULT file=missing-file.conf ) }}"
  when:  "missing-file.conf" is exists 




相关问题
Strip whitespace in generated HTML using pure Python code

I am using Jinja2 to generate HTML files which are typically very huge in size. I noticed that the generated HTML had a lot of whitespace. Is there a pure-Python tool that I can use to minimize this ...

String concatenation in Jinja

I just want to loop through an existing list and make a comma delimited string out of it. Something like this: my_string = stuff, stuff, stuff, stuff I already know about loop.last, I just need to ...

How do you sort a list in Jinja2?

I am trying to do this: {% for movie in movie_list | sort(movie.rating) %} But that s not right...the documentation is vague...how do you do this in Jinja2?

How to get django context automatically in Jinja2 filters?

For example, I have an paginator object with a lot of attributes, and don t want do write something like {{ paginate(paginator) }} in templates. How can a get context automatically in the filter ...

热门标签