Add role to ensure python3 is installed on target host

Change-Id: I3b514f3a3cb1f50ac121ca9d865eb500cf49b975
This commit is contained in:
Federico Ressi 2020-03-24 12:00:47 +01:00
parent 5f01cdc90a
commit 10c1eda1fc
7 changed files with 276 additions and 2 deletions

View File

@ -0,0 +1,7 @@
---
python_version: '3'
python_command: 'python{{ python_version }}'
unversioned_python_command: '/usr/bin/python'
unversioned_python_alternative: '/usr/bin/python3'

View File

@ -0,0 +1,142 @@
# Copyright 2018 Red Hat
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import
import argparse
import json
import logging
import os
import subprocess
import sys
LOG = logging.getLogger(__name__)
GET_PYTHON_VERSION_SCRIPT = """
import sys
version = '.'.join(str(i) for i in sys.version_info[:3])
print(version)
"""
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--quiet', '-q',
action="store_true",
help="mute logging messages from STDERR")
parser.add_argument('--base', '-b',
action="store_true",
help="print Python base prefix")
args = parser.parse_args()
setup_logging(quiet=args.quiet)
version = get_python_version()
executables = [
info['executable']
for info in iter_python_executables_info(match_version=version,
base=args.base)]
info = {'version': version, 'executables': executables}
output = json.dumps(info, indent=4, sort_keys=True)
print(output)
def setup_logging(quiet=False):
if quiet:
level = logging.ERROR
else:
level = logging.DEBUG
logging.basicConfig(
level=level,
stream=sys.stderr,
format='%(name)-s: %(levelname)-7s %(asctime)-15s | %(message)s')
def get_python_version():
return '.'.join(str(i) for i in sys.version_info[:3])
def iter_python_executables_info(match_version=None, base=None):
last_error = None
for executable in iter_python_executables(base=base):
command = subprocess.list2cmdline(
[executable, '-c', GET_PYTHON_VERSION_SCRIPT])
try:
version = execute(command).splitlines()[0]
except subprocess.CalledProcessError:
LOG.exception('Unable to get version from script')
else:
if not match_version or match_version == version:
yield {'executable': executable, 'version': version}
else:
if last_error:
raise last_error
def iter_python_executables(base=None):
iterated = set()
for executable in _iter_python_executables(base=base):
# Iterate every executable only once
if executable not in iterated:
iterated.add(executable)
yield executable
def _iter_python_executables(base):
if base:
base_prefix = getattr(sys, 'base_prefix', None)
if base_prefix:
for executable in iter_prefix_executables(base_prefix):
yield executable
for executable in iter_prefix_executables(sys.prefix):
yield executable
yield sys.executable
def iter_prefix_executables(prefix):
if os.path.isdir(prefix):
for python_name in iter_versioned_names():
executable = os.path.join(prefix, 'bin', python_name)
if os.path.isfile(executable):
yield executable
def iter_versioned_names(unversioned=None):
unversioned = unversioned or 'python'
short_versioned = unversioned + str(sys.version_info[0])
long_versioned = '.'.join([short_versioned, str(sys.version_info[1])])
yield long_versioned
yield short_versioned
yield unversioned
def execute(command, *args, **kwargs):
if args or kwargs:
command = command.format(*args, **kwargs)
LOG.debug('%s', command)
env = kwargs.get('env', None)
return subprocess.check_output(command, shell=True,
universal_newlines=True,
env=env)
def name_from_path(path):
return os.path.splitext(os.path.basename(path))[0]
if __name__ == '__main__':
LOG = logging.getLogger(name_from_path(__file__))
main()

View File

@ -0,0 +1,4 @@
---
dependencies:
- role: tobiko-common

View File

