<p>See:</p>
<ul>
<li><p><a href="https://stackoverflow.com/questions/61257164/ansible-is-using-wrong-version-of-python">Ansible is using the wrong version of Python</a></p>
</li>
<li><p><a href="https://stackoverflow.com/questions/58450608/how-can-i-specify-the-version-of-python-to-use-in-an-ansible-playbook">How can I specify the version of Python to use in an Ansible playbook?</a></p>
</li>
<li><p><a href="https://stackoverflow.com/questions/59380824/how-to-choose-a-python-interpreter-for-ansible-playbook">How to choose Python interpreter for Ansible playbook?</a></p>
</li>
</ul>
<p>See also <a href="https://docs.ansible.com/ansible/latest/os_guide/intro_bsd.html#setting-the-python-interpreter" rel="nofollow noreferrer">Setting the Python interpreter</a> that explains various configurations' options, variables, and debugging.</p>
<hr>
<p>Q: <em><strong>&quot;None of that explains why - and that is/was my point - <strong>ansible_python wrongly claims to be running on Python 3.x on the local node</strong>. Because all I do by setting <strong>interpreter_python</strong> in ansible.cfg is to give Ansible free rein on how it interprets things.&quot;</strong></em></p>
<p>Short answer: You can't configure the version of Python the Ansible utils use ($ ansible --version). <a href="https://docs.ansible.com/ansible/latest/reference_appendices/config.html#interpreter-python" rel="nofollow noreferrer">INTERPRETER_PYTHON</a> configures the Python version used by the Ansible modules on the remote hosts.</p>
<p>A: Configuration option INTERPRETER_PYTHON works as expected. There is nothing wrong when Python3 is discovered when asked by <code>interpreter_python=auto</code>.</p>
<ul>
<li><a href="https://docs.ansible.com/ansible/latest/reference_appendices/config.html#interpreter-python" rel="nofollow noreferrer">auto</a> (default)</li>
</ul>
<p>See implementation details of the function <a href="https://github.com/ansible/ansible/blob/devel/lib/ansible/executor/interpreter_discovery.py" rel="nofollow noreferrer">discover_interpreter</a>. When the version of the distro is not in <a href="https://github.com/ansible/ansible/blob/devel/lib/ansible/config/base.yml" rel="nofollow noreferrer">_INTERPRETER_PYTHON_DISTRO_MAP</a></p>
<blockquote>
<p><em>&quot;return nearest previous version we're newer than&quot;</em></p>
</blockquote>
<pre class="lang-py prettyprint-override"><code>def _version_fuzzy_match(version, version_map):
...
    # slot match; return nearest previous version we're newer than
    kpos = bisect.bisect(sorted_looseversions, find_looseversion)
