194

I'm running into an error I've never seen before. Here is the command and the error:

$ ansible-playbook create_api.yml

PLAY [straw] ******************************************************************

GATHERING FACTS ***************************************************************
failed: [104.55.47.224] => {"failed": true, "parsed": false}
/bin/sh: 1: /usr/bin/python: not found


TASK: [typical | install required system packages] *****************************
FATAL: no hosts matched or all hosts have already failed -- aborting


PLAY RECAP ********************************************************************
           to retry, use: --limit @/Users/john/create_api.retry

104.55.47.224               : ok=0    changed=0    unreachable=0    failed=1

Here is the create_api.yml file:

---

- hosts: api
  remote_user: root
  roles:
    - api

And here is the hosts file:

[api]
104.55.47.224

I can remove the roles section and it won't make it to the first TASK, it will instead make it will only make it to the line /bin/sh: 1: /usr/bin/python: not found. What could be going on here?


NOTE: In case anyone is pinging the IP address and failing to get a response, you should know I've changed the IP address since pasting code.

EDIT python was installed locally, the problem was that it was not installed on the remote machine, which was running Ubuntu 15.04

jdavis
  • 1,987
  • 2
  • 10
  • 5
  • **Searchers**: if you're on Debian, run `apt-cache policy ansible` to see if you have version >2.8 available and if you do, make sure to install it. I got 2.7 installed by default, so I had to run `apt-get install ansible=2.9.16+dfsg-1~bpo10+2` specifically to get the version that uses Python 3. – ᴍᴇʜᴏᴠ Oct 26 '22 at 20:09

20 Answers20

175

I stumbled upon this error running ansible on Ubuntu 15.10 server, because it ships with Python 3.4.3 and ansible requires Python 2.

This is how my provision.yml looks now:

- hosts: my_app
  sudo: yes
  remote_user: root
  gather_facts: no
  pre_tasks:
    - name: 'install python2'
      raw: sudo apt-get -y install python

  tasks:
    - name: 'ensure user {{ project_name }} exists'
      user: name={{ project_name }} state=present
  • Don't forget the -y (says yes to all questions) option with apt-get (or raw module will get stuck silently)

  • gather_facts: no line is also critical (because we can't gather facts without python)

Evgenia Karunus
  • 10,715
  • 5
  • 56
  • 70
  • 12
    So then subsequent roles cannot use facts...is there a way to gather facts again? aha, http://stackoverflow.com/questions/31054453/ansible-actions-before-gathering-facts – tread May 22 '16 at 12:40
  • 16
    Note that the 'gather_facts: no' line is *also* critical. – rcreswick Jul 13 '16 at 20:31
  • 6
    @surfer190 great find! I also found that adding `action: setup` as the final pre_task also worked great :) – mrooney Aug 29 '16 at 19:40
  • 1
    @surfer190 see my answer here if you're using EC2 with ansible, you can use CloudInit to install python2 so that you can use gather facts as usual. – Miroslav Oct 15 '16 at 12:40
  • 1
    In case anyone is also wondering, it's not *necessary* to run the `raw` task to install Python 2 in `pre_tasks`; regular `tasks` works fine too. But putting it in `pre_tasks`, with another to task to call the Ansible `setup` module too, will ensure facts are available to any roles assigned to the host. – Kenny Evitt Apr 18 '17 at 03:11
  • 1
    Why do you install python-simplejson and not python itself? – Henning Oct 01 '17 at 19:52
  • 1
    This solution used to help me bypass the error when using sshpass (text password clients) and running the playbook against an Ubuntu 18.04 client. – h3ct0r Aug 04 '18 at 22:45
130

