├── .README.html ├── .ansible-lint ├── .codespell_ignores ├── .codespellrc ├── .commitlintrc.js ├── .fmf └── version ├── .github ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── ansible-lint.yml │ ├── ansible-managed-var-comment.yml │ ├── ansible-test.yml │ ├── build_docs.yml │ ├── changelog_to_tag.yml │ ├── codespell.yml │ ├── markdownlint.yml │ ├── pr-title-lint.yml │ ├── qemu-kvm-integration-tests.yml │ ├── shellcheck.yml │ ├── test_converting_readme.yml │ ├── tft.yml │ ├── tft_citest_bad.yml │ ├── weekly_ci.yml │ └── woke.yml ├── .gitignore ├── .markdownlint.yaml ├── .ostree ├── README.md ├── get_ostree_data.sh ├── packages-runtime-CentOS-7.txt ├── packages-runtime-RedHat-6.txt ├── packages-runtime-RedHat-7.txt ├── packages-runtime.txt ├── packages-testing-CentOS-7.txt ├── packages-testing-RedHat-6.txt ├── packages-testing-RedHat-7.txt └── packages-testing.txt ├── .pandoc_template.html5 ├── .sanity-ansible-ignore-2.10.txt ├── .sanity-ansible-ignore-2.11.txt ├── .sanity-ansible-ignore-2.12.txt ├── .sanity-ansible-ignore-2.13.txt ├── .sanity-ansible-ignore-2.14.txt ├── .sanity-ansible-ignore-2.15.txt ├── .sanity-ansible-ignore-2.16.txt ├── .sanity-ansible-ignore-2.17.txt ├── .sanity-ansible-ignore-2.9.txt ├── .yamllint.yml ├── CHANGELOG.md ├── COPYING ├── README-ansible.md ├── README-ostree.md ├── README.md ├── ansible_pytest_extra_requirements.txt ├── contributing.md ├── custom_requirements.txt ├── defaults └── main.yml ├── examples ├── multiple-ntp-servers.yml └── single-pool.yml ├── handlers └── main.yml ├── library ├── timesync_provider.sh └── timesync_provider.yml ├── meta ├── collection-requirements.yml └── main.yml ├── molecule └── default │ ├── Dockerfile.j2 │ └── molecule.yml ├── molecule_extra_requirements.txt ├── plans ├── README-plans.md └── test_playbooks_parallel.fmf ├── pylint_extra_requirements.txt ├── pylintrc ├── pytest_extra_requirements.txt ├── tasks ├── main.yml └── set_vars.yml ├── templates ├── chrony.conf.j2 ├── chronyd.sysconfig.j2 ├── ntp.conf.j2 ├── ntpd.sysconfig.j2 ├── phc2sys.sysconfig.j2 ├── ptp4l.conf.j2 ├── ptp4l.sysconfig.j2 └── timemaster.conf.j2 ├── tests ├── .fmf │ └── version ├── inventory.yaml.j2 ├── provision.fmf ├── roles │ └── linux-system-roles.timesync │ │ ├── defaults │ │ ├── handlers │ │ ├── library │ │ ├── meta │ │ ├── tasks │ │ ├── templates │ │ └── vars ├── setup-snapshot.yml ├── tasks │ ├── check_header.yml │ ├── cleanup.yml │ └── setup.yml ├── templates │ └── get_ansible_managed.j2 ├── tests_chrony.yml ├── tests_default.yml ├── tests_default_vars.yml ├── tests_default_wrapper.yml ├── tests_ntp.yml ├── tests_ntp_provider1.yml ├── tests_ntp_provider2.yml ├── tests_ntp_provider3.yml ├── tests_ntp_provider4.yml ├── tests_ntp_provider5.yml ├── tests_ntp_provider6.yml ├── tests_ntp_ptp.yml ├── tests_options.yml ├── tests_ptp_multi.yml ├── tests_ptp_single.yml └── vars │ └── rh_distros_vars.yml ├── tox.ini └── vars ├── AlmaLinux_10.yml ├── AlmaLinux_8.yml ├── AlmaLinux_9.yml ├── CentOS_10.yml ├── CentOS_6.yml ├── CentOS_7.yml ├── CentOS_8.yml ├── CentOS_9.yml ├── Debian.yml ├── Fedora.yml ├── OracleLinux_10.yml ├── OracleLinux_6.yml ├── OracleLinux_7.yml ├── OracleLinux_8.yml ├── OracleLinux_9.yml ├── RedHat_10.yml ├── RedHat_6.yml ├── RedHat_7.yml ├── RedHat_8.yml ├── RedHat_9.yml ├── Rocky_10.yml ├── Rocky_8.yml ├── Rocky_9.yml ├── SL-Micro.yml ├── default.yml └── main.yml /.ansible-lint: -------------------------------------------------------------------------------- 1 | --- 2 | profile: production 3 | extra_vars: 4 | targets: target_hosts 5 | kinds: 6 | - yaml: "**/meta/collection-requirements.yml" 7 | - playbook: "**/tests/get_coverage.yml" 8 | - yaml: "**/tests/collection-requirements.yml" 9 | - playbook: "**/tests/tests_*.yml" 10 | - playbook: "**/tests/setup-snapshot.yml" 11 | - tasks: "**/tests/*.yml" 12 | - playbook: "**/tests/playbooks/*.yml" 13 | - tasks: "**/tests/tasks/*.yml" 14 | - tasks: "**/tests/tasks/*/*.yml" 15 | - vars: "**/tests/vars/*.yml" 16 | - playbook: "**/examples/*.yml" 17 | skip_list: 18 | - fqcn-builtins 19 | - var-naming[no-role-prefix] 20 | - sanity[cannot-ignore] # wokeignore:rule=sanity 21 | exclude_paths: 22 | - tests/roles/ 23 | - .github/ 24 | - .markdownlint.yaml 25 | - examples/roles/ 26 | mock_roles: 27 | - linux-system-roles.timesync 28 | supported_ansible_also: 29 | - "2.14.0" 30 | -------------------------------------------------------------------------------- /.codespell_ignores: -------------------------------------------------------------------------------- 1 | passt 2 | -------------------------------------------------------------------------------- /.codespellrc: -------------------------------------------------------------------------------- 1 | [codespell] 2 | check-hidden = true 3 | # Note that `-w` doesn't work when ignore-multiline-regex is set 4 | # https://github.com/codespell-project/codespell/issues/3642 5 | ignore-multiline-regex = codespell:ignore-begin.*codespell:ignore-end 6 | ignore-words = .codespell_ignores 7 | # skip-file is not available https://github.com/codespell-project/codespell/pull/2759 8 | # .pandoc_template.html5 contains a typo in Licence that we shouldn't edit 9 | # .README.html is generated from README.md automatically - no need to check spelling 10 | skip = .pandoc_template.html5,.README.html 11 | -------------------------------------------------------------------------------- /.commitlintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parserPreset: 'conventional-changelog-conventionalcommits', 3 | rules: { 4 | 'body-leading-blank': [1, 'always'], 5 | 'body-max-line-length': [2, 'always', 100], 6 | 'footer-leading-blank': [1, 'always'], 7 | 'footer-max-line-length': [2, 'always', 100], 8 | 'header-max-length': [2, 'always', 100], 9 | 'subject-case': [ 10 | 2, 11 | 'never', 12 | ['start-case', 'pascal-case', 'upper-case'], 13 | ], 14 | 'subject-empty': [2, 'never'], 15 | 'subject-full-stop': [2, 'never', '.'], 16 | 'type-case': [2, 'always', 'lower-case'], 17 | 'type-empty': [2, 'never'], 18 | 'type-enum': [ 19 | 2, 20 | 'always', 21 | [ 22 | 'build', 23 | 'chore', 24 | 'ci', 25 | 'docs', 26 | 'feat', 27 | 'fix', 28 | 'perf', 29 | 'refactor', 30 | 'revert', 31 | 'style', 32 | 'test', 33 | 'tests', 34 | ], 35 | ], 36 | }, 37 | prompt: { 38 | questions: { 39 | type: { 40 | description: "Select the type of change that you're committing", 41 | enum: { 42 | feat: { 43 | description: 'A new feature', 44 | title: 'Features', 45 | emoji: '✨', 46 | }, 47 | fix: { 48 | description: 'A bug fix', 49 | title: 'Bug Fixes', 50 | emoji: '🐛', 51 | }, 52 | docs: { 53 | description: 'Documentation only changes', 54 | title: 'Documentation', 55 | emoji: '📚', 56 | }, 57 | style: { 58 | description: 59 | 'Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)', 60 | title: 'Styles', 61 | emoji: '💎', 62 | }, 63 | refactor: { 64 | description: 65 | 'A code change that neither fixes a bug nor adds a feature', 66 | title: 'Code Refactoring', 67 | emoji: '📦', 68 | }, 69 | perf: { 70 | description: 'A code change that improves performance', 71 | title: 'Performance Improvements', 72 | emoji: '🚀', 73 | }, 74 | test: { 75 | description: 'Adding missing tests or correcting existing tests', 76 | title: 'Tests', 77 | emoji: '🚨', 78 | }, 79 | tests: { 80 | description: 'Adding missing tests or correcting existing tests', 81 | title: 'Tests', 82 | emoji: '🚨', 83 | }, 84 | build: { 85 | description: 86 | 'Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)', 87 | title: 'Builds', 88 | emoji: '🛠', 89 | }, 90 | ci: { 91 | description: 92 | 'Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)', 93 | title: 'Continuous Integrations', 94 | emoji: '⚙️', 95 | }, 96 | chore: { 97 | description: "Other changes that don't modify src or test files", 98 | title: 'Chores', 99 | emoji: '♻️', 100 | }, 101 | revert: { 102 | description: 'Reverts a previous commit', 103 | title: 'Reverts', 104 | emoji: '🗑', 105 | }, 106 | }, 107 | }, 108 | scope: { 109 | description: 110 | 'What is the scope of this change (e.g. component or file name)', 111 | }, 112 | subject: { 113 | description: 114 | 'Write a short, imperative tense description of the change', 115 | }, 116 | body: { 117 | description: 'Provide a longer description of the change', 118 | }, 119 | isBreaking: { 120 | description: 'Are there any breaking changes?', 121 | }, 122 | breakingBody: { 123 | description: 124 | 'A BREAKING CHANGE commit requires a body. Please enter a longer description of the commit itself', 125 | }, 126 | breaking: { 127 | description: 'Describe the breaking changes', 128 | }, 129 | isIssueAffected: { 130 | description: 'Does this change affect any open issues?', 131 | }, 132 | issuesBody: { 133 | description: 134 | 'If issues are closed, the commit requires a body. Please enter a longer description of the commit itself', 135 | }, 136 | issues: { 137 | description: 'Add issue references (e.g. "fix #123", "re #123".)', 138 | }, 139 | }, 140 | }, 141 | }; 142 | -------------------------------------------------------------------------------- /.fmf/version: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | updates: 4 | - package-ecosystem: github-actions 5 | directory: / 6 | schedule: 7 | interval: monthly 8 | commit-message: 9 | prefix: ci 10 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Enhancement: 2 | 3 | Reason: 4 | 5 | Result: 6 | 7 | Issue Tracker Tickets (Jira or BZ if any): 8 | -------------------------------------------------------------------------------- /.github/workflows/ansible-lint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Ansible Lint 3 | on: # yamllint disable-line rule:truthy 4 | pull_request: 5 | merge_group: 6 | branches: 7 | - main 8 | types: 9 | - checks_requested 10 | push: 11 | branches: 12 | - main 13 | workflow_dispatch: 14 | env: 15 | LSR_ROLE2COLL_NAMESPACE: fedora 16 | LSR_ROLE2COLL_NAME: linux_system_roles 17 | permissions: 18 | contents: read 19 | jobs: 20 | ansible_lint: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: Update pip, git 24 | run: | 25 | set -euxo pipefail 26 | sudo apt update 27 | sudo apt install -y git 28 | 29 | - name: Checkout repo 30 | uses: actions/checkout@v4 31 | 32 | - name: Install tox, tox-lsr 33 | run: | 34 | set -euxo pipefail 35 | pip3 install "git+https://github.com/linux-system-roles/tox-lsr@3.11.0" 36 | 37 | - name: Convert role to collection format 38 | id: collection 39 | run: | 40 | set -euxo pipefail 41 | TOXENV=collection lsr_ci_runtox 42 | coll_dir=".tox/ansible_collections/$LSR_ROLE2COLL_NAMESPACE/$LSR_ROLE2COLL_NAME" 43 | # cleanup after collection conversion 44 | rm -rf "$coll_dir/.ansible" .tox/ansible-plugin-scan 45 | # ansible-lint action requires a .git directory??? 46 | # https://github.com/ansible/ansible-lint/blob/main/action.yml#L45 47 | mkdir -p "$coll_dir/.git" 48 | meta_req_file="${{ github.workspace }}/meta/collection-requirements.yml" 49 | test_req_file="${{ github.workspace }}/tests/collection-requirements.yml" 50 | if [ -f "$meta_req_file" ] && [ -f "$test_req_file" ]; then 51 | coll_req_file="${{ github.workspace }}/req.yml" 52 | python -c 'import sys; import yaml 53 | hsh1 = yaml.safe_load(open(sys.argv[1])) 54 | hsh2 = yaml.safe_load(open(sys.argv[2])) 55 | coll = {} 56 | for item in hsh1["collections"] + hsh2["collections"]: 57 | if isinstance(item, dict): 58 | name = item["name"] 59 | rec = item 60 | else: 61 | name = item # assume string 62 | rec = {"name": name} 63 | if name not in coll: 64 | coll[name] = rec 65 | hsh1["collections"] = list(coll.values()) 66 | yaml.safe_dump(hsh1, open(sys.argv[3], "w"))' "$meta_req_file" "$test_req_file" "$coll_req_file" 67 | echo merged "$coll_req_file" 68 | cat "$coll_req_file" 69 | elif [ -f "$meta_req_file" ]; then 70 | coll_req_file="$meta_req_file" 71 | elif [ -f "$test_req_file" ]; then 72 | coll_req_file="$test_req_file" 73 | else 74 | coll_req_file="" 75 | fi 76 | echo "coll_req_file=$coll_req_file" >> $GITHUB_OUTPUT 77 | 78 | - name: Run ansible-lint 79 | uses: ansible/ansible-lint@v25 80 | with: 81 | working_directory: ${{ github.workspace }}/.tox/ansible_collections/${{ env.LSR_ROLE2COLL_NAMESPACE }}/${{ env.LSR_ROLE2COLL_NAME }} 82 | requirements_file: ${{ steps.collection.outputs.coll_req_file }} 83 | env: 84 | ANSIBLE_COLLECTIONS_PATH: ${{ github.workspace }}/.tox 85 | -------------------------------------------------------------------------------- /.github/workflows/ansible-managed-var-comment.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Check for ansible_managed variable use in comments 3 | on: # yamllint disable-line rule:truthy 4 | pull_request: 5 | merge_group: 6 | branches: 7 | - main 8 | types: 9 | - checks_requested 10 | push: 11 | branches: 12 | - main 13 | workflow_dispatch: 14 | permissions: 15 | contents: read 16 | jobs: 17 | ansible_managed_var_comment: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Update pip, git 21 | run: | 22 | set -euxo pipefail 23 | python3 -m pip install --upgrade pip 24 | sudo apt update 25 | sudo apt install -y git 26 | 27 | - name: Checkout repo 28 | uses: actions/checkout@v4 29 | 30 | - name: Install tox, tox-lsr 31 | run: | 32 | set -euxo pipefail 33 | pip3 install "git+https://github.com/linux-system-roles/tox-lsr@3.11.0" 34 | 35 | - name: Run ansible-plugin-scan 36 | run: | 37 | set -euxo pipefail 38 | TOXENV=ansible-managed-var-comment lsr_ci_runtox 39 | -------------------------------------------------------------------------------- /.github/workflows/ansible-test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Ansible Test 3 | on: # yamllint disable-line rule:truthy 4 | pull_request: 5 | merge_group: 6 | branches: 7 | - main 8 | types: 9 | - checks_requested 10 | push: 11 | branches: 12 | - main 13 | workflow_dispatch: 14 | env: 15 | LSR_ROLE2COLL_NAMESPACE: fedora 16 | LSR_ROLE2COLL_NAME: linux_system_roles 17 | permissions: 18 | contents: read 19 | jobs: 20 | ansible_test: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: Update pip, git 24 | run: | 25 | set -euxo pipefail 26 | python3 -m pip install --upgrade pip 27 | sudo apt update 28 | sudo apt install -y git 29 | 30 | - name: Checkout repo 31 | uses: actions/checkout@v4 32 | 33 | - name: Install tox, tox-lsr 34 | run: | 35 | set -euxo pipefail 36 | pip3 install "git+https://github.com/linux-system-roles/tox-lsr@3.11.0" 37 | 38 | - name: Convert role to collection format 39 | run: | 40 | set -euxo pipefail 41 | TOXENV=collection lsr_ci_runtox 42 | 43 | - name: Run ansible-test 44 | uses: ansible-community/ansible-test-gh-action@release/v1 45 | with: 46 | testing-type: sanity # wokeignore:rule=sanity 47 | ansible-core-version: stable-2.17 48 | collection-src-directory: ${{ github.workspace }}/.tox/ansible_collections/${{ env.LSR_ROLE2COLL_NAMESPACE }}/${{ env.LSR_ROLE2COLL_NAME }} 49 | -------------------------------------------------------------------------------- /.github/workflows/build_docs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # yamllint disable rule:line-length 3 | name: Convert README.md to HTML and push to docs branch 4 | on: # yamllint disable-line rule:truthy 5 | push: 6 | branches: 7 | - main 8 | paths: 9 | - README.md 10 | release: 11 | types: 12 | - published 13 | permissions: 14 | contents: read 15 | jobs: 16 | build_docs: 17 | runs-on: ubuntu-latest 18 | permissions: 19 | contents: write 20 | steps: 21 | - name: Update pip, git 22 | run: | 23 | set -euxo pipefail 24 | sudo apt update 25 | sudo apt install -y git 26 | 27 | - name: Check out code 28 | uses: actions/checkout@v4 29 | with: 30 | fetch-depth: 0 31 | - name: Ensure the docs branch 32 | run: | 33 | set -euxo pipefail 34 | branch=docs 35 | existed_in_remote=$(git ls-remote --heads origin $branch) 36 | 37 | if [ -z "${existed_in_remote}" ]; then 38 | echo "Creating $branch branch" 39 | git config --global user.name "${{ github.actor }}" 40 | git config --global user.email "${{ github.actor }}@users.noreply.github.com" 41 | git checkout --orphan $branch 42 | git reset --hard 43 | git commit --allow-empty -m "Initializing $branch branch" 44 | git push origin $branch 45 | echo "Created $branch branch" 46 | else 47 | echo "Branch $branch already exists" 48 | fi 49 | 50 | - name: Checkout the docs branch 51 | uses: actions/checkout@v4 52 | with: 53 | ref: docs 54 | 55 | - name: Fetch README.md and .pandoc_template.html5 template from the workflow branch 56 | uses: actions/checkout@v4 57 | with: 58 | sparse-checkout: | 59 | README.md 60 | .pandoc_template.html5 61 | sparse-checkout-cone-mode: false 62 | path: ref_branch 63 | - name: Set RELEASE_VERSION based on whether run on release or on push 64 | run: | 65 | set -euxo pipefail 66 | if [ ${{ github.event_name }} = release ]; then 67 | echo "RELEASE_VERSION=${{ github.event.release.tag_name }}" >> $GITHUB_ENV 68 | elif [ ${{ github.event_name }} = push ]; then 69 | echo "RELEASE_VERSION=latest" >> $GITHUB_ENV 70 | else 71 | echo Unsupported event 72 | exit 1 73 | fi 74 | 75 | - name: Ensure that version and docs directories exist 76 | run: mkdir -p ${{ env.RELEASE_VERSION }} docs 77 | 78 | - name: Remove badges from README.md prior to converting to HTML 79 | run: sed -i '1,8 {/^\[\!.*actions\/workflows/d}' ref_branch/README.md 80 | 81 | - name: Convert README.md to HTML and save to the version directory 82 | uses: docker://pandoc/core:latest 83 | with: 84 | args: >- 85 | --from gfm --to html5 --toc --shift-heading-level-by=-1 86 | --template ref_branch/.pandoc_template.html5 87 | --output ${{ env.RELEASE_VERSION }}/README.html ref_branch/README.md 88 | 89 | - name: Copy latest README.html to docs/index.html for GitHub pages 90 | if: env.RELEASE_VERSION == 'latest' 91 | run: cp ${{ env.RELEASE_VERSION }}/README.html docs/index.html 92 | 93 | - name: Upload README.html as an artifact 94 | uses: actions/upload-artifact@master 95 | with: 96 | name: README.html 97 | path: ${{ env.RELEASE_VERSION }}/README.html 98 | 99 | - name: Commit changes 100 | run: | 101 | git config --global user.name "${{ github.actor }}" 102 | git config --global user.email "${{ github.actor }}@users.noreply.github.com" 103 | git add ${{ env.RELEASE_VERSION }}/README.html docs/index.html 104 | git commit -m "Update README.html for ${{ env.RELEASE_VERSION }}" 105 | 106 | - name: Push changes 107 | uses: ad-m/github-push-action@master 108 | with: 109 | github_token: ${{ secrets.GITHUB_TOKEN }} 110 | branch: docs 111 | -------------------------------------------------------------------------------- /.github/workflows/changelog_to_tag.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # yamllint disable rule:line-length 3 | name: Tag, release, and publish role based on CHANGELOG.md push 4 | on: # yamllint disable-line rule:truthy 5 | push: 6 | branches: 7 | - main 8 | paths: 9 | - CHANGELOG.md 10 | permissions: 11 | contents: read 12 | jobs: 13 | tag_release_publish: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: write 17 | steps: 18 | - name: Update pip, git 19 | run: | 20 | set -euxo pipefail 21 | sudo apt update 22 | sudo apt install -y git 23 | 24 | - name: checkout PR 25 | uses: actions/checkout@v4 26 | 27 | - name: Get tag and message from the latest CHANGELOG.md commit 28 | id: tag 29 | run: | 30 | set -euxo pipefail 31 | print=false 32 | while read -r line; do 33 | if [[ "$line" =~ ^\[([0-9]+\.[0-9]+\.[0-9]+)\]\ -\ [0-9-]+ ]]; then 34 | if [ "$print" = false ]; then 35 | _tagname="${BASH_REMATCH[1]}" 36 | echo "$line" 37 | print=true 38 | else 39 | break 40 | fi 41 | elif [ "$print" = true ]; then 42 | echo "$line" 43 | fi 44 | done < CHANGELOG.md > ./.tagmsg.txt 45 | git fetch --all --tags 46 | for t in $( git tag -l ); do 47 | if [ "$t" = "$_tagname" ]; then 48 | echo INFO: tag "$t" already exists 49 | exit 1 50 | fi 51 | done 52 | # Get name of the branch that the change was pushed to 53 | _branch="${GITHUB_REF_NAME:-}" 54 | if [ "$_branch" = master ] || [ "$_branch" = main ]; then 55 | echo Using branch name ["$_branch"] as push branch 56 | else 57 | echo WARNING: GITHUB_REF_NAME ["$_branch"] is not main or master 58 | _branch=$( git branch -r | grep -o 'origin/HEAD -> origin/.*$' | \ 59 | awk -F'/' '{print $3}' || : ) 60 | fi 61 | if [ -z "$_branch" ]; then 62 | _branch=$( git branch --points-at HEAD --no-color --format='%(refname:short)' ) 63 | fi 64 | if [ -z "$_branch" ]; then 65 | echo ERROR: unable to determine push branch 66 | git branch -a 67 | exit 1 68 | fi 69 | echo "tagname=$_tagname" >> "$GITHUB_OUTPUT" 70 | echo "branch=$_branch" >> "$GITHUB_OUTPUT" 71 | - name: Create tag 72 | uses: mathieudutour/github-tag-action@v6.2 73 | with: 74 | github_token: ${{ secrets.GITHUB_TOKEN }} 75 | custom_tag: ${{ steps.tag.outputs.tagname }} 76 | tag_prefix: '' 77 | 78 | - name: Create Release 79 | id: create_release 80 | uses: ncipollo/release-action@v1 81 | with: 82 | tag: ${{ steps.tag.outputs.tagname }} 83 | name: Version ${{ steps.tag.outputs.tagname }} 84 | bodyFile: ./.tagmsg.txt 85 | makeLatest: true 86 | 87 | - name: Publish role to Galaxy 88 | uses: robertdebock/galaxy-action@1.2.1 89 | with: 90 | galaxy_api_key: ${{ secrets.galaxy_api_key }} 91 | git_branch: ${{ steps.tag.outputs.branch }} 92 | -------------------------------------------------------------------------------- /.github/workflows/codespell.yml: -------------------------------------------------------------------------------- 1 | # Codespell configuration is within .codespellrc 2 | --- 3 | name: Codespell 4 | on: # yamllint disable-line rule:truthy 5 | - pull_request 6 | permissions: 7 | contents: read 8 | jobs: 9 | codespell: 10 | name: Check for spelling errors 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | 16 | - name: Codespell 17 | uses: codespell-project/actions-codespell@v2 18 | -------------------------------------------------------------------------------- /.github/workflows/markdownlint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # yamllint disable rule:line-length 3 | name: Markdown Lint 4 | on: # yamllint disable-line rule:truthy 5 | pull_request: 6 | merge_group: 7 | branches: 8 | - main 9 | types: 10 | - checks_requested 11 | push: 12 | branches: 13 | - main 14 | workflow_dispatch: 15 | permissions: 16 | contents: read 17 | jobs: 18 | markdownlint: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Update pip, git 22 | run: | 23 | set -euxo pipefail 24 | sudo apt update 25 | sudo apt install -y git 26 | 27 | - name: Check out code 28 | uses: actions/checkout@v4 29 | 30 | # CHANGELOG.md is generated automatically from PR titles and descriptions 31 | # It might have issues but they are not critical 32 | - name: Lint all markdown files except for CHANGELOG.md 33 | uses: docker://avtodev/markdown-lint:master 34 | with: 35 | args: >- 36 | --ignore=CHANGELOG.md 37 | **/*.md 38 | config: .markdownlint.yaml 39 | -------------------------------------------------------------------------------- /.github/workflows/pr-title-lint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: PR Title Lint 3 | on: # yamllint disable-line rule:truthy 4 | pull_request: 5 | types: 6 | - opened 7 | - synchronize 8 | - reopened 9 | - edited 10 | merge_group: 11 | branches: 12 | - main 13 | types: 14 | - checks_requested 15 | permissions: 16 | contents: read 17 | jobs: 18 | commit-checks: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v4 22 | with: 23 | fetch-depth: 0 24 | 25 | - name: Install conventional-commit linter 26 | run: npm install @commitlint/config-conventional @commitlint/cli 27 | 28 | - name: Run commitlint on PR title 29 | env: 30 | PR_TITLE: ${{ github.event.pull_request.title }} 31 | # Echo from env variable to avoid bash errors with extra characters 32 | run: echo "$PR_TITLE" | npx commitlint --verbose 33 | -------------------------------------------------------------------------------- /.github/workflows/qemu-kvm-integration-tests.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test 3 | on: # yamllint disable-line rule:truthy 4 | pull_request: 5 | merge_group: 6 | branches: 7 | - main 8 | types: 9 | - checks_requested 10 | push: 11 | branches: 12 | - main 13 | workflow_dispatch: 14 | 15 | permissions: 16 | contents: read 17 | # This is required for the ability to create/update the Pull request status 18 | statuses: write 19 | jobs: 20 | scenario: 21 | runs-on: ubuntu-latest 22 | 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | scenario: 27 | # QEMU 28 | - { image: "centos-9", env: "qemu-ansible-core-2.16" } 29 | - { image: "centos-10", env: "qemu-ansible-core-2.17" } 30 | # ansible/libdnf5 bug: https://issues.redhat.com/browse/RHELMISC-10110 31 | # - { image: "fedora-41", env: "qemu-ansible-core-2.17" } 32 | - { image: "fedora-42", env: "qemu-ansible-core-2.19" } 33 | 34 | # container 35 | - { image: "centos-9", env: "container-ansible-core-2.16" } 36 | - { image: "centos-9-bootc", env: "container-ansible-core-2.16" } 37 | # broken on non-running dbus 38 | # - { image: "centos-10", env: "container-ansible-core-2.17" } 39 | - { image: "centos-10-bootc", env: "container-ansible-core-2.17" } 40 | - { image: "fedora-41", env: "container-ansible-core-2.17" } 41 | - { image: "fedora-42", env: "container-ansible-core-2.17" } 42 | - { image: "fedora-41-bootc", env: "container-ansible-core-2.17" } 43 | - { image: "fedora-42-bootc", env: "container-ansible-core-2.17" } 44 | 45 | env: 46 | TOX_ARGS: "--skip-tags tests::infiniband,tests::nvme,tests::scsi" 47 | 48 | steps: 49 | - name: Checkout repo 50 | uses: actions/checkout@v4 51 | 52 | - name: Check if platform is supported 53 | id: check_platform 54 | run: | 55 | set -euxo pipefail 56 | image="${{ matrix.scenario.image }}" 57 | image="${image%-bootc}" 58 | 59 | # convert image to tag formats 60 | platform= 61 | platform_version= 62 | case "$image" in 63 | centos-*) platform=el; platform_version=el"${image#centos-}" ;; 64 | fedora-*) platform=fedora; platform_version="${image/-/}" ;; 65 | esac 66 | supported= 67 | if yq -e '.galaxy_info.galaxy_tags[] | select(. == "'${platform_version}'" or . == "'${platform}'")' meta/main.yml; then 68 | supported=true 69 | fi 70 | 71 | # bootc build support (in buildah) has a separate flag 72 | if [ "${{ matrix.scenario.image }}" != "$image" ]; then 73 | if ! yq -e '.galaxy_info.galaxy_tags[] | select(. == "containerbuild")' meta/main.yml; then 74 | supported= 75 | fi 76 | else 77 | # roles need to opt into support for running in a system container 78 | env="${{ matrix.scenario.env }}" 79 | if [ "${env#container}" != "$env" ] && 80 | ! yq -e '.galaxy_info.galaxy_tags[] | select(. == "container")' meta/main.yml; then 81 | supported= 82 | fi 83 | fi 84 | 85 | echo "supported=$supported" >> "$GITHUB_OUTPUT" 86 | 87 | - name: Set up /dev/kvm 88 | if: steps.check_platform.outputs.supported 89 | run: | 90 | echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm.rules 91 | sudo udevadm control --reload-rules 92 | sudo udevadm trigger --name-match=kvm --settle 93 | ls -l /dev/kvm 94 | 95 | - name: Disable man-db to speed up package install 96 | if: steps.check_platform.outputs.supported 97 | run: | 98 | echo "set man-db/auto-update false" | sudo debconf-communicate 99 | sudo dpkg-reconfigure man-db 100 | 101 | - name: Install test dependencies 102 | if: steps.check_platform.outputs.supported 103 | run: | 104 | set -euxo pipefail 105 | python3 -m pip install --upgrade pip 106 | sudo apt update 107 | sudo apt install -y --no-install-recommends git ansible-core genisoimage qemu-system-x86 108 | pip3 install "git+https://github.com/linux-system-roles/tox-lsr@3.11.0" 109 | 110 | # HACK: Drop this when moving this workflow to 26.04 LTS 111 | - name: Update podman to 5.x for compatibility with bootc-image-builder's podman 5 112 | if: steps.check_platform.outputs.supported && endsWith(matrix.scenario.image, '-bootc') 113 | run: | 114 | sed 's/noble/plucky/g' /etc/apt/sources.list.d/ubuntu.sources | sudo tee /etc/apt/sources.list.d/plucky.sources >/dev/null 115 | cat </dev/null 116 | Package: podman buildah golang-github-containers-common crun libgpgme11t64 libgpg-error0 golang-github-containers-image catatonit conmon containers-storage 117 | Pin: release n=plucky 118 | Pin-Priority: 991 119 | 120 | Package: libsubid4 netavark passt aardvark-dns containernetworking-plugins libslirp0 slirp4netns 121 | Pin: release n=plucky 122 | Pin-Priority: 991 123 | 124 | Package: * 125 | Pin: release n=plucky 126 | Pin-Priority: 400 127 | EOF 128 | 129 | sudo apt update 130 | sudo apt install -y podman crun conmon containers-storage 131 | 132 | - name: Configure tox-lsr 133 | if: steps.check_platform.outputs.supported 134 | run: >- 135 | curl -o ~/.config/linux-system-roles.json 136 | https://raw.githubusercontent.com/linux-system-roles/linux-system-roles.github.io/master/download/linux-system-roles.json 137 | 138 | - name: Run qemu integration tests 139 | if: steps.check_platform.outputs.supported && startsWith(matrix.scenario.env, 'qemu') 140 | run: >- 141 | tox -e ${{ matrix.scenario.env }} -- --image-name ${{ matrix.scenario.image }} --make-batch 142 | --log-level debug $TOX_ARGS --skip-tags tests::bootc-e2e 143 | --lsr-report-errors-url DEFAULT -- 144 | 145 | - name: Qemu result summary 146 | if: steps.check_platform.outputs.supported && startsWith(matrix.scenario.env, 'qemu') && always() 147 | run: | 148 | set -euo pipefail 149 | # some platforms may have setup/cleanup playbooks - need to find the 150 | # actual test playbook that starts with tests_ 151 | while read code start end test_files; do 152 | for f in $test_files; do 153 | test_file="$f" 154 | f="$(basename $test_file)" 155 | if [[ "$f" =~ ^tests_ ]]; then 156 | break 157 | fi 158 | done 159 | if [ "$code" = "0" ]; then 160 | echo -n "PASS: " 161 | mv "$test_file.log" "${test_file}-SUCCESS.log" 162 | else 163 | echo -n "FAIL: " 164 | mv "$test_file.log" "${test_file}-FAIL.log" 165 | fi 166 | echo "$f" 167 | done < batch.report 168 | 169 | - name: Run container tox integration tests 170 | if: steps.check_platform.outputs.supported && startsWith(matrix.scenario.env, 'container') 171 | run: | 172 | set -euo pipefail 173 | # HACK: debug.py/profile.py setup is broken 174 | export LSR_CONTAINER_PROFILE=false 175 | export LSR_CONTAINER_PRETTY=false 176 | rc=0 177 | for t in tests/tests_*.yml; do 178 | if tox -e ${{ matrix.scenario.env }} -- --image-name ${{ matrix.scenario.image }} $t > ${t}.log 2>&1; then 179 | echo "PASS: $(basename $t)" 180 | mv "${t}.log" "${t}-SUCCESS.log" 181 | else 182 | echo "FAIL: $(basename $t)" 183 | mv "${t}.log" "${t}-FAIL.log" 184 | rc=1 185 | fi 186 | done 187 | exit $rc 188 | 189 | - name: Run bootc validation tests in QEMU 190 | if: steps.check_platform.outputs.supported && 191 | startsWith(matrix.scenario.env, 'container') && 192 | endsWith(matrix.scenario.image, '-bootc') 193 | run: | 194 | set -euxo pipefail 195 | env=$(echo "${{ matrix.scenario.env }}" | sed 's/^container-/qemu-/') 196 | 197 | for image_file in $(ls tests/tmp/*/qcow2/disk.qcow2 2>/dev/null); do 198 | test="tests/$(basename $(dirname $(dirname $image_file))).yml" 199 | if tox -e "$env" -- --image-file "$(pwd)/$image_file" \ 200 | --log-level debug $TOX_ARGS \ 201 | --lsr-report-errors-url DEFAULT \ 202 | -e __bootc_validation=true \ 203 | -- "$test" >out 2>&1; then 204 | mv out "${test}-PASS.log" 205 | else 206 | mv out "${test}-FAIL.log" 207 | exit 1 208 | fi 209 | done 210 | 211 | - name: Upload test logs on failure 212 | if: failure() 213 | uses: actions/upload-artifact@v4 214 | with: 215 | name: "logs-${{ matrix.scenario.image }}-${{ matrix.scenario.env }}" 216 | path: | 217 | tests/*.log 218 | artifacts/default_provisioners.log 219 | artifacts/*.qcow2.*.log 220 | batch.txt 221 | batch.report 222 | retention-days: 30 223 | 224 | - name: Show test log failures 225 | if: steps.check_platform.outputs.supported && failure() 226 | run: | 227 | set -euo pipefail 228 | # grab check_logs.py script 229 | curl -s -L -o check_logs.py https://raw.githubusercontent.com/linux-system-roles/auto-maintenance/refs/heads/main/check_logs.py 230 | chmod +x check_logs.py 231 | declare -a cmdline=(./check_logs.py --github-action-format) 232 | for log in tests/*-FAIL.log; do 233 | cmdline+=(--lsr-error-log "$log") 234 | done 235 | "${cmdline[@]}" 236 | 237 | - name: Set commit status as success with a description that platform is skipped 238 | if: ${{ steps.check_platform.outputs.supported == '' }} 239 | uses: myrotvorets/set-commit-status-action@master 240 | with: 241 | status: success 242 | context: "${{ github.workflow }} / scenario (${{ matrix.scenario.image }}, ${{ matrix.scenario.env }}) (pull_request)" 243 | description: The role does not support this platform. Skipping. 244 | targetUrl: "" 245 | -------------------------------------------------------------------------------- /.github/workflows/shellcheck.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: ShellCheck 3 | on: # yamllint disable-line rule:truthy 4 | pull_request: 5 | merge_group: 6 | branches: 7 | - main 8 | types: 9 | - checks_requested 10 | push: 11 | branches: 12 | - main 13 | workflow_dispatch: 14 | env: 15 | # some scripts source tox-lsr scripts - suppress that check 16 | SHELLCHECK_OPTS: -e SC1091 17 | permissions: 18 | contents: read 19 | jobs: 20 | shellcheck: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: Update git 24 | run: | 25 | set -euxo pipefail 26 | sudo apt update 27 | sudo apt install -y git 28 | 29 | - name: Checkout repo 30 | uses: actions/checkout@v4 31 | 32 | - name: Run ShellCheck 33 | id: shellcheck_id 34 | uses: ludeeus/action-shellcheck@master 35 | 36 | - name: Show file paths scanned 37 | run: | 38 | echo Files scanned: 39 | echo "${{ steps.shellcheck_id.outputs.files }}" 40 | -------------------------------------------------------------------------------- /.github/workflows/test_converting_readme.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # yamllint disable rule:line-length 3 | name: Test converting README.md to README.html 4 | on: # yamllint disable-line rule:truthy 5 | pull_request: 6 | merge_group: 7 | branches: 8 | - main 9 | types: 10 | - checks_requested 11 | push: 12 | branches: 13 | - main 14 | permissions: 15 | contents: read 16 | jobs: 17 | test_converting_readme: 18 | runs-on: ubuntu-latest 19 | permissions: 20 | contents: write 21 | steps: 22 | - name: Update pip, git 23 | run: | 24 | set -euxo pipefail 25 | sudo apt update 26 | sudo apt install -y git 27 | 28 | - name: Check out code 29 | uses: actions/checkout@v4 30 | 31 | - name: Remove badges from README.md prior to converting to HTML 32 | run: sed -i '1,8 {/^\[\!.*actions\/workflows/d}' README.md 33 | 34 | - name: Convert README.md to HTML 35 | uses: docker://pandoc/core:latest 36 | with: 37 | args: >- 38 | --from gfm --to html5 --toc --shift-heading-level-by=-1 39 | --template .pandoc_template.html5 40 | --output README.html README.md 41 | 42 | - name: Upload README.html as an artifact 43 | uses: actions/upload-artifact@master 44 | with: 45 | name: README.html 46 | path: README.html 47 | -------------------------------------------------------------------------------- /.github/workflows/tft.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Run integration tests in Testing Farm 3 | on: 4 | issue_comment: 5 | types: 6 | - created 7 | permissions: 8 | contents: read 9 | # This is required for the ability to create/update the Pull request status 10 | statuses: write 11 | jobs: 12 | prepare_vars: 13 | name: Get info from role and PR to determine if and how to test 14 | # The concurrency key is used to prevent multiple workflows from running at the same time 15 | concurrency: 16 | # group name contains reponame-pr_num to allow simualteneous runs in different PRs 17 | group: testing-farm-${{ github.event.repository.name }}-${{ github.event.issue.number }} 18 | cancel-in-progress: true 19 | # Let's schedule tests only on user request. NOT automatically. 20 | # Only repository owner or member can schedule tests 21 | if: | 22 | github.event.issue.pull_request 23 | && contains(github.event.comment.body, '[citest]') 24 | && (contains(fromJson('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) 25 | || contains('systemroller', github.event.comment.user.login)) 26 | runs-on: ubuntu-latest 27 | outputs: 28 | supported_platforms: ${{ steps.supported_platforms.outputs.supported_platforms }} 29 | head_sha: ${{ steps.head_sha.outputs.head_sha }} 30 | memory: ${{ steps.memory.outputs.memory }} 31 | steps: 32 | - name: Dump github context 33 | run: echo "$GITHUB_CONTEXT" 34 | shell: bash 35 | env: 36 | GITHUB_CONTEXT: ${{ toJson(github) }} 37 | 38 | - name: Checkout repo 39 | uses: actions/checkout@v4 40 | 41 | - name: Get head sha of the PR 42 | id: head_sha 43 | run: | 44 | head_sha=$(gh api "repos/$REPO/pulls/$PR_NO" --jq '.head.sha') 45 | echo "head_sha=$head_sha" >> $GITHUB_OUTPUT 46 | env: 47 | REPO: ${{ github.repository }} 48 | PR_NO: ${{ github.event.issue.number }} 49 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 50 | 51 | - name: Checkout PR 52 | uses: actions/checkout@v4 53 | with: 54 | ref: ${{ steps.head_sha.outputs.head_sha }} 55 | 56 | - name: Get memory 57 | id: memory 58 | run: | 59 | if [ -d tests/provision.fmf ]; then 60 | memory=$(grep -rPo ' m: \K(.*)' tests/provision.fmf) 61 | fi 62 | if [ -n "$memory" ]; then 63 | echo "memory=$memory" >> $GITHUB_OUTPUT 64 | else 65 | echo "memory=2048" >> $GITHUB_OUTPUT 66 | fi 67 | 68 | - name: Get supported platforms 69 | id: supported_platforms 70 | run: | 71 | supported_platforms="" 72 | meta_main=meta/main.yml 73 | # All Fedora are supported, add latest Fedora versions to supported_platforms 74 | if yq '.galaxy_info.galaxy_tags[]' "$meta_main" | grep -qi fedora$; then 75 | supported_platforms+=" Fedora-41" 76 | supported_platforms+=" Fedora-42" 77 | fi 78 | # Specific Fedora versions supported 79 | if yq '.galaxy_info.galaxy_tags[]' "$meta_main" | grep -qiP 'fedora\d+$'; then 80 | for fedora_ver in $(yq '.galaxy_info.galaxy_tags[]' "$meta_main" | grep -iPo 'fedora\K(\d+$)'); do 81 | supported_platforms+=" Fedora-$fedora_ver" 82 | done 83 | fi 84 | if yq '.galaxy_info.galaxy_tags[]' "$meta_main" | grep -qi el7; then 85 | supported_platforms+=" CentOS-7-latest" 86 | fi 87 | for ver in 8 9 10; do 88 | if yq '.galaxy_info.galaxy_tags[]' "$meta_main" | grep -qi el"$ver"; then 89 | supported_platforms+=" CentOS-Stream-$ver" 90 | fi 91 | done 92 | echo "supported_platforms=$supported_platforms" >> $GITHUB_OUTPUT 93 | 94 | testing-farm: 95 | name: ${{ matrix.platform }}/ansible-${{ matrix.ansible_version }} 96 | needs: prepare_vars 97 | strategy: 98 | fail-fast: false 99 | matrix: 100 | include: 101 | - platform: Fedora-41 102 | ansible_version: 2.17 103 | - platform: Fedora-42 104 | ansible_version: 2.19 105 | - platform: CentOS-7-latest 106 | ansible_version: 2.9 107 | - platform: CentOS-Stream-8 108 | ansible_version: 2.9 109 | # On CentOS-Stream-8, latest supported Ansible is 2.16 110 | - platform: CentOS-Stream-8 111 | ansible_version: 2.16 112 | - platform: CentOS-Stream-9 113 | ansible_version: 2.17 114 | - platform: CentOS-Stream-10 115 | ansible_version: 2.17 116 | runs-on: ubuntu-latest 117 | env: 118 | ARTIFACTS_DIR_NAME: "tf_${{ github.event.repository.name }}-${{ github.event.issue.number }}_\ 119 | ${{ matrix.platform }}-${{ matrix.ansible_version }}_\ 120 | ${{ needs.prepare_vars.outputs.datetime }}/artifacts" 121 | ARTIFACT_TARGET_DIR: /srv/pub/alt/${{ vars.SR_LSR_USER }}/logs 122 | steps: 123 | - name: Set variables with DATETIME and artifact location 124 | id: set_vars 125 | run: | 126 | printf -v DATETIME '%(%Y%m%d-%H%M%S)T' -1 127 | ARTIFACTS_DIR_NAME="tf_${{ github.event.repository.name }}-${{ github.event.issue.number }}_\ 128 | ${{ matrix.platform }}-${{ matrix.ansible_version }}_$DATETIME/artifacts" 129 | ARTIFACTS_TARGET_DIR=/srv/pub/alt/${{ vars.SR_LSR_USER }}/logs 130 | ARTIFACTS_DIR=$ARTIFACTS_TARGET_DIR/$ARTIFACTS_DIR_NAME 131 | ARTIFACTS_URL=https://dl.fedoraproject.org/pub/alt/${{ vars.SR_LSR_USER }}/logs/$ARTIFACTS_DIR_NAME 132 | echo "DATETIME=$DATETIME" >> $GITHUB_OUTPUT 133 | echo "ARTIFACTS_DIR=$ARTIFACTS_DIR" >> $GITHUB_OUTPUT 134 | echo "ARTIFACTS_URL=$ARTIFACTS_URL" >> $GITHUB_OUTPUT 135 | 136 | - name: Set commit status as pending 137 | if: contains(needs.prepare_vars.outputs.supported_platforms, matrix.platform) 138 | uses: myrotvorets/set-commit-status-action@master 139 | with: 140 | sha: ${{ needs.prepare_vars.outputs.head_sha }} 141 | status: pending 142 | context: ${{ matrix.platform }}|ansible-${{ matrix.ansible_version }} 143 | description: Test started 144 | targetUrl: "" 145 | 146 | - name: Set commit status as success with a description that platform is skipped 147 | if: "!contains(needs.prepare_vars.outputs.supported_platforms, matrix.platform)" 148 | uses: myrotvorets/set-commit-status-action@master 149 | with: 150 | sha: ${{ needs.prepare_vars.outputs.head_sha }} 151 | status: success 152 | context: ${{ matrix.platform }}|ansible-${{ matrix.ansible_version }} 153 | description: The role does not support this platform. Skipping. 154 | targetUrl: "" 155 | 156 | - name: Run test in testing farm 157 | uses: sclorg/testing-farm-as-github-action@v4 158 | if: contains(needs.prepare_vars.outputs.supported_platforms, matrix.platform) 159 | with: 160 | git_ref: main 161 | pipeline_settings: '{ "type": "tmt-multihost" }' 162 | environment_settings: '{ "provisioning": { "tags": { "BusinessUnit": "system_roles" } } }' 163 | # Keeping SR_ARTIFACTS_URL at the bottom makes the link in logs clickable 164 | variables: "SR_ANSIBLE_VER=${{ matrix.ansible_version }};\ 165 | SR_REPO_NAME=${{ github.event.repository.name }};\ 166 | SR_GITHUB_ORG=${{ github.repository_owner }};\ 167 | SR_PR_NUM=${{ github.event.issue.number }};\ 168 | SR_ARTIFACTS_DIR=${{ steps.set_vars.outputs.ARTIFACTS_DIR }};\ 169 | SR_TEST_LOCAL_CHANGES=false;\ 170 | SR_LSR_USER=${{ vars.SR_LSR_USER }};\ 171 | SR_ARTIFACTS_URL=${{ steps.set_vars.outputs.ARTIFACTS_URL }}" 172 | # Note that LINUXSYSTEMROLES_SSH_KEY must be single-line, TF doesn't read multi-line variables fine. 173 | secrets: "SR_LSR_DOMAIN=${{ secrets.SR_LSR_DOMAIN }};\ 174 | SR_LSR_SSH_KEY=${{ secrets.SR_LSR_SSH_KEY }}" 175 | compose: ${{ matrix.platform }} 176 | # There are two blockers for using public ranch: 177 | # 1. multihost is not supported in public https://github.com/teemtee/tmt/issues/2620 178 | # 2. Security issue that leaks long secrets - Jira TFT-2698 179 | tf_scope: private 180 | api_key: ${{ secrets.TF_API_KEY_RH }} 181 | update_pull_request_status: false 182 | tmt_plan_filter: "tag:playbooks_parallel,timesync" 183 | 184 | - name: Set final commit status 185 | uses: myrotvorets/set-commit-status-action@master 186 | if: always() && contains(needs.prepare_vars.outputs.supported_platforms, matrix.platform) 187 | with: 188 | sha: ${{ needs.prepare_vars.outputs.head_sha }} 189 | status: ${{ job.status }} 190 | context: ${{ matrix.platform }}|ansible-${{ matrix.ansible_version }} 191 | description: Test finished 192 | targetUrl: ${{ steps.set_vars.outputs.ARTIFACTS_URL }} 193 | -------------------------------------------------------------------------------- /.github/workflows/tft_citest_bad.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Re-run failed testing farm tests 3 | on: 4 | issue_comment: 5 | types: 6 | - created 7 | permissions: 8 | contents: read 9 | jobs: 10 | citest_bad_rerun: 11 | if: | 12 | github.event.issue.pull_request 13 | && contains(fromJson('["[citest_bad]", "[citest-bad]", "[citest bad]"]'), github.event.comment.body) 14 | && contains(fromJson('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) 15 | permissions: 16 | actions: write # for re-running failed jobs: https://docs.github.com/en/rest/actions/workflow-runs?apiVersion=2022-11-28#re-run-a-job-from-a-workflow-run 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Wait 10s until tft.yml workflow is created and skipped because new comment don't match [citest] 20 | run: sleep 10s 21 | 22 | - name: Re-run failed jobs for this PR 23 | env: 24 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 25 | REPO: ${{ github.repository }} 26 | PR_TITLE: ${{ github.event.issue.title }} 27 | run: | 28 | PENDING_RUN=$(gh api "repos/$REPO/actions/workflows/tft.yml/runs?event=issue_comment" \ 29 | | jq -r "[.workflow_runs[] | select( .display_title == \"$PR_TITLE\") | \ 30 | select(.status == \"pending\" or .status == \"queued\" or .status == \"in_progress\") | .id][0]") 31 | # if pending run don't exist, take the last run with failure state 32 | if [ "$PENDING_RUN" != "null" ]; then 33 | echo "The workflow $PENDING_RUN is still running, wait for it to finish to re-run" 34 | exit 1 35 | fi 36 | RUN_ID=$(gh api "repos/$REPO/actions/workflows/tft.yml/runs?event=issue_comment" \ 37 | | jq -r "[.workflow_runs[] | select( .display_title == \"$PR_TITLE\" ) | select( .conclusion == \"failure\" ) | .id][0]") 38 | if [ "$RUN_ID" = "null" ]; then 39 | echo "Failed workflow not found, exiting" 40 | exit 1 41 | fi 42 | echo "Re-running workflow $RUN_ID" 43 | gh api --method POST repos/$REPO/actions/runs/$RUN_ID/rerun-failed-jobs 44 | -------------------------------------------------------------------------------- /.github/workflows/weekly_ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # yamllint disable rule:line-length 3 | name: Weekly CI trigger 4 | on: # yamllint disable-line rule:truthy 5 | workflow_dispatch: 6 | schedule: 7 | - cron: 0 2 * * 0 8 | env: 9 | BRANCH_NAME: weekly-ci 10 | COMMIT_MESSAGE: "ci: This PR is to trigger periodic CI testing" 11 | BODY_MESSAGE: >- 12 | This PR is for the purpose of triggering periodic CI testing. 13 | We don't currently have a way to trigger CI without a PR, 14 | so this PR serves that purpose. 15 | COMMENT: "[citest]" 16 | permissions: 17 | contents: read 18 | jobs: 19 | weekly_ci: 20 | runs-on: ubuntu-latest 21 | permissions: 22 | issues: write 23 | pull-requests: write 24 | contents: write 25 | steps: 26 | - name: Update pip, git 27 | run: | 28 | set -euxo pipefail 29 | sudo apt update 30 | sudo apt install -y git 31 | 32 | - name: Checkout latest code 33 | uses: actions/checkout@v4 34 | with: 35 | fetch-depth: 0 36 | - name: Create or rebase commit, add dump_packages callback 37 | run: | 38 | set -euxo pipefail 39 | 40 | git config --global user.name "github-actions[bot]" 41 | git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" 42 | git checkout ${{ env.BRANCH_NAME }} || git checkout -b ${{ env.BRANCH_NAME }} 43 | git rebase main 44 | if [ ! -d tests/callback_plugins ]; then 45 | mkdir -p tests/callback_plugins 46 | fi 47 | curl -L -s -o tests/callback_plugins/dump_packages.py https://raw.githubusercontent.com/linux-system-roles/auto-maintenance/main/callback_plugins/dump_packages.py 48 | git add tests/callback_plugins 49 | git commit --allow-empty -m "${{ env.COMMIT_MESSAGE }}" 50 | git push -f --set-upstream origin ${{ env.BRANCH_NAME }} 51 | 52 | - name: Create and comment pull request 53 | uses: actions/github-script@v7 54 | with: 55 | github-token: ${{ secrets.GH_PUSH_TOKEN }} 56 | script: | 57 | const head = [context.repo.owner, ":", "${{ env.BRANCH_NAME }}"].join(""); 58 | const response = await github.rest.pulls.list({ 59 | owner: context.repo.owner, 60 | repo: context.repo.repo, 61 | head: head, 62 | base: context.ref, 63 | state: "open" 64 | }); 65 | let pr_number = ''; 66 | if (response.data.length === 0) { 67 | pr_number = (await github.rest.pulls.create({ 68 | owner: context.repo.owner, 69 | repo: context.repo.repo, 70 | title: "${{ env.COMMIT_MESSAGE }}", 71 | body: "${{ env.BODY_MESSAGE }}", 72 | head: "${{ env.BRANCH_NAME }}", 73 | base: context.ref, 74 | draft: true 75 | })).data.number; 76 | } else { 77 | pr_number = response.data[0].number; 78 | } 79 | github.rest.issues.createComment({ 80 | owner: context.repo.owner, 81 | repo: context.repo.repo, 82 | issue_number: pr_number, 83 | body: "${{ env.COMMENT }}", 84 | }); 85 | -------------------------------------------------------------------------------- /.github/workflows/woke.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # yamllint disable rule:line-length 3 | name: Woke 4 | on: # yamllint disable-line rule:truthy 5 | - pull_request 6 | jobs: 7 | woke: 8 | name: Detect non-inclusive language 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v4 13 | 14 | - name: Run lsr-woke-action 15 | # Originally, uses: get-woke/woke-action@v0 16 | uses: linux-system-roles/lsr-woke-action@main 17 | with: 18 | woke-args: "-c https://raw.githubusercontent.com/linux-system-roles/tox-lsr/main/src/tox_lsr/config_files/woke.yml --count-only-error-for-failure" 19 | # Cause the check to fail on any broke rules 20 | fail-on-error: true 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | passes.yml 2 | vault.yml 3 | *.pyc 4 | *.retry 5 | /tests/.coverage 6 | /tests/htmlcov* 7 | /.tox 8 | /venv*/ 9 | /.venv/ 10 | .vscode/ 11 | artifacts/ 12 | __pycache__/ 13 | *~ 14 | .pytest_cache/ 15 | -------------------------------------------------------------------------------- /.markdownlint.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Default state for all rules 3 | default: true 4 | 5 | # Path to configuration file to extend 6 | extends: null 7 | 8 | # MD001/heading-increment/header-increment - Heading levels should only increment by one level at a time 9 | MD001: true 10 | 11 | # MD002/first-heading-h1/first-header-h1 - First heading should be a top-level heading 12 | MD002: 13 | # Heading level 14 | level: 1 15 | 16 | # MD003/heading-style/header-style - Heading style 17 | MD003: 18 | # Heading style 19 | style: "consistent" 20 | 21 | # MD004/ul-style - Unordered list style 22 | MD004: 23 | # List style 24 | style: "consistent" 25 | 26 | # MD005/list-indent - Inconsistent indentation for list items at the same level 27 | MD005: true 28 | 29 | # MD006/ul-start-left - Consider starting bulleted lists at the beginning of the line 30 | MD006: true 31 | 32 | # MD007/ul-indent - Unordered list indentation 33 | MD007: 34 | # Spaces for indent 35 | indent: 2 36 | # Whether to indent the first level of the list 37 | start_indented: false 38 | # Spaces for first level indent (when start_indented is set) 39 | start_indent: 2 40 | 41 | # MD009/no-trailing-spaces - Trailing spaces 42 | MD009: 43 | # Spaces for line break 44 | br_spaces: 2 45 | # Allow spaces for empty lines in list items 46 | list_item_empty_lines: false 47 | # Include unnecessary breaks 48 | strict: false 49 | 50 | # MD010/no-hard-tabs - Hard tabs 51 | MD010: 52 | # Include code blocks 53 | code_blocks: true 54 | # Fenced code languages to ignore 55 | ignore_code_languages: [] 56 | # Number of spaces for each hard tab 57 | spaces_per_tab: 1 58 | 59 | # MD011/no-reversed-links - Reversed link syntax 60 | MD011: true 61 | 62 | # MD012/no-multiple-blanks - Multiple consecutive blank lines 63 | MD012: 64 | # Consecutive blank lines 65 | maximum: 1 66 | 67 | # Modified for LSR 68 | # GFM does not limit line length 69 | # MD013/line-length - Line length 70 | MD013: false 71 | # # Number of characters 72 | # # line_length: 80 73 | # line_length: 999 74 | # # Number of characters for headings 75 | # heading_line_length: 80 76 | # # Number of characters for code blocks 77 | # code_block_line_length: 80 78 | # # Include code blocks 79 | # code_blocks: true 80 | # # Include tables 81 | # tables: true 82 | # # Include headings 83 | # headings: true 84 | # # Include headings 85 | # headers: true 86 | # # Strict length checking 87 | # strict: false 88 | # # Stern length checking 89 | # stern: false 90 | 91 | # MD014/commands-show-output - Dollar signs used before commands without showing output 92 | MD014: true 93 | 94 | # MD018/no-missing-space-atx - No space after hash on atx style heading 95 | MD018: true 96 | 97 | # MD019/no-multiple-space-atx - Multiple spaces after hash on atx style heading 98 | MD019: true 99 | 100 | # MD020/no-missing-space-closed-atx - No space inside hashes on closed atx style heading 101 | MD020: true 102 | 103 | # MD021/no-multiple-space-closed-atx - Multiple spaces inside hashes on closed atx style heading 104 | MD021: true 105 | 106 | # MD022/blanks-around-headings/blanks-around-headers - Headings should be surrounded by blank lines 107 | MD022: 108 | # Blank lines above heading 109 | lines_above: 1 110 | # Blank lines below heading 111 | lines_below: 1 112 | 113 | # MD023/heading-start-left/header-start-left - Headings must start at the beginning of the line 114 | MD023: true 115 | 116 | # MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the same content 117 | MD024: true 118 | 119 | # MD025/single-title/single-h1 - Multiple top-level headings in the same document 120 | MD025: 121 | # Heading level 122 | level: 1 123 | # RegExp for matching title in front matter 124 | front_matter_title: "^\\s*title\\s*[:=]" 125 | 126 | # MD026/no-trailing-punctuation - Trailing punctuation in heading 127 | MD026: 128 | # Punctuation characters not allowed at end of headings 129 | punctuation: ".,;:!。,;:!" 130 | 131 | # MD027/no-multiple-space-blockquote - Multiple spaces after blockquote symbol 132 | MD027: true 133 | 134 | # MD028/no-blanks-blockquote - Blank line inside blockquote 135 | MD028: true 136 | 137 | # MD029/ol-prefix - Ordered list item prefix 138 | MD029: 139 | # List style 140 | style: "one_or_ordered" 141 | 142 | # MD030/list-marker-space - Spaces after list markers 143 | MD030: 144 | # Spaces for single-line unordered list items 145 | ul_single: 1 146 | # Spaces for single-line ordered list items 147 | ol_single: 1 148 | # Spaces for multi-line unordered list items 149 | ul_multi: 1 150 | # Spaces for multi-line ordered list items 151 | ol_multi: 1 152 | 153 | # MD031/blanks-around-fences - Fenced code blocks should be surrounded by blank lines 154 | MD031: 155 | # Include list items 156 | list_items: true 157 | 158 | # MD032/blanks-around-lists - Lists should be surrounded by blank lines 159 | MD032: true 160 | 161 | # MD033/no-inline-html - Inline HTML 162 | MD033: 163 | # Allowed elements 164 | allowed_elements: [] 165 | 166 | # MD034/no-bare-urls - Bare URL used 167 | MD034: true 168 | 169 | # MD035/hr-style - Horizontal rule style 170 | MD035: 171 | # Horizontal rule style 172 | style: "consistent" 173 | 174 | # MD036/no-emphasis-as-heading/no-emphasis-as-header - Emphasis used instead of a heading 175 | MD036: 176 | # Punctuation characters 177 | punctuation: ".,;:!?。,;:!?" 178 | 179 | # MD037/no-space-in-emphasis - Spaces inside emphasis markers 180 | MD037: true 181 | 182 | # MD038/no-space-in-code - Spaces inside code span elements 183 | MD038: true 184 | 185 | # MD039/no-space-in-links - Spaces inside link text 186 | MD039: true 187 | 188 | # MD040/fenced-code-language - Fenced code blocks should have a language specified 189 | MD040: 190 | # List of languages 191 | allowed_languages: [] 192 | # Require language only 193 | language_only: false 194 | 195 | # MD041/first-line-heading/first-line-h1 - First line in a file should be a top-level heading 196 | MD041: 197 | # Heading level 198 | level: 1 199 | # RegExp for matching title in front matter 200 | front_matter_title: "^\\s*title\\s*[:=]" 201 | 202 | # MD042/no-empty-links - No empty links 203 | MD042: true 204 | 205 | # Modified for LSR 206 | # Disabling, we do not need this 207 | # MD043/required-headings/required-headers - Required heading structure 208 | MD043: false 209 | # # List of headings 210 | # headings: [] 211 | # # List of headings 212 | # headers: [] 213 | # # Match case of headings 214 | # match_case: false 215 | 216 | # MD044/proper-names - Proper names should have the correct capitalization 217 | MD044: 218 | # List of proper names 219 | names: [] 220 | # Include code blocks 221 | code_blocks: true 222 | # Include HTML elements 223 | html_elements: true 224 | 225 | # MD045/no-alt-text - Images should have alternate text (alt text) 226 | MD045: true 227 | 228 | # MD046/code-block-style - Code block style 229 | MD046: 230 | # Block style 231 | style: "consistent" 232 | 233 | # MD047/single-trailing-newline - Files should end with a single newline character 234 | MD047: true 235 | 236 | # MD048/code-fence-style - Code fence style 237 | MD048: 238 | # Code fence style 239 | style: "consistent" 240 | 241 | # MD049/emphasis-style - Emphasis style should be consistent 242 | MD049: 243 | # Emphasis style should be consistent 244 | style: "consistent" 245 | 246 | # MD050/strong-style - Strong style should be consistent 247 | MD050: 248 | # Strong style should be consistent 249 | style: "consistent" 250 | 251 | # MD051/link-fragments - Link fragments should be valid 252 | MD051: true 253 | 254 | # MD052/reference-links-images - Reference links and images should use a label that is defined 255 | MD052: true 256 | 257 | # MD053/link-image-reference-definitions - Link and image reference definitions should be needed 258 | MD053: 259 | # Ignored definitions 260 | ignored_definitions: 261 | - "//" 262 | -------------------------------------------------------------------------------- /.ostree/README.md: -------------------------------------------------------------------------------- 1 | *NOTE*: The `*.txt` files are used by `get_ostree_data.sh` to create the lists 2 | of packages, and to find other system roles used by this role. DO NOT use them 3 | directly. 4 | -------------------------------------------------------------------------------- /.ostree/get_ostree_data.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | ostree_dir="${OSTREE_DIR:-"$(dirname "$(realpath "$0")")"}" 6 | 7 | if [ -z "${4:-}" ] || [ "${1:-}" = help ] || [ "${1:-}" = -h ]; then 8 | cat <&2 echo ERROR - could not find role "$role" - please use ANSIBLE_COLLECTIONS_PATH 64 | exit 2 65 | } 66 | 67 | get_packages() { 68 | local ostree_dir pkgtype pkgfile rolefile 69 | ostree_dir="$1" 70 | for pkgtype in "${pkgtypes[@]}"; do 71 | for suff in "" "-$distro" "-${distro}-${major_ver}" "-${distro}-${ver}"; do 72 | pkgfile="$ostree_dir/packages-${pkgtype}${suff}.txt" 73 | if [ -f "$pkgfile" ]; then 74 | cat "$pkgfile" 75 | fi 76 | done 77 | rolefile="$ostree_dir/roles-${pkgtype}.txt" 78 | if [ -f "$rolefile" ]; then 79 | local roles role rolepath 80 | roles="$(cat "$rolefile")" 81 | for role in $roles; do 82 | rolepath="$(get_rolepath "$ostree_dir" "$role")" 83 | if [ -z "$rolepath" ]; then 84 | 1>&2 echo ERROR - could not find role "$role" - please use ANSIBLE_COLLECTIONS_PATH 85 | exit 2 86 | fi 87 | get_packages "$rolepath" 88 | done 89 | fi 90 | done | sort -u 91 | } 92 | 93 | format_packages_json() { 94 | local comma pkgs pkg 95 | comma="" 96 | pkgs="[" 97 | while read -r pkg; do 98 | pkgs="${pkgs}${comma}\"${pkg}\"" 99 | comma=, 100 | done 101 | pkgs="${pkgs}]" 102 | echo "$pkgs" 103 | } 104 | 105 | format_packages_raw() { 106 | cat 107 | } 108 | 109 | format_packages_yaml() { 110 | while read -r pkg; do 111 | echo "- $pkg" 112 | done 113 | } 114 | 115 | format_packages_toml() { 116 | while read -r pkg; do 117 | echo "[[packages]]" 118 | echo "name = \"$pkg\"" 119 | echo "version = \"*\"" 120 | done 121 | } 122 | 123 | distro="${distro_ver%%-*}" 124 | ver="${distro_ver##*-}" 125 | if [[ "$ver" =~ ^([0-9]*) ]]; then 126 | major_ver="${BASH_REMATCH[1]}" 127 | else 128 | echo ERROR: cannot parse major version number from version "$ver" 129 | exit 1 130 | fi 131 | 132 | "get_$category" "$ostree_dir" | "format_${category}_$format" 133 | -------------------------------------------------------------------------------- /.ostree/packages-runtime-CentOS-7.txt: -------------------------------------------------------------------------------- 1 | ntp 2 | -------------------------------------------------------------------------------- /.ostree/packages-runtime-RedHat-6.txt: -------------------------------------------------------------------------------- 1 | ntp 2 | -------------------------------------------------------------------------------- /.ostree/packages-runtime-RedHat-7.txt: -------------------------------------------------------------------------------- 1 | ntp 2 | -------------------------------------------------------------------------------- /.ostree/packages-runtime.txt: -------------------------------------------------------------------------------- 1 | chrony 2 | linuxptp 3 | -------------------------------------------------------------------------------- /.ostree/packages-testing-CentOS-7.txt: -------------------------------------------------------------------------------- 1 | ntp 2 | -------------------------------------------------------------------------------- /.ostree/packages-testing-RedHat-6.txt: -------------------------------------------------------------------------------- 1 | ntp 2 | -------------------------------------------------------------------------------- /.ostree/packages-testing-RedHat-7.txt: -------------------------------------------------------------------------------- 1 | ntp 2 | -------------------------------------------------------------------------------- /.ostree/packages-testing.txt: -------------------------------------------------------------------------------- 1 | chrony 2 | ethtool 3 | iproute 4 | -------------------------------------------------------------------------------- /.pandoc_template.html5: -------------------------------------------------------------------------------- 1 | $--| GitHub HTML5 Pandoc Template" v2.2 | 2020/08/12 | pandoc v2.1.1 2 | 3 | 51 | $-------------------------------------------------------------------------> lang 52 | 53 | 54 | $--============================================================================= 55 | $-- METADATA 56 | $--============================================================================= 57 | 58 | 59 | 60 | $-----------------------------------------------------------------------> author 61 | $for(author-meta)$ 62 | 63 | $endfor$ 64 | $-------------------------------------------------------------------------> date 65 | $if(date-meta)$ 66 | 67 | $endif$ 68 | $---------------------------------------------------------------------> keywords 69 | $if(keywords)$ 70 | 71 | $endif$ 72 | $------------------------------------------------------------------> description 73 | $if(description)$ 74 | 75 | $endif$ 76 | $------------------------------------------------------------------------> title 77 | $if(title-prefix)$$title-prefix$ – $endif$$pagetitle$ 78 | $--=========================================================================== 79 | $-- CSS STYLESHEETS 80 | $--=========================================================================== 81 | $-- Here comes the placeholder (within double braces) that will be replaced 82 | $-- by the CSS file in the finalized template: 83 | 86 | $------------------------------------------------------------------------------- 87 | 88 | $------------------------------------------------------------------------------- 89 | $if(quotes)$ 90 | 91 | $endif$ 92 | $-------------------------------------------------------------> highlighting-css 93 | $if(highlighting-css)$ 94 | 97 | $endif$ 98 | $--------------------------------------------------------------------------> css 99 | $for(css)$ 100 | 101 | $endfor$ 102 | $-------------------------------------------------------------------------> math 103 | $if(math)$ 104 | $math$ 105 | $endif$ 106 | $------------------------------------------------------------------------------- 107 | 110 | $--------------------------------------------------------------> header-includes 111 | $for(header-includes)$ 112 | $header-includes$ 113 | $endfor$ 114 | $------------------------------------------------------------------------------- 115 | 116 | 117 |
118 | $---------------------------------------------------------------> include-before 119 | $for(include-before)$ 120 | $include-before$ 121 | $endfor$ 122 | $-->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> IF: title 123 | $if(title)$ 124 |
125 |