</code></pre>
<pre class="lang-ini prettyprint-override"><code>interpreter_python=auto
</code></pre>
<pre class="lang-yaml prettyprint-override"><code>shell&gt; ansible localhost -m setup -a 'filter=ansible_python_version'
localhost | SUCCESS =&gt; {
    &quot;ansible_facts&quot;: {
        &quot;ansible_python_version&quot;: &quot;3.7.3&quot;, 
        &quot;discovered_interpreter_python&quot;: &quot;/usr/bin/python3&quot;
    }, 
    &quot;changed&quot;: false
}
</code></pre>
<ul>
<li><a href="https://docs.ansible.com/ansible/latest/reference_appendices/config.html#interpreter-python" rel="nofollow noreferrer">auto_legacy</a> (default is <em>/usr/bin/python</em>)</li>
</ul>
<blockquote>
<p><em>&quot;The default value of <strong>auto_legacy</strong> provides all the same behavior, but for backward compatibility with older Ansible releases that always defaulted to <strong>/usr/bin/python</strong>, will use that interpreter if present (and issue a warning that the default behavior will change to that of auto in a future Ansible release.&quot;</em></p>
</blockquote>
<pre class="lang-ini prettyprint-override"><code>interpreter_python=auto_legacy
</code></pre>
<pre class="lang-yaml prettyprint-override"><code>shell&gt; ansible localhost -m setup -a 'filter=ansible_python_version'
[DEPRECATION WARNING]: Distribution Ubuntu 19.04 on host localhost should use /usr/bin/python3, but is using /usr/bin/python for backward compatibility with prior Ansible releases. A future
 Ansible release will default to using the discovered platform python for this host. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more 
information. This feature will be removed in version 2.12. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
localhost | SUCCESS =&gt; {
    &quot;ansible_facts&quot;: {
        &quot;ansible_python_version&quot;: &quot;2.7.16&quot;, 
        &quot;discovered_interpreter_python&quot;: &quot;/usr/bin/python&quot;
    }, 
    &quot;changed&quot;: false
}
</code></pre>
<p>See the notes and examples below.</p>
<hr>
<p>Q: <em><strong>&quot;What does the ansible command line tool have to do with the behavior I see with ansible-playbook?&quot;</strong></em></p>
<p>A: Both ansible command line tool and ansible-playbook will report the same value of <em>ansible_python_version</em>. For example,</p>
<pre class="lang-yaml prettyprint-override"><code>shell&gt; ansible localhost -m setup | grep ansible_python_version
        &quot;ansible_python_version&quot;: &quot;2.7.16&quot;,
</code></pre>
<pre class="lang-yaml prettyprint-override"><code>shell&gt; cat playbook.yml
- hosts: localhost
  tasks:
    - debug:
        var: ansible_python_version
</code></pre>
<pre class="lang-yaml prettyprint-override"><code>shell&gt; ansible-playbook playbook.yml

ok: [localhost] =&gt; {
    &quot;ansible_python_version&quot;: &quot;2.7.16&quot;
}
</code></pre>
<hr>
Notes
<ol>
<li><a href="https://docs.ansible.com/ansible/latest/reference_appendices/config.html#interpreter-python" rel="nofollow noreferrer">INTERPRETER_PYTHON</a> configuration parameter says:</li>
</ol>
<blockquote>
<p>Path to the Python interpreter to be used for module execution <strong>on remote targets</strong></p>
</blockquote>
<p>See <a href="https://stackoverflow.com/questions/59380824/how-to-choose-a-python-interpreter-for-ansible-playbook/">How to choose a python interpreter for Ansible playbook?</a></p>
<ol start="2">
<li>Ansible will tell what Python is used <strong>on master</strong></li>
</ol>
<pre class="lang-bash prettyprint-override"><code>shell&gt; ansible --version
ansible 2.9.2
  config file = /home/vlado/.ansible.cfg
  configured module search path = [u'/home/vlado/.ansible/my_modules']
  ansible python module location = /usr/lib/python2.7/dist-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.16 (default, Oct  7 2019, 17:36:04) [GCC 8.3.0]
</code></pre>
<ol start="3">
<li><a href="https://docs.ansible.com/ansible/latest/plugins/lookup/dig.html#dig-query-dns-using-the-dnspython-library" rel="nofollow noreferrer">dig</a> says</li>
</ol>
<blockquote>
<p>The below requirements are needed <strong>on the local master node</strong> that executes this lookup: dnspython</p>
</blockquote>
<ol start="4">
<li>It's possible to install both versions</li>
</ol>
<pre class="lang-bash prettyprint-override"><code>shell&gt; dpkg -l | grep dnspython
ii  python-dnspython         1.16.0-1 all          DNS toolkit for Python
ii  python3-dnspython        1.16.0-1 all          DNS toolkit for Python 3
</code></pre>
<ol start="5">
<li><p>See <a href="https://packages.ubuntu.com/search?keywords=ansible" rel="nofollow noreferrer">Ansible packages in Ubuntu</a> what version of Python was used.</p>
</li>
<li><p><a href="https://docs.ansible.com/ansible/latest/reference_appendices/config.html#interpreter-python" rel="nofollow noreferrer">ansible_python_interpreter</a> works as expected</p>
</li>
</ol>
<p><strong>On master(Ubuntu) the playbook</strong></p>
<pre class="lang-yaml prettyprint-override"><code>shell&gt; cat playbook.yml
- hosts: test_01
  tasks:
    - getent:
        database: hosts
</code></pre>
<pre class="lang-ini prettyprint-override"><code>shell&gt; cat hosts
test_01 ansible_python_interpreter=/usr/local/bin/python3.6
</code></pre>
<pre class="lang-yaml prettyprint-override"><code>shell&gt; ansible-playbook playbook.yml -i hosts -vvv | grep python
</code></pre>
<p>gives</p>
<blockquote>
<p>ansible python module location = /usr/lib/python2.7/dist-packages/ansible
<strong>python version = 2.7.16</strong> (default, Oct  7 2019, 17:36:04) [GCC 8.3.0]</p>
</blockquote>
<p>...</p>
<blockquote>
<p>&lt;test_01&gt; SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User=&quot;admin&quot;' -o ConnectTimeout=10 -o ControlPath=/export/home/vlado.config/.ansible/cp/5a3ab05cf7 -tt test_01 '/bin/sh -c '&quot;'&quot;'sudo -H -S -n  -u root /bin/sh -c '&quot;'&quot;'&quot;'&quot;'&quot;'&quot;'&quot;'&quot;'echo BECOME-SUCCESS-xnvwkdksdqqoehvrgebdgcnrzcrmoszv ; <strong>/usr/local/bin/python3.6</strong> /home/admin/.ansible/tmp/ansible-tmp-1576667662.62-67468482045094/AnsiballZ_getent.py'&quot;'&quot;'&quot;'&quot;'&quot;'&quot;'&quot;'&quot;' &amp;&amp; sleep 0'&quot;'&quot;''</p>
</blockquote>
<ul>
<li>ansible-playbook <strong>on master</strong> runs with <code>python version = 2.7.16</code></li>
<li>module <strong>on remote</strong> (test_01) runs with <code>/usr/local/bin/python3.6</code></li>
</ul>
<p><strong>Change ansible_python_interpreter</strong></p>
<pre class="lang-ini prettyprint-override"><code>shell&gt; cat hosts
test_01 ansible_python_interpreter=/usr/local/bin/python2.7
</code></pre>
<p>the same playbook gives</p>
<blockquote>
<p>ansible python module location = /usr/lib/python2.7/dist-packages/ansible
<strong>python version = 2.7.16</strong> (default, Oct  7 2019, 17:36:04) [GCC 8.3.0]</p>
</blockquote>
<p>...</p>
<blockquote>
<p>&lt;test_01&gt; SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User=&quot;admin&quot;' -o ConnectTimeout=10 -o ControlPath=/export/home/vlado.config/.ansible/cp/5a3ab05cf7 -tt test_01 '/bin/sh -c '&quot;'&quot;'sudo -H -S -n  -u root /bin/sh -c '&quot;'&quot;'&quot;'&quot;'&quot;'&quot;'&quot;'&quot;'echo BECOME-SUCCESS-fidpgwptggdoizirumvjmzuorxqawypu ; <strong>/usr/local/bin/python2.7</strong> /home/admin/.ansible/tmp/ansible-tmp-1576668475.22-210929624017605/AnsiballZ_getent.py'&quot;'&quot;'&quot;'&quot;'&quot;'&quot;'&quot;'&quot;' &amp;&amp; sleep 0'&quot;'&quot;''</p>
</blockquote>
<ul>
<li>ansible-playbook <strong>on master</strong> runs with <code>python version = 2.7.16</code></li>
<li>but module <strong>on remote</strong> (test_01) runs with <code>/usr/local/bin/python2.7</code> as requested</li>
</ul>

CC BY-SA 4.0
2019-12-18T09:01:41.990Z
Yes, but _none_ of that explains why - and _that_ is/was my point - `ansible_python` wrongly claims to be running on Python 3.x on the _local node_. Because all I do by setting `interpreter_python` in `ansible.cfg` is to give Ansible free rein on how it interprets things. But since I am depending on its choices and its information being consistent (which it evidently isn't), things blow up in my face. Regarding point 4, it's completely beside the point, because I am attempting to install the appropriate version _based on_ `ansible_python`, but Ansible simply lies to me ...
0xC0000022L
CC BY-SA 4.0
2019-12-18T11:51:36.480Z
There is no evidence that *`ansible_python wrongly claims to be running on Python 3.x on the local node`*. I've added more details that might help to clarify the situation.
Vladimir Botka
CC BY-SA 4.0
2019-12-18T13:19:04.433Z
The evidence is the behavior one gets _with_ `when:` ... the wrong package that gets installed (provided _neither_, i.e. `python-dnspython`/`python3-dnspython`, was installed before). This is actually reproducible. But you're right, this only happens with `interpreter_python=auto` set in `ansible.cfg` for me. But then I consistently see it (and I have actually used the `debug` module to verify the value of `ansible_python` is wrong). Only if `python-dnspython` is preinstalled (`samba` installs it, for example) don't I see the issue. Otherwise I can reproduce on all 18.04 so far.
0xC0000022L
CC BY-SA 4.0
2019-12-18T13:33:49.790Z
Just to make sure. I understand that the lookup plugins get called on the control/master. In my real-world case the `Preparing Ansible host` part is getting actually executed on the control/master, but the `Preparations` part gets executed on the remote machine(s). With the sole exception of the loopkup plugin, so I understand that the `dnspython` package needs to be installed on control/master and that the _result_ of the lookup is passed (by value) to the remote machine(s). In my MWE I had to condense this for obvious reasons.
0xC0000022L
CC BY-SA 4.0
2019-12-18T14:12:22.233Z
_A: Both ansible command line tool and ansible-playbook will report the same value of **ansible_python_version**._ ... unfortunately not true, at least not unconditionally (see amendments to the question).
0xC0000022L
CC BY-SA 4.0
2023-08-04T15:29:26.700Z
When executing "remotely" on the control host, Ansible uses the logic used for remote hosts when determining which Python to use for execution. The Python it was started with may not be the same.
reinierpost
CC BY-SA 4.0
2023-08-07T10:07:40.283Z
@Vladimir Botka: This is where I ended up now. StackExchange questions and answers are supposed to be useful to anyone who finds them, so they must be kept up to date.
reinierpost
CC BY-SA 4.0
2023-08-07T20:55:10.147Z
I updated the answer. Is there anything, in particular, you can't find?
Vladimir Botka
CC BY-SA 4.0
2019-12-18T02:53:21.290Z
2023-08-07T20:49:35.070Z
2023-08-07T20:49:35.070Z
Vladimir Botka
<p>Since this confused the hell out of me and cost me about an hour to figure out, I am going to post an answer.</p>

<p>As you can see I am using <code>ansible_python</code> to determine the Python version and install the appropriate version of <code>dnspython</code>. Since this is on Ubuntu 18.04 it defaults to <code>python3</code>. I am suppressing the respective warning with <code>interpreter_python=auto</code> in the <code>ansible.cfg</code> inside the same folder.</p>

<p>But here's the bummer. Apparently <code>connection: local</code> somehow violates the rule about using <code>python3</code> whose <code>dnspython</code> package is installed. It made sense once I looked for <code>dig.py</code> (hoping for the <a href="https://docs.ansible.com/ansible/latest/plugins/lookup/dig.html" rel="nofollow noreferrer">Ansible lookup module of the same name</a>). And indeed I found it:</p>

<pre><code>$ locate dig.py
/usr/lib/python2.7/dist-packages/ansible/plugins/lookup/dig.py
/usr/lib/python2.7/dist-packages/ansible/plugins/lookup/dig.pyc
</code></pre>

<p>... alas, this means my version of Ansible uses Python 2.7 and so the <code>dig</code> module won't be able to make use of the <code>python3-dnspython</code> (Debian) package which I installed.</p>

<p>Still puzzling. But with the cause determined, I can work around it now.</p>

CC BY-SA 4.0
2019-12-18T00:22:31.457Z
2019-12-18T00:22:31.457Z
0xC0000022L
<p>In ansible, <code>lookup</code>'s are <a href="https://docs.ansible.com/ansible/latest/plugins/lookup.html" rel="nofollow noreferrer">executed locally</a> where ansible runs, not on the target server.</p>
<p>So you will have to ensure that <code>dnspython</code> is installed on your ansible machine.</p>

CC BY-SA 4.0
2021-05-16T19:53:56.407Z
2021-05-16T19:53:56.407Z
lifeofguenter
<p>I am trying to look up the IPv4 address (a single one, which I will <code>assert</code> on!) for a given name using Ansible and use that as a fact "down the line".</p>

<p>An MWE looks like this:</p>

<pre><code>---
# vim: set autoindent smartindent tabstop=2 shiftwidth=2 softtabstop=2 expandtab filetype=ansible:

- name: Preparations
  hosts: localhost
  connection: local
  vars:
    host2lookup: "example.com"
  tasks:
    - getent:
        database: ahostsv4
        key: "{{host2lookup}}"
    - debug:
        var: getent_ahostsv4
    - name: Lookup for {{host2lookup}}
      debug:
        msg: "{{lookup('dig', '{{host2lookup}}./A')}}"
</code></pre>

<p>... and fails as follows (relevant excerpt):</p>

<pre><code>  localhost failed | msg: An unhandled exception occurred while running the lookup plugin 'dig'. Error was a &lt;class 'ansible.errors.AnsibleError'&gt;, original message: The dig lookup requires the python 'dnspython' library and it is not installed
</code></pre>

<p>So I thought to myself I'd install the <code>dnspython</code> library for the appropriate Python version, making the playbook look as follows:</p>

<pre><code>---
# vim: set autoindent smartindent tabstop=2 shiftwidth=2 softtabstop=2 expandtab filetype=ansible:

- name: Preparing Ansible host
  hosts: localhost
  connection: local
  tasks:
    - name: Install dnspython package on Ansible host
      block:
        - name: Installing python-dnspython package
          apt:
            pkg:
              - python-dnspython
            update_cache: yes
            install_recommends: no
            state: present
          when: ansible_python.version.major == 2
        - name: Installing python3-dnspython package
          apt:
            pkg:
              - python3-dnspython
            update_cache: yes
            install_recommends: no
            state: present
          when: ansible_python.version.major == 3
      become: true
- name: Preparations
  hosts: localhost
  connection: local
  vars:
    host2lookup: "example.com"
  tasks:
    - getent:
        database: ahostsv4
        key: "{{host2lookup}}"
    - debug:
        var: getent_ahostsv4
    - name: Lookup for {{host2lookup}}
      debug:
        msg: "{{lookup('dig', '{{host2lookup}}./A')}}"
</code></pre>

<p>But the ultimate failure remains the same. Either that error is misleading or something else is fishy ...</p>

<pre><code>Executing playbook dbg.yml

- Preparing Ansible host on hosts: localhost -
Gathering Facts...
  localhost ok
Installing python-dnspython package...
Installing python3-dnspython package...
  localhost ok

- Preparations on hosts: localhost -
Gathering Facts...
  localhost ok
getent...
  localhost ok
debug...
  localhost ok: {
    "changed": false,
    "getent_ahostsv4": {
        "93.184.216.34": [
            "RAW"
        ]
    }
}
Lookup for example.com...
  localhost failed | msg: An unhandled exception occurred while running the lookup plugin 'dig'. Error was a &lt;class 'ansible.errors.AnsibleError'&gt;, original message: The dig lookup requires the python 'dnspython' library and it is not installed

- Play recap -
  localhost                  : ok=5    changed=0    unreachable=0    failed=1    rescued=0    ignored=0
</code></pre>

<p>What other than the appropriate <code>dnspython</code> package am I missing here?</p>

<hr>

<p>The <code>dnsutils</code> package (containing <code>dig</code>) and its library dependencies is installed. Adding these as prerequisites into the <code>apt:</code> task therefore makes no difference.</p>

<p>Found another smoking gun, though:</p>

<pre><code>$ python3 -m dnspython
/usr/bin/python3: No module named dnspython
$ sudo -H pip3 install dnspython
Requirement already satisfied: dnspython in /usr/lib/python3/dist-packages
</code></pre>

<p>I stand corrected. As per the <a href="http://www.dnspython.org/examples.html" rel="nofollow noreferrer">examples page</a> I should import something like <code>dns.query</code> (<code>python3 -m dns.query</code>). And that succeeds.</p>

<p>My <code>ansible.cfg</code> is this:</p>

<pre><code>$ cat ansible.cfg
[defaults]
nocows = true
command_warnings = false
stdout_callback = unixy
display_skipped_hosts = false
interpreter_python=auto
</code></pre>

<p>The whole thing runs on Ubuntu 18.04.</p>

<hr>

<p>As per request in a comment, I am providing more output. This is still on Ubuntu 18.04. With the (above shown) <code>ansible.cfg</code> in place, I get the following output:</p>

<pre><code>$ ansible localhost -m setup -a 'filter=ansible_python*'
localhost | SUCCESS =&gt; {
    "ansible_facts": {
        "ansible_python": {
            "executable": "/usr/bin/python2",
            "has_sslcontext": true,
            "type": "CPython",
            "version": {
                "major": 2,
                "micro": 17,
                "minor": 7,
                "releaselevel": "final",
                "serial": 0
            },
            "version_info": [
                2,
                7,
                17,
                "final",
                0
            ]
        },
        "ansible_python_version": "2.7.17"
    },
    "changed": false
}
</code></pre>

<p>However, <code>ansible-playbook</code> behaves differently here. Consider the following playbook:</p>

<pre><code>---

- hosts: all
  tasks:
    - debug:
        var: ansible_python
    - debug:
        var: ansible_python_version
</code></pre>

<p>The output becomes:</p>

<pre><code>$ ansible-playbook -i localhost, -c local dbg.yml
Executing playbook dbg.yml

- all on hosts: all -
Gathering Facts...
  localhost ok
debug...
  localhost ok: {
    "ansible_python": {
        "executable": "/usr/bin/python3",
        "has_sslcontext": true,
        "type": "cpython",
        "version": {
            "major": 3,
            "micro": 9,
            "minor": 6,
            "releaselevel": "final",
            "serial": 0
        },
        "version_info": [
            3,
            6,
            9,
            "final",
            0
        ]
    },
    "changed": false
}
debug...
  localhost ok: {
    "ansible_python_version": "3.6.9",
    "changed": false
}

- Play recap -
  localhost                  : ok=3    changed=0    unreachable=0    failed=0    rescued=0    ignored=0
</code></pre>

<p>... <strong>comment out the following line</strong> from <code>ansible.cfg</code>:</p>

<pre><code>interpreter_python=auto
</code></pre>

<p>... and the output (when rerunning the previous command) becomes:</p>

<pre><code>$ ansible-playbook -i localhost, -c local dbg.yml
Executing playbook dbg.yml

- all on hosts: all -
Gathering Facts...
  localhost ok
debug...
  localhost ok: {
    "ansible_python": {
        "executable": "/usr/bin/python",
        "has_sslcontext": true,
        "type": "CPython",
        "version": {
            "major": 2,
            "micro": 17,
            "minor": 7,
            "releaselevel": "final",
            "serial": 0
        },
        "version_info": [
            2,
            7,
            17,
            "final",
            0
        ]
    },
    "changed": false
}
debug...
  localhost ok: {
    "ansible_python_version": "2.7.17",
    "changed": false
}

- Play recap -
  localhost                  : ok=3    changed=0    unreachable=0    failed=0    rescued=0    ignored=0
</code></pre>

<p>Nothing <a href="https://docs.ansible.com/ansible/latest/reference_appendices/interpreter_discovery.html" rel="nofollow noreferrer">in the documentation</a> suggests that this should happen.</p>

CC BY-SA 4.0
2019-12-18T13:33:55.640Z
Update the question and add the output of the command `ansible localhost -m setup | grep ansible_python_version`
Vladimir Botka
CC BY-SA 4.0
2019-12-18T13:42:19.497Z
@VladimirBotka I _honestly_ appreciate your effort, but what does the `ansible` command line tool have to do with behavior I see with `ansible-playbook`? I will amend the question shortly. You'll notice that the two tools behave differently in this exact case. No clue why, but it can be reproduced.
0xC0000022L
CC BY-SA 4.0
2019-12-18T13:58:44.157Z
@VladimirBotka amended the question with requested information ... and a little more, since the information you requested isn't representative of the issue I am encountering.
0xC0000022L
CC BY-SA 4.0
2019-12-18T14:15:12.240Z
You did not amend the question with the requested information. I asked you to use `-hosts: localhost`, but you used `- hosts: all`. This is really `little more`.
Vladimir Botka
CC BY-SA 4.0
2019-12-18T14:23:19.333Z
@VladimirBotka Huh? Which part of `ansible localhost -m setup -a 'filter=ansible_python*'` (other than using a filter instead of `grep`) _lacks_ the requested information? Your request was for output from `ansible` and I pointed out the discrepancy with that of `ansible-playbook` based on the `interpreter_python=auto` setting.
0xC0000022L
<ansible>
Using `dig` Ansible module?
0xC0000022L
# Using `dig` Ansible module?

(Asked by: username_0 on 2019-12-18)

I am trying to look up the IPv4 address (a single one, which I will assert on!) for a given name using Ansible and use that as a fact "down the line".
An MWE looks like this:
---
# vim: set autoindent smartindent tabstop=2 shiftwidth=2 softtabstop=2 expandtab filetype=ansible:

- name: Preparations
  hosts: localhost
  connection: local
  vars:
    host2lookup: "example.com"
  tasks:
    - getent:
        database: ahostsv4
        key: "{{host2lookup}}"
    - debug:
        var: getent_ahostsv4
    - name: Lookup for {{host2lookup}}
      debug:
        msg: "{{lookup('dig', '{{host2lookup}}./A')}}"

... and fails as follows (relevant excerpt):
  localhost failed | msg: An unhandled exception occurred while running the lookup plugin 'dig'. Error was a , original message: The dig lookup requires the python 'dnspython' library and it is not installed

So I thought to myself I'd install the dnspython library for the appropriate Python version, making the playbook look as follows:
---
# vim: set autoindent smartindent tabstop=2 shiftwidth=2 softtabstop=2 expandtab filetype=ansible:

- name: Preparing Ansible host
  hosts: localhost
  connection: local
  tasks:
    - name: Install dnspython package on Ansible host
      block:
        - name: Installing python-dnspython package
          apt:
            pkg:
              - python-dnspython
            update_cache: yes
            install_recommends: no
            state: present
          when: ansible_python.version.major == 2
        - name: Installing python3-dnspython package
          apt:
            pkg:
              - python3-dnspython
            update_cache: yes
            install_recommends: no
            state: present
          when: ansible_python.version.major == 3
      become: true
- name: Preparations
  hosts: localhost
  connection: local
  vars:
    host2lookup: "example.com"
  tasks:
    - getent:
        database: ahostsv4
        key: "{{host2lookup}}"
    - debug:
        var: getent_ahostsv4
    - name: Lookup for {{host2lookup}}
      debug:
        msg: "{{lookup('dig', '{{host2lookup}}./A')}}"

But the ultimate failure remains the same. Either that error is misleading or something else is fishy ...
Executing playbook dbg.yml

- Preparing Ansible host on hosts: localhost -
Gathering Facts...
  localhost ok
Installing python-dnspython package...
Installing python3-dnspython package...
  localhost ok

- Preparations on hosts: localhost -
Gathering Facts...
  localhost ok
getent...
  localhost ok
debug...
  localhost ok: {
    "changed": false,
    "getent_ahostsv4": {
        "93.184.216.34": [
            "RAW"
        ]
    }
}
Lookup for example.com...
  localhost failed | msg: An unhandled exception occurred while running the lookup plugin 'dig'. Error was a , original message: The dig lookup requires the python 'dnspython' library and it is not installed

- Play recap -
  localhost                  : ok=5    changed=0    unreachable=0    failed=1    rescued=0    ignored=0

What other than the appropriate dnspython package am I missing here?

The dnsutils package (containing dig) and its library dependencies is installed. Adding these as prerequisites into the apt: task therefore makes no difference.
Found another smoking gun, though:
$ python3 -m dnspython
/usr/bin/python3: No module named dnspython
$ sudo -H pip3 install dnspython
Requirement already satisfied: dnspython in /usr/lib/python3/dist-packages

I stand corrected. As per the examples page I should import something like dns.query (python3 -m dns.query). And that succeeds.
My ansible.cfg is this:
$ cat ansible.cfg
[defaults]
nocows = true
command_warnings = false
stdout_callback = unixy
display_skipped_hosts = false
interpreter_python=auto

The whole thing runs on Ubuntu 18.04.

As per request in a comment, I am providing more output. This is still on Ubuntu 18.04. With the (above shown) ansible.cfg in place, I get the following output:
$ ansible localhost -m setup -a 'filter=ansible_python*'
localhost | SUCCESS => {
    "ansible_facts": {
        "ansible_python": {
            "executable": "/usr/bin/python2",
            "has_sslcontext": true,
            "type": "CPython",
            "version": {
                "major": 2,
                "micro": 17,
                "minor": 7,
                "releaselevel": "final",
                "serial": 0
            },
            "version_info": [
                2,
                7,
                17,
                "final",
                0
            ]
        },
        "ansible_python_version": "2.7.17"
    },
    "changed": false
}

However, ansible-playbook behaves differently here. Consider the following playbook:
---

- hosts: all
  tasks:
    - debug:
        var: ansible_python
    - debug:
        var: ansible_python_version

The output becomes:
$ ansible-playbook -i localhost, -c local dbg.yml
Executing playbook dbg.yml

- all on hosts: all -
Gathering Facts...
  localhost ok
debug...
  localhost ok: {
    "ansible_python": {
        "executable": "/usr/bin/python3",
        "has_sslcontext": true,
        "type": "cpython",
        "version": {
            "major": 3,
            "micro": 9,
            "minor": 6,
            "releaselevel": "final",
            "serial": 0
        },
        "version_info": [
            3,
            6,
            9,
            "final",
            0
        ]
    },
    "changed": false
}
debug...
  localhost ok: {
    "ansible_python_version": "3.6.9",
    "changed": false
}

- Play recap -
  localhost                  : ok=3    changed=0    unreachable=0    failed=0    rescued=0    ignored=0

... comment out the following line from ansible.cfg:
interpreter_python=auto

... and the output (when rerunning the previous command) becomes:
$ ansible-playbook -i localhost, -c local dbg.yml
Executing playbook dbg.yml

- all on hosts: all -
Gathering Facts...
  localhost ok
debug...
  localhost ok: {
    "ansible_python": {
        "executable": "/usr/bin/python",
        "has_sslcontext": true,
        "type": "CPython",
        "version": {
            "major": 2,
            "micro": 17,
            "minor": 7,
            "releaselevel": "final",
            "serial": 0
        },
        "version_info": [
            2,
            7,
            17,
            "final",
            0
        ]
    },
    "changed": false
}
debug...
  localhost ok: {
    "ansible_python_version": "2.7.17",
    "changed": false
}

- Play recap -
  localhost                  : ok=3    changed=0    unreachable=0    failed=0    rescued=0    ignored=0

Nothing in the documentation suggests that this should happen.

**Tags:** ansible

> **Comments:**
> **username_1**: Update the question and add the output of the command `ansible localhost -m setup | grep ansible_python_version`  
> **username_0**: @VladimirBotka I _honestly_ appreciate your effort, but what does the `ansible` command line tool have to do with behavior I see with `ansible-playbook`? I will amend the question shortly. You'll notice that the two tools behave differently in this exact case. No clue why, but it can be reproduced.  
> **username_0**: @VladimirBotka amended the question with requested information ... and a little more, since the information you requested isn't representative of the issue I am encountering.  
> **username_1**: You did not amend the question with the requested information. I asked you to use `-hosts: localhost`, but you used `- hosts: all`. This is really `little more`.  
> **username_0**: @VladimirBotka Huh? Which part of `ansible localhost -m setup -a 'filter=ansible_python*'` (other than using a filter instead of `grep`) _lacks_ the requested information? Your request was for output from `ansible` and I pointed out the discrepancy with that of `ansible-playbook` based on the `interpreter_python=auto` setting.  

---

## 3 answers

### Answer by username_1 on 2019-12-18. Score: 2 | Accepted answer

See:

Ansible is using the wrong version of Python

How can I specify the version of Python to use in an Ansible playbook?

How to choose Python interpreter for Ansible playbook?


See also Setting the Python interpreter that explains various configurations' options, variables, and debugging.

Q: "None of that explains why - and that is/was my point - ansible_python wrongly claims to be running on Python 3.x on the local node. Because all I do by setting interpreter_python in ansible.cfg is to give Ansible free rein on how it interprets things."
Short answer: You can't configure the version of Python the Ansible utils use ($ ansible --version). INTERPRETER_PYTHON configures the Python version used by the Ansible modules on the remote hosts.
A: Configuration option INTERPRETER_PYTHON works as expected. There is nothing wrong when Python3 is discovered when asked by interpreter_python=auto.

auto (default)

See implementation details of the function discover_interpreter. When the version of the distro is not in _INTERPRETER_PYTHON_DISTRO_MAP

"return nearest previous version we're newer than"

def _version_fuzzy_match(version, version_map):
...
    # slot match; return nearest previous version we're newer than
    kpos = bisect.bisect(sorted_looseversions, find_looseversion)

interpreter_python=auto

shell> ansible localhost -m setup -a 'filter=ansible_python_version'
localhost | SUCCESS => {
    "ansible_facts": {
        "ansible_python_version": "3.7.3", 
        "discovered_interpreter_python": "/usr/bin/python3"
    }, 
    "changed": false
}


auto_legacy (default is /usr/bin/python)


"The default value of auto_legacy provides all the same behavior, but for backward compatibility with older Ansible releases that always defaulted to /usr/bin/python, will use that interpreter if present (and issue a warning that the default behavior will change to that of auto in a future Ansible release."

interpreter_python=auto_legacy

shell> ansible localhost -m setup -a 'filter=ansible_python_version'
[DEPRECATION WARNING]: Distribution Ubuntu 19.04 on host localhost should use /usr/bin/python3, but is using /usr/bin/python for backward compatibility with prior Ansible releases. A future
 Ansible release will default to using the discovered platform python for this host. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more 
information. This feature will be removed in version 2.12. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
localhost | SUCCESS => {
    "ansible_facts": {
        "ansible_python_version": "2.7.16", 
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false
}

See the notes and examples below.

Q: "What does the ansible command line tool have to do with the behavior I see with ansible-playbook?"
A: Both ansible command line tool and ansible-playbook will report the same value of ansible_python_version. For example,
shell> ansible localhost -m setup | grep ansible_python_version
        "ansible_python_version": "2.7.16",

shell> cat playbook.yml
- hosts: localhost
  tasks:
    - debug:
        var: ansible_python_version

shell> ansible-playbook playbook.yml

ok: [localhost] => {
    "ansible_python_version": "2.7.16"
}


Notes

INTERPRETER_PYTHON configuration parameter says:


Path to the Python interpreter to be used for module execution on remote targets

See How to choose a python interpreter for Ansible playbook?

Ansible will tell what Python is used on master

shell> ansible --version
ansible 2.9.2
  config file = /home/vlado/.ansible.cfg
  configured module search path = [u'/home/vlado/.ansible/my_modules']
  ansible python module location = /usr/lib/python2.7/dist-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.16 (default, Oct  7 2019, 17:36:04) [GCC 8.3.0]


dig says


The below requirements are needed on the local master node that executes this lookup: dnspython


It's possible to install both versions

shell> dpkg -l | grep dnspython
ii  python-dnspython         1.16.0-1 all          DNS toolkit for Python
ii  python3-dnspython        1.16.0-1 all          DNS toolkit for Python 3


See Ansible packages in Ubuntu what version of Python was used.

ansible_python_interpreter works as expected


On master(Ubuntu) the playbook
shell> cat playbook.yml
- hosts: test_01
  tasks:
    - getent:
        database: hosts

shell> cat hosts
test_01 ansible_python_interpreter=/usr/local/bin/python3.6

shell> ansible-playbook playbook.yml -i hosts -vvv | grep python

gives

ansible python module location = /usr/lib/python2.7/dist-packages/ansible
python version = 2.7.16 (default, Oct  7 2019, 17:36:04) [GCC 8.3.0]

...

 SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="admin"' -o ConnectTimeout=10 -o ControlPath=/export/home/vlado.config/.ansible/cp/5a3ab05cf7 -tt test_01 '/bin/sh -c '"'"'sudo -H -S -n  -u root /bin/sh -c '"'"'"'"'"'"'"'"'echo BECOME-SUCCESS-xnvwkdksdqqoehvrgebdgcnrzcrmoszv ; /usr/local/bin/python3.6 /home/admin/.ansible/tmp/ansible-tmp-1576667662.62-67468482045094/AnsiballZ_getent.py'"'"'"'"'"'"'"'"' && sleep 0'"'"''


ansible-playbook on master runs with python version = 2.7.16
module on remote (test_01) runs with /usr/local/bin/python3.6

Change ansible_python_interpreter
shell> cat hosts
test_01 ansible_python_interpreter=/usr/local/bin/python2.7

the same playbook gives

ansible python module location = /usr/lib/python2.7/dist-packages/ansible
python version = 2.7.16 (default, Oct  7 2019, 17:36:04) [GCC 8.3.0]

...

 SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="admin"' -o ConnectTimeout=10 -o ControlPath=/export/home/vlado.config/.ansible/cp/5a3ab05cf7 -tt test_01 '/bin/sh -c '"'"'sudo -H -S -n  -u root /bin/sh -c '"'"'"'"'"'"'"'"'echo BECOME-SUCCESS-fidpgwptggdoizirumvjmzuorxqawypu ; /usr/local/bin/python2.7 /home/admin/.ansible/tmp/ansible-tmp-1576668475.22-210929624017605/AnsiballZ_getent.py'"'"'"'"'"'"'"'"' && sleep 0'"'"''


ansible-playbook on master runs with python version = 2.7.16
but module on remote (test_01) runs with /usr/local/bin/python2.7 as requested

> **Comments:**
> **username_0**: Yes, but _none_ of that explains why - and _that_ is/was my point - `ansible_python` wrongly claims to be running on Python 3.x on the _local node_. Because all I do by setting `interpreter_python` in `ansible.cfg` is to give Ansible free rein on how it interprets things. But since I am depending on its choices and its information being consistent (which it evidently isn't), things blow up in my face. Regarding point 4, it's completely beside the point, because I am attempting to install the appropriate version _based on_ `ansible_python`, but Ansible simply lies to me ...  
> **username_1**: There is no evidence that *`ansible_python wrongly claims to be running on Python 3.x on the local node`*. I've added more details that might help to clarify the situation.  
> **username_0**: The evidence is the behavior one gets _with_ `when:` ... the wrong package that gets installed (provided _neither_, i.e. `python-dnspython`/`python3-dnspython`, was installed before). This is actually reproducible. But you're right, this only happens with `interpreter_python=auto` set in `ansible.cfg` for me. But then I consistently see it (and I have actually used the `debug` module to verify the value of `ansible_python` is wrong). Only if `python-dnspython` is preinstalled (`samba` installs it, for example) don't I see the issue. Otherwise I can reproduce on all 18.04 so far.  
> **username_0**: Just to make sure. I understand that the lookup plugins get called on the control/master. In my real-world case the `Preparing Ansible host` part is getting actually executed on the control/master, but the `Preparations` part gets executed on the remote machine(s). With the sole exception of the loopkup plugin, so I understand that the `dnspython` package needs to be installed on control/master and that the _result_ of the lookup is passed (by value) to the remote machine(s). In my MWE I had to condense this for obvious reasons.  
> **username_0**: _A: Both ansible command line tool and ansible-playbook will report the same value of **ansible_python_version**._ ... unfortunately not true, at least not unconditionally (see amendments to the question).  
> **username_2**: When executing "remotely" on the control host, Ansible uses the logic used for remote hosts when determining which Python to use for execution. The Python it was started with may not be the same.  
> **username_2**: @username_1: This is where I ended up now. StackExchange questions and answers are supposed to be useful to anyone who finds them, so they must be kept up to date.  
> **username_1**: I updated the answer. Is there anything, in particular, you can't find?  

---

### Answer by username_0 on 2019-12-18. Score: 1

Since this confused the hell out of me and cost me about an hour to figure out, I am going to post an answer.
As you can see I am using ansible_python to determine the Python version and install the appropriate version of dnspython. Since this is on Ubuntu 18.04 it defaults to python3. I am suppressing the respective warning with interpreter_python=auto in the ansible.cfg inside the same folder.
But here's the bummer. Apparently connection: local somehow violates the rule about using python3 whose dnspython package is installed. It made sense once I looked for dig.py (hoping for the Ansible lookup module of the same name). And indeed I found it:
$ locate dig.py
/usr/lib/python2.7/dist-packages/ansible/plugins/lookup/dig.py
/usr/lib/python2.7/dist-packages/ansible/plugins/lookup/dig.pyc

... alas, this means my version of Ansible uses Python 2.7 and so the dig module won't be able to make use of the python3-dnspython (Debian) package which I installed.
Still puzzling. But with the cause determined, I can work around it now.


---

### Answer by username_3 on 2021-05-16. Score: 0

In ansible, `lookup`'s are [executed locally](https://docs.ansible.com/ansible/latest/plugins/lookup.html) where ansible runs, not on the target server.

So you will have to ensure that `dnspython` is installed on your ansible machine.


---

