openstack-ansible/scripts/get-ansible-role-requirements.yml
Duncan Martin Walker bf10b55c9e Use parallel git clone
The git clones of required roles contained in ansible-role-requirements
are currently single-threaded and can take a significant time when
running scripts/bootstrap-ansible.sh. A new module, git_requirements has
been implemented in order to multithreaded this, which takes
input as provided in clone_roles in get-ansible-requirements.yml.

The result of this implementation is a sizeable speed increase (~factor
of five across the full bootstrap_ansible script) for the git clone
repos task when tested on a two core VM.

The new module requires all repo information ahead of time in order to
distribute the clones between threads. This is unlike the previous
implementation, which performed the clones in sequence and generated
the appropriate data (destination URL, default refspec etc) per loop
iteration. This information is now generated within the ansible module,
with defaults supplied at the task level.

The retry and delay parameters used for the loop with the built in ansible
git module have been re-implemented in the new module to allow force
cloning, alongside retries and time delays for failed clones.
Change-Id: I3cdb248204c5e03ef98803d366e3a19ed7f8bdd8
2020-11-09 15:22:06 +00:00

140 lines
5.9 KiB
YAML

---
# Copyright 2016, 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: Clone the role ansible-role-requirements
hosts: localhost
connection: local
user: root
gather_facts: false
tasks:
- name: Remove target directory if required
file:
path: "{{ item.path | default(role_path_default) }}/{{ item.name | default(item.src | basename) }}"
state: absent
when:
- ((item.path | default(role_path_default) ~ '/' ~ item.name | default(item.src | basename) ~ '/.git') is not directory) or
(lookup('env', 'DROP_ROLE_DIRS') | bool is true)
with_items: "{{ required_roles }}"
- name: Ensure the default roles directory exists
file:
path: "{{ role_path_default }}"
state: directory
- name: Use Zuul provided sources in Zuul environment
block:
- name: Set Zuul sources path
set_fact:
zuul_src_path: "{{ lookup('env', 'ZUUL_SRC_PATH') }}"
- name: Check the Zuul src dir for cloned roles
stat:
path: "{{ zuul_src_path }}/{{ item.src.split('/')[-3:] | join('/') }}"
get_attributes: no
get_checksum: no
get_mime: no
register: zuul_roles
when:
- item.scm == "git" or item.scm is undefined
with_items: "{{ required_roles }}"
- name: Link the Zuul provided roles
file:
src: "{{ zuul_src_path }}/{{ item.item.src.split('/')[-3:] | join('/') }}"
dest: "{{ item.item.path | default(role_path_default) }}/{{ item.item.name | default(item.item.src | basename) }}"
state: link
owner: root
group: root
with_items: "{{ zuul_roles.results
| selectattr('stat.exists')
| list }}"
# NOTE(mnaser): We need to make sure that all the roles
# are checked out by Zuul so we hard fail
# if any roles are not.
- name: Fail if any roles were not cloned
fail:
msg: |
The following roles were not cloned automatically by Zuul,
make sure that they're included in required-projects {{ uncloned_roles|join(',') }}
when: uncloned_roles | length > 0
vars:
uncloned_roles: "{{ zuul_roles.results | rejectattr('stat.exists')
| map(attribute='item')
| map(attribute='src')
| select('match', 'opendev.org')
| list }}"
# Use the cached repos in the CI images rather than clone from opendev.org
- name: Generate list of openstack service repositories
set_fact:
service_git_repos: "{{ lookup('file', openstack_services_file).splitlines() |
select('contains', 'git_repo') |
select('contains', 'opendev.org') | list |
replace('https://', 'file:///openstack/src/') }}"
- name: Ensure overrides directory exists
file:
path: '/etc/openstack_deploy'
state: directory
- name: Create overrides for openstack_services git repos to use local cache
blockinfile:
path: '/etc/openstack_deploy/user_variables_zuulrepos.yml'
block: "{{ service_git_repos | join('\n') }}"
create: yes
when:
- "lookup('env', 'ZUUL_SRC_PATH') != ''"
- "lookup('env', 'UPGRADE_TARGET_BRANCH') == ''"
- name: Generate a list of user overridden roles
set_fact:
user_overridden_roles: "{{ user_roles | json_query('[*].name') }}"
- name: Generate a list of roles excluding user overridden roles
set_fact:
clone_roles: "{{ (clone_roles | default([])) + [ item ] }}"
when:
- item.scm == "git" or item.scm is undefined
- item.name not in user_overridden_roles
with_items: "{{ (zuul_roles.results | default([]) |
selectattr('stat', 'defined') |
rejectattr('stat.exists') |
map(attribute='item') | list)
| default(required_roles, True) }}"
- name: Append user overridden roles
set_fact:
clone_roles: "{{ clone_roles + user_roles }}"
- name: Clone git repos (parallel)
git_requirements:
default_path: "{{ role_path_default }}"
default_depth: 10
default_version: "master"
repo_info: "{{ clone_roles }}"
retries: "{{ git_clone_retries }}"
delay: "{{ git_clone_retry_delay }}"
force: true
core_multiplier: 4
vars:
ansible_python_interpreter: "/opt/ansible-runtime/bin/python"
required_roles: "{{ lookup('file', role_file) | from_yaml }}"
openstack_services_file: "{{ playbook_dir }}/../playbooks/defaults/repo_packages/openstack_services.yml"
role_file: "{{ playbook_dir }}/../ansible-role-requirements.yml"
role_path_default: '/etc/ansible/roles'
user_roles: "{{ lookup('file', user_role_path, errors='ignore')|default([], true) | from_yaml }}"
user_role_path: "{{ lookup('env', 'OSA_CONFIG_DIR') | default('/etc/openstack_deploy', true) ~ '/' ~ (user_role_file|default('')) }}"
git_clone_retries: 2
git_clone_retry_delay: 5