Execution
Date 15 Dec 2025 10:16:58 +0000
Duration 00:02:05.54
Controller aio1.openstack.local
User root
Versions
Ansible 2.18.6
ara 1.7.4 / 1.7.4
Python 3.12.3
Summary
17 Hosts
64 Tasks
194 Results
13 Plays
46 Files
0 Records

File: /home/zuul/src/opendev.org/openstack/ansible-role-python_venv_build/tasks/python_venv_install.yml

---
# Copyright 2018, Rackspace US, Inc.
#
# 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.

- name: Slurp up the constraints file for later re-deployment
  delegate_to: "{{ venv_build_host }}"
  ansible.builtin.slurp:
    src: "{{ _venv_build_requirements_prefix }}-constraints.txt"
  register: _constraints_file_slurp
  when: venv_wheel_build_enable | bool

# TODO(odyssey4me):
# Set a fact for the selective inclusion of the build package list.
# Perhaps do this if the distro/architecture of the target host differs
# from the build host.

# NOTE(jrosser) remove the use: parameter when https://github.com/ansible/ansible/issues/82598 is fixed
- name: Install distro packages for venv build
  ansible.builtin.package:
    name: >-
      {{
        (venv_wheel_build_enable | bool) | ternary(
          venv_install_base_distro_package_list | union(venv_install_distro_package_list),
          (
            venv_build_base_distro_package_list |
            union(venv_build_distro_package_list) |
            union(venv_install_base_distro_package_list) |
            union(venv_install_distro_package_list)
          )
        )
      }}
    state: "{{ venv_distro_package_state }}"
    update_cache: "{{ (ansible_facts['pkg_mgr'] == 'apt') | ternary('yes', omit) }}"
    cache_valid_time: "{{ (ansible_facts['pkg_mgr'] == 'apt') | ternary(venv_distro_cache_valid_time, omit) }}"
    use: "{{ ansible_facts['pkg_mgr'] }}"
  when:
    - ((venv_build_base_distro_package_list | union(venv_build_distro_package_list)) | length > 0) or
      ((venv_install_base_distro_package_list | union(venv_install_distro_package_list)) | length > 0)
  register: _install_distro_packages
  until: _install_distro_packages is success
  retries: 5
  delay: 2

- name: Ensure a fresh venv_install_destination_path if venv_rebuild is enabled
  ansible.builtin.file:
    path: "{{ venv_install_destination_path }}"
    state: absent
  when:
    - venv_rebuild | bool

- name: Create the venv_install_destination_path parent directory
  ansible.builtin.file:
    path: "{{ venv_install_destination_path }}"
    state: directory
    mode: "0755"

# Note(jrosser)
# If the constraints file from the wheel build has been previously collected
# then we must remove any git+... requirements from the input packages list
# and replace them with the package name. The slurped constraints file will
# contain the built version in the form foo=1.2.3dev4, and the requirements
# file must just specify 'foo'.
- name: Create the requirements file contents
  ansible.builtin.set_fact:
    _venv_install_pip_packages: |
      {% if (venv_wheel_build_enable | bool) and
            (_constraints_file_slurp is defined) and
            (_constraints_file_slurp.content is defined) %}
      {% set _newlist = [] %}
      {%    for item in _venv_pip_packages  %}
      {% set _x = _newlist.append(item.split('egg=')[-1]) %}
      {%    endfor %}
      {{ _newlist }}
      {% else %}
      {{ _venv_pip_packages }}
      {% endif %}

# Note(odyssey4me):
# This requirements file is not used for anything except to determine
# if requirements have changed. This helps reduce the execution time
# of the role and to make the role execution idempotent. If not for
# the conditional when installing the packages, any git constraints
# would result in the package for that constraint always being
# reinstalled.
- name: Build requirement and constraint files for the venv
  ansible.builtin.copy:
    dest: "{{ item.dest }}"
    content: "{{ item.content }}"
    mode: "0644"
  loop:
    - dest: "{{ venv_install_destination_path }}/requirements.txt"
      content: |-
        {% for item in _venv_install_pip_packages | select() %}
        {{ item }}
        {% endfor %}
    - dest: "{{ venv_install_destination_path }}/global-constraints.txt"
      content: |-
        {% for item in venv_build_global_constraints | select() %}
        {{ item }}
        {% endfor %}
    - dest: "{{ venv_install_destination_path }}/constraints.txt"
      content: |-
        {%- if (venv_wheel_build_enable | bool) and
              (_constraints_file_slurp is defined) and
              (_constraints_file_slurp.content is defined) %}
        {{ _constraints_file_slurp.content | b64decode }}
        {%- else %}
        {%   for item in venv_build_constraints | select() %}
        {{ item }}
        {%   endfor %}
        {%- endif %}
  loop_control:
    label: "{{ item.dest }}"
  register: _requirement_constraints_file

