Create Ansible role to install arbitrary Python version

The purpose of this is to be able to install a version
of python that is not available on target node. It
downloads source code from Python site, compiles it
using default compiler and install it.

It also installs and upgrade python packages (pip,
setuptools, etc.) and install additional ones required
by user via Pip

Change-Id: I3212d6fc1872451942aaa82e613236351f8592a3
This commit is contained in:
Federico Ressi 2019-12-04 10:44:49 +01:00
parent d2741460ff
commit 9a76b8f9f0
9 changed files with 294 additions and 0 deletions

4
.ansible-lint Normal file
View File

@ -0,0 +1,4 @@
---
skip_list:
- '403'

1
roles/python/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
tox-py38

94
roles/python/Vagrantfile vendored Normal file
View File

@ -0,0 +1,94 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"
# Customize the count of CPU cores on the VM
CPUS = 4
# Customize the amount of memory on the VM
MEMORY = 8192
# Every Vagrant development environment requires a box. You can search for
# boxes at https://vagrantcloud.com/search.
BOX = "generic/centos7"
HOSTNAME = "tobiko"
TOX_INI_DIR = '../..'
# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
# The most common configuration options are documented and commented below.
# For a complete reference, please see the online documentation at
# https://docs.vagrantup.com.
config.vm.box = BOX
config.vm.hostname = HOSTNAME
# Disable automatic box update checking. If you disable this, then
# boxes will only be checked for updates when the user runs
# `vagrant box outdated`. This is not recommended.
config.vm.box_check_update = false
# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine. In the example below,
# accessing "localhost:8080" will access port 80 on the guest machine.
# NOTE: This will enable public access to the opened port
# config.vm.network "forwarded_port", guest: 80, host: 8080
# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine and only allow access
# via 127.0.0.1 to disable public access
# config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"
# Create a private network, which allows host-only access to the machine
# using a specific IP.
# config.vm.network "private_network", ip: DEVSTACK_HOST_IP
# Create a public network, which generally matched to bridged network.
# Bridged networks make the machine appear as another physical device on
# your network.
# config.vm.network "public_network", ip: "172.18.161.6"
# Share an additional folder to the guest VM. The first argument is
# the path on the host to the actual folder. The second argument is
# the path on the guest to mount the folder. And the optional third
# argument is a set of non-required options.
# config.vm.synced_folder "../data", "/vagrant_data"
# Provider-specific configuration so you can fine-tune various
# backing providers for Vagrant. These expose provider-specific options.
# Example for VirtualBox:
config.vm.provider "virtualbox" do |vb|
# Display the VirtualBox GUI when booting the machine
vb.gui = false
vb.cpus = CPUS
vb.memory = MEMORY
end
config.vm.provider "libvirt" do |libvirt|
libvirt.cpus = CPUS
libvirt.memory = MEMORY
end
# No need to copy tox.ini folder to nodes to execute test cases
config.vm.synced_folder TOX_INI_DIR, "/vagrant", type: "rsync",
rsync__exclude: [".tox/"]
config.vm.provision "ansible" do |ansible|
ansible.playbook = "resolv_conf.yaml"
end
config.vm.provision "ansible" do |ansible|
ansible.playbook = "tox-py38.yaml"
end
end

3
roles/python/ansible.cfg Normal file
View File

@ -0,0 +1,3 @@
[default]
# human-readable stdout/stderr results display
stdout_callback = debug

View File

@ -0,0 +1,31 @@
python_release: "3.8.0"
python_version: "3.8"
python_command: "python{{ python_version }}"
python_name: "Python-{{ python_release }}"
python_prefix: "/opt/{{ python_name }}"
python_executable: "{{ python_prefix }}/bin/{{ python_command }}"
python_url: "https://www.python.org/ftp/python/{{ python_release }}/{{ python_name }}.tgz"
python_tar: "{{ ansible_env.HOME }}/{{ python_url | basename }}"
python_src_dir: "{{ ansible_env.HOME }}/{{ python_name }}"
pip_command: "pip{{ python_version }}"
pip_executable: "{{ python_prefix }}/bin/{{ pip_command }}"
pip_url: "https://bootstrap.pypa.io/get-pip.py"
pip_installer: "{{ ansible_env.HOME }}/{{ pip_url | basename }}"
profile_file: "/etc/profile.d/{{ python_name }}.sh"
make_jobs: "{{ ansible_processor_vcpus }}"
yum_install_packages:
- "@Development tools"
- zlib-devel
- openssl-devel
- bzip2-devel
- libffi-devel
pip_install_base_packages:
- setuptools
- pip
- wheel
pip_install_packages: []