Ansible 2.2 features a tech preview of Python 3 support. To take advantage of this (so you don't have to install Python 2 on Ubuntu 16.04), just set the ansible_python_interpreter config option to /usr/bin/python3. This can be done on a per-host basis in your inventory file:

[db]
123.123.123.123 ansible_python_interpreter=/usr/bin/python3
jamix
  • 5,484
  • 5
  • 26
  • 35
  • 1
    I tried adding /usr/bin/python to this variable, but it didn't work. Adding python3 instead worked and this issue got fixed – Deep LF Apr 25 '18 at 14:07
104

Solution 1:

If you're using Ansible >2.2.0, you can set the ansible_python_interpreter configuration option to /usr/bin/python3:

ansible my_ubuntu_host -m ping -e 'ansible_python_interpreter=/usr/bin/python3'

or in your inventory file:

[ubuntu_hosts]
<xxx.xxx.xxx.xxx>

[ubuntu_hosts:vars]
ansible_python_interpreter=/usr/bin/python3

Solution 2:

If you're using Ansible <2.2.0 then you can add these pre_tasks to your playbook:

gather_facts: False
pre_tasks:
  - name: Install python for Ansible
    raw: test -e /usr/bin/python || (apt -y update && apt install -y python-minimal)
    register: output
    changed_when: output.stdout != ""
    tags: always
  - setup: # aka gather_facts

UPDATE With ansible 2.8.x, you don't need to worry about it, it's working out of the box for python > 3.5 for both controller and target machine(s)

Arbab Nazar
  • 22,378
  • 10
  • 76
  • 82
  • If you run your playbooks using tags, make sure you add tags: always to the setup task - otherwise ansible won't gather up the facts when you're using tags. – Ionut Bajescu Mar 25 '17 at 20:02
  • 17
    I have `ansible 2.3.0.0` and it doesn't work out of the box. Same error as posted by the OP. – The Coder Jun 05 '17 at 08:05
  • In case this isn't explicitly clear, you have to add this to the host inventory file, not to an included vars, i.e., it sits in the same inventory file as the host address/name. – Shawn Mehan Aug 26 '17 at 00:13
  • worked for me after updating ansible from 2.5 to the latest version, thanks for the update – Immi Dec 22 '20 at 14:46
  • i have ansible 2.9.18 and debian 11, and even if python2.7 and python3.9 are installed, that doesn't work ... always ... python not found" :'( – mik3fly-4steri5k Sep 27 '21 at 11:21
33

You can use the raw module to install Python on the remote hosts:

- raw: sudo apt-get install python-simplejson
udondan
  • 57,263
  • 20
  • 190
  • 175
  • 11
    To make sure this gets called before tasks in your role, and before any dependencies in your meta file, add it like this to your playbook: ```pre_tasks: - raw: sudo apt-get install python-simplejson ``` – Laurens Rietveld Nov 05 '15 at 13:30
  • 5
    Note that in a playbook, you must *also* disable gather_facts, or this will fail before running the raw command. (gather_facts: no) – rcreswick Jul 13 '16 at 20:30
  • @rcreswick That was my problem and your solution worked for me. Thanks so much. I put "gather_facts: no" line in my main .yml file (setup-ansible.yml) and executed the playbook with this command: "ansible-playbook -i hosts setup-ansible.yml --flush-cache -vvvvvv -kK". I used "-kK" options with ansible-playbook because the default ubuntu installation needs password to do "sudo". – IAmAliYousefi Oct 08 '16 at 09:57
  • why are you installing simplejson and not pytghon but talk about installing python? – Henning Oct 01 '17 at 19:51
  • @Henning `python-simplejson` is written in Python and therefore requires Python. simplejson also is a requirement for most Ansible core modules. By installing `python-simplejson` through `apt-get`/`yum` you also install Python and therefore cover all basic Ansible dependencies... – udondan Oct 02 '17 at 09:46
  • I know how dependencies work. But that way of installing python indirectly leads to an additional package that not neceesarily is required (at least here you wrote only that python is required). So my question was why you install a package that *depends on* python instead of python itself. – Henning Oct 06 '17 at 12:10
  • So what i do is: `- raw: sudo bash -c "test -e /usr/bin/python || (apt -y update && apt install -y python-minimal)"` and that works, too - I install exacrtly the package that I need and not something else that I dont need by itself, but which installs the package I need as dependency. – Henning Oct 06 '17 at 12:13
  • You can be as verbose as you want in your code. But simplejson IS a requirement for Ansible core modules. And this is from the docs btw... http://docs.ansible.com/ansible/latest/intro_installation.html#managed-node-requirements – udondan Oct 07 '17 at 08:46
  • That's very handy to setup everything from scratch! – deepdive Nov 01 '17 at 01:49
  • Perfect this is the real reason it fails, it reports /bin/sh: 1: /usr/bin/python: not found, but it exists, install python-simplejson and now it works. – trevrobwhite Aug 12 '21 at 09:23
18

To summarize everyone else's answers, here are the combined settings that worked for me:

 - hosts: all
   become: true
   gather_facts: false

   # Ansible requires python2, which is not installed by default on Ubuntu Xenial
   pre_tasks:
     - raw: sudo apt-get -y install python-simplejson
     # action: setup will gather facts after python2 has been installed
     - action: setup
Bobby
  • 191
  • 1
  • 3
14

I personally found 3 possible solutions to this problem that work well in different situations:

Option 1 - Set ansible_python_interpreter: /usr/bin/python3 for hosts that have python3 installed by default

I think this is the superior method for solving the problem if you have a way to group your hosts by whether or not they have python3 installed by default. As far as I'm aware, python3 is available on all Ubuntu releases 16.04 and higher.

  • If all your hosts definitely have python3, you could add the variable to your group_vars/all.yml (or equivalent):
# group_vars/all.yml

ansible_python_interpreter: /usr/bin/python3
  • If some of your hosts don't have python3 and you have a way to tag them when using dynamic inventory (e.g. AWS tagging for ec2.py), you could apply the variable to certain hosts like this:
# group_vars/tag_OS_ubuntu1804.yml

ansible_python_interpreter: /usr/bin/python3
  • If you use static inventory and are able to group hosts based on whether they have python3, you could do something like this:
# inventory/hosts

[python2_hosts]
centos7_server

[python3_hosts]
u1804_server

[python3_hosts:vars]
ansible_python_interpreter=/usr/bin/python3

I like this option the most because it requires no changes on the remote host and only minor changes to variables, as opposed to options 2 and 3, which require additions to every playbook.

Option 2 - Install Python 2 using raw

This option requires putting a play at the top of every playbook with gather_facts: false that uses raw to install python:

- name: install python2 on all instances
  hosts: "*"
  gather_facts: false
  tasks:
    - name: run apt-get update and install python
      raw: "{{ item }}"
      loop:
        - sudo apt-get update
        - sudo apt-get -y install python
      become: true
      ignore_errors: true

ignore_errors: true is required if you plan to run the play on hosts that don't have apt-get installed (e.g. anything RHEL-based), otherwise they will error out in the first play.

This solution works, but is the lowest on my list for a few reasons:

  1. Needs to go at the top of every playbook (as opposed to option 1)
  2. Assumes apt is on the system and ignores errors (as opposed to option 3)
  3. apt-get commands are slow (as opposed to option 3)

Option 3 - Symlink /usr/bin/python -> /usr/bin/python3 using raw

I haven't seen this solution proposed by anyone else. It's not ideal, but I think it's superior to option 2 in a lot of ways. My suggestion is to use raw to run a shell command to symlink /usr/bin/python -> /usr/bin/python3 if python3 is on the system and python is not:

- name: symlink /usr/bin/python -> /usr/bin/python3
  hosts: "*"
  gather_facts: false
  tasks:
    - name: symlink /usr/bin/python -> /usr/bin/python3
      raw: |
        if [ -f /usr/bin/python3 ] && [ ! -f /usr/bin/python ]; then
          ln --symbolic /usr/bin/python3 /usr/bin/python; 
        fi
      become: true

This solution is similar to option 2 in that we need to put it at the top of every playbook, but I think it's superior in a few ways:

  • Only creates the symlink in the specific case that python3 is present and python is not -- it won't override Python 2 if it's already installed
  • Does not assume apt is installed
  • Can run against all hosts without any special error handling
  • Is super fast compared to anything with apt-get

Obviously if you need Python 2 installed at /usr/bin/python, this solution is a no go and option 2 is better.

Conclusion

  • I suggest using option 1 in all cases if you can.
  • I suggest using option 3 if your inventory is really large/complex and you have no way to easily group hosts with python3, making option 1 much more difficult and error-prone.
  • I only suggest option 2 over option 3 if you need Python 2 installed at /usr/bin/python.

Sources

percygrunwald
  • 423
  • 4
  • 9
13

You need python 2.7 to run Ansible. On Ubuntu 16.04, you can install it via this command:

sudo apt-get install python-minimal

After that, I could run

ansible-playbook -i inventories/staging playbook.yml

Run ansible successfully

Zoe
  • 27,060
  • 21
  • 118
  • 148
phanvugiap
  • 336
  • 3
  • 8
12

What I used to get this working on ubuntu 15.10 on a fresh Digital Ocean droplet:

# my-playbook.yml
- name: python2
  hosts: test
  gather_facts: no
  pre_tasks:
    - raw: sudo apt-get -y install python-simplejson

$ ansible-playbook path/to/my-playbook.yml

For ubuntu 16.04 on a fresh OVH SSD, I had to apt-get upgrade before the python2 packages were available.

deadghost
  • 5,017
  • 3
  • 34
  • 46
8

I found out that it's actually possible to have multiple plays in a single playbook, so my setup now contains a "dependency provisioning" play which runs on all hosts, and other plays for specific hosts. So no more pre_tasks.

For example:

- name: dependency provisioning
  hosts: all
  become: yes
  become_method: sudo
  gather_facts: false
  tasks:
    - name: install python2
      raw: sudo apt-get -y install python-simplejson

- name: production
  hosts: production_host
  roles:
    - nginx
  tasks:
    - name: update apt cache
      apt: update_cache=yes cache_valid_time=3600
  # ....

- name: staging
  hosts: staging_host
  roles:
    - nginx
  tasks:
    - name: update apt cache
      apt: update_cache=yes cache_valid_time=3600
  # ....
Koen.
  • 25,449
  • 7
  • 83
  • 78
6

As others said, this is due to missing python2. Other answers here provide a workaround with pre_tasks and gather_facts: no, however if you're on EC2 and you spin up the instance with ansible you can use user_data option:

- ec2:
    key_name: mykey
    instance_type: t2.micro
    image: ami-123456
    wait: yes
    group: webserver
    count: 3
    vpc_subnet_id: subnet-29e63245
    assign_public_ip: yes
    user_data: |
      #!/bin/bash
      apt-get update
      apt-get install -y python-simplejson
    register: ec2

Then people usually wait for ssh to be available like this:

  - name: "Wait for the instances to boot and start ssh"
    wait_for:
      host: "{{item.public_ip}}"
      port: 22
      delay: 5
      timeout: 300
    with_items: "{{ ec2.tagged_instances }}"
    when: ec2|changed

However I've found, that this isn't always long enough as CloudInit is executed quite late in the boot process so the python2 still might not be installed right after ssh is available. So I've added a pause in case the instance was just created:

  - name: "Wait for cloud init on first boot"
    pause: minutes=2
    when: ec2|changed

This will do the job perfectly and as an advantage you're not checking for python2 on every run and you don't have to do any workarounds to gather facts later.

I'm sure other cloud providers provide similar CloudInit functionality, so adapt for your use case.

Miroslav
  • 528
  • 1
  • 6
  • 11
3

THose using Packer may find below solution helpful

let's assume that you use ansible provisioner of packer, your config may look like below

you could install python using shell provisioner first then configure ansible_python_intepreter option as shown below

"provisioners": [
    {
      "type": "shell",
      "inline": [
        "apk update && apk add --no-cache python python-dev ansible bash"
      ]
    },
    {
      "type": "ansible-local",
      "playbook_file": "playbooks/your-play-book.yml",
      "playbook_dir": "playbooks",
      "extra_arguments": [
        "-e",
        "'ansible_python_interpreter=/usr/bin/python3'",
        "-vvv"
      ]
    },
smakintel.com
  • 416
  • 4
  • 5
2

By default, Ansible requires Python 2, however, Ansible 2.2+ can work with Python 3 as well.

So either install Python 2 using the raw module, e.g.

ansible localhost --sudo -m raw -a "yum install -y python2 python-simplejson"

or set ansible_python_interpreter variable in the inventory file, like:

[local]
localhost ansible_python_interpreter="env python3"

For Docker, you can add the following line:

RUN printf '[local]\r\nlocalhost ansible_python_interpreter="env python3"\r\n' > /etc/ansible/hosts

or run it as:

ansible-playbook /ansible/provision.yml -e 'ansible_python_interpreter=/usr/bin/python3' -c local
kenorb
  • 155,785
  • 88
  • 678
  • 743
1

According to this Gist you can install Python2 on Ubuntu 16.04 as follows:

enter code here
gather_facts: False
pre_tasks:
  - raw: test -e /usr/bin/python || (apt -y update && apt install -y python-minimal)
  - setup: # aka gather_facts

tasks:
  # etc. etc.
wedesoft
  • 2,781
  • 28
  • 25
1

I was able to fix the same problem by installing Python on target machine i.e. the machine which we want to SSH to. I had used following command:

sudo apt-get install python-minimal
kenorb
  • 155,785
  • 88
  • 678
  • 743
Anshul Singhal
  • 1,983
  • 20
  • 25
1

Lots of answers.. Thanks for posting as I got started from this page too!

I did a bit of digging and it was solid with Ubuntu 14.04LTS, Ubuntu 15.04LTS appeared to have dropped the latest python, and Ubuntu 16.04LTS appears to have dropped aptitude.

I put the following action in my bootstrap before doing any apt calls:

- name: "FIX: Ubuntu 16.04 LTS doesn't come with certain modules, required by ansible"
  raw: apt-get install python-minimal aptitude -y
  become: true
  become_user: root
  become_method: sudo

If you manage become elsewhere, feel free to strip it.

Sources:

sonjz
  • 4,870
  • 3
  • 42
  • 60
1

@Miroslav, thanks for pointing me in the right direction. I used user_data in the ec2_instance module too and it works like a treat.

I.e.

- name: Creating single EC2 instance 
  ec2_instance:
    region: "{{ aws_region }}"
    key_name: "{{ aws_ec2_key_pair }}"
    name: "some-cool-name"
    instance_type: t1.micro
    image_id: ami-d38a4ab1
    security_group: sg-123456
    vpc_subnet_id: sn-678901234
    network:
        assign_public_ip: no
    volumes:
      - device_name: /dev/sda1
        ebs:
          volume_type: gp2
          volume_size: 15
    user_data: |
      #!/bin/bash
      #
      apt update
      apt install -y python-simplejson              
    termination_protection: yes
    wait: yes     
Luuk
  • 11
  • 3
1

You can indicate to Ubuntu 18.04 that you want to use python3 as the the first priority for /usr/bin/python.

- hosts: all
  become: true
  pre_tasks:
    - raw: update-alternatives --install /usr/bin/python python /usr/bin/python3 1
Ryan
  • 4,594
  • 1
  • 32
  • 35
1

If you run ansible against localhost (other-words trying provision own machine) you can do as follow.

Get path to python3

$ which python3
/usr/bin/python3

Update ansible.cfg file with interpreter_python set to python3 path.

# ansible.cfg
[defaults]
interpreter_python  = /usr/bin/python3
transport           = local
Lukasz Dynowski
  • 11,169
  • 9
  • 81
  • 124
0

I had the same issue, until I realised you also need to install python on the remote host as well as your own local machine. now it works!

GAV
  • 1,205
  • 2
  • 18
  • 38
-2

We just run into this.

We deploy ubuntu 16.04 on a vagrant so if you are not using vagrant my comment is pointless.

We installed the following vagrant plugins (trigger, shell-commander) and we get python 2.7.6 installed on the machine (which were not without thioose plugins) and after ansible can deploy

It was our last test, otherwise we were about to include this installation in a shell command in the Vagrant file

Hope it can help someone

wadoo
  • 190
  • 2
  • 5
  • 2
    If you're using Ansible, the Ansible solution below is the correct fix. Hoping that Vagrant will accidentally install it for you as a side effect of some plugins seems like asking for trouble. – Paul Becotte Jun 17 '16 at 19:44
  • Excuse me but how can you run an ansible pretask if it has not python ??? I tried the solution and it fail at the task setup so event before the pre task. The vagrant shell command in the vagrant file IS the best way to do that (for vagrant case of course) but i just notice that the vagrant plugin i had installed on my dev does the job. I'm not relying on the plugin but on the vagrant file, i just pointed out that it work also with the plugin but the vagrant file is a better choice (also for automation) because you manually don't need to do anything on each destroy/provision – wadoo Jun 18 '16 at 10:58
  • 1
    I copied that code block exactly as it was, and it worked exactly as described right before you posted your answer. I believe you probably didn't put the `gather_facts: no` line in- that requires python. The other possibility is that you also need python on the host machine, but I would guess that would have caused you errors even earlier in the process. – Paul Becotte Jun 20 '16 at 14:47
  • i copied paste too but it was friday nigth at work. I can't honnestly remember if i took the gather_fact part. Anyway in another computer ifaced it again this morning and i've just put the command in vagrantfile, it's enough for me until we go to production on real server. I will test again with a fresh mind (so not a friday evening) the answer ;) – wadoo Jun 20 '16 at 18:37