- name: Upgrade pip/setuptools/wheel to the versions we want
  ansible.builtin.pip:
    name:
      - "{{ venv_install_tool }}"
      - setuptools
      - wheel
    state: "{{ venv_pip_package_state }}"
    virtualenv: "{{ venv_install_destination_path }}"
    virtualenv_command: "{{ venv_python_executable }} -m venv"
    extra_args: >-
      --constraint {{ venv_install_destination_path }}/global-constraints.txt
      --constraint {{ venv_install_destination_path }}/constraints.txt
      --log /var/log/python_venv_build.log
      {{ venv_default_pip_install_args }}
      {{ venv_pip_install_args }}
  environment: "{{ venv_pip_install_env | combine(_pip_upgrade_noconf) }}"
  vars:
    _pip_upgrade_noconf:
      PIP_CONFIG_FILE: "{{ (venv_pip_upgrade_noconf | bool) | ternary('/dev/null', '') }}"
  register: _update_virtualenv_packages
  until: _update_virtualenv_packages is success
  retries: 5
  delay: 2
  notify:
    - venv changed

- name: Install python packages into the venv
  block:
    - name: Install python packages into the venv (pip)
      ansible.builtin.pip:
        name: "{{ _venv_install_pip_packages }}"
        state: "{{ venv_pip_package_state }}"
        executable: "{{ venv_install_destination_path }}/bin/pip"
        extra_args: >-
          --constraint {{ venv_install_destination_path }}/global-constraints.txt
          --constraint {{ venv_install_destination_path }}/constraints.txt
          --pre
          --log /var/log/python_venv_build.log
          {{ venv_default_pip_install_args }}
          {{ venv_pip_install_args }}
      environment: "{{ venv_pip_install_env }}"
      when:
        - _requirement_constraints_file.results | selectattr('changed') | length > 0
        - venv_install_tool == 'pip'
      register: _install_venv_pip_packages
      until: _install_venv_pip_packages is success
      retries: 5
      delay: 2
      notify:
        - venv changed

    - name: Install python packages into the venv (uv)
      ansible.builtin.command: >-
        {{ venv_install_destination_path }}/bin/uv pip
          {{ (venv_pip_package_state == 'absent') | ternary('uninstall', 'install') }}
          {% if venv_pip_package_state == 'latest' %}--upgrade{% endif %}
          {% if venv_pip_package_state == 'forcereinstall' %}--reinstall{% endif %}
          --python {{ venv_install_destination_path }}/bin/{{ venv_python_executable }}
          --constraint {{ venv_install_destination_path }}/global-constraints.txt
          --constraint {{ venv_install_destination_path }}/constraints.txt
          --prerelease if-necessary-or-explicit
          {{ venv_default_pip_install_args }}
          {{ venv_pip_install_args }}
          {{ _venv_install_pip_packages | join(' ') }}
      changed_when:
        - _install_venv_uv_packages.stderr_lines | select('match', '^(Installed|Uninstalled) \d* packages in \d*') | length > 0
      environment: "{{ venv_pip_install_env }}"
      when:
        - _requirement_constraints_file.results | selectattr('changed') | length > 0
        - venv_install_tool == 'uv'
      register: _install_venv_uv_packages
      until: _install_venv_uv_packages is success
      retries: 5
      delay: 2
      notify:
        - venv changed
  rescue:
    - name: Remove venv requirements/constraints files due to install failure
      ansible.builtin.file:
        path: "{{ item }}"
        state: absent
      with_items:
        - "{{ venv_install_destination_path }}/constraints.txt"
        - "{{ venv_install_destination_path }}/global-constraints.txt"
        - "{{ venv_install_destination_path }}/requirements.txt"
    - name: Show venv install failure message
      ansible.builtin.fail:
        msg: >
          The python packages have failed to install, please check the log file
          located at /var/log/python_venv_build.log for more information.

- name: Add symlinks from distribution python packages
  ansible.builtin.include_tasks: python_venv_install_symlink.yml
  args:
    apply:
      tags:
        - install
  when:
    - venv_packages_to_symlink | length > 0