Compare commits
7 Commits
v2.17.12
...
release1.8
| Author | SHA1 | Date | |
|---|---|---|---|
| ad675235e2 | |||
| 6598c83b02 | |||
| 5b3c82f4ba | |||
| e5344a7167 | |||
| 7fcfc7450d | |||
| dece0c3f08 | |||
| 672a2cc037 |
@ -1,209 +0,0 @@
|
||||
trigger:
|
||||
batch: true
|
||||
branches:
|
||||
include:
|
||||
- devel
|
||||
- stable-*
|
||||
|
||||
pr:
|
||||
autoCancel: true
|
||||
branches:
|
||||
include:
|
||||
- devel
|
||||
- stable-*
|
||||
|
||||
schedules:
|
||||
- cron: 0 7 * * *
|
||||
displayName: Nightly
|
||||
always: true
|
||||
branches:
|
||||
include:
|
||||
- devel
|
||||
- stable-*
|
||||
|
||||
variables:
|
||||
- name: checkoutPath
|
||||
value: ansible
|
||||
- name: coverageBranches
|
||||
value: devel
|
||||
- name: entryPoint
|
||||
value: .azure-pipelines/commands/entry-point.sh
|
||||
- name: fetchDepth
|
||||
value: 500
|
||||
- name: defaultContainer
|
||||
value: quay.io/ansible/azure-pipelines-test-container:6.0.0
|
||||
|
||||
pool: Standard
|
||||
|
||||
stages:
|
||||
- stage: Sanity
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Test {0}
|
||||
testFormat: sanity/{0}
|
||||
targets:
|
||||
- test: 1
|
||||
- test: 2
|
||||
- stage: Units
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Python {0}
|
||||
testFormat: units/{0}
|
||||
targets:
|
||||
- test: 3.7
|
||||
- test: 3.8
|
||||
- test: 3.9
|
||||
- test: '3.10'
|
||||
- test: 3.11
|
||||
- test: 3.12
|
||||
- stage: Windows
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Server {0}
|
||||
testFormat: windows/{0}/1
|
||||
targets:
|
||||
- test: 2016
|
||||
- test: 2019
|
||||
- test: 2022
|
||||
- stage: Remote
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml # context/target
|
||||
parameters:
|
||||
targets:
|
||||
- name: macOS 14.3
|
||||
test: macos/14.3
|
||||
- name: RHEL 9.3 py39
|
||||
test: rhel/9.3@3.9
|
||||
- name: RHEL 9.3 py311
|
||||
test: rhel/9.3@3.11
|
||||
- name: FreeBSD 13.3
|
||||
test: freebsd/13.3
|
||||
- name: FreeBSD 14.1
|
||||
test: freebsd/14.1
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
- template: templates/matrix.yml # context/controller
|
||||
parameters:
|
||||
targets:
|
||||
- name: macOS 14.3
|
||||
test: macos/14.3
|
||||
- name: RHEL 9.3
|
||||
test: rhel/9.3
|
||||
- name: FreeBSD 13.3
|
||||
test: freebsd/13.3
|
||||
- name: FreeBSD 14.1
|
||||
test: freebsd/14.1
|
||||
groups:
|
||||
- 3
|
||||
- 4
|
||||
- 5
|
||||
- template: templates/matrix.yml # context/controller (ansible-test container management)
|
||||
parameters:
|
||||
targets:
|
||||
- name: Alpine 3.19
|
||||
test: alpine/3.19
|
||||
- name: Fedora 39
|
||||
test: fedora/39
|
||||
- name: RHEL 9.3
|
||||
test: rhel/9.3
|
||||
- name: Ubuntu 22.04
|
||||
test: ubuntu/22.04
|
||||
groups:
|
||||
- 6
|
||||
- stage: Docker
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: linux/{0}
|
||||
targets:
|
||||
- name: Alpine 3.19
|
||||
test: alpine319
|
||||
- name: Fedora 39
|
||||
test: fedora39
|
||||
- name: Ubuntu 20.04
|
||||
test: ubuntu2004
|
||||
- name: Ubuntu 22.04
|
||||
test: ubuntu2204
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: linux/{0}
|
||||
targets:
|
||||
- name: Alpine 3.19
|
||||
test: alpine319
|
||||
- name: Fedora 39
|
||||
test: fedora39
|
||||
- name: Ubuntu 22.04
|
||||
test: ubuntu2204
|
||||
groups:
|
||||
- 3
|
||||
- 4
|
||||
- 5
|
||||
- stage: Galaxy
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Python {0}
|
||||
testFormat: galaxy/{0}/1
|
||||
targets:
|
||||
- test: '3.10'
|
||||
- test: 3.11
|
||||
- test: 3.12
|
||||
- stage: Generic
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Python {0}
|
||||
testFormat: generic/{0}/1
|
||||
targets:
|
||||
- test: '3.10'
|
||||
- test: 3.11
|
||||
- test: 3.12
|
||||
- stage: Incidental_Windows
|
||||
displayName: Incidental Windows
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Server {0}
|
||||
testFormat: i/windows/{0}
|
||||
targets:
|
||||
- test: 2016
|
||||
- test: 2019
|
||||
- test: 2022
|
||||
- stage: Incidental
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: i/{0}/1
|
||||
targets:
|
||||
- name: IOS Python
|
||||
test: ios/csr1000v/
|
||||
- stage: Summary
|
||||
condition: succeededOrFailed()
|
||||
dependsOn:
|
||||
- Sanity
|
||||
- Units
|
||||
- Windows
|
||||
- Remote
|
||||
- Docker
|
||||
- Galaxy
|
||||
- Generic
|
||||
- Incidental_Windows
|
||||
- Incidental
|
||||
jobs:
|
||||
- template: templates/coverage.yml
|
||||
@ -1 +0,0 @@
|
||||
remote.sh
|
||||
@ -1,34 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -o pipefail -eux
|
||||
|
||||
declare -a args
|
||||
IFS='/:' read -ra args <<< "$1"
|
||||
|
||||
cloud="${args[0]}"
|
||||
python="${args[1]}"
|
||||
group="${args[2]}"
|
||||
|
||||
target="shippable/${cloud}/group${group}/"
|
||||
|
||||
stage="${S:-prod}"
|
||||
|
||||
changed_all_target="shippable/${cloud}/smoketest/"
|
||||
|
||||
if ! ansible-test integration "${changed_all_target}" --list-targets > /dev/null 2>&1; then
|
||||
# no smoketest tests are available for this cloud
|
||||
changed_all_target="none"
|
||||
fi
|
||||
|
||||
if [ "${group}" == "1" ]; then
|
||||
# only run smoketest tests for group1
|
||||
changed_all_mode="include"
|
||||
else
|
||||
# smoketest tests already covered by group1
|
||||
changed_all_mode="exclude"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \
|
||||
--remote-terminate always --remote-stage "${stage}" \
|
||||
--docker --python "${python}" --changed-all-target "${changed_all_target}" --changed-all-mode "${changed_all_mode}"
|
||||
@ -1,62 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -o pipefail -eux
|
||||
|
||||
declare -a args
|
||||
IFS='/:' read -ra args <<< "$1"
|
||||
|
||||
script="${args[0]}"
|
||||
|
||||
test="$1"
|
||||
|
||||
docker images
|
||||
docker ps
|
||||
|
||||
for container in $(docker ps --format '{{.Image}} {{.ID}}' | grep -v -e '^quay.io/ansible/azure-pipelines-test-container:' | sed 's/^.* //'); do
|
||||
docker rm -f "${container}" || true # ignore errors
|
||||
done
|
||||
|
||||
docker ps
|
||||
|
||||
export PATH="${PWD}/bin:${PATH}"
|
||||
export PYTHONIOENCODING='utf-8'
|
||||
|
||||
if [ -n "${COVERAGE:-}" ]; then
|
||||
# on-demand coverage reporting triggered by setting the COVERAGE environment variable to a non-empty value
|
||||
export COVERAGE="--coverage"
|
||||
elif [[ "${COMMIT_MESSAGE}" =~ ci_coverage ]]; then
|
||||
# on-demand coverage reporting triggered by having 'ci_coverage' in the latest commit message
|
||||
export COVERAGE="--coverage"
|
||||
else
|
||||
# on-demand coverage reporting disabled (default behavior, always-on coverage reporting remains enabled)
|
||||
export COVERAGE="--coverage-check"
|
||||
fi
|
||||
|
||||
if [ -n "${COMPLETE:-}" ]; then
|
||||
# disable change detection triggered by setting the COMPLETE environment variable to a non-empty value
|
||||
export CHANGED=""
|
||||
elif [[ "${COMMIT_MESSAGE}" =~ ci_complete ]]; then
|
||||
# disable change detection triggered by having 'ci_complete' in the latest commit message
|
||||
export CHANGED=""
|
||||
else
|
||||
# enable change detection (default behavior)
|
||||
export CHANGED="--changed"
|
||||
fi
|
||||
|
||||
if [ "${IS_PULL_REQUEST:-}" == "true" ]; then
|
||||
# run unstable tests which are targeted by focused changes on PRs
|
||||
export UNSTABLE="--allow-unstable-changed"
|
||||
else
|
||||
# do not run unstable tests outside PRs
|
||||
export UNSTABLE=""
|
||||
fi
|
||||
|
||||
if [[ "${COVERAGE:-}" == "--coverage" ]]; then
|
||||
timeout=60
|
||||
else
|
||||
timeout=50
|
||||
fi
|
||||
|
||||
ansible-test env --dump --show --timeout "${timeout}" --color -v
|
||||
|
||||
".azure-pipelines/commands/${script}.sh" "${test}"
|
||||
@ -1 +0,0 @@
|
||||
remote.sh
|
||||
@ -1 +0,0 @@
|
||||
remote.sh
|
||||
@ -1 +0,0 @@
|
||||
cloud.sh
|
||||
@ -1,18 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -o pipefail -eux
|
||||
|
||||
declare -a args
|
||||
IFS='/:' read -ra args <<< "$1"
|
||||
|
||||
python="${args[1]}"
|
||||
|
||||
if [ "${#args[@]}" -gt 2 ]; then
|
||||
target="shippable/generic/group${args[2]}/"
|
||||
else
|
||||
target="shippable/generic/"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \
|
||||
--docker default --python "${python}"
|
||||
@ -1,14 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Temporary script to support side-by-side testing for incidental code coverage.
|
||||
# Once collection migration has been completed the incidental tests can be combined with the regular tests.
|
||||
|
||||
set -o pipefail -eux
|
||||
|
||||
declare -a args
|
||||
IFS='/:' read -ra args <<< "$1"
|
||||
|
||||
script="${args[1]}"
|
||||
|
||||
IFS='/' test="${args[*]:1}"
|
||||
|
||||
".azure-pipelines/commands/incidental/${script}.sh" "${test}"
|
||||
@ -1 +0,0 @@
|
||||
network.sh
|
||||
@ -1,40 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -o pipefail -eux
|
||||
|
||||
declare -a args
|
||||
IFS='/:' read -ra args <<< "$1"
|
||||
|
||||
platform="${args[0]}"
|
||||
version="${args[1]}"
|
||||
python_version="${args[2]}"
|
||||
|
||||
target="shippable/${platform}/incidental/"
|
||||
|
||||
stage="${S:-prod}"
|
||||
provider="${P:-default}"
|
||||
|
||||
# python versions to test in order
|
||||
# all versions run full tests
|
||||
IFS=' ' read -r -a python_versions <<< \
|
||||
"$(PYTHONPATH="${PWD}/test/lib" python -c 'from ansible_test._internal import constants; print(" ".join(constants.CONTROLLER_PYTHON_VERSIONS))')"
|
||||
|
||||
if [ "${python_version}" ]; then
|
||||
# limit tests to a single python version
|
||||
python_versions=("${python_version}")
|
||||
fi
|
||||
|
||||
for python_version in "${python_versions[@]}"; do
|
||||
# terminate remote instances on the final python version tested
|
||||
if [ "${python_version}" = "${python_versions[-1]}" ]; then
|
||||
terminate="always"
|
||||
else
|
||||
terminate="never"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
ansible-test network-integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \
|
||||
--platform "${platform}/${version}" \
|
||||
--docker default --python "${python_version}" \
|
||||
--remote-terminate "${terminate}" --remote-stage "${stage}" --remote-provider "${provider}"
|
||||
done
|
||||
@ -1,52 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -o pipefail -eux
|
||||
|
||||
declare -a args
|
||||
IFS='/:' read -ra args <<< "$1"
|
||||
|
||||
version="${args[1]}"
|
||||
|
||||
target="shippable/windows/incidental/"
|
||||
|
||||
stage="${S:-prod}"
|
||||
provider="${P:-default}"
|
||||
|
||||
# python version to run full tests on while other versions run minimal tests
|
||||
python_default="$(PYTHONPATH="${PWD}/test/lib" python -c 'from ansible_test._internal import constants; print(constants.CONTROLLER_MIN_PYTHON_VERSION)')"
|
||||
|
||||
# version to test when only testing a single version
|
||||
single_version=2022
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
ansible-test windows-integration --list-targets -v ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} > /tmp/explain.txt 2>&1 || { cat /tmp/explain.txt && false; }
|
||||
{ grep ' windows-integration: .* (targeted)$' /tmp/explain.txt || true; } > /tmp/windows.txt
|
||||
|
||||
if [ -s /tmp/windows.txt ] || [ "${CHANGED:+$CHANGED}" == "" ]; then
|
||||
echo "Detected changes requiring integration tests specific to Windows:"
|
||||
cat /tmp/windows.txt
|
||||
|
||||
echo "Running Windows integration tests for multiple versions concurrently."
|
||||
|
||||
platforms=(
|
||||
--windows "${version}"
|
||||
)
|
||||
else
|
||||
echo "No changes requiring integration tests specific to Windows were detected."
|
||||
echo "Running Windows integration tests for a single version only: ${single_version}"
|
||||
|
||||
if [ "${version}" != "${single_version}" ]; then
|
||||
echo "Skipping this job since it is for: ${version}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
platforms=(
|
||||
--windows "${version}"
|
||||
)
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
ansible-test windows-integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \
|
||||
"${platforms[@]}" \
|
||||
--docker default --python "${python_default}" \
|
||||
--remote-terminate always --remote-stage "${stage}" --remote-provider "${provider}"
|
||||
@ -1,18 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -o pipefail -eux
|
||||
|
||||
declare -a args
|
||||
IFS='/:' read -ra args <<< "$1"
|
||||
|
||||
image="${args[1]}"
|
||||
|
||||
if [ "${#args[@]}" -gt 2 ]; then
|
||||
target="shippable/posix/group${args[2]}/"
|
||||
else
|
||||
target="shippable/posix/"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \
|
||||
--docker "${image}"
|
||||
@ -1 +0,0 @@
|
||||
remote.sh
|
||||
@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -o pipefail -eux
|
||||
|
||||
declare -a args
|
||||
IFS='/:' read -ra args <<< "$1"
|
||||
|
||||
platform="${args[0]}"
|
||||
version="${args[1]}"
|
||||
pyver=default
|
||||
|
||||
# check for explicit python version like 8.3@3.8
|
||||
declare -a splitversion
|
||||
IFS='@' read -ra splitversion <<< "$version"
|
||||
|
||||
if [ "${#splitversion[@]}" -gt 1 ]; then
|
||||
version="${splitversion[0]}"
|
||||
pyver="${splitversion[1]}"
|
||||
fi
|
||||
|
||||
if [ "${#args[@]}" -gt 2 ]; then
|
||||
target="shippable/posix/group${args[2]}/"
|
||||
else
|
||||
target="shippable/posix/"
|
||||
fi
|
||||
|
||||
stage="${S:-prod}"
|
||||
provider="${P:-default}"
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \
|
||||
--python "${pyver}" --remote "${platform}/${version}" --remote-terminate always --remote-stage "${stage}" --remote-provider "${provider}" \
|
||||
@ -1 +0,0 @@
|
||||
remote.sh
|
||||
@ -1,41 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -o pipefail -eux
|
||||
|
||||
declare -a args
|
||||
IFS='/:' read -ra args <<< "$1"
|
||||
|
||||
group="${args[1]}"
|
||||
|
||||
group2_include=(
|
||||
ansible-doc
|
||||
changelog
|
||||
package-data
|
||||
pep8
|
||||
pylint
|
||||
validate-modules
|
||||
)
|
||||
|
||||
group1_exclude=(
|
||||
"${group2_include[@]}"
|
||||
)
|
||||
|
||||
options=()
|
||||
|
||||
case "${group}" in
|
||||
1)
|
||||
for name in "${group1_exclude[@]}"; do
|
||||
options+=(--skip-test "${name}")
|
||||
done
|
||||
;;
|
||||
2)
|
||||
for name in "${group2_include[@]}"; do
|
||||
options+=(--test "${name}")
|
||||
done
|
||||
;;
|
||||
esac
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
ansible-test sanity --color -v --junit ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} \
|
||||
--docker \
|
||||
"${options[@]}" --allow-disabled
|
||||
@ -1 +0,0 @@
|
||||
remote.sh
|
||||
@ -1,11 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -o pipefail -eux
|
||||
|
||||
declare -a args
|
||||
IFS='/:' read -ra args <<< "$1"
|
||||
|
||||
version="${args[1]}"
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
ansible-test units --color -v --docker default --python "${version}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} \
|
||||
@ -1,94 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -o pipefail -eux
|
||||
|
||||
declare -a args
|
||||
IFS='/:' read -ra args <<< "$1"
|
||||
|
||||
version="${args[1]}"
|
||||
group="${args[2]}"
|
||||
|
||||
target="shippable/windows/group${group}/"
|
||||
|
||||
stage="${S:-prod}"
|
||||
provider="${P:-default}"
|
||||
|
||||
# python versions to test in order
|
||||
IFS=' ' read -r -a python_versions <<< \
|
||||
"$(PYTHONPATH="${PWD}/test/lib" python -c 'from ansible_test._internal import constants; print(" ".join(constants.CONTROLLER_PYTHON_VERSIONS))')"
|
||||
|
||||
# python version to run full tests on while other versions run minimal tests
|
||||
python_default="$(PYTHONPATH="${PWD}/test/lib" python -c 'from ansible_test._internal import constants; print(constants.CONTROLLER_MIN_PYTHON_VERSION)')"
|
||||
|
||||
# version to test when only testing a single version
|
||||
single_version=2022
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
ansible-test windows-integration --list-targets -v ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} > /tmp/explain.txt 2>&1 || { cat /tmp/explain.txt && false; }
|
||||
{ grep ' windows-integration: .* (targeted)$' /tmp/explain.txt || true; } > /tmp/windows.txt
|
||||
|
||||
if [ -s /tmp/windows.txt ] || [ "${CHANGED:+$CHANGED}" == "" ]; then
|
||||
echo "Detected changes requiring integration tests specific to Windows:"
|
||||
cat /tmp/windows.txt
|
||||
|
||||
echo "Running Windows integration tests for multiple versions concurrently."
|
||||
|
||||
platforms=(
|
||||
--windows "${version}"
|
||||
)
|
||||
else
|
||||
echo "No changes requiring integration tests specific to Windows were detected."
|
||||
echo "Running Windows integration tests for a single version only: ${single_version}"
|
||||
|
||||
if [ "${version}" != "${single_version}" ]; then
|
||||
echo "Skipping this job since it is for: ${version}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
platforms=(
|
||||
--windows "${version}"
|
||||
)
|
||||
fi
|
||||
|
||||
for version in "${python_versions[@]}"; do
|
||||
changed_all_target="all"
|
||||
changed_all_mode="default"
|
||||
|
||||
if [ "${version}" == "${python_default}" ]; then
|
||||
# smoketest tests
|
||||
if [ "${CHANGED}" ]; then
|
||||
# with change detection enabled run tests for anything changed
|
||||
# use the smoketest tests for any change that triggers all tests
|
||||
ci="${target}"
|
||||
changed_all_target="shippable/windows/smoketest/"
|
||||
if [ "${target}" == "shippable/windows/group1/" ]; then
|
||||
# only run smoketest tests for group1
|
||||
changed_all_mode="include"
|
||||
else
|
||||
# smoketest tests already covered by group1
|
||||
changed_all_mode="exclude"
|
||||
fi
|
||||
else
|
||||
# without change detection enabled run entire test group
|
||||
ci="${target}"
|
||||
fi
|
||||
else
|
||||
# only run minimal tests for group1
|
||||
if [ "${target}" != "shippable/windows/group1/" ]; then continue; fi
|
||||
# minimal tests for other python versions
|
||||
ci="shippable/windows/minimal/"
|
||||
fi
|
||||
|
||||
# terminate remote instances on the final python version tested
|
||||
if [ "${version}" = "${python_versions[-1]}" ]; then
|
||||
terminate="always"
|
||||
else
|
||||
terminate="never"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
ansible-test windows-integration --color -v --retry-on-error "${ci}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \
|
||||
"${platforms[@]}" --changed-all-target "${changed_all_target}" --changed-all-mode "${changed_all_mode}" \
|
||||
--docker default --python "${version}" \
|
||||
--remote-terminate "${terminate}" --remote-stage "${stage}" --remote-provider "${provider}"
|
||||
done
|
||||
@ -1,20 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Aggregate code coverage results for later processing.
|
||||
|
||||
set -o pipefail -eu
|
||||
|
||||
agent_temp_directory="$1"
|
||||
|
||||
PATH="${PWD}/bin:${PATH}"
|
||||
|
||||
mkdir "${agent_temp_directory}/coverage/"
|
||||
|
||||
options=(--venv --color -v)
|
||||
|
||||
ansible-test coverage combine --group-by command --export "${agent_temp_directory}/coverage/" "${options[@]}"
|
||||
|
||||
if ansible-test coverage analyze targets generate --help >/dev/null 2>&1; then
|
||||
# Only analyze coverage if the installed version of ansible-test supports it.
|
||||
# Doing so allows this script to work unmodified for multiple Ansible versions.
|
||||
ansible-test coverage analyze targets generate "${agent_temp_directory}/coverage/coverage-analyze-targets.json" "${options[@]}"
|
||||
fi
|
||||
@ -1,59 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Combine coverage data from multiple jobs, keeping the data only from the most recent attempt from each job.
|
||||
Coverage artifacts must be named using the format: "Coverage $(System.JobAttempt) {StableUniqueNameForEachJob}"
|
||||
The recommended coverage artifact name format is: Coverage $(System.JobAttempt) $(System.StageDisplayName) $(System.JobDisplayName)
|
||||
Keep in mind that Azure Pipelines does not enforce unique job display names (only names).
|
||||
It is up to pipeline authors to avoid name collisions when deviating from the recommended format.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
"""Main program entry point."""
|
||||
source_directory = sys.argv[1]
|
||||
|
||||
if '/ansible_collections/' in os.getcwd():
|
||||
output_path = "tests/output"
|
||||
else:
|
||||
output_path = "test/results"
|
||||
|
||||
destination_directory = os.path.join(output_path, 'coverage')
|
||||
|
||||
if not os.path.exists(destination_directory):
|
||||
os.makedirs(destination_directory)
|
||||
|
||||
jobs = {}
|
||||
count = 0
|
||||
|
||||
for name in os.listdir(source_directory):
|
||||
match = re.search('^Coverage (?P<attempt>[0-9]+) (?P<label>.+)$', name)
|
||||
label = match.group('label')
|
||||
attempt = int(match.group('attempt'))
|
||||
jobs[label] = max(attempt, jobs.get(label, 0))
|
||||
|
||||
for label, attempt in jobs.items():
|
||||
name = 'Coverage {attempt} {label}'.format(label=label, attempt=attempt)
|
||||
source = os.path.join(source_directory, name)
|
||||
source_files = os.listdir(source)
|
||||
|
||||
for source_file in source_files:
|
||||
source_path = os.path.join(source, source_file)
|
||||
destination_path = os.path.join(destination_directory, source_file + '.' + label)
|
||||
print('"%s" -> "%s"' % (source_path, destination_path))
|
||||
shutil.copyfile(source_path, destination_path)
|
||||
count += 1
|
||||
|
||||
print('Coverage file count: %d' % count)
|
||||
print('##vso[task.setVariable variable=coverageFileCount]%d' % count)
|
||||
print('##vso[task.setVariable variable=outputPath]%s' % output_path)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@ -1,24 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Check the test results and set variables for use in later steps.
|
||||
|
||||
set -o pipefail -eu
|
||||
|
||||
if [[ "$PWD" =~ /ansible_collections/ ]]; then
|
||||
output_path="tests/output"
|
||||
else
|
||||
output_path="test/results"
|
||||
fi
|
||||
|
||||
echo "##vso[task.setVariable variable=outputPath]${output_path}"
|
||||
|
||||
if compgen -G "${output_path}"'/junit/*.xml' > /dev/null; then
|
||||
echo "##vso[task.setVariable variable=haveTestResults]true"
|
||||
fi
|
||||
|
||||
if compgen -G "${output_path}"'/bot/ansible-test-*' > /dev/null; then
|
||||
echo "##vso[task.setVariable variable=haveBotResults]true"
|
||||
fi
|
||||
|
||||
if compgen -G "${output_path}"'/coverage/*' > /dev/null; then
|
||||
echo "##vso[task.setVariable variable=haveCoverageData]true"
|
||||
fi
|
||||
@ -1,102 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Upload code coverage reports to codecov.io.
|
||||
Multiple coverage files from multiple languages are accepted and aggregated after upload.
|
||||
Python coverage, as well as PowerShell and Python stubs can all be uploaded.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import dataclasses
|
||||
import pathlib
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import typing as t
|
||||
import urllib.request
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class CoverageFile:
|
||||
name: str
|
||||
path: pathlib.Path
|
||||
flags: t.List[str]
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class Args:
|
||||
dry_run: bool
|
||||
path: pathlib.Path
|
||||
|
||||
|
||||
def parse_args() -> Args:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-n', '--dry-run', action='store_true')
|
||||
parser.add_argument('path', type=pathlib.Path)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Store arguments in a typed dataclass
|
||||
fields = dataclasses.fields(Args)
|
||||
kwargs = {field.name: getattr(args, field.name) for field in fields}
|
||||
|
||||
return Args(**kwargs)
|
||||
|
||||
|
||||
def process_files(directory: pathlib.Path) -> t.Tuple[CoverageFile, ...]:
|
||||
processed = []
|
||||
for file in directory.joinpath('reports').glob('coverage*.xml'):
|
||||
name = file.stem.replace('coverage=', '')
|
||||
|
||||
# Get flags from name
|
||||
flags = name.replace('-powershell', '').split('=') # Drop '-powershell' suffix
|
||||
flags = [flag if not flag.startswith('stub') else flag.split('-')[0] for flag in flags] # Remove "-01" from stub files
|
||||
|
||||
processed.append(CoverageFile(name, file, flags))
|
||||
|
||||
return tuple(processed)
|
||||
|
||||
|
||||
def upload_files(codecov_bin: pathlib.Path, files: t.Tuple[CoverageFile, ...], dry_run: bool = False) -> None:
|
||||
for file in files:
|
||||
cmd = [
|
||||
str(codecov_bin),
|
||||
'--name', file.name,
|
||||
'--file', str(file.path),
|
||||
]
|
||||
for flag in file.flags:
|
||||
cmd.extend(['--flags', flag])
|
||||
|
||||
if dry_run:
|
||||
print(f'DRY-RUN: Would run command: {cmd}')
|
||||
continue
|
||||
|
||||
subprocess.run(cmd, check=True)
|
||||
|
||||
|
||||
def download_file(url: str, dest: pathlib.Path, flags: int, dry_run: bool = False) -> None:
|
||||
if dry_run:
|
||||
print(f'DRY-RUN: Would download {url} to {dest} and set mode to {flags:o}')
|
||||
return
|
||||
|
||||
with urllib.request.urlopen(url) as resp:
|
||||
with dest.open('w+b') as f:
|
||||
# Read data in chunks rather than all at once
|
||||
shutil.copyfileobj(resp, f, 64 * 1024)
|
||||
|
||||
dest.chmod(flags)
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
url = 'https://ci-files.testing.ansible.com/codecov/linux/codecov'
|
||||
with tempfile.TemporaryDirectory(prefix='codecov-') as tmpdir:
|
||||
codecov_bin = pathlib.Path(tmpdir) / 'codecov'
|
||||
download_file(url, codecov_bin, 0o755, args.dry_run)
|
||||
|
||||
files = process_files(args.path)
|
||||
upload_files(codecov_bin, files, args.dry_run)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@ -1,17 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Generate code coverage reports for uploading to Azure Pipelines and codecov.io.
|
||||
|
||||
set -o pipefail -eu
|
||||
|
||||
PATH="${PWD}/bin:${PATH}"
|
||||
|
||||
if ! ansible-test --help >/dev/null 2>&1; then
|
||||
# Install the devel version of ansible-test for generating code coverage reports.
|
||||
# This is only used by Ansible Collections, which are typically tested against multiple Ansible versions (in separate jobs).
|
||||
# Since a version of ansible-test is required that can work the output from multiple older releases, the devel version is used.
|
||||
pip install https://github.com/ansible/ansible/archive/devel.tar.gz --disable-pip-version-check
|
||||
fi
|
||||
|
||||
# Generate stubs using docker (if supported) otherwise fall back to using a virtual environment instead.
|
||||
# The use of docker is required when Powershell code is present, but Ansible 2.12 was the first version to support --docker with coverage.
|
||||
ansible-test coverage xml --group-by command --stub --docker --color -v || ansible-test coverage xml --group-by command --stub --venv --color -v
|
||||
@ -1,34 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Configure the test environment and run the tests.
|
||||
|
||||
set -o pipefail -eu
|
||||
|
||||
entry_point="$1"
|
||||
test="$2"
|
||||
read -r -a coverage_branches <<< "$3" # space separated list of branches to run code coverage on for scheduled builds
|
||||
|
||||
export COMMIT_MESSAGE
|
||||
export COMPLETE
|
||||
export COVERAGE
|
||||
export IS_PULL_REQUEST
|
||||
|
||||
if [ "${SYSTEM_PULLREQUEST_TARGETBRANCH:-}" ]; then
|
||||
IS_PULL_REQUEST=true
|
||||
COMMIT_MESSAGE=$(git log --format=%B -n 1 HEAD^2)
|
||||
else
|
||||
IS_PULL_REQUEST=
|
||||
COMMIT_MESSAGE=$(git log --format=%B -n 1 HEAD)
|
||||
fi
|
||||
|
||||
COMPLETE=
|
||||
COVERAGE=
|
||||
|
||||
if [ "${BUILD_REASON}" = "Schedule" ]; then
|
||||
COMPLETE=yes
|
||||
|
||||
if printf '%s\n' "${coverage_branches[@]}" | grep -q "^${BUILD_SOURCEBRANCHNAME}$"; then
|
||||
COVERAGE=yes
|
||||
fi
|
||||
fi
|
||||
|
||||
"${entry_point}" "${test}" 2>&1 | "$(dirname "$0")/time-command.py"
|
||||
@ -1,24 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""Prepends a relative timestamp to each input line from stdin and writes it to stdout."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import time
|
||||
|
||||
|
||||
def main():
|
||||
"""Main program entry point."""
|
||||
start = time.time()
|
||||
|
||||
sys.stdin.reconfigure(errors='surrogateescape')
|
||||
sys.stdout.reconfigure(errors='surrogateescape')
|
||||
|
||||
for line in sys.stdin:
|
||||
seconds = time.time() - start
|
||||
sys.stdout.write('%02d:%02d %s' % (seconds // 60, seconds % 60, line))
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@ -1,29 +0,0 @@
|
||||
# This template adds a job for processing code coverage data.
|
||||
# It will upload results to Azure Pipelines and codecov.io.
|
||||
# Use it from a job stage that completes after all other jobs have completed.
|
||||
# This can be done by placing it in a separate summary stage that runs after the test stage(s) have completed.
|
||||
|
||||
jobs:
|
||||
- job: Coverage
|
||||
displayName: Code Coverage
|
||||
container: $[ variables.defaultContainer ]
|
||||
workspace:
|
||||
clean: all
|
||||
steps:
|
||||
- checkout: self
|
||||
fetchDepth: $(fetchDepth)
|
||||
path: $(checkoutPath)
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: Download Coverage Data
|
||||
inputs:
|
||||
path: coverage/
|
||||
patterns: "Coverage */*=coverage.combined"
|
||||
- bash: .azure-pipelines/scripts/combine-coverage.py coverage/
|
||||
displayName: Combine Coverage Data
|
||||
- bash: .azure-pipelines/scripts/report-coverage.sh
|
||||
displayName: Generate Coverage Report
|
||||
condition: gt(variables.coverageFileCount, 0)
|
||||
- bash: .azure-pipelines/scripts/publish-codecov.py "$(outputPath)"
|
||||
displayName: Publish to codecov.io
|
||||
condition: gt(variables.coverageFileCount, 0)
|
||||
continueOnError: true
|
||||
@ -1,55 +0,0 @@
|
||||
# This template uses the provided targets and optional groups to generate a matrix which is then passed to the test template.
|
||||
# If this matrix template does not provide the required functionality, consider using the test template directly instead.
|
||||
|
||||
parameters:
|
||||
# A required list of dictionaries, one per test target.
|
||||
# Each item in the list must contain a "test" or "name" key.
|
||||
# Both may be provided. If one is omitted, the other will be used.
|
||||
- name: targets
|
||||
type: object
|
||||
|
||||
# An optional list of values which will be used to multiply the targets list into a matrix.
|
||||
# Values can be strings or numbers.
|
||||
- name: groups
|
||||
type: object
|
||||
default: []
|
||||
|
||||
# An optional format string used to generate the job name.
|
||||
# - {0} is the name of an item in the targets list.
|
||||
- name: nameFormat
|
||||
type: string
|
||||
default: "{0}"
|
||||
|
||||
# An optional format string used to generate the test name.
|
||||
# - {0} is the name of an item in the targets list.
|
||||
- name: testFormat
|
||||
type: string
|
||||
default: "{0}"
|
||||
|
||||
# An optional format string used to add the group to the job name.
|
||||
# {0} is the formatted name of an item in the targets list.
|
||||
# {{1}} is the group -- be sure to include the double "{{" and "}}".
|
||||
- name: nameGroupFormat
|
||||
type: string
|
||||
default: "{0} - {{1}}"
|
||||
|
||||
# An optional format string used to add the group to the test name.
|
||||
# {0} is the formatted test of an item in the targets list.
|
||||
# {{1}} is the group -- be sure to include the double "{{" and "}}".
|
||||
- name: testGroupFormat
|
||||
type: string
|
||||
default: "{0}/{{1}}"
|
||||
|
||||
jobs:
|
||||
- template: test.yml
|
||||
parameters:
|
||||
jobs:
|
||||
- ${{ if eq(length(parameters.groups), 0) }}:
|
||||
- ${{ each target in parameters.targets }}:
|
||||
- name: ${{ format(parameters.nameFormat, coalesce(target.name, target.test)) }}
|
||||
test: ${{ format(parameters.testFormat, coalesce(target.test, target.name)) }}
|
||||
- ${{ if not(eq(length(parameters.groups), 0)) }}:
|
||||
- ${{ each group in parameters.groups }}:
|
||||
- ${{ each target in parameters.targets }}:
|
||||
- name: ${{ format(format(parameters.nameGroupFormat, parameters.nameFormat), coalesce(target.name, target.test), group) }}
|
||||
test: ${{ format(format(parameters.testGroupFormat, parameters.testFormat), coalesce(target.test, target.name), group) }}
|
||||
@ -1,45 +0,0 @@
|
||||
# This template uses the provided list of jobs to create test one or more test jobs.
|
||||
# It can be used directly if needed, or through the matrix template.
|
||||
|
||||
parameters:
|
||||
# A required list of dictionaries, one per test job.
|
||||
# Each item in the list must contain a "job" and "name" key.
|
||||
- name: jobs
|
||||
type: object
|
||||
|
||||
jobs:
|
||||
- ${{ each job in parameters.jobs }}:
|
||||
- job: test_${{ replace(replace(replace(replace(job.test, '/', '_'), '.', '_'), '-', '_'), '@', '_') }}
|
||||
displayName: ${{ job.name }}
|
||||
container: $[ variables.defaultContainer ]
|
||||
workspace:
|
||||
clean: all
|
||||
steps:
|
||||
- checkout: self
|
||||
fetchDepth: $(fetchDepth)
|
||||
path: $(checkoutPath)
|
||||
- bash: .azure-pipelines/scripts/run-tests.sh "$(entryPoint)" "${{ job.test }}" "$(coverageBranches)"
|
||||
displayName: Run Tests
|
||||
- bash: .azure-pipelines/scripts/process-results.sh
|
||||
condition: succeededOrFailed()
|
||||
displayName: Process Results
|
||||
- bash: .azure-pipelines/scripts/aggregate-coverage.sh "$(Agent.TempDirectory)"
|
||||
condition: eq(variables.haveCoverageData, 'true')
|
||||
displayName: Aggregate Coverage Data
|
||||
- task: PublishTestResults@2
|
||||
condition: eq(variables.haveTestResults, 'true')
|
||||
inputs:
|
||||
testResultsFiles: "$(outputPath)/junit/*.xml"
|
||||
displayName: Publish Test Results
|
||||
- task: PublishPipelineArtifact@1
|
||||
condition: eq(variables.haveBotResults, 'true')
|
||||
displayName: Publish Bot Results
|
||||
inputs:
|
||||
targetPath: "$(outputPath)/bot/"
|
||||
artifactName: "Bot $(System.JobAttempt) $(System.StageDisplayName) $(System.JobDisplayName)"
|
||||
- task: PublishPipelineArtifact@1
|
||||
condition: eq(variables.haveCoverageData, 'true')
|
||||
displayName: Publish Coverage Data
|
||||
inputs:
|
||||
targetPath: "$(Agent.TempDirectory)/coverage/"
|
||||
artifactName: "Coverage $(System.JobAttempt) $(System.StageDisplayName) $(System.JobDisplayName)"
|
||||
@ -1,5 +0,0 @@
|
||||
team = "ansible"
|
||||
repo = "ansible"
|
||||
check_sha = "f31421576b00f0b167cdbe61217c31c21a41ac02" # the very first commit in repo
|
||||
fix_commit_msg = false # Don't replace "#" with "GH-"
|
||||
default_branch = "devel"
|
||||
@ -1,4 +0,0 @@
|
||||
# .git-blame-ignore-revs
|
||||
# Bulk PowerShell sanity fixes
|
||||
6def4a3180fe03981ba64c6d8db28fed3bb39c0c
|
||||
716631189cb5a3f66b3add98f39e64e98bc17bf7
|
||||
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -1 +0,0 @@
|
||||
.github/ export-ignore
|
||||
3
.github/CODE_OF_CONDUCT.md
vendored
3
.github/CODE_OF_CONDUCT.md
vendored
@ -1,3 +0,0 @@
|
||||
# Community Code of Conduct
|
||||
|
||||
Please see the official [Ansible Community Code of Conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html).
|
||||
70
.github/CONTRIBUTING.md
vendored
70
.github/CONTRIBUTING.md
vendored
@ -1,70 +0,0 @@
|
||||
# WELCOME TO ANSIBLE GITHUB
|
||||
|
||||
Hi! Nice to see you here!
|
||||
|
||||
## QUESTIONS ?
|
||||
|
||||
Please see the [Community Guide](https://docs.ansible.com/ansible/latest/community/index.html) for information on how to ask questions on the [mailing lists](https://docs.ansible.com/ansible/latest/community/communication.html#mailing-list-information) and IRC.
|
||||
|
||||
The GitHub issue tracker is not the best place for questions for various reasons, but both IRC and the mailing list are very helpful places for those things, as the community page explains best.
|
||||
|
||||
## CONTRIBUTING ?
|
||||
|
||||
By contributing to this project you agree to the [Developer Certificate of Origin (DCO)](#developer-certificate-of-origin-dco).
|
||||
|
||||
The Ansible project is licensed under the [GPL-3.0](COPYING) or later. Some portions of the code fall under other licenses as noted in individual files.
|
||||
|
||||
The Ansible project accepts contributions through GitHub pull requests. Please review the [Community Guide](https://docs.ansible.com/ansible/latest/community/index.html) for more information on contributing to Ansible.
|
||||
|
||||
## BUG TO REPORT ?
|
||||
|
||||
First and foremost, also check the [Community Guide](https://docs.ansible.com/ansible/latest/community/index.html).
|
||||
|
||||
You can report bugs or make enhancement requests at the [Ansible GitHub issue page](http://github.com/ansible/ansible/issues/new/choose) by filling out the issue template that will be presented.
|
||||
|
||||
Also please make sure you are testing on the latest released version of Ansible or the development branch; see the [Installation Guide](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html) for details.
|
||||
|
||||
Thanks!
|
||||
|
||||
## DEVELOPER CERTIFICATE OF ORIGIN (DCO)
|
||||
|
||||
This document was created by the Linux Kernel community and is a simple statement that you, as a contributor, have the legal right to make the contribution.
|
||||
|
||||
```text
|
||||
Developer Certificate of Origin
|
||||
Version 1.1
|
||||
|
||||
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||
1 Letterman Drive
|
||||
Suite D4700
|
||||
San Francisco, CA, 94129
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
|
||||
Developer's Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the open source license(s) involved.
|
||||
```
|
||||
1
.github/ISSUE_TEMPLATE.md
vendored
1
.github/ISSUE_TEMPLATE.md
vendored
@ -1 +0,0 @@
|
||||
Please raise issues via the [new interface](https://github.com/ansible/ansible/issues/new/choose)
|
||||
265
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
265
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -1,265 +0,0 @@
|
||||
---
|
||||
name: 🐛 Bug report
|
||||
description: Create a report to help us improve
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: >
|
||||
**Thank you for wanting to report a bug in ansible-core!**
|
||||
|
||||
|
||||
⚠
|
||||
Verify first that your issue is not [already reported on
|
||||
GitHub][issue search] and keep in mind that we may have to keep
|
||||
the current behavior because [every change breaks someone's
|
||||
workflow][XKCD 1172].
|
||||
We try to be mindful about this.
|
||||
|
||||
Also test if the latest release and devel branch are affected too.
|
||||
|
||||
|
||||
**Tip:** If you are seeking community support, please consider
|
||||
[starting a mailing list thread or chatting in IRC][ML||IRC].
|
||||
|
||||
|
||||
|
||||
[ML||IRC]:
|
||||
https://docs.ansible.com/ansible-core/devel/community/communication.html?utm_medium=github&utm_source=issue_form--bug_report.yml#mailing-list-information
|
||||
|
||||
[issue search]: ../search?q=is%3Aissue&type=issues
|
||||
|
||||
[XKCD 1172]: https://xkcd.com/1172/
|
||||
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Summary
|
||||
description: Explain the problem briefly below.
|
||||
placeholder: >-
|
||||
When I try to do X with ansible-core from the devel branch on GitHub, Y
|
||||
breaks in a way Z under the env E. Here are all the details I know
|
||||
about this problem...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Issue Type
|
||||
description: >
|
||||
Please select the single available option in the drop-down.
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<em>Why?</em>
|
||||
</summary>
|
||||
|
||||
We would do it by ourselves but unfortunatelly, the curent
|
||||
edition of GitHub Issue Forms Alpha does not support this yet 🤷
|
||||
|
||||
|
||||
_We will make it easier in the future, once GitHub
|
||||
supports dropdown defaults. Promise!_
|
||||
|
||||
</details>
|
||||
# FIXME: Once GitHub allows defining the default choice, update this
|
||||
options:
|
||||
- Bug Report
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Component Name
|
||||
description: >
|
||||
Write the short name of the rst file, module, plugin, task or
|
||||
feature below, *use your best guess if unsure*.
|
||||
|
||||
|
||||
**Tip:** Cannot find it in this repository? Please be advised that
|
||||
the source for some parts of the documentation are hosted outside
|
||||
of this repository. If the page you are reporting describes
|
||||
modules/plugins/etc that are not officially supported by the
|
||||
Ansible Core Engineering team, there is a good chance that it is
|
||||
coming from one of the [Ansible Collections maintained by the
|
||||
community][collections org]. If this is the case, please make sure
|
||||
to file an issue under the appropriate project there instead.
|
||||
|
||||
|
||||
[collections org]: /ansible-collections
|
||||
placeholder: dnf, apt, pip, user etc.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Ansible Version
|
||||
description: >-
|
||||
Paste verbatim output from `ansible --version` below, under
|
||||
the prompt line. Please don't wrap it with triple backticks — your
|
||||
whole input will be turned into a code snippet automatically.
|
||||
render: console
|
||||
value: |
|
||||
$ ansible --version
|
||||
placeholder: |
|
||||
$ ansible --version
|
||||
ansible [core 2.11.0b4.post0] (detached HEAD ref: refs/) last updated 2021/04/02 00:33:35 (GMT +200)
|
||||
config file = None
|
||||
configured module search path = ['~/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
|
||||
ansible python module location = ~/src/github/ansible/ansible/lib/ansible
|
||||
ansible collection location = ~/.ansible/collections:/usr/share/ansible/collections
|
||||
executable location = bin/ansible
|
||||
python version = 3.9.0 (default, Oct 26 2020, 13:08:59) [GCC 10.2.0]
|
||||
jinja version = 2.11.3
|
||||
libyaml = True
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Configuration
|
||||
description: >-
|
||||
Paste verbatim output from `ansible-config dump --only-changed -t all` below, under the prompt line.
|
||||
(if using a version older than ansible-core 2.12 you should omit the '-t all')
|
||||
Please don't wrap it with triple backticks — your
|
||||
whole input will be turned into a code snippet automatically.
|
||||
render: console
|
||||
value: |
|
||||
# if using a version older than ansible-core 2.12 you should omit the '-t all'
|
||||
$ ansible-config dump --only-changed -t all
|
||||
placeholder: |
|
||||
# if using a version older than ansible-core 2.12 you should omit the '-t all'
|
||||
$ ansible-config dump --only-changed -t all
|
||||
DEFAULT_GATHERING(~/src/github/ansible/ansible/ansible.cfg) = smart
|
||||
DEFAULT_HOST_LIST(~/src/github/ansible/ansible/ansible.cfg) = ['~/src/github/ansible/ansible/hosts']
|
||||
DEFAULT_VAULT_PASSWORD_FILE(~/src/github/ansible/ansible/ansible.cfg) = ~/src/github/ansible/ansible/vault/print-password.sh
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: OS / Environment
|
||||
description: >-
|
||||
Provide all relevant information below, e.g. target OS versions,
|
||||
network device firmware, etc.
|
||||
placeholder: RHEL 8, CentOS Stream etc.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to Reproduce
|
||||
description: |
|
||||
Describe exactly how to reproduce the problem, using a minimal test-case. It would *really* help us understand your problem if you could also pased any playbooks, configs and commands you used.
|
||||
|
||||
**HINT:** You can paste https://gist.github.com links for larger files.
|
||||
value: |
|
||||
<!--- Paste example playbooks or commands between quotes below -->
|
||||
```yaml (paste below)
|
||||
|
||||
```
|
||||
placeholder: |
|
||||
1. Implement the following playbook:
|
||||
|
||||
```yaml
|
||||
---
|
||||
# ping.yml
|
||||
- hosts: all
|
||||
gather_facts: false
|
||||
tasks:
|
||||
- ping:
|
||||
...
|
||||
```
|
||||
2. Then run `ANSIBLE_DEBUG=1 ansible-playbook ping.yml -vvvvv`
|
||||
3. An error occurs.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected Results
|
||||
description: >-
|
||||
Describe what you expected to happen when running the steps above.
|
||||
placeholder: >-
|
||||
I expected X to happen because I assumed Y and was shocked
|
||||
that it did not.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Actual Results
|
||||
description: |
|
||||
Describe what actually happened. If possible run with extra verbosity (`-vvvv`).
|
||||
|
||||
Paste verbatim command output and don't wrap it with triple backticks — your
|
||||
whole input will be turned into a code snippet automatically.
|
||||
render: console
|
||||
placeholder: >-
|
||||
Certificate did not match expected hostname: files.pythonhosted.org. Certificate: {'notAfter': 'Apr 28 19:20:25 2021 GMT', 'subjectAltName': ((u'DNS', 'r.ssl.fastly.net'), (u'DNS', '*.catchpoint.com'), (u'DNS', '*.cnn.io'), (u'DNS', '*.dollarshaveclub.com'), (u'DNS', '*.eater.com'), (u'DNS', '*.fastly.picmonkey.com'), (u'DNS', '*.files.saymedia-content.com'), (u'DNS', '*.ft.com'), (u'DNS', '*.meetupstatic.com'), (u'DNS', '*.nfl.com'), (u'DNS', '*.pagar.me'), (u'DNS', '*.picmonkey.com'), (u'DNS', '*.realself.com'), (u'DNS', '*.sbnation.com'), (u'DNS', '*.shakr.com'), (u'DNS', '*.streamable.com'), (u'DNS', '*.surfly.com'), (u'DNS', '*.theverge.com'), (u'DNS', '*.thrillist.com'), (u'DNS', '*.vox-cdn.com'), (u'DNS', '*.vox.com'), (u'DNS', '*.voxmedia.com'), (u'DNS', 'eater.com'), (u'DNS', 'ft.com'), (u'DNS', 'i.gse.io'), (u'DNS', 'picmonkey.com'), (u'DNS', 'realself.com'), (u'DNS', 'static.wixstatic.com'), (u'DNS', 'streamable.com'), (u'DNS', 'surfly.com'), (u'DNS', 'theverge.com'), (u'DNS', 'vox-cdn.com'), (u'DNS', 'vox.com'), (u'DNS', 'www.joyent.com')), 'subject': ((('countryName', u'US'),), (('stateOrProvinceName', u'California'),), (('localityName', u'San Francisco'),), (('organizationName', u'Fastly, Inc'),), (('commonName', u'r.ssl.fastly.net'),))}
|
||||
Exception:
|
||||
Traceback (most recent call last):
|
||||
File "/usr/local/lib/python2.6/dist-packages/pip/basecommand.py", line 215, in main
|
||||
status = self.run(options, args)
|
||||
File "/usr/local/lib/python2.6/dist-packages/pip/commands/install.py", line 335, in run
|
||||
wb.build(autobuilding=True)
|
||||
File "/usr/local/lib/python2.6/dist-packages/pip/wheel.py", line 749, in build
|
||||
self.requirement_set.prepare_files(self.finder)
|
||||
File "/usr/local/lib/python2.6/dist-packages/pip/req/req_set.py", line 380, in prepare_files
|
||||
ignore_dependencies=self.ignore_dependencies))
|
||||
File "/usr/local/lib/python2.6/dist-packages/pip/req/req_set.py", line 620, in _prepare_file
|
||||
session=self.session, hashes=hashes)
|
||||
File "/usr/local/lib/python2.6/dist-packages/pip/download.py", line 821, in unpack_url
|
||||
hashes=hashes
|
||||
File "/usr/local/lib/python2.6/dist-packages/pip/download.py", line 659, in unpack_http_url
|
||||
hashes)
|
||||
File "/usr/local/lib/python2.6/dist-packages/pip/download.py", line 853, in _download_http_url
|
||||
stream=True,
|
||||
File "/usr/local/lib/python2.6/dist-packages/pip/_vendor/requests/sessions.py", line 521, in get
|
||||
return self.request('GET', url, **kwargs)
|
||||
File "/usr/local/lib/python2.6/dist-packages/pip/download.py", line 386, in request
|
||||
return super(PipSession, self).request(method, url, *args, **kwargs)
|
||||
File "/usr/local/lib/python2.6/dist-packages/pip/_vendor/requests/sessions.py", line 508, in request
|
||||
resp = self.send(prep, **send_kwargs)
|
||||
File "/usr/local/lib/python2.6/dist-packages/pip/_vendor/requests/sessions.py", line 618, in send
|
||||
r = adapter.send(request, **kwargs)
|
||||
File "/usr/local/lib/python2.6/dist-packages/pip/_vendor/cachecontrol/adapter.py", line 47, in send
|
||||
resp = super(CacheControlAdapter, self).send(request, **kw)
|
||||
File "/usr/local/lib/python2.6/dist-packages/pip/_vendor/requests/adapters.py", line 506, in send
|
||||
raise SSLError(e, request=request)
|
||||
SSLError: HTTPSConnectionPool(host='files.pythonhosted.org', port=443): Max retries exceeded with url: /packages/ef/ab/aa12712415809bf698e719b307419f953e25344e8f42d557533d7a02b276/netapp_lib-2020.7.16-py2-none-any.whl (Caused by SSLError(CertificateError("hostname 'files.pythonhosted.org' doesn't match either of 'r.ssl.fastly.net', '*.catchpoint.com', '*.cnn.io', '*.dollarshaveclub.com', '*.eater.com', '*.fastly.picmonkey.com', '*.files.saymedia-content.com', '*.ft.com', '*.meetupstatic.com', '*.nfl.com', '*.pagar.me', '*.picmonkey.com', '*.realself.com', '*.sbnation.com', '*.shakr.com', '*.streamable.com', '*.surfly.com', '*.theverge.com', '*.thrillist.com', '*.vox-cdn.com', '*.vox.com', '*.voxmedia.com', 'eater.com', 'ft.com', 'i.gse.io', 'picmonkey.com', 'realself.com', 'static.wixstatic.com', 'streamable.com', 'surfly.com', 'theverge.com', 'vox-cdn.com', 'vox.com', 'www.joyent.com'",),))
|
||||
ERROR: Command "/usr/bin/python2.6 /root/ansible/test/lib/ansible_test/_data/quiet_pip.py install --disable-pip-version-check -r /root/ansible/test/lib/ansible_test/_data/requirements/units.txt -r tests/unit/requirements.txt -c /root/ansible/test/lib/ansible_test/_data/requirements/constraints.txt" returned exit status 2.
|
||||
ERROR: Command "docker exec d47eb360db4ce779c1f690db964655b76e68895c4360ff252c46fe7fe6f5c75a /usr/bin/env ANSIBLE_TEST_CONTENT_ROOT=/root/ansible_collections/netapp/ontap LC_ALL=en_US.UTF-8 /usr/bin/python3.6 /root/ansible/bin/ansible-test units --metadata tests/output/.tmp/metadata-9i2qfrcl.json --truncate 200 --redact --color yes --requirements --python default --requirements-mode only" returned exit status 1.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: >
|
||||
*One last thing...*
|
||||
|
||||
|
||||
*Please, complete **all** sections as described, this form
|
||||
is [processed automatically by a robot][ansibot help].*
|
||||
|
||||
|
||||
Thank you for your collaboration!
|
||||
|
||||
|
||||
[ansibot help]:
|
||||
/ansible/ansibotmini#ansibotmini
|
||||
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: |
|
||||
Read the [Ansible Code of Conduct][CoC] first.
|
||||
|
||||
[CoC]: https://docs.ansible.com/ansible/latest/community/code_of_conduct.html?utm_medium=github&utm_source=issue_form--bug_report.yml
|
||||
options:
|
||||
- label: I agree to follow the Ansible Code of Conduct
|
||||
required: true
|
||||
...
|
||||
26
.github/ISSUE_TEMPLATE/config.yml
vendored
26
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,26 +0,0 @@
|
||||
# Ref: https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser
|
||||
blank_issues_enabled: false # default: true
|
||||
contact_links:
|
||||
- name: 🔐 Security bug report 🔥
|
||||
url: https://docs.ansible.com/ansible/latest/community/reporting_bugs_and_features.html?utm_medium=github&utm_source=issue_template_chooser
|
||||
about: |
|
||||
Please learn how to report security vulnerabilities here.
|
||||
|
||||
For all security related bugs, email security@ansible.com
|
||||
instead of using this issue tracker and you will receive
|
||||
a prompt response.
|
||||
|
||||
For more information, see
|
||||
https://docs.ansible.com/ansible/latest/community/reporting_bugs_and_features.html
|
||||
- name: 📝 Ansible Code of Conduct
|
||||
url: https://docs.ansible.com/ansible/latest/community/code_of_conduct.html?utm_medium=github&utm_source=issue_template_chooser
|
||||
about: ❤ Be nice to other members of the community. ☮ Behave.
|
||||
- name: 💬 Talks to the community
|
||||
url: https://docs.ansible.com/ansible/latest/community/communication.html?utm_medium=github&utm_source=issue_template_chooser#mailing-list-information
|
||||
about: Please ask and answer usage questions here
|
||||
- name: ⚡ Working groups
|
||||
url: https://github.com/ansible/community/wiki
|
||||
about: Interested in improving a specific area? Become a part of a working group!
|
||||
- name: 💼 For Enterprise
|
||||
url: https://www.ansible.com/products/engine?utm_medium=github&utm_source=issue_template_chooser
|
||||
about: Red Hat offers support for the Ansible Automation Platform
|
||||
212
.github/ISSUE_TEMPLATE/documentation_report.yml
vendored
212
.github/ISSUE_TEMPLATE/documentation_report.yml
vendored
@ -1,212 +0,0 @@
|
||||
---
|
||||
name: 📝 Documentation Report
|
||||
description: Ask us about docs
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: >
|
||||
**Thank you for wanting to report a problem with ansible-core
|
||||
documentation!**
|
||||
|
||||
|
||||
Please fill out your suggestions below. If the problem seems
|
||||
straightforward, feel free to go ahead and
|
||||
[submit a pull request] instead!
|
||||
|
||||
|
||||
⚠
|
||||
Verify first that your issue is not [already reported on
|
||||
GitHub][issue search].
|
||||
|
||||
Also test if the latest release and devel branch are affected too.
|
||||
|
||||
|
||||
**Tip:** If you are seeking community support, please consider
|
||||
[starting a mailing list thread or chatting in IRC][ML||IRC].
|
||||
|
||||
|
||||
[ML||IRC]:
|
||||
https://docs.ansible.com/ansible-core/devel/community/communication.html?utm_medium=github&utm_source=issue_form--documentation_report.yml#mailing-list-information
|
||||
|
||||
[issue search]: ../search?q=is%3Aissue&type=issues
|
||||
|
||||
[submit a pull request]:
|
||||
https://docs.ansible.com/ansible-core/devel/community/documentation_contributions.html
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: >
|
||||
**Check the repository for your issue.**
|
||||
|
||||
Source files for Ansible community documentation are
|
||||
hosted in different repositories. Please make sure to file an
|
||||
issue in the correct project.
|
||||
|
||||
Documentation for modules/plugins/etc that are officially
|
||||
supported by the Ansible Core Engineering team is available
|
||||
in this (`ansible/ansible`) repository.
|
||||
|
||||
The Installation Guide, Playbook Guide, Developer Guide,
|
||||
and other documentation is available in the
|
||||
[ansible/ansible-documentation] repository.
|
||||
Documentation for other modules/plugins/etc is likely to be
|
||||
available in one of the [Ansible Collections][collections index].
|
||||
If available in the collection documentation, select the
|
||||
**Issue Tracker** button to go directly to the GitHub issues.
|
||||
|
||||
|
||||
[ansible/ansible-documentation]: /ansible/ansible-documentation
|
||||
|
||||
[collections index]:
|
||||
https://docs.ansible.com/ansible/latest/collections/index.html
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Summary
|
||||
description: >
|
||||
Explain the problem briefly below, add suggestions to wording
|
||||
or structure.
|
||||
|
||||
|
||||
**HINT:** Did you know the documentation has a `View on GitHub`
|
||||
link on some pages? Feel free to use it to start a pull request
|
||||
right from the GitHub UI!
|
||||
placeholder: >-
|
||||
I was reading the ansible-core documentation of version X and I'm having
|
||||
problems understanding Y. It would be very helpful if that got
|
||||
rephrased as Z.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Issue Type
|
||||
description: >
|
||||
Please select the single available option in the drop-down.
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<em>Why?</em>
|
||||
</summary>
|
||||
|
||||
|
||||
_We will make it easier in the future, once GitHub
|
||||
supports dropdown defaults. Promise!_
|
||||
|
||||
</details>
|
||||
# FIXME: Once GitHub allows defining the default choice, update this
|
||||
options:
|
||||
- Documentation Report
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Component Name
|
||||
description: >
|
||||
Write the short name of the rst file, module, plugin, task or
|
||||
feature below, *use your best guess if unsure*.
|
||||
|
||||
placeholder: lib/ansible/modules/copy.py
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Ansible Version
|
||||
description: >-
|
||||
Paste verbatim output from `ansible --version` below, under
|
||||
the prompt line. Please don't wrap it with triple backticks — your
|
||||
whole input will be turned into a code snippet automatically.
|
||||
render: console
|
||||
value: |
|
||||
$ ansible --version
|
||||
placeholder: |
|
||||
$ ansible --version
|
||||
ansible [core 2.11.0b4.post0] (detached HEAD ref: refs/) last updated 2021/04/02 00:33:35 (GMT +200)
|
||||
config file = None
|
||||
configured module search path = ['~/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
|
||||
ansible python module location = ~/src/github/ansible/ansible/lib/ansible
|
||||
ansible collection location = ~/.ansible/collections:/usr/share/ansible/collections
|
||||
executable location = bin/ansible
|
||||
python version = 3.9.0 (default, Oct 26 2020, 13:08:59) [GCC 10.2.0]
|
||||
jinja version = 2.11.3
|
||||
libyaml = True
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Configuration
|
||||
description: >-
|
||||
Paste verbatim output from `ansible-config dump --only-changed -t all` below, under the prompt line.
|
||||
(if using a version older than ansible-core 2.12 you should omit the '-t all')
|
||||
Please don't wrap it with triple backticks — your
|
||||
whole input will be turned into a code snippet automatically.
|
||||
render: console
|
||||
value: |
|
||||
# if using a version older than ansible-core 2.12 you should omit the '-t all'
|
||||
$ ansible-config dump --only-changed -t all
|
||||
placeholder: |
|
||||
# if using a version older than ansible-core 2.12 you should omit the '-t all'
|
||||
$ ansible-config dump --only-changed -t all
|
||||
DEFAULT_GATHERING(~/src/github/ansible/ansible/ansible.cfg) = smart
|
||||
DEFAULT_HOST_LIST(~/src/github/ansible/ansible/ansible.cfg) = ['~/src/github/ansible/ansible/hosts']
|
||||
DEFAULT_VAULT_PASSWORD_FILE(~/src/github/ansible/ansible/ansible.cfg) = ~/src/github/ansible/ansible/vault/print-password.sh
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: OS / Environment
|
||||
description: >-
|
||||
Provide all relevant information below, e.g. OS version,
|
||||
browser, etc.
|
||||
placeholder: Fedora 33, Firefox etc.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional Information
|
||||
description: |
|
||||
Describe how this improves the documentation, e.g. before/after situation or screenshots.
|
||||
|
||||
**HINT:** You can paste https://gist.github.com links for larger files.
|
||||
placeholder: >-
|
||||
When the improvement is applied, it makes it more straightforward
|
||||
to understand X.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: >
|
||||
*One last thing...*
|
||||
|
||||
|
||||
*Please, complete **all** sections as described, this form
|
||||
is [processed automatically by a robot][ansibot help].*
|
||||
|
||||
|
||||
Thank you for your collaboration!
|
||||
|
||||
|
||||
[ansibot help]:
|
||||
/ansible/ansibotmini#ansibotmini
|
||||
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: |
|
||||
Read the [Ansible Code of Conduct][CoC] first.
|
||||
|
||||
[CoC]: https://docs.ansible.com/ansible/latest/community/code_of_conduct.html?utm_medium=github&utm_source=issue_form--documentation_report.yml
|
||||
options:
|
||||
- label: I agree to follow the Ansible Code of Conduct
|
||||
required: true
|
||||
...
|
||||
192
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
192
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@ -1,192 +0,0 @@
|
||||
---
|
||||
name: ✨ Feature request
|
||||
description: Suggest an idea for this project
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: >
|
||||
**Thank you for wanting to suggest a feature for ansible-core!**
|
||||
|
||||
|
||||
|
||||
💡
|
||||
Before you go ahead with your request, please first consider if it
|
||||
would be useful for majority of the ansible-core users. As a
|
||||
general rule of thumb, any feature that is only of interest to a
|
||||
small sub group should be [implemented in a third-party Ansible
|
||||
Collection][contribute to collections] or maybe even just your
|
||||
project alone. Be mindful of the fact that the essential
|
||||
ansible-core features have a broad impact.
|
||||
|
||||
If unsure, consider filing a [new proposal] instead outlining your
|
||||
use-cases, the research and implementation considerations. Then,
|
||||
start a discussion on one of the public [IRC meetings] we have just
|
||||
for this.
|
||||
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
❗ Every change breaks someone's workflow.
|
||||
</summary>
|
||||
|
||||
|
||||
[
|
||||
](https://xkcd.com/1172/)
|
||||
</details>
|
||||
|
||||
|
||||
⚠
|
||||
Verify first that your idea is not [already requested on
|
||||
GitHub][issue search].
|
||||
|
||||
Also test if the devel branch does not already implement this.
|
||||
|
||||
|
||||
**Tip:** If you are seeking community support, please consider
|
||||
[starting a mailing list thread or chatting in IRC][ML||IRC].
|
||||
|
||||
|
||||
|
||||
[contribute to collections]:
|
||||
https://docs.ansible.com/ansible-core/devel/community/contributing_maintained_collections.html?utm_medium=github&utm_source=issue_form--feature_request.yml
|
||||
|
||||
[IRC meetings]:
|
||||
https://docs.ansible.com/ansible-core/devel/community/communication.html?utm_medium=github&utm_source=issue_form--feature_request.yml#irc-meetings
|
||||
|
||||
[issue search]: ../search?q=is%3Aissue&type=issues
|
||||
|
||||
[ML||IRC]:
|
||||
https://docs.ansible.com/ansible-core/devel/community/communication.html?utm_medium=github&utm_source=issue_form--feature_request.yml#mailing-list-information
|
||||
|
||||
[new proposal]: ../../proposals/issues/new
|
||||
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Summary
|
||||
description: >
|
||||
Describe the new feature/improvement you would like briefly below.
|
||||
|
||||
|
||||
What's the problem this feature will solve?
|
||||
|
||||
What are you trying to do, that you are unable to achieve
|
||||
with ansible-core as it currently stands?
|
||||
|
||||
|
||||
* Provide examples of real-world use cases that this would enable
|
||||
and how it solves the problem you described.
|
||||
|
||||
* How do you solve this now?
|
||||
|
||||
* Have you tried to work around the problem using other tools?
|
||||
|
||||
* Could there be a different approach to solving this issue?
|
||||
|
||||
|
||||
*Know **exactly** what you want?* Consider filing a [new proposal]
|
||||
instead outlining your research and implementation considerations.
|
||||
|
||||
|
||||
[new proposal]: ../../proposals/issues/new
|
||||
placeholder: >-
|
||||
I am trying to do X with ansible-core from the devel branch on GitHub and
|
||||
I think that implementing a feature Y would be very helpful for me and
|
||||
every other user of ansible-core because of Z.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Issue Type
|
||||
description: >
|
||||
Please select the single available option in the drop-down.
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<em>Why?</em>
|
||||
</summary>
|
||||
|
||||
We would do it by ourselves but unfortunatelly, the curent
|
||||
edition of GitHub Issue Forms Alpha does not support this yet 🤷
|
||||
|
||||
|
||||
_We will make it easier in the future, once GitHub
|
||||
supports dropdown defaults. Promise!_
|
||||
|
||||
</details>
|
||||
# FIXME: Once GitHub allows defining the default choice, update this
|
||||
options:
|
||||
- Feature Idea
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Component Name
|
||||
description: >
|
||||
Write the short name of the module, plugin, task or feature below,
|
||||
*use your best guess if unsure*.
|
||||
|
||||
|
||||
Be aware that a lot of content that used to reside in this repository
|
||||
previously, is now hosted under [individual collection
|
||||
projects][collections org].
|
||||
If this is the case, please make sure to file an issue under the
|
||||
appropriate project there instead.
|
||||
|
||||
|
||||
[collections org]: /ansible-collections
|
||||
placeholder: dnf, apt, pip, user etc.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional Information
|
||||
description: |
|
||||
Describe how the feature would be used, why it is needed and what it would solve.
|
||||
|
||||
**HINT:** You can paste https://gist.github.com links for larger files.
|
||||
value: |
|
||||
<!--- Paste example playbooks or commands between quotes below -->
|
||||
```yaml (paste below)
|
||||
|
||||
```
|
||||
placeholder: >-
|
||||
I asked on https://stackoverflow.com/.... and the community
|
||||
advised me to do X, Y and Z.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: >
|
||||
*One last thing...*
|
||||
|
||||
|
||||
*Please, complete **all** sections as described, this form
|
||||
is [processed automatically by a robot][ansibot help].*
|
||||
|
||||
|
||||
Thank you for your collaboration!
|
||||
|
||||
|
||||
[ansibot help]:
|
||||
/ansible/ansibotmini#ansibotmini
|
||||
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: |
|
||||
Read the [Ansible Code of Conduct][CoC] first.
|
||||
|
||||
[CoC]: https://docs.ansible.com/ansible/latest/community/code_of_conduct.html?utm_medium=github&utm_source=issue_form--feature_request.yml
|
||||
options:
|
||||
- label: I agree to follow the Ansible Code of Conduct
|
||||
required: true
|
||||
...
|
||||
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1 +0,0 @@
|
||||
PULL_REQUEST_TEMPLATE/Unclear purpose or motivation.md
|
||||
20
.github/PULL_REQUEST_TEMPLATE/Bug fix.md
vendored
20
.github/PULL_REQUEST_TEMPLATE/Bug fix.md
vendored
@ -1,20 +0,0 @@
|
||||
##### SUMMARY
|
||||
|
||||
<!--- Describe the change below, including rationale and design decisions -->
|
||||
|
||||
<!--- HINT: Include "Fixes #nnn" if you are fixing an existing issue -->
|
||||
|
||||
##### ISSUE TYPE
|
||||
|
||||
- Bugfix Pull Request
|
||||
|
||||
##### ADDITIONAL INFORMATION
|
||||
|
||||
<!--- Include additional information to help people understand the change here -->
|
||||
<!--- A step-by-step reproduction of the problem is helpful if there is no related issue -->
|
||||
|
||||
<!--- Paste verbatim command output below, e.g. before and after your change -->
|
||||
|
||||
```paste below
|
||||
|
||||
```
|
||||
@ -1,19 +0,0 @@
|
||||
##### SUMMARY
|
||||
|
||||
<!--- Describe the change below, including rationale -->
|
||||
|
||||
<!--- HINT: Include "Closes #nnn" if you are fixing an existing issue -->
|
||||
|
||||
##### ISSUE TYPE
|
||||
|
||||
- Docs Pull Request
|
||||
|
||||
##### ADDITIONAL INFORMATION
|
||||
|
||||
<!--- Include additional information to help people understand the change here -->
|
||||
|
||||
<!--- Paste verbatim command output below, e.g. before and after your change -->
|
||||
|
||||
```paste below
|
||||
|
||||
```
|
||||
19
.github/PULL_REQUEST_TEMPLATE/New feature.md
vendored
19
.github/PULL_REQUEST_TEMPLATE/New feature.md
vendored
@ -1,19 +0,0 @@
|
||||
##### SUMMARY
|
||||
|
||||
<!--- Describe the change below, including rationale and design decisions -->
|
||||
|
||||
<!--- HINT: Include "Resolves #nnn" if you are fixing an existing issue -->
|
||||
|
||||
##### ISSUE TYPE
|
||||
|
||||
- Feature Pull Request
|
||||
|
||||
##### ADDITIONAL INFORMATION
|
||||
|
||||
<!--- Include additional information to help people understand the change here -->
|
||||
|
||||
<!--- Paste verbatim command output below, e.g. before and after your change -->
|
||||
|
||||
```paste below
|
||||
|
||||
```
|
||||
@ -1 +0,0 @@
|
||||
Unclear purpose or motivation.md
|
||||
20
.github/PULL_REQUEST_TEMPLATE/Tests.md
vendored
20
.github/PULL_REQUEST_TEMPLATE/Tests.md
vendored
@ -1,20 +0,0 @@
|
||||
##### SUMMARY
|
||||
|
||||
<!--- Describe the change below, including rationale and design decisions -->
|
||||
|
||||
<!--- HINT: Include "Closes #nnn" if you are fixing an existing issue -->
|
||||
|
||||
##### ISSUE TYPE
|
||||
|
||||
- Test Pull Request
|
||||
|
||||
##### ADDITIONAL INFORMATION
|
||||
|
||||
<!--- Include additional information to help people understand the change here -->
|
||||
<!--- A step-by-step reproduction of the problem is helpful if there is no related issue -->
|
||||
|
||||
<!--- Paste verbatim command output below, e.g. before and after your change -->
|
||||
|
||||
```paste below
|
||||
|
||||
```
|
||||
@ -1,25 +0,0 @@
|
||||
##### SUMMARY
|
||||
|
||||
<!--- Describe the change below, including rationale and design decisions -->
|
||||
|
||||
<!--- HINT: Include "Fixes #nnn" if you are fixing an existing issue -->
|
||||
|
||||
##### ISSUE TYPE
|
||||
|
||||
<!--- Pick one below and delete the rest -->
|
||||
|
||||
- Bugfix Pull Request
|
||||
- Docs Pull Request
|
||||
- Feature Pull Request
|
||||
- Test Pull Request
|
||||
|
||||
##### ADDITIONAL INFORMATION
|
||||
|
||||
<!--- Include additional information to help people understand the change here -->
|
||||
<!--- A step-by-step reproduction of the problem is helpful if there is no related issue -->
|
||||
|
||||
<!--- Paste verbatim command output below, e.g. before and after your change -->
|
||||
|
||||
```paste below
|
||||
|
||||
```
|
||||
18
.github/RELEASE_NAMES.txt
vendored
18
.github/RELEASE_NAMES.txt
vendored
@ -1,18 +0,0 @@
|
||||
2.17.0 Gallows Pole
|
||||
2.16.0 All My Love
|
||||
2.15.0 Ten Years Gone
|
||||
2.14.0 C'mon Everybody
|
||||
2.13.0 Nobody's Fault but Mine
|
||||
2.12.0 Dazed and Confused
|
||||
2.11.0 Hey Hey, What Can I Do
|
||||
2.10.0 When the Levee Breaks
|
||||
2.9.0 Immigrant Song
|
||||
2.8.0 How Many More Times
|
||||
2.7.0 In the Light
|
||||
2.6.0 Heartbreaker
|
||||
2.5.0 Kashmir
|
||||
2.4.0 Dancing Days
|
||||
2.3.0 Ramble On
|
||||
2.2.0 The Battle of Evermore
|
||||
2.1.0 The Song Remains the Same
|
||||
2.0.0 Over the Hills and Far Away
|
||||
15
.github/SECURITY.md
vendored
15
.github/SECURITY.md
vendored
@ -1,15 +0,0 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Ansible applies security fixes according to the 3-versions-back support
|
||||
policy. Please find more information in [our docs](
|
||||
https://docs.ansible.com/ansible/devel/reference_appendices/release_and_maintenance.html#release-status
|
||||
).
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
We encourage responsible disclosure practices for security
|
||||
vulnerabilities. Please read our [policies for reporting bugs](
|
||||
https://docs.ansible.com/ansible/devel/community/reporting_bugs_and_features.html#reporting-a-bug
|
||||
) if you want to report a security issue that might affect Ansible.
|
||||
101
.gitignore
vendored
101
.gitignore
vendored
@ -2,10 +2,9 @@
|
||||
*.py[co]
|
||||
build
|
||||
AUTHORS.TXT
|
||||
# Emacs backup and autosave files...
|
||||
# Emacs backup files...
|
||||
*~
|
||||
.\#*
|
||||
\#*
|
||||
# RPM stuff...
|
||||
MANIFEST
|
||||
dist
|
||||
@ -17,108 +16,38 @@ rpm-build
|
||||
.idea
|
||||
#IntelliJ IDEA stuff..
|
||||
*.iml
|
||||
#VSCode stuff..
|
||||
.vscode/
|
||||
# Mac OS X stuff...
|
||||
.DS_Store
|
||||
# manpage build stuff...
|
||||
docs/man/man1/ansible*.1
|
||||
docs/man/man1/ansible*.1.asciidoc.in
|
||||
docs/man/man3/*
|
||||
# Sublime stuff
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
# docsite stuff...
|
||||
docs/docsite/_build
|
||||
docs/docsite/*.html
|
||||
docs/docsite/htmlout
|
||||
docs/docsite/rst/conf.py
|
||||
docs/docsite/rst/index.rst
|
||||
docs/docsite/rst/cli/ansible-*.rst
|
||||
docs/docsite/rst/cli/ansible.rst
|
||||
docs/docsite/rst/dev_guide/collections_galaxy_meta.rst
|
||||
docs/docsite/rst/dev_guide/testing/sanity/index.rst.new
|
||||
docs/docsite/rst/dev_guide/index.rst
|
||||
docs/docsite/rst/modules/*.rst
|
||||
docs/docsite/rst/collections/*.rst
|
||||
!docs/docsite/rst/collections/all_plugins.rst
|
||||
docs/docsite/rst/collections/*/*.rst
|
||||
docs/docsite/rst/playbooks_directives.rst
|
||||
docs/docsite/rst/plugins_by_category.rst
|
||||
docs/docsite/rst/plugins/*/*.rst
|
||||
docs/docsite/rst/reference_appendices/config.rst
|
||||
docs/docsite/rst/reference_appendices/playbooks_keywords.rst
|
||||
docs/docsite/rst_warnings
|
||||
docs/docsite/searchindex.js
|
||||
docs/docsite/_static/*.gif
|
||||
docs/docsite/_static/*.png
|
||||
docs/docsite/_static/websupport.js
|
||||
docsite/rst/modules_by_category.rst
|
||||
docsite/rst/list_of_*.rst
|
||||
docsite/rst/*_module.rst
|
||||
docsite/*.html
|
||||
docsite/_static/*.gif
|
||||
docsite/_static/*.png
|
||||
docsite/_static/websupport.js
|
||||
docsite/searchindex.js
|
||||
docsite/htmlout
|
||||
# deb building stuff...
|
||||
debian/
|
||||
deb-build
|
||||
# Vim swap files
|
||||
*.swp
|
||||
*.swo
|
||||
[._]*.un~
|
||||
credentials.yml
|
||||
# test output
|
||||
*.retry
|
||||
*.out
|
||||
.pytest_cache/
|
||||
.tox
|
||||
.cache
|
||||
.pytest_cache
|
||||
.coverage
|
||||
results.xml
|
||||
coverage.xml
|
||||
/test/units/cover-html
|
||||
/test/integration/inventory
|
||||
/test/integration/targets/*/backup/
|
||||
/test/cache/*
|
||||
# Development
|
||||
/test/develop
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
venv
|
||||
env
|
||||
Vagrantfile
|
||||
.vagrant
|
||||
# Backwards compatibility with `stable-2.9` and earlier branches.
|
||||
# Also used in the `devel` branch during early Ansible 2.10 development.
|
||||
/lib/ansible.egg-info/
|
||||
# First used in the `devel` branch during Ansible 2.10 development.
|
||||
/lib/ansible_base.egg-info/
|
||||
# First used in the `devel` branch during Ansible 2.11 development.
|
||||
/lib/ansible_core.egg-info/
|
||||
# vendored lib dir
|
||||
lib/ansible/_vendor/*
|
||||
!lib/ansible/_vendor/__init__.py
|
||||
# test stuff
|
||||
/test/integration/cloud-config-*.*
|
||||
!/test/integration/cloud-config-*.*.template
|
||||
.python-version
|
||||
# Release directory
|
||||
packaging/release/ansible_release
|
||||
/.cache/
|
||||
/test/results/
|
||||
/test/integration/cloud-config-aws.yml
|
||||
/test/integration/inventory.networking
|
||||
/test/integration/inventory.winrm
|
||||
/test/integration/cloud-config-aws.yml
|
||||
/test/integration/cloud-config-cs.ini
|
||||
# python 'rope' stuff
|
||||
.ropeproject
|
||||
# local 'ack' config files
|
||||
.ackrc
|
||||
# default 'coverage html' results
|
||||
htmlcov/
|
||||
# default 'coverage' tool data
|
||||
.coverage
|
||||
# ansible-test coverage results
|
||||
test/units/.coverage.*
|
||||
/test/integration/cloud-config-azure.yml
|
||||
/SYMLINK_CACHE.json
|
||||
changelogs/.plugin-cache.yaml
|
||||
.ansible-test-timeout.json
|
||||
ansible.egg-info/
|
||||
|
||||
16
.gitmodules
vendored
Normal file
16
.gitmodules
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
[submodule "lib/ansible/modules/core"]
|
||||
path = lib/ansible/modules/core
|
||||
url = https://github.com/ansible/ansible-modules-core.git
|
||||
branch = devel
|
||||
[submodule "lib/ansible/modules/extras"]
|
||||
path = lib/ansible/modules/extras
|
||||
url = https://github.com/ansible/ansible-modules-extras.git
|
||||
branch = devel
|
||||
[submodule "v2/ansible/modules/core"]
|
||||
path = v2/ansible/modules/core
|
||||
url = https://github.com/ansible/ansible-modules-core.git
|
||||
branch = devel
|
||||
[submodule "v2/ansible/modules/extras"]
|
||||
path = v2/ansible/modules/extras
|
||||
url = https://github.com/ansible/ansible-modules-extras.git
|
||||
branch = devel
|
||||
36
.mailmap
36
.mailmap
@ -1,36 +0,0 @@
|
||||
Michael DeHaan <michael.dehaan@gmail.com> <michael@ansibleworks.com>
|
||||
Michael DeHaan <michael.dehaan@gmail.com> Michael DeHaan <michael@ansibleworks.com>
|
||||
Michael DeHaan <michael.dehaan@gmail.com> <michael@ansible.com>
|
||||
<bcoca@ansible.com> <brian.coca+git@gmail.com>
|
||||
<bcoca@ansible.com> <briancoca+ansible@gmail.com>
|
||||
<bcoca@ansible.com> <briancoca+dev@gmail.com>
|
||||
<bcoca@ansible.com> <bcoca@tablethotels.com>
|
||||
<a.badger@gmail.com> <toshio@fedoraproject.org>
|
||||
James Tanner <tanner.jc@gmail.com>
|
||||
<jimi@sngx.net> <jcammarata@ansibleworks.com>
|
||||
Michael Scherer <misc@zarb.org> mscherer <misc@zarb.org>
|
||||
<misc@zarb.org> <misc@ephaone.org>
|
||||
<misc@zarb.org> <mscherer@users.noreply.github.com>
|
||||
James Martin <jmartin@ansible.com> <jmartin@basho.com>
|
||||
James Laska <jlaska@ansible.com> <jlaska@redhat.com>
|
||||
James Laska <jlaska@ansible.com> <jlaska@ansibleworks.com>
|
||||
James Laska <jlaska@ansible.com> <jlaska@James-Laskas-MacBook-Pro.local>
|
||||
James Laska <jlaska@ansible.com> <jlaska@users.noreply.github.com>
|
||||
Jason McKerr <mckerrj@gmail.com> <jmckerr@jmckerr-OSX.local>
|
||||
Chris Houseknecht <chouseknecht@ansible.com>
|
||||
Chris Houseknecht <chouseknecht@ansible.com> chouseknecht <chouse@ansible.com>
|
||||
Chris Houseknecht <chouseknecht@ansible.com> Chris Houseknecht <chouse@ansibleworks.com>
|
||||
Stephen Fromm <sfromm@gmail.com> Stephen Fromm <stephenf@nero.net>
|
||||
Sandra Wills <docschick@ansible.com> Sandra Wills <swills@ansible.com>
|
||||
Will Thames <will@thames.id.au> willthames <will@thames.id.au>
|
||||
Will Thames <will@thames.id.au> Will Thames <willdthames@gmail.com>
|
||||
Will Thames <will@thames.id.au> willthames <willdthames@gmail.com>
|
||||
Will Thames <will@thames.id.au> willthames <will@musc.org.uk>
|
||||
Will Thames <will@thames.id.au> u348095 <will.thames@suncorp.com.au>
|
||||
Will Thames <will@thames.id.au> willthames <will.thames@suncorp.com.au>
|
||||
Will Thames <will@thames.id.au> Will Thames <will.thames@xvt.com.au>
|
||||
nitzmahone <mdavis@ansible.com> Matt Davis <nitzmahone@users.noreply.github.com>
|
||||
Dag Wieers <dag@wieers.com> Dag Wieërs <dag@wieers.com>
|
||||
Dag Wieers <dag@wieers.com> Dag Wieers <dag.wieers@gmail.com>
|
||||
Rene Moser <mail@renemoser.net> René Moser <mail@renemoser.net>
|
||||
Rene Moser <mail@renemoser.net> Rene Moser <rene.moser@swisstxt.ch>
|
||||
1458
CHANGELOG.md
Normal file
1458
CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load Diff
319
CODING_GUIDELINES.md
Normal file
319
CODING_GUIDELINES.md
Normal file
@ -0,0 +1,319 @@
|
||||
Coding Guidelines
|
||||
=================
|
||||
|
||||
Hi! Thanks for interest in contributing to Ansible.
|
||||
|
||||
Here are some guidelines for contributing code. The purpose of this document are to establish what we're looking for in code contributions, and to make sure
|
||||
new contributions know some of the conventions that we've been using.
|
||||
|
||||
We don't think much of this should be too strange to readers familiar with contributing to Python projects, though it helps if we all get on the same page.
|
||||
|
||||
Language
|
||||
========
|
||||
|
||||
* While not all components of Ansible must be in Python, core contributions to the Ansible repo must be written in Python. This is to maximize the ability of everyone to contribute.
|
||||
* If you want to write non-Python ansible modules or inventory scripts, that's fine, but they are not going to get merged in most likely. Sorry!!
|
||||
|
||||
PEP8 and basic style checks
|
||||
===========================
|
||||
|
||||
* PEP8 is a great Python style guide, which you should read.
|
||||
* PEP8 must not be strictly followed in all aspects, but most of it is good advice
|
||||
* In particular, we don't really care about line lengths. Buy a bigger monitor!
|
||||
* To run checks for things we care about, run "make pep8"
|
||||
* Similarly, additional checks can be made with "make pyflakes"
|
||||
* There is no need to submit code changes for pep8 and pyflake fixes, as these break attribution history. Project leadership will make these periodically.
|
||||
* Do not submit pull requests that simply adjust whitespace in the code
|
||||
|
||||
Testing
|
||||
=======
|
||||
|
||||
* Much of ansible's testing needs are in integration, not unit tests. Add module tests there.
|
||||
* That being said, there are unit tests too!
|
||||
* Code written must absolutely pass tests (i.e. "make tests")
|
||||
* You should anticipate any error paths in your code and test down those error paths.
|
||||
* Additions to tests for core code is welcome, but not always possible. Be sure things are at least well tested manually in that case.
|
||||
|
||||
Whitespace
|
||||
==========
|
||||
|
||||
* Four space indent is strictly required
|
||||
* Include meaningful whitespace between lines of code
|
||||
|
||||
Shebang Lines
|
||||
=============
|
||||
|
||||
* /usr/bin/scripts should start with '/usr/bin/env python'
|
||||
* module code should still use '/usr/bin/python' as this is replaced automatically by 'ansible_python_interpreter', see the FAQ in the docs for more info.
|
||||
|
||||
Comments
|
||||
========
|
||||
|
||||
* Readability is one of the most important goals for this project
|
||||
* Comment any non-trivial code where someone might not know why you are doing something in a particular way
|
||||
* Though if something should be commented, that's often a sign someone should write a function
|
||||
* All new functions must have a basic docstring comment
|
||||
* Commenting above a line is preferable to commenting at the end of a line
|
||||
|
||||
Classes
|
||||
=======
|
||||
|
||||
* With the exception of module code (where inline is better), it is desirable to see classes in their own files.
|
||||
* Classes should generally not cause side effects as soon as they are instantiated, move meaningful behavior to methods rather than constructors.
|
||||
|
||||
Functions and Methods
|
||||
=====================
|
||||
|
||||
* In general, functions should not be 'too long' and should describe a meaningful amount of work
|
||||
* When code gets too nested, that's usually the sign the loop body could benefit from being a function
|
||||
* Parts of our existing code are not the best examples of this at times.
|
||||
* Functions should have names that describe what they do, along with docstrings
|
||||
* Functions should be named with_underscores
|
||||
* "Don't repeat yourself" is generally a good philosophy
|
||||
|
||||
Variables
|
||||
=========
|
||||
|
||||
* Use descriptive variable names instead of variables like 'x', unless x is a obvious loop index
|
||||
* Ansible python code uses identifiers like 'ClassesLikeThis and variables_like_this
|
||||
* Module parameters should also use_underscores and not runtogether
|
||||
|
||||
Module Security
|
||||
===============
|
||||
|
||||
* Modules must take steps to avoid passing user input from the shell and always check return codes
|
||||
* always use module.run_command instead of subprocess or Popen or os.system -- this is mandatory
|
||||
* if you use need the shell you must pass use_unsafe_shell=True to module.run_command
|
||||
* if you do not need the shell, avoid using the shell
|
||||
* any variables that can come from the user input with use_unsafe_shell=True must be wrapped by pipes.quote(x)
|
||||
* downloads of https:// resource urls must import module_utils.urls and use the fetch_url method
|
||||
|
||||
Misc Preferences
|
||||
================
|
||||
|
||||
Use the dict constructor where possible when allocating dictionaries:
|
||||
|
||||
# not this:
|
||||
foo = {
|
||||
'a' : 12,
|
||||
'b' : 34
|
||||
}
|
||||
|
||||
# this:
|
||||
foo = dict(
|
||||
a = 12,
|
||||
b = 34
|
||||
)
|
||||
|
||||
Line up variables
|
||||
|
||||
# not this
|
||||
a = 12
|
||||
foosball = 34
|
||||
xyz = 'dog'
|
||||
|
||||
# this
|
||||
a = 12
|
||||
foosball = 34
|
||||
xyz = 'dog'
|
||||
|
||||
Don't use line continuations:
|
||||
|
||||
# no
|
||||
if (this_is_a_very_long_line and foo and \
|
||||
i_am_going_to_continue_it):
|
||||
bar()
|
||||
|
||||
# better:
|
||||
if (this_is_a_very_long_line and foo and i_am_going_to_continue_it):
|
||||
bar()
|
||||
|
||||
Spacing:
|
||||
|
||||
# no
|
||||
x=[1,2,3]
|
||||
|
||||
# no
|
||||
x = [1,2,3]
|
||||
|
||||
# yes
|
||||
x = [ 1, 2, 3 ]
|
||||
|
||||
Spacing continued:
|
||||
|
||||
# no
|
||||
x=foo(12)
|
||||
|
||||
# no
|
||||
x = foo( 12 )
|
||||
|
||||
# yes
|
||||
x = foo(12)
|
||||
|
||||
Licenses
|
||||
========
|
||||
|
||||
Every file should have a license header, including the copyright of the original author. Major additions to the module are allowed
|
||||
to add an additional copyright line, and this is especially true of rewrites, but original authorship copyright messages should be preserved.
|
||||
|
||||
All contributions to the core repo should preserve original licenses and new contributions must include the GPLv3 header.
|
||||
|
||||
Module Documentation
|
||||
====================
|
||||
|
||||
All module pull requests must include a DOCUMENTATION docstring (YAML format,
|
||||
see other modules for examples) as well as an EXAMPLES docstring, which is free form.
|
||||
|
||||
When adding new modules, any new parameter must have a "version_added" attribute.
|
||||
When submitting a new module, the module should have a "version_added" attribute in the
|
||||
pull request as well, set to the current development version.
|
||||
|
||||
Be sure to check grammar and spelling.
|
||||
|
||||
It's frequently the case that modules get submitted with YAML that isn't valid,
|
||||
so you can run "make webdocs" from the checkout to preview your module's documentation.
|
||||
If it fails to build, take a look at your DOCUMENTATION string
|
||||
or you might have a Python syntax error in there too.
|
||||
|
||||
Python Imports
|
||||
==============
|
||||
|
||||
To make it clear what a module is importing, imports should not be sprinkled throughout the code.
|
||||
|
||||
Python Imports should happen at the top of the file, exempting code from module_utils.
|
||||
|
||||
When a conditional runtime import is required, do so something like this instead:
|
||||
|
||||
HAS_FOO = False
|
||||
try:
|
||||
import foo
|
||||
HAS_FOO = True
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
...
|
||||
|
||||
if not HAS_FOO:
|
||||
raise Exception("the foo library is required")
|
||||
|
||||
This makes it clear what optional dependencies are but allows this to be deferred until runtime. In the case of module code, the raising of the Exception will be replaced
|
||||
with a "module.exit_json" call.
|
||||
|
||||
Exceptions
|
||||
==========
|
||||
|
||||
In the main body of the code, use typed exceptions where possible:
|
||||
|
||||
# not this
|
||||
raise Exception("panic!")
|
||||
|
||||
# this
|
||||
from ansible import errors
|
||||
...
|
||||
raise errors.AnsibleError("panic!")
|
||||
|
||||
Similarly, exception checking should be fine grained:
|
||||
|
||||
# not this
|
||||
try:
|
||||
foo()
|
||||
except:
|
||||
bar()
|
||||
|
||||
# but this
|
||||
try:
|
||||
foo()
|
||||
except SomeTypedException:
|
||||
bar()
|
||||
|
||||
List Comprehensions
|
||||
===================
|
||||
|
||||
In general list comprehensions are always preferred to map() and filter() calls.
|
||||
|
||||
However, they can be abused. Optimize for readability, and avoid nesting them too deeply.
|
||||
|
||||
Regexes
|
||||
=======
|
||||
|
||||
There is a time and place for them, but here's an illustrative joke.
|
||||
|
||||
"A developer had a problem, and used a regular expression to solve it. Now the developer had two problems".
|
||||
|
||||
Often regexes are difficult to maintain, and a trusty call to other string operations can be a great solution, faster,
|
||||
and more readable.
|
||||
|
||||
File Conventions
|
||||
================
|
||||
|
||||
If a piece of code looks for a named YAML file in a directory, it should assume it can take no extension, or an extension of '.yml' or '.yaml'.
|
||||
This should be true against all code that loads files.
|
||||
|
||||
Any code that uses directories should consider the possibility that the directory may be symlink.
|
||||
|
||||
New Ansible language parameters
|
||||
===============================
|
||||
|
||||
If adding a new parameter, like 'can_fizzbuzz: True/False' be sure the value of the parameter is templated somewhere in the Runner code, as if anything can be parameterized in Ansible,
|
||||
there is a user that will try to parameterize it.
|
||||
|
||||
String Find
|
||||
===========
|
||||
|
||||
Use 'in':
|
||||
|
||||
# not this:
|
||||
if x.find('foo') != -1:
|
||||
|
||||
# this:
|
||||
if 'foo' in x:
|
||||
|
||||
String checks
|
||||
=============
|
||||
|
||||
To test if something is a string, consider that it may be unicode.
|
||||
|
||||
# no
|
||||
if type(x) == str:
|
||||
|
||||
# yes
|
||||
if isinstance(x, basestring):
|
||||
|
||||
Cleverness
|
||||
==========
|
||||
|
||||
Ansible's code is intended to be read by as many people as possible, so we don't particularly encourage clever or heavily idiomatic code.
|
||||
|
||||
In particular, metaclasses are probably not appropriate, however entertaining they may be to add.
|
||||
|
||||
Git Practices
|
||||
=============
|
||||
|
||||
Pull requests cannot be accepted that contain merge commits.
|
||||
|
||||
Always do "git pull --rebase" and "git rebase" vs "git pull" or "git merge".
|
||||
|
||||
Always create a new branch for each pull request to avoid intertwingling different features or fixes on the same branch.
|
||||
|
||||
|
||||
Python Version Compliance
|
||||
=========================
|
||||
|
||||
All code in Ansible core must support a minimum version of Python 2.6.
|
||||
|
||||
Module code must support a minimum of Python 2.4, with occasional exception for modules that require code that themselves require 2.6 and later.
|
||||
|
||||
A quick reminder is that list comprehensions in Python 2.4 are not as fully fleshed out, there are no 'dict' comprehensions, and there is no 'with' statement.
|
||||
But otherwise it's pretty much all the same.
|
||||
|
||||
The End
|
||||
=======
|
||||
|
||||
This was not meant to be a scary document, so we hope it wasn't, but we also hope this helps you write code that is easier to maintain by others in the future.
|
||||
If you have questions about this document, please ask on the ansible-devel mailing list.
|
||||
|
||||
Thank you!
|
||||
|
||||
|
||||
|
||||
|
||||
27
CONTRIBUTING.md
Normal file
27
CONTRIBUTING.md
Normal file
@ -0,0 +1,27 @@
|
||||
Welcome To Ansible GitHub
|
||||
=========================
|
||||
|
||||
Hi! Nice to see you here!
|
||||
|
||||
If you'd like to ask a question
|
||||
===============================
|
||||
|
||||
Please see [this web page ](http://docs.ansible.com/community.html) for community information, which includes pointers on how to ask questions on the [mailing lists](http://docs.ansible.com/community.html#mailing-list-information) and IRC.
|
||||
|
||||
The github issue tracker is not the best place for questions for various reasons, but both IRC and the mailing list are very helpful places for those things, and that page has the pointers to those.
|
||||
|
||||
If you'd like to contribute code
|
||||
================================
|
||||
|
||||
Please see [this web page](http://docs.ansible.com/community.html) for information about the contribution process. Important license agreement information is also included on that page.
|
||||
|
||||
If you'd like to file a bug
|
||||
===========================
|
||||
|
||||
I'd also read the community page above, but in particular, make sure you copy [this issue template](https://github.com/ansible/ansible/blob/devel/ISSUE_TEMPLATE.md) into your ticket description. We have a friendly neighborhood bot that will remind you if you forget :) This template helps us organize tickets faster and prevents asking some repeated questions, so it's very helpful to us and we appreciate your help with it.
|
||||
|
||||
Also please make sure you are testing on the latest released version of Ansible or the development branch.
|
||||
|
||||
Thanks!
|
||||
|
||||
|
||||
9
COPYING
9
COPYING
@ -1,7 +1,7 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
@ -664,11 +664,12 @@ might be different; for a GUI interface, you would use an "about box".
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
|
||||
|
||||
28
ISSUE_TEMPLATE.md
Normal file
28
ISSUE_TEMPLATE.md
Normal file
@ -0,0 +1,28 @@
|
||||
##### Issue Type:
|
||||
|
||||
Can you help us out in labelling this by telling us what kind of ticket this this? You can say “Bug Report”, “Feature Idea”, “Feature Pull Request”, “New Module Pull Request”, “Bugfix Pull Request”, “Documentation Report”, or “Docs Pull Request”.
|
||||
|
||||
##### Ansible Version:
|
||||
|
||||
Let us know what version of Ansible you are using. Please supply the verbatim output from running “ansible --version”. If you're filing a ticket on a version of Ansible which is not the latest, we'd greatly appreciate it if you could retest on the latest version first. We don't expect you to test against the development branch most of the time, but we may ask for that if you have cycles. Thanks!
|
||||
|
||||
##### Environment:
|
||||
|
||||
What OS are you running Ansible from and what OS are you managing? Examples include RHEL 5/6, Centos 5/6, Ubuntu 12.04/13.10, *BSD, Solaris. If this is a generic feature request or it doesn’t apply, just say “N/A”. Not all tickets may be about operating system related things and we understand that.
|
||||
|
||||
##### Summary:
|
||||
|
||||
Please summarize your request in this space. You will earn bonus points for being succinct, but please add enough detail so we can understand the request. Thanks!
|
||||
|
||||
##### Steps To Reproduce:
|
||||
|
||||
If this is a bug ticket, please enter the steps you use to reproduce the problem in the space below. If this is a feature request, please enter the steps you would use to use the feature. If an example playbook is useful, please include a short reproducer inline, indented by four spaces. If a longer one is necessary, linking to one uploaded to gist.github.com would be great. Much appreciated!
|
||||
|
||||
##### Expected Results:
|
||||
|
||||
Please enter your expected results in this space. When running the steps supplied above in the previous section, what did you expect to happen? If showing example output, please indent your output by four spaces so it will render correctly in GitHub's viewer thingy.
|
||||
|
||||
##### Actual Results:
|
||||
|
||||
Please enter your actual results in this space. When running the steps supplied above, what actually happened? If you are showing example output, please indent your output by four spaces so it will render correctly in GitHub. Thanks again!
|
||||
|
||||
22
MANIFEST.in
22
MANIFEST.in
@ -1,11 +1,11 @@
|
||||
include COPYING
|
||||
include bin/*
|
||||
include changelogs/CHANGELOG*.rst
|
||||
include changelogs/changelog.yaml
|
||||
include licenses/*.txt
|
||||
include requirements.txt
|
||||
recursive-include packaging *.py *.j2
|
||||
recursive-include test/integration *
|
||||
recursive-include test/sanity *.in *.json *.py *.txt
|
||||
recursive-include test/support *.py *.ps1 *.psm1 *.cs *.md
|
||||
recursive-include test/units *
|
||||
include README.md packaging/rpm/ansible.spec COPYING
|
||||
include examples/hosts
|
||||
include examples/ansible.cfg
|
||||
include lib/ansible/module_utils/powershell.ps1
|
||||
recursive-include lib/ansible/modules *
|
||||
recursive-include docs *
|
||||
include Makefile
|
||||
include VERSION
|
||||
include MANIFEST.in
|
||||
prune lib/ansible/modules/core/.git
|
||||
prune lib/ansible/modules/extras/.git
|
||||
|
||||
258
Makefile
Normal file
258
Makefile
Normal file
@ -0,0 +1,258 @@
|
||||
#!/usr/bin/make
|
||||
# WARN: gmake syntax
|
||||
########################################################
|
||||
# Makefile for Ansible
|
||||
#
|
||||
# useful targets:
|
||||
# make sdist ---------------- produce a tarball
|
||||
# make srpm ----------------- produce a SRPM
|
||||
# make rpm ----------------- produce RPMs
|
||||
# make deb-src -------------- produce a DEB source
|
||||
# make deb ------------------ produce a DEB
|
||||
# make docs ----------------- rebuild the manpages (results are checked in)
|
||||
# make tests ---------------- run the tests
|
||||
# make pyflakes, make pep8 -- source code checks
|
||||
|
||||
########################################################
|
||||
# variable section
|
||||
|
||||
NAME = ansible
|
||||
OS = $(shell uname -s)
|
||||
|
||||
# Manpages are currently built with asciidoc -- would like to move to markdown
|
||||
# This doesn't evaluate until it's called. The -D argument is the
|
||||
# directory of the target file ($@), kinda like `dirname`.
|
||||
MANPAGES := docs/man/man1/ansible.1 docs/man/man1/ansible-playbook.1 docs/man/man1/ansible-pull.1 docs/man/man1/ansible-doc.1 docs/man/man1/ansible-galaxy.1 docs/man/man1/ansible-vault.1
|
||||
ifneq ($(shell which a2x 2>/dev/null),)
|
||||
ASCII2MAN = a2x -D $(dir $@) -d manpage -f manpage $<
|
||||
ASCII2HTMLMAN = a2x -D docs/html/man/ -d manpage -f xhtml
|
||||
else
|
||||
ASCII2MAN = @echo "ERROR: AsciiDoc 'a2x' command is not installed but is required to build $(MANPAGES)" && exit 1
|
||||
endif
|
||||
|
||||
PYTHON=python
|
||||
SITELIB = $(shell $(PYTHON) -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")
|
||||
|
||||
# VERSION file provides one place to update the software version
|
||||
VERSION := $(shell cat VERSION)
|
||||
|
||||
# Get the branch information from git
|
||||
ifneq ($(shell which git),)
|
||||
GIT_DATE := $(shell git log -n 1 --format="%ai")
|
||||
endif
|
||||
|
||||
ifeq ($(shell echo $(OS) | egrep -c 'Darwin|FreeBSD|OpenBSD'),1)
|
||||
DATE := $(shell date -j -r $(shell git log -n 1 --format="%at") +%Y%m%d%H%M)
|
||||
else
|
||||
DATE := $(shell date --utc --date="$(GIT_DATE)" +%Y%m%d%H%M)
|
||||
endif
|
||||
|
||||
# DEB build parameters
|
||||
DEBUILD_BIN ?= debuild
|
||||
DEBUILD_OPTS = --source-option="-I"
|
||||
DPUT_BIN ?= dput
|
||||
DPUT_OPTS ?=
|
||||
ifeq ($(OFFICIAL),yes)
|
||||
DEB_RELEASE = 1ppa
|
||||
# Sign OFFICIAL builds using 'DEBSIGN_KEYID'
|
||||
# DEBSIGN_KEYID is required when signing
|
||||
ifneq ($(DEBSIGN_KEYID),)
|
||||
DEBUILD_OPTS += -k$(DEBSIGN_KEYID)
|
||||
endif
|
||||
else
|
||||
DEB_RELEASE = 0.git$(DATE)
|
||||
# Do not sign unofficial builds
|
||||
DEBUILD_OPTS += -uc -us
|
||||
DPUT_OPTS += -u
|
||||
endif
|
||||
DEBUILD = $(DEBUILD_BIN) $(DEBUILD_OPTS)
|
||||
DEB_PPA ?= ppa
|
||||
# Choose the desired Ubuntu release: lucid precise saucy trusty
|
||||
DEB_DIST ?= unstable
|
||||
|
||||
# RPM build parameters
|
||||
RPMSPECDIR= packaging/rpm
|
||||
RPMSPEC = $(RPMSPECDIR)/ansible.spec
|
||||
RPMDIST = $(shell rpm --eval '%{?dist}')
|
||||
RPMRELEASE = 1
|
||||
ifneq ($(OFFICIAL),yes)
|
||||
RPMRELEASE = 0.git$(DATE)
|
||||
endif
|
||||
RPMNVR = "$(NAME)-$(VERSION)-$(RPMRELEASE)$(RPMDIST)"
|
||||
|
||||
# MOCK build parameters
|
||||
MOCK_BIN ?= mock
|
||||
MOCK_CFG ?=
|
||||
|
||||
NOSETESTS ?= nosetests
|
||||
|
||||
NOSETESTS3 ?= nosetests-3.3
|
||||
|
||||
########################################################
|
||||
|
||||
all: clean python
|
||||
|
||||
tests:
|
||||
PYTHONPATH=./lib $(NOSETESTS) -d -w test/units -v # Could do: --with-coverage --cover-package=ansible
|
||||
|
||||
newtests:
|
||||
PYTHONPATH=./v2:./lib $(NOSETESTS) -d -w v2/test -v --with-coverage --cover-package=ansible --cover-branches
|
||||
|
||||
newtests-py3:
|
||||
PYTHONPATH=./v2:./lib $(NOSETESTS3) -d -w v2/test -v --with-coverage --cover-package=ansible --cover-branches
|
||||
|
||||
authors:
|
||||
sh hacking/authors.sh
|
||||
|
||||
# Regenerate %.1.asciidoc if %.1.asciidoc.in has been modified more
|
||||
# recently than %.1.asciidoc.
|
||||
%.1.asciidoc: %.1.asciidoc.in
|
||||
sed "s/%VERSION%/$(VERSION)/" $< > $@
|
||||
|
||||
# Regenerate %.1 if %.1.asciidoc or VERSION has been modified more
|
||||
# recently than %.1. (Implicitly runs the %.1.asciidoc recipe)
|
||||
%.1: %.1.asciidoc VERSION
|
||||
$(ASCII2MAN)
|
||||
|
||||
loc:
|
||||
sloccount lib library bin
|
||||
|
||||
pep8:
|
||||
@echo "#############################################"
|
||||
@echo "# Running PEP8 Compliance Tests"
|
||||
@echo "#############################################"
|
||||
-pep8 -r --ignore=E501,E221,W291,W391,E302,E251,E203,W293,E231,E303,E201,E225,E261,E241 lib/ bin/
|
||||
# -pep8 -r --ignore=E501,E221,W291,W391,E302,E251,E203,W293,E231,E303,E201,E225,E261,E241 --filename "*" library/
|
||||
|
||||
pyflakes:
|
||||
pyflakes lib/ansible/*.py lib/ansible/*/*.py bin/*
|
||||
|
||||
clean:
|
||||
@echo "Cleaning up distutils stuff"
|
||||
rm -rf build
|
||||
rm -rf dist
|
||||
@echo "Cleaning up byte compiled python stuff"
|
||||
find . -type f -regex ".*\.py[co]$$" -delete
|
||||
@echo "Cleaning up editor backup files"
|
||||
find . -type f \( -name "*~" -or -name "#*" \) -delete
|
||||
find . -type f \( -name "*.swp" \) -delete
|
||||
@echo "Cleaning up manpage stuff"
|
||||
find ./docs/man -type f -name "*.xml" -delete
|
||||
find ./docs/man -type f -name "*.asciidoc" -delete
|
||||
find ./docs/man/man3 -type f -name "*.3" -delete
|
||||
@echo "Cleaning up output from test runs"
|
||||
rm -rf test/test_data
|
||||
@echo "Cleaning up RPM building stuff"
|
||||
rm -rf MANIFEST rpm-build
|
||||
@echo "Cleaning up Debian building stuff"
|
||||
rm -rf debian
|
||||
rm -rf deb-build
|
||||
rm -rf docs/json
|
||||
rm -rf docs/js
|
||||
@echo "Cleaning up authors file"
|
||||
rm -f AUTHORS.TXT
|
||||
|
||||
python:
|
||||
$(PYTHON) setup.py build
|
||||
|
||||
install:
|
||||
$(PYTHON) setup.py install
|
||||
|
||||
sdist: clean docs
|
||||
$(PYTHON) setup.py sdist
|
||||
|
||||
rpmcommon: $(MANPAGES) sdist
|
||||
@mkdir -p rpm-build
|
||||
@cp dist/*.gz rpm-build/
|
||||
@sed -e 's#^Version:.*#Version: $(VERSION)#' -e 's#^Release:.*#Release: $(RPMRELEASE)%{?dist}#' $(RPMSPEC) >rpm-build/$(NAME).spec
|
||||
|
||||
mock-srpm: /etc/mock/$(MOCK_CFG).cfg rpmcommon
|
||||
$(MOCK_BIN) -r $(MOCK_CFG) --resultdir rpm-build/ --buildsrpm --spec rpm-build/$(NAME).spec --sources rpm-build/
|
||||
@echo "#############################################"
|
||||
@echo "Ansible SRPM is built:"
|
||||
@echo rpm-build/*.src.rpm
|
||||
@echo "#############################################"
|
||||
|
||||
mock-rpm: /etc/mock/$(MOCK_CFG).cfg mock-srpm
|
||||
$(MOCK_BIN) -r $(MOCK_CFG) --resultdir rpm-build/ --rebuild rpm-build/$(NAME)-*.src.rpm
|
||||
@echo "#############################################"
|
||||
@echo "Ansible RPM is built:"
|
||||
@echo rpm-build/*.noarch.rpm
|
||||
@echo "#############################################"
|
||||
|
||||
srpm: rpmcommon
|
||||
@rpmbuild --define "_topdir %(pwd)/rpm-build" \
|
||||
--define "_builddir %{_topdir}" \
|
||||
--define "_rpmdir %{_topdir}" \
|
||||
--define "_srcrpmdir %{_topdir}" \
|
||||
--define "_specdir $(RPMSPECDIR)" \
|
||||
--define "_sourcedir %{_topdir}" \
|
||||
-bs rpm-build/$(NAME).spec
|
||||
@rm -f rpm-build/$(NAME).spec
|
||||
@echo "#############################################"
|
||||
@echo "Ansible SRPM is built:"
|
||||
@echo " rpm-build/$(RPMNVR).src.rpm"
|
||||
@echo "#############################################"
|
||||
|
||||
rpm: rpmcommon
|
||||
@rpmbuild --define "_topdir %(pwd)/rpm-build" \
|
||||
--define "_builddir %{_topdir}" \
|
||||
--define "_rpmdir %{_topdir}" \
|
||||
--define "_srcrpmdir %{_topdir}" \
|
||||
--define "_specdir $(RPMSPECDIR)" \
|
||||
--define "_sourcedir %{_topdir}" \
|
||||
--define "_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm" \
|
||||
--define "__python `which $(PYTHON)`" \
|
||||
-ba rpm-build/$(NAME).spec
|
||||
@rm -f rpm-build/$(NAME).spec
|
||||
@echo "#############################################"
|
||||
@echo "Ansible RPM is built:"
|
||||
@echo " rpm-build/$(RPMNVR).noarch.rpm"
|
||||
@echo "#############################################"
|
||||
|
||||
debian: sdist
|
||||
@for DIST in $(DEB_DIST) ; do \
|
||||
mkdir -p deb-build/$${DIST} ; \
|
||||
tar -C deb-build/$${DIST} -xvf dist/$(NAME)-$(VERSION).tar.gz ; \
|
||||
cp -a packaging/debian deb-build/$${DIST}/$(NAME)-$(VERSION)/ ; \
|
||||
sed -ie "s#^$(NAME) (\([^)]*\)) \([^;]*\);#ansible (\1-$(DEB_RELEASE)~$${DIST}) $${DIST};#" deb-build/$${DIST}/$(NAME)-$(VERSION)/debian/changelog ; \
|
||||
done
|
||||
|
||||
deb: debian
|
||||
@for DIST in $(DEB_DIST) ; do \
|
||||
(cd deb-build/$${DIST}/$(NAME)-$(VERSION)/ && $(DEBUILD) -b) ; \
|
||||
done
|
||||
@echo "#############################################"
|
||||
@echo "Ansible DEB artifacts:"
|
||||
@for DIST in $(DEB_DIST) ; do \
|
||||
echo deb-build/$${DIST}/$(NAME)_$(VERSION)-$(DEB_RELEASE)~$${DIST}_amd64.changes ; \
|
||||
done
|
||||
@echo "#############################################"
|
||||
|
||||
deb-src: debian
|
||||
@for DIST in $(DEB_DIST) ; do \
|
||||
(cd deb-build/$${DIST}/$(NAME)-$(VERSION)/ && $(DEBUILD) -S) ; \
|
||||
done
|
||||
@echo "#############################################"
|
||||
@echo "Ansible DEB artifacts:"
|
||||
@for DIST in $(DEB_DIST) ; do \
|
||||
echo deb-build/$${DIST}/$(NAME)_$(VERSION)-$(DEB_RELEASE)~$${DIST}_source.changes ; \
|
||||
done
|
||||
@echo "#############################################"
|
||||
|
||||
deb-upload: deb
|
||||
@for DIST in $(DEB_DIST) ; do \
|
||||
$(DPUT_BIN) $(DPUT_OPTS) $(DEB_PPA) deb-build/$${DIST}/$(NAME)_$(VERSION)-$(DEB_RELEASE)~$${DIST}_amd64.changes ; \
|
||||
done
|
||||
|
||||
deb-src-upload: deb-src
|
||||
@for DIST in $(DEB_DIST) ; do \
|
||||
$(DPUT_BIN) $(DPUT_OPTS) $(DEB_PPA) deb-build/$${DIST}/$(NAME)_$(VERSION)-$(DEB_RELEASE)~$${DIST}_source.changes ; \
|
||||
done
|
||||
|
||||
# for arch or gentoo, read instructions in the appropriate 'packaging' subdirectory directory
|
||||
|
||||
webdocs: $(MANPAGES)
|
||||
(cd docsite/; make docs)
|
||||
|
||||
docs: $(MANPAGES)
|
||||
113
README.md
113
README.md
@ -1,89 +1,56 @@
|
||||
[](https://pypi.org/project/ansible-core)
|
||||
[](https://docs.ansible.com/ansible/latest/)
|
||||
[](https://docs.ansible.com/ansible/latest/community/communication.html)
|
||||
[](https://dev.azure.com/ansible/ansible/_build/latest?definitionId=20&branchName=devel)
|
||||
[](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html)
|
||||
[](https://docs.ansible.com/ansible/latest/community/communication.html#mailing-list-information)
|
||||
[](COPYING)
|
||||
[](https://bestpractices.coreinfrastructure.org/projects/2372)
|
||||
[](http://badge.fury.io/py/ansible) [](https://pypi.python.org/pypi/ansible)
|
||||
|
||||
# Ansible
|
||||
|
||||
Ansible is a radically simple IT automation system. It handles
|
||||
configuration management, application deployment, cloud provisioning,
|
||||
ad-hoc task execution, network automation, and multi-node orchestration. Ansible makes complex
|
||||
changes like zero-downtime rolling updates with load balancers easy. More information on the Ansible [website](https://ansible.com/).
|
||||
Ansible
|
||||
=======
|
||||
|
||||
## Design Principles
|
||||
Ansible is a radically simple IT automation system. It handles configuration-management, application deployment, cloud provisioning, ad-hoc task-execution, and multinode orchestration - including trivializing things like zero downtime rolling updates with load balancers.
|
||||
|
||||
* Have an extremely simple setup process with a minimal learning curve.
|
||||
* Manage machines quickly and in parallel.
|
||||
* Avoid custom-agents and additional open ports, be agentless by
|
||||
leveraging the existing SSH daemon.
|
||||
* Describe infrastructure in a language that is both machine and human
|
||||
friendly.
|
||||
* Focus on security and easy auditability/review/rewriting of content.
|
||||
* Manage new remote machines instantly, without bootstrapping any
|
||||
software.
|
||||
* Allow module development in any dynamic language, not just Python.
|
||||
* Be usable as non-root.
|
||||
* Be the easiest IT automation system to use, ever.
|
||||
Read the documentation and more at http://ansible.com/
|
||||
|
||||
## Use Ansible
|
||||
Many users run straight from the development branch (it's generally fine to do so), but you might also wish to consume a release.
|
||||
|
||||
You can install a released version of Ansible with `pip` or a package manager. See our
|
||||
[installation guide](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html) for details on installing Ansible
|
||||
on a variety of platforms.
|
||||
You can find instructions [here](http://docs.ansible.com/intro_getting_started.html) for a variety of platforms. If you decide to go with the development branch, be sure to run "git submodule update --init --recursive" after doing a checkout.
|
||||
|
||||
Power users and developers can run the `devel` branch, which has the latest
|
||||
features and fixes, directly. Although it is reasonably stable, you are more likely to encounter
|
||||
breaking changes when running the `devel` branch. We recommend getting involved
|
||||
in the Ansible community if you want to run the `devel` branch.
|
||||
If you want to download a tarball of a release, go to [releases.ansible.com](http://releases.ansible.com/ansible), though most users use yum (using the EPEL instructions linked above), apt (using the PPA instructions linked above), or "pip install ansible".
|
||||
|
||||
## Get Involved
|
||||
Design Principles
|
||||
=================
|
||||
|
||||
* Read [Community Information](https://docs.ansible.com/ansible/latest/community) for all
|
||||
kinds of ways to contribute to and interact with the project,
|
||||
including mailing list information and how to submit bug reports and
|
||||
code to Ansible.
|
||||
* Join a [Working Group](https://docs.ansible.com/ansible/devel/community/communication.html#working-groups),
|
||||
an organized community devoted to a specific technology domain or platform.
|
||||
* Submit a proposed code update through a pull request to the `devel` branch.
|
||||
* Talk to us before making larger changes
|
||||
to avoid duplicate efforts. This not only helps everyone
|
||||
know what is going on, but it also helps save time and effort if we decide
|
||||
some changes are needed.
|
||||
* For a list of email lists, IRC channels and Working Groups, see the
|
||||
[Communication page](https://docs.ansible.com/ansible/devel/community/communication.html)
|
||||
* Have a dead simple setup process and a minimal learning curve
|
||||
* Manage machines very quickly and in parallel
|
||||
* Avoid custom-agents and additional open ports, be agentless by leveraging the existing SSH daemon
|
||||
* Describe infrastructure in a language that is both machine and human friendly
|
||||
* Focus on security and easy auditability/review/rewriting of content
|
||||
* Manage new remote machines instantly, without bootstrapping any software
|
||||
* Allow module development in any dynamic language, not just Python
|
||||
* Be usable as non-root
|
||||
* Be the easiest IT automation system to use, ever.
|
||||
|
||||
Get Involved
|
||||
============
|
||||
|
||||
## Coding Guidelines
|
||||
* Read [Community Information](http://docs.ansible.com/community.html) for all kinds of ways to contribute to and interact with the project, including mailing list information and how to submit bug reports and code to Ansible.
|
||||
* All code submissions are done through pull requests. Take care to make sure no merge commits are in the submission, and use "git rebase" vs "git merge" for this reason. If submitting a large code change (other than modules), it's probably a good idea to join ansible-devel and talk about what you would like to do or add first and to avoid duplicate efforts. This not only helps everyone know what's going on, it also helps save time and effort if we decide some changes are needed.
|
||||
* Users list: [ansible-project](http://groups.google.com/group/ansible-project)
|
||||
* Development list: [ansible-devel](http://groups.google.com/group/ansible-devel)
|
||||
* Announcement list: [ansible-announce](http://groups.google.com/group/ansible-announce) - read only
|
||||
* irc.freenode.net: #ansible
|
||||
|
||||
We document our Coding Guidelines in the [Developer Guide](https://docs.ansible.com/ansible/devel/dev_guide/). We particularly suggest you review:
|
||||
Branch Info
|
||||
===========
|
||||
|
||||
* [Contributing your module to Ansible](https://docs.ansible.com/ansible/devel/dev_guide/developing_modules_checklist.html)
|
||||
* [Conventions, tips, and pitfalls](https://docs.ansible.com/ansible/devel/dev_guide/developing_modules_best_practices.html)
|
||||
* Releases are named after Van Halen songs.
|
||||
* The devel branch corresponds to the release actively under development.
|
||||
* As of 1.8, modules are kept in different repos, you'll want to follow [core](https://github.com/ansible/ansible-modules-core) and [extras](https://github.com/ansible/ansible-modules-extras)
|
||||
* Various release-X.Y branches exist for previous releases.
|
||||
* We'd love to have your contributions, read [Community Information](http://docs.ansible.com/community.html) for notes on how to get started.
|
||||
|
||||
## Branch Info
|
||||
Authors
|
||||
=======
|
||||
|
||||
* The `devel` branch corresponds to the release actively under development.
|
||||
* The `stable-2.X` branches correspond to stable releases.
|
||||
* Create a branch based on `devel` and set up a [dev environment](https://docs.ansible.com/ansible/latest/dev_guide/developing_modules_general.html#common-environment-setup) if you want to open a PR.
|
||||
* See the [Ansible release and maintenance](https://docs.ansible.com/ansible/devel/reference_appendices/release_and_maintenance.html) page for information about active branches.
|
||||
Ansible was created by [Michael DeHaan](https://github.com/mpdehaan) (michael@ansible.com) and has contributions from over
|
||||
900 users (and growing). Thanks everyone!
|
||||
|
||||
## Roadmap
|
||||
Ansible is sponsored by [Ansible, Inc](http://ansible.com)
|
||||
|
||||
Based on team and community feedback, an initial roadmap will be published for a major or minor version (ex: 2.7, 2.8).
|
||||
The [Ansible Roadmap page](https://docs.ansible.com/ansible/devel/roadmap/) details what is planned and how to influence the roadmap.
|
||||
|
||||
## Authors
|
||||
|
||||
Ansible was created by [Michael DeHaan](https://github.com/mpdehaan)
|
||||
and has contributions from over 5000 users (and growing). Thanks everyone!
|
||||
|
||||
[Ansible](https://www.ansible.com) is sponsored by [Red Hat, Inc.](https://www.redhat.com)
|
||||
|
||||
## License
|
||||
|
||||
GNU General Public License v3.0 or later
|
||||
|
||||
See [COPYING](COPYING) to see the full text.
|
||||
|
||||
61
RELEASES.txt
Normal file
61
RELEASES.txt
Normal file
@ -0,0 +1,61 @@
|
||||
Ansible Releases at a Glance
|
||||
============================
|
||||
|
||||
Active Development
|
||||
++++++++++++++++++
|
||||
|
||||
1.9 "Dancing In the Streets" WINTER 2015
|
||||
|
||||
Released
|
||||
++++++++
|
||||
|
||||
1.8.1 "You Really Got Me" ---- 11-26-2014
|
||||
1.8 "You Really Got Me" ---- 11-25-2014
|
||||
1.7.2 "Summer Nights" -------- 09-24-2014
|
||||
1.7.1 "Summer Nights" -------- 08-14-2014
|
||||
1.7 "Summer Nights" -------- 08-06-2014
|
||||
1.6.10 "The Cradle Will Rock" - 07-25-2014
|
||||
1.6.9 "The Cradle Will Rock" - 07-24-2014
|
||||
1.6.8 "The Cradle Will Rock" - 07-22-2014
|
||||
1.6.7 "The Cradle Will Rock" - 07-21-2014
|
||||
1.6.6 "The Cradle Will Rock" - 07-01-2014
|
||||
1.6.5 "The Cradle Will Rock" - 06-25-2014
|
||||
1.6.4 "The Cradle Will Rock" - 06-25-2014
|
||||
1.6.3 "The Cradle Will Rock" - 06-09-2014
|
||||
1.6.2 "The Cradle Will Rock" - 05-23-2014
|
||||
1.6.1 "The Cradle Will Rock" - 05-07-2014
|
||||
1.6 "The Cradle Will Rock" - 05-05-2014
|
||||
1.5.5 "Love Walks In" -------- 04-18-2014
|
||||
1.5.4 "Love Walks In" -------- 04-01-2014
|
||||
1.5.3 "Love Walks In" -------- 03-13-2014
|
||||
1.5.2 "Love Walks In" -------- 03-11-2014
|
||||
1.5.1 "Love Walks In" -------- 03-10-2014
|
||||
1.5 "Love Walks In" -------- 02-28-2014
|
||||
1.4.5 "Could This Be Magic?" - 02-12-2014
|
||||
1.4.4 "Could This Be Magic?" - 01-06-2014
|
||||
1.4.3 "Could This Be Magic?" - 12-20-2013
|
||||
1.4.2 "Could This Be Magic?" - 12-18-2013
|
||||
1.4.1 "Could This Be Magic?" - 11-27-2013
|
||||
1.4 "Could This Be Magic?" - 11-21-2013
|
||||
1.3.4 "Top of the World" ----- 10-29-2013
|
||||
1.3.3 "Top of the World" ----- 10-09-2013
|
||||
1.3.2 "Top of the World" ----- 09-19-2013
|
||||
1.3.1 "Top of the World" ----- 09-16-2013
|
||||
1.3 "Top of the World" ----- 09-13-2013
|
||||
1.2.3 "Hear About It Later" -- 08-21-2013
|
||||
1.2.2 "Hear About It Later" -- 07-05-2013
|
||||
1.2.1 "Hear About It Later" -- 07-04-2013
|
||||
1.2 "Right Now" ------------ 06-10-2013
|
||||
1.1 "Mean Street" ---------- 04-02-2013
|
||||
1.0 "Eruption" ------------- 02-01-2013
|
||||
0.9 "Dreams" --------------- 11-30-2012
|
||||
0.8 "Cathedral" ------------ 10-19-2012
|
||||
0.7 "Panama" --------------- 09-06-2012
|
||||
0.6 "Cabo" ----------------- 08-06-2012
|
||||
0.5 "Amsterdam" ------------ 07-04-2012
|
||||
0.4 "Unchained" ------------ 05-23-2012
|
||||
0.3 "Baluchitherium" ------- 04-23-2012
|
||||
0.0.2 Untitled
|
||||
0.0.1 Untitled
|
||||
|
||||
|
||||
@ -1 +0,0 @@
|
||||
../lib/ansible/cli/adhoc.py
|
||||
204
bin/ansible
Executable file
204
bin/ansible
Executable file
@ -0,0 +1,204 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
########################################################
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from ansible.runner import Runner
|
||||
import ansible.constants as C
|
||||
from ansible import utils
|
||||
from ansible import errors
|
||||
from ansible import callbacks
|
||||
from ansible import inventory
|
||||
########################################################
|
||||
|
||||
class Cli(object):
|
||||
''' code behind bin/ansible '''
|
||||
|
||||
# ----------------------------------------------
|
||||
|
||||
def __init__(self):
|
||||
self.stats = callbacks.AggregateStats()
|
||||
self.callbacks = callbacks.CliRunnerCallbacks()
|
||||
if C.DEFAULT_LOAD_CALLBACK_PLUGINS:
|
||||
callbacks.load_callback_plugins()
|
||||
|
||||
# ----------------------------------------------
|
||||
|
||||
def parse(self):
|
||||
''' create an options parser for bin/ansible '''
|
||||
|
||||
parser = utils.base_parser(
|
||||
constants=C,
|
||||
runas_opts=True,
|
||||
subset_opts=True,
|
||||
async_opts=True,
|
||||
output_opts=True,
|
||||
connect_opts=True,
|
||||
check_opts=True,
|
||||
diff_opts=False,
|
||||
usage='%prog <host-pattern> [options]'
|
||||
)
|
||||
|
||||
parser.add_option('-a', '--args', dest='module_args',
|
||||
help="module arguments", default=C.DEFAULT_MODULE_ARGS)
|
||||
parser.add_option('-m', '--module-name', dest='module_name',
|
||||
help="module name to execute (default=%s)" % C.DEFAULT_MODULE_NAME,
|
||||
default=C.DEFAULT_MODULE_NAME)
|
||||
|
||||
options, args = parser.parse_args()
|
||||
self.callbacks.options = options
|
||||
|
||||
if len(args) == 0 or len(args) > 1:
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
# su and sudo command line arguments need to be mutually exclusive
|
||||
if (options.su or options.su_user or options.ask_su_pass) and \
|
||||
(options.sudo or options.sudo_user or options.ask_sudo_pass):
|
||||
parser.error("Sudo arguments ('--sudo', '--sudo-user', and '--ask-sudo-pass') "
|
||||
"and su arguments ('-su', '--su-user', and '--ask-su-pass') are "
|
||||
"mutually exclusive")
|
||||
|
||||
if (options.ask_vault_pass and options.vault_password_file):
|
||||
parser.error("--ask-vault-pass and --vault-password-file are mutually exclusive")
|
||||
|
||||
return (options, args)
|
||||
|
||||
# ----------------------------------------------
|
||||
|
||||
def run(self, options, args):
|
||||
''' use Runner lib to do SSH things '''
|
||||
|
||||
pattern = args[0]
|
||||
|
||||
sshpass = None
|
||||
sudopass = None
|
||||
su_pass = None
|
||||
vault_pass = None
|
||||
|
||||
options.ask_pass = options.ask_pass or C.DEFAULT_ASK_PASS
|
||||
# Never ask for an SSH password when we run with local connection
|
||||
if options.connection == "local":
|
||||
options.ask_pass = False
|
||||
options.ask_sudo_pass = options.ask_sudo_pass or C.DEFAULT_ASK_SUDO_PASS
|
||||
options.ask_su_pass = options.ask_su_pass or C.DEFAULT_ASK_SU_PASS
|
||||
options.ask_vault_pass = options.ask_vault_pass or C.DEFAULT_ASK_VAULT_PASS
|
||||
|
||||
(sshpass, sudopass, su_pass, vault_pass) = utils.ask_passwords(ask_pass=options.ask_pass, ask_sudo_pass=options.ask_sudo_pass, ask_su_pass=options.ask_su_pass, ask_vault_pass=options.ask_vault_pass)
|
||||
|
||||
# read vault_pass from a file
|
||||
if not options.ask_vault_pass and options.vault_password_file:
|
||||
vault_pass = utils.read_vault_file(options.vault_password_file)
|
||||
|
||||
inventory_manager = inventory.Inventory(options.inventory, vault_password=vault_pass)
|
||||
if options.subset:
|
||||
inventory_manager.subset(options.subset)
|
||||
hosts = inventory_manager.list_hosts(pattern)
|
||||
if len(hosts) == 0:
|
||||
callbacks.display("No hosts matched", stderr=True)
|
||||
sys.exit(0)
|
||||
|
||||
if options.listhosts:
|
||||
for host in hosts:
|
||||
callbacks.display(' %s' % host)
|
||||
sys.exit(0)
|
||||
|
||||
if ((options.module_name == 'command' or options.module_name == 'shell')
|
||||
and not options.module_args):
|
||||
callbacks.display("No argument passed to %s module" % options.module_name, color='red', stderr=True)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if options.su_user or options.ask_su_pass:
|
||||
options.su = True
|
||||
options.sudo_user = options.sudo_user or C.DEFAULT_SUDO_USER
|
||||
options.su_user = options.su_user or C.DEFAULT_SU_USER
|
||||
if options.tree:
|
||||
utils.prepare_writeable_dir(options.tree)
|
||||
|
||||
runner = Runner(
|
||||
module_name=options.module_name,
|
||||
module_path=options.module_path,
|
||||
module_args=options.module_args,
|
||||
remote_user=options.remote_user,
|
||||
remote_pass=sshpass,
|
||||
inventory=inventory_manager,
|
||||
timeout=options.timeout,
|
||||
private_key_file=options.private_key_file,
|
||||
forks=options.forks,
|
||||
pattern=pattern,
|
||||
callbacks=self.callbacks,
|
||||
sudo=options.sudo,
|
||||
sudo_pass=sudopass,
|
||||
sudo_user=options.sudo_user,
|
||||
transport=options.connection,
|
||||
subset=options.subset,
|
||||
check=options.check,
|
||||
diff=options.check,
|
||||
su=options.su,
|
||||
su_pass=su_pass,
|
||||
su_user=options.su_user,
|
||||
vault_pass=vault_pass
|
||||
)
|
||||
|
||||
if options.seconds:
|
||||
callbacks.display("background launch...\n\n", color='cyan')
|
||||
results, poller = runner.run_async(options.seconds)
|
||||
results = self.poll_while_needed(poller, options)
|
||||
else:
|
||||
results = runner.run()
|
||||
|
||||
return (runner, results)
|
||||
|
||||
# ----------------------------------------------
|
||||
|
||||
def poll_while_needed(self, poller, options):
|
||||
''' summarize results from Runner '''
|
||||
|
||||
# BACKGROUND POLL LOGIC when -B and -P are specified
|
||||
if options.seconds and options.poll_interval > 0:
|
||||
poller.wait(options.seconds, options.poll_interval)
|
||||
|
||||
return poller.results
|
||||
|
||||
|
||||
########################################################
|
||||
|
||||
if __name__ == '__main__':
|
||||
callbacks.display("", log_only=True)
|
||||
callbacks.display(" ".join(sys.argv), log_only=True)
|
||||
callbacks.display("", log_only=True)
|
||||
|
||||
cli = Cli()
|
||||
(options, args) = cli.parse()
|
||||
try:
|
||||
(runner, results) = cli.run(options, args)
|
||||
for result in results['contacted'].values():
|
||||
if 'failed' in result or result.get('rc', 0) != 0:
|
||||
sys.exit(2)
|
||||
if results['dark']:
|
||||
sys.exit(3)
|
||||
except errors.AnsibleError, e:
|
||||
# Generic handler for ansible specific errors
|
||||
callbacks.display("ERROR: %s" % str(e), stderr=True, color='red')
|
||||
sys.exit(1)
|
||||
|
||||
@ -1 +0,0 @@
|
||||
../lib/ansible/cli/config.py
|
||||
@ -1 +0,0 @@
|
||||
../lib/ansible/cli/scripts/ansible_connection_cli_stub.py
|
||||
@ -1 +0,0 @@
|
||||
../lib/ansible/cli/console.py
|
||||
@ -1 +0,0 @@
|
||||
../lib/ansible/cli/doc.py
|
||||
324
bin/ansible-doc
Executable file
324
bin/ansible-doc
Executable file
@ -0,0 +1,324 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# (c) 2012, Jan-Piet Mens <jpmens () gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
import os
|
||||
import sys
|
||||
import textwrap
|
||||
import re
|
||||
import optparse
|
||||
import datetime
|
||||
import subprocess
|
||||
from ansible import utils
|
||||
from ansible.utils import module_docs
|
||||
import ansible.constants as C
|
||||
from ansible.utils import version
|
||||
import traceback
|
||||
|
||||
MODULEDIR = C.DEFAULT_MODULE_PATH
|
||||
|
||||
BLACKLIST_EXTS = ('.pyc', '.swp', '.bak', '~', '.rpm')
|
||||
IGNORE_FILES = [ "COPYING", "CONTRIBUTING", "LICENSE", "README" ]
|
||||
|
||||
_ITALIC = re.compile(r"I\(([^)]+)\)")
|
||||
_BOLD = re.compile(r"B\(([^)]+)\)")
|
||||
_MODULE = re.compile(r"M\(([^)]+)\)")
|
||||
_URL = re.compile(r"U\(([^)]+)\)")
|
||||
_CONST = re.compile(r"C\(([^)]+)\)")
|
||||
PAGER = 'less'
|
||||
LESS_OPTS = 'FRSX' # -F (quit-if-one-screen) -R (allow raw ansi control chars)
|
||||
# -S (chop long lines) -X (disable termcap init and de-init)
|
||||
|
||||
def pager_print(text):
|
||||
''' just print text '''
|
||||
print text
|
||||
|
||||
def pager_pipe(text, cmd):
|
||||
''' pipe text through a pager '''
|
||||
if 'LESS' not in os.environ:
|
||||
os.environ['LESS'] = LESS_OPTS
|
||||
try:
|
||||
cmd = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=sys.stdout)
|
||||
cmd.communicate(input=text)
|
||||
except IOError:
|
||||
pass
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
def pager(text):
|
||||
''' find reasonable way to display text '''
|
||||
# this is a much simpler form of what is in pydoc.py
|
||||
if not sys.stdout.isatty():
|
||||
pager_print(text)
|
||||
elif 'PAGER' in os.environ:
|
||||
if sys.platform == 'win32':
|
||||
pager_print(text)
|
||||
else:
|
||||
pager_pipe(text, os.environ['PAGER'])
|
||||
elif hasattr(os, 'system') and os.system('(less) 2> /dev/null') == 0:
|
||||
pager_pipe(text, 'less')
|
||||
else:
|
||||
pager_print(text)
|
||||
|
||||
def tty_ify(text):
|
||||
|
||||
t = _ITALIC.sub("`" + r"\1" + "'", text) # I(word) => `word'
|
||||
t = _BOLD.sub("*" + r"\1" + "*", t) # B(word) => *word*
|
||||
t = _MODULE.sub("[" + r"\1" + "]", t) # M(word) => [word]
|
||||
t = _URL.sub(r"\1", t) # U(word) => word
|
||||
t = _CONST.sub("`" + r"\1" + "'", t) # C(word) => `word'
|
||||
|
||||
return t
|
||||
|
||||
def get_man_text(doc):
|
||||
|
||||
opt_indent=" "
|
||||
text = []
|
||||
text.append("> %s\n" % doc['module'].upper())
|
||||
|
||||
desc = " ".join(doc['description'])
|
||||
|
||||
text.append("%s\n" % textwrap.fill(tty_ify(desc), initial_indent=" ", subsequent_indent=" "))
|
||||
|
||||
if 'option_keys' in doc and len(doc['option_keys']) > 0:
|
||||
text.append("Options (= is mandatory):\n")
|
||||
|
||||
for o in sorted(doc['option_keys']):
|
||||
opt = doc['options'][o]
|
||||
|
||||
if opt.get('required', False):
|
||||
opt_leadin = "="
|
||||
else:
|
||||
opt_leadin = "-"
|
||||
|
||||
text.append("%s %s" % (opt_leadin, o))
|
||||
|
||||
desc = " ".join(opt['description'])
|
||||
|
||||
if 'choices' in opt:
|
||||
choices = ", ".join(str(i) for i in opt['choices'])
|
||||
desc = desc + " (Choices: " + choices + ")"
|
||||
if 'default' in opt:
|
||||
default = str(opt['default'])
|
||||
desc = desc + " [Default: " + default + "]"
|
||||
text.append("%s\n" % textwrap.fill(tty_ify(desc), initial_indent=opt_indent,
|
||||
subsequent_indent=opt_indent))
|
||||
|
||||
if 'notes' in doc and len(doc['notes']) > 0:
|
||||
notes = " ".join(doc['notes'])
|
||||
text.append("Notes:%s\n" % textwrap.fill(tty_ify(notes), initial_indent=" ",
|
||||
subsequent_indent=opt_indent))
|
||||
|
||||
|
||||
if 'requirements' in doc and doc['requirements'] is not None and len(doc['requirements']) > 0:
|
||||
req = ", ".join(doc['requirements'])
|
||||
text.append("Requirements:%s\n" % textwrap.fill(tty_ify(req), initial_indent=" ",
|
||||
subsequent_indent=opt_indent))
|
||||
|
||||
if 'examples' in doc and len(doc['examples']) > 0:
|
||||
text.append("Example%s:\n" % ('' if len(doc['examples']) < 2 else 's'))
|
||||
for ex in doc['examples']:
|
||||
text.append("%s\n" % (ex['code']))
|
||||
|
||||
if 'plainexamples' in doc and doc['plainexamples'] is not None:
|
||||
text.append(doc['plainexamples'])
|
||||
text.append('')
|
||||
|
||||
return "\n".join(text)
|
||||
|
||||
|
||||
def get_snippet_text(doc):
|
||||
|
||||
text = []
|
||||
desc = tty_ify(" ".join(doc['short_description']))
|
||||
text.append("- name: %s" % (desc))
|
||||
text.append(" action: %s" % (doc['module']))
|
||||
|
||||
for o in sorted(doc['options'].keys()):
|
||||
opt = doc['options'][o]
|
||||
desc = tty_ify(" ".join(opt['description']))
|
||||
|
||||
if opt.get('required', False):
|
||||
s = o + "="
|
||||
else:
|
||||
s = o
|
||||
|
||||
text.append(" %-20s # %s" % (s, desc))
|
||||
text.append('')
|
||||
|
||||
return "\n".join(text)
|
||||
|
||||
def get_module_list_text(module_list):
|
||||
columns = max(60, int(os.popen('stty size', 'r').read().split()[1]))
|
||||
displace = max(len(x) for x in module_list)
|
||||
linelimit = columns - displace - 5
|
||||
text = []
|
||||
deprecated = []
|
||||
for module in sorted(set(module_list)):
|
||||
|
||||
if module in module_docs.BLACKLIST_MODULES:
|
||||
continue
|
||||
|
||||
filename = utils.plugins.module_finder.find_plugin(module)
|
||||
|
||||
if filename is None:
|
||||
continue
|
||||
if filename.endswith(".ps1"):
|
||||
continue
|
||||
if os.path.isdir(filename):
|
||||
continue
|
||||
|
||||
try:
|
||||
doc, plainexamples = module_docs.get_docstring(filename)
|
||||
desc = tty_ify(doc.get('short_description', '?')).strip()
|
||||
if len(desc) > linelimit:
|
||||
desc = desc[:linelimit] + '...'
|
||||
|
||||
if module.startswith('_'): # Handle deprecated
|
||||
deprecated.append("%-*s %-*.*s" % (displace, module[1:], linelimit, len(desc), desc))
|
||||
else:
|
||||
text.append("%-*s %-*.*s" % (displace, module, linelimit, len(desc), desc))
|
||||
except:
|
||||
traceback.print_exc()
|
||||
sys.stderr.write("ERROR: module %s has a documentation error formatting or is missing documentation\n" % module)
|
||||
|
||||
if len(deprecated) > 0:
|
||||
text.append("\nDEPRECATED:")
|
||||
text.extend(deprecated)
|
||||
return "\n".join(text)
|
||||
|
||||
def find_modules(path, module_list):
|
||||
|
||||
if os.path.isdir(path):
|
||||
for module in os.listdir(path):
|
||||
if module.startswith('.'):
|
||||
continue
|
||||
elif os.path.isdir(module):
|
||||
find_modules(module, module_list)
|
||||
elif any(module.endswith(x) for x in BLACKLIST_EXTS):
|
||||
continue
|
||||
elif module.startswith('__'):
|
||||
continue
|
||||
elif module in IGNORE_FILES:
|
||||
continue
|
||||
elif module.startswith('_'):
|
||||
fullpath = '/'.join([path,module])
|
||||
if os.path.islink(fullpath): # avoids aliases
|
||||
continue
|
||||
|
||||
module = os.path.splitext(module)[0] # removes the extension
|
||||
module_list.append(module)
|
||||
|
||||
def main():
|
||||
|
||||
p = optparse.OptionParser(
|
||||
version=version("%prog"),
|
||||
usage='usage: %prog [options] [module...]',
|
||||
description='Show Ansible module documentation',
|
||||
)
|
||||
|
||||
p.add_option("-M", "--module-path",
|
||||
action="store",
|
||||
dest="module_path",
|
||||
default=MODULEDIR,
|
||||
help="Ansible modules/ directory")
|
||||
p.add_option("-l", "--list",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest='list_dir',
|
||||
help='List available modules')
|
||||
p.add_option("-s", "--snippet",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest='show_snippet',
|
||||
help='Show playbook snippet for specified module(s)')
|
||||
p.add_option('-v', action='version', help='Show version number and exit')
|
||||
|
||||
(options, args) = p.parse_args()
|
||||
|
||||
if options.module_path is not None:
|
||||
for i in options.module_path.split(os.pathsep):
|
||||
utils.plugins.module_finder.add_directory(i)
|
||||
|
||||
if options.list_dir:
|
||||
# list modules
|
||||
paths = utils.plugins.module_finder._get_paths()
|
||||
module_list = []
|
||||
for path in paths:
|
||||
find_modules(path, module_list)
|
||||
|
||||
pager(get_module_list_text(module_list))
|
||||
sys.exit()
|
||||
|
||||
if len(args) == 0:
|
||||
p.print_help()
|
||||
|
||||
def print_paths(finder):
|
||||
''' Returns a string suitable for printing of the search path '''
|
||||
|
||||
# Uses a list to get the order right
|
||||
ret = []
|
||||
for i in finder._get_paths():
|
||||
if i not in ret:
|
||||
ret.append(i)
|
||||
return os.pathsep.join(ret)
|
||||
|
||||
text = ''
|
||||
for module in args:
|
||||
|
||||
filename = utils.plugins.module_finder.find_plugin(module)
|
||||
if filename is None:
|
||||
sys.stderr.write("module %s not found in %s\n" % (module, print_paths(utils.plugins.module_finder)))
|
||||
continue
|
||||
|
||||
if any(filename.endswith(x) for x in BLACKLIST_EXTS):
|
||||
continue
|
||||
|
||||
try:
|
||||
doc, plainexamples = module_docs.get_docstring(filename)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
sys.stderr.write("ERROR: module %s has a documentation error formatting or is missing documentation\n" % module)
|
||||
continue
|
||||
|
||||
if doc is not None:
|
||||
|
||||
all_keys = []
|
||||
for (k,v) in doc['options'].iteritems():
|
||||
all_keys.append(k)
|
||||
all_keys = sorted(all_keys)
|
||||
doc['option_keys'] = all_keys
|
||||
|
||||
doc['filename'] = filename
|
||||
doc['docuri'] = doc['module'].replace('_', '-')
|
||||
doc['now_date'] = datetime.date.today().strftime('%Y-%m-%d')
|
||||
doc['plainexamples'] = plainexamples
|
||||
|
||||
if options.show_snippet:
|
||||
text += get_snippet_text(doc)
|
||||
else:
|
||||
text += get_man_text(doc)
|
||||
else:
|
||||
# this typically means we couldn't even parse the docstring, not just that the YAML is busted,
|
||||
# probably a quoting issue.
|
||||
sys.stderr.write("ERROR: module %s missing documentation (or could not parse documentation)\n" % module)
|
||||
pager(text)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@ -1 +0,0 @@
|
||||
../lib/ansible/cli/galaxy.py
|
||||
954
bin/ansible-galaxy
Executable file
954
bin/ansible-galaxy
Executable file
@ -0,0 +1,954 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
########################################################################
|
||||
#
|
||||
# (C) 2013, James Cammarata <jcammarata@ansible.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
########################################################################
|
||||
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import os.path
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tarfile
|
||||
import tempfile
|
||||
import urllib
|
||||
import urllib2
|
||||
import yaml
|
||||
|
||||
from collections import defaultdict
|
||||
from distutils.version import LooseVersion
|
||||
from jinja2 import Environment
|
||||
from optparse import OptionParser
|
||||
|
||||
import ansible.constants as C
|
||||
import ansible.utils
|
||||
from ansible.errors import AnsibleError
|
||||
|
||||
default_meta_template = """---
|
||||
galaxy_info:
|
||||
author: {{ author }}
|
||||
description: {{description}}
|
||||
company: {{ company }}
|
||||
# Some suggested licenses:
|
||||
# - BSD (default)
|
||||
# - MIT
|
||||
# - GPLv2
|
||||
# - GPLv3
|
||||
# - Apache
|
||||
# - CC-BY
|
||||
license: {{ license }}
|
||||
min_ansible_version: {{ min_ansible_version }}
|
||||
#
|
||||
# Below are all platforms currently available. Just uncomment
|
||||
# the ones that apply to your role. If you don't see your
|
||||
# platform on this list, let us know and we'll get it added!
|
||||
#
|
||||
#platforms:
|
||||
{%- for platform,versions in platforms.iteritems() %}
|
||||
#- name: {{ platform }}
|
||||
# versions:
|
||||
# - all
|
||||
{%- for version in versions %}
|
||||
# - {{ version }}
|
||||
{%- endfor %}
|
||||
{%- endfor %}
|
||||
#
|
||||
# Below are all categories currently available. Just as with
|
||||
# the platforms above, uncomment those that apply to your role.
|
||||
#
|
||||
#categories:
|
||||
{%- for category in categories %}
|
||||
#- {{ category.name }}
|
||||
{%- endfor %}
|
||||
dependencies: []
|
||||
# List your role dependencies here, one per line. Only
|
||||
# dependencies available via galaxy should be listed here.
|
||||
# Be sure to remove the '[]' above if you add dependencies
|
||||
# to this list.
|
||||
{% for dependency in dependencies %}
|
||||
#- {{ dependency }}
|
||||
{% endfor %}
|
||||
|
||||
"""
|
||||
|
||||
default_readme_template = """Role Name
|
||||
=========
|
||||
|
||||
A brief description of the role goes here.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
|
||||
|
||||
Role Variables
|
||||
--------------
|
||||
|
||||
A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
|
||||
|
||||
Example Playbook
|
||||
----------------
|
||||
|
||||
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
|
||||
|
||||
- hosts: servers
|
||||
roles:
|
||||
- { role: username.rolename, x: 42 }
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
BSD
|
||||
|
||||
Author Information
|
||||
------------------
|
||||
|
||||
An optional section for the role authors to include contact information, or a website (HTML is not allowed).
|
||||
"""
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
# Utility functions for parsing actions/options
|
||||
#-------------------------------------------------------------------------------------
|
||||
|
||||
VALID_ACTIONS = ("init", "info", "install", "list", "remove")
|
||||
SKIP_INFO_KEYS = ("platforms","readme_html", "related", "summary_fields", "average_aw_composite", "average_aw_score", "url" )
|
||||
|
||||
def get_action(args):
|
||||
"""
|
||||
Get the action the user wants to execute from the
|
||||
sys argv list.
|
||||
"""
|
||||
for i in range(0,len(args)):
|
||||
arg = args[i]
|
||||
if arg in VALID_ACTIONS:
|
||||
del args[i]
|
||||
return arg
|
||||
return None
|
||||
|
||||
def build_option_parser(action):
|
||||
"""
|
||||
Builds an option parser object based on the action
|
||||
the user wants to execute.
|
||||
"""
|
||||
|
||||
usage = "usage: %%prog [%s] [--help] [options] ..." % "|".join(VALID_ACTIONS)
|
||||
epilog = "\nSee '%s <command> --help' for more information on a specific command.\n\n" % os.path.basename(sys.argv[0])
|
||||
OptionParser.format_epilog = lambda self, formatter: self.epilog
|
||||
parser = OptionParser(usage=usage, epilog=epilog)
|
||||
|
||||
if not action:
|
||||
parser.print_help()
|
||||
sys.exit()
|
||||
|
||||
# options for all actions
|
||||
# - none yet
|
||||
|
||||
# options specific to actions
|
||||
if action == "info":
|
||||
parser.set_usage("usage: %prog info [options] role_name[,version]")
|
||||
elif action == "init":
|
||||
parser.set_usage("usage: %prog init [options] role_name")
|
||||
parser.add_option(
|
||||
'-p', '--init-path', dest='init_path', default="./",
|
||||
help='The path in which the skeleton role will be created. '
|
||||
'The default is the current working directory.')
|
||||
parser.add_option(
|
||||
'--offline', dest='offline', default=False, action='store_true',
|
||||
help="Don't query the galaxy API when creating roles")
|
||||
elif action == "install":
|
||||
parser.set_usage("usage: %prog install [options] [-r FILE | role_name(s)[,version] | scm+role_repo_url[,version] | tar_file(s)]")
|
||||
parser.add_option(
|
||||
'-i', '--ignore-errors', dest='ignore_errors', action='store_true', default=False,
|
||||
help='Ignore errors and continue with the next specified role.')
|
||||
parser.add_option(
|
||||
'-n', '--no-deps', dest='no_deps', action='store_true', default=False,
|
||||
help='Don\'t download roles listed as dependencies')
|
||||
parser.add_option(
|
||||
'-r', '--role-file', dest='role_file',
|
||||
help='A file containing a list of roles to be imported')
|
||||
elif action == "remove":
|
||||
parser.set_usage("usage: %prog remove role1 role2 ...")
|
||||
elif action == "list":
|
||||
parser.set_usage("usage: %prog list [role_name]")
|
||||
|
||||
# options that apply to more than one action
|
||||
if action != "init":
|
||||
parser.add_option(
|
||||
'-p', '--roles-path', dest='roles_path', default=C.DEFAULT_ROLES_PATH,
|
||||
help='The path to the directory containing your roles. '
|
||||
'The default is the roles_path configured in your '
|
||||
'ansible.cfg file (/etc/ansible/roles if not configured)')
|
||||
|
||||
if action in ("info","init","install"):
|
||||
parser.add_option(
|
||||
'-s', '--server', dest='api_server', default="galaxy.ansible.com",
|
||||
help='The API server destination')
|
||||
|
||||
if action in ("init","install"):
|
||||
parser.add_option(
|
||||
'-f', '--force', dest='force', action='store_true', default=False,
|
||||
help='Force overwriting an existing role')
|
||||
# done, return the parser
|
||||
return parser
|
||||
|
||||
def get_opt(options, k, defval=""):
|
||||
"""
|
||||
Returns an option from an Optparse values instance.
|
||||
"""
|
||||
try:
|
||||
data = getattr(options, k)
|
||||
except:
|
||||
return defval
|
||||
if k == "roles_path":
|
||||
if os.pathsep in data:
|
||||
data = data.split(os.pathsep)[0]
|
||||
return data
|
||||
|
||||
def exit_without_ignore(options, rc=1):
|
||||
"""
|
||||
Exits with the specified return code unless the
|
||||
option --ignore-errors was specified
|
||||
"""
|
||||
|
||||
if not get_opt(options, "ignore_errors", False):
|
||||
print '- you can use --ignore-errors to skip failed roles.'
|
||||
sys.exit(rc)
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
# Galaxy API functions
|
||||
#-------------------------------------------------------------------------------------
|
||||
|
||||
def api_get_config(api_server):
|
||||
"""
|
||||
Fetches the Galaxy API current version to ensure
|
||||
the API server is up and reachable.
|
||||
"""
|
||||
|
||||
try:
|
||||
url = 'https://%s/api/' % api_server
|
||||
data = json.load(urllib2.urlopen(url))
|
||||
if not data.get("current_version",None):
|
||||
return None
|
||||
else:
|
||||
return data
|
||||
except:
|
||||
return None
|
||||
|
||||
def api_lookup_role_by_name(api_server, role_name, notify=True):
|
||||
"""
|
||||
Uses the Galaxy API to do a lookup on the role owner/name.
|
||||
"""
|
||||
|
||||
role_name = urllib.quote(role_name)
|
||||
|
||||
try:
|
||||
parts = role_name.split(".")
|
||||
user_name = ".".join(parts[0:-1])
|
||||
role_name = parts[-1]
|
||||
if notify:
|
||||
print "- downloading role '%s', owned by %s" % (role_name, user_name)
|
||||
except:
|
||||
parser.print_help()
|
||||
print "- invalid role name (%s). Specify role as format: username.rolename" % role_name
|
||||
sys.exit(1)
|
||||
|
||||
url = 'https://%s/api/v1/roles/?owner__username=%s&name=%s' % (api_server,user_name,role_name)
|
||||
try:
|
||||
data = json.load(urllib2.urlopen(url))
|
||||
if len(data["results"]) == 0:
|
||||
return None
|
||||
else:
|
||||
return data["results"][0]
|
||||
except:
|
||||
return None
|
||||
|
||||
def api_fetch_role_related(api_server, related, role_id):
|
||||
"""
|
||||
Uses the Galaxy API to fetch the list of related items for
|
||||
the given role. The url comes from the 'related' field of
|
||||
the role.
|
||||
"""
|
||||
|
||||
try:
|
||||
url = 'https://%s/api/v1/roles/%d/%s/?page_size=50' % (api_server, int(role_id), related)
|
||||
data = json.load(urllib2.urlopen(url))
|
||||
results = data['results']
|
||||
done = (data.get('next', None) == None)
|
||||
while not done:
|
||||
url = 'https://%s%s' % (api_server, data['next'])
|
||||
print url
|
||||
data = json.load(urllib2.urlopen(url))
|
||||
results += data['results']
|
||||
done = (data.get('next', None) == None)
|
||||
return results
|
||||
except:
|
||||
return None
|
||||
|
||||
def api_get_list(api_server, what):
|
||||
"""
|
||||
Uses the Galaxy API to fetch the list of items specified.
|
||||
"""
|
||||
|
||||
try:
|
||||
url = 'https://%s/api/v1/%s/?page_size' % (api_server, what)
|
||||
data = json.load(urllib2.urlopen(url))
|
||||
if "results" in data:
|
||||
results = data['results']
|
||||
else:
|
||||
results = data
|
||||
done = True
|
||||
if "next" in data:
|
||||
done = (data.get('next', None) == None)
|
||||
while not done:
|
||||
url = 'https://%s%s' % (api_server, data['next'])
|
||||
print url
|
||||
data = json.load(urllib2.urlopen(url))
|
||||
results += data['results']
|
||||
done = (data.get('next', None) == None)
|
||||
return results
|
||||
except:
|
||||
print "- failed to download the %s list" % what
|
||||
return None
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
# scm repo utility functions
|
||||
#-------------------------------------------------------------------------------------
|
||||
|
||||
def scm_archive_role(scm, role_url, role_version, role_name):
|
||||
if scm not in ['hg', 'git']:
|
||||
print "- scm %s is not currently supported" % scm
|
||||
return False
|
||||
tempdir = tempfile.mkdtemp()
|
||||
clone_cmd = [scm, 'clone', role_url, role_name]
|
||||
with open('/dev/null', 'w') as devnull:
|
||||
try:
|
||||
print "- executing: %s" % " ".join(clone_cmd)
|
||||
popen = subprocess.Popen(clone_cmd, cwd=tempdir, stdout=devnull, stderr=devnull)
|
||||
except:
|
||||
raise AnsibleError("error executing: %s" % " ".join(clone_cmd))
|
||||
rc = popen.wait()
|
||||
if rc != 0:
|
||||
print "- command %s failed" % ' '.join(clone_cmd)
|
||||
print " in directory %s" % tempdir
|
||||
return False
|
||||
|
||||
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.tar')
|
||||
if scm == 'hg':
|
||||
archive_cmd = ['hg', 'archive', '--prefix', "%s/" % role_name]
|
||||
if role_version:
|
||||
archive_cmd.extend(['-r', role_version])
|
||||
archive_cmd.append(temp_file.name)
|
||||
if scm == 'git':
|
||||
archive_cmd = ['git', 'archive', '--prefix=%s/' % role_name, '--output=%s' % temp_file.name]
|
||||
if role_version:
|
||||
archive_cmd.append(role_version)
|
||||
else:
|
||||
archive_cmd.append('HEAD')
|
||||
|
||||
with open('/dev/null', 'w') as devnull:
|
||||
print "- executing: %s" % " ".join(archive_cmd)
|
||||
popen = subprocess.Popen(archive_cmd, cwd=os.path.join(tempdir, role_name),
|
||||
stderr=devnull, stdout=devnull)
|
||||
rc = popen.wait()
|
||||
if rc != 0:
|
||||
print "- command %s failed" % ' '.join(archive_cmd)
|
||||
print " in directory %s" % tempdir
|
||||
return False
|
||||
|
||||
shutil.rmtree(tempdir)
|
||||
|
||||
return temp_file.name
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
# Role utility functions
|
||||
#-------------------------------------------------------------------------------------
|
||||
|
||||
def get_role_path(role_name, options):
|
||||
"""
|
||||
Returns the role path based on the roles_path option
|
||||
and the role name.
|
||||
"""
|
||||
roles_path = get_opt(options,'roles_path')
|
||||
roles_path = os.path.join(roles_path, role_name)
|
||||
roles_path = os.path.expanduser(roles_path)
|
||||
return roles_path
|
||||
|
||||
def get_role_metadata(role_name, options):
|
||||
"""
|
||||
Returns the metadata as YAML, if the file 'meta/main.yml'
|
||||
exists in the specified role_path
|
||||
"""
|
||||
role_path = os.path.join(get_role_path(role_name, options), 'meta/main.yml')
|
||||
try:
|
||||
if os.path.isfile(role_path):
|
||||
f = open(role_path, 'r')
|
||||
meta_data = yaml.safe_load(f)
|
||||
f.close()
|
||||
return meta_data
|
||||
else:
|
||||
return None
|
||||
except:
|
||||
return None
|
||||
|
||||
def get_galaxy_install_info(role_name, options):
|
||||
"""
|
||||
Returns the YAML data contained in 'meta/.galaxy_install_info',
|
||||
if it exists.
|
||||
"""
|
||||
|
||||
try:
|
||||
info_path = os.path.join(get_role_path(role_name, options), 'meta/.galaxy_install_info')
|
||||
if os.path.isfile(info_path):
|
||||
f = open(info_path, 'r')
|
||||
info_data = yaml.safe_load(f)
|
||||
f.close()
|
||||
return info_data
|
||||
else:
|
||||
return None
|
||||
except:
|
||||
return None
|
||||
|
||||
def write_galaxy_install_info(role_name, role_version, options):
|
||||
"""
|
||||
Writes a YAML-formatted file to the role's meta/ directory
|
||||
(named .galaxy_install_info) which contains some information
|
||||
we can use later for commands like 'list' and 'info'.
|
||||
"""
|
||||
|
||||
info = dict(
|
||||
version = role_version,
|
||||
install_date = datetime.datetime.utcnow().strftime("%c"),
|
||||
)
|
||||
try:
|
||||
info_path = os.path.join(get_role_path(role_name, options), 'meta/.galaxy_install_info')
|
||||
f = open(info_path, 'w+')
|
||||
info_data = yaml.safe_dump(info, f)
|
||||
f.close()
|
||||
except:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def remove_role(role_name, options):
|
||||
"""
|
||||
Removes the specified role from the roles path. There is a
|
||||
sanity check to make sure there's a meta/main.yml file at this
|
||||
path so the user doesn't blow away random directories
|
||||
"""
|
||||
if get_role_metadata(role_name, options):
|
||||
role_path = get_role_path(role_name, options)
|
||||
shutil.rmtree(role_path)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def fetch_role(role_name, target, role_data, options):
|
||||
"""
|
||||
Downloads the archived role from github to a temp location, extracts
|
||||
it, and then copies the extracted role to the role library path.
|
||||
"""
|
||||
|
||||
# first grab the file and save it to a temp location
|
||||
if '://' in role_name:
|
||||
archive_url = role_name
|
||||
else:
|
||||
archive_url = 'https://github.com/%s/%s/archive/%s.tar.gz' % (role_data["github_user"], role_data["github_repo"], target)
|
||||
print "- downloading role from %s" % archive_url
|
||||
|
||||
try:
|
||||
url_file = urllib2.urlopen(archive_url)
|
||||
temp_file = tempfile.NamedTemporaryFile(delete=False)
|
||||
data = url_file.read()
|
||||
while data:
|
||||
temp_file.write(data)
|
||||
data = url_file.read()
|
||||
temp_file.close()
|
||||
return temp_file.name
|
||||
except Exception, e:
|
||||
# TODO: better urllib2 error handling for error
|
||||
# messages that are more exact
|
||||
print "- error: failed to download the file."
|
||||
return False
|
||||
|
||||
def install_role(role_name, role_version, role_filename, options):
|
||||
# the file is a tar, so open it that way and extract it
|
||||
# to the specified (or default) roles directory
|
||||
|
||||
if not tarfile.is_tarfile(role_filename):
|
||||
print "- error: the file downloaded was not a tar.gz"
|
||||
return False
|
||||
else:
|
||||
if role_filename.endswith('.gz'):
|
||||
role_tar_file = tarfile.open(role_filename, "r:gz")
|
||||
else:
|
||||
role_tar_file = tarfile.open(role_filename, "r")
|
||||
# verify the role's meta file
|
||||
meta_file = None
|
||||
members = role_tar_file.getmembers()
|
||||
# next find the metadata file
|
||||
for member in members:
|
||||
if "/meta/main.yml" in member.name:
|
||||
meta_file = member
|
||||
break
|
||||
if not meta_file:
|
||||
print "- error: this role does not appear to have a meta/main.yml file."
|
||||
return False
|
||||
else:
|
||||
try:
|
||||
meta_file_data = yaml.safe_load(role_tar_file.extractfile(meta_file))
|
||||
except:
|
||||
print "- error: this role does not appear to have a valid meta/main.yml file."
|
||||
return False
|
||||
|
||||
# we strip off the top-level directory for all of the files contained within
|
||||
# the tar file here, since the default is 'github_repo-target', and change it
|
||||
# to the specified role's name
|
||||
role_path = os.path.join(get_opt(options, 'roles_path'), role_name)
|
||||
role_path = os.path.expanduser(role_path)
|
||||
print "- extracting %s to %s" % (role_name, role_path)
|
||||
try:
|
||||
if os.path.exists(role_path):
|
||||
if not os.path.isdir(role_path):
|
||||
print "- error: the specified roles path exists and is not a directory."
|
||||
return False
|
||||
elif not get_opt(options, "force", False):
|
||||
print "- error: the specified role %s appears to already exist. Use --force to replace it." % role_name
|
||||
return False
|
||||
else:
|
||||
# using --force, remove the old path
|
||||
if not remove_role(role_name, options):
|
||||
print "- error: %s doesn't appear to contain a role." % role_path
|
||||
print " please remove this directory manually if you really want to put the role here."
|
||||
return False
|
||||
else:
|
||||
os.makedirs(role_path)
|
||||
|
||||
# now we do the actual extraction to the role_path
|
||||
for member in members:
|
||||
# we only extract files, and remove any relative path
|
||||
# bits that might be in the file for security purposes
|
||||
# and drop the leading directory, as mentioned above
|
||||
if member.isreg():
|
||||
parts = member.name.split("/")[1:]
|
||||
final_parts = []
|
||||
for part in parts:
|
||||
if part != '..' and '~' not in part and '$' not in part:
|
||||
final_parts.append(part)
|
||||
member.name = os.path.join(*final_parts)
|
||||
role_tar_file.extract(member, role_path)
|
||||
|
||||
# write out the install info file for later use
|
||||
write_galaxy_install_info(role_name, role_version, options)
|
||||
except OSError, e:
|
||||
print "- error: you do not have permission to modify files in %s" % role_path
|
||||
return False
|
||||
|
||||
# return the parsed yaml metadata
|
||||
print "- %s was installed successfully" % role_name
|
||||
return meta_file_data
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
# Action functions
|
||||
#-------------------------------------------------------------------------------------
|
||||
|
||||
def execute_init(args, options, parser):
|
||||
"""
|
||||
Executes the init action, which creates the skeleton framework
|
||||
of a role that complies with the galaxy metadata format.
|
||||
"""
|
||||
|
||||
init_path = get_opt(options, 'init_path', './')
|
||||
api_server = get_opt(options, "api_server", "galaxy.ansible.com")
|
||||
force = get_opt(options, 'force', False)
|
||||
offline = get_opt(options, 'offline', False)
|
||||
|
||||
if not offline:
|
||||
api_config = api_get_config(api_server)
|
||||
if not api_config:
|
||||
print "- the API server (%s) is not responding, please try again later." % api_server
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
role_name = args.pop(0).strip()
|
||||
if role_name == "":
|
||||
raise Exception("")
|
||||
role_path = os.path.join(init_path, role_name)
|
||||
if os.path.exists(role_path):
|
||||
if os.path.isfile(role_path):
|
||||
print "- the path %s already exists, but is a file - aborting" % role_path
|
||||
sys.exit(1)
|
||||
elif not force:
|
||||
print "- the directory %s already exists." % role_path
|
||||
print " you can use --force to re-initialize this directory,\n" + \
|
||||
" however it will reset any main.yml files that may have\n" + \
|
||||
" been modified there already."
|
||||
sys.exit(1)
|
||||
except Exception, e:
|
||||
parser.print_help()
|
||||
print "- no role name specified for init"
|
||||
sys.exit(1)
|
||||
|
||||
ROLE_DIRS = ('defaults','files','handlers','meta','tasks','templates','vars')
|
||||
|
||||
# create the default README.md
|
||||
if not os.path.exists(role_path):
|
||||
os.makedirs(role_path)
|
||||
readme_path = os.path.join(role_path, "README.md")
|
||||
f = open(readme_path, "wb")
|
||||
f.write(default_readme_template)
|
||||
f.close
|
||||
|
||||
for dir in ROLE_DIRS:
|
||||
dir_path = os.path.join(init_path, role_name, dir)
|
||||
main_yml_path = os.path.join(dir_path, 'main.yml')
|
||||
# create the directory if it doesn't exist already
|
||||
if not os.path.exists(dir_path):
|
||||
os.makedirs(dir_path)
|
||||
|
||||
# now create the main.yml file for that directory
|
||||
if dir == "meta":
|
||||
# create a skeleton meta/main.yml with a valid galaxy_info
|
||||
# datastructure in place, plus with all of the available
|
||||
# tags/platforms included (but commented out) and the
|
||||
# dependencies section
|
||||
platforms = []
|
||||
if not offline:
|
||||
platforms = api_get_list(api_server, "platforms") or []
|
||||
categories = []
|
||||
if not offline:
|
||||
categories = api_get_list(api_server, "categories") or []
|
||||
|
||||
# group the list of platforms from the api based
|
||||
# on their names, with the release field being
|
||||
# appended to a list of versions
|
||||
platform_groups = defaultdict(list)
|
||||
for platform in platforms:
|
||||
platform_groups[platform['name']].append(platform['release'])
|
||||
platform_groups[platform['name']].sort()
|
||||
|
||||
inject = dict(
|
||||
author = 'your name',
|
||||
company = 'your company (optional)',
|
||||
license = 'license (GPLv2, CC-BY, etc)',
|
||||
min_ansible_version = '1.2',
|
||||
platforms = platform_groups,
|
||||
categories = categories,
|
||||
)
|
||||
rendered_meta = Environment().from_string(default_meta_template).render(inject)
|
||||
f = open(main_yml_path, 'w')
|
||||
f.write(rendered_meta)
|
||||
f.close()
|
||||
pass
|
||||
elif dir not in ('files','templates'):
|
||||
# just write a (mostly) empty YAML file for main.yml
|
||||
f = open(main_yml_path, 'w')
|
||||
f.write('---\n# %s file for %s\n' % (dir,role_name))
|
||||
f.close()
|
||||
print "- %s was created successfully" % role_name
|
||||
|
||||
def execute_info(args, options, parser):
|
||||
"""
|
||||
Executes the info action. This action prints out detailed
|
||||
information about an installed role as well as info available
|
||||
from the galaxy API.
|
||||
"""
|
||||
|
||||
if len(args) == 0:
|
||||
# the user needs to specify a role
|
||||
parser.print_help()
|
||||
print "- you must specify a user/role name"
|
||||
sys.exit(1)
|
||||
|
||||
api_server = get_opt(options, "api_server", "galaxy.ansible.com")
|
||||
api_config = api_get_config(api_server)
|
||||
roles_path = get_opt(options, "roles_path")
|
||||
|
||||
for role in args:
|
||||
|
||||
role_info = {}
|
||||
|
||||
install_info = get_galaxy_install_info(role, options)
|
||||
if install_info:
|
||||
if 'version' in install_info:
|
||||
install_info['intalled_version'] = install_info['version']
|
||||
del install_info['version']
|
||||
role_info.update(install_info)
|
||||
|
||||
remote_data = api_lookup_role_by_name(api_server, role, False)
|
||||
if remote_data:
|
||||
role_info.update(remote_data)
|
||||
|
||||
metadata = get_role_metadata(role, options)
|
||||
if metadata:
|
||||
role_info.update(metadata)
|
||||
|
||||
role_spec = ansible.utils.role_spec_parse(role)
|
||||
if role_spec:
|
||||
role_info.update(role_spec)
|
||||
|
||||
if role_info:
|
||||
print "- %s:" % (role)
|
||||
for k in sorted(role_info.keys()):
|
||||
|
||||
if k in SKIP_INFO_KEYS:
|
||||
continue
|
||||
|
||||
if isinstance(role_info[k], dict):
|
||||
print "\t%s: " % (k)
|
||||
for key in sorted(role_info[k].keys()):
|
||||
if key in SKIP_INFO_KEYS:
|
||||
continue
|
||||
print "\t\t%s: %s" % (key, role_info[k][key])
|
||||
else:
|
||||
print "\t%s: %s" % (k, role_info[k])
|
||||
else:
|
||||
print "- the role %s was not found" % role
|
||||
|
||||
def execute_install(args, options, parser):
|
||||
"""
|
||||
Executes the installation action. The args list contains the
|
||||
roles to be installed, unless -f was specified. The list of roles
|
||||
can be a name (which will be downloaded via the galaxy API and github),
|
||||
or it can be a local .tar.gz file.
|
||||
"""
|
||||
|
||||
role_file = get_opt(options, "role_file", None)
|
||||
|
||||
if len(args) == 0 and role_file is None:
|
||||
# the user needs to specify one of either --role-file
|
||||
# or specify a single user/role name
|
||||
parser.print_help()
|
||||
print "- you must specify a user/role name or a roles file"
|
||||
sys.exit()
|
||||
elif len(args) == 1 and not role_file is None:
|
||||
# using a role file is mutually exclusive of specifying
|
||||
# the role name on the command line
|
||||
parser.print_help()
|
||||
print "- please specify a user/role name, or a roles file, but not both"
|
||||
sys.exit(1)
|
||||
|
||||
api_server = get_opt(options, "api_server", "galaxy.ansible.com")
|
||||
no_deps = get_opt(options, "no_deps", False)
|
||||
roles_path = get_opt(options, "roles_path")
|
||||
|
||||
roles_done = []
|
||||
if role_file:
|
||||
f = open(role_file, 'r')
|
||||
if role_file.endswith('.yaml') or role_file.endswith('.yml'):
|
||||
roles_left = map(ansible.utils.role_yaml_parse, yaml.safe_load(f))
|
||||
else:
|
||||
# roles listed in a file, one per line
|
||||
roles_left = map(ansible.utils.role_spec_parse, f.readlines())
|
||||
f.close()
|
||||
else:
|
||||
# roles were specified directly, so we'll just go out grab them
|
||||
# (and their dependencies, unless the user doesn't want us to).
|
||||
roles_left = map(ansible.utils.role_spec_parse, args)
|
||||
|
||||
while len(roles_left) > 0:
|
||||
# query the galaxy API for the role data
|
||||
role_data = None
|
||||
role = roles_left.pop(0)
|
||||
role_src = role.get("src")
|
||||
role_scm = role.get("scm")
|
||||
role_path = role.get("path")
|
||||
|
||||
if role_path:
|
||||
options.roles_path = role_path
|
||||
else:
|
||||
options.roles_path = roles_path
|
||||
|
||||
if os.path.isfile(role_src):
|
||||
# installing a local tar.gz
|
||||
tmp_file = role_src
|
||||
else:
|
||||
if role_scm:
|
||||
# create tar file from scm url
|
||||
tmp_file = scm_archive_role(role_scm, role_src, role.get("version"), role.get("name"))
|
||||
elif '://' in role_src:
|
||||
# just download a URL - version will probably be in the URL
|
||||
tmp_file = fetch_role(role_src, None, None, options)
|
||||
else:
|
||||
# installing from galaxy
|
||||
api_config = api_get_config(api_server)
|
||||
if not api_config:
|
||||
print "- the API server (%s) is not responding, please try again later." % api_server
|
||||
sys.exit(1)
|
||||
|
||||
role_data = api_lookup_role_by_name(api_server, role_src)
|
||||
if not role_data:
|
||||
print "- sorry, %s was not found on %s." % (role_src, api_server)
|
||||
exit_without_ignore(options)
|
||||
continue
|
||||
|
||||
role_versions = api_fetch_role_related(api_server, 'versions', role_data['id'])
|
||||
if "version" not in role or role['version'] == '':
|
||||
# convert the version names to LooseVersion objects
|
||||
# and sort them to get the latest version. If there
|
||||
# are no versions in the list, we'll grab the head
|
||||
# of the master branch
|
||||
if len(role_versions) > 0:
|
||||
loose_versions = [LooseVersion(a.get('name',None)) for a in role_versions]
|
||||
loose_versions.sort()
|
||||
role["version"] = str(loose_versions[-1])
|
||||
else:
|
||||
role["version"] = 'master'
|
||||
elif role['version'] != 'master':
|
||||
if role_versions and role["version"] not in [a.get('name', None) for a in role_versions]:
|
||||
print 'role is %s' % role
|
||||
print "- the specified version (%s) was not found in the list of available versions (%s)." % (role['version'], role_versions)
|
||||
exit_without_ignore(options)
|
||||
continue
|
||||
|
||||
# download the role. if --no-deps was specified, we stop here,
|
||||
# otherwise we recursively grab roles and all of their deps.
|
||||
tmp_file = fetch_role(role_src, role["version"], role_data, options)
|
||||
installed = False
|
||||
if tmp_file:
|
||||
installed = install_role(role.get("name"), role.get("version"), tmp_file, options)
|
||||
# we're done with the temp file, clean it up
|
||||
if tmp_file != role_src:
|
||||
os.unlink(tmp_file)
|
||||
# install dependencies, if we want them
|
||||
if not no_deps and installed:
|
||||
if not role_data:
|
||||
role_data = get_role_metadata(role.get("name"), options)
|
||||
role_dependencies = role_data['dependencies']
|
||||
else:
|
||||
role_dependencies = role_data['summary_fields']['dependencies'] # api_fetch_role_related(api_server, 'dependencies', role_data['id'])
|
||||
for dep in role_dependencies:
|
||||
if isinstance(dep, basestring):
|
||||
dep = ansible.utils.role_spec_parse(dep)
|
||||
else:
|
||||
dep = ansible.utils.role_yaml_parse(dep)
|
||||
if not get_role_metadata(dep["name"], options):
|
||||
if dep not in roles_left:
|
||||
print '- adding dependency: %s' % dep["name"]
|
||||
roles_left.append(dep)
|
||||
else:
|
||||
print '- dependency %s already pending installation.' % dep["name"]
|
||||
else:
|
||||
print '- dependency %s is already installed, skipping.' % dep["name"]
|
||||
if not tmp_file or not installed:
|
||||
print "- %s was NOT installed successfully." % role.get("name")
|
||||
exit_without_ignore(options)
|
||||
sys.exit(0)
|
||||
|
||||
def execute_remove(args, options, parser):
|
||||
"""
|
||||
Executes the remove action. The args list contains the list
|
||||
of roles to be removed. This list can contain more than one role.
|
||||
"""
|
||||
|
||||
if len(args) == 0:
|
||||
parser.print_help()
|
||||
print '- you must specify at least one role to remove.'
|
||||
sys.exit()
|
||||
|
||||
for role in args:
|
||||
if get_role_metadata(role, options):
|
||||
if remove_role(role, options):
|
||||
print '- successfully removed %s' % role
|
||||
else:
|
||||
print "- failed to remove role: %s" % role
|
||||
else:
|
||||
print '- %s is not installed, skipping.' % role
|
||||
sys.exit(0)
|
||||
|
||||
def execute_list(args, options, parser):
|
||||
"""
|
||||
Executes the list action. The args list can contain zero
|
||||
or one role. If one is specified, only that role will be
|
||||
shown, otherwise all roles in the specified directory will
|
||||
be shown.
|
||||
"""
|
||||
|
||||
if len(args) > 1:
|
||||
print "- please specify only one role to list, or specify no roles to see a full list"
|
||||
sys.exit(1)
|
||||
|
||||
if len(args) == 1:
|
||||
# show only the request role, if it exists
|
||||
role_name = args[0]
|
||||
metadata = get_role_metadata(role_name, options)
|
||||
if metadata:
|
||||
install_info = get_galaxy_install_info(role_name, options)
|
||||
version = None
|
||||
if install_info:
|
||||
version = install_info.get("version", None)
|
||||
if not version:
|
||||
version = "(unknown version)"
|
||||
# show some more info about single roles here
|
||||
print "- %s, %s" % (role_name, version)
|
||||
else:
|
||||
print "- the role %s was not found" % role_name
|
||||
else:
|
||||
# show all valid roles in the roles_path directory
|
||||
roles_path = get_opt(options, 'roles_path')
|
||||
roles_path = os.path.expanduser(roles_path)
|
||||
if not os.path.exists(roles_path):
|
||||
parser.print_help()
|
||||
print "- the path %s does not exist. Please specify a valid path with --roles-path" % roles_path
|
||||
sys.exit(1)
|
||||
elif not os.path.isdir(roles_path):
|
||||
print "- %s exists, but it is not a directory. Please specify a valid path with --roles-path" % roles_path
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
path_files = os.listdir(roles_path)
|
||||
for path_file in path_files:
|
||||
if get_role_metadata(path_file, options):
|
||||
install_info = get_galaxy_install_info(path_file, options)
|
||||
version = None
|
||||
if install_info:
|
||||
version = install_info.get("version", None)
|
||||
if not version:
|
||||
version = "(unknown version)"
|
||||
print "- %s, %s" % (path_file, version)
|
||||
sys.exit(0)
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
# The main entry point
|
||||
#-------------------------------------------------------------------------------------
|
||||
|
||||
def main():
|
||||
# parse the CLI options
|
||||
action = get_action(sys.argv)
|
||||
parser = build_option_parser(action)
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
# execute the desired action
|
||||
if 1: #try:
|
||||
fn = globals()["execute_%s" % action]
|
||||
fn(args, options, parser)
|
||||
#except KeyError, e:
|
||||
# print "- error: %s is not a valid action. Valid actions are: %s" % (action, ", ".join(VALID_ACTIONS))
|
||||
# sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -1 +0,0 @@
|
||||
../lib/ansible/cli/inventory.py
|
||||
@ -1 +0,0 @@
|
||||
../lib/ansible/cli/playbook.py
|
||||
330
bin/ansible-playbook
Executable file
330
bin/ansible-playbook
Executable file
@ -0,0 +1,330 @@
|
||||
#!/usr/bin/env python
|
||||
# (C) 2012, Michael DeHaan, <michael.dehaan@gmail.com>
|
||||
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#######################################################
|
||||
|
||||
#__requires__ = ['ansible']
|
||||
#import pkg_resources
|
||||
|
||||
import sys
|
||||
import os
|
||||
import stat
|
||||
|
||||
# Augment PYTHONPATH to find Python modules relative to this file path
|
||||
# This is so that we can find the modules when running from a local checkout
|
||||
# installed as editable with `pip install -e ...` or `python setup.py develop`
|
||||
local_module_path = os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), '..', 'lib')
|
||||
)
|
||||
sys.path.append(local_module_path)
|
||||
|
||||
import ansible.playbook
|
||||
import ansible.constants as C
|
||||
import ansible.utils.template
|
||||
from ansible import errors
|
||||
from ansible import callbacks
|
||||
from ansible import utils
|
||||
from ansible.color import ANSIBLE_COLOR, stringc
|
||||
from ansible.callbacks import display
|
||||
|
||||
def colorize(lead, num, color):
|
||||
""" Print 'lead' = 'num' in 'color' """
|
||||
if num != 0 and ANSIBLE_COLOR and color is not None:
|
||||
return "%s%s%-15s" % (stringc(lead, color), stringc("=", color), stringc(str(num), color))
|
||||
else:
|
||||
return "%s=%-4s" % (lead, str(num))
|
||||
|
||||
def hostcolor(host, stats, color=True):
|
||||
if ANSIBLE_COLOR and color:
|
||||
if stats['failures'] != 0 or stats['unreachable'] != 0:
|
||||
return "%-37s" % stringc(host, 'red')
|
||||
elif stats['changed'] != 0:
|
||||
return "%-37s" % stringc(host, 'yellow')
|
||||
else:
|
||||
return "%-37s" % stringc(host, 'green')
|
||||
return "%-26s" % host
|
||||
|
||||
|
||||
def main(args):
|
||||
''' run ansible-playbook operations '''
|
||||
|
||||
# create parser for CLI options
|
||||
parser = utils.base_parser(
|
||||
constants=C,
|
||||
usage = "%prog playbook.yml",
|
||||
connect_opts=True,
|
||||
runas_opts=True,
|
||||
subset_opts=True,
|
||||
check_opts=True,
|
||||
diff_opts=True
|
||||
)
|
||||
#parser.add_option('--vault-password', dest="vault_password",
|
||||
# help="password for vault encrypted files")
|
||||
parser.add_option('-e', '--extra-vars', dest="extra_vars", action="append",
|
||||
help="set additional variables as key=value or YAML/JSON", default=[])
|
||||
parser.add_option('-t', '--tags', dest='tags', default='all',
|
||||
help="only run plays and tasks tagged with these values")
|
||||
parser.add_option('--skip-tags', dest='skip_tags',
|
||||
help="only run plays and tasks whose tags do not match these values")
|
||||
parser.add_option('--syntax-check', dest='syntax', action='store_true',
|
||||
help="perform a syntax check on the playbook, but do not execute it")
|
||||
parser.add_option('--list-tasks', dest='listtasks', action='store_true',
|
||||
help="list all tasks that would be executed")
|
||||
parser.add_option('--step', dest='step', action='store_true',
|
||||
help="one-step-at-a-time: confirm each task before running")
|
||||
parser.add_option('--start-at-task', dest='start_at',
|
||||
help="start the playbook at the task matching this name")
|
||||
parser.add_option('--force-handlers', dest='force_handlers', action='store_true',
|
||||
help="run handlers even if a task fails")
|
||||
parser.add_option('--flush-cache', dest='flush_cache', action='store_true',
|
||||
help="clear the fact cache")
|
||||
|
||||
options, args = parser.parse_args(args)
|
||||
|
||||
if len(args) == 0:
|
||||
parser.print_help(file=sys.stderr)
|
||||
return 1
|
||||
|
||||
# su and sudo command line arguments need to be mutually exclusive
|
||||
if (options.su or options.su_user or options.ask_su_pass) and \
|
||||
(options.sudo or options.sudo_user or options.ask_sudo_pass):
|
||||
parser.error("Sudo arguments ('--sudo', '--sudo-user', and '--ask-sudo-pass') "
|
||||
"and su arguments ('-su', '--su-user', and '--ask-su-pass') are "
|
||||
"mutually exclusive")
|
||||
|
||||
if (options.ask_vault_pass and options.vault_password_file):
|
||||
parser.error("--ask-vault-pass and --vault-password-file are mutually exclusive")
|
||||
|
||||
sshpass = None
|
||||
sudopass = None
|
||||
su_pass = None
|
||||
vault_pass = None
|
||||
|
||||
options.ask_vault_pass = options.ask_vault_pass or C.DEFAULT_ASK_VAULT_PASS
|
||||
|
||||
if options.listhosts or options.syntax or options.listtasks:
|
||||
(_, _, _, vault_pass) = utils.ask_passwords(ask_vault_pass=options.ask_vault_pass)
|
||||
else:
|
||||
options.ask_pass = options.ask_pass or C.DEFAULT_ASK_PASS
|
||||
# Never ask for an SSH password when we run with local connection
|
||||
if options.connection == "local":
|
||||
options.ask_pass = False
|
||||
options.ask_sudo_pass = options.ask_sudo_pass or C.DEFAULT_ASK_SUDO_PASS
|
||||
options.ask_su_pass = options.ask_su_pass or C.DEFAULT_ASK_SU_PASS
|
||||
(sshpass, sudopass, su_pass, vault_pass) = utils.ask_passwords(ask_pass=options.ask_pass, ask_sudo_pass=options.ask_sudo_pass, ask_su_pass=options.ask_su_pass, ask_vault_pass=options.ask_vault_pass)
|
||||
options.sudo_user = options.sudo_user or C.DEFAULT_SUDO_USER
|
||||
options.su_user = options.su_user or C.DEFAULT_SU_USER
|
||||
|
||||
# read vault_pass from a file
|
||||
if not options.ask_vault_pass and options.vault_password_file:
|
||||
vault_pass = utils.read_vault_file(options.vault_password_file)
|
||||
|
||||
extra_vars = {}
|
||||
for extra_vars_opt in options.extra_vars:
|
||||
if extra_vars_opt.startswith("@"):
|
||||
# Argument is a YAML file (JSON is a subset of YAML)
|
||||
extra_vars = utils.combine_vars(extra_vars, utils.parse_yaml_from_file(extra_vars_opt[1:], vault_password=vault_pass))
|
||||
elif extra_vars_opt and extra_vars_opt[0] in '[{':
|
||||
# Arguments as YAML
|
||||
extra_vars = utils.combine_vars(extra_vars, utils.parse_yaml(extra_vars_opt))
|
||||
else:
|
||||
# Arguments as Key-value
|
||||
extra_vars = utils.combine_vars(extra_vars, utils.parse_kv(extra_vars_opt))
|
||||
|
||||
only_tags = options.tags.split(",")
|
||||
skip_tags = options.skip_tags
|
||||
if options.skip_tags is not None:
|
||||
skip_tags = options.skip_tags.split(",")
|
||||
|
||||
for playbook in args:
|
||||
if not os.path.exists(playbook):
|
||||
raise errors.AnsibleError("the playbook: %s could not be found" % playbook)
|
||||
if not (os.path.isfile(playbook) or stat.S_ISFIFO(os.stat(playbook).st_mode)):
|
||||
raise errors.AnsibleError("the playbook: %s does not appear to be a file" % playbook)
|
||||
|
||||
inventory = ansible.inventory.Inventory(options.inventory, vault_password=vault_pass)
|
||||
inventory.subset(options.subset)
|
||||
if len(inventory.list_hosts()) == 0:
|
||||
raise errors.AnsibleError("provided hosts list is empty")
|
||||
|
||||
# run all playbooks specified on the command line
|
||||
for playbook in args:
|
||||
|
||||
stats = callbacks.AggregateStats()
|
||||
playbook_cb = callbacks.PlaybookCallbacks(verbose=utils.VERBOSITY)
|
||||
if options.step:
|
||||
playbook_cb.step = options.step
|
||||
if options.start_at:
|
||||
playbook_cb.start_at = options.start_at
|
||||
runner_cb = callbacks.PlaybookRunnerCallbacks(stats, verbose=utils.VERBOSITY)
|
||||
|
||||
pb = ansible.playbook.PlayBook(
|
||||
playbook=playbook,
|
||||
module_path=options.module_path,
|
||||
inventory=inventory,
|
||||
forks=options.forks,
|
||||
remote_user=options.remote_user,
|
||||
remote_pass=sshpass,
|
||||
callbacks=playbook_cb,
|
||||
runner_callbacks=runner_cb,
|
||||
stats=stats,
|
||||
timeout=options.timeout,
|
||||
transport=options.connection,
|
||||
sudo=options.sudo,
|
||||
sudo_user=options.sudo_user,
|
||||
sudo_pass=sudopass,
|
||||
extra_vars=extra_vars,
|
||||
private_key_file=options.private_key_file,
|
||||
only_tags=only_tags,
|
||||
skip_tags=skip_tags,
|
||||
check=options.check,
|
||||
diff=options.diff,
|
||||
su=options.su,
|
||||
su_pass=su_pass,
|
||||
su_user=options.su_user,
|
||||
vault_password=vault_pass,
|
||||
force_handlers=options.force_handlers
|
||||
)
|
||||
|
||||
if options.flush_cache:
|
||||
display(callbacks.banner("FLUSHING FACT CACHE"))
|
||||
pb.SETUP_CACHE.flush()
|
||||
|
||||
if options.listhosts or options.listtasks or options.syntax:
|
||||
print ''
|
||||
print 'playbook: %s' % playbook
|
||||
print ''
|
||||
playnum = 0
|
||||
for (play_ds, play_basedir) in zip(pb.playbook, pb.play_basedirs):
|
||||
playnum += 1
|
||||
play = ansible.playbook.Play(pb, play_ds, play_basedir,
|
||||
vault_password=pb.vault_password)
|
||||
label = play.name
|
||||
hosts = pb.inventory.list_hosts(play.hosts)
|
||||
|
||||
# Filter all tasks by given tags
|
||||
if pb.only_tags != 'all':
|
||||
if options.subset and not hosts:
|
||||
continue
|
||||
matched_tags, unmatched_tags = play.compare_tags(pb.only_tags)
|
||||
|
||||
# Remove skipped tasks
|
||||
matched_tags = matched_tags - set(pb.skip_tags)
|
||||
|
||||
unmatched_tags.discard('all')
|
||||
unknown_tags = ((set(pb.only_tags) | set(pb.skip_tags)) -
|
||||
(matched_tags | unmatched_tags))
|
||||
|
||||
if unknown_tags:
|
||||
continue
|
||||
|
||||
if options.listhosts:
|
||||
print ' play #%d (%s): host count=%d' % (playnum, label, len(hosts))
|
||||
for host in hosts:
|
||||
print ' %s' % host
|
||||
|
||||
if options.listtasks:
|
||||
print ' play #%d (%s):' % (playnum, label)
|
||||
|
||||
for task in play.tasks():
|
||||
if (set(task.tags).intersection(pb.only_tags) and not
|
||||
set(task.tags).intersection(pb.skip_tags)):
|
||||
if getattr(task, 'name', None) is not None:
|
||||
# meta tasks have no names
|
||||
print ' %s' % task.name
|
||||
if options.listhosts or options.listtasks:
|
||||
print ''
|
||||
continue
|
||||
|
||||
if options.syntax:
|
||||
# if we've not exited by now then we are fine.
|
||||
print 'Playbook Syntax is fine'
|
||||
return 0
|
||||
|
||||
failed_hosts = []
|
||||
unreachable_hosts = []
|
||||
|
||||
try:
|
||||
|
||||
pb.run()
|
||||
|
||||
hosts = sorted(pb.stats.processed.keys())
|
||||
display(callbacks.banner("PLAY RECAP"))
|
||||
playbook_cb.on_stats(pb.stats)
|
||||
|
||||
for h in hosts:
|
||||
t = pb.stats.summarize(h)
|
||||
if t['failures'] > 0:
|
||||
failed_hosts.append(h)
|
||||
if t['unreachable'] > 0:
|
||||
unreachable_hosts.append(h)
|
||||
|
||||
retries = failed_hosts + unreachable_hosts
|
||||
|
||||
if len(retries) > 0:
|
||||
filename = pb.generate_retry_inventory(retries)
|
||||
if filename:
|
||||
display(" to retry, use: --limit @%s\n" % filename)
|
||||
|
||||
for h in hosts:
|
||||
t = pb.stats.summarize(h)
|
||||
|
||||
display("%s : %s %s %s %s" % (
|
||||
hostcolor(h, t),
|
||||
colorize('ok', t['ok'], 'green'),
|
||||
colorize('changed', t['changed'], 'yellow'),
|
||||
colorize('unreachable', t['unreachable'], 'red'),
|
||||
colorize('failed', t['failures'], 'red')),
|
||||
screen_only=True
|
||||
)
|
||||
|
||||
display("%s : %s %s %s %s" % (
|
||||
hostcolor(h, t, False),
|
||||
colorize('ok', t['ok'], None),
|
||||
colorize('changed', t['changed'], None),
|
||||
colorize('unreachable', t['unreachable'], None),
|
||||
colorize('failed', t['failures'], None)),
|
||||
log_only=True
|
||||
)
|
||||
|
||||
|
||||
print ""
|
||||
if len(failed_hosts) > 0:
|
||||
return 2
|
||||
if len(unreachable_hosts) > 0:
|
||||
return 3
|
||||
|
||||
except errors.AnsibleError, e:
|
||||
display("ERROR: %s" % e, color='red')
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
display(" ", log_only=True)
|
||||
display(" ".join(sys.argv), log_only=True)
|
||||
display(" ", log_only=True)
|
||||
try:
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
except errors.AnsibleError, e:
|
||||
display("ERROR: %s" % e, color='red', stderr=True)
|
||||
sys.exit(1)
|
||||
except KeyboardInterrupt, ke:
|
||||
display("ERROR: interrupted", color='red', stderr=True)
|
||||
sys.exit(1)
|
||||
@ -1 +0,0 @@
|
||||
../lib/ansible/cli/pull.py
|
||||
240
bin/ansible-pull
Executable file
240
bin/ansible-pull
Executable file
@ -0,0 +1,240 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# (c) 2012, Stephen Fromm <sfromm@gmail.com>
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ansible-pull is a script that runs ansible in local mode
|
||||
# after checking out a playbooks directory from source repo. There is an
|
||||
# example playbook to bootstrap this script in the examples/ dir which
|
||||
# installs ansible and sets it up to run on cron.
|
||||
|
||||
# usage:
|
||||
# ansible-pull -d /var/lib/ansible \
|
||||
# -U http://example.net/content.git [-C production] \
|
||||
# [path/playbook.yml]
|
||||
#
|
||||
# the -d and -U arguments are required; the -C argument is optional.
|
||||
#
|
||||
# ansible-pull accepts an optional argument to specify a playbook
|
||||
# location underneath the workdir and then searches the source repo
|
||||
# for playbooks in the following order, stopping at the first match:
|
||||
#
|
||||
# 1. $workdir/path/playbook.yml, if specified
|
||||
# 2. $workdir/$fqdn.yml
|
||||
# 3. $workdir/$hostname.yml
|
||||
# 4. $workdir/local.yml
|
||||
#
|
||||
# the source repo must contain at least one of these playbooks.
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import datetime
|
||||
import socket
|
||||
import random
|
||||
import time
|
||||
from ansible import utils
|
||||
from ansible.utils import cmd_functions
|
||||
from ansible import errors
|
||||
from ansible import inventory
|
||||
|
||||
DEFAULT_REPO_TYPE = 'git'
|
||||
DEFAULT_PLAYBOOK = 'local.yml'
|
||||
PLAYBOOK_ERRORS = {1: 'File does not exist',
|
||||
2: 'File is not readable'}
|
||||
|
||||
VERBOSITY=0
|
||||
|
||||
def increment_debug(option, opt, value, parser):
|
||||
global VERBOSITY
|
||||
VERBOSITY += 1
|
||||
|
||||
def try_playbook(path):
|
||||
if not os.path.exists(path):
|
||||
return 1
|
||||
if not os.access(path, os.R_OK):
|
||||
return 2
|
||||
return 0
|
||||
|
||||
|
||||
def select_playbook(path, args):
|
||||
playbook = None
|
||||
if len(args) > 0 and args[0] is not None:
|
||||
playbook = "%s/%s" % (path, args[0])
|
||||
rc = try_playbook(playbook)
|
||||
if rc != 0:
|
||||
print >>sys.stderr, "%s: %s" % (playbook, PLAYBOOK_ERRORS[rc])
|
||||
return None
|
||||
return playbook
|
||||
else:
|
||||
fqdn = socket.getfqdn()
|
||||
hostpb = "%s/%s.yml" % (path, fqdn)
|
||||
shorthostpb = "%s/%s.yml" % (path, fqdn.split('.')[0])
|
||||
localpb = "%s/%s" % (path, DEFAULT_PLAYBOOK)
|
||||
errors = []
|
||||
for pb in [hostpb, shorthostpb, localpb]:
|
||||
rc = try_playbook(pb)
|
||||
if rc == 0:
|
||||
playbook = pb
|
||||
break
|
||||
else:
|
||||
errors.append("%s: %s" % (pb, PLAYBOOK_ERRORS[rc]))
|
||||
if playbook is None:
|
||||
print >>sys.stderr, "\n".join(errors)
|
||||
return playbook
|
||||
|
||||
|
||||
def main(args):
|
||||
""" Set up and run a local playbook """
|
||||
usage = "%prog [options] [playbook.yml]"
|
||||
parser = utils.SortedOptParser(usage=usage)
|
||||
parser.add_option('--purge', default=False, action='store_true',
|
||||
help='purge checkout after playbook run')
|
||||
parser.add_option('-o', '--only-if-changed', dest='ifchanged', default=False, action='store_true',
|
||||
help='only run the playbook if the repository has been updated')
|
||||
parser.add_option('-s', '--sleep', dest='sleep', default=None,
|
||||
help='sleep for random interval (between 0 and n number of seconds) before starting. this is a useful way to disperse git requests')
|
||||
parser.add_option('-f', '--force', dest='force', default=False,
|
||||
action='store_true',
|
||||
help='run the playbook even if the repository could '
|
||||
'not be updated')
|
||||
parser.add_option('-d', '--directory', dest='dest', default=None,
|
||||
help='directory to checkout repository to')
|
||||
#parser.add_option('-l', '--live', default=True, action='store_live',
|
||||
# help='Print the ansible-playbook output while running')
|
||||
parser.add_option('-U', '--url', dest='url', default=None,
|
||||
help='URL of the playbook repository')
|
||||
parser.add_option('-C', '--checkout', dest='checkout',
|
||||
help='branch/tag/commit to checkout. '
|
||||
'Defaults to behavior of repository module.')
|
||||
parser.add_option('-i', '--inventory-file', dest='inventory',
|
||||
help="location of the inventory host file")
|
||||
parser.add_option('-e', '--extra-vars', dest="extra_vars", action="append",
|
||||
help="set additional variables as key=value or YAML/JSON", default=[])
|
||||
parser.add_option('-v', '--verbose', default=False, action="callback",
|
||||
callback=increment_debug,
|
||||
help='Pass -vvvv to ansible-playbook')
|
||||
parser.add_option('-m', '--module-name', dest='module_name',
|
||||
default=DEFAULT_REPO_TYPE,
|
||||
help='Module name used to check out repository. '
|
||||
'Default is %s.' % DEFAULT_REPO_TYPE)
|
||||
parser.add_option('--vault-password-file', dest='vault_password_file',
|
||||
help="vault password file")
|
||||
parser.add_option('-K', '--ask-sudo-pass', default=False, dest='ask_sudo_pass', action='store_true',
|
||||
help='ask for sudo password')
|
||||
parser.add_option('-t', '--tags', dest='tags', default=False,
|
||||
help='only run plays and tasks tagged with these values')
|
||||
options, args = parser.parse_args(args)
|
||||
|
||||
hostname = socket.getfqdn()
|
||||
if not options.dest:
|
||||
# use a hostname dependent directory, in case of $HOME on nfs
|
||||
options.dest = utils.prepare_writeable_dir('~/.ansible/pull/%s' % hostname)
|
||||
|
||||
options.dest = os.path.abspath(options.dest)
|
||||
|
||||
if not options.url:
|
||||
parser.error("URL for repository not specified, use -h for help")
|
||||
return 1
|
||||
|
||||
now = datetime.datetime.now()
|
||||
print >>sys.stderr, now.strftime("Starting ansible-pull at %F %T")
|
||||
|
||||
# Attempt to use the inventory passed in as an argument
|
||||
# It might not yet have been downloaded so use localhost if note
|
||||
if not options.inventory or not os.path.exists(options.inventory):
|
||||
inv_opts = 'localhost,'
|
||||
else:
|
||||
inv_opts = options.inventory
|
||||
limit_opts = 'localhost:%s:127.0.0.1' % hostname
|
||||
repo_opts = "name=%s dest=%s" % (options.url, options.dest)
|
||||
|
||||
if VERBOSITY == 0:
|
||||
base_opts = '-c local --limit "%s"' % limit_opts
|
||||
elif VERBOSITY > 0:
|
||||
debug_level = ''.join([ "v" for x in range(0, VERBOSITY) ])
|
||||
base_opts = '-%s -c local --limit "%s"' % (debug_level, limit_opts)
|
||||
|
||||
if options.checkout:
|
||||
repo_opts += ' version=%s' % options.checkout
|
||||
path = utils.plugins.module_finder.find_plugin(options.module_name)
|
||||
if path is None:
|
||||
sys.stderr.write("module '%s' not found.\n" % options.module_name)
|
||||
return 1
|
||||
cmd = 'ansible localhost -i "%s" %s -m %s -a "%s"' % (
|
||||
inv_opts, base_opts, options.module_name, repo_opts
|
||||
)
|
||||
|
||||
if options.sleep:
|
||||
try:
|
||||
secs = random.randint(0,int(options.sleep));
|
||||
except ValueError:
|
||||
parser.error("%s is not a number." % options.sleep)
|
||||
return 1
|
||||
|
||||
print >>sys.stderr, "Sleeping for %d seconds..." % secs
|
||||
time.sleep(secs);
|
||||
|
||||
|
||||
# RUN THe CHECKOUT COMMAND
|
||||
rc, out, err = cmd_functions.run_cmd(cmd, live=True)
|
||||
|
||||
if rc != 0:
|
||||
if options.force:
|
||||
print "Unable to update repository. Continuing with (forced) run of playbook."
|
||||
else:
|
||||
return rc
|
||||
elif options.ifchanged and '"changed": true' not in out:
|
||||
print "Repository has not changed, quitting."
|
||||
return 0
|
||||
|
||||
playbook = select_playbook(options.dest, args)
|
||||
|
||||
if playbook is None:
|
||||
print >>sys.stderr, "Could not find a playbook to run."
|
||||
return 1
|
||||
|
||||
cmd = 'ansible-playbook %s %s' % (base_opts, playbook)
|
||||
if options.vault_password_file:
|
||||
cmd += " --vault-password-file=%s" % options.vault_password_file
|
||||
if options.inventory:
|
||||
cmd += ' -i "%s"' % options.inventory
|
||||
for ev in options.extra_vars:
|
||||
cmd += ' -e "%s"' % ev
|
||||
if options.ask_sudo_pass:
|
||||
cmd += ' -K'
|
||||
if options.tags:
|
||||
cmd += ' -t "%s"' % options.tags
|
||||
os.chdir(options.dest)
|
||||
|
||||
# RUN THE PLAYBOOK COMMAND
|
||||
rc, out, err = cmd_functions.run_cmd(cmd, live=True)
|
||||
|
||||
if options.purge:
|
||||
os.chdir('/')
|
||||
try:
|
||||
shutil.rmtree(options.dest)
|
||||
except Exception, e:
|
||||
print >>sys.stderr, "Failed to remove %s: %s" % (options.dest, str(e))
|
||||
|
||||
return rc
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
except KeyboardInterrupt, e:
|
||||
print >>sys.stderr, "Exit on user request.\n"
|
||||
sys.exit(1)
|
||||
@ -1 +0,0 @@
|
||||
../test/lib/ansible_test/_util/target/cli/ansible_test_cli_stub.py
|
||||
@ -1 +0,0 @@
|
||||
../lib/ansible/cli/vault.py
|
||||
233
bin/ansible-vault
Executable file
233
bin/ansible-vault
Executable file
@ -0,0 +1,233 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# (c) 2014, James Tanner <tanner.jc@gmail.com>
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ansible-vault is a script that encrypts/decrypts YAML files. See
|
||||
# http://docs.ansible.com/playbooks_vault.html for more details.
|
||||
|
||||
#__requires__ = ['ansible']
|
||||
#import pkg_resources
|
||||
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
import ansible.constants as C
|
||||
|
||||
from ansible import utils
|
||||
from ansible import errors
|
||||
from ansible.utils.vault import VaultEditor
|
||||
|
||||
from optparse import OptionParser
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
# Utility functions for parsing actions/options
|
||||
#-------------------------------------------------------------------------------------
|
||||
|
||||
VALID_ACTIONS = ("create", "decrypt", "edit", "encrypt", "rekey", "view")
|
||||
|
||||
def build_option_parser(action):
|
||||
"""
|
||||
Builds an option parser object based on the action
|
||||
the user wants to execute.
|
||||
"""
|
||||
|
||||
usage = "usage: %%prog [%s] [--help] [options] file_name" % "|".join(VALID_ACTIONS)
|
||||
epilog = "\nSee '%s <command> --help' for more information on a specific command.\n\n" % os.path.basename(sys.argv[0])
|
||||
OptionParser.format_epilog = lambda self, formatter: self.epilog
|
||||
parser = OptionParser(usage=usage, epilog=epilog)
|
||||
|
||||
if not action:
|
||||
parser.print_help()
|
||||
sys.exit()
|
||||
|
||||
# options for all actions
|
||||
#parser.add_option('-c', '--cipher', dest='cipher', default="AES256", help="cipher to use")
|
||||
parser.add_option('--debug', dest='debug', action="store_true", help="debug")
|
||||
parser.add_option('--vault-password-file', dest='password_file',
|
||||
help="vault password file", default=C.DEFAULT_VAULT_PASSWORD_FILE)
|
||||
|
||||
# options specific to actions
|
||||
if action == "create":
|
||||
parser.set_usage("usage: %prog create [options] file_name")
|
||||
elif action == "decrypt":
|
||||
parser.set_usage("usage: %prog decrypt [options] file_name")
|
||||
elif action == "edit":
|
||||
parser.set_usage("usage: %prog edit [options] file_name")
|
||||
elif action == "view":
|
||||
parser.set_usage("usage: %prog view [options] file_name")
|
||||
elif action == "encrypt":
|
||||
parser.set_usage("usage: %prog encrypt [options] file_name")
|
||||
elif action == "rekey":
|
||||
parser.set_usage("usage: %prog rekey [options] file_name")
|
||||
|
||||
# done, return the parser
|
||||
return parser
|
||||
|
||||
def get_action(args):
|
||||
"""
|
||||
Get the action the user wants to execute from the
|
||||
sys argv list.
|
||||
"""
|
||||
for i in range(0,len(args)):
|
||||
arg = args[i]
|
||||
if arg in VALID_ACTIONS:
|
||||
del args[i]
|
||||
return arg
|
||||
return None
|
||||
|
||||
def get_opt(options, k, defval=""):
|
||||
"""
|
||||
Returns an option from an Optparse values instance.
|
||||
"""
|
||||
try:
|
||||
data = getattr(options, k)
|
||||
except:
|
||||
return defval
|
||||
if k == "roles_path":
|
||||
if os.pathsep in data:
|
||||
data = data.split(os.pathsep)[0]
|
||||
return data
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
# Command functions
|
||||
#-------------------------------------------------------------------------------------
|
||||
|
||||
def execute_create(args, options, parser):
|
||||
if len(args) > 1:
|
||||
raise errors.AnsibleError("'create' does not accept more than one filename")
|
||||
|
||||
if not options.password_file:
|
||||
password, new_password = utils.ask_vault_passwords(ask_vault_pass=True, confirm_vault=True)
|
||||
else:
|
||||
password = utils.read_vault_file(options.password_file)
|
||||
|
||||
cipher = 'AES256'
|
||||
if hasattr(options, 'cipher'):
|
||||
cipher = options.cipher
|
||||
|
||||
this_editor = VaultEditor(cipher, password, args[0])
|
||||
this_editor.create_file()
|
||||
|
||||
def execute_decrypt(args, options, parser):
|
||||
|
||||
if not options.password_file:
|
||||
password, new_password = utils.ask_vault_passwords(ask_vault_pass=True)
|
||||
else:
|
||||
password = utils.read_vault_file(options.password_file)
|
||||
|
||||
cipher = 'AES256'
|
||||
if hasattr(options, 'cipher'):
|
||||
cipher = options.cipher
|
||||
|
||||
for f in args:
|
||||
this_editor = VaultEditor(cipher, password, f)
|
||||
this_editor.decrypt_file()
|
||||
|
||||
print "Decryption successful"
|
||||
|
||||
def execute_edit(args, options, parser):
|
||||
|
||||
if len(args) > 1:
|
||||
raise errors.AnsibleError("edit does not accept more than one filename")
|
||||
|
||||
if not options.password_file:
|
||||
password, new_password = utils.ask_vault_passwords(ask_vault_pass=True)
|
||||
else:
|
||||
password = utils.read_vault_file(options.password_file)
|
||||
|
||||
cipher = None
|
||||
|
||||
for f in args:
|
||||
this_editor = VaultEditor(cipher, password, f)
|
||||
this_editor.edit_file()
|
||||
|
||||
def execute_view(args, options, parser):
|
||||
|
||||
if len(args) > 1:
|
||||
raise errors.AnsibleError("view does not accept more than one filename")
|
||||
|
||||
if not options.password_file:
|
||||
password, new_password = utils.ask_vault_passwords(ask_vault_pass=True)
|
||||
else:
|
||||
password = utils.read_vault_file(options.password_file)
|
||||
|
||||
cipher = None
|
||||
|
||||
for f in args:
|
||||
this_editor = VaultEditor(cipher, password, f)
|
||||
this_editor.view_file()
|
||||
|
||||
def execute_encrypt(args, options, parser):
|
||||
|
||||
if not options.password_file:
|
||||
password, new_password = utils.ask_vault_passwords(ask_vault_pass=True, confirm_vault=True)
|
||||
else:
|
||||
password = utils.read_vault_file(options.password_file)
|
||||
|
||||
cipher = 'AES256'
|
||||
if hasattr(options, 'cipher'):
|
||||
cipher = options.cipher
|
||||
|
||||
for f in args:
|
||||
this_editor = VaultEditor(cipher, password, f)
|
||||
this_editor.encrypt_file()
|
||||
|
||||
print "Encryption successful"
|
||||
|
||||
def execute_rekey(args, options, parser):
|
||||
|
||||
if not options.password_file:
|
||||
password, __ = utils.ask_vault_passwords(ask_vault_pass=True)
|
||||
else:
|
||||
password = utils.read_vault_file(options.password_file)
|
||||
|
||||
__, new_password = utils.ask_vault_passwords(ask_vault_pass=False, ask_new_vault_pass=True, confirm_new=True)
|
||||
|
||||
cipher = None
|
||||
for f in args:
|
||||
this_editor = VaultEditor(cipher, password, f)
|
||||
this_editor.rekey_file(new_password)
|
||||
|
||||
print "Rekey successful"
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
# MAIN
|
||||
#-------------------------------------------------------------------------------------
|
||||
|
||||
def main():
|
||||
|
||||
action = get_action(sys.argv)
|
||||
parser = build_option_parser(action)
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if not len(args):
|
||||
raise errors.AnsibleError(
|
||||
"The '%s' command requires a filename as the first argument" % action
|
||||
)
|
||||
|
||||
# execute the desired action
|
||||
try:
|
||||
fn = globals()["execute_%s" % action]
|
||||
fn(args, options, parser)
|
||||
except Exception, err:
|
||||
if options.debug:
|
||||
print traceback.format_exc()
|
||||
print "ERROR:",err
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -1,543 +0,0 @@
|
||||
==============================================
|
||||
ansible-core 2.17 "Gallows Pole" Release Notes
|
||||
==============================================
|
||||
|
||||
.. contents:: Topics
|
||||
|
||||
v2.17.12
|
||||
========
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
| Release Date: 2025-05-19
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- ansible-test - Replace FreeBSD 14.0 remote with FreeBSD 14.1.
|
||||
- ansible-test - Use the ``-t`` option to set the stop timeout when stopping a container. This avoids use of the ``--time`` option which was deprecated in Docker v28.0.
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Ansible will now ensure predictable permissions on remote artifacts, until now it only ensured executable and relied on system masks for the rest.
|
||||
- ansible-doc - fix indentation for first line of descriptions of suboptions and sub-return values (https://github.com/ansible/ansible/pull/84690).
|
||||
- ansible-doc - fix line wrapping for first line of description of options and return values (https://github.com/ansible/ansible/pull/84690).
|
||||
- script - Fix up become support for Windows scripts when become was set through host variables and not on the task directly - https://github.com/ansible/ansible/issues/85076
|
||||
|
||||
v2.17.11
|
||||
========
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
| Release Date: 2025-04-21
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- build - Pin ``wheel`` in ``pyproject.toml`` to ensure compatibility with supported ``setuptools`` versions.
|
||||
- gather_facts action, will now add setup when 'smart' appears with other modules in the FACTS_MODULES setting (#84750).
|
||||
|
||||
v2.17.10
|
||||
========
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
| Release Date: 2025-03-25
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Windows - add support for running on system where WDAC is in audit mode with ``Dynamic Code Security`` enabled.
|
||||
|
||||
v2.17.9
|
||||
=======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
| Release Date: 2025-02-24
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- ansible-test - Automatically retry HTTP GET/PUT/DELETE requests on exceptions.
|
||||
- ansible-test - Use Python's ``urllib`` instead of ``curl`` for HTTP requests.
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- include_vars - fixed erroneous warning if an unreserved variable name contains a single character that matches a reserved variable. (https://github.com/ansible/ansible/issues/84623)
|
||||
|
||||
v2.17.8
|
||||
=======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
| Release Date: 2025-01-27
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Ansible will now also warn when reserved keywords are set via a module (set_fact, include_vars, etc).
|
||||
- Ansible.Basic - Fix ``required_if`` check when the option value to check is unset or set to null.
|
||||
- Use consistent multiprocessing context for action write locks
|
||||
- ansible-test - Fix up coverage reporting to properly translate the temporary path of integration test modules to the expected static test module path.
|
||||
- ansible-vault will now correctly handle `--prompt`, previously it would issue an error about stdin if no 2nd argument was passed
|
||||
- copy action now prevents user from setting internal options.
|
||||
- gather_facts action now defaults to `ansible.legacy.setup` if `smart` was set, no network OS was found and no other alias for `setup` was present.
|
||||
- gather_facts action will now issues errors and warnings as appropriate if a network OS is detected but no facts modules are defined for it.
|
||||
- ssh - connection options were incorrectly templated during ``reset_connection`` tasks (https://github.com/ansible/ansible/pull/84238).
|
||||
|
||||
v2.17.7
|
||||
=======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
| Release Date: 2024-12-02
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- remove extraneous selinux import (https://github.com/ansible/ansible/issues/83657).
|
||||
|
||||
Security Fixes
|
||||
--------------
|
||||
|
||||
- Templating will not prefer AnsibleUnsafe when a variable is referenced via hostvars - CVE-2024-11079
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Fix returning 'unreachable' for the overall task result. This prevents false positives when a looped task has unignored unreachable items (https://github.com/ansible/ansible/issues/84019).
|
||||
- ansible-test - Fix traceback that occurs after an interactive command fails.
|
||||
- dnf5 - fix installing a package using ``state=latest`` when a binary of the same name as the package is already installed (https://github.com/ansible/ansible/issues/84259)
|
||||
- dnf5 - matching on a binary can be achieved only by specifying a full path (https://github.com/ansible/ansible/issues/84334)
|
||||
|
||||
v2.17.6
|
||||
=======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
| Release Date: 2024-11-04
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- ansible-test - Improve container runtime probe error handling. When unexpected probe output is encountered, an error with more useful debugging information is provided.
|
||||
|
||||
Security Fixes
|
||||
--------------
|
||||
|
||||
- include_vars action - Ensure that result masking is correctly requested when vault-encrypted files are read. (CVE-2024-8775)
|
||||
- task result processing - Ensure that action-sourced result masking (``_ansible_no_log=True``) is preserved. (CVE-2024-8775)
|
||||
- user action won't allow ssh-keygen, chown and chmod to run on existing ssh public key file, avoiding traversal on existing symlinks (CVE-2024-9902).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Fix disabling SSL verification when installing collections and roles from git repositories. If ``--ignore-certs`` isn't provided, the value for the ``GALAXY_IGNORE_CERTS`` configuration option will be used (https://github.com/ansible/ansible/issues/83326).
|
||||
- Improve performance on large inventories by reducing the number of implicit meta tasks.
|
||||
- Use the requested error message in the ansible.module_utils.facts.timeout timeout function instead of hardcoding one.
|
||||
- ansible-test - Enable the ``sys.unraisablehook`` work-around for the ``pylint`` sanity test on Python 3.11. Previously the work-around was only enabled for Python 3.12 and later. However, the same issue has been discovered on Python 3.11.
|
||||
- debconf - set empty password values (https://github.com/ansible/ansible/issues/83214).
|
||||
- facts - skip if distribution file path is directory, instead of raising error (https://github.com/ansible/ansible/issues/84006).
|
||||
- user action will now require O(force) to overwrite the public part of an ssh key when generating ssh keys, as was already the case for the private part.
|
||||
- user module now avoids changing ownership of files symlinked in provided home dir skeleton
|
||||
|
||||
v2.17.5
|
||||
=======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
| Release Date: 2024-10-07
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Add descriptions for ``ansible-galaxy install --help` and ``ansible-galaxy role|collection install --help``.
|
||||
- Errors now preserve stacked error messages even when YAML is involved.
|
||||
- ``ansible-galaxy install --help`` - Fix the usage text and document that the requirements file passed to ``-r`` can include collections and roles.
|
||||
- copy - mtime/atime not updated. Fix now update mtime/atime(https://github.com/ansible/ansible/issues/83013)
|
||||
- delay keyword is now a float, matching the underlying 'time' API and user expectations.
|
||||
- dnf5 - re-introduce the ``state: installed`` alias to ``state: present`` (https://github.com/ansible/ansible/issues/83960)
|
||||
- module_utils atomic_move (used by most file based modules), now correctly handles permission copy and setting mtime correctly across all paths
|
||||
|
||||
v2.17.4
|
||||
=======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
| Release Date: 2024-09-09
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Fix ``SemanticVersion.parse()`` to store the version string so that ``__repr__`` reports it instead of ``None`` (https://github.com/ansible/ansible/pull/83831).
|
||||
- Fix an issue where registered variable was not available for templating in ``loop_control.label`` on skipped looped tasks (https://github.com/ansible/ansible/issues/83619)
|
||||
- Fix for ``meta`` tasks breaking host/fork affinity with ``host_pinned`` strategy (https://github.com/ansible/ansible/issues/83294)
|
||||
- Fix using the current task's directory for looking up relative paths within roles (https://github.com/ansible/ansible/issues/82695).
|
||||
- atomic_move - fix using the setgid bit on the parent directory when creating files (https://github.com/ansible/ansible/issues/46742, https://github.com/ansible/ansible/issues/67177).
|
||||
- connection plugins using the 'extras' option feature would need variables to match the plugin's loaded name, sometimes requiring fqcn, which is not the same as the documented/declared/expected variables. Now we fall back to the 'basename' of the fqcn, but plugin authors can still set the expected value directly.
|
||||
- csvfile lookup - give an error when no search term is provided using modern config syntax (https://github.com/ansible/ansible/issues/83689).
|
||||
- include_tasks - Display location when attempting to load a task list where ``include_*`` did not specify any value - https://github.com/ansible/ansible/issues/83874
|
||||
- powershell - Improve CLIXML decoding to decode all control characters and unicode characters that are encoded as surrogate pairs.
|
||||
- psrp - Fix bug when attempting to fetch a file path that contains special glob characters like ``[]``
|
||||
- runtime-metadata sanity test - do not crash on deprecations if ``galaxy.yml`` contains an empty ``version`` field (https://github.com/ansible/ansible/pull/83831).
|
||||
- ssh - Fix bug when attempting to fetch a file path with characters that should be quoted when using the ``piped`` transfer method
|
||||
|
||||
v2.17.3
|
||||
=======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
| Release Date: 2024-08-12
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- ansible-test - Improve the error message shown when an unknown ``--remote`` or ``--docker`` option is given.
|
||||
- ansible-test - Removed the ``vyos/1.1.8`` network remote as it is no longer functional.
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Warning now includes filename and line number of variable when specifying a list of dictionaries for vars (https://github.com/ansible/ansible/issues/82528).
|
||||
- config, restored the ability to set module compression via a variable
|
||||
- debconf - fix normalization of value representation for boolean vtypes in new packages (https://github.com/ansible/ansible/issues/83594)
|
||||
- linear strategy: fix handlers included via ``include_tasks`` handler to be executed in lockstep (https://github.com/ansible/ansible/issues/83019)
|
||||
|
||||
v2.17.2
|
||||
=======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
| Release Date: 2024-07-15
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Fix a traceback when an environment variable contains certain special characters (https://github.com/ansible/ansible/issues/83498)
|
||||
- dnf - reverted incomplete fix from 2.17.2rc1 (https://github.com/ansible/ansible/pull/83504)
|
||||
- dnf, dnf5 - fix for installing a set of packages by specifying them using a wildcard character (https://github.com/ansible/ansible/issues/83373)
|
||||
- linear strategy now provides a properly templated task name to the v2_runner_on_started callback event.
|
||||
- package_facts - ignore warnings sent by apk on stderr (https://github.com/ansible/ansible/issues/83501).
|
||||
- replace - Updated before/after example (https://github.com/ansible/ansible/issues/83390).
|
||||
- templating hostvars under native jinja will not cause serialization errors anymore.
|
||||
|
||||
v2.17.1
|
||||
=======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
| Release Date: 2024-06-17
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- ansible-test - Update ``pypi-test-container`` to version 3.1.0.
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Fix rapid memory usage growth when notifying handlers using the ``listen`` keyword (https://github.com/ansible/ansible/issues/83392)
|
||||
- Fix the task attribute ``resolved_action`` to show the FQCN instead of ``None`` when ``action`` or ``local_action`` is used in the playbook.
|
||||
- Fix using ``module_defaults`` with ``local_action``/``action`` (https://github.com/ansible/ansible/issues/81905).
|
||||
- fixed unit test test_borken_cowsay to address mock not been properly applied when existing unix system already have cowsay installed.
|
||||
- powershell - Implement more robust deletion mechanism for C# code compilation temporary files. This should avoid scenarios where the underlying temporary directory may be temporarily locked by antivirus tools or other IO problems. A failure to delete one of these temporary directories will result in a warning rather than an outright failure.
|
||||
- shell plugin - properly quote all needed components of shell commands (https://github.com/ansible/ansible/issues/82535)
|
||||
|
||||
v2.17.0
|
||||
=======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
| Release Date: 2024-05-20
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
|
||||
Major Changes
|
||||
-------------
|
||||
|
||||
- urls.py - Removed support for Python 2
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- Add ``dump`` and ``passno`` mount information to facts component (https://github.com/ansible/ansible/issues/80478)
|
||||
- Added MIRACLE LINUX 9.2 in RedHat OS Family.
|
||||
- Interpreter Discovery - Remove hardcoded references to specific python interpreters to use for certain distro versions, and modify logic for python3 to become the default.
|
||||
- Use Python's built-in ``functools.update_wrapper`` instead an inline copy from Python 3.7.
|
||||
- User can now set ansible.log to record higher verbosity than what is specified for display via new configuration item LOG_VERBOSITY.
|
||||
- ``DEFAULT_PRIVATE_ROLE_VARS`` is now overridden by explicit setting of ``public`` for ``include_roles`` and ``import_roles``.
|
||||
- ``ansible-galaxy role|collection init`` - accept ``--extra-vars`` to supplement/override the variables ``ansible-galaxy`` injects for templating ``.j2`` files in the skeleton.
|
||||
- ``import_role`` action now also gets a ``public`` option that controls variable exports, default depending on ``DEFAULT_PRIVATE_ROLE_VARS`` (if using defaults equates to ``public=True``).
|
||||
- added configuration item ``TARGET_LOG_INFO`` that allows the user/author to add an information string to the log output on targets.
|
||||
- ansible-doc - treat double newlines in documentation strings as paragraph breaks. This is useful to create multi-paragraph notes in module/plugin documentation (https://github.com/ansible/ansible/pull/82465).
|
||||
- ansible-doc output has been revamped to make it more visually pleasing when going to a terminal, also more concise, use -v to show extra information.
|
||||
- ansible-galaxy - Started normalizing build directory with a trailing separator when building collections, internally. (https://github.com/ansible/ansible/pull/81619).
|
||||
- ansible-galaxy dependency resolution messages have changed the unexplained 'virtual' collection for the specific type ('scm', 'dir', etc) that is more user friendly
|
||||
- ansible-test - Add Alpine 3.19 container.
|
||||
- ansible-test - Add Alpine 3.19 to remotes.
|
||||
- ansible-test - Add Fedora 39 container.
|
||||
- ansible-test - Add Fedora 39 remote.
|
||||
- ansible-test - Add a work-around for permission denied errors when using ``pytest >= 8`` on multi-user systems with an installed version of ``ansible-test``.
|
||||
- ansible-test - Add support for RHEL 9.3 remotes.
|
||||
- ansible-test - Added a macOS 14.3 remote VM.
|
||||
- ansible-test - Bump the ``nios-test-container`` from version 2.0.0 to version 3.0.0.
|
||||
- ansible-test - Containers and remotes managed by ansible-test will have their Python ``EXTERNALLY-MANAGED`` marker (PEP668) removed. This provides backwards compatibility for existing tests running in newer environments which mark their Python as externally managed. A future version of ansible-test may change this behavior, requiring tests to be adapted to such environments.
|
||||
- ansible-test - Make Python 3.12 the default version used in the ``base`` and ``default`` containers.
|
||||
- ansible-test - Remove Alpine 3(.18) container.
|
||||
- ansible-test - Remove Alpine 3.18 from remotes.
|
||||
- ansible-test - Remove Fedora 38 remote support.
|
||||
- ansible-test - Remove Fedora 38 test container.
|
||||
- ansible-test - Remove rhel/9.2 test remote
|
||||
- ansible-test - Remove the FreeBSD 13.2 remote.
|
||||
- ansible-test - Removed fallback to ``virtualenv`` when ``-m venv`` is non-functional.
|
||||
- ansible-test - Removed test remotes: macos/13.2
|
||||
- ansible-test - Removed the ``no-basestring`` sanity test. The test is no longer necessary now that Python 3 is required.
|
||||
- ansible-test - Removed the ``no-dict-iteritems``, ``no-dict-iterkeys`` and ``no-dict-itervalues`` sanity tests. The tests are no longer necessary since Python 3 is required.
|
||||
- ansible-test - Removed the ``no-main-display`` sanity test. The unwanted pattern is unlikely to occur, since the test has existed since Ansible 2.8.
|
||||
- ansible-test - Removed the ``no-unicode-literals`` sanity test. The test is unnecessary now that Python 3 is required and the ``unicode_literals`` feature has no effect.
|
||||
- ansible-test - Special handling for installation of ``cryptography`` has been removed, as it is no longer necessary.
|
||||
- ansible-test - The ``shellcheck`` sanity test no longer disables the ``SC2164`` check. In most cases, seeing this error means the script is missing ``set -e``.
|
||||
- ansible-test - The ``unidiomatic-typecheck`` rule has been enabled in the ``pylint`` sanity test.
|
||||
- ansible-test - The ``unidiomatic-typecheck`` rule has been removed from the ``validate-modules`` sanity test.
|
||||
- ansible-test - Update the base and default containers to use Ubuntu 22.04 for the base image. This also updates PowerShell to version 7.4.0 with .NET 8.0.0 and ShellCheck to version 0.8.0.
|
||||
- ansible-test - Updated the CloudStack test container to version 1.7.0.
|
||||
- ansible-test - Updated the distro test containers to version 6.3.0 to include coverage 7.3.2 for Python 3.8+. The alpine3 container is now based on 3.18 instead of 3.17 and includes Python 3.11 instead of Python 3.10.
|
||||
- ansible-test - Updated the distro test containers to version 7.1.0.
|
||||
- ansible-test - When ansible-test installs requirements, it now instructs pip to allow installs on externally managed environments as defined by PEP 668. This only occurs in ephemeral environments managed by ansible-test, such as containers, or when the `--requirements` option is used.
|
||||
- ansible-test - When invoking ``sleep`` in containers during container setup, the ``env`` command is used to avoid invoking the shell builtin, if present.
|
||||
- ansible-test - document block name now included in error message for YAML parsing errors (https://github.com/ansible/ansible/issues/82353).
|
||||
- ansible-test - sanity test allows ``EXAMPLES`` to be multi-document YAML (https://github.com/ansible/ansible/issues/82353).
|
||||
- ansible-test now has FreeBSD 13.3 and 14.0 support
|
||||
- ansible.builtin.user - Remove user not found warning (https://github.com/ansible/ansible/issues/80267)
|
||||
- apt_repository.py - use api.launchpad.net endpoint instead of launchpad.net/api
|
||||
- async tasks can now also support check mode at the same time.
|
||||
- async_status now supports check mode.
|
||||
- constructed inventory plugin - Adding a note that only group_vars of explicit groups are loaded (https://github.com/ansible/ansible/pull/82580).
|
||||
- csvfile - add a keycol parameter to specify in which column to search.
|
||||
- dnf - add the ``best`` option
|
||||
- dnf5 - add the ``best`` option
|
||||
- filter plugin - Add the count and mandatory_count parameters in the regex_replace filter
|
||||
- find - add a encoding parameter to specify which encoding of the files to be searched.
|
||||
- git module - gpg_allowlist name was added in 2.17 and we will eventually deprecate the gpg_whitelist alias.
|
||||
- import_role - allow subdirectories with ``_from`` options for parity with ``include_role`` (https://github.com/ansible/ansible/issues/82584).
|
||||
- module argument spec - Allow module authors to include arbitrary additional context in the argument spec, by making use of a new top level key called ``context``. This key should be a dict type. This allows for users to customize what they place in the argument spec, without having to ignore sanity tests that validate the schema.
|
||||
- modules - Add the ability for an action plugin to call ``self._execute_module(*, ignore_unknown_opts=True)`` to execute a module with options that may not be supported for the version being called. This tells the module basic wrapper to ignore validating the options provided match the arg spec.
|
||||
- package action now has a configuration that overrides the detected package manager, it is still overridden itself by the use option.
|
||||
- py3compat - Remove ``ansible.utils.py3compat`` as it is no longer necessary
|
||||
- removed the unused argument ``create_new_password`` from ``CLI.build_vault_ids`` (https://github.com/ansible/ansible/pull/82066).
|
||||
- urls - Add support for TLS 1.3 post handshake certificate authentication - https://github.com/ansible/ansible/issues/81782
|
||||
- urls - reduce complexity of ``Request.open``
|
||||
- user - accept yescrypt hash as user password
|
||||
- validate-modules tests now correctly handles ``choices`` in dictionary format.
|
||||
|
||||
Breaking Changes / Porting Guide
|
||||
--------------------------------
|
||||
|
||||
- assert - Nested templating may result in an inability for the conditional to be evaluated. See the porting guide for more information.
|
||||
|
||||
Deprecated Features
|
||||
-------------------
|
||||
|
||||
- Old style vars plugins which use the entrypoints `get_host_vars` or `get_group_vars` are deprecated. The plugin should be updated to inherit from `BaseVarsPlugin` and define a `get_vars` method as the entrypoint.
|
||||
- The 'required' parameter in 'ansible.module_utils.common.process.get_bin_path' API is deprecated (https://github.com/ansible/ansible/issues/82464).
|
||||
- ``module_utils`` - importing the following convenience helpers from ``ansible.module_utils.basic`` has been deprecated: ``get_exception``, ``literal_eval``, ``_literal_eval``, ``datetime``, ``signal``, ``types``, ``chain``, ``repeat``, ``PY2``, ``PY3``, ``b``, ``binary_type``, ``integer_types``, ``iteritems``, ``string_types``, ``test_type``, ``map`` and ``shlex_quote``.
|
||||
- ansible-doc - role entrypoint attributes are deprecated and eventually will no longer be shown in ansible-doc from ansible-core 2.20 on (https://github.com/ansible/ansible/issues/82639, https://github.com/ansible/ansible/pull/82678).
|
||||
- paramiko connection plugin, configuration items in the global scope are being deprecated and will be removed in favor or the existing same options in the plugin itself. Users should not need to change anything (how to configure them are the same) but plugin authors using the global constants should move to using the plugin's get_option().
|
||||
|
||||
Removed Features (previously deprecated)
|
||||
----------------------------------------
|
||||
|
||||
- Remove deprecated APIs from ansible-docs (https://github.com/ansible/ansible/issues/81716).
|
||||
- Remove deprecated JINJA2_NATIVE_WARNING environment variable (https://github.com/ansible/ansible/issues/81714)
|
||||
- Remove deprecated ``scp_if_ssh`` from ssh connection plugin (https://github.com/ansible/ansible/issues/81715).
|
||||
- Remove deprecated crypt support from ansible.utils.encrypt (https://github.com/ansible/ansible/issues/81717)
|
||||
- Removed Python 2.7 and Python 3.6 as a supported remote version. Python 3.7+ is now required for target execution.
|
||||
- With the removal of Python 2 support, the yum module and yum action plugin are removed and redirected to ``dnf``.
|
||||
|
||||
Security Fixes
|
||||
--------------
|
||||
|
||||
- ANSIBLE_NO_LOG - Address issue where ANSIBLE_NO_LOG was ignored (CVE-2024-0690)
|
||||
- ansible-galaxy - Prevent roles from using symlinks to overwrite files outside of the installation directory (CVE-2023-5115)
|
||||
- templating - Address issues where internal templating can cause unsafe variables to lose their unsafe designation (CVE-2023-5764)
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Add a version ceiling constraint for pypsrp to avoid potential breaking changes in the 1.0.0 release.
|
||||
- All core lookups now use set_option(s) even when doing their own custom parsing. This ensures that the options are always the proper type.
|
||||
- Allow for searching handler subdir for included task via include_role (https://github.com/ansible/ansible/issues/81722)
|
||||
- AnsibleModule.atomic_move - fix preserving extended ACLs of the destination when it exists (https://github.com/ansible/ansible/issues/72929).
|
||||
- Cache host_group_vars after instantiating it once and limit the amount of repetitive work it needs to do every time it runs.
|
||||
- Call PluginLoader.all() once for vars plugins, and load vars plugins that run automatically or are enabled specifically by name subsequently.
|
||||
- Consolidate systemd detection logic into one place (https://github.com/ansible/ansible/issues/80975).
|
||||
- Consolidated the list of internal static vars, centralized them as constant and completed from some missing entries.
|
||||
- Do not print undefined error message twice (https://github.com/ansible/ansible/issues/78703).
|
||||
- Enable file cache for vaulted files during vars lookup to fix a strong performance penalty in huge and complex playbboks.
|
||||
- Fix NEVRA parsing of package names that include digit(s) in them (https://github.com/ansible/ansible/issues/76463, https://github.com/ansible/ansible/issues/81018)
|
||||
- Fix ``force_handlers`` not working with ``any_errors_fatal`` (https://github.com/ansible/ansible/issues/36308)
|
||||
- Fix ``run_once`` being incorrectly interpreted on handlers (https://github.com/ansible/ansible/issues/81666)
|
||||
- Fix an issue when setting a plugin name from an unsafe source resulted in ``ValueError: unmarshallable object`` (https://github.com/ansible/ansible/issues/82708)
|
||||
- Fix check for missing _sub_plugin attribute in older connection plugins (https://github.com/ansible/ansible/pull/82954)
|
||||
- Fix condition for unquoting configuration strings from ini files (https://github.com/ansible/ansible/issues/82387).
|
||||
- Fix for when ``any_errors_fatal`` was ignored if error occurred in a block with always (https://github.com/ansible/ansible/issues/31543)
|
||||
- Fix handlers not being executed in lockstep using the linear strategy in some cases (https://github.com/ansible/ansible/issues/82307)
|
||||
- Fix handling missing urls in ansible.module_utils.urls.fetch_file for Python 3.
|
||||
- Fix issue where an ``include_tasks`` handler in a role was not able to locate a file in ``tasks/`` when ``tasks_from`` was used as a role entry point and ``main.yml`` was not present (https://github.com/ansible/ansible/issues/82241)
|
||||
- Fix issues when tasks withing nested blocks wouldn't run when ``force_handlers`` is set (https://github.com/ansible/ansible/issues/81533)
|
||||
- Fix loading vars_plugins in roles (https://github.com/ansible/ansible/issues/82239).
|
||||
- Fix notifying role handlers by listen keyword topics with the "role_name : " prefix (https://github.com/ansible/ansible/issues/82849).
|
||||
- Fix setting proper locale for git executable when running on non english systems, ensuring git output can always be parsed.
|
||||
- Fix tasks in always section not being executed for nested blocks with ``any_errors_fatal`` (https://github.com/ansible/ansible/issues/73246)
|
||||
- Fixes permission for cache json file from 600 to 644 (https://github.com/ansible/ansible/issues/82683).
|
||||
- Give the tombstone error for ``include`` pre-fork like other tombstoned action/module plugins.
|
||||
- Harden python templates for respawn and ansiballz around str literal quoting
|
||||
- Include the task location when a module or action plugin is deprecated (https://github.com/ansible/ansible/issues/82450).
|
||||
- Interpreter discovery - Add ``Amzn`` to ``OS_FAMILY_MAP`` for correct family fallback for interpreter discovery (https://github.com/ansible/ansible/issues/80882).
|
||||
- Mirror the behavior of dnf on the command line when handling NEVRAs with omitted epoch (https://github.com/ansible/ansible/issues/71808)
|
||||
- Plugin loader does not dedupe nor cache filter/test plugins by file basename, but full path name.
|
||||
- Properly template tags in parent blocks (https://github.com/ansible/ansible/issues/81053)
|
||||
- Provide additional information about the alternative plugin in the deprecation message (https://github.com/ansible/ansible/issues/80561).
|
||||
- Remove the galaxy_info field ``platforms`` from the role templates (https://github.com/ansible/ansible/issues/82453).
|
||||
- Restoring the ability of filters/tests can have same file base name but different tests/filters defined inside.
|
||||
- Reword the error message when the module fails to parse parameters in JSON format (https://github.com/ansible/ansible/issues/81188).
|
||||
- Reword warning if the reserved keyword _ansible_ used as a module parameter (https://github.com/ansible/ansible/issues/82514).
|
||||
- Run all handlers with the same ``listen`` topic, even when notified from another handler (https://github.com/ansible/ansible/issues/82363).
|
||||
- Slight optimization to hostvars (instantiate template only once per host, vs per call to var).
|
||||
- Stopped misleadingly advertising ``async`` mode support in the ``reboot`` module (https://github.com/ansible/ansible/issues/71517).
|
||||
- ``ansible-galaxy role import`` - fix using the ``role_name`` in a standalone role's ``galaxy_info`` metadata by disabling automatic removal of the ``ansible-role-`` prefix. This matches the behavior of the Galaxy UI which also no longer implicitly removes the ``ansible-role-`` prefix. Use the ``--role-name`` option or add a ``role_name`` to the ``galaxy_info`` dictionary in the role's ``meta/main.yml`` to use an alternate role name.
|
||||
- ``ansible-test sanity --test runtime-metadata`` - add ``action_plugin`` as a valid field for modules in the schema (https://github.com/ansible/ansible/pull/82562).
|
||||
- ``ansible.module_utils.service`` - ensure binary data transmission in ``daemonize()``
|
||||
- ``any_errors_fatal`` should fail all hosts and rescue all of them when a ``rescue`` section is specified (https://github.com/ansible/ansible/issues/80981)
|
||||
- ``include_role`` - properly execute ``v2_playbook_on_include`` and ``v2_runner_on_failed`` callbacks as well as increase ``ok`` and ``failed`` stats in the play recap, when appropriate (https://github.com/ansible/ansible/issues/77336)
|
||||
- allow_duplicates - fix evaluating if the current role allows duplicates instead of using the initial value from the duplicate's cached role.
|
||||
- ansible-config init will now dedupe ini entries from plugins.
|
||||
- ansible-config will now properly template defaults before dumping them.
|
||||
- ansible-doc - fixed "inicates" typo in output
|
||||
- ansible-doc - format top-level descriptions with multiple paragraphs as multiple paragraphs, instead of concatenating them (https://github.com/ansible/ansible/pull/83155).
|
||||
- ansible-galaxy - Deprecate use of the Galaxy v2 API (https://github.com/ansible/ansible/issues/81781)
|
||||
- ansible-galaxy - Provide a better error message when using a requirements file with an invalid format - https://github.com/ansible/ansible/issues/81901
|
||||
- ansible-galaxy - Resolve issue with the dataclass used for galaxy.yml manifest caused by using future annotations
|
||||
- ansible-galaxy - ensure path to ansible collection when installing or downloading doesn't have a backslash (https://github.com/ansible/ansible/pull/79705).
|
||||
- ansible-galaxy - started allowing the use of pre-releases for collections that do not have any stable versions published. (https://github.com/ansible/ansible/pull/81606)
|
||||
- ansible-galaxy - started allowing the use of pre-releases for dependencies on any level of the dependency tree that specifically demand exact pre-release versions of collections and not version ranges. (https://github.com/ansible/ansible/pull/81606)
|
||||
- ansible-galaxy error on dependency resolution will not error itself due to 'virtual' collections not having a name/namespace.
|
||||
- ansible-galaxy info - fix reporting no role found when lookup_role_by_name returns None.
|
||||
- ansible-galaxy role import - exit with 1 when the import fails (https://github.com/ansible/ansible/issues/82175).
|
||||
- ansible-galaxy role install - fix installing roles from Galaxy that have version ``None`` (https://github.com/ansible/ansible/issues/81832).
|
||||
- ansible-galaxy role install - fix symlinks (https://github.com/ansible/ansible/issues/82702, https://github.com/ansible/ansible/issues/81965).
|
||||
- ansible-galaxy role install - normalize tarfile paths and symlinks using ``ansible.utils.path.unfrackpath`` and consider them valid as long as the realpath is in the tarfile's role directory (https://github.com/ansible/ansible/issues/81965).
|
||||
- ansible-inventory - index available_hosts for major performance boost when dumping large inventories
|
||||
- ansible-pull now will expand relative paths for the ``-d|--directory`` option is now expanded before use.
|
||||
- ansible-pull will now correctly handle become and connection password file options for ansible-playbook.
|
||||
- ansible-test - Add a ``pylint`` plugin to work around a known issue on Python 3.12.
|
||||
- ansible-test - Explicitly supply ``ControlPath=none`` when setting up port forwarding over SSH to address the scenario where the local ssh configuration uses ``ControlPath`` for all hosts, and would prevent ports to be forwarded after the initial connection to the host.
|
||||
- ansible-test - Fix parsing of cgroup entries which contain a ``:`` in the path (https://github.com/ansible/ansible/issues/81977).
|
||||
- ansible-test - Include missing ``pylint`` requirements for Python 3.10.
|
||||
- ansible-test - Properly detect docker host when using ``ssh://`` protocol for connecting to the docker daemon.
|
||||
- ansible-test - The ``libexpat`` package is automatically upgraded during remote bootstrapping to maintain compatibility with newer Python packages.
|
||||
- ansible-test - The ``validate-modules`` sanity test no longer attempts to process files with unrecognized extensions as Python (resolves https://github.com/ansible/ansible/issues/82604).
|
||||
- ansible-test - Update ``pylint`` to version 3.0.1.
|
||||
- ansible-test ansible-doc sanity test - do not remove underscores from plugin names in collections before calling ``ansible-doc`` (https://github.com/ansible/ansible/pull/82574).
|
||||
- ansible-test validate-modules sanity test - do not treat leading underscores for plugin names in collections as an attempted deprecation (https://github.com/ansible/ansible/pull/82575).
|
||||
- ansible-test — Python 3.8–3.12 will use ``coverage`` v7.3.2.
|
||||
- ansible.builtin.apt - calling clean = true does not properly clean certain cache files such as /var/cache/apt/pkgcache.bin and /var/cache/apt/pkgcache.bin (https://github.com/ansible/ansible/issues/82611)
|
||||
- ansible.builtin.uri - the module was ignoring the ``force`` parameter and always requesting a cached copy (via the ``If-Modified-Since`` header) when downloading to an existing local file. Disable caching when ``force`` is ``true``, as documented (https://github.com/ansible/ansible/issues/82166).
|
||||
- ansible_managed restored it's 'templatability' by ensuring the possible injection routes are cut off earlier in the process.
|
||||
- apt - honor install_recommends and dpkg_options while installing python3-apt library (https://github.com/ansible/ansible/issues/40608).
|
||||
- apt - install recommended packages when installing package via deb file (https://github.com/ansible/ansible/issues/29726).
|
||||
- apt_repository - do not modify repo files if the file is a symlink (https://github.com/ansible/ansible/issues/49809).
|
||||
- apt_repository - update PPA URL to point to https URL (https://github.com/ansible/ansible/issues/82463).
|
||||
- assemble - fixed missing parameter 'content' in _get_diff_data API (https://github.com/ansible/ansible/issues/82359).
|
||||
- async - Fix bug that stopped running async task in ``--check`` when ``check_mode: False`` was set as a task attribute - https://github.com/ansible/ansible/issues/82811
|
||||
- blockinfile - when ``create=true`` is used with a filename without path, the module crashed (https://github.com/ansible/ansible/pull/81638).
|
||||
- check if there are attributes to set before attempting to set them (https://github.com/ansible/ansible/issues/76727)
|
||||
- copy action now also generates temprary files as hidden ('.' prefixed) to avoid accidental pickup by running services that glob by extension.
|
||||
- copy action now ensures that tempfiles use the same suffix as destination, to allow for ``validate`` to work with utilities that check extensions.
|
||||
- deb822_repository - handle idempotency if the order of parameters is changed (https://github.com/ansible/ansible/issues/82454).
|
||||
- debconf - allow user to specify a list for value when vtype is multiselect (https://github.com/ansible/ansible/issues/81345).
|
||||
- delegate_to when set to an empty or undefined variable will now give a proper error.
|
||||
- distribution.py - Recognize ALP-Dolomite as part of the SUSE OS family in Ansible, fixing its previous misidentification (https://github.com/ansible/ansible/pull/82496).
|
||||
- distro - bump bundled distro version from 1.6.0 to 1.8.0 (https://github.com/ansible/ansible/issues/81713).
|
||||
- dnf - fix an issue when cached RPMs were left in the cache directory even when the keepcache setting was unset (https://github.com/ansible/ansible/issues/81954)
|
||||
- dnf - fix an issue when installing a package by specifying a file it provides could result in installing a different package providing the same file than the package already installed resulting in resolution failure (https://github.com/ansible/ansible/issues/82461)
|
||||
- dnf - properly set gpg check options on enabled repositories according to the ``disable_gpg_check`` option (https://github.com/ansible/ansible/issues/80110)
|
||||
- dnf - properly skip unavailable packages when ``skip_broken`` is enabled (https://github.com/ansible/ansible/issues/80590)
|
||||
- dnf - the ``nobest`` option only overrides the distribution default when explicitly used, and is used for all supported operations (https://github.com/ansible/ansible/issues/82616)
|
||||
- dnf5 - replace removed API calls
|
||||
- dnf5 - respect ``allow_downgrade`` when installing packages directly from rpm files
|
||||
- dnf5 - the ``nobest`` option only overrides the distribution default when used
|
||||
- dwim functions for lookups should be better at detectging role context even in abscense of tasks/main.
|
||||
- ensure we have logger before we log when we have increased verbosity.
|
||||
- expect - fix argument spec error using timeout=null (https://github.com/ansible/ansible/issues/80982).
|
||||
- fact gathering on linux now handles thread count by using rounding vs dropping decimals, it should give slightly more accurate numbers.
|
||||
- facts - add a generic detection for VMware in product name.
|
||||
- facts - detect VMware ESXi 8.0 virtualization by product name VMware20,1
|
||||
- fetch - Do not calculate the file size for Windows fetch targets to improve performance.
|
||||
- fetch - add error message when using ``dest`` with a trailing slash that becomes a local directory - https://github.com/ansible/ansible/issues/82878
|
||||
- find - do not fail on Permission errors (https://github.com/ansible/ansible/issues/82027).
|
||||
- first_found lookup now always returns a full (absolute) and normalized path
|
||||
- first_found lookup now always takes into account k=v options
|
||||
- flush_handlers - properly handle a handler failure in a nested block when ``force_handlers`` is set (http://github.com/ansible/ansible/issues/81532)
|
||||
- galaxy - skip verification for unwanted Python compiled bytecode files (https://github.com/ansible/ansible/issues/81628).
|
||||
- handle exception raised while validating with elements='int' and value is not within choices (https://github.com/ansible/ansible/issues/82776).
|
||||
- include_tasks - include `ansible_loop_var` and `ansible_index_var` in a loop (https://github.com/ansible/ansible/issues/82655).
|
||||
- include_vars - fix calculating ``depth`` relative to the root and ensure all files are included (https://github.com/ansible/ansible/issues/80987).
|
||||
- interpreter_discovery - handle AnsibleError exception raised while interpreter discovery (https://github.com/ansible/ansible/issues/78264).
|
||||
- iptables - add option choices 'src,src' and 'dst,dst' in match_set_flags (https://github.com/ansible/ansible/issues/81281).
|
||||
- iptables - set jump to DSCP when set_dscp_mark or set_dscp_mark_class is set (https://github.com/ansible/ansible/issues/77077).
|
||||
- known_hosts - Fix issue with `@cert-authority` entries in known_hosts incorrectly being removed.
|
||||
- module no_log will no longer affect top level booleans, for example ``no_log_module_parameter='a'`` will no longer hide ``changed=False`` as a 'no log value' (matches 'a').
|
||||
- moved assemble, raw, copy, fetch, reboot, script and wait_for_connection to query task instead of play_context ensuring they get the lastest and most correct data.
|
||||
- reboot action now handles connections with 'timeout' vs only 'connection_timeout' settings.
|
||||
- role params now have higher precedence than host facts again, matching documentation, this had unintentionally changed in 2.15.
|
||||
- roles, code cleanup and performance optimization of dependencies, now cached, and ``public`` setting is now determined once, at role instantiation.
|
||||
- roles, the ``static`` property is now correctly set, this will fix issues with ``public`` and ``DEFAULT_PRIVATE_ROLE_VARS`` controls on exporting vars.
|
||||
- set_option method for plugins to update config now properly passes through type casting and validation.
|
||||
- ssh - add tests for the SSH connection plugin.
|
||||
- support url-encoded credentials in URLs like http://x%40:%40@example.com (https://github.com/ansible/ansible/pull/82552)
|
||||
- syslog - Handle ValueError exception raised when sending Null Characters to syslog with Python 3.12.
|
||||
- systemd_services - update documentation regarding required_one_of and required_by parameters (https://github.com/ansible/ansible/issues/82914).
|
||||
- template - Fix error when templating an unsafe string which corresponds to an invalid type in Python (https://github.com/ansible/ansible/issues/82600).
|
||||
- template action will also inherit the behavior from copy (as it uses it internally).
|
||||
- templating - ensure syntax errors originating from a template being compiled into Python code object result in a failure (https://github.com/ansible/ansible/issues/82606)
|
||||
- unarchive - add support for 8 character permission strings for zip archives (https://github.com/ansible/ansible/pull/81705).
|
||||
- unarchive - force unarchive if symlink target changes (https://github.com/ansible/ansible/issues/30420).
|
||||
- unarchive modules now uses zipinfo options without relying on implementation defaults, making it more compatible with all OS/distributions.
|
||||
- unsafe data - Address an incompatibility when iterating or getting a single index from ``AnsibleUnsafeBytes``
|
||||
- unsafe data - Address an incompatibility with ``AnsibleUnsafeText`` and ``AnsibleUnsafeBytes`` when pickling with ``protocol=0``
|
||||
- unsafe data - Enable directly using ``AnsibleUnsafeText`` with Python ``pathlib`` (https://github.com/ansible/ansible/issues/82414)
|
||||
- uri - update the documentation for follow_redirects.
|
||||
- uri action plugin now skipped during check mode (not supported) instead of even trying to execute the module, which already skipped, this does not really change the result, but returns much faster.
|
||||
- vars - handle exception while combining VarsWithSources and dict (https://github.com/ansible/ansible/issues/81659).
|
||||
- wait_for should not handle 'non mmapable files' again.
|
||||
- winrm - Better handle send input failures when communicating with hosts under load
|
||||
- winrm - Do not raise another exception during cleanup when a task is timed out - https://github.com/ansible/ansible/issues/81095
|
||||
- winrm - does not hang when attempting to get process output when stdin write failed
|
||||
@ -1,8 +0,0 @@
|
||||
Changelogs
|
||||
==========
|
||||
|
||||
As part of the release process a version-specific `CHANGELOG-vX.Y.rst` will be generated from fragments in
|
||||
the `fragments` directory.
|
||||
|
||||
On release branches once a release has been created, consult the branch's version-specific file for changes that have
|
||||
occurred in that branch. The `devel` branch does not have a generated changelog, only changelog fragments.
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,22 +0,0 @@
|
||||
---
|
||||
title: ansible-core
|
||||
release_tag_re: '(v(?:[\d.ab\-]|rc)+)'
|
||||
pre_release_tag_re: '(?P<pre_release>(?:[ab]|rc)+\d*)$'
|
||||
changes_file: changelog.yaml
|
||||
changes_format: combined
|
||||
keep_fragments: true
|
||||
always_refresh: true
|
||||
ignore_other_fragment_extensions: true
|
||||
mention_ancestor: false
|
||||
notesdir: fragments
|
||||
prelude_section_name: release_summary
|
||||
new_plugins_after_name: removed_features
|
||||
sections:
|
||||
- ['major_changes', 'Major Changes']
|
||||
- ['minor_changes', 'Minor Changes']
|
||||
- ['breaking_changes', 'Breaking Changes / Porting Guide']
|
||||
- ['deprecated_features', 'Deprecated Features']
|
||||
- ['removed_features', 'Removed Features (previously deprecated)']
|
||||
- ['security_fixes', 'Security Fixes']
|
||||
- ['bugfixes', 'Bugfixes']
|
||||
- ['known_issues', 'Known Issues']
|
||||
@ -1,3 +0,0 @@
|
||||
release_summary: |
|
||||
| Release Date: 2024-05-20
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
@ -1,3 +0,0 @@
|
||||
release_summary: |
|
||||
| Release Date: 2024-04-08
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
@ -1,3 +0,0 @@
|
||||
release_summary: |
|
||||
| Release Date: 2024-04-29
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
@ -1,3 +0,0 @@
|
||||
release_summary: |
|
||||
| Release Date: 2024-05-13
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
@ -1,3 +0,0 @@
|
||||
release_summary: |
|
||||
| Release Date: 2025-03-25
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
@ -1,3 +0,0 @@
|
||||
release_summary: |
|
||||
| Release Date: 2025-03-17
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
@ -1,3 +0,0 @@
|
||||
release_summary: |
|
||||
| Release Date: 2025-04-21
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
@ -1,3 +0,0 @@
|
||||
release_summary: |
|
||||
| Release Date: 2025-04-14
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
@ -1,3 +0,0 @@
|
||||
release_summary: |
|
||||
| Release Date: 2025-05-19
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
@ -1,3 +0,0 @@
|
||||
release_summary: |
|
||||
| Release Date: 2025-05-12
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
@ -1,3 +0,0 @@
|
||||
release_summary: |
|
||||
| Release Date: 2024-06-17
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
@ -1,3 +0,0 @@
|
||||
release_summary: |
|
||||
| Release Date: 2024-06-10
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
@ -1,3 +0,0 @@
|
||||
release_summary: |
|
||||
| Release Date: 2024-07-15
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
@ -1,3 +0,0 @@
|
||||
release_summary: |
|
||||
| Release Date: 2024-07-08
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
@ -1,3 +0,0 @@
|
||||
release_summary: |
|
||||
| Release Date: 2024-07-09
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
@ -1,3 +0,0 @@
|
||||
release_summary: |
|
||||
| Release Date: 2024-08-12
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
@ -1,3 +0,0 @@
|
||||
release_summary: |
|
||||
| Release Date: 2024-08-05
|
||||
| `Porting Guide <https://docs.ansible.com/ansible-core/2.17/porting_guides/porting_guide_core_2.17.html>`__
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user