@ -0,0 +1,102 @@
---
- name: "validate python_version value: {{ python_version }}"
assert:
that:
- (python_version|string).split(".") | length >= 1
- (python_version|string).split(".") | length <= 2
- (python_version|string).split(".")[0] == '3'
- name: "include OS-specific variables"
include_vars: "{{ item }}"
ignore_errors: yes
with_first_found:
- "{{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yaml"
- "{{ ansible_distribution }}.{{ ansible_architecture }}.yaml"
- "{{ ansible_distribution }}.yaml"
- "{{ ansible_os_family }}.yaml"
- block:
- name: "get Python info for '{{ python_command }}'"
script:
cmd: get_python_info.py --base --quiet
executable: '{{ python_command }}'
register: get_python_info
rescue:
- name: "install Python {{ python_version }} packages"
become: true
package:
name: "{{ python_packages }}"
- name: "get Python info for '{{ python_command }}'"
script:
cmd: get_python_info.py --base --quiet
executable: '{{ python_command }}'
register: get_python_info
- name: "set python_info fact"
set_fact:
python_info: '{{ get_python_info.stdout | from_json }}'
- name: "update Python executable facts"
set_fact:
python_command: '{{ python_info.executables | first | basename }}'
python_executable: '{{ python_info.executables | first }}'
python_version: '{{ python_info.version }}'
- name: "show Python executables facts"
debug:
msg:
python_command: '{{ python_command }}'
python_executable: '{{ python_executable }}'
python_info: '{{ python_info }}'
python_version: '{{ python_version }}'
- block:
- name: "get Python info for '{{ unversioned_python_command }}'"
script:
cmd: get_python_info.py --base --quiet
executable: '{{ unversioned_python_command }}'
register: get_unversioned_python_info
rescue:
- name: "set '{{ python_executable }}' as default alternative for python"
become: true
command: "alternatives --set python '{{ unversioned_python_alternative }}'"
- name: "get Python info for '{{ unversioned_python_command }}'"
script:
cmd: get_python_info.py --base --quiet
executable: '{{ unversioned_python_command }}'
register: get_unversioned_python_info
- name: "set unversioned_python_info fact"
set_fact:
unversioned_python_info: '{{ get_unversioned_python_info.stdout | from_json }}'
- name: "update unversioned Python executable facts"
set_fact:
unversioned_python_command:
'{{ unversioned_python_info.executables | first | basename }}'
unversioned_python_executable:
'{{ unversioned_python_info.executables | first }}'
unversioned_python_version:
'{{ unversioned_python_info.version }}'
- name: "show unversioned Python executables facts"
debug:
msg:
unversioned_python_command: '{{ unversioned_python_command }}'
unversioned_python_executable: '{{ unversioned_python_executable }}'
unversioned_python_info: '{{ unversioned_python_info }}'
unversioned_python_version: '{{ unversioned_python_version }}'

View File

@ -0,0 +1,18 @@
---
python_command: '/usr/bin/python{{ python_version }}'
python_package_name: "python{{ python_version | regex_replace('\\.', '') }}"
python_packages:
- bzip2-devel
- gcc
- make
- openssl-devel
- readline-devel
- sqlite-devel
- zlib-devel
- '{{ python_package_name }}'
- '{{ python_package_name }}-devel'
- '{{ python_package_name }}-setuptools'
- '{{ python_package_name }}-virtualenv'

View File

@ -11,9 +11,9 @@ tox_command_line: >
{% if tox_envlist %} -e {{ tox_envlist | quote }} {% endif %}
{{ tox_extra_args }}
tox_python: 'python3'
tox_python: '{{ python_executable }}'
tox_report_dir: '{{ test_report_dir | realpath }}'
tox_report_name: '{{ test_report_name }}_{{ tox_envlist }}'
tox_report_name: '{{ test_report_name }}{% if tox_envlist %}_{{ tox_envlist }}{% endif %}'
tox_report_env:
TOBIKO_TEST_REPORT_DIR: '{{ tox_report_dir }}'
TOBIKO_TEST_REPORT_NAME: '{{ tox_report_name }}'

View File

@ -2,3 +2,4 @@
dependencies:
- role: tobiko-common
- role: tobiko-ensure-python