$title$

126 | $---------------------------------------------------------------------> subtitle 127 | $if(subtitle)$ 128 |

$subtitle$

129 | $endif$ 130 | $-----------------------------------------------------------------------> author 131 | $for(author)$ 132 |

$author$

133 | $endfor$ 134 | $-------------------------------------------------------------------------> date 135 | $if(date)$ 136 |

$date$

137 | $endif$ 138 | $----------------------------------------------------------------------> summary 139 | $if(summary)$ 140 |
141 | $summary$ 142 |
143 | $endif$ 144 | $------------------------------------------------------------------------------- 145 |
146 | $endif$ 147 | $--<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< END IF: title 148 | $--------------------------------------------------------------------------> toc 149 | $if(toc)$ 150 |
151 | 155 |
156 | $endif$ 157 | $-------------------------------------------------------------------------> body 158 | $body$ 159 | $----------------------------------------------------------------> include-after 160 | $for(include-after)$ 161 | $include-after$ 162 | $endfor$ 163 | $------------------------------------------------------------------------------- 164 |
165 | 166 | 167 | -------------------------------------------------------------------------------- /.sanity-ansible-ignore-2.10.txt: -------------------------------------------------------------------------------- 1 | plugins/modules/timesync_provider.sh shebang 2 | plugins/modules/timesync_provider.yml validate-modules:missing-gplv3-license 3 | -------------------------------------------------------------------------------- /.sanity-ansible-ignore-2.11.txt: -------------------------------------------------------------------------------- 1 | plugins/modules/timesync_provider.sh shebang 2 | plugins/modules/timesync_provider.yml validate-modules:missing-gplv3-license 3 | -------------------------------------------------------------------------------- /.sanity-ansible-ignore-2.12.txt: -------------------------------------------------------------------------------- 1 | plugins/modules/timesync_provider.sh shebang 2 | plugins/modules/timesync_provider.yml validate-modules:missing-gplv3-license 3 | -------------------------------------------------------------------------------- /.sanity-ansible-ignore-2.13.txt: -------------------------------------------------------------------------------- 1 | plugins/modules/timesync_provider.sh shebang 2 | plugins/modules/timesync_provider.yml validate-modules:missing-gplv3-license 3 | -------------------------------------------------------------------------------- /.sanity-ansible-ignore-2.14.txt: -------------------------------------------------------------------------------- 1 | plugins/modules/timesync_provider.sh shebang 2 | plugins/modules/timesync_provider.yml validate-modules:missing-gplv3-license 3 | plugins/modules/timesync_provider.sh validate-modules:invalid-extension 4 | plugins/modules/timesync_provider.sh validate-modules:python-syntax-error 5 | -------------------------------------------------------------------------------- /.sanity-ansible-ignore-2.15.txt: -------------------------------------------------------------------------------- 1 | plugins/modules/timesync_provider.sh shebang 2 | plugins/modules/timesync_provider.yml validate-modules:missing-gplv3-license 3 | plugins/modules/timesync_provider.sh validate-modules:invalid-extension 4 | plugins/modules/timesync_provider.sh validate-modules:python-syntax-error 5 | -------------------------------------------------------------------------------- /.sanity-ansible-ignore-2.16.txt: -------------------------------------------------------------------------------- 1 | plugins/modules/timesync_provider.sh shebang 2 | plugins/modules/timesync_provider.yml validate-modules:missing-gplv3-license 3 | plugins/modules/timesync_provider.sh validate-modules:invalid-extension 4 | plugins/modules/timesync_provider.sh validate-modules:python-syntax-error 5 | -------------------------------------------------------------------------------- /.sanity-ansible-ignore-2.17.txt: -------------------------------------------------------------------------------- 1 | plugins/modules/timesync_provider.sh shebang 2 | plugins/modules/timesync_provider.yml validate-modules:missing-gplv3-license 3 | plugins/modules/timesync_provider.sh validate-modules:invalid-extension 4 | plugins/modules/timesync_provider.sh validate-modules:missing-gplv3-license 5 | -------------------------------------------------------------------------------- /.sanity-ansible-ignore-2.9.txt: -------------------------------------------------------------------------------- 1 | plugins/modules/timesync_provider.sh shebang 2 | plugins/modules/timesync_provider.yml validate-modules:missing-gplv3-license 3 | -------------------------------------------------------------------------------- /.yamllint.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | ignore: | 4 | /.tox/ 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | [1.10.2] - 2025-07-09 5 | -------------------- 6 | 7 | ### Other Changes 8 | 9 | - ci: bump sclorg/testing-farm-as-github-action from 3 to 4 (#290) 10 | - ci: bump tox-lsr to 3.8.0; rename qemu/kvm tests (#291) 11 | - ci: Add Fedora 42; use tox-lsr 3.9.0; use lsr-report-errors for qemu tests (#292) 12 | - ci: Add support for bootc end-to-end validation tests (#293) 13 | - ci: Use ansible 2.19 for fedora 42 testing; support python 3.13 (#294) 14 | - refactor: support ansible 2.19; fix ansible-lint issues (#295) 15 | 16 | [1.10.1] - 2025-04-28 17 | -------------------- 18 | 19 | ### Other Changes 20 | 21 | - test: add test for secccomp on el9/el10 (#288) 22 | 23 | [1.10.0] - 2025-04-23 24 | -------------------- 25 | 26 | ### New Features 27 | 28 | - feat: add support for timesync_ntp_ip_family (#277) 29 | 30 | ### Bug Fixes 31 | 32 | - fix: add default seccomp filters for el9/10 (#279) 33 | 34 | ### Other Changes 35 | 36 | - ci: Check spelling with codespell (#267) 37 | - ci: ansible-plugin-scan is disabled for now (#268) 38 | - ci: bump ansible-lint to v25; provide collection requirements for ansible-lint (#271) 39 | - ci: Add test plan that runs CI tests and customize it for each role (#273) 40 | - ci: In test plans, prefix all relate variables with SR_ (#274) 41 | - ci: Create inventory for wrapper test with inventory_hostname (#275) 42 | - ci: Fix bug with ARTIFACTS_URL after prefixing with SR_ (#276) 43 | - ci: several changes related to new qemu test, ansible-lint, python versions, ubuntu versions (#281) 44 | - refactor: make OracleLinux vars files symlinks to RedHat vars files (#283) 45 | - ci: use tox-lsr 3.6.0; improve qemu test logging (#284) 46 | - ci: skip storage scsi, nvme tests in github qemu ci (#285) 47 | - test: ensure tests cleanup and restore the system state (#286) 48 | 49 | [1.9.2] - 2025-01-09 50 | -------------------- 51 | 52 | ### Other Changes 53 | 54 | - ci: Use Fedora 41, drop Fedora 39 (#264) 55 | - ci: Use Fedora 41, drop Fedora 39 - part two (#265) 56 | 57 | [1.9.1] - 2024-10-30 58 | -------------------- 59 | 60 | ### Other Changes 61 | 62 | - ci: Add tags to TF workflow, allow more [citest bad] formats (#258) 63 | - ci: ansible-test action now requires ansible-core version (#259) 64 | - ci: add YAML header to github action workflow files (#260) 65 | - refactor: Use vars/RedHat_N.yml symlink for CentOS, Rocky, Alma wherever possible (#262) 66 | 67 | [1.9.0] - 2024-08-16 68 | -------------------- 69 | 70 | ### New Features 71 | 72 | - feat: Handle reboot for transactional update systems (#256) 73 | 74 | ### Other Changes 75 | 76 | - ci: Add workflow for ci_test bad, use remote fmf plan (#254) 77 | - ci: Fix missing slash in ARTIFACTS_URL (#255) 78 | 79 | [1.8.6] - 2024-08-01 80 | -------------------- 81 | 82 | ### Other Changes 83 | 84 | - ci: Add tft plan and workflow (#248) 85 | - docs: add module documentation for timesync_provider (#250) 86 | - ci: Update fmf plan to add a separate job to prepare managed nodes (#251) 87 | - ci: Bump sclorg/testing-farm-as-github-action from 2 to 3 (#252) 88 | 89 | [1.8.5] - 2024-07-02 90 | -------------------- 91 | 92 | ### Bug Fixes 93 | 94 | - fix: add support for EL10 (#245) 95 | - fix: Don't use chrony-dhcp sourcedir on EL8 systems (#246) 96 | 97 | ### Other Changes 98 | 99 | - ci: ansible-lint action now requires absolute directory (#244) 100 | 101 | [1.8.4] - 2024-06-11 102 | -------------------- 103 | 104 | ### Other Changes 105 | 106 | - ci: use tox-lsr 3.3.0 which uses ansible-test 2.17 (#239) 107 | - ci: tox-lsr 3.4.0 - fix py27 tests; move other checks to py310 (#241) 108 | - ci: Add supported_ansible_also to .ansible-lint (#242) 109 | 110 | [1.8.3] - 2024-04-04 111 | -------------------- 112 | 113 | ### Other Changes 114 | 115 | - ci: fix python unit test - copy pytest config to tests/unit (#234) 116 | - ci: Bump ansible/ansible-lint from 6 to 24 (#235) 117 | - ci: Bump mathieudutour/github-tag-action from 6.1 to 6.2 (#237) 118 | 119 | [1.8.2] - 2024-01-16 120 | -------------------- 121 | 122 | ### Other Changes 123 | 124 | - ci: support ansible-lint and ansible-test 2.16 (#231) 125 | - ci: Use supported ansible-lint action; run ansible-lint against the collection (#232) 126 | 127 | [1.8.1] - 2023-12-08 128 | -------------------- 129 | 130 | ### Other Changes 131 | 132 | - ci: bump actions/github-script from 6 to 7 (#227) 133 | - refactor: get_ostree_data.sh use env shebang - remove from .sanity* (#228) 134 | 135 | [1.8.0] - 2023-11-29 136 | -------------------- 137 | 138 | ### New Features 139 | 140 | - feat: support for ostree systems (#224) 141 | 142 | [1.7.9] - 2023-11-06 143 | -------------------- 144 | 145 | ### Other Changes 146 | 147 | - ci: Fix implicit octal values in main.yml (#220) 148 | - ci: Add ALP-Dolomite var file (#221) 149 | 150 | [1.7.8] - 2023-10-23 151 | -------------------- 152 | 153 | ### Other Changes 154 | 155 | - Bump actions/checkout from 3 to 4 (#209) 156 | - ci: ensure dependabot git commit message conforms to commitlint (#212) 157 | - ci: use dump_packages.py callback to get packages used by role (#214) 158 | - refactor: Add CentOS 7-8, RedHat 7-8 and AlmaLinux 8-9 var files (#216) 159 | - ci: tox-lsr version 3.1.1 (#218) 160 | 161 | [1.7.7] - 2023-09-11 162 | -------------------- 163 | 164 | ### Other Changes 165 | 166 | - ci: Add markdownlint, test_converting_readme, and build_docs workflows (#205) 167 | 168 | - markdownlint runs against README.md to avoid any issues with 169 | converting it to HTML 170 | - test_converting_readme converts README.md > HTML and uploads this test 171 | artifact to ensure that conversion works fine 172 | - build_docs converts README.md > HTML and pushes the result to the 173 | docs branch to publish dosc to GitHub pages site. 174 | - Fix markdown issues in README.md 175 | 176 | Signed-off-by: Sergei Petrosian 177 | 178 | - docs: Make badges consistent, run markdownlint on all .md files (#206) 179 | 180 | - Consistently generate badges for GH workflows in README RHELPLAN-146921 181 | - Run markdownlint on all .md files 182 | - Add custom-woke-action if not used already 183 | - Rename woke action to Woke for a pretty badge 184 | 185 | Signed-off-by: Sergei Petrosian 186 | 187 | - ci: Remove badges from README.md prior to converting to HTML (#207) 188 | 189 | - Remove thematic break after badges 190 | - Remove badges from README.md prior to converting to HTML 191 | 192 | Signed-off-by: Sergei Petrosian 193 | 194 | [1.7.7] - 2023-09-11 195 | -------------------- 196 | 197 | ### Other Changes 198 | 199 | - ci: Add markdownlint, test_converting_readme, and build_docs workflows (#205) 200 | 201 | - markdownlint runs against README.md to avoid any issues with 202 | converting it to HTML 203 | - test_converting_readme converts README.md > HTML and uploads this test 204 | artifact to ensure that conversion works fine 205 | - build_docs converts README.md > HTML and pushes the result to the 206 | docs branch to publish dosc to GitHub pages site. 207 | - Fix markdown issues in README.md 208 | 209 | Signed-off-by: Sergei Petrosian 210 | 211 | - docs: Make badges consistent, run markdownlint on all .md files (#206) 212 | 213 | - Consistently generate badges for GH workflows in README RHELPLAN-146921 214 | - Run markdownlint on all .md files 215 | - Add custom-woke-action if not used already 216 | - Rename woke action to Woke for a pretty badge 217 | 218 | Signed-off-by: Sergei Petrosian 219 | 220 | - ci: Remove badges from README.md prior to converting to HTML (#207) 221 | 222 | - Remove thematic break after badges 223 | - Remove badges from README.md prior to converting to HTML 224 | 225 | Signed-off-by: Sergei Petrosian 226 | 227 | [1.7.6] - 2023-07-19 228 | -------------------- 229 | 230 | ### Bug Fixes 231 | 232 | - fix: facts being gathered unnecessarily (#202) 233 | 234 | ### Other Changes 235 | 236 | - docs: Consistent contributing.md for all roles - allow role specific contributing.md section (#196) 237 | - ci: update tox-lsr to version 3.0.0 (#197) 238 | - ci: Add pull request template and run commitlint on PR title only (#198) 239 | - ci: Rename commitlint to PR title Lint, echo PR titles from env var (#199) 240 | - ci: ansible-lint - ignore var-naming[no-role-prefix] (#200) 241 | - ci: ansible-test ignores file for ansible-core 2.15 (#201) 242 | 243 | [1.7.5] - 2023-04-27 244 | -------------------- 245 | 246 | ### Other Changes 247 | 248 | - test: check generated files for ansible_managed, fingerprint 249 | - ci: Add commitlint GitHub action to ensure conventional commits with feedback 250 | 251 | [1.7.4] - 2023-04-13 252 | -------------------- 253 | 254 | ### Other Changes 255 | 256 | - use pipefail if shell uses pipes (#189) 257 | 258 | [1.7.3] - 2023-04-06 259 | -------------------- 260 | 261 | ### Bug Fixes 262 | 263 | - Update chrony.conf location for Debian (#187) 264 | 265 | ### Other Changes 266 | 267 | - suppress shellcheck issue (#176) 268 | - Fix jinja formatting issue (#178) 269 | - Add README-ansible.md to refer Ansible intro page on linux-system-roles.github.io (#185) 270 | - Fingerprint RHEL System Role managed config files (#186) 271 | 272 | [1.7.2] - 2023-01-20 273 | -------------------- 274 | 275 | ### New Features 276 | 277 | - none 278 | 279 | ### Bug Fixes 280 | 281 | - fixes for ansible-lint 6.x 282 | 283 | ### Other Changes 284 | 285 | - ensure ethtool is installed where needed 286 | - Add check for non-inclusive language; (#170) 287 | - Clean up / Workaround non-inclusive words 288 | - update ignore files for ansible-test 2.14 (#172) 289 | 290 | [1.7.1] - 2022-11-01 291 | -------------------- 292 | 293 | ### New Features 294 | 295 | - none 296 | 297 | ### Bug Fixes 298 | 299 | - none 300 | 301 | ### Other Changes 302 | 303 | - Update timestamping checks using ethtool 304 | 305 | New ethtool versions don't print the upper-case timestamping constants 306 | anymore. Match the lower-case strings instead. 307 | 308 | [1.7.0] - 2022-09-19 309 | -------------------- 310 | 311 | ### New Features 312 | 313 | - adding support for Oracle Linux 6,7,8 and 9 314 | 315 | ### Bug Fixes 316 | 317 | - Update chrony.conf.j2 318 | 319 | Variables are defined as string in Jinja2 template and the operators 320 | on line 26 gives '<' not supported between instances of 'str' and 'float 321 | on line 43 gives '>' not supported between instances of 'str' and 'float 322 | 323 | - Updated: type casting in overall timesync templates for testing 324 | 325 | - Updated: type casting adjusted (timesync_max_distance <= int) 326 | 327 | ### Other Changes 328 | 329 | - use ansible_playbook_filepath for ansible-playbook command 330 | 331 | [1.6.9] - 2022-07-19 332 | -------------------- 333 | 334 | ### New Features 335 | 336 | - none 337 | 338 | ### Bug Fixes 339 | 340 | - none 341 | 342 | ### Other Changes 343 | 344 | - make all tests work with gather_facts: false 345 | 346 | Ensure all tests work when using ANSIBLE_GATHERING=explicit 347 | 348 | - make min_ansible_version a string in meta/main.yml 349 | 350 | The Ansible developers say that `min_ansible_version` in meta/main.yml 351 | must be a `string` value like `"2.9"`, not a `float` value like `2.9`. 352 | 353 | - Add CHANGELOG.md 354 | 355 | [1.6.8] - 2022-05-06 356 | -------------------- 357 | 358 | ### New Features 359 | 360 | - none 361 | 362 | ### Bug Fixes 363 | 364 | - none 365 | 366 | ### Other Changes 367 | 368 | - bump tox-lsr version to 2.11.0; remove py37; add py310 369 | 370 | [1.6.7] - 2022-04-14 371 | -------------------- 372 | 373 | ### New Features 374 | 375 | - support gather\_facts: false; support setup-snapshot.yml 376 | 377 | ### Bug Fixes 378 | 379 | - none 380 | 381 | ### Other Changes 382 | 383 | - none 384 | 385 | [1.6.6] - 2022-02-28 386 | -------------------- 387 | 388 | ### New Features 389 | 390 | - none 391 | 392 | ### Bug Fixes 393 | 394 | - handle errors with stopping services 395 | 396 | ### Other Changes 397 | 398 | - bump tox-lsr version to 2.10.1 399 | 400 | [1.6.5] - 2022-01-12 401 | -------------------- 402 | 403 | ### New Features 404 | 405 | - Initial version for Debian 406 | 407 | ### Bug Fixes 408 | 409 | - none 410 | 411 | ### Other Changes 412 | 413 | - none 414 | 415 | [1.6.4] - 2022-01-10 416 | -------------------- 417 | 418 | ### New Features 419 | 420 | - none 421 | 422 | ### Bug Fixes 423 | 424 | - Fix an issue if a service is listed by service\_facts that does not have the 'status' property defined 425 | 426 | ### Other Changes 427 | 428 | - bump tox-lsr version to 2.8.3 429 | - change recursive role symlink to individual role dir symlinks 430 | 431 | [1.6.3] - 2021-11-22 432 | -------------------- 433 | 434 | ### New Features 435 | 436 | - none 437 | 438 | ### Bug Fixes 439 | 440 | - evaluate is\_ntp\_default as boolean, not string 441 | - reject services which have a status == not-found 442 | - also reject masked and failed services 443 | 444 | ### Other Changes 445 | 446 | - update tox-lsr version to 2.8.0 447 | 448 | [1.6.2] - 2021-11-08 449 | -------------------- 450 | 451 | ### New Features 452 | 453 | - make role work with ansible-core-2.11 ansible-lint and ansible-test 454 | 455 | ### Bug Fixes 456 | 457 | - none 458 | 459 | ### Other Changes 460 | 461 | - update tox-lsr version to 2.7.1 462 | - support python 39, ansible-core 2.12, ansible-plugin-scan 463 | - Check port state in PTP tests 464 | 465 | [1.6.1] - 2021-09-21 466 | -------------------- 467 | 468 | ### New Features 469 | 470 | - replace json\_query with selectattr/map 471 | 472 | ### Bug Fixes 473 | 474 | - Use {{ ansible\_managed | comment }} to fix multi-line ansible\_managed 475 | 476 | ### Other Changes 477 | 478 | - use apt-get install -y 479 | - use tox-lsr version 2.5.1 480 | 481 | [1.6.0] - 2021-08-10 482 | -------------------- 483 | 484 | ### New Features 485 | 486 | - Raise supported Ansible version to 2.9 487 | 488 | ### Bug Fixes 489 | 490 | - none 491 | 492 | ### Other Changes 493 | 494 | - none 495 | 496 | [1.5.0] - 2021-06-03 497 | -------------------- 498 | 499 | ### New Features 500 | 501 | - Add NTS support 502 | 503 | ### Bug Fixes 504 | 505 | - none 506 | 507 | ### Other Changes 508 | 509 | - none 510 | 511 | [1.4.1] - 2021-05-27 512 | -------------------- 513 | 514 | ### New Features 515 | 516 | - none 517 | 518 | ### Bug Fixes 519 | 520 | - none 521 | 522 | ### Other Changes 523 | 524 | - CI: Add support for RHEL-9 525 | 526 | [1.4.0] - 2021-05-06 527 | -------------------- 528 | 529 | ### New Features 530 | 531 | - use public: true to expose role parameters; ansible 2.8 532 | - Add hybrid\_e2e option to PTP domain 533 | 534 | ### Bug Fixes 535 | 536 | - Cleaning up ansible-lint errors except 301 and 303. 537 | - Fix ansible-test errors 538 | 539 | ### Other Changes 540 | 541 | - tag tests with slow 542 | - Remove python-26 environment from tox testing 543 | - update to tox-lsr 2.4.0 - add support for ansible-test with docker 544 | 545 | [1.3.0] - 2021-02-11 546 | -------------------- 547 | 548 | ### New Features 549 | 550 | - Support chrony configuration in Fedora 33 551 | - Add support for HW timestamping with NTP 552 | - add timesync\_chrony\_custom\_settings variable for free-form local configs 553 | 554 | ### Bug Fixes 555 | 556 | - Fix centos6 repos; use standard centos images; add centos8 557 | - Fix package versions 558 | 559 | ### Other Changes 560 | 561 | - use tox-lsr 2.2.0 562 | - use molecule v3, drop v2 563 | - remove ansible 2.7 support from molecule 564 | - use tox for ansible-lint instead of molecule 565 | - use new tox-lsr plugin 566 | - use github actions instead of travis 567 | 568 | [1.2.0] - 2020-11-02 569 | -------------------- 570 | 571 | ### New Features 572 | 573 | - Add option to configure max distance 574 | - Use service\_facts instead of chkconfig/systemctl 575 | 576 | ### Bug Fixes 577 | 578 | - none 579 | 580 | ### Other Changes 581 | 582 | - add jmespath as dependency for molecule 583 | - lock ansible-lint version at 4.3.5; suppress role name lint warning 584 | - sync collections related changes from template to timesync role 585 | - Lock ansible-lint on version 4.2.0 586 | 587 | [1.1.0] - 2020-08-12 588 | -------------------- 589 | 590 | ### New Features 591 | 592 | - Remove ignore\_errors for Disable services tasks 593 | 594 | ### Bug Fixes 595 | 596 | - bug with current implementation of platform/version … 597 | - add support for tox; fix yamllint issues 598 | - use molecule v2 599 | - Fix check mode 600 | - Enable line-length check. Fix found issues. 601 | 602 | ### Other Changes 603 | 604 | - Synchronize files from linux-system-roles/template 605 | - sync with latest template including shellcheck 606 | 607 | [1.0.2] - 2019-06-13 608 | -------------------- 609 | 610 | ### New Features 611 | 612 | - Use linux-system-roles.timesync as name for role 613 | - Use `is version` instead of `|version_compare` 614 | - Add empty timesync\_ntp\_provider to defaults 615 | 616 | ### Bug Fixes 617 | 618 | - Remove unused/now invalid semaphore config 619 | - Fixes for syntax problems found by Molecule 620 | - Fix yamllint error 621 | - Fix role to work in check mode 622 | 623 | ### Other Changes 624 | 625 | - Add default vars test 626 | - Run tests\_default both in normal and in check mode 627 | - use yaml syntax 628 | - Configure Molecule and Travis CI \(this PR should trigger build in CI\) 629 | - Add example playbooks 630 | - Add provision file to tests 631 | - Add test for setting the default NTP provider for OS release 632 | 633 | [1.0.1] - 2018-08-21 634 | -------------------- 635 | 636 | ### New Features 637 | 638 | - none 639 | 640 | ### Bug Fixes 641 | 642 | - none 643 | 644 | ### Other Changes 645 | 646 | - Update Galaxy tags. 647 | 648 | [1.0.0] - 2018-08-21 649 | -------------------- 650 | 651 | ### Initial Release 652 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Red Hat, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README-ansible.md: -------------------------------------------------------------------------------- 1 | # Introduction to Ansible for Linux System Roles 2 | 3 | If you are not familiar with Ansible, please see 4 | [Introduction to Ansible for Linux System Roles](https://linux-system-roles.github.io/documentation/intro-to-ansible-for-system-roles.html), 5 | where many useful links are presented. 6 | -------------------------------------------------------------------------------- /README-ostree.md: -------------------------------------------------------------------------------- 1 | # rpm-ostree 2 | 3 | The role supports running on [rpm-ostree](https://coreos.github.io/rpm-ostree/) 4 | systems. The primary issue is that the `/usr` filesystem is read-only, and the 5 | role cannot install packages. Instead, it will just verify that the necessary 6 | packages and any other `/usr` files are pre-installed. The role will change the 7 | package manager to one that is compatible with `rpm-ostree` systems. 8 | 9 | ## Building 10 | 11 | To build an ostree image for a particular operating system distribution and 12 | version, use the script `.ostree/get_ostree_data.sh` to get the list of 13 | packages. If the role uses other system roles, then the script will include the 14 | packages for the other roles in the list it outputs. The list of packages will 15 | be sorted in alphanumeric order. 16 | 17 | Usage: 18 | 19 | ```bash 20 | .ostree/get_ostree_data.sh packages runtime DISTRO-VERSION FORMAT 21 | ``` 22 | 23 | `DISTRO-VERSION` is in the format that Ansible uses for `ansible_distribution` 24 | and `ansible_distribution_version` - for example, `Fedora-38`, `CentOS-8`, 25 | `RedHat-9.4` 26 | 27 | `FORMAT` is one of `toml`, `json`, `yaml`, `raw` 28 | 29 | * `toml` - each package in a TOML `[[packages]]` element 30 | 31 | ```toml 32 | [[packages]] 33 | name = "package-a" 34 | version = "*" 35 | [[packages]] 36 | name = "package-b" 37 | version = "*" 38 | ... 39 | ``` 40 | 41 | * `yaml` - a YAML list of packages 42 | 43 | ```yaml 44 | - package-a 45 | - package-b 46 | ... 47 | ``` 48 | 49 | * `json` - a JSON list of packages 50 | 51 | ```json 52 | ["package-a","package-b",...] 53 | ``` 54 | 55 | * `raw` - a plain text list of packages, one per line 56 | 57 | ```bash 58 | package-a 59 | package-b 60 | ... 61 | ``` 62 | 63 | What format you choose depends on which image builder you are using. For 64 | example, if you are using something based on 65 | [osbuild-composer](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html-single/composing_installing_and_managing_rhel_for_edge_images/index#creating-an-image-builder-blueprint-for-a-rhel-for-edge-image-using-the-command-line-interface_composing-a-rhel-for-edge-image-using-image-builder-command-line), 66 | you will probably want to use the `toml` output format. 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # timesync 2 | 3 | [![ansible-lint.yml](https://github.com/linux-system-roles/timesync/actions/workflows/ansible-lint.yml/badge.svg)](https://github.com/linux-system-roles/timesync/actions/workflows/ansible-lint.yml) [![ansible-test.yml](https://github.com/linux-system-roles/timesync/actions/workflows/ansible-test.yml/badge.svg)](https://github.com/linux-system-roles/timesync/actions/workflows/ansible-test.yml) [![codespell.yml](https://github.com/linux-system-roles/timesync/actions/workflows/codespell.yml/badge.svg)](https://github.com/linux-system-roles/timesync/actions/workflows/codespell.yml) [![markdownlint.yml](https://github.com/linux-system-roles/timesync/actions/workflows/markdownlint.yml/badge.svg)](https://github.com/linux-system-roles/timesync/actions/workflows/markdownlint.yml) [![qemu-kvm-integration-tests.yml](https://github.com/linux-system-roles/timesync/actions/workflows/qemu-kvm-integration-tests.yml/badge.svg)](https://github.com/linux-system-roles/timesync/actions/workflows/qemu-kvm-integration-tests.yml) [![shellcheck.yml](https://github.com/linux-system-roles/timesync/actions/workflows/shellcheck.yml/badge.svg)](https://github.com/linux-system-roles/timesync/actions/workflows/shellcheck.yml) [![tft.yml](https://github.com/linux-system-roles/timesync/actions/workflows/tft.yml/badge.svg)](https://github.com/linux-system-roles/timesync/actions/workflows/tft.yml) [![tft_citest_bad.yml](https://github.com/linux-system-roles/timesync/actions/workflows/tft_citest_bad.yml/badge.svg)](https://github.com/linux-system-roles/timesync/actions/workflows/tft_citest_bad.yml) [![woke.yml](https://github.com/linux-system-roles/timesync/actions/workflows/woke.yml/badge.svg)](https://github.com/linux-system-roles/timesync/actions/workflows/woke.yml) 4 | 5 | This role installs and configures an NTP and/or PTP implementation to operate 6 | 7 | as an NTP client and/or PTP slave in order to synchronize the system clock with 8 | 9 | NTP servers and/or grandmasters in PTP domains. Supported NTP/PTP 10 | implementations are chrony, ntp (the reference implementation) and linuxptp. 11 | 12 | ## Warning 13 | 14 | The role replaces the configuration of the given or detected provider 15 | service on the managed host. Previous settings will be lost, even if 16 | they are not specified in the role variables (no attempt is made to 17 | preserve or merge the previous settings, the configuration files are 18 | replaced entirely). The only setting which is preserved is the choice 19 | of provider if `timesync_ntp_provider` is not defined (see the 20 | description of this variable below). 21 | 22 | ## Requirements 23 | 24 | See below 25 | 26 | ### Collection requirements 27 | 28 | In order to manage `rpm-ostree` systems, the role requires modules from external 29 | collections. Use the following command to install them: 30 | 31 | ```bash 32 | ansible-galaxy collection install -vv -r meta/collection-requirements.yml 33 | ``` 34 | 35 | ## Role Variables 36 | 37 | The variables that can be passed to this role are as follows: 38 | 39 | ```yaml 40 | # List of NTP servers 41 | timesync_ntp_servers: 42 | - hostname: foo.example.com # Hostname or address of the server 43 | minpoll: 4 # Minimum polling interval (default 6) 44 | maxpoll: 8 # Maximum polling interval (default 10) 45 | iburst: true # Flag enabling fast initial synchronization 46 | # (default false) 47 | pool: false # Flag indicating that each resolved address 48 | # of the hostname is a separate NTP server 49 | # (default false) 50 | nts: false # Flag enabling Network Time Security (NTS) 51 | # authentication mechanism (default false, 52 | # supported only with chrony >= 4.0) 53 | prefer: false # Flag marking the source to be preferred for 54 | # synchronization over other sources 55 | # (default false) 56 | trust: false # Flag marking the source to be trusted over 57 | # sources that don't have this flag 58 | # (default false) 59 | xleave: false # Flag enabling interleaved mode (default false) 60 | filter: 1 # Number of NTP measurements per clock update 61 | # (default 1) 62 | 63 | # List of PTP domains 64 | timesync_ptp_domains: 65 | - number: 0 # PTP domain number 66 | interfaces: [eth0] # List of interfaces in the domain 67 | delay: 0.000010 # Assumed maximum network delay to the 68 | # grandmaster in seconds # wokeignore:rule=master 69 | # (default 100 microsecond) 70 | transport: UDPv4 # Network transport: UDPv4, UDPv6, L2 71 | # (default UDPv4) 72 | udp_ttl: 1 # TTL for UDPv4 and UDPv6 transports 73 | # (default 1) 74 | hybrid_e2e: false # Flag enabling unicast end-to-end delay 75 | # requests (default false) 76 | 77 | # Flag enabling use of NTP servers provided by DHCP (default false) 78 | timesync_dhcp_ntp_servers: false 79 | 80 | # Minimum offset of the clock which can be corrected by stepping (default is 81 | # specific to NTP/PTP implementation: chrony 1.0, ntp 0.128, linuxptp 0.00002). 82 | # Zero threshold disables all steps. 83 | timesync_step_threshold: 1.0 84 | 85 | # Maximum root distance to accept measurements from NTP servers 86 | # Set to 0 to use provider default 87 | timesync_max_distance: 0 88 | 89 | # Minimum number of selectable time sources required to allow synchronization 90 | # of the clock (default 1) 91 | timesync_min_sources: 1 92 | 93 | # List of interfaces which should have hardware timestamping enabled for NTP 94 | # (default empty list). As a special value, '*' enables the timestamping on all 95 | # interfaces that support it. 96 | timesync_ntp_hwts_interfaces: ["*"] 97 | 98 | # Name of the package which should be installed and configured for NTP. 99 | # Possible values are "chrony" and "ntp". If not defined, the currently active 100 | # or enabled service will be configured. If no service is active or enabled, a 101 | # package specific to the system and its version will be selected. 102 | timesync_ntp_provider: chrony 103 | 104 | # Sometimes administrators might need extended configurations for chrony which 105 | # are not covered by the predefined settings provided by this role. 106 | # 'timesync_chrony_custom_settings' allows to define a list of custom settings 107 | # for the chrony.conf file, by providing a list of settings. As an example, 108 | # for debugging, one might need to log measurements, statistics and tracking. 109 | # This information is usually stored in the /var/log/chrony directory. For 110 | # that, one needs to define two different settings (logdir and log), as 111 | # follows: 112 | timesync_chrony_custom_settings: 113 | - "logdir /var/log/chrony" 114 | - "log measurements statistics tracking" 115 | 116 | # This variable is applicable only for transactional update systems. 117 | # If a transactional update requires a reboot, the role will proceed with the 118 | # reboot if `timesync_transactional_update_reboot_ok` is set to `true`. If set 119 | # to `false`, the role will notify the user that a reboot is required, allowing 120 | # for custom handling of the reboot requirement. If this variable is not set, 121 | # the role will fail to ensure the reboot requirement is not overlooked. 122 | # For non-transactional update systems, this variable is ignored. 123 | timesync_transactional_update_reboot_ok: true 124 | 125 | # This option is useful on systems where IPv6 or IPv4 are disabled. 126 | # chronyd will work on a IPv6-disabled host without -4, but it logs error messages 127 | # when binding to the IPv6 sockets fails. Adding the -4 option disables those sockets 128 | # and there are no error messages. It's also useful to prevent the client from using 129 | # IPv6 servers when IPv4 is known to work better (e.g. IPv6 over a tunnel). 130 | # Corresponds to the `-4` and `-6` OPTIONS for chronyd and ntpd. Values are: 131 | # * IPv4 - use only IPv4 132 | # * IPv6 - use only IPv6 133 | # * all - use both IPv4 and IPv6 134 | # Default is all 135 | timesync_ntp_ip_family: all 136 | ``` 137 | 138 | ## Example Playbooks 139 | 140 | Install and configure ntp to synchronize the system clock with three NTP servers: 141 | 142 | ```yaml 143 | - name: Manage timesync with 3 servers 144 | hosts: targets 145 | vars: 146 | timesync_ntp_servers: 147 | - hostname: foo.example.com 148 | iburst: true 149 | - hostname: bar.example.com 150 | iburst: true 151 | - hostname: baz.example.com 152 | iburst: true 153 | roles: 154 | - linux-system-roles.timesync 155 | ``` 156 | 157 | Install and configure linuxptp to synchronize the system clock with a 158 | 159 | grandmaster in PTP domain number 0, which is accessible on interface eth0: 160 | 161 | ```yaml 162 | - name: Manage timesync in PTP domain 0, interface eth0 163 | hosts: targets 164 | vars: 165 | timesync_ptp_domains: 166 | - number: 0 167 | interfaces: [eth0] 168 | roles: 169 | - linux-system-roles.timesync 170 | ``` 171 | 172 | Install and configure chrony and linuxptp to synchronize the system clock with 173 | multiple NTP servers and PTP domains for a highly accurate and resilient 174 | synchronization: 175 | 176 | ```yaml 177 | - name: Manage multiple NTP servers and PTP domains 178 | hosts: targets 179 | vars: 180 | timesync_ntp_servers: 181 | - hostname: foo.example.com 182 | maxpoll: 6 183 | - hostname: bar.example.com 184 | maxpoll: 6 185 | - hostname: baz.example.com 186 | maxpoll: 6 187 | timesync_ptp_domains: 188 | - number: 0 189 | interfaces: [eth0, eth1] 190 | transport: L2 191 | delay: 0.000010 192 | - number: 1 193 | interfaces: [eth2] 194 | transport: UDPv4 195 | delay: 0.000010 196 | roles: 197 | - linux-system-roles.timesync 198 | ``` 199 | 200 | Install and configure chrony with multiple NTP servers and custom advanced 201 | settings: log `measurements`, `statistics` and `tracking` 202 | into `/var/log/chrony`: 203 | 204 | ```yaml 205 | - name: Manage with custom advanced settings 206 | hosts: targets 207 | vars: 208 | timesync_ntp_servers: 209 | - hostname: foo.example.com 210 | - hostname: bar.example.com 211 | - hostname: baz.example.com 212 | timesync_chrony_custom_settings: 213 | - "logdir /var/log/chrony" 214 | - "log measurements statistics tracking" 215 | roles: 216 | - linux-system-roles.timesync 217 | ``` 218 | 219 | ## rpm-ostree 220 | 221 | See README-ostree.md 222 | -------------------------------------------------------------------------------- /ansible_pytest_extra_requirements.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | # ansible and dependencies for all supported platforms 4 | ansible ; python_version > "2.6" 5 | idna<2.8 ; python_version < "2.7" 6 | PyYAML<5.1 ; python_version < "2.7" 7 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to the timesync Linux System Role 2 | 3 | ## Where to start 4 | 5 | The first place to go is [Contribute](https://linux-system-roles.github.io/contribute.html). 6 | This has all of the common information that all role developers need: 7 | 8 | * Role structure and layout 9 | * Development tools - How to run tests and checks 10 | * Ansible recommended practices 11 | * Basic git and github information 12 | * How to create git commits and submit pull requests 13 | 14 | **Bugs and needed implementations** are listed on 15 | [Github Issues](https://github.com/linux-system-roles/timesync/issues). 16 | Issues labeled with 17 | [**help wanted**](https://github.com/linux-system-roles/timesync/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) 18 | are likely to be suitable for new contributors! 19 | 20 | **Code** is managed on [Github](https://github.com/linux-system-roles/timesync), using 21 | [Pull Requests](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests). 22 | -------------------------------------------------------------------------------- /custom_requirements.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | # Write requirements for running your custom commands in tox here: 4 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | timesync_ntp_servers: [] 3 | timesync_ptp_domains: [] 4 | timesync_dhcp_ntp_servers: false 5 | timesync_step_threshold: -1.0 6 | timesync_min_sources: 1 7 | timesync_ntp_hwts_interfaces: [] 8 | timesync_ntp_provider: "" 9 | timesync_max_distance: 0 10 | timesync_transactional_update_reboot_ok: null 11 | # options are all, IPv4, IPv6 12 | # default is none which is platform default 13 | timesync_ntp_ip_family: "" 14 | -------------------------------------------------------------------------------- /examples/multiple-ntp-servers.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Example with multiple servers 3 | hosts: "{{ targets }}" 4 | vars: 5 | timesync_ntp_servers: 6 | - hostname: 0.pool.ntp.org 7 | iburst: true 8 | - hostname: 1.pool.ntp.org 9 | iburst: true 10 | - hostname: 2.pool.ntp.org 11 | iburst: true 12 | - hostname: 3.pool.ntp.org 13 | iburst: true 14 | roles: 15 | - linux-system-roles.timesync 16 | -------------------------------------------------------------------------------- /examples/single-pool.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Example with single pool 3 | hosts: "{{ targets }}" 4 | vars: 5 | timesync_ntp_servers: 6 | - hostname: 2.pool.ntp.org 7 | pool: true 8 | iburst: true 9 | roles: 10 | - linux-system-roles.timesync 11 | -------------------------------------------------------------------------------- /handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Restart chronyd 3 | service: 4 | name: chronyd 5 | state: restarted 6 | 7 | - name: Restart ntpd 8 | service: 9 | name: ntpd 10 | state: restarted 11 | 12 | - name: Restart ptp4l 13 | service: 14 | name: ptp4l 15 | state: restarted 16 | 17 | - name: Restart phc2sys 18 | service: 19 | name: phc2sys 20 | state: restarted 21 | 22 | # wokeignore:rule=master 23 | - name: Restart timemaster 24 | service: 25 | # wokeignore:rule=master 26 | name: timemaster 27 | state: restarted 28 | -------------------------------------------------------------------------------- /library/timesync_provider.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # WANT_JSON 3 | 4 | is_service_enabled() { 5 | local name runlevel prev_runlevel 6 | name="$1" 7 | 8 | # shellcheck disable=SC2034 9 | read -r prev_runlevel runlevel < <(runlevel) 10 | 11 | systemctl is-enabled "$name.service" &> /dev/null || \ 12 | chkconfig --list "$name" 2>/dev/null | grep -q "$runlevel:on" 13 | } 14 | 15 | is_service_active() { 16 | local name=$1 17 | 18 | systemctl is-active "$name.service" &> /dev/null || \ 19 | service "$name" status &>/dev/null 20 | } 21 | 22 | get_current_ntp_providers() { 23 | is_service_active chronyd || is_service_enabled chronyd && echo chrony 24 | is_service_active ntpd || is_service_enabled ntpd && echo ntp 25 | } 26 | 27 | 28 | current_ntp_providers=$(get_current_ntp_providers) 29 | 30 | ntp_provider_count=$(echo -n "$current_ntp_providers" | wc -w) 31 | 32 | case $ntp_provider_count in 33 | 0) 34 | ntp_provider_current="" 35 | ;; 36 | 1) 37 | ntp_provider_current=$current_ntp_providers 38 | ;; 39 | *) 40 | ntp_provider_current="" 41 | error_message="Multiple NTP providers are currently active/enabled." 42 | ;; 43 | esac 44 | 45 | if [ -z "$error_message" ]; then 46 | printf '{"ansible_facts": {"timesync_ntp_provider_current": "%s"}}' "$ntp_provider_current" 47 | else 48 | printf '{"failed": "True", "msg": "%s"}' "$error_message" 49 | fi 50 | -------------------------------------------------------------------------------- /library/timesync_provider.yml: -------------------------------------------------------------------------------- 1 | DOCUMENTATION: 2 | module: timesync_provider 3 | short_description: Determine the timesync provider on the system 4 | version_added: "2.14.0" 5 | description: 6 | - "WARNING: Do not use this module directly! It is only for role internal use." 7 | - "Determine the timesync provider on the system and return it." 8 | options: {} 9 | author: 10 | - Miroslav Lichvar (@mlichvar) 11 | EXAMPLES: |- 12 | - name: Determine current NTP provider 13 | timesync_provider: 14 | RETURN: 15 | timesync_ntp_provider_current: 16 | description: Name of current provider e.g. chrony or ntp 17 | returned: success 18 | type: str 19 | sample: chrony 20 | -------------------------------------------------------------------------------- /meta/collection-requirements.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | collections: 4 | - ansible.posix 5 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: Miroslav Lichvar 4 | description: Configure NTP and/or PTP 5 | company: Red Hat, Inc. 6 | license: MIT 7 | min_ansible_version: "2.9" 8 | platforms: 9 | - name: Fedora 10 | versions: 11 | - all 12 | - name: EL 13 | versions: 14 | - "6" 15 | - "7" 16 | - "8" 17 | - "9" 18 | galaxy_tags: 19 | - centos 20 | - chrony 21 | - el6 22 | - el7 23 | - el8 24 | - el9 25 | - el10 26 | - fedora 27 | - ntp 28 | - ntpd 29 | - ptp 30 | - redhat 31 | - rhel 32 | - system 33 | - time 34 | -------------------------------------------------------------------------------- /molecule/default/Dockerfile.j2: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # Molecule managed 3 | 4 | {% if item.registry is defined %} 5 | FROM {{ item.registry.url }}/{{ item.image }} 6 | {% else %} 7 | FROM {{ item.image }} 8 | {% endif %} 9 | 10 | RUN set -euo pipefail; \ 11 | pkgs="python sudo yum-plugin-ovl bash"; \ 12 | if grep 'CentOS release 6' /etc/centos-release > /dev/null 2>&1; then \ 13 | for file in /etc/yum.repos.d/CentOS-*.repo; do \ 14 | if ! grep '^baseurl=.*vault[.]centos[.]org' "$file"; then \ 15 | sed -i -e 's,^mirrorlist,#mirrorlist,' \ 16 | -e 's,^#baseurl=,baseurl=,' \ 17 | -e 's,mirror.centos.org/centos/$releasever,vault.centos.org/6.10,' \ 18 | "$file"; \ 19 | fi; \ 20 | done; \ 21 | pkgs="$pkgs upstart chkconfig initscripts"; \ 22 | fi; \ 23 | if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ 24 | elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python3 sudo python3-devel python3-dnf bash && dnf clean all; \ 25 | elif [ $(command -v yum) ]; then yum makecache fast && yum install -y $pkgs && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \ 26 | elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml && zypper clean -a; \ 27 | elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \ 28 | elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates && xbps-remove -O; fi 29 | -------------------------------------------------------------------------------- /molecule/default/molecule.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | dependency: 4 | name: galaxy 5 | driver: 6 | name: ${LSR_MOLECULE_DRIVER:-docker} 7 | platforms: 8 | - name: centos-6 9 | image: registry.centos.org/centos:6 10 | volumes: 11 | - /sys/fs/cgroup:/sys/fs/cgroup:ro 12 | privileged: true 13 | command: /sbin/init 14 | - name: centos-7 15 | image: registry.centos.org/centos/systemd:latest 16 | volumes: 17 | - /sys/fs/cgroup:/sys/fs/cgroup:ro 18 | privileged: true 19 | command: /usr/lib/systemd/systemd --system 20 | - name: centos-8 21 | image: registry.centos.org/centos:8 22 | volumes: 23 | - /sys/fs/cgroup:/sys/fs/cgroup:ro 24 | privileged: true 25 | command: /usr/lib/systemd/systemd --system 26 | provisioner: 27 | name: ansible 28 | log: true 29 | playbooks: 30 | converge: ../../tests/tests_default.yml 31 | scenario: 32 | name: default 33 | test_sequence: 34 | - destroy 35 | - create 36 | - converge 37 | - idempotence 38 | - check 39 | - destroy 40 | -------------------------------------------------------------------------------- /molecule_extra_requirements.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | # Write extra requirements for running molecule here: 4 | -------------------------------------------------------------------------------- /plans/README-plans.md: -------------------------------------------------------------------------------- 1 | # Introduction CI Testing Plans 2 | 3 | Linux System Roles CI runs [tmt](https://tmt.readthedocs.io/en/stable/index.html) test plans in [Testing farm](https://docs.testing-farm.io/Testing%20Farm/0.1/index.html) with the [tft.yml](https://github.com/linux-system-roles/timesync/blob/main/.github/workflows/tft.yml) GitHub workflow. 4 | 5 | The `plans/test_playbooks_parallel.fmf` plan is a test plan that runs test playbooks in parallel on multiple managed nodes. 6 | `plans/test_playbooks_parallel.fmf` is generated centrally from `https://github.com/linux-system-roles/.github/`. 7 | The automation calculates the number of managed nodes to provision with this formula: 8 | 9 | ```plain 10 | number-of-test-playbooks / 10 + 1 11 | ``` 12 | 13 | The `plans/test_playbooks_parallel.fmf` plan does the following steps: 14 | 15 | 1. Provisions systems to be used as a control node and as managed nodes. 16 | 2. Does the required preparation on systems. 17 | 3. For the given role and the given PR, runs the general test from [test.sh](https://github.com/linux-system-roles/tft-tests/blob/main/tests/general/test.sh). 18 | 19 | The [tft.yml](https://github.com/linux-system-roles/timesync/blob/main/.github/workflows/tft.yml) workflow runs the above plan and uploads the results to our Fedora storage for public access. 20 | This workflow uses Testing Farm's Github Action [Schedule tests on Testing Farm](https://github.com/marketplace/actions/schedule-tests-on-testing-farm). 21 | 22 | ## Running Tests 23 | 24 | You can run tests locally with the `tmt try` cli or remotely in Testing Farm. 25 | 26 | ### Running Tests Locally 27 | 28 | 1. Install `tmt` as described in [Installation](https://tmt.readthedocs.io/en/stable/stories/install.html). 29 | 2. Change to the role repository directory. 30 | 3. Modify `plans/test_playbooks_parallel.fmf` to suit your requirements: 31 | 1. Due to [issue #3138](https://github.com/teemtee/tmt/issues/3138), comment out all managed nodes except for one. 32 | 2. Optionally modify environment variables to, e.g. run only specified test playbooks by modifying `SYSTEM_ROLES_ONLY_TESTS`. 33 | 4. Enter `tmt try -p plans/test_playbooks_parallel `. 34 | This command identifies the `plans/test_playbooks_parallel.fmf` plan and provisions local VMs, a control node and a managed node. 35 | 5. `tmt try` is in development and does not identify tests from URL automatically, so after provisioning the machines, you must type `t`, `p`, `t` from the interactive prompt to identify tests, run preparation steps, and run the tests. 36 | 37 | ### Running in Testing Farm 38 | 39 | 1. Install `testing-farm` as described in [Installation](https://gitlab.com/testing-farm/cli/-/blob/main/README.adoc#user-content-installation). 40 | 2. Change to the role repository directory. 41 | 3. If you want to run tests with edits in your branch, you need to commit and push changes first to some branch. 42 | 4. You can uncomment "Inject your ssh public key to test systems" discover step in the plan if you want to troubleshoot tests by SSHing into test systems in Testing Farm. 43 | 5. Enter `testing-farm request`. 44 | Edit to your needs. 45 | 46 | ```bash 47 | $ TESTING_FARM_API_TOKEN= \ 48 | testing-farm request --pipeline-type="tmt-multihost" \ 49 | --plan-filter="tag:playbooks_parallel" \ 50 | --git-url "https://github.com//timesync" \ 51 | --git-ref "" \ 52 | --compose CentOS-Stream-9 \ 53 | -e "SYSTEM_ROLES_ONLY_TESTS=tests_default.yml" \ 54 | --no-wait 55 | ``` 56 | -------------------------------------------------------------------------------- /plans/test_playbooks_parallel.fmf: -------------------------------------------------------------------------------- 1 | summary: A general test for a system role 2 | tag: playbooks_parallel 3 | provision: 4 | # TF uses `how: artemis`, and `tmt try`` uses `how: virtual`. 5 | # Hence there is no need to define `how` explicitly. 6 | - name: control-node1 7 | role: control_node 8 | - name: managed-node1 9 | role: managed_node 10 | - name: managed-node2 11 | role: managed_node 12 | - name: managed-node3 13 | role: managed_node 14 | environment: 15 | SR_ANSIBLE_VER: 2.17 16 | SR_REPO_NAME: timesync 17 | SR_PYTHON_VERSION: 3.12 18 | SR_ONLY_TESTS: "" # tests_default.yml 19 | SR_TEST_LOCAL_CHANGES: true 20 | SR_PR_NUM: "" 21 | SR_LSR_USER: "" 22 | SR_LSR_DOMAIN: "" 23 | SR_LSR_SSH_KEY: "" 24 | SR_ARTIFACTS_DIR: "" 25 | SR_ARTIFACTS_URL: "" 26 | SR_TFT_DEBUG: false 27 | prepare: 28 | - name: Use vault.centos.org repos (CS 7, 8 EOL workaround) 29 | script: | 30 | if grep -q 'CentOS Stream release 8' /etc/redhat-release; then 31 | sed -i '/^mirror/d;s/#\(baseurl=http:\/\/\)mirror/\1vault/' /etc/yum.repos.d/*.repo 32 | fi 33 | if grep -q 'CentOS Linux release 7.9' /etc/redhat-release; then 34 | sed -i '/^mirror/d;s/#\?\(baseurl=http:\/\/\)mirror/\1vault/' /etc/yum.repos.d/*.repo 35 | fi 36 | # Replace with feature: epel: enabled once https://github.com/teemtee/tmt/pull/3128 is merged 37 | - name: Enable epel to install beakerlib 38 | script: | 39 | # CS 10 and Fedora doesn't require epel 40 | if grep -q -e 'CentOS Stream release 10' -e 'Fedora release' /etc/redhat-release; then 41 | exit 0 42 | fi 43 | yum install epel-release yum-utils -y 44 | yum-config-manager --enable epel epel-debuginfo epel-source 45 | discover: 46 | - name: Prepare managed node 47 | how: fmf 48 | where: managed_node 49 | filter: tag:prep_managed_node 50 | url: https://github.com/linux-system-roles/tft-tests 51 | ref: main 52 | - name: Run test playbooks from control_node 53 | how: fmf 54 | where: control_node 55 | filter: tag:test_playbooks 56 | url: https://github.com/linux-system-roles/tft-tests 57 | ref: main 58 | # Uncomment this step for troubleshooting 59 | # This is required because currently testing-farm cli doesn't support running multi-node plans 60 | # You can set ID_RSA_PUB in the environment section above to your public key to distribute it to nodes 61 | # - name: Inject your ssh public key to test systems 62 | # how: fmf 63 | # where: control_node 64 | # filter: tag:reserve_system 65 | # url: https://github.com/linux-system-roles/tft-tests 66 | # ref: main 67 | execute: 68 | how: tmt 69 | -------------------------------------------------------------------------------- /pylint_extra_requirements.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | # Write extra requirements for running pylint here: 4 | -------------------------------------------------------------------------------- /pylintrc: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | # This file was generated using `pylint --generate-rcfile > pylintrc` command. 4 | [MASTER] 5 | 6 | # A comma-separated list of package or module names from where C extensions may 7 | # be loaded. Extensions are loading into the active Python interpreter and may 8 | # run arbitrary code 9 | extension-pkg-whitelist= 10 | 11 | # Add files or directories to the blacklist. They should be base names, not 12 | # paths. 13 | ignore=.git,.tox 14 | 15 | # Add files or directories matching the regex patterns to the blacklist. The 16 | # regex matches against base names, not paths. 17 | ignore-patterns= 18 | 19 | # Python code to execute, usually for sys.path manipulation such as 20 | # pygtk.require(). 21 | #init-hook= 22 | 23 | # Use multiple processes to speed up Pylint. 24 | jobs=1 25 | 26 | # List of plugins (as comma separated values of python modules names) to load, 27 | # usually to register additional checkers. 28 | load-plugins= 29 | 30 | # Pickle collected data for later comparisons. 31 | persistent=yes 32 | 33 | # Specify a configuration file. 34 | #rcfile= 35 | 36 | # When enabled, pylint would attempt to guess common misconfiguration and emit 37 | # user-friendly hints instead of false-positive error messages 38 | suggestion-mode=yes 39 | 40 | # Allow loading of arbitrary C extensions. Extensions are imported into the 41 | # active Python interpreter and may run arbitrary code. 42 | unsafe-load-any-extension=no 43 | 44 | 45 | [MESSAGES CONTROL] 46 | 47 | # Only show warnings with the listed confidence levels. Leave empty to show 48 | # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED 49 | confidence= 50 | 51 | # Disable the message, report, category or checker with the given id(s). You 52 | # can either give multiple identifiers separated by comma (,) or put this 53 | # option multiple times (only on the command line, not in the configuration 54 | # file where it should appear only once).You can also use "--disable=all" to 55 | # disable everything first and then re-enable specific checks. For example, if 56 | # you want to run only the similarities checker, you can use "--disable=all 57 | # --enable=similarities". If you want to run only the classes checker, but have 58 | # no Warning level messages displayed, use"--disable=all --enable=classes 59 | # --disable=W" 60 | disable=wrong-import-position 61 | #disable=print-statement, 62 | # parameter-unpacking, 63 | # unpacking-in-except, 64 | # old-raise-syntax, 65 | # backtick, 66 | # long-suffix, 67 | # old-ne-operator, 68 | # old-octal-literal, 69 | # import-star-module-level, 70 | # non-ascii-bytes-literal, 71 | # raw-checker-failed, 72 | # bad-inline-option, 73 | # locally-disabled, 74 | # locally-enabled, 75 | # file-ignored, 76 | # suppressed-message, 77 | # useless-suppression, 78 | # deprecated-pragma, 79 | # apply-builtin, 80 | # basestring-builtin, 81 | # buffer-builtin, 82 | # cmp-builtin, 83 | # coerce-builtin, 84 | # execfile-builtin, 85 | # file-builtin, 86 | # long-builtin, 87 | # raw_input-builtin, 88 | # reduce-builtin, 89 | # standarderror-builtin, 90 | # unicode-builtin, 91 | # xrange-builtin, 92 | # coerce-method, 93 | # delslice-method, 94 | # getslice-method, 95 | # setslice-method, 96 | # no-absolute-import, 97 | # old-division, 98 | # dict-iter-method, 99 | # dict-view-method, 100 | # next-method-called, 101 | # metaclass-assignment, 102 | # indexing-exception, 103 | # raising-string, 104 | # reload-builtin, 105 | # oct-method, 106 | # hex-method, 107 | # nonzero-method, 108 | # cmp-method, 109 | # input-builtin, 110 | # round-builtin, 111 | # intern-builtin, 112 | # unichr-builtin, 113 | # map-builtin-not-iterating, 114 | # zip-builtin-not-iterating, 115 | # range-builtin-not-iterating, 116 | # filter-builtin-not-iterating, 117 | # using-cmp-argument, 118 | # eq-without-hash, 119 | # div-method, 120 | # idiv-method, 121 | # rdiv-method, 122 | # exception-message-attribute, 123 | # invalid-str-codec, 124 | # sys-max-int, 125 | # bad-python3-import, 126 | # deprecated-string-function, 127 | # deprecated-str-translate-call, 128 | # deprecated-itertools-function, 129 | # deprecated-types-field, 130 | # next-method-defined, 131 | # dict-items-not-iterating, 132 | # dict-keys-not-iterating, 133 | # dict-values-not-iterating 134 | 135 | # Enable the message, report, category or checker with the given id(s). You can 136 | # either give multiple identifier separated by comma (,) or put this option 137 | # multiple time (only on the command line, not in the configuration file where 138 | # it should appear only once). See also the "--disable" option for examples. 139 | enable=c-extension-no-member 140 | 141 | 142 | [REPORTS] 143 | 144 | # Python expression which should return a note less than 10 (10 is the highest 145 | # note). You have access to the variables errors warning, statement which 146 | # respectively contain the number of errors / warnings messages and the total 147 | # number of statements analyzed. This is used by the global evaluation report 148 | # (RP0004). 149 | evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) 150 | 151 | # Template used to display messages. This is a python new-style format string 152 | # used to format the message information. See doc for all details 153 | #msg-template= 154 | 155 | # Set the output format. Available formats are text, parseable, colorized, json 156 | # and msvs (visual studio).You can also give a reporter class, eg 157 | # mypackage.mymodule.MyReporterClass. 158 | output-format=text 159 | 160 | # Tells whether to display a full report or only the messages 161 | reports=no 162 | 163 | # Activate the evaluation score. 164 | score=yes 165 | 166 | 167 | [REFACTORING] 168 | 169 | # Maximum number of nested blocks for function / method body 170 | max-nested-blocks=5 171 | 172 | # Complete name of functions that never returns. When checking for 173 | # inconsistent-return-statements if a never returning function is called then 174 | # it will be considered as an explicit return statement and no message will be 175 | # printed. 176 | never-returning-functions=optparse.Values,sys.exit 177 | 178 | 179 | [LOGGING] 180 | 181 | # Logging modules to check that the string format arguments are in logging 182 | # function parameter format 183 | logging-modules=logging 184 | 185 | 186 | [TYPECHECK] 187 | 188 | # List of decorators that produce context managers, such as 189 | # contextlib.contextmanager. Add to this list to register other decorators that 190 | # produce valid context managers. 191 | contextmanager-decorators=contextlib.contextmanager 192 | 193 | # List of members which are set dynamically and missed by pylint inference 194 | # system, and so shouldn't trigger E1101 when accessed. Python regular 195 | # expressions are accepted. 196 | generated-members= 197 | 198 | # Tells whether missing members accessed in mixin class should be ignored. A 199 | # mixin class is detected if its name ends with "mixin" (case insensitive). 200 | ignore-mixin-members=yes 201 | 202 | # This flag controls whether pylint should warn about no-member and similar 203 | # checks whenever an opaque object is returned when inferring. The inference 204 | # can return multiple potential results while evaluating a Python object, but 205 | # some branches might not be evaluated, which results in partial inference. In 206 | # that case, it might be useful to still emit no-member and other checks for 207 | # the rest of the inferred objects. 208 | ignore-on-opaque-inference=yes 209 | 210 | # List of class names for which member attributes should not be checked (useful 211 | # for classes with dynamically set attributes). This supports the use of 212 | # qualified names. 213 | ignored-classes=optparse.Values,thread._local,_thread._local 214 | 215 | # List of module names for which member attributes should not be checked 216 | # (useful for modules/projects where namespaces are manipulated during runtime 217 | # and thus existing member attributes cannot be deduced by static analysis. It 218 | # supports qualified module names, as well as Unix pattern matching. 219 | ignored-modules= 220 | 221 | # Show a hint with possible names when a member name was not found. The aspect 222 | # of finding the hint is based on edit distance. 223 | missing-member-hint=yes 224 | 225 | # The minimum edit distance a name should have in order to be considered a 226 | # similar match for a missing member name. 227 | missing-member-hint-distance=1 228 | 229 | # The total number of similar names that should be taken in consideration when 230 | # showing a hint for a missing member. 231 | missing-member-max-choices=1 232 | 233 | 234 | [FORMAT] 235 | 236 | # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. 237 | expected-line-ending-format= 238 | 239 | # Regexp for a line that is allowed to be longer than the limit. 240 | ignore-long-lines=^\s*(# )??$ 241 | 242 | # Number of spaces of indent required inside a hanging or continued line. 243 | indent-after-paren=4 244 | 245 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 246 | # tab). 247 | indent-string=' ' 248 | 249 | # Maximum number of characters on a single line. 250 | max-line-length=88 251 | 252 | # Maximum number of lines in a module 253 | max-module-lines=1000 254 | 255 | # List of optional constructs for which whitespace checking is disabled. `dict- 256 | # separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. 257 | # `trailing-comma` allows a space between comma and closing bracket: (a, ). 258 | # `empty-line` allows space-only lines. 259 | no-space-check=trailing-comma, 260 | dict-separator 261 | 262 | # Allow the body of a class to be on the same line as the declaration if body 263 | # contains single statement. 264 | single-line-class-stmt=no 265 | 266 | # Allow the body of an if to be on the same line as the test if there is no 267 | # else. 268 | single-line-if-stmt=no 269 | 270 | 271 | [SPELLING] 272 | 273 | # Limits count of emitted suggestions for spelling mistakes 274 | max-spelling-suggestions=4 275 | 276 | # Spelling dictionary name. Available dictionaries: none. To make it working 277 | # install python-enchant package. 278 | spelling-dict= 279 | 280 | # List of comma separated words that should not be checked. 281 | spelling-ignore-words= 282 | 283 | # A path to a file that contains private dictionary; one word per line. 284 | spelling-private-dict-file= 285 | 286 | # Tells whether to store unknown words to indicated private dictionary in 287 | # --spelling-private-dict-file option instead of raising a message. 288 | spelling-store-unknown-words=no 289 | 290 | 291 | [SIMILARITIES] 292 | 293 | # Ignore comments when computing similarities. 294 | ignore-comments=yes 295 | 296 | # Ignore docstrings when computing similarities. 297 | ignore-docstrings=yes 298 | 299 | # Ignore imports when computing similarities. 300 | ignore-imports=no 301 | 302 | # Minimum lines number of a similarity. 303 | min-similarity-lines=4 304 | 305 | 306 | [VARIABLES] 307 | 308 | # List of additional names supposed to be defined in builtins. Remember that 309 | # you should avoid to define new builtins when possible. 310 | additional-builtins= 311 | 312 | # Tells whether unused global variables should be treated as a violation. 313 | allow-global-unused-variables=yes 314 | 315 | # List of strings which can identify a callback function by name. A callback 316 | # name must start or end with one of those strings. 317 | callbacks=cb_, 318 | _cb 319 | 320 | # A regular expression matching the name of dummy variables (i.e. expectedly 321 | # not used). 322 | dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ 323 | 324 | # Argument names that match this expression will be ignored. Default to name 325 | # with leading underscore 326 | ignored-argument-names=_.*|^ignored_|^unused_ 327 | 328 | # Tells whether we should check for unused import in __init__ files. 329 | init-import=no 330 | 331 | # List of qualified module names which can have objects that can redefine 332 | # builtins. 333 | redefining-builtins-modules=six.moves,past.builtins,future.builtins 334 | 335 | 336 | [BASIC] 337 | 338 | # Naming style matching correct argument names 339 | argument-naming-style=snake_case 340 | 341 | # Regular expression matching correct argument names. Overrides argument- 342 | # naming-style 343 | #argument-rgx= 344 | 345 | # Naming style matching correct attribute names 346 | attr-naming-style=snake_case 347 | 348 | # Regular expression matching correct attribute names. Overrides attr-naming- 349 | # style 350 | #attr-rgx= 351 | 352 | # Bad variable names which should always be refused, separated by a comma 353 | bad-names=foo, 354 | bar, 355 | baz, 356 | toto, 357 | tutu, 358 | tata 359 | 360 | # Naming style matching correct class attribute names 361 | class-attribute-naming-style=any 362 | 363 | # Regular expression matching correct class attribute names. Overrides class- 364 | # attribute-naming-style 365 | #class-attribute-rgx= 366 | 367 | # Naming style matching correct class names 368 | class-naming-style=PascalCase 369 | 370 | # Regular expression matching correct class names. Overrides class-naming-style 371 | #class-rgx= 372 | 373 | # Naming style matching correct constant names 374 | const-naming-style=UPPER_CASE 375 | 376 | # Regular expression matching correct constant names. Overrides const-naming- 377 | # style 378 | #const-rgx= 379 | 380 | # Minimum line length for functions/classes that require docstrings, shorter 381 | # ones are exempt. 382 | docstring-min-length=-1 383 | 384 | # Naming style matching correct function names 385 | function-naming-style=snake_case 386 | 387 | # Regular expression matching correct function names. Overrides function- 388 | # naming-style 389 | #function-rgx= 390 | 391 | # Good variable names which should always be accepted, separated by a comma 392 | good-names=i, 393 | j, 394 | k, 395 | ex, 396 | Run, 397 | _ 398 | 399 | # Include a hint for the correct naming format with invalid-name 400 | include-naming-hint=no 401 | 402 | # Naming style matching correct inline iteration names 403 | inlinevar-naming-style=any 404 | 405 | # Regular expression matching correct inline iteration names. Overrides 406 | # inlinevar-naming-style 407 | #inlinevar-rgx= 408 | 409 | # Naming style matching correct method names 410 | method-naming-style=snake_case 411 | 412 | # Regular expression matching correct method names. Overrides method-naming- 413 | # style 414 | #method-rgx= 415 | 416 | # Naming style matching correct module names 417 | module-naming-style=snake_case 418 | 419 | # Regular expression matching correct module names. Overrides module-naming- 420 | # style 421 | #module-rgx= 422 | 423 | # Colon-delimited sets of names that determine each other's naming style when 424 | # the name regexes allow several styles. 425 | name-group= 426 | 427 | # Regular expression which should only match function or class names that do 428 | # not require a docstring. 429 | no-docstring-rgx=^_ 430 | 431 | # List of decorators that produce properties, such as abc.abstractproperty. Add 432 | # to this list to register other decorators that produce valid properties. 433 | property-classes=abc.abstractproperty 434 | 435 | # Naming style matching correct variable names 436 | variable-naming-style=snake_case 437 | 438 | # Regular expression matching correct variable names. Overrides variable- 439 | # naming-style 440 | #variable-rgx= 441 | 442 | 443 | [MISCELLANEOUS] 444 | 445 | # List of note tags to take in consideration, separated by a comma. 446 | notes=FIXME, 447 | XXX, 448 | TODO 449 | 450 | 451 | [IMPORTS] 452 | 453 | # Allow wildcard imports from modules that define __all__. 454 | allow-wildcard-with-all=no 455 | 456 | # Analyse import fallback blocks. This can be used to support both Python 2 and 457 | # 3 compatible code, which means that the block might have code that exists 458 | # only in one or another interpreter, leading to false positives when analysed. 459 | analyse-fallback-blocks=no 460 | 461 | # Deprecated modules which should not be used, separated by a comma 462 | deprecated-modules=regsub, 463 | TERMIOS, 464 | Bastion, 465 | rexec 466 | 467 | # Create a graph of external dependencies in the given file (report RP0402 must 468 | # not be disabled) 469 | ext-import-graph= 470 | 471 | # Create a graph of every (i.e. internal and external) dependencies in the 472 | # given file (report RP0402 must not be disabled) 473 | import-graph= 474 | 475 | # Create a graph of internal dependencies in the given file (report RP0402 must 476 | # not be disabled) 477 | int-import-graph= 478 | 479 | # Force import order to recognize a module as part of the standard 480 | # compatibility libraries. 481 | known-standard-library= 482 | 483 | # Force import order to recognize a module as part of a third party library. 484 | known-third-party=enchant 485 | 486 | 487 | [DESIGN] 488 | 489 | # Maximum number of arguments for function / method 490 | max-args=5 491 | 492 | # Maximum number of attributes for a class (see R0902). 493 | max-attributes=7 494 | 495 | # Maximum number of boolean expressions in a if statement 496 | max-bool-expr=5 497 | 498 | # Maximum number of branch for function / method body 499 | max-branches=12 500 | 501 | # Maximum number of locals for function / method body 502 | max-locals=15 503 | 504 | # Maximum number of parents for a class (see R0901). 505 | max-parents=7 506 | 507 | # Maximum number of public methods for a class (see R0904). 508 | max-public-methods=20 509 | 510 | # Maximum number of return / yield for function / method body 511 | max-returns=6 512 | 513 | # Maximum number of statements in function / method body 514 | max-statements=50 515 | 516 | # Minimum number of public methods for a class (see R0903). 517 | min-public-methods=2 518 | 519 | 520 | [CLASSES] 521 | 522 | # List of method names used to declare (i.e. assign) instance attributes. 523 | defining-attr-methods=__init__, 524 | __new__, 525 | setUp 526 | 527 | # List of member names, which should be excluded from the protected access 528 | # warning. 529 | exclude-protected=_asdict, 530 | _fields, 531 | _replace, 532 | _source, 533 | _make 534 | 535 | # List of valid names for the first argument in a class method. 536 | valid-classmethod-first-arg=cls 537 | 538 | # List of valid names for the first argument in a metaclass class method. 539 | valid-metaclass-classmethod-first-arg=mcs 540 | 541 | 542 | [EXCEPTIONS] 543 | 544 | # Exceptions that will emit a warning when being caught. Defaults to 545 | # "Exception" 546 | overgeneral-exceptions=Exception 547 | -------------------------------------------------------------------------------- /pytest_extra_requirements.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | # Write extra requirements for running pytest here: 4 | # If you need ansible then uncomment the following line: 5 | #-ransible_pytest_extra_requirements.txt 6 | # If you need mock then uncomment the following line: 7 | #mock ; python_version < "3.0" 8 | -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Set version specific variables 3 | include_tasks: tasks/set_vars.yml 4 | 5 | - name: Populate service facts 6 | service_facts: 7 | 8 | - name: Set variable `timesync_services` with filtered uniq service names 9 | set_fact: 10 | timesync_services: "{{ ansible_facts.services.values() | 11 | selectattr('name', 'defined') | 12 | selectattr('status', 'defined') | 13 | rejectattr('status', 'match', '^not-found$') | 14 | rejectattr('status', 'match', '^masked$') | 15 | rejectattr('status', 'match', '^failed$') | 16 | map(attribute='name') | 17 | map('regex_replace', '[.]service.*$', '') | 18 | map('regex_replace', '@$', '') | 19 | unique | 20 | list }}" 21 | 22 | - name: Check that variable 'timesync_services' is defined 23 | assert: 24 | that: timesync_services is defined 25 | fail_msg: "Variable 'timesync_services' is not defined" 26 | 27 | - name: Check if only NTP is needed 28 | set_fact: 29 | timesync_mode: 1 30 | when: timesync_ptp_domains | length == 0 31 | 32 | - name: Check if single PTP is needed 33 | set_fact: 34 | timesync_mode: 2 35 | when: 36 | - timesync_mode is not defined 37 | - timesync_ntp_servers | length == 0 38 | - timesync_ptp_domains | length == 1 39 | - timesync_ptp_domains[0].interfaces | length == 1 40 | 41 | - name: Check if both NTP and PTP are needed 42 | set_fact: 43 | timesync_mode: 3 44 | when: timesync_mode is not defined 45 | 46 | - name: Determine current NTP provider 47 | timesync_provider: # noqa fqcn[action] 48 | when: 49 | - timesync_mode != 2 50 | - timesync_ntp_provider | length == 0 51 | 52 | - name: Select NTP provider 53 | set_fact: 54 | timesync_ntp_provider: "{{ 55 | timesync_ntp_provider_current | 56 | default(timesync_ntp_provider_os_default, true) }}" 57 | when: 58 | - timesync_mode != 2 59 | - timesync_ntp_provider | length == 0 60 | 61 | - name: Install chrony 62 | package: 63 | name: chrony 64 | state: present 65 | use: "{{ (__timesync_is_ostree | d(false)) | 66 | ternary('ansible.posix.rhel_rpm_ostree', omit) }}" 67 | when: 68 | - timesync_mode != 2 69 | - timesync_ntp_provider == 'chrony' 70 | register: timesync_chrony_result 71 | 72 | - name: Install ntp 73 | package: 74 | name: ntp 75 | state: present 76 | use: "{{ (__timesync_is_ostree | d(false)) | 77 | ternary('ansible.posix.rhel_rpm_ostree', omit) }}" 78 | when: 79 | - timesync_mode != 2 80 | - timesync_ntp_provider == 'ntp' 81 | register: timesync_ntp_result 82 | 83 | - name: Install linuxptp 84 | package: 85 | name: linuxptp 86 | state: present 87 | use: "{{ (__timesync_is_ostree | d(false)) | 88 | ternary('ansible.posix.rhel_rpm_ostree', omit) }}" 89 | when: timesync_mode != 1 90 | register: timesync_linuxptp_result 91 | 92 | - name: Handle reboot for transactional update systems 93 | when: 94 | - __timesync_is_transactional | d(false) 95 | - timesync_chrony_result is changed or 96 | timesync_ntp_result is changed or 97 | timesync_linuxptp_result is changed 98 | block: 99 | - name: Notify user that reboot is needed to apply changes 100 | debug: 101 | msg: > 102 | Reboot required to apply changes due to transactional updates. 103 | 104 | - name: Reboot transactional update systems 105 | reboot: 106 | msg: Rebooting the system to apply transactional update changes. 107 | when: timesync_transactional_update_reboot_ok | bool 108 | 109 | - name: Fail if reboot is needed and not set 110 | fail: 111 | msg: > 112 | Reboot is required but not allowed. Please set 113 | 'timesync_transactional_update_reboot_ok' to proceed. 114 | when: 115 | - timesync_transactional_update_reboot_ok is none 116 | 117 | - name: Gather package facts 118 | package_facts: 119 | manager: auto 120 | 121 | - name: Run phc_ctl on PTP interface 122 | command: phc_ctl -q {{ timesync_ptp_domains[0].interfaces[0] }} 123 | register: timesync_phc_ctl_output 124 | changed_when: false 125 | check_mode: false 126 | when: timesync_mode == 2 127 | ignore_errors: true 128 | 129 | - name: Check if PTP interface supports HW timestamping 130 | set_fact: 131 | timesync_mode2_hwts: "{{ timesync_phc_ctl_output.rc == 0 }}" 132 | when: timesync_mode == 2 133 | 134 | - name: Generate chrony.conf file 135 | template: 136 | src: chrony.conf.j2 137 | dest: "{{ timesync_chrony_conf_path }}" 138 | backup: true 139 | mode: "0644" 140 | # wokeignore:rule=master 141 | notify: Restart {{ 'chronyd' if timesync_mode == 1 else 'timemaster' }} 142 | when: 143 | - timesync_mode != 2 144 | - timesync_ntp_provider == 'chrony' 145 | - "'chrony' in ansible_facts.packages" 146 | 147 | - name: Generate chronyd sysconfig file 148 | template: 149 | src: chronyd.sysconfig.j2 150 | dest: "{{ timesync_chrony_sysconfig_path }}" 151 | backup: true 152 | mode: "0644" 153 | notify: Restart chronyd 154 | when: 155 | - timesync_mode == 1 156 | - timesync_ntp_provider == 'chrony' 157 | - "'chrony' in ansible_facts.packages" 158 | 159 | - name: Generate ntp.conf file 160 | template: 161 | src: ntp.conf.j2 162 | dest: /etc/ntp.conf 163 | backup: true 164 | mode: "0644" 165 | # wokeignore:rule=master 166 | notify: Restart {{ 'ntpd' if timesync_mode == 1 else 'timemaster' }} 167 | when: 168 | - timesync_mode != 2 169 | - timesync_ntp_provider == 'ntp' 170 | - "'ntp' in ansible_facts.packages" 171 | 172 | - name: Generate ntpd sysconfig file 173 | template: 174 | src: ntpd.sysconfig.j2 175 | dest: "{{ timesync_ntp_sysconfig_path }}" 176 | backup: true 177 | mode: "0644" 178 | notify: Restart ntpd 179 | when: 180 | - timesync_mode == 1 181 | - timesync_ntp_provider == 'ntp' 182 | - "'ntp' in ansible_facts.packages" 183 | 184 | - name: Generate ptp4l.conf file 185 | template: 186 | src: ptp4l.conf.j2 187 | dest: /etc/ptp4l.conf 188 | backup: true 189 | mode: "0644" 190 | notify: Restart ptp4l 191 | when: 192 | - timesync_mode == 2 193 | - "'linuxptp' in ansible_facts.packages" 194 | 195 | - name: Generate ptp4l sysconfig file 196 | template: 197 | src: ptp4l.sysconfig.j2 198 | dest: "{{ timesync_ptp4l_sysconfig_path }}" 199 | backup: true 200 | mode: "0644" 201 | notify: Restart ptp4l 202 | when: 203 | - timesync_mode == 2 204 | - "'linuxptp' in ansible_facts.packages" 205 | - timesync_ptp4l_sysconfig_path | length > 0 206 | 207 | - name: Generate phc2sys sysconfig file 208 | template: 209 | src: phc2sys.sysconfig.j2 210 | dest: /etc/sysconfig/phc2sys 211 | backup: true 212 | mode: "0644" 213 | notify: Restart phc2sys 214 | when: 215 | - timesync_mode == 2 216 | - timesync_mode2_hwts 217 | - "'linuxptp' in ansible_facts.packages" 218 | - timesync_phc2sys_sysconfig_path | length > 0 219 | 220 | # wokeignore:rule=master 221 | - name: Generate timemaster.conf file 222 | template: 223 | # wokeignore:rule=master 224 | src: timemaster.conf.j2 225 | # wokeignore:rule=master 226 | dest: "{{ timesync_timemaster_config_path }}" 227 | backup: true 228 | mode: "0644" 229 | # wokeignore:rule=master 230 | notify: Restart timemaster 231 | when: 232 | - timesync_mode == 3 233 | - "'linuxptp' in ansible_facts.packages" 234 | 235 | - name: Update network sysconfig file 236 | lineinfile: 237 | dest: /etc/sysconfig/network 238 | create: true 239 | backup: true 240 | mode: "0644" 241 | regexp: '^PEERNTP=' 242 | line: 'PEERNTP=no' 243 | # yamllint disable-line rule:line-length 244 | state: "{{ 'absent' if timesync_dhcp_ntp_servers else 'present' }}" # noqa args[module] 245 | notify: Restart {{ timesync_ntp_provider + 'd' }} 246 | when: 247 | - timesync_mode == 1 248 | - ansible_facts['os_family'] == 'RedHat' 249 | 250 | - name: Disable chronyd 251 | service: 252 | name: chronyd 253 | state: stopped 254 | enabled: false 255 | when: 256 | - timesync_mode != 1 or timesync_ntp_provider != 'chrony' 257 | - "'chronyd' in timesync_services" 258 | register: __disable_result 259 | failed_when: 260 | - __disable_result is failed 261 | - not __disable_result.msg is match( 262 | 'Could not find the requested service chronyd:') 263 | 264 | - name: Disable ntpd 265 | service: 266 | name: ntpd 267 | state: stopped 268 | enabled: false 269 | when: 270 | - timesync_mode != 1 or timesync_ntp_provider != 'ntp' 271 | - "'ntpd' in timesync_services" 272 | register: __disable_result 273 | failed_when: 274 | - __disable_result is failed 275 | - not __disable_result.msg is match( 276 | 'Could not find the requested service ntpd:') 277 | 278 | - name: Disable ntpdate 279 | service: 280 | name: ntpdate 281 | state: stopped 282 | enabled: false 283 | when: "'ntpdate' in timesync_services" 284 | register: __disable_result 285 | failed_when: 286 | - __disable_result is failed 287 | - not __disable_result.msg is match( 288 | 'Could not find the requested service ntpdate:') 289 | 290 | - name: Disable sntp 291 | service: 292 | name: sntp 293 | state: stopped 294 | enabled: false 295 | when: "'sntp' in timesync_services" 296 | register: __disable_result 297 | failed_when: 298 | - __disable_result is failed 299 | - not __disable_result.msg is match( 300 | 'Could not find the requested service sntp:') 301 | 302 | - name: Disable ptp4l 303 | service: 304 | name: ptp4l 305 | state: stopped 306 | enabled: false 307 | when: 308 | - timesync_mode != 2 309 | - "'ptp4l' in timesync_services" 310 | register: __disable_result 311 | failed_when: 312 | - __disable_result is failed 313 | - not __disable_result.msg is match( 314 | 'Could not find the requested service ptp4l:') 315 | 316 | - name: Disable phc2sys 317 | service: 318 | name: phc2sys 319 | state: stopped 320 | enabled: false 321 | when: 322 | - timesync_mode != 2 or not timesync_mode2_hwts 323 | - "'phc2sys' in timesync_services" 324 | register: __disable_result 325 | failed_when: 326 | - __disable_result is failed 327 | - not __disable_result.msg is match( 328 | 'Could not find the requested service phc2sys:') 329 | 330 | # wokeignore:rule=master 331 | - name: Disable timemaster 332 | vars: 333 | # wokeignore:rule=master 334 | __timemstr: timemaster 335 | service: 336 | name: "{{ __timemstr }}" 337 | state: stopped 338 | enabled: false 339 | when: 340 | - timesync_mode != 3 341 | - __timemstr in timesync_services 342 | register: __disable_result 343 | failed_when: 344 | - __disable_result is failed 345 | - not __disable_result.msg is match( 346 | 'Could not find the requested service ' ~ __timemstr ~ ':') 347 | 348 | - name: Enable chronyd 349 | service: 350 | name: chronyd 351 | state: started 352 | enabled: true 353 | when: 354 | - timesync_mode == 1 355 | - timesync_ntp_provider == 'chrony' 356 | 357 | - name: Enable ntpd 358 | service: 359 | name: ntpd 360 | state: started 361 | enabled: true 362 | when: 363 | - timesync_mode == 1 364 | - timesync_ntp_provider == 'ntp' 365 | 366 | - name: Enable ptp4l 367 | service: 368 | name: ptp4l 369 | state: started 370 | enabled: true 371 | when: timesync_mode == 2 372 | 373 | - name: Enable phc2sys 374 | service: 375 | name: phc2sys 376 | state: started 377 | enabled: true 378 | when: 379 | - timesync_mode == 2 380 | - timesync_mode2_hwts 381 | 382 | # wokeignore:rule=master 383 | - name: Enable timemaster 384 | service: 385 | # wokeignore:rule=master 386 | name: timemaster 387 | state: started 388 | enabled: true 389 | when: timesync_mode == 3 390 | -------------------------------------------------------------------------------- /tasks/set_vars.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Ensure ansible_facts used by role 3 | setup: 4 | gather_subset: "{{ __timesync_required_facts_subsets }}" 5 | when: __timesync_required_facts | 6 | difference(ansible_facts.keys() | list) | length > 0 7 | 8 | - name: Determine if system is ostree and set flag 9 | when: not __timesync_is_ostree is defined 10 | block: 11 | - name: Check if system is ostree 12 | stat: 13 | path: /run/ostree-booted 14 | register: __ostree_booted_stat 15 | 16 | - name: Set flag to indicate system is ostree 17 | set_fact: 18 | __timesync_is_ostree: "{{ __ostree_booted_stat.stat.exists }}" 19 | 20 | - name: Determine if system is transactional update and set flag 21 | when: not __timesync_is_transactional is defined 22 | block: 23 | - name: Check if transactional-update exists in /sbin 24 | stat: 25 | path: /sbin/transactional-update 26 | register: __transactional_update_stat 27 | 28 | - name: Set flag if transactional-update exists 29 | set_fact: 30 | __timesync_is_transactional: "{{ __transactional_update_stat.stat.exists }}" 31 | 32 | - name: Set platform/version specific variables 33 | include_vars: "{{ lookup('first_found', ffparams) }}" 34 | vars: 35 | ffparams: 36 | files: 37 | - "{{ ansible_facts['distribution'] }}_\ 38 | {{ ansible_facts['distribution_version'] }}.yml" 39 | - "{{ ansible_facts['distribution'] }}_\ 40 | {{ ansible_facts['distribution_major_version'] }}.yml" 41 | - "{{ ansible_facts['distribution'] }}.yml" 42 | - "{{ ansible_facts['os_family'] }}.yml" 43 | - "default.yml" 44 | paths: 45 | - "{{ role_path }}/vars" 46 | -------------------------------------------------------------------------------- /templates/chrony.conf.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment }} 2 | {{ "system_role:timesync" | comment(prefix="", postfix="") }} 3 | 4 | {% for value in timesync_ntp_servers %} 5 | {{ 'pool' if 'pool' in value and value['pool'] else 'server' }} {{ 6 | value['hostname'] }}{{ 7 | ' minpoll {0}'.format(value['minpoll']) if 'minpoll' in value else '' }}{{ 8 | ' maxpoll {0}'.format(value['maxpoll']) if 'maxpoll' in value else '' }}{{ 9 | ' nts' if 'nts' in value and value['nts'] else '' }}{{ 10 | ' iburst' if 'iburst' in value and value['iburst'] else '' }}{{ 11 | ' prefer' if 'prefer' in value and value['prefer'] else '' }}{{ 12 | ' trust' if 'trust' in value and value['trust'] else '' }}{{ 13 | ' xleave' if __timesync_chrony_version is version('3.0', '>=') and 14 | 'xleave' in value and value['xleave'] else '' }}{{ 15 | ' filter {0}'.format(value['filter']) 16 | if __timesync_chrony_version is version('3.4', '>=') and 17 | 'filter' in value and value['filter'] > 1 else '' }} 18 | {% endfor %} 19 | 20 | {% if timesync_dhcp_ntp_servers and timesync_chrony_dhcp_sourcedir %} 21 | # Use NTP servers from DHCP. 22 | sourcedir {{ timesync_chrony_dhcp_sourcedir }} 23 | 24 | {% endif %} 25 | {% if timesync_step_threshold | float != 0.0 %} 26 | # Allow the system clock to be stepped in the first three updates. 27 | makestep {{ timesync_step_threshold if timesync_step_threshold | float > 0.0 else '1.0' }} 3 28 | 29 | {% endif %} 30 | # Enable kernel synchronization of the real-time clock (RTC). 31 | rtcsync 32 | 33 | {% if __timesync_chrony_version is version('3.0', '>=') and 34 | timesync_ntp_hwts_interfaces|length > 0 %} 35 | # Enable hardware timestamping. 36 | {% for interface in timesync_ntp_hwts_interfaces %} 37 | hwtimestamp {{ interface }}{% if __timesync_chrony_version is version('3.1', '>=') %} 38 | minpoll {{ (timesync_ntp_servers | rejectattr('minpoll', 'undefined') | 39 | map(attribute='minpoll') | list + [0]) | min }}{% endif %} 40 | 41 | {% endfor %} 42 | 43 | {% endif %} 44 | {% if timesync_min_sources | int > 1 %} 45 | # Increase the minimum number of selectable sources required to adjust 46 | # the system clock. 47 | minsources {{ timesync_min_sources }} 48 | 49 | {% endif %} 50 | # Record the rate at which the system clock gains/losses time. 51 | driftfile /var/lib/chrony/drift 52 | 53 | {% if __timesync_chrony_version is version('2.0', '<') %} 54 | # Decrease weight of stratum in source selection. 55 | stratumweight 0.001 56 | 57 | # Listen for commands only on localhost. 58 | bindcmdaddress 127.0.0.1 59 | bindcmdaddress ::1 60 | 61 | {% endif %} 62 | {% if __timesync_chrony_version is version('2.2', '<') %} 63 | # Specify file containing keys for NTP and command authentication. 64 | keyfile /etc/chrony.keys 65 | 66 | # Specify ID of command key. 67 | commandkey 1 68 | 69 | # Generate command key if missing. 70 | generatecommandkey 71 | 72 | {% endif %} 73 | {% if __timesync_chrony_version is version('4.0', '>=') %} 74 | # Save NTS keys and cookies. 75 | ntsdumpdir /var/lib/chrony 76 | 77 | {% endif %} 78 | {% if timesync_max_distance | int != 0 %} 79 | # Limit maximum root distance. 80 | maxdistance {{ timesync_max_distance }} 81 | 82 | {% endif %} 83 | {% if timesync_chrony_custom_settings | default([]) | length > 0 %} 84 | # Custom settings 85 | {% for custom_setting in timesync_chrony_custom_settings %} 86 | {{ custom_setting }} 87 | {% endfor %} 88 | {% endif %} 89 | -------------------------------------------------------------------------------- /templates/chronyd.sysconfig.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment }} 2 | {{ "system_role:timesync" | comment(prefix="", postfix="") }} 3 | 4 | OPTIONS="{{ timesync_chrony_sysconfig_options }}{{ ' -4' if timesync_ntp_ip_family == 'IPv4' 5 | else ' -6' if timesync_ntp_ip_family == 'IPv6' else '' }}" 6 | -------------------------------------------------------------------------------- /templates/ntp.conf.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment }} 2 | {{ "system_role:timesync" | comment(prefix="", postfix="") }} 3 | 4 | {% for value in timesync_ntp_servers %} 5 | {{ 'pool' if 'pool' in value and value['pool'] else 'server' }} {{ 6 | value['hostname'] }}{{ 7 | ' minpoll {0}'.format(value['minpoll']) if 'minpoll' in value else '' }}{{ 8 | ' maxpoll {0}'.format(value['maxpoll']) if 'maxpoll' in value else '' }}{{ 9 | ' iburst' if 'iburst' in value and value['iburst'] else '' }}{{ 10 | ' prefer' if 'prefer' in value and value['prefer'] else '' }}{{ 11 | ' true' if 'trust' in value and value['trust'] else '' }} 12 | {% if 'nts' in value and value['nts'] %} 13 | # Disable clock control due to missing NTS support. 14 | disable ntp 15 | {% endif %} 16 | {% endfor %} 17 | 18 | driftfile /var/lib/ntp/drift 19 | 20 | # Permit time synchronization with our time source, but do not 21 | # permit the source to query or modify the service on this system. 22 | restrict default nomodify notrap nopeer noquery 23 | {% if __timesync_ntp_version is version('4.2.6', '<') %} 24 | restrict -6 default nomodify notrap nopeer noquery 25 | {% endif %} 26 | 27 | # Permit all access over the loopback interface. This could 28 | # be tightened as well, but to do so would effect some of 29 | # the administrative functions. 30 | restrict 127.0.0.1 31 | restrict ::1 32 | 33 | {% if timesync_step_threshold | float >= 0.0 %} 34 | # Specify the step threshold. 35 | tinker step {{ timesync_step_threshold }} 36 | 37 | {% endif %} 38 | {% if timesync_min_sources | int > 1 %} 39 | # Increase the minimum number of selectable sources required to adjust 40 | # the system clock. 41 | tos minsane {{ timesync_min_sources }} 42 | 43 | {% endif %} 44 | {% if timesync_max_distance | int != 0 %} 45 | # Limit maximum root distance. 46 | tos maxdist {{ timesync_max_distance }} 47 | 48 | {% endif %} 49 | -------------------------------------------------------------------------------- /templates/ntpd.sysconfig.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment }} 2 | {{ "system_role:timesync" | comment(prefix="", postfix="") }} 3 | 4 | OPTIONS="-g{{ ' -u ntp:ntp -p /var/run/ntpd.pid' if ansible_distribution in ['OracleLinux', 'RedHat', 'CentOS'] and ansible_distribution_major_version | int < 7 else '' }}{{ 5 | ' -4' if timesync_ntp_ip_family == 'IPv4' 6 | else ' -6' if timesync_ntp_ip_family == 'IPv6' 7 | else '' }}" 8 | -------------------------------------------------------------------------------- /templates/phc2sys.sysconfig.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment }} 2 | {{ "system_role:timesync" | comment(prefix="", postfix="") }} 3 | 4 | OPTIONS="-a -r{{ ' -F ' ~ timesync_step_threshold if timesync_step_threshold | float >= 0.0 else '' }}" 5 | -------------------------------------------------------------------------------- /templates/ptp4l.conf.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment }} 2 | {{ "system_role:timesync" | comment(prefix="", postfix="") }} 3 | 4 | [global] 5 | {# wokeignore:rule=slave #} 6 | slaveOnly 1 7 | domainNumber {{ timesync_ptp_domains[0].number }} 8 | time_stamping {{ 'hardware' if timesync_mode2_hwts else 'software' }} 9 | {% if 'transport' in timesync_ptp_domains[0] %} 10 | network_transport {{ timesync_ptp_domains[0]['transport'] }} 11 | {% endif %} 12 | {% if 'udp_ttl' in timesync_ptp_domains[0] %} 13 | udp_ttl {{ timesync_ptp_domains[0]['udp_ttl'] }} 14 | {% endif %} 15 | {% if 'hybrid_e2e' in timesync_ptp_domains[0] and timesync_ptp_domains[0]['hybrid_e2e'] %} 16 | hybrid_e2e 1 17 | {% endif %} 18 | {% if not timesync_mode2_hwts and timesync_step_threshold | float >= 0.0 %} 19 | first_step_threshold {{ timesync_step_threshold }} 20 | {% endif %} 21 | -------------------------------------------------------------------------------- /templates/ptp4l.sysconfig.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment }} 2 | {{ "system_role:timesync" | comment(prefix="", postfix="") }} 3 | 4 | OPTIONS="-f /etc/ptp4l.conf -i {{ timesync_ptp_domains[0].interfaces[0] }}" 5 | -------------------------------------------------------------------------------- /templates/timemaster.conf.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment }} 2 | {{ "system_role:timesync" | comment(prefix="", postfix="") }} 3 | 4 | {% for value in timesync_ptp_domains %} 5 | [ptp_domain {{ value['number'] }}] 6 | interfaces {{ value['interfaces']|join(' ') }} 7 | {% if 'delay' in value %} 8 | delay {{ value['delay'] }} 9 | {% endif %} 10 | {% if 'transport' in value %} 11 | ptp4l_option network_transport {{ value['transport'] }} 12 | {% endif %} 13 | {% if 'udp_ttl' in value %} 14 | ptp4l_option udp_ttl {{ value['udp_ttl'] }} 15 | {% endif %} 16 | {% if 'hybrid_e2e' in value and value['hybrid_e2e'] %} 17 | ptp4l_option hybrid_e2e 1 18 | {% endif %} 19 | 20 | {% endfor %} 21 | {# wokeignore:rule=master #} 22 | [timemaster] 23 | ntp_program {{ 'ntpd' if timesync_ntp_provider == 'ntp' else 'chronyd' }} 24 | 25 | [chrony.conf] 26 | include /etc/chrony.conf 27 | 28 | [ntp.conf] 29 | includefile /etc/ntp.conf 30 | 31 | [ptp4l.conf] 32 | 33 | [chronyd] 34 | path /usr/sbin/chronyd 35 | {% if timesync_ntp_provider == 'chrony' and __timesync_chrony_version is version('1.30', '<') %} 36 | options -u chrony 37 | {% endif %} 38 | 39 | [ntpd] 40 | path /usr/sbin/ntpd 41 | options -u ntp:ntp -g 42 | 43 | [phc2sys] 44 | path /usr/sbin/phc2sys 45 | options -l 6 46 | 47 | [ptp4l] 48 | path /usr/sbin/ptp4l 49 | options -l 6 50 | -------------------------------------------------------------------------------- /tests/.fmf/version: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /tests/inventory.yaml.j2: -------------------------------------------------------------------------------- 1 | all: 2 | hosts: 3 | {{ inventory_hostname }}: 4 | {% for key in ["ansible_all_ipv4_addresses", "ansible_all_ipv6_addresses", 5 | "ansible_default_ipv4", "ansible_default_ipv6", "ansible_host", 6 | "ansible_port", "ansible_ssh_common_args", 7 | "ansible_ssh_private_key_file","ansible_user"] %} 8 | {% if key in hostvars[inventory_hostname] %} 9 | {{ key }}: {{ hostvars[inventory_hostname][key] }} 10 | {% endif %} 11 | {% endfor %} 12 | -------------------------------------------------------------------------------- /tests/provision.fmf: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | standard-inventory-qcow2: 4 | qemu: 5 | net_nic: 6 | # Use qemu-system-x86_64 -net nic,model=help for a list of available devices. 7 | model: e1000 8 | -------------------------------------------------------------------------------- /tests/roles/linux-system-roles.timesync/defaults: -------------------------------------------------------------------------------- 1 | ../../../defaults -------------------------------------------------------------------------------- /tests/roles/linux-system-roles.timesync/handlers: -------------------------------------------------------------------------------- 1 | ../../../handlers -------------------------------------------------------------------------------- /tests/roles/linux-system-roles.timesync/library: -------------------------------------------------------------------------------- 1 | ../../../library -------------------------------------------------------------------------------- /tests/roles/linux-system-roles.timesync/meta: -------------------------------------------------------------------------------- 1 | ../../../meta -------------------------------------------------------------------------------- /tests/roles/linux-system-roles.timesync/tasks: -------------------------------------------------------------------------------- 1 | ../../../tasks -------------------------------------------------------------------------------- /tests/roles/linux-system-roles.timesync/templates: -------------------------------------------------------------------------------- 1 | ../../../templates -------------------------------------------------------------------------------- /tests/roles/linux-system-roles.timesync/vars: -------------------------------------------------------------------------------- 1 | ../../../vars -------------------------------------------------------------------------------- /tests/setup-snapshot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Setup snapshot 3 | hosts: all 4 | tasks: 5 | - name: Set platform/version specific variables 6 | include_role: 7 | name: linux-system-roles.timesync 8 | tasks_from: set_vars.yml 9 | public: true 10 | 11 | - name: Install test packages to put them in the local cache 12 | package: 13 | name: 14 | - chrony 15 | - ntp 16 | - linuxptp 17 | state: present 18 | no_log: true 19 | ignore_errors: true # noqa ignore-errors 20 | 21 | - name: Uninstall test packages 22 | package: 23 | name: 24 | - chrony 25 | - ntp 26 | - linuxptp 27 | state: absent 28 | no_log: true 29 | -------------------------------------------------------------------------------- /tests/tasks/check_header.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Get file 4 | slurp: 5 | path: "{{ __file }}" 6 | register: __content 7 | when: not __file_content is defined 8 | 9 | - name: Check for presence of ansible managed header, fingerprint 10 | assert: 11 | that: 12 | - __ansible_managed in content 13 | - __fingerprint in content 14 | vars: 15 | content: "{{ (__file_content | d(__content)).content | b64decode }}" 16 | __ansible_managed: "{{ lookup('template', 'get_ansible_managed.j2') }}" 17 | -------------------------------------------------------------------------------- /tests/tasks/cleanup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Reset settings and provider to os default 3 | include_role: 4 | name: linux-system-roles.timesync 5 | vars: 6 | timesync_ntp_provider: "{{ timesync_ntp_provider_os_default }}" 7 | timesync_ptp_domains: [] 8 | timesync_ntp_servers: [] 9 | -------------------------------------------------------------------------------- /tests/tasks/setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # common test setup tasks 3 | - name: Determine if system is ostree and set flag 4 | when: not __timesync_is_ostree is defined 5 | block: 6 | - name: Check if system is ostree 7 | stat: 8 | path: /run/ostree-booted 9 | register: __ostree_booted_stat 10 | 11 | - name: Set flag to indicate system is ostree 12 | set_fact: 13 | __timesync_is_ostree: "{{ __ostree_booted_stat.stat.exists }}" 14 | 15 | - name: Ensure iproute for gathering default_ipv4 fact 16 | package: 17 | name: iproute # for fact gathering for ip facts 18 | state: present 19 | use: "{{ (__timesync_is_ostree | d(false)) | 20 | ternary('ansible.posix.rhel_rpm_ostree', omit) }}" 21 | 22 | - name: Ensure ansible_facts used by test 23 | setup: 24 | gather_subset: "{{ __required_facts_subsets }}" 25 | when: __required_facts | 26 | difference(ansible_facts.keys() | list) | length > 0 27 | vars: 28 | __required_facts: 29 | - default_ipv4 30 | - distribution 31 | - distribution_major_version 32 | - distribution_version 33 | - os_family 34 | __required_facts_subsets: "{{ ['!all', '!min'] + __required_facts }}" 35 | 36 | - name: Debug 37 | debug: 38 | msg: facts {{ ansible_facts | to_nice_json }} 39 | 40 | - name: Skip test on ostree systems if unsupported 41 | meta: end_host 42 | when: 43 | - __timesync_ostree_unsupported | d(false) 44 | - __timesync_is_ostree | d(false) 45 | -------------------------------------------------------------------------------- /tests/templates/get_ansible_managed.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment(__comment_type | d("plain")) }} 2 | -------------------------------------------------------------------------------- /tests/tests_chrony.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Configure time synchronization with NTP servers 3 | hosts: all 4 | vars: 5 | timesync_chrony_custom_settings: 6 | - "logdir /var/log/chrony" 7 | - "log tracking" 8 | timesync_ntp_servers: 9 | - hostname: 172.16.123.1 10 | - hostname: 172.16.123.2 11 | iburst: true 12 | minpoll: 4 13 | - hostname: 172.16.123.3 14 | pool: true 15 | iburst: true 16 | minpoll: 4 17 | maxpoll: 6 18 | timesync_ntp_provider: chrony 19 | timesync_step_threshold: 0.01 20 | timesync_dhcp_ntp_servers: true 21 | timesync_min_sources: 2 22 | 23 | tasks: 24 | - name: Run the role 25 | include_role: 26 | name: linux-system-roles.timesync 27 | public: true 28 | 29 | - name: Run chrony tests 30 | tags: tests::verify 31 | block: 32 | - name: Flush handlers 33 | meta: flush_handlers 34 | 35 | - name: Wait for services to start 36 | wait_for: 37 | timeout: 2 38 | 39 | - name: Get list of currently used time sources 40 | shell: chronyc -n sources || ntpq -pn 41 | register: sources 42 | changed_when: false 43 | 44 | - name: Check time sources 45 | assert: 46 | that: 47 | - "'172.16.123.1' in sources.stdout" 48 | - "'172.16.123.2' in sources.stdout" 49 | - "'172.16.123.3' in sources.stdout" 50 | 51 | - name: Get list of log files 52 | command: ls /var/log/chrony/ 53 | register: logfiles 54 | changed_when: false 55 | 56 | - name: Check log generated files 57 | assert: 58 | that: 59 | - "'tracking.log' in logfiles.stdout" 60 | 61 | - name: Check headers for ansible_managed, fingerprint 62 | include_tasks: tasks/check_header.yml 63 | loop: 64 | - "{{ timesync_chrony_conf_path }}" 65 | - "{{ timesync_chrony_sysconfig_path }}" 66 | loop_control: 67 | loop_var: __file 68 | vars: 69 | __fingerprint: "system_role:timesync" 70 | 71 | always: 72 | - name: Cleanup after tests 73 | include_tasks: tasks/cleanup.yml 74 | -------------------------------------------------------------------------------- /tests/tests_default.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Ensure that the role runs with default parameters 3 | hosts: all 4 | gather_facts: false 5 | roles: 6 | - linux-system-roles.timesync 7 | -------------------------------------------------------------------------------- /tests/tests_default_vars.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Ensure that the role declares all parameters in defaults 3 | hosts: all 4 | tasks: 5 | - name: Include the timesync role 6 | include_role: 7 | name: linux-system-roles.timesync 8 | public: true 9 | 10 | - name: Check that public vars are defined 11 | assert: 12 | that: vars[item] is defined 13 | loop: 14 | - timesync_ntp_servers 15 | - timesync_ptp_domains 16 | - timesync_dhcp_ntp_servers 17 | - timesync_step_threshold 18 | - timesync_min_sources 19 | - timesync_ntp_hwts_interfaces 20 | - timesync_ntp_provider 21 | - timesync_max_distance 22 | - timesync_ntp_ip_family 23 | -------------------------------------------------------------------------------- /tests/tests_default_wrapper.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create static inventory from hostvars 3 | tags: 4 | - 'tests::slow' 5 | hosts: all 6 | tasks: 7 | - name: Create temporary file 8 | tempfile: 9 | state: file 10 | suffix: .inventory.yaml 11 | register: tempinventory 12 | delegate_to: localhost 13 | 14 | - name: Create static inventory from hostvars 15 | template: 16 | src: inventory.yaml.j2 17 | dest: "{{ tempinventory.path }}" 18 | mode: '0644' 19 | delegate_to: localhost 20 | 21 | - name: Run tests_default.yml normally 22 | tags: 23 | - 'tests::slow' 24 | import_playbook: tests_default.yml 25 | 26 | - name: Run tests_default.yml in check_mode 27 | tags: 28 | - 'tests::slow' 29 | hosts: all 30 | tasks: 31 | - name: Run ansible-playbook with tests_default.yml in check mode 32 | command: >- 33 | {{ __ap_cmd | quote }} -vvv -i {{ tempinventory.path }} 34 | --check tests_default.yml 35 | changed_when: false 36 | delegate_to: localhost 37 | vars: 38 | __ap_cmd: "{{ ansible_playbook_filepath | d('ansible-playbook') }}" 39 | 40 | - name: Remove the temporary file 41 | file: 42 | path: "{{ tempinventory.path }}" 43 | state: absent 44 | when: tempinventory.path is defined 45 | delegate_to: localhost 46 | -------------------------------------------------------------------------------- /tests/tests_ntp.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Configure time synchronization with NTP servers 3 | hosts: all 4 | vars: 5 | timesync_ntp_servers: 6 | - hostname: 172.16.123.1 7 | - hostname: 172.16.123.2 8 | iburst: false 9 | prefer: false 10 | trust: false 11 | minpoll: 4 12 | - hostname: 172.16.123.3 13 | pool: true 14 | iburst: true 15 | minpoll: 4 16 | maxpoll: 6 17 | prefer: true 18 | trust: true 19 | xleave: true 20 | filter: 3 21 | timesync_step_threshold: 0.01 22 | timesync_dhcp_ntp_servers: true 23 | timesync_min_sources: 2 24 | timesync_ntp_hwts_interfaces: ["*"] 25 | tasks: 26 | - name: Run the role 27 | include_role: 28 | name: linux-system-roles.timesync 29 | public: true 30 | 31 | - name: Run test 32 | tags: tests::verify 33 | block: 34 | - name: Flush handlers 35 | meta: flush_handlers 36 | 37 | - name: Wait for services to start 38 | wait_for: 39 | timeout: 2 40 | 41 | - name: Get list of currently used time sources 42 | shell: chronyc -n sources || ntpq -pn 43 | register: sources 44 | changed_when: false 45 | 46 | - name: Check time sources 47 | assert: 48 | that: 49 | - "'172.16.123.1' in sources.stdout" 50 | - "'172.16.123.2' in sources.stdout" 51 | - "'172.16.123.3' in sources.stdout" 52 | 53 | - name: Fetch chrony.conf file 54 | slurp: 55 | src: /etc/chrony.conf 56 | when: "'LastRx' in sources.stdout" 57 | register: chrony_conf_encoded 58 | 59 | - name: Fetch ntp.conf file 60 | slurp: 61 | src: /etc/ntp.conf 62 | when: "'LastRx' not in sources.stdout" 63 | register: ntp_conf_encoded 64 | 65 | - name: Decode chrony.conf file 66 | set_fact: 67 | chrony_conf: "{{ chrony_conf_encoded.content | b64decode }}" 68 | when: chrony_conf_encoded is not skipped 69 | 70 | - name: Decode ntp.conf file 71 | set_fact: 72 | ntp_conf: "{{ ntp_conf_encoded.content | b64decode }}" 73 | when: ntp_conf_encoded is not skipped 74 | 75 | - name: Check chrony.conf file 76 | assert: 77 | that: 78 | - chrony_conf is not search('172\.16\.123\.1.*iburst') 79 | - chrony_conf is not search('172\.16\.123\.2.*iburst') 80 | - chrony_conf is search('172\.16\.123\.3.*iburst') 81 | - chrony_conf is not search('172\.16\.123\.1.*prefer') 82 | - chrony_conf is not search('172\.16\.123\.2.*prefer') 83 | - chrony_conf is search('172\.16\.123\.3.*prefer') 84 | - chrony_conf is not search('172\.16\.123\.1.*trust') 85 | - chrony_conf is not search('172\.16\.123\.2.*trust') 86 | - chrony_conf is search('172\.16\.123\.3.*trust') 87 | - chrony_conf is not search('172\.16\.123\.1.*xleave') 88 | - chrony_conf is not search('172\.16\.123\.2.*xleave') 89 | - chrony_conf is search('172\.16\.123\.3.*xleave') == 90 | __timesync_chrony_version is version('3.0', '>=') 91 | - chrony_conf is not search('172\.16\.123\.1.*filter') 92 | - chrony_conf is not search('172\.16\.123\.2.*filter') 93 | - chrony_conf is search('172\.16\.123\.3.*filter 3') == 94 | __timesync_chrony_version is version('3.4', '>=') 95 | - chrony_conf is search('hwtimestamp \*') == 96 | __timesync_chrony_version is version('3.0', '>=') 97 | - chrony_conf is search('hwtimestamp .* minpoll 0') == 98 | __timesync_chrony_version is version('3.1', '>=') 99 | when: chrony_conf is defined 100 | 101 | - name: Check ntp.conf file 102 | assert: 103 | that: 104 | - ntp_conf is not search('172\.16\.123\.1.*iburst') 105 | - ntp_conf is not search('172\.16\.123\.2.*iburst') 106 | - ntp_conf is search('172\.16\.123\.3.*iburst') 107 | - ntp_conf is not search('172\.16\.123\.1.*prefer') 108 | - ntp_conf is not search('172\.16\.123\.2.*prefer') 109 | - ntp_conf is search('172\.16\.123\.3.*prefer') 110 | - ntp_conf is not search('172\.16\.123\.1.*true') 111 | - ntp_conf is not search('172\.16\.123\.2.*true') 112 | - ntp_conf is search('172\.16\.123\.3.*true') 113 | when: ntp_conf is defined 114 | 115 | - name: Check chrony conf for ansible_managed, fingerprint 116 | include_tasks: tasks/check_header.yml 117 | vars: 118 | __file_content: "{{ chrony_conf_encoded }}" 119 | __fingerprint: "system_role:timesync" 120 | when: chrony_conf is defined 121 | 122 | - name: Check chrony sysconf for ansible_managed, fingerprint 123 | include_tasks: tasks/check_header.yml 124 | vars: 125 | __file: "{{ timesync_chrony_sysconfig_path }}" 126 | __fingerprint: "system_role:timesync" 127 | when: chrony_conf is defined 128 | 129 | - name: Check ntp conf for ansible_managed, fingerprint 130 | include_tasks: tasks/check_header.yml 131 | vars: 132 | __file_content: "{{ ntp_conf_encoded }}" 133 | __fingerprint: "system_role:timesync" 134 | when: ntp_conf is defined 135 | 136 | - name: Check ntp sysconf for ansible_managed, fingerprint 137 | include_tasks: tasks/check_header.yml 138 | vars: 139 | __file: "{{ timesync_ntp_sysconfig_path }}" 140 | __fingerprint: "system_role:timesync" 141 | when: ntp_conf is defined 142 | -------------------------------------------------------------------------------- /tests/tests_ntp_provider1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Configure NTP with default provider 3 | hosts: all 4 | vars: 5 | timesync_ntp_servers: 6 | - hostname: 172.16.123.1 7 | roles: 8 | - linux-system-roles.timesync 9 | 10 | pre_tasks: 11 | - name: Common test setup tasks 12 | include_tasks: tasks/setup.yml 13 | vars: 14 | __timesync_ostree_unsupported: true 15 | 16 | - name: Remove provider packages 17 | check_mode: false 18 | tags: tests::setup 19 | block: 20 | - name: Remove NTP providers 21 | package: 22 | name: "{{ item }}" 23 | state: absent 24 | with_items: 25 | - chrony 26 | - ntp 27 | 28 | post_tasks: 29 | - name: Verify provider is working 30 | tags: tests::verify 31 | block: 32 | - name: Wait for services to start 33 | wait_for: 34 | timeout: 2 35 | 36 | - name: Get list of currently used time sources 37 | shell: chronyc -n sources || ntpq -pn 38 | register: sources 39 | changed_when: false 40 | 41 | - name: Check time sources 42 | assert: 43 | that: 44 | - "'172.16.123.1' in sources.stdout" 45 | -------------------------------------------------------------------------------- /tests/tests_ntp_provider2.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Configure NTP with chrony as current provider 3 | hosts: all 4 | vars: 5 | timesync_ntp_servers: 6 | - hostname: 172.16.123.1 7 | roles: 8 | - linux-system-roles.timesync 9 | 10 | pre_tasks: 11 | - name: Common test setup tasks 12 | include_tasks: tasks/setup.yml 13 | vars: 14 | __timesync_ostree_unsupported: true 15 | 16 | - name: Remove the install ntp 17 | check_mode: false 18 | tags: tests::setup 19 | block: 20 | - name: Remove ntp 21 | package: 22 | name: ntp 23 | state: absent 24 | 25 | - name: Install chrony 26 | package: 27 | name: chrony 28 | state: present 29 | register: package_install 30 | ignore_errors: true # noqa ignore-errors 31 | 32 | - name: Skip test if no package 33 | meta: end_play 34 | when: package_install.failed 35 | 36 | - name: Enable chronyd 37 | service: 38 | name: chronyd 39 | state: started 40 | enabled: true 41 | 42 | post_tasks: 43 | - name: Verify chronyd running 44 | tags: tests::verify 45 | block: 46 | - name: Wait for services to start 47 | wait_for: 48 | timeout: 2 49 | 50 | - name: Check chronyd service 51 | command: chronyc -n tracking 52 | changed_when: false 53 | -------------------------------------------------------------------------------- /tests/tests_ntp_provider3.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Configure NTP with ntp as current provider 3 | hosts: all 4 | vars: 5 | timesync_ntp_servers: 6 | - hostname: 172.16.123.1 7 | roles: 8 | - linux-system-roles.timesync 9 | 10 | pre_tasks: 11 | - name: Common test setup tasks 12 | include_tasks: tasks/setup.yml 13 | vars: 14 | __timesync_ostree_unsupported: true 15 | 16 | - name: Remove then install chrony 17 | check_mode: false 18 | tags: tests::setup 19 | block: 20 | - name: Remove chrony 21 | package: 22 | name: chrony 23 | state: absent 24 | 25 | - name: Install ntp 26 | package: 27 | name: ntp 28 | state: present 29 | register: package_install 30 | ignore_errors: true # noqa ignore-errors 31 | 32 | - name: Skip test if no package 33 | meta: end_play 34 | when: package_install.failed 35 | 36 | - name: Enable ntpd 37 | service: 38 | name: ntpd 39 | state: started 40 | enabled: true 41 | 42 | post_tasks: 43 | - name: Verify ntpd is running 44 | tags: tests::verify 45 | block: 46 | - name: Wait for services to start 47 | wait_for: 48 | timeout: 2 49 | 50 | - name: Check ntpd service 51 | shell: | 52 | set -eu 53 | if set -o | grep -q pipefail; then 54 | set -o pipefail # no pipefail on debian, some ubuntu 55 | fi 56 | ntpq -c rv | grep 'associd=0' 57 | changed_when: false 58 | -------------------------------------------------------------------------------- /tests/tests_ntp_provider4.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Configure NTP with chrony as specified provider 3 | hosts: all 4 | vars: 5 | timesync_ntp_servers: 6 | - hostname: 172.16.123.1 7 | timesync_ntp_provider: chrony 8 | tasks: 9 | - name: Common test setup tasks 10 | include_tasks: tasks/setup.yml 11 | vars: 12 | __timesync_ostree_unsupported: true 13 | 14 | - name: Install then remove chrony 15 | check_mode: false 16 | tags: tests::setup 17 | block: 18 | - name: Install chrony 19 | package: 20 | name: chrony 21 | state: present 22 | register: package_install 23 | ignore_errors: true # noqa ignore-errors 24 | 25 | - name: Skip test if no package 26 | meta: end_play 27 | when: package_install is failed 28 | 29 | - name: Remove chrony 30 | package: 31 | name: chrony 32 | state: absent 33 | 34 | - name: Run the test 35 | block: 36 | - name: Run the role 37 | include_role: 38 | name: linux-system-roles.timesync 39 | public: true 40 | 41 | - name: Verify chronyd is running 42 | tags: tests::verify 43 | block: 44 | - name: Wait for services to start 45 | wait_for: 46 | timeout: 2 47 | 48 | - name: Check chronyd service 49 | command: chronyc -n tracking 50 | changed_when: false 51 | 52 | always: 53 | - name: Cleanup after tests 54 | include_tasks: tasks/cleanup.yml 55 | -------------------------------------------------------------------------------- /tests/tests_ntp_provider5.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Configure NTP with ntp as specified provider 3 | hosts: all 4 | vars: 5 | timesync_ntp_servers: 6 | - hostname: 172.16.123.1 7 | timesync_ntp_provider: ntp 8 | tasks: 9 | - name: Common test setup tasks 10 | include_tasks: tasks/setup.yml 11 | vars: 12 | __timesync_ostree_unsupported: true 13 | 14 | - name: Install then remove ntp 15 | check_mode: false 16 | tags: tests::setup 17 | block: 18 | - name: Install ntp 19 | package: 20 | name: ntp 21 | state: present 22 | register: package_install 23 | ignore_errors: true # noqa ignore-errors 24 | 25 | - name: Skip test if no ntp package 26 | meta: end_play 27 | when: package_install.failed 28 | 29 | - name: Remove ntp 30 | package: 31 | name: ntp 32 | state: absent 33 | 34 | - name: Run test 35 | block: 36 | - name: Run the role 37 | include_role: 38 | name: linux-system-roles.timesync 39 | public: true 40 | 41 | - name: Verify ntpd service 42 | tags: tests::verify 43 | block: 44 | - name: Wait for services to start 45 | wait_for: 46 | timeout: 2 47 | 48 | - name: Check ntpd service 49 | shell: | 50 | set -eu 51 | if set -o | grep -q pipefail; then 52 | set -o pipefail # no pipefail on debian, some ubuntu 53 | fi 54 | ntpq -c rv | grep 'associd=0' 55 | changed_when: false 56 | 57 | always: 58 | - name: Cleanup after tests 59 | include_tasks: tasks/cleanup.yml 60 | -------------------------------------------------------------------------------- /tests/tests_ntp_provider6.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: >- 3 | Configure NTP with OS release non-default provider and 4 | then change it to the default provider 5 | hosts: all 6 | gather_facts: true 7 | vars: 8 | is_ntp_default: "{{ ansible_distribution in ['RedHat', 'CentOS'] 9 | and ansible_distribution_version is version('7.0', '<') }}" 10 | both_avail: true 11 | 12 | tasks: 13 | - name: Common test setup tasks 14 | include_tasks: tasks/setup.yml 15 | vars: 16 | __timesync_ostree_unsupported: true 17 | 18 | - name: See if both providers are available 19 | check_mode: false 20 | tags: tests::setup 21 | block: 22 | - name: Check for availability of both NTP providers 23 | package: 24 | name: "{{ item }}" 25 | state: present 26 | register: package_install 27 | ignore_errors: true # noqa ignore-errors 28 | with_items: 29 | - chrony 30 | - ntp 31 | 32 | # meta doesn't pickup with_item in when conditional, 33 | # following set_fact workarounds that 34 | # see https://github.com/ansible/ansible/issues/35890 35 | - name: Set the availability of both NTP providers 36 | set_fact: 37 | both_avail: false 38 | when: item.failed 39 | with_items: 40 | - "{{ package_install.results }}" 41 | 42 | - name: End test if not both available 43 | meta: end_play 44 | when: not both_avail 45 | 46 | - name: Remove NTP providers 47 | package: 48 | name: "{{ item }}" 49 | state: absent 50 | with_items: 51 | - chrony 52 | - ntp 53 | 54 | - name: Call role to change provider 55 | include_role: 56 | name: linux-system-roles.timesync 57 | public: true 58 | vars: 59 | # ntp is the default choice for RedHat and CentOS 60 | # version < 7.0 - reverse it 61 | timesync_ntp_provider: "{{ 'chrony' if is_ntp_default else 'ntp' }}" 62 | 63 | - name: Verify provider set correctly 64 | tags: tests::verify 65 | block: 66 | - name: Flush handlers 67 | meta: flush_handlers 68 | 69 | - name: Wait for services to start 70 | wait_for: 71 | timeout: 2 72 | 73 | - name: Check chronyd service 74 | command: chronyc -n tracking 75 | when: is_ntp_default | bool 76 | changed_when: false 77 | 78 | - name: Check ntpd service 79 | shell: | 80 | set -eu 81 | if set -o | grep -q pipefail; then 82 | set -o pipefail # no pipefail on debian, some ubuntu 83 | fi 84 | ntpq -c rv | grep 'associd=0' 85 | when: not is_ntp_default 86 | changed_when: false 87 | 88 | - name: Call role to reset default provider 89 | include_role: 90 | name: linux-system-roles.timesync 91 | vars: 92 | timesync_ntp_provider: "{{ timesync_ntp_provider_os_default }}" 93 | 94 | - name: Verify provider set correctly - 2 95 | tags: tests::verify 96 | block: 97 | - name: Wait for services to start 98 | wait_for: 99 | timeout: 2 100 | 101 | - name: Check ntpd service 102 | shell: | 103 | set -eu 104 | if set -o | grep -q pipefail; then 105 | set -o pipefail # no pipefail on debian, some ubuntu 106 | fi 107 | ntpq -c rv | grep 'associd=0' 108 | when: is_ntp_default | bool 109 | changed_when: false 110 | 111 | - name: Check chronyd service 112 | command: chronyc -n tracking 113 | when: not is_ntp_default 114 | changed_when: false 115 | -------------------------------------------------------------------------------- /tests/tests_ntp_ptp.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Configure time synchronization with NTP servers and PTP domains 3 | hosts: all 4 | gather_facts: true 5 | vars: 6 | timesync_ntp_servers: 7 | - hostname: 172.16.123.1 8 | minpoll: 0 9 | maxpoll: 2 10 | - hostname: 172.16.123.2 11 | minpoll: 0 12 | maxpoll: 2 13 | timesync_ptp_domains: 14 | - number: 0 15 | interfaces: "{{ [ ansible_default_ipv4['interface'] ] }}" 16 | delay: 0.0001 17 | - number: 1 18 | interfaces: "{{ [ ansible_default_ipv4['interface'] ] }}" 19 | delay: 0.0001 20 | timesync_step_threshold: 0.001 21 | timesync_dhcp_ntp_servers: false 22 | timesync_min_sources: 2 23 | tasks: 24 | - name: Run test 25 | tags: tests::verify 26 | block: 27 | - name: Common test setup tasks 28 | include_tasks: tasks/setup.yml 29 | 30 | - name: Run role 31 | include_role: 32 | name: linux-system-roles.timesync 33 | public: true 34 | 35 | - name: Flush handlers 36 | meta: flush_handlers 37 | 38 | - name: Ensure ethtool is installed 39 | package: 40 | name: ethtool 41 | state: present 42 | use: "{{ (__timesync_is_ostree | d(false)) | 43 | ternary('ansible.posix.rhel_rpm_ostree', omit) }}" 44 | 45 | - name: Check if SW/HW timestamping is supported 46 | command: ethtool -T "{{ ansible_default_ipv4['interface'] }}" 47 | register: ethtool 48 | ignore_errors: true # noqa ignore-errors 49 | changed_when: false 50 | 51 | - name: Run test 52 | when: "'ware-transmit' in ethtool.stdout" 53 | block: 54 | - name: Wait for services to start 55 | wait_for: 56 | timeout: 2 57 | 58 | - name: Get list of currently used time sources 59 | shell: chronyc -n sources || ntpq -pn 60 | register: sources 61 | changed_when: false 62 | 63 | - name: Check time sources 64 | assert: 65 | that: 66 | - "'172.16.123.1' in sources.stdout" 67 | - "'172.16.123.2' in sources.stdout" 68 | - "'PTP0' in sources.stdout" 69 | - "'PTP1' in sources.stdout" 70 | 71 | always: 72 | - name: Cleanup after tests 73 | include_tasks: tasks/cleanup.yml 74 | vars: 75 | timesync_ntp_servers: [] 76 | timesync_ptp_domains: [] 77 | -------------------------------------------------------------------------------- /tests/tests_options.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Test setting OPTIONS 3 | hosts: all 4 | gather_facts: true 5 | tasks: 6 | - name: Run tests 7 | block: 8 | - name: Get vars needed to run the tests 9 | include_role: 10 | name: linux-system-roles.timesync 11 | public: true 12 | tasks_from: set_vars.yml 13 | 14 | # install the provider to install the sysconfig file 15 | - name: Install chrony 16 | package: 17 | name: chrony 18 | state: present 19 | use: "{{ (__timesync_is_ostree | d(false)) | 20 | ternary('ansible.posix.rhel_rpm_ostree', omit) }}" 21 | when: timesync_ntp_provider == 'chrony' 22 | 23 | - name: Install ntp 24 | package: 25 | name: ntp 26 | state: present 27 | use: "{{ (__timesync_is_ostree | d(false)) | 28 | ternary('ansible.posix.rhel_rpm_ostree', omit) }}" 29 | when: timesync_ntp_provider == 'ntp' 30 | 31 | - name: Get OPTIONS before running 32 | command: grep ^OPTIONS= {{ timesync_chrony_sysconfig_path }} 33 | changed_when: false 34 | register: __timesync_config_before 35 | 36 | - name: Run role with no arguments to get provider 37 | include_role: 38 | name: linux-system-roles.timesync 39 | public: true 40 | 41 | - name: Get OPTIONS after running 42 | command: grep ^OPTIONS= {{ timesync_chrony_sysconfig_path }} 43 | changed_when: false 44 | register: __timesync_config_after 45 | 46 | - name: Show options 47 | debug: 48 | msg: | 49 | before [{{ __timesync_config_before.stdout }}] 50 | after [{{ __timesync_config_after.stdout }}] 51 | 52 | - name: Ensure that OPTIONS did not change 53 | assert: 54 | that: __timesync_config_before.stdout == __timesync_config_after.stdout 55 | 56 | - name: Try timesync_ntp_ip_family IPv4 57 | include_role: 58 | name: linux-system-roles.timesync 59 | vars: 60 | timesync_ntp_ip_family: IPv4 61 | 62 | - name: Verify IPv4 setting 63 | command: grep 'OPTIONS=.* -4' {{ timesync_chrony_sysconfig_path }} 64 | changed_when: false 65 | 66 | - name: Try timesync_ntp_ip_family IPv6 67 | include_role: 68 | name: linux-system-roles.timesync 69 | vars: 70 | timesync_ntp_ip_family: IPv6 71 | 72 | - name: Verify IPv6 setting 73 | command: grep 'OPTIONS=.* -6' {{ timesync_chrony_sysconfig_path }} 74 | changed_when: false 75 | 76 | always: 77 | - name: Reset OPTIONS 78 | include_role: 79 | name: linux-system-roles.timesync 80 | vars: 81 | timesync_ntp_ip_family: all 82 | 83 | - name: Verify reset 84 | shell: | 85 | set -eux 86 | if grep 'OPTIONS=.* -4' {{ timesync_chrony_sysconfig_path }} || \ 87 | grep 'OPTIONS=.* -6' {{ timesync_chrony_sysconfig_path }}; then 88 | echo ERROR: {{ timesync_chrony_sysconfig_path }} has incorrect OPTIONS 89 | exit 1 90 | fi 91 | exit 0 92 | changed_when: false 93 | -------------------------------------------------------------------------------- /tests/tests_ptp_multi.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Configure time synchronization with multiple PTP domains 3 | hosts: all 4 | gather_facts: true 5 | vars: 6 | timesync_ptp_domains: 7 | - number: 0 8 | interfaces: "{{ [ ansible_default_ipv4['interface'] ] }}" 9 | transport: L2 10 | - number: 1 11 | interfaces: "{{ [ ansible_default_ipv4['interface'] ] }}" 12 | delay: 0.001 13 | transport: UDPv4 14 | hybrid_e2e: true 15 | udp_ttl: 2 16 | timesync_step_threshold: 0.0001 17 | timesync_min_sources: 2 18 | roles: 19 | - linux-system-roles.timesync 20 | 21 | tasks: 22 | - name: Run test 23 | tags: tests::verify 24 | block: 25 | - name: Flush handlers 26 | meta: flush_handlers 27 | 28 | - name: Common test setup tasks 29 | include_tasks: tasks/setup.yml 30 | 31 | - name: Ensure ethtool is installed 32 | package: 33 | name: ethtool 34 | state: present 35 | use: "{{ (__timesync_is_ostree | d(false)) | 36 | ternary('ansible.posix.rhel_rpm_ostree', omit) }}" 37 | 38 | - name: Check if SW/HW timestamping is supported 39 | command: ethtool -T "{{ ansible_default_ipv4['interface'] }}" 40 | register: ethtool 41 | ignore_errors: true # noqa ignore-errors 42 | changed_when: false 43 | 44 | - name: Run test 45 | when: "'ware-transmit' in ethtool.stdout" 46 | block: 47 | - name: Wait for services to start 48 | wait_for: 49 | timeout: 2 50 | 51 | - name: Get list of currently used time sources 52 | shell: chronyc -n sources || ntpq -pn 53 | register: sources 54 | changed_when: false 55 | 56 | - name: Check time sources 57 | assert: 58 | that: 59 | - "'PTP0' in sources.stdout" 60 | - "'PTP1' in sources.stdout" 61 | 62 | - name: Run pmc 63 | vars: 64 | # wokeignore:rule=master 65 | __sock: /var/run/timemaster/ptp4l.0.socket 66 | command: >- 67 | pmc -u -b 0 -d 0 -s "{{ __sock }}" 'GET DOMAIN' 68 | 'GET PORT_DATA_SET' 69 | register: pmc 70 | changed_when: false 71 | 72 | - name: Check PTP domain 73 | assert: 74 | that: 75 | - "'domainNumber 0' in pmc.stdout" 76 | 77 | - name: Check PTP port state 78 | assert: 79 | that: 80 | - pmc.stdout is search("portState\s+LISTENING") 81 | when: 82 | - ansible_distribution not in ['RedHat', 'CentOS'] or 83 | ansible_distribution_version is not version('8.3', '<') 84 | 85 | - name: Run pmc 86 | vars: 87 | # wokeignore:rule=master 88 | __sock: /var/run/timemaster/ptp4l.1.socket 89 | command: >- 90 | pmc -u -b 0 -d 1 -s "{{ __sock }}" 'GET DOMAIN' 91 | 'GET PORT_DATA_SET' 92 | register: pmc 93 | changed_when: false 94 | 95 | - name: Check PTP domain 96 | assert: 97 | that: 98 | - "'domainNumber 1' in pmc.stdout" 99 | 100 | - name: Check PTP port state 101 | assert: 102 | that: 103 | - pmc.stdout is search("portState\s+LISTENING") 104 | when: 105 | - ansible_distribution not in ['RedHat', 'CentOS'] or 106 | ansible_distribution_version is not version('8.3', '<') 107 | -------------------------------------------------------------------------------- /tests/tests_ptp_single.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Configure time synchronization with single PTP domain 3 | hosts: all 4 | gather_facts: true 5 | vars: 6 | timesync_ptp_domains: 7 | - number: 3 8 | interfaces: "{{ [ ansible_default_ipv4['interface'] ] }}" 9 | roles: 10 | - linux-system-roles.timesync 11 | 12 | tasks: 13 | - name: Run test 14 | tags: tests::verify 15 | block: 16 | - name: Flush handlers 17 | meta: flush_handlers 18 | 19 | - name: Common test setup tasks 20 | include_tasks: tasks/setup.yml 21 | 22 | - name: Ensure ethtool is installed 23 | package: 24 | name: ethtool 25 | state: present 26 | use: "{{ (__timesync_is_ostree | d(false)) | 27 | ternary('ansible.posix.rhel_rpm_ostree', omit) }}" 28 | 29 | - name: Check if SW/HW timestamping is supported 30 | command: ethtool -T "{{ ansible_default_ipv4['interface'] }}" 31 | register: ethtool 32 | ignore_errors: true # noqa ignore-errors 33 | changed_when: false 34 | 35 | - name: Run test 36 | when: "'ware-transmit' in ethtool.stdout" 37 | block: 38 | - name: Wait for services to start 39 | wait_for: 40 | timeout: 2 41 | 42 | - name: Run pmc 43 | command: pmc -u -b 0 -d 3 'GET DOMAIN' 'GET PORT_DATA_SET' 44 | register: pmc 45 | changed_when: false 46 | 47 | - name: Check PTP domain 48 | assert: 49 | that: 50 | - "'domainNumber 3' in pmc.stdout" 51 | 52 | - name: Check PTP port state 53 | assert: 54 | that: 55 | - pmc.stdout is search("portState\s+LISTENING") 56 | when: 57 | - ansible_distribution not in ['RedHat', 'CentOS'] or 58 | ansible_distribution_version is not version('8.3', '<') 59 | -------------------------------------------------------------------------------- /tests/vars/rh_distros_vars.yml: -------------------------------------------------------------------------------- 1 | # vars for handling conditionals for RedHat and clones 2 | # DO NOT EDIT - file is auto-generated 3 | # repo is https://github.com/linux-system-roles/.github 4 | # file is playbooks/templates/tests/vars/rh_distros_vars.yml 5 | --- 6 | # Ansible distribution identifiers that the role treats like RHEL 7 | __timesync_rh_distros: 8 | - AlmaLinux 9 | - CentOS 10 | - RedHat 11 | - Rocky 12 | 13 | # Same as above but includes Fedora 14 | __timesync_rh_distros_fedora: "{{ __timesync_rh_distros + ['Fedora'] }}" 15 | 16 | # Use this in conditionals to check if distro is Red Hat or clone 17 | __timesync_is_rh_distro: "{{ ansible_distribution in __timesync_rh_distros }}" 18 | 19 | # Use this in conditionals to check if distro is Red Hat or clone, or Fedora 20 | __timesync_is_rh_distro_fedora: "{{ ansible_distribution in __timesync_rh_distros_fedora }}" 21 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | [lsr_config] 3 | lsr_enable = true 4 | 5 | [lsr_ansible-lint] 6 | configfile = .ansible-lint 7 | -------------------------------------------------------------------------------- /vars/AlmaLinux_10.yml: -------------------------------------------------------------------------------- 1 | RedHat_10.yml -------------------------------------------------------------------------------- /vars/AlmaLinux_8.yml: -------------------------------------------------------------------------------- 1 | RedHat_8.yml -------------------------------------------------------------------------------- /vars/AlmaLinux_9.yml: -------------------------------------------------------------------------------- 1 | RedHat_9.yml -------------------------------------------------------------------------------- /vars/CentOS_10.yml: -------------------------------------------------------------------------------- 1 | RedHat_10.yml -------------------------------------------------------------------------------- /vars/CentOS_6.yml: -------------------------------------------------------------------------------- 1 | RedHat_6.yml -------------------------------------------------------------------------------- /vars/CentOS_7.yml: -------------------------------------------------------------------------------- 1 | RedHat_7.yml -------------------------------------------------------------------------------- /vars/CentOS_8.yml: -------------------------------------------------------------------------------- 1 | RedHat_8.yml -------------------------------------------------------------------------------- /vars/CentOS_9.yml: -------------------------------------------------------------------------------- 1 | RedHat_9.yml -------------------------------------------------------------------------------- /vars/Debian.yml: -------------------------------------------------------------------------------- 1 | --- 2 | timesync_ntp_provider_os_default: "chrony" 3 | timesync_chrony_dhcp_sourcedir: "/run/chrony-dhcp" 4 | timesync_chrony_sysconfig_path: "/etc/default/chrony" 5 | timesync_chrony_sysconfig_options: "" 6 | timesync_chrony_conf_path: "/etc/chrony/chrony.conf" 7 | timesync_ntp_sysconfig_path: "/etc/default/ntp" 8 | timesync_ptp4l_sysconfig_path: "" 9 | timesync_phc2sys_sysconfig_path: "" 10 | # wokeignore:rule=master 11 | timesync_timemaster_config_path: "/etc/linuxptp/timemaster.conf" 12 | -------------------------------------------------------------------------------- /vars/Fedora.yml: -------------------------------------------------------------------------------- 1 | --- 2 | timesync_ntp_provider_os_default: chrony 3 | timesync_chrony_dhcp_sourcedir: /run/chrony-dhcp 4 | timesync_chrony_sysconfig_path: /etc/sysconfig/chronyd 5 | timesync_chrony_sysconfig_options: "-F 2" 6 | timesync_chrony_conf_path: "/etc/chrony.conf" 7 | timesync_ntp_sysconfig_path: /etc/sysconfig/ntpd 8 | timesync_ptp4l_sysconfig_path: /etc/sysconfig/ptp4l 9 | timesync_phc2sys_sysconfig_path: /etc/sysconfig/phc2sys 10 | # wokeignore:rule=master 11 | timesync_timemaster_config_path: /etc/timemaster.conf 12 | -------------------------------------------------------------------------------- /vars/OracleLinux_10.yml: -------------------------------------------------------------------------------- 1 | RedHat_10.yml -------------------------------------------------------------------------------- /vars/OracleLinux_6.yml: -------------------------------------------------------------------------------- 1 | RedHat_6.yml -------------------------------------------------------------------------------- /vars/OracleLinux_7.yml: -------------------------------------------------------------------------------- 1 | RedHat_7.yml -------------------------------------------------------------------------------- /vars/OracleLinux_8.yml: -------------------------------------------------------------------------------- 1 | RedHat_8.yml -------------------------------------------------------------------------------- /vars/OracleLinux_9.yml: -------------------------------------------------------------------------------- 1 | RedHat_9.yml -------------------------------------------------------------------------------- /vars/RedHat_10.yml: -------------------------------------------------------------------------------- 1 | --- 2 | timesync_ntp_provider_os_default: chrony 3 | timesync_chrony_dhcp_sourcedir: /run/chrony-dhcp 4 | timesync_chrony_sysconfig_path: /etc/sysconfig/chronyd 5 | timesync_chrony_sysconfig_options: "-F 2" 6 | timesync_chrony_conf_path: "/etc/chrony.conf" 7 | timesync_ntp_sysconfig_path: /etc/sysconfig/ntpd 8 | timesync_ptp4l_sysconfig_path: /etc/sysconfig/ptp4l 9 | timesync_phc2sys_sysconfig_path: /etc/sysconfig/phc2sys 10 | # wokeignore:rule=master 11 | timesync_timemaster_config_path: /etc/timemaster.conf 12 | -------------------------------------------------------------------------------- /vars/RedHat_6.yml: -------------------------------------------------------------------------------- 1 | --- 2 | timesync_ntp_provider_os_default: ntp 3 | timesync_chrony_dhcp_sourcedir: "" 4 | timesync_chrony_sysconfig_path: /etc/sysconfig/chronyd 5 | timesync_chrony_sysconfig_options: "" 6 | timesync_chrony_conf_path: "/etc/chrony.conf" 7 | timesync_ntp_sysconfig_path: /etc/sysconfig/ntpd 8 | timesync_ptp4l_sysconfig_path: /etc/sysconfig/ptp4l 9 | timesync_phc2sys_sysconfig_path: /etc/sysconfig/phc2sys 10 | # wokeignore:rule=master 11 | timesync_timemaster_config_path: /etc/timemaster.conf 12 | -------------------------------------------------------------------------------- /vars/RedHat_7.yml: -------------------------------------------------------------------------------- 1 | --- 2 | timesync_ntp_provider_os_default: chrony 3 | timesync_chrony_dhcp_sourcedir: "" 4 | timesync_chrony_sysconfig_path: /etc/sysconfig/chronyd 5 | timesync_chrony_sysconfig_options: "" 6 | timesync_chrony_conf_path: "/etc/chrony.conf" 7 | timesync_ntp_sysconfig_path: /etc/sysconfig/ntpd 8 | timesync_ptp4l_sysconfig_path: /etc/sysconfig/ptp4l 9 | timesync_phc2sys_sysconfig_path: /etc/sysconfig/phc2sys 10 | # wokeignore:rule=master 11 | timesync_timemaster_config_path: /etc/timemaster.conf 12 | -------------------------------------------------------------------------------- /vars/RedHat_8.yml: -------------------------------------------------------------------------------- 1 | --- 2 | timesync_ntp_provider_os_default: chrony 3 | timesync_chrony_dhcp_sourcedir: "" 4 | timesync_chrony_sysconfig_path: /etc/sysconfig/chronyd 5 | timesync_chrony_sysconfig_options: "" 6 | timesync_chrony_conf_path: "/etc/chrony.conf" 7 | timesync_ntp_sysconfig_path: /etc/sysconfig/ntpd 8 | timesync_ptp4l_sysconfig_path: /etc/sysconfig/ptp4l 9 | timesync_phc2sys_sysconfig_path: /etc/sysconfig/phc2sys 10 | # wokeignore:rule=master 11 | timesync_timemaster_config_path: /etc/timemaster.conf 12 | -------------------------------------------------------------------------------- /vars/RedHat_9.yml: -------------------------------------------------------------------------------- 1 | --- 2 | timesync_ntp_provider_os_default: chrony 3 | timesync_chrony_dhcp_sourcedir: /run/chrony-dhcp 4 | timesync_chrony_sysconfig_path: /etc/sysconfig/chronyd 5 | timesync_chrony_sysconfig_options: "-F 2" 6 | timesync_chrony_conf_path: "/etc/chrony.conf" 7 | timesync_ntp_sysconfig_path: /etc/sysconfig/ntpd 8 | timesync_ptp4l_sysconfig_path: /etc/sysconfig/ptp4l 9 | timesync_phc2sys_sysconfig_path: /etc/sysconfig/phc2sys 10 | # wokeignore:rule=master 11 | timesync_timemaster_config_path: /etc/timemaster.conf 12 | -------------------------------------------------------------------------------- /vars/Rocky_10.yml: -------------------------------------------------------------------------------- 1 | RedHat_10.yml -------------------------------------------------------------------------------- /vars/Rocky_8.yml: -------------------------------------------------------------------------------- 1 | RedHat_8.yml -------------------------------------------------------------------------------- /vars/Rocky_9.yml: -------------------------------------------------------------------------------- 1 | RedHat_9.yml -------------------------------------------------------------------------------- /vars/SL-Micro.yml: -------------------------------------------------------------------------------- 1 | --- 2 | timesync_ntp_provider_os_default: chrony 3 | timesync_chrony_dhcp_sourcedir: /run/chrony-dhcp 4 | timesync_chrony_sysconfig_path: /etc/sysconfig/chronyd 5 | timesync_chrony_sysconfig_options: "" 6 | timesync_chrony_conf_path: "/etc/chrony.conf" 7 | timesync_ntp_sysconfig_path: /etc/sysconfig/ntpd 8 | timesync_ptp4l_sysconfig_path: /etc/sysconfig/ptp4l 9 | timesync_phc2sys_sysconfig_path: /etc/sysconfig/phc2sys 10 | # wokeignore:rule=master 11 | timesync_timemaster_config_path: /etc/timemaster.conf 12 | -------------------------------------------------------------------------------- /vars/default.yml: -------------------------------------------------------------------------------- 1 | --- 2 | timesync_ntp_provider_os_default: chrony 3 | timesync_chrony_dhcp_sourcedir: "" 4 | timesync_chrony_sysconfig_path: /etc/sysconfig/chronyd 5 | timesync_chrony_sysconfig_options: "" 6 | timesync_chrony_conf_path: "/etc/chrony.conf" 7 | timesync_ntp_sysconfig_path: /etc/sysconfig/ntpd 8 | timesync_ptp4l_sysconfig_path: /etc/sysconfig/ptp4l 9 | timesync_phc2sys_sysconfig_path: /etc/sysconfig/phc2sys 10 | # wokeignore:rule=master 11 | timesync_timemaster_config_path: /etc/timemaster.conf 12 | -------------------------------------------------------------------------------- /vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | __timesync_chrony_version: "{{ 3 | ansible_facts.packages['chrony'][0].version | default('0') 4 | if 'chrony' in ansible_facts.packages else '0' }}" 5 | __timesync_ntp_version: "{{ 6 | ansible_facts.packages['ntp'][0].version | default('0') 7 | if 'ntp' in ansible_facts.packages else '0' }}" 8 | 9 | # ansible_facts required by the role 10 | __timesync_required_facts: 11 | - distribution 12 | - distribution_major_version 13 | - distribution_version 14 | - os_family 15 | 16 | # the subsets of ansible_facts that need to be gathered in case any of the 17 | # facts in required_facts is missing; see the documentation of 18 | # the 'gather_subset' parameter of the 'setup' module 19 | __timesync_required_facts_subsets: "{{ ['!all', '!min'] + 20 | __timesync_required_facts }}" 21 | 22 | # BEGIN - DO NOT EDIT THIS BLOCK - rh distros variables 23 | # Ansible distribution identifiers that the role treats like RHEL 24 | __timesync_rh_distros: 25 | - AlmaLinux 26 | - CentOS 27 | - RedHat 28 | - Rocky 29 | 30 | # Same as above but includes Fedora 31 | __timesync_rh_distros_fedora: "{{ __timesync_rh_distros + ['Fedora'] }}" 32 | 33 | # Use this in conditionals to check if distro is Red Hat or clone 34 | __timesync_is_rh_distro: "{{ ansible_distribution in __timesync_rh_distros }}" 35 | 36 | # Use this in conditionals to check if distro is Red Hat or clone, or Fedora 37 | __timesync_is_rh_distro_fedora: "{{ ansible_distribution in __timesync_rh_distros_fedora }}" 38 | # END - DO NOT EDIT THIS BLOCK - rh distros variables 39 | --------------------------------------------------------------------------------