|  | 
|  | 1 | +--- | 
|  | 2 | +title:  "CentOS 7, Ansible and the end of Python 2" | 
|  | 3 | +author: Dr John A Stevenson | 
|  | 4 | +categories: | 
|  | 5 | +  - devops | 
|  | 6 | +tags: | 
|  | 7 | +  - Python | 
|  | 8 | +  - CentOS | 
|  | 9 | +  - Ansible | 
|  | 10 | +  - continuous-integration | 
|  | 11 | +--- | 
|  | 12 | + | 
|  | 13 | +The [sunsetting of Python 2](https://www.python.org/doc/sunset-python-2/) on 1 January 2020 has started to cause problems with administering our CentOS 7 servers. | 
|  | 14 | +We use [Ansible](https://www.ansible.com/) for automatic configuration and | 
|  | 15 | +a few of our long-running jobs recently began to fail as a result Python 2 / Python 3 mistmatches. | 
|  | 16 | +Officially, CentOS 7 is [supported until | 
|  | 17 | +2024-06-30](https://wiki.centos.org/About/Product), but I think the end of | 
|  | 18 | +Python 2 will hasten its demise. | 
|  | 19 | + | 
|  | 20 | +The notes below explain how we solved our issues with CentOS 7 and Ansible. | 
|  | 21 | +Hopefully they will be helpful to others. | 
|  | 22 | + | 
|  | 23 | + | 
|  | 24 | +## Background | 
|  | 25 | + | 
|  | 26 | +When you configure a machine with Ansible, it connects to it via SSH and runs | 
|  | 27 | +shell scripts and/or Python scripts to apply the settings that you requested. | 
|  | 28 | +In CentOS 7, these scripts are run by Python 2.7. | 
|  | 29 | +This causes the following problems with Python tools installed using the `pip` | 
|  | 30 | +package manager.   | 
|  | 31 | + | 
|  | 32 | + | 
|  | 33 | +  - An increasing number of packages are Python 3 only, and others e.g. `docker-compose`, are not being updated for Python 2.  As Ansible's `pip` module uses the system Python 2 interpreter by default, it may fail or get an out-of-date version. | 
|  | 34 | +  - Other Ansible modules (e.g. `docker-compose, `nsupdate`), rely on Python | 
|  | 35 | +    libraries installed on the system.  By default, Ansible will try to use | 
|  | 36 | +the Python 2 version.  Again, these may be old or may not even exist. | 
|  | 37 | + | 
|  | 38 | + | 
|  | 39 | +## Making Ansible work with Python 3 | 
|  | 40 | + | 
|  | 41 | +The solution to this is to make Ansible use Python 3 on the target system.  The | 
|  | 42 | +following steps allow this: | 
|  | 43 | + | 
|  | 44 | +### Ensure Python 3 and pip are present | 
|  | 45 | + | 
|  | 46 | +The following tasks ensure the server is able to use Python 3. | 
|  | 47 | + | 
|  | 48 | +```yaml | 
|  | 49 | +--- | 
|  | 50 | +- name: Install latest python 3 and pip3 | 
|  | 51 | +  yum: | 
|  | 52 | +    name: | 
|  | 53 | +      - python36 | 
|  | 54 | +      - libselinux-python | 
|  | 55 | +    state: present | 
|  | 56 | + | 
|  | 57 | +- name: Install SELinux for Python 3 | 
|  | 58 | +  pip: | 
|  | 59 | +    name: | 
|  | 60 | +      - selinux | 
|  | 61 | +    state: present | 
|  | 62 | +    executable: pip3 | 
|  | 63 | +  vars: | 
|  | 64 | +    ansible_python_interpreter: /usr/bin/python3 | 
|  | 65 | +``` | 
|  | 66 | +
 | 
|  | 67 | +Note that `pip3` is automatically installed with Python 3.6, and that the | 
|  | 68 | +SELinux dependencies are required by some Ansible modules. | 
|  | 69 | + | 
|  | 70 | +### Use pip3 to install packages | 
|  | 71 | + | 
|  | 72 | +The Ansible `pip` module has a flag to specify `pip3`.  Use this to install | 
|  | 73 | +packages to the system's Python 3. | 
|  | 74 | + | 
|  | 75 | +```yaml | 
|  | 76 | +- name: Install ETLHelper | 
|  | 77 | +  pip: | 
|  | 78 | +    name: | 
|  | 79 | +      - etlhelper | 
|  | 80 | +    state: present | 
|  | 81 | +    executable: pip3 | 
|  | 82 | +``` | 
|  | 83 | + | 
|  | 84 | +### Tell Ansible to use Python 3 interpreter where required | 
|  | 85 | + | 
|  | 86 | +Ansible uses the `ansible_python_interpreter` variable to define with Python to | 
|  | 87 | +use.  This must be specified wherever a module uses a Python 3 dependency e.g. | 
|  | 88 | + | 
|  | 89 | +```yaml | 
|  | 90 | +- name: Use dnsupdate to update ip host | 
|  | 91 | +  nsupdate: | 
|  | 92 | +    server: "{{ dns_server }}" | 
|  | 93 | +    zone: "{{ dns_path }}" | 
|  | 94 | +    record: "{{ ansible_hostname }}" | 
|  | 95 | +    value: "{{ ansible_default_ipv4.address }}" | 
|  | 96 | +    ttl: 600 | 
|  | 97 | +  vars: | 
|  | 98 | +    ansible_python_interpreter: /usr/bin/python3 | 
|  | 99 | +``` | 
|  | 100 | + | 
|  | 101 | +Ansible reads variable definitions according to a [defined | 
|  | 102 | +hierarchy](https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable). | 
|  | 103 | +In the example above, the variable sets Python 3 as interpreter for a single | 
|  | 104 | +task and Python 2 is used elsewhere.  We find this preferable as it is most | 
|  | 105 | +explicit and some other modules, most notably `yum`, will only work with Python | 
|  | 106 | +2. | 
0 commit comments