View File

@ -0,0 +1,13 @@
---
- hosts: all
tasks:
- name: Copy /etc/resolv.conf
become: yes
become_user: root
copy:
src: /etc/resolv.conf
dest: /etc/resolv.conf
owner: root
group: root
mode: '0644'

View File

@ -0,0 +1,105 @@
---
- block:
- name: check '{{ python_executable }}' is installed
shell: |
'{{ python_executable }}' --version 2>&1 | \
grep 'Python {{ python_version}}'
changed_when: false
rescue:
- name: install '{{ python_name }}' build requeirements
become: yes
become_user: root
yum:
state: present
name: '{{ yum_install_packages }}'
when: "(yum_install_packages | length) > 0"
- name: download '{{ python_name }}' from '{{ python_url }}'
get_url:
url: "{{ python_url }}"
dest: "{{ python_tar }}"
- name: ensure '{{ python_src_dir | dirname }}' directory exists
file:
path: '{{ python_src_dir }}'
state: directory
- name: extract '{{ python_tar | basename }}' into '{{ python_src_dir }}'
unarchive:
src: '{{ python_tar }}'
dest: '{{ python_src_dir | dirname }}'
remote_src: yes
- name: configure '{{ python_name }}'
command:
cmd: './configure "--prefix={{ python_prefix }}"'
chdir: '{{ python_src_dir }}'
- name: compile '{{ python_name }}'
command:
cmd: 'make -j {{ make_jobs }}'
chdir: '{{ python_src_dir }}'
- name: install '{{ python_name }}'
become: yes
become_user: root
command:
cmd: 'make install'
chdir: '{{ python_src_dir }}'
- name: check '{{ python_executable }}' is installed
shell: |
'{{ python_executable }}' --version 2>&1 | \
grep 'Python {{ python_version}}'
changed_when: false
- block:
- name: check '{{ pip_executable }}' is installed
command: "'{{ pip_executable }}' --version"
changed_when: false
rescue:
- name: download Pip installer from '{{ pip_url }}'
get_url:
url: "{{ pip_url }}"
dest: "{{ pip_installer }}"
- name: "Install '{{ pip_executable }}'"
become: yes
become_user: root
command: "'{{ python_executable }}' '{{ pip_installer }}'"
- name: check Pip is installed for '{{ pip_executable }}'
command: "'{{ pip_executable }}' --version"
changed_when: false
- name: "ensure base Python packages are installed and up-to-date"
become: true
become_user: root
pip:
executable: '{{ pip_executable }}'
state: latest
name: "{{ item }}"
vars:
ansible_python_interpreter: '{{ python_executable }}'
when: (item | length ) > 0
loop:
- "{{ pip_install_base_packages }}"
- "{{ pip_install_packages }}"
- name: add '{{ profile_file }}'
become: yes
become_user: root
template:
src: profile.sh.j2
dest: '{{ profile_file }}'
owner: root
group: root
mode: '0644'

View File

@ -0,0 +1 @@
PATH={{ python_executable | dirname }}:$PATH

View File

@ -0,0 +1,42 @@
---
- hosts: all
roles:
- role: .
vars:
python_version: "3.8"
python_release: "3.8.0"
pip_install_packages:
- virtualenv
- tox
tasks:
- name: run test cases
shell:
cmd: tox -e py38
chdir: /vagrant
register: run_test_cases
ignore_errors: true
- name: produce test reports
shell:
cmd: tox -e report 2>&1
chdir: /vagrant
- name: get test reports
fetch:
src: "/vagrant/{{ item }}"
dest: "{{ playbook_dir }}/tox-py38/{{ inventory_hostname }}/{{ item }}"
flat: yes
loop:
- tobiko_results.html
- name: check test cases result
assert:
that: run_test_cases.rc == 0
fail_msg: |
Test cases failed
{{ run_test_cases.stdout }}
success_msg: |
Test cases passed
{{ run_test_cases.stdout }}