diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs deleted file mode 100644 index 9623a78..0000000 --- a/.git-blame-ignore-revs +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) Ansible Project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -# Reformat YAML: https://github.com/ansible-collections/community.routeros/pull/369 -08152376de116e7d933d19ee25318f7a2eb222ae diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f71b322..2f4ff90 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,7 +9,3 @@ updates: directory: "/" schedule: interval: "weekly" - groups: - ci: - patterns: - - "*" diff --git a/.github/workflows/ansible-test.yml b/.github/workflows/ansible-test.yml new file mode 100644 index 0000000..4431bea --- /dev/null +++ b/.github/workflows/ansible-test.yml @@ -0,0 +1,146 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +# For the comprehensive list of the inputs supported by the ansible-community/ansible-test-gh-action GitHub Action, see +# https://github.com/marketplace/actions/ansible-test + +name: CI +on: + # Run CI against all pushes (direct commits, also merged PRs), Pull Requests + push: + branches: + - main + - stable-* + pull_request: + # Run CI once per day (at 05:15 UTC) + schedule: + - cron: '15 5 * * *' + +jobs: + sanity: + name: Sanity (Ⓐ${{ matrix.ansible }}) + strategy: + matrix: + ansible: + # It's important that Sanity is tested against all stable-X.Y branches + # Testing against `devel` may fail as new tests are added. + - stable-2.15 + - stable-2.16 + - stable-2.17 + - stable-2.18 + - devel + # Ansible-test on various stable branches does not yet work well with cgroups v2. + # Since ubuntu-latest now uses Ubuntu 22.04, we need to fall back to the ubuntu-20.04 + # image for these stable branches. The list of branches where this is necessary will + # shrink over time, check out https://github.com/ansible-collections/news-for-maintainers/issues/28 + # for the latest list. + runs-on: >- + ${{ contains(fromJson( + '["stable-2.9", "stable-2.10", "stable-2.11"]' + ), matrix.ansible) && 'ubuntu-20.04' || 'ubuntu-latest' }} + steps: + - name: Perform sanity testing + uses: felixfontein/ansible-test-gh-action@main + with: + ansible-core-github-repository-slug: ${{ contains(fromJson('["stable-2.9", "stable-2.10", "stable-2.11"]'), matrix.ansible) && 'ansible-community/eol-ansible' || 'ansible/ansible' }} + ansible-core-version: ${{ matrix.ansible }} + codecov-token: ${{ secrets.CODECOV_TOKEN }} + testing-type: sanity + test-deps: >- + git+https://github.com/ansible-collections/ansible.utils.git,main + git+https://github.com/ansible-collections/ansible.netcommon.git,main + + units: + # Ansible-test on various stable branches does not yet work well with cgroups v2. + # Since ubuntu-latest now uses Ubuntu 22.04, we need to fall back to the ubuntu-20.04 + # image for these stable branches. The list of branches where this is necessary will + # shrink over time, check out https://github.com/ansible-collections/news-for-maintainers/issues/28 + # for the latest list. + runs-on: >- + ${{ contains(fromJson( + '["stable-2.9", "stable-2.10", "stable-2.11"]' + ), matrix.ansible) && 'ubuntu-20.04' || 'ubuntu-latest' }} + name: Units (Ⓐ${{ matrix.ansible }}) + strategy: + # As soon as the first unit test fails, cancel the others to free up the CI queue + fail-fast: true + matrix: + ansible: + - stable-2.16 + - stable-2.17 + - stable-2.18 + - devel + + steps: + - name: >- + Perform unit testing against + Ansible version ${{ matrix.ansible }} + uses: felixfontein/ansible-test-gh-action@main + with: + ansible-core-github-repository-slug: ${{ contains(fromJson('["stable-2.9", "stable-2.10", "stable-2.11"]'), matrix.ansible) && 'ansible-community/eol-ansible' || 'ansible/ansible' }} + ansible-core-version: ${{ matrix.ansible }} + codecov-token: ${{ secrets.CODECOV_TOKEN }} + testing-type: units + test-deps: >- + git+https://github.com/ansible-collections/ansible.utils.git,main + git+https://github.com/ansible-collections/ansible.netcommon.git,main + + integration: + # Ansible-test on various stable branches does not yet work well with cgroups v2. + # Since ubuntu-latest now uses Ubuntu 22.04, we need to fall back to the ubuntu-20.04 + # image for these stable branches. The list of branches where this is necessary will + # shrink over time, check out https://github.com/ansible-collections/news-for-maintainers/issues/28 + # for the latest list. + runs-on: >- + ${{ contains(fromJson( + '["stable-2.9", "stable-2.10", "stable-2.11"]' + ), matrix.ansible) && 'ubuntu-20.04' || 'ubuntu-latest' }} + name: I (Ⓐ${{ matrix.ansible }}+py${{ matrix.python }}) + strategy: + fail-fast: false + matrix: + ansible: + - devel + python: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + include: + # 2.15 + - ansible: stable-2.15 + python: "2.7" + - ansible: stable-2.15 + python: "3.6" + - ansible: stable-2.15 + python: "3.7" + # 2.16 + - ansible: stable-2.16 + python: "3.10" + # 2.17 + - ansible: stable-2.17 + python: "3.8" + # 2.18 + - ansible: stable-2.18 + python: "3.9" + + steps: + - name: >- + Perform integration testing against + Ansible version ${{ matrix.ansible }} + under Python ${{ matrix.python }} + uses: felixfontein/ansible-test-gh-action@main + with: + ansible-core-github-repository-slug: ${{ contains(fromJson('["stable-2.9", "stable-2.10", "stable-2.11"]'), matrix.ansible) && 'ansible-community/eol-ansible' || 'ansible/ansible' }} + ansible-core-version: ${{ matrix.ansible }} + codecov-token: ${{ secrets.CODECOV_TOKEN }} + integration-continue-on-error: 'false' + integration-diff: 'false' + integration-retry-on-error: 'true' + test-deps: >- + git+https://github.com/ansible-collections/ansible.utils.git,main + git+https://github.com/ansible-collections/ansible.netcommon.git,main + target-python-version: ${{ matrix.python }} + testing-type: integration diff --git a/.github/workflows/docs-pr.yml b/.github/workflows/docs-pr.yml index 63135a1..7d35e5c 100644 --- a/.github/workflows/docs-pr.yml +++ b/.github/workflows/docs-pr.yml @@ -7,7 +7,7 @@ name: Collection Docs concurrency: group: docs-pr-${{ github.head_ref }} cancel-in-progress: true -'on': +on: pull_request_target: types: [opened, synchronize, reopened, closed] diff --git a/.github/workflows/docs-push.yml b/.github/workflows/docs-push.yml index 3d2c2b1..80f62f5 100644 --- a/.github/workflows/docs-push.yml +++ b/.github/workflows/docs-push.yml @@ -7,7 +7,7 @@ name: Collection Docs concurrency: group: docs-push-${{ github.sha }} cancel-in-progress: true -'on': +on: push: branches: - main diff --git a/.github/workflows/ee.yml b/.github/workflows/ee.yml new file mode 100644 index 0000000..9d6f3fd --- /dev/null +++ b/.github/workflows/ee.yml @@ -0,0 +1,158 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +name: execution environment +on: + # Run CI against all pushes (direct commits, also merged PRs), Pull Requests + push: + branches: + - main + - stable-* + pull_request: + # Run CI once per day (at 05:15 UTC) + # This ensures that even if there haven't been commits that we are still testing against latest version of ansible-builder + schedule: + - cron: '15 5 * * *' + +env: + NAMESPACE: community + COLLECTION_NAME: routeros + +jobs: + build: + name: Build and test EE (${{ matrix.name }}) + strategy: + fail-fast: false + matrix: + name: + - '' + ansible_core: + - '' + ansible_runner: + - '' + base_image: + - '' + pre_base: + - '' + extra_vars: + - '' + other_deps: + - '' + exclude: + - ansible_core: '' + include: + - name: ansible-core devel @ RHEL UBI 9 + ansible_core: https://github.com/ansible/ansible/archive/devel.tar.gz + ansible_runner: ansible-runner + other_deps: |2 + python_interpreter: + package_system: python3.11 python3.11-pip python3.11-wheel python3.11-cryptography + python_path: "/usr/bin/python3.11" + base_image: docker.io/redhat/ubi9:latest + pre_base: '"#"' + - name: ansible-core 2.15 @ Rocky Linux 9 + ansible_core: https://github.com/ansible/ansible/archive/stable-2.15.tar.gz + ansible_runner: ansible-runner + base_image: quay.io/rockylinux/rockylinux:9 + pre_base: '"#"' + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v4 + with: + path: ansible_collections/${{ env.NAMESPACE }}/${{ env.COLLECTION_NAME }} + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install ansible-builder and ansible-navigator + run: pip install ansible-builder ansible-navigator + + - name: Verify requirements + run: ansible-builder introspect --sanitize . + + - name: Make sure galaxy.yml has version entry + run: >- + python -c + 'import yaml ; + f = open("galaxy.yml", "rb") ; + data = yaml.safe_load(f) ; + f.close() ; + data["version"] = data.get("version") or "0.0.1" ; + f = open("galaxy.yml", "wb") ; + f.write(yaml.dump(data).encode("utf-8")) ; + f.close() ; + ' + working-directory: ansible_collections/${{ env.NAMESPACE }}/${{ env.COLLECTION_NAME }} + + - name: Build collection + run: | + ansible-galaxy collection build --output-path ../../../ + working-directory: ansible_collections/${{ env.NAMESPACE }}/${{ env.COLLECTION_NAME }} + + - name: Create files for building execution environment + run: | + COLLECTION_FILENAME="$(ls "${{ env.NAMESPACE }}-${{ env.COLLECTION_NAME }}"-*.tar.gz)" + + # EE config + cat > execution-environment.yml < requirements.yml < + ansible-navigator run + --mode stdout + --container-engine docker + --pull-policy never + --set-environment-variable ANSIBLE_PRIVATE_ROLE_VARS=true + --execution-environment-image test-ee:latest + -v + all.yml + ${{ matrix.extra_vars }} + working-directory: ansible_collections/${{ env.NAMESPACE }}/${{ env.COLLECTION_NAME }}/tests/ee diff --git a/.github/workflows/extra-tests.yml b/.github/workflows/extra-tests.yml new file mode 100644 index 0000000..54872aa --- /dev/null +++ b/.github/workflows/extra-tests.yml @@ -0,0 +1,51 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +name: extra-tests +on: + # Run CI against all pushes (direct commits, also merged PRs), Pull Requests + push: + branches: + - main + - stable-* + pull_request: + # Run CI once per day (at 05:15 UTC) + # This ensures that even if there haven't been commits that we are still testing against latest version of ansible-test for each ansible-base version + schedule: + - cron: '15 5 * * *' +env: + NAMESPACE: community + COLLECTION_NAME: routeros + +jobs: + extra-sanity: + name: Extra Sanity + runs-on: ubuntu-latest + steps: + + - name: Check out code + uses: actions/checkout@v4 + with: + path: ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install ansible-core + run: pip install https://github.com/ansible/ansible/archive/devel.tar.gz --disable-pip-version-check + + - name: Install collection dependencies + run: | + git clone --depth=1 --single-branch https://github.com/ansible-collections/community.internal_test_tools.git ./ansible_collections/community/internal_test_tools + git clone --depth=1 --single-branch https://github.com/ansible-collections/ansible.netcommon.git ./ansible_collections/ansible/netcommon + git clone --depth=1 --single-branch https://github.com/ansible-collections/ansible.utils.git ./ansible_collections/ansible/utils + # NOTE: we're installing with git to work around Galaxy being a huge PITA (https://github.com/ansible/galaxy/issues/2429) + # run: ansible-galaxy collection install community.internal_test_tools ansible.netcommon -p . + + - name: Run sanity tests + run: ../../community/internal_test_tools/tools/run.py --color + working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} diff --git a/.github/workflows/import-galaxy.yml b/.github/workflows/import-galaxy.yml new file mode 100644 index 0000000..0c0ee40 --- /dev/null +++ b/.github/workflows/import-galaxy.yml @@ -0,0 +1,20 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +name: import-galaxy +'on': + # Run CI against all pushes (direct commits, also merged PRs) to main, and all Pull Requests + push: + branches: + - main + - stable-* + pull_request: + +jobs: + import-galaxy: + permissions: + contents: read + name: Test to import built collection artifact with Galaxy importer + uses: ansible-community/github-action-test-galaxy-import/.github/workflows/test-galaxy-import.yml@main diff --git a/.github/workflows/nox.yml b/.github/workflows/nox.yml deleted file mode 100644 index 43cce99..0000000 --- a/.github/workflows/nox.yml +++ /dev/null @@ -1,35 +0,0 @@ ---- -# Copyright (c) Ansible Project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -name: nox -'on': - push: - branches: - - main - - stable-* - pull_request: - # Run CI once per day (at 05:15 UTC) - schedule: - - cron: '15 5 * * *' - workflow_dispatch: - -jobs: - nox: - runs-on: ubuntu-latest - name: "Run extra sanity tests" - steps: - - name: Check out collection - uses: actions/checkout@v4 - with: - persist-credentials: false - - name: Run nox - uses: ansible-community/antsibull-nox@main - - ansible-test: - uses: ansible-community/antsibull-nox/.github/workflows/reusable-nox-matrix.yml@main - with: - upload-codecov: true - secrets: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/reuse.yml b/.github/workflows/reuse.yml new file mode 100644 index 0000000..b552396 --- /dev/null +++ b/.github/workflows/reuse.yml @@ -0,0 +1,31 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +name: Verify REUSE + +on: + push: + branches: + - main + - stable-* + pull_request: + branches: + - main + - stable-* + # Run CI once per day (at 05:15 UTC) + schedule: + - cron: '15 5 * * *' + +jobs: + check: + permissions: + contents: read + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: REUSE Compliance Check + uses: fsfe/reuse-action@v4 diff --git a/.gitignore b/.gitignore index 728531b..8e398ff 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ /tests/output/ /changelogs/.plugin-cache.yaml -/tests/integration/inventory # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/.reuse/dep5 b/.reuse/dep5 new file mode 100644 index 0000000..0c3745e --- /dev/null +++ b/.reuse/dep5 @@ -0,0 +1,5 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ + +Files: changelogs/fragments/* +Copyright: Ansible Project +License: GPL-3.0-or-later diff --git a/.yamllint b/.yamllint deleted file mode 100644 index a6707e2..0000000 --- a/.yamllint +++ /dev/null @@ -1,53 +0,0 @@ ---- -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later -# SPDX-FileCopyrightText: 2025 Felix Fontein - -extends: default - -ignore: | - /changelogs/ - -rules: - line-length: - max: 300 - level: error - document-start: - present: true - document-end: false - truthy: - level: error - allowed-values: - - 'true' - - 'false' - indentation: - spaces: 2 - indent-sequences: true - key-duplicates: enable - trailing-spaces: enable - new-line-at-end-of-file: disable - hyphens: - max-spaces-after: 1 - empty-lines: - max: 2 - max-start: 0 - max-end: 0 - commas: - max-spaces-before: 0 - min-spaces-after: 1 - max-spaces-after: 1 - colons: - max-spaces-before: 0 - max-spaces-after: 1 - brackets: - min-spaces-inside: 0 - max-spaces-inside: 0 - braces: - min-spaces-inside: 0 - max-spaces-inside: 1 - octal-values: - forbid-implicit-octal: true - forbid-explicit-octal: true - comments: - min-spaces-from-content: 1 - comments-indentation: false diff --git a/.yamllint-docs b/.yamllint-docs deleted file mode 100644 index de8947d..0000000 --- a/.yamllint-docs +++ /dev/null @@ -1,54 +0,0 @@ ---- -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later -# SPDX-FileCopyrightText: 2025 Felix Fontein - -extends: default - -ignore: | - /changelogs/ - -rules: - line-length: - max: 160 - level: error - document-start: - present: false - document-end: - present: false - truthy: - level: error - allowed-values: - - 'true' - - 'false' - indentation: - spaces: 2 - indent-sequences: true - key-duplicates: enable - trailing-spaces: enable - new-line-at-end-of-file: disable - hyphens: - max-spaces-after: 1 - empty-lines: - max: 2 - max-start: 0 - max-end: 0 - commas: - max-spaces-before: 0 - min-spaces-after: 1 - max-spaces-after: 1 - colons: - max-spaces-before: 0 - max-spaces-after: 1 - brackets: - min-spaces-inside: 0 - max-spaces-inside: 0 - braces: - min-spaces-inside: 0 - max-spaces-inside: 1 - octal-values: - forbid-implicit-octal: true - forbid-explicit-octal: true - comments: - min-spaces-from-content: 1 - comments-indentation: false diff --git a/.yamllint-examples b/.yamllint-examples deleted file mode 100644 index 062ac5a..0000000 --- a/.yamllint-examples +++ /dev/null @@ -1,54 +0,0 @@ ---- -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later -# SPDX-FileCopyrightText: 2025 Felix Fontein - -extends: default - -ignore: | - /changelogs/ - -rules: - line-length: - max: 160 - level: error - document-start: - present: true - document-end: - present: false - truthy: - level: error - allowed-values: - - 'true' - - 'false' - indentation: - spaces: 2 - indent-sequences: true - key-duplicates: enable - trailing-spaces: enable - new-line-at-end-of-file: disable - hyphens: - max-spaces-after: 1 - empty-lines: - max: 2 - max-start: 0 - max-end: 0 - commas: - max-spaces-before: 0 - min-spaces-after: 1 - max-spaces-after: 1 - colons: - max-spaces-before: 0 - max-spaces-after: 1 - brackets: - min-spaces-inside: 0 - max-spaces-inside: 0 - braces: - min-spaces-inside: 0 - max-spaces-inside: 1 - octal-values: - forbid-implicit-octal: true - forbid-explicit-octal: true - comments: - min-spaces-from-content: 1 - comments-indentation: false diff --git a/.yamllint-extra-docs b/.yamllint-extra-docs deleted file mode 100644 index 7e24c0f..0000000 --- a/.yamllint-extra-docs +++ /dev/null @@ -1,53 +0,0 @@ ---- -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later -# SPDX-FileCopyrightText: 2025 Felix Fontein - -extends: default - -ignore: | - /changelogs/ - -rules: - line-length: - max: 160 - level: error - document-start: disable - document-end: - present: false - truthy: - level: error - allowed-values: - - 'true' - - 'false' - indentation: - spaces: 2 - indent-sequences: true - key-duplicates: enable - trailing-spaces: enable - new-line-at-end-of-file: disable - hyphens: - max-spaces-after: 1 - empty-lines: - max: 2 - max-start: 0 - max-end: 0 - commas: - max-spaces-before: 0 - min-spaces-after: 1 - max-spaces-after: 1 - colons: - max-spaces-before: 0 - max-spaces-after: 1 - brackets: - min-spaces-inside: 0 - max-spaces-inside: 0 - braces: - min-spaces-inside: 0 - max-spaces-inside: 1 - octal-values: - forbid-implicit-octal: true - forbid-explicit-octal: true - comments: - min-spaces-from-content: 1 - comments-indentation: false diff --git a/CHANGELOG.md b/CHANGELOG.md index 88a7efe..55853f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,297 +2,132 @@ **Topics** -- v3\.8\.1 - - Release Summary - - Bugfixes -- v3\.8\.0 - - Release Summary - - Minor Changes -- v3\.7\.0 - - Release Summary - - Minor Changes -- v3\.6\.0 - - Release Summary - - Minor Changes -- v3\.5\.0 - - Release Summary - - Minor Changes -- v3\.4\.0 - - Release Summary - - Minor Changes - - Bugfixes -- v3\.3\.0 - - Release Summary - - Minor Changes -- v3\.2\.0 - - Release Summary - - Minor Changes -- v3\.1\.0 - - Release Summary - - Minor Changes - - Bugfixes - v3\.0\.0 - - Release Summary + - Release Summary - Breaking Changes / Porting Guide - Removed Features \(previously deprecated\) - v2\.20\.0 - - Release Summary - - Minor Changes + - Release Summary + - Minor Changes - v2\.19\.0 - - Release Summary - - Minor Changes + - Release Summary + - Minor Changes - v2\.18\.0 - - Release Summary - - Minor Changes + - Release Summary + - Minor Changes - Deprecated Features - - Bugfixes + - Bugfixes - v2\.17\.0 - - Release Summary - - Minor Changes + - Release Summary + - Minor Changes - v2\.16\.0 - - Release Summary - - Minor Changes + - Release Summary + - Minor Changes - v2\.15\.0 - - Release Summary - - Minor Changes + - Release Summary + - Minor Changes - v2\.14\.0 - - Release Summary - - Minor Changes + - Release Summary + - Minor Changes - v2\.13\.0 - - Release Summary - - Minor Changes - - Bugfixes + - Release Summary + - Minor Changes + - Bugfixes - v2\.12\.0 - - Release Summary - - Minor Changes + - Release Summary + - Minor Changes - v2\.11\.0 - - Release Summary - - Minor Changes + - Release Summary + - Minor Changes - v2\.10\.0 - - Release Summary - - Minor Changes - - Bugfixes + - Release Summary + - Minor Changes + - Bugfixes - v2\.9\.0 - - Release Summary - - Minor Changes - - Bugfixes + - Release Summary + - Minor Changes + - Bugfixes - v2\.8\.3 - - Release Summary + - Release Summary - Known Issues - v2\.8\.2 - - Release Summary - - Bugfixes + - Release Summary + - Bugfixes - v2\.8\.1 - - Release Summary - - Bugfixes + - Release Summary + - Bugfixes - v2\.8\.0 - - Release Summary - - Minor Changes - - Bugfixes + - Release Summary + - Minor Changes + - Bugfixes - v2\.7\.0 - - Release Summary - - Minor Changes - - Bugfixes + - Release Summary + - Minor Changes + - Bugfixes - v2\.6\.0 - - Release Summary - - Minor Changes - - Bugfixes + - Release Summary + - Minor Changes + - Bugfixes - v2\.5\.0 - - Release Summary - - Minor Changes - - Bugfixes + - Release Summary + - Minor Changes + - Bugfixes - v2\.4\.0 - - Release Summary - - Minor Changes - - Bugfixes + - Release Summary + - Minor Changes + - Bugfixes - Known Issues - v2\.3\.1 - - Release Summary + - Release Summary - Known Issues - v2\.3\.0 - - Release Summary - - Minor Changes - - Bugfixes + - Release Summary + - Minor Changes + - Bugfixes - v2\.2\.1 - - Release Summary - - Bugfixes + - Release Summary + - Bugfixes - v2\.2\.0 - - Release Summary - - Minor Changes - - Bugfixes + - Release Summary + - Minor Changes + - Bugfixes - New Modules - v2\.1\.0 - - Release Summary - - Minor Changes - - Bugfixes + - Release Summary + - Minor Changes + - Bugfixes - New Modules - v2\.0\.0 - - Release Summary - - Minor Changes + - Release Summary + - Minor Changes - Breaking Changes / Porting Guide - - Bugfixes + - Bugfixes - New Plugins - Filter - v1\.2\.0 - - Release Summary - - Minor Changes - - Bugfixes + - Release Summary + - Minor Changes + - Bugfixes - v1\.1\.0 - - Release Summary - - Minor Changes + - Release Summary + - Minor Changes - v1\.0\.1 - - Release Summary - - Bugfixes + - Release Summary + - Bugfixes - v1\.0\.0 - - Release Summary - - Bugfixes + - Release Summary + - Bugfixes - v0\.1\.1 - - Release Summary - - Bugfixes + - Release Summary + - Bugfixes - v0\.1\.0 - - Release Summary - - Minor Changes - - -## v3\.8\.1 - - -### Release Summary - -Bugfix release\. - - -### Bugfixes - -* facts and api\_facts modules \- prevent deprecation warnings when used with ansible\-core 2\.19 \([https\://github\.com/ansible\-collections/community\.routeros/pull/384](https\://github\.com/ansible\-collections/community\.routeros/pull/384)\)\. - - -## v3\.8\.0 - - -### Release Summary - -Feature release\. - - -### Minor Changes - -* api\_info\, api\_modify \- add interface ethernet switch port\-isolation which is supported since RouterOS 6\.43 \([https\://github\.com/ansible\-collections/community\.routeros/pull/375](https\://github\.com/ansible\-collections/community\.routeros/pull/375)\)\. -* api\_info\, api\_modify \- add routing bfd configuration\. Officially stabilized BFD support for BGP and OSPF is available since RouterOS 7\.11 - \([https\://github\.com/ansible\-collections/community\.routeros/pull/375](https\://github\.com/ansible\-collections/community\.routeros/pull/375)\)\. -* api\_modify\, api\_info \- support API path ip ipsec mode\-config \([https\://github\.com/ansible\-collections/community\.routeros/pull/376](https\://github\.com/ansible\-collections/community\.routeros/pull/376)\)\. - - -## v3\.7\.0 - - -### Release Summary - -Feature release\. - - -### Minor Changes - -* api\_find\_and\_modify \- allow to control whether dynamic and/or builtin entries are ignored with the new ignore\_dynamic and ignore\_builtin options \([https\://github\.com/ansible\-collections/community\.routeros/issues/372](https\://github\.com/ansible\-collections/community\.routeros/issues/372)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/373](https\://github\.com/ansible\-collections/community\.routeros/pull/373)\)\. -* api\_info\, api\_modify \- add port\-cost\-mode to interface bridge which is supported since RouterOS 7\.13 \([https\://github\.com/ansible\-collections/community\.routeros/pull/371](https\://github\.com/ansible\-collections/community\.routeros/pull/371)\)\. - - -## v3\.6\.0 - - -### Release Summary - -Feature release\. - - -### Minor Changes - -* api\_info\, api\_modify \- add mdns\-repeat\-ifaces to ip dns for RouterOS 7\.16 and newer \([https\://github\.com/ansible\-collections/community\.routeros/pull/358](https\://github\.com/ansible\-collections/community\.routeros/pull/358)\)\. -* api\_info\, api\_modify \- field name change in routing bgp connection path implemented by RouterOS 7\.19 and newer \([https\://github\.com/ansible\-collections/community\.routeros/pull/360](https\://github\.com/ansible\-collections/community\.routeros/pull/360)\)\. -* api\_info\, api\_modify \- rename is\-responder property in interface wireguard peers to responder for RouterOS 7\.17 and newer \([https\://github\.com/ansible\-collections/community\.routeros/pull/364](https\://github\.com/ansible\-collections/community\.routeros/pull/364)\)\. - - -## v3\.5\.0 - - -### Release Summary - -Feature release\. - - -### Minor Changes - -* api\_info\, api\_modify \- change default for /ip/cloud/ddns\-enabled for RouterOS 7\.17 and newer from yes to auto \([https\://github\.com/ansible\-collections/community\.routeros/pull/350](https\://github\.com/ansible\-collections/community\.routeros/pull/350)\)\. - - -## v3\.4\.0 - - -### Release Summary - -Feature and bugfix release\. - - -### Minor Changes - -* api\_info\, api\_modify \- add support for the ip dns forwarders path implemented by RouterOS 7\.17 and newer \([https\://github\.com/ansible\-collections/community\.routeros/pull/343](https\://github\.com/ansible\-collections/community\.routeros/pull/343)\)\. - - -### Bugfixes - -* api\_info\, api\_modify \- remove the primary key action from the interface wifi provisioning path\, since RouterOS also allows to create completely duplicate entries \([https\://github\.com/ansible\-collections/community\.routeros/issues/344](https\://github\.com/ansible\-collections/community\.routeros/issues/344)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/345](https\://github\.com/ansible\-collections/community\.routeros/pull/345)\)\. - - -## v3\.3\.0 - - -### Release Summary - -Feature release\. - - -### Minor Changes - -* api\_info\, api\_modify \- add missing attribute require\-message\-auth for the radius path which exists since RouterOS version 7\.15 \([https\://github\.com/ansible\-collections/community\.routeros/issues/338](https\://github\.com/ansible\-collections/community\.routeros/issues/338)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/339](https\://github\.com/ansible\-collections/community\.routeros/pull/339)\)\. -* api\_info\, api\_modify \- add the interface 6to4 path\. Used to manage IPv6 tunnels via tunnel\-brokers like HE\, where native IPv6 is not provided \([https\://github\.com/ansible\-collections/community\.routeros/pull/342](https\://github\.com/ansible\-collections/community\.routeros/pull/342)\)\. -* api\_info\, api\_modify \- add the interface wireless access\-list and interface wireless connect\-list paths \([https\://github\.com/ansible\-collections/community\.routeros/issues/284](https\://github\.com/ansible\-collections/community\.routeros/issues/284)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/340](https\://github\.com/ansible\-collections/community\.routeros/pull/340)\)\. -* api\_info\, api\_modify \- add the use\-interface\-duid option for ipv6 dhcp\-client path\. This option prevents issues with Fritzbox modems and routers\, when using virtual interfaces \(like VLANs\) may create duplicated records in hosts config\, this breaks original \"expose\-host\" function\. Also add the script\, custom\-duid and validate\-server\-duid as backport from 7\.15 version update \([https\://github\.com/ansible\-collections/community\.routeros/pull/341](https\://github\.com/ansible\-collections/community\.routeros/pull/341)\)\. - - -## v3\.2\.0 - - -### Release Summary - -Feature release\. - - -### Minor Changes - -* api\_info\, api\_modify \- add support for the routing filter community\-list path implemented by RouterOS 7 and newer \([https\://github\.com/ansible\-collections/community\.routeros/pull/331](https\://github\.com/ansible\-collections/community\.routeros/pull/331)\)\. - - -## v3\.1\.0 - - -### Release Summary - -Bugfix and feature release\. - - -### Minor Changes - -* api\_info\, api\_modify \- add missing fields comment\, next\-pool to ip pool path \([https\://github\.com/ansible\-collections/community\.routeros/pull/327](https\://github\.com/ansible\-collections/community\.routeros/pull/327)\)\. - - -### Bugfixes - -* api\_info\, api\_modify \- fields log and log\-prefix in paths ip firewall filter\, ip firewall mangle\, ip firewall nat\, ip firewall raw now have the correct default values \([https\://github\.com/ansible\-collections/community\.routeros/pull/324](https\://github\.com/ansible\-collections/community\.routeros/pull/324)\)\. + - Release Summary + - Minor Changes ## v3\.0\.0 - + ### Release Summary Major release that drops support for End of Life Python versions and fixes check mode for community\.routeros\.command\. @@ -310,12 +145,12 @@ Major release that drops support for End of Life Python versions and fixes check ## v2\.20\.0 - + ### Release Summary Feature release\. - + ### Minor Changes * api\_info\, api\_modify \- add new parameters from the RouterOS 7\.16 release \([https\://github\.com/ansible\-collections/community\.routeros/pull/323](https\://github\.com/ansible\-collections/community\.routeros/pull/323)\)\. @@ -326,12 +161,12 @@ Feature release\. ## v2\.19\.0 - + ### Release Summary Feature release\. - + ### Minor Changes * api\_info\, api\_modify \- add support for the ip dns adlist path implemented by RouterOS 7\.15 and newer \([https\://github\.com/ansible\-collections/community\.routeros/pull/310](https\://github\.com/ansible\-collections/community\.routeros/pull/310)\)\. @@ -343,12 +178,12 @@ Feature release\. ## v2\.18\.0 - + ### Release Summary Feature release\. - + ### Minor Changes * api\_info \- allow to restrict the output by limiting fields to specific values with the new restrict option \([https\://github\.com/ansible\-collections/community\.routeros/pull/305](https\://github\.com/ansible\-collections/community\.routeros/pull/305)\)\. @@ -364,7 +199,7 @@ Feature release\. * The collection deprecates support for all Ansible/ansible\-base/ansible\-core versions that are currently End of Life\, [according to the ansible\-core support matrix](https\://docs\.ansible\.com/ansible\-core/devel/reference\_appendices/release\_and\_maintenance\.html\#ansible\-core\-support\-matrix)\. This means that the next major release of the collection will no longer support Ansible 2\.9\, ansible\-base 2\.10\, ansible\-core 2\.11\, ansible\-core 2\.12\, ansible\-core 2\.13\, and ansible\-core 2\.14\. - + ### Bugfixes * api\_modify\, api\_info \- change the default of ingress\-filtering in paths interface bridge and interface bridge port back to false for RouterOS before version 7 \([https\://github\.com/ansible\-collections/community\.routeros/pull/305](https\://github\.com/ansible\-collections/community\.routeros/pull/305)\)\. @@ -372,12 +207,12 @@ Feature release\. ## v2\.17\.0 - + ### Release Summary Feature release\. - + ### Minor Changes * api\_info\, api\_modify \- add system health settings path \([https\://github\.com/ansible\-collections/community\.routeros/pull/294](https\://github\.com/ansible\-collections/community\.routeros/pull/294)\)\. @@ -387,12 +222,12 @@ Feature release\. ## v2\.16\.0 - + ### Release Summary Feature release\. - + ### Minor Changes * api\_info\, api\_modify \- add missing path /ppp secret \([https\://github\.com/ansible\-collections/community\.routeros/pull/286](https\://github\.com/ansible\-collections/community\.routeros/pull/286)\)\. @@ -401,12 +236,12 @@ Feature release\. ## v2\.15\.0 - + ### Release Summary Feature release\. - + ### Minor Changes * api\_info\, api\_modify \- Add RouterOS 7\.x support to /mpls ldp path \([https\://github\.com/ansible\-collections/community\.routeros/pull/271](https\://github\.com/ansible\-collections/community\.routeros/pull/271)\)\. @@ -423,12 +258,12 @@ Feature release\. ## v2\.14\.0 - + ### Release Summary Feature release\. - + ### Minor Changes * api\_info\, api\_modify \- add read\-only fields installed\-version\, latest\-version and status in system package update \([https\://github\.com/ansible\-collections/community\.routeros/pull/263](https\://github\.com/ansible\-collections/community\.routeros/pull/263)\)\. @@ -438,18 +273,18 @@ Feature release\. ## v2\.13\.0 - + ### Release Summary Bugfix and feature release\. - + ### Minor Changes * api\_info\, api\_modify \- make path user group modifiable and add comment attribute \([https\://github\.com/ansible\-collections/community\.routeros/issues/256](https\://github\.com/ansible\-collections/community\.routeros/issues/256)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/257](https\://github\.com/ansible\-collections/community\.routeros/pull/257)\)\. * api\_modify\, api\_info \- add support for the ip vrf path in RouterOS 7 \([https\://github\.com/ansible\-collections/community\.routeros/pull/259](https\://github\.com/ansible\-collections/community\.routeros/pull/259)\) - + ### Bugfixes * facts \- fix date not getting removed for idempotent config export \([https\://github\.com/ansible\-collections/community\.routeros/pull/262](https\://github\.com/ansible\-collections/community\.routeros/pull/262)\)\. @@ -457,12 +292,12 @@ Bugfix and feature release\. ## v2\.12\.0 - + ### Release Summary Feature release\. - + ### Minor Changes * api\_info\, api\_modify \- add interface ovpn\-client path \([https\://github\.com/ansible\-collections/community\.routeros/issues/242](https\://github\.com/ansible\-collections/community\.routeros/issues/242)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/244](https\://github\.com/ansible\-collections/community\.routeros/pull/244)\)\. @@ -476,12 +311,12 @@ Feature release\. ## v2\.11\.0 - + ### Release Summary Feature and bugfix release\. - + ### Minor Changes * api\_info\, api\_modify \- add missing DoH parameters doh\-max\-concurrent\-queries\, doh\-max\-server\-connections\, and doh\-timeout to the ip dns path \([https\://github\.com/ansible\-collections/community\.routeros/issues/230](https\://github\.com/ansible\-collections/community\.routeros/issues/230)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/235](https\://github\.com/ansible\-collections/community\.routeros/pull/235)\) @@ -496,12 +331,12 @@ Feature and bugfix release\. ## v2\.10\.0 - + ### Release Summary Bugfix and feature release\. - + ### Minor Changes * api\_info \- add new include\_read\_only option to select behavior for read\-only values\. By default these are not returned \([https\://github\.com/ansible\-collections/community\.routeros/pull/213](https\://github\.com/ansible\-collections/community\.routeros/pull/213)\)\. @@ -525,7 +360,7 @@ Bugfix and feature release\. * api\_modify \- add new handle\_read\_only and handle\_write\_only options to handle the module\'s behavior for read\-only and write\-only fields \([https\://github\.com/ansible\-collections/community\.routeros/pull/213](https\://github\.com/ansible\-collections/community\.routeros/pull/213)\)\. * api\_modify\, api\_info \- support API paths routing id\, routing bgp connection \([https\://github\.com/ansible\-collections/community\.routeros/pull/220](https\://github\.com/ansible\-collections/community\.routeros/pull/220)\)\. - + ### Bugfixes * api\_info\, api\_modify \- in the snmp path\, ensure that engine\-id\-suffix is only available on RouterOS 7\.10\+\, and that engine\-id is read\-only on RouterOS 7\.10\+ \([https\://github\.com/ansible\-collections/community\.routeros/issues/208](https\://github\.com/ansible\-collections/community\.routeros/issues/208)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/218](https\://github\.com/ansible\-collections/community\.routeros/pull/218)\)\. @@ -533,18 +368,18 @@ Bugfix and feature release\. ## v2\.9\.0 - + ### Release Summary Bugfix and feature release\. - + ### Minor Changes * api\_info\, api\_modify \- add path caps\-man channel and enable path caps\-man manager interface \([https\://github\.com/ansible\-collections/community\.routeros/issues/193](https\://github\.com/ansible\-collections/community\.routeros/issues/193)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/194](https\://github\.com/ansible\-collections/community\.routeros/pull/194)\)\. * api\_info\, api\_modify \- add path ip traffic\-flow target \([https\://github\.com/ansible\-collections/community\.routeros/issues/191](https\://github\.com/ansible\-collections/community\.routeros/issues/191)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/192](https\://github\.com/ansible\-collections/community\.routeros/pull/192)\)\. - + ### Bugfixes * api\_modify\, api\_info \- add missing parameter engine\-id\-suffix for the snmp path \([https\://github\.com/ansible\-collections/community\.routeros/issues/189](https\://github\.com/ansible\-collections/community\.routeros/issues/189)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/190](https\://github\.com/ansible\-collections/community\.routeros/pull/190)\)\. @@ -552,7 +387,7 @@ Bugfix and feature release\. ## v2\.8\.3 - + ### Release Summary Maintenance release with updated documentation\. @@ -573,12 +408,12 @@ for the rendered HTML version of the documentation of the latest release\. ## v2\.8\.2 - + ### Release Summary Bugfix release\. - + ### Bugfixes * api\_modify\, api\_info \- add missing parameter tls for the tool e\-mail path \([https\://github\.com/ansible\-collections/community\.routeros/issues/179](https\://github\.com/ansible\-collections/community\.routeros/issues/179)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/180](https\://github\.com/ansible\-collections/community\.routeros/pull/180)\)\. @@ -586,12 +421,12 @@ Bugfix release\. ## v2\.8\.1 - + ### Release Summary Bugfix release\. - + ### Bugfixes * facts \- do not crash in CLI output preprocessing in unexpected situations during line unwrapping \([https\://github\.com/ansible\-collections/community\.routeros/issues/170](https\://github\.com/ansible\-collections/community\.routeros/issues/170)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/177](https\://github\.com/ansible\-collections/community\.routeros/pull/177)\)\. @@ -599,12 +434,12 @@ Bugfix release\. ## v2\.8\.0 - + ### Release Summary Bugfix and feature release\. - + ### Minor Changes * api\_modify \- adapt data for API paths ip dhcp\-server network \([https\://github\.com/ansible\-collections/community\.routeros/pull/156](https\://github\.com/ansible\-collections/community\.routeros/pull/156)\)\. @@ -614,7 +449,7 @@ Bugfix and feature release\. * api\_modify \- support API paths ip firewall layer7\-protocol \([https\://github\.com/ansible\-collections/community\.routeros/pull/153](https\://github\.com/ansible\-collections/community\.routeros/pull/153)\)\. * command \- workaround for extra characters in stdout in RouterOS versions between 6\.49 and 7\.1\.5 \([https\://github\.com/ansible\-collections/community\.routeros/issues/62](https\://github\.com/ansible\-collections/community\.routeros/issues/62)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/161](https\://github\.com/ansible\-collections/community\.routeros/pull/161)\)\. - + ### Bugfixes * api\_info\, api\_modify \- fix default and remove behavior for dhcp\-options in path ip dhcp\-client \([https\://github\.com/ansible\-collections/community\.routeros/issues/148](https\://github\.com/ansible\-collections/community\.routeros/issues/148)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/154](https\://github\.com/ansible\-collections/community\.routeros/pull/154)\)\. @@ -624,17 +459,17 @@ Bugfix and feature release\. ## v2\.7\.0 - + ### Release Summary Bugfix and feature release\. - + ### Minor Changes * api\_modify\, api\_info \- support API paths ip arp\, ip firewall raw\, ipv6 firewall raw \([https\://github\.com/ansible\-collections/community\.routeros/pull/144](https\://github\.com/ansible\-collections/community\.routeros/pull/144)\)\. - + ### Bugfixes * api\_modify\, api\_info \- defaults corrected for fields in interface wireguard peers API path \([https\://github\.com/ansible\-collections/community\.routeros/pull/144](https\://github\.com/ansible\-collections/community\.routeros/pull/144)\)\. @@ -642,18 +477,18 @@ Bugfix and feature release\. ## v2\.6\.0 - + ### Release Summary Regular bugfix and feature release\. - + ### Minor Changes * api\_modify\, api\_info \- add field regexp to ip dns static \([https\://github\.com/ansible\-collections/community\.routeros/issues/141](https\://github\.com/ansible\-collections/community\.routeros/issues/141)\)\. * api\_modify\, api\_info \- support API paths interface wireguard\, interface wireguard peers \([https\://github\.com/ansible\-collections/community\.routeros/pull/143](https\://github\.com/ansible\-collections/community\.routeros/pull/143)\)\. - + ### Bugfixes * api\_modify \- do not use name as a unique key in ip dns static \([https\://github\.com/ansible\-collections/community\.routeros/issues/141](https\://github\.com/ansible\-collections/community\.routeros/issues/141)\)\. @@ -662,17 +497,17 @@ Regular bugfix and feature release\. ## v2\.5\.0 - + ### Release Summary Feature and bugfix release\. - + ### Minor Changes * api\_info\, api\_modify \- support API paths interface ethernet poe\, interface gre6\, interface vrrp and also support all previously missing fields of entries in ip dhcp\-server \([https\://github\.com/ansible\-collections/community\.routeros/pull/137](https\://github\.com/ansible\-collections/community\.routeros/pull/137)\)\. - + ### Bugfixes * api\_modify \- address\-pool field of entries in API path ip dhcp\-server is not required anymore \([https\://github\.com/ansible\-collections/community\.routeros/pull/137](https\://github\.com/ansible\-collections/community\.routeros/pull/137)\)\. @@ -680,12 +515,12 @@ Feature and bugfix release\. ## v2\.4\.0 - + ### Release Summary Feature release improving the api\* modules\. - + ### Minor Changes * api\* modules \- Add new option force\_no\_cert to connect with ADH ciphers \([https\://github\.com/ansible\-collections/community\.routeros/pull/124](https\://github\.com/ansible\-collections/community\.routeros/pull/124)\)\. @@ -706,7 +541,7 @@ Feature release improving the api\* modules\. * api\_modify\, api\_info \- support for fields blackhole\, pref\-src\, routing\-table\, suppress\-hw\-offload\, type\, vrf\-interface in ip route path \([https\://github\.com/ansible\-collections/community\.routeros/pull/131](https\://github\.com/ansible\-collections/community\.routeros/pull/131)\)\. * api\_modify\, api\_info \- support paths system ntp client servers and system ntp server available in ROS7\, as well as new fields servers\, mode\, and vrf for system ntp client \([https\://github\.com/ansible\-collections/community\.routeros/pull/122](https\://github\.com/ansible\-collections/community\.routeros/pull/122)\)\. - + ### Bugfixes * api\_modify \- ip route entry can be defined without the need of gateway field\, which is correct for unreachable/blackhole type of routes \([https\://github\.com/ansible\-collections/community\.routeros/pull/131](https\://github\.com/ansible\-collections/community\.routeros/pull/131)\)\. @@ -724,7 +559,7 @@ Feature release improving the api\* modules\. ## v2\.3\.1 - + ### Release Summary Maintenance release with improved documentation\. @@ -737,19 +572,19 @@ Maintenance release with improved documentation\. ## v2\.3\.0 - + ### Release Summary Feature and bugfix release\. - + ### Minor Changes * The collection repository conforms to the [REUSE specification](https\://reuse\.software/spec/) except for the changelog fragments \([https\://github\.com/ansible\-collections/community\.routeros/pull/108](https\://github\.com/ansible\-collections/community\.routeros/pull/108)\)\. * api\* modules \- added timeout parameter \([https\://github\.com/ansible\-collections/community\.routeros/pull/109](https\://github\.com/ansible\-collections/community\.routeros/pull/109)\)\. * api\_modify\, api\_info \- support API path ip firewall mangle \([https\://github\.com/ansible\-collections/community\.routeros/pull/110](https\://github\.com/ansible\-collections/community\.routeros/pull/110)\)\. - + ### Bugfixes * api\_modify\, api\_info \- make API path ip dhcp\-server support script\, and ip firewall nat support in\-interface and in\-interface\-list \([https\://github\.com/ansible\-collections/community\.routeros/pull/110](https\://github\.com/ansible\-collections/community\.routeros/pull/110)\)\. @@ -757,12 +592,12 @@ Feature and bugfix release\. ## v2\.2\.1 - + ### Release Summary Bugfix release\. - + ### Bugfixes * api\_modify\, api\_info \- make API path ip dhcp\-server lease support server\=all \([https\://github\.com/ansible\-collections/community\.routeros/issues/104](https\://github\.com/ansible\-collections/community\.routeros/issues/104)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/107](https\://github\.com/ansible\-collections/community\.routeros/pull/107)\)\. @@ -771,17 +606,17 @@ Bugfix release\. ## v2\.2\.0 - + ### Release Summary New feature release\. - + ### Minor Changes * All software licenses are now in the LICENSES/ directory of the collection root\. Moreover\, SPDX\-License\-Identifier\: is used to declare the applicable license for every file that is not automatically generated \([https\://github\.com/ansible\-collections/community\.routeros/pull/101](https\://github\.com/ansible\-collections/community\.routeros/pull/101)\)\. - + ### Bugfixes * Include LICENSES/BSD\-2\-Clause\.txt file for the routeros module utils \([https\://github\.com/ansible\-collections/community\.routeros/pull/101](https\://github\.com/ansible\-collections/community\.routeros/pull/101)\)\. @@ -795,12 +630,12 @@ New feature release\. ## v2\.1\.0 - + ### Release Summary Feature and bugfix release with new modules\. - + ### Minor Changes * Added a community\.routeros\.api module defaults group\. Use with group/community\.routeros\.api to provide options for all API\-based modules \([https\://github\.com/ansible\-collections/community\.routeros/pull/89](https\://github\.com/ansible\-collections/community\.routeros/pull/89)\)\. @@ -809,7 +644,7 @@ Feature and bugfix release with new modules\. * api \- update query to accept symbolic parameters \([https\://github\.com/ansible\-collections/community\.routeros/pull/63](https\://github\.com/ansible\-collections/community\.routeros/pull/63)\)\. * api\* modules \- allow to set an encoding other than the default ASCII for communicating with the API \([https\://github\.com/ansible\-collections/community\.routeros/pull/95](https\://github\.com/ansible\-collections/community\.routeros/pull/95)\)\. - + ### Bugfixes * query \- fix query function check for \.id vs\. id arguments to not conflict with routeros arguments like identity \([https\://github\.com/ansible\-collections/community\.routeros/pull/68](https\://github\.com/ansible\-collections/community\.routeros/pull/68)\, [https\://github\.com/ansible\-collections/community\.routeros/issues/67](https\://github\.com/ansible\-collections/community\.routeros/issues/67)\)\. @@ -824,12 +659,12 @@ Feature and bugfix release with new modules\. ## v2\.0\.0 - + ### Release Summary A new major release with breaking changes in the behavior of community\.routeros\.api and community\.routeros\.command\. - + ### Minor Changes * api \- make validation of WHERE for query more strict \([https\://github\.com/ansible\-collections/community\.routeros/pull/53](https\://github\.com/ansible\-collections/community\.routeros/pull/53)\)\. @@ -843,7 +678,7 @@ A new major release with breaking changes in the behavior of community\.ro * api \- splitting commands no longer uses a naive split by whitespace\, but a more RouterOS CLI compatible splitting algorithm \([https\://github\.com/ansible\-collections/community\.routeros/pull/45](https\://github\.com/ansible\-collections/community\.routeros/pull/45)\)\. * command \- the module now always indicates that a change happens\. If this is not correct\, please use changed\_when to determine the correct changed status for a task \([https\://github\.com/ansible\-collections/community\.routeros/pull/50](https\://github\.com/ansible\-collections/community\.routeros/pull/50)\)\. - + ### Bugfixes * api \- improve splitting of WHERE queries \([https\://github\.com/ansible\-collections/community\.routeros/pull/47](https\://github\.com/ansible\-collections/community\.routeros/pull/47)\)\. @@ -865,12 +700,12 @@ A new major release with breaking changes in the behavior of community\.ro ## v1\.2\.0 - + ### Release Summary Bugfix and feature release\. - + ### Minor Changes * Avoid internal ansible\-core module\_utils in favor of equivalent public API available since at least Ansible 2\.9 \([https\://github\.com/ansible\-collections/community\.routeros/pull/38](https\://github\.com/ansible\-collections/community\.routeros/pull/38)\)\. @@ -878,7 +713,7 @@ Bugfix and feature release\. * api \- rename option ssl to tls\, and keep the old name as an alias \([https\://github\.com/ansible\-collections/community\.routeros/pull/37](https\://github\.com/ansible\-collections/community\.routeros/pull/37)\)\. * fact \- add fact ansible\_net\_config\_nonverbose to get idempotent config \(no date\, no verbose\) \([https\://github\.com/ansible\-collections/community\.routeros/pull/23](https\://github\.com/ansible\-collections/community\.routeros/pull/23)\)\. - + ### Bugfixes * api \- when using TLS/SSL\, remove explicit cipher configuration to insecure values\, which also makes it impossible to connect to newer RouterOS versions \([https\://github\.com/ansible\-collections/community\.routeros/pull/34](https\://github\.com/ansible\-collections/community\.routeros/pull/34)\)\. @@ -886,12 +721,12 @@ Bugfix and feature release\. ## v1\.1\.0 - + ### Release Summary This release allow dashes in usernames for SSH\-based modules\. - + ### Minor Changes * command \- added support for a dash \(\-\) in username \([https\://github\.com/ansible\-collections/community\.routeros/pull/18](https\://github\.com/ansible\-collections/community\.routeros/pull/18)\)\. @@ -900,12 +735,12 @@ This release allow dashes in usernames for SSH\-based modules\. ## v1\.0\.1 - + ### Release Summary Maintenance release with a bugfix for api\. - + ### Bugfixes * api \- remove id to \.id as default requirement which conflicts with RouterOS id configuration parameter \([https\://github\.com/ansible\-collections/community\.routeros/pull/15](https\://github\.com/ansible\-collections/community\.routeros/pull/15)\)\. @@ -913,12 +748,12 @@ Maintenance release with a bugfix for api\. ## v1\.0\.0 - + ### Release Summary This is the first production \(non\-prerelease\) release of community\.routeros\. - + ### Bugfixes * routeros terminal plugin \- allow slashes in hostnames for terminal detection\. Without this\, slashes in hostnames will result in connection timeouts \([https\://github\.com/ansible\-collections/community\.network/pull/138](https\://github\.com/ansible\-collections/community\.network/pull/138)\)\. @@ -926,12 +761,12 @@ This is the first production \(non\-prerelease\) release of community\.rou ## v0\.1\.1 - + ### Release Summary Small improvements and bugfixes over the initial release\. - + ### Bugfixes * api \- fix crash when the ssl parameter is used \([https\://github\.com/ansible\-collections/community\.routeros/pull/3](https\://github\.com/ansible\-collections/community\.routeros/pull/3)\)\. @@ -939,12 +774,12 @@ Small improvements and bugfixes over the initial release\. ## v0\.1\.0 - + ### Release Summary The community\.routeros continues the work on the Ansible RouterOS modules from their state in community\.network 1\.2\.0\. The changes listed here are thus relative to the modules community\.network\.routeros\_\*\. - + ### Minor Changes * facts \- now also collecting data about BGP and OSPF \([https\://github\.com/ansible\-collections/community\.network/pull/101](https\://github\.com/ansible\-collections/community\.network/pull/101)\)\. diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 2e73ef4..4b511c3 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,142 +4,6 @@ Community RouterOS Release Notes .. contents:: Topics -v3.8.1 -====== - -Release Summary ---------------- - -Bugfix release. - -Bugfixes --------- - -- facts and api_facts modules - prevent deprecation warnings when used with ansible-core 2.19 (https://github.com/ansible-collections/community.routeros/pull/384). - -v3.8.0 -====== - -Release Summary ---------------- - -Feature release. - -Minor Changes -------------- - -- api_info, api_modify - add ``interface ethernet switch port-isolation`` which is supported since RouterOS 6.43 (https://github.com/ansible-collections/community.routeros/pull/375). -- api_info, api_modify - add ``routing bfd configuration``. Officially stabilized BFD support for BGP and OSPF is available since RouterOS 7.11 - (https://github.com/ansible-collections/community.routeros/pull/375). -- api_modify, api_info - support API path ``ip ipsec mode-config`` (https://github.com/ansible-collections/community.routeros/pull/376). - -v3.7.0 -====== - -Release Summary ---------------- - -Feature release. - -Minor Changes -------------- - -- api_find_and_modify - allow to control whether ``dynamic`` and/or ``builtin`` entries are ignored with the new ``ignore_dynamic`` and ``ignore_builtin`` options (https://github.com/ansible-collections/community.routeros/issues/372, https://github.com/ansible-collections/community.routeros/pull/373). -- api_info, api_modify - add ``port-cost-mode`` to ``interface bridge`` which is supported since RouterOS 7.13 (https://github.com/ansible-collections/community.routeros/pull/371). - -v3.6.0 -====== - -Release Summary ---------------- - -Feature release. - -Minor Changes -------------- - -- api_info, api_modify - add ``mdns-repeat-ifaces`` to ``ip dns`` for RouterOS 7.16 and newer (https://github.com/ansible-collections/community.routeros/pull/358). -- api_info, api_modify - field name change in ``routing bgp connection`` path implemented by RouterOS 7.19 and newer (https://github.com/ansible-collections/community.routeros/pull/360). -- api_info, api_modify - rename ``is-responder`` property in ``interface wireguard peers`` to ``responder`` for RouterOS 7.17 and newer (https://github.com/ansible-collections/community.routeros/pull/364). - -v3.5.0 -====== - -Release Summary ---------------- - -Feature release. - -Minor Changes -------------- - -- api_info, api_modify - change default for ``/ip/cloud/ddns-enabled`` for RouterOS 7.17 and newer from ``yes`` to ``auto`` (https://github.com/ansible-collections/community.routeros/pull/350). - -v3.4.0 -====== - -Release Summary ---------------- - -Feature and bugfix release. - -Minor Changes -------------- - -- api_info, api_modify - add support for the ``ip dns forwarders`` path implemented by RouterOS 7.17 and newer (https://github.com/ansible-collections/community.routeros/pull/343). - -Bugfixes --------- - -- api_info, api_modify - remove the primary key ``action`` from the ``interface wifi provisioning`` path, since RouterOS also allows to create completely duplicate entries (https://github.com/ansible-collections/community.routeros/issues/344, https://github.com/ansible-collections/community.routeros/pull/345). - -v3.3.0 -====== - -Release Summary ---------------- - -Feature release. - -Minor Changes -------------- - -- api_info, api_modify - add missing attribute ``require-message-auth`` for the ``radius`` path which exists since RouterOS version 7.15 (https://github.com/ansible-collections/community.routeros/issues/338, https://github.com/ansible-collections/community.routeros/pull/339). -- api_info, api_modify - add the ``interface 6to4`` path. Used to manage IPv6 tunnels via tunnel-brokers like HE, where native IPv6 is not provided (https://github.com/ansible-collections/community.routeros/pull/342). -- api_info, api_modify - add the ``interface wireless access-list`` and ``interface wireless connect-list`` paths (https://github.com/ansible-collections/community.routeros/issues/284, https://github.com/ansible-collections/community.routeros/pull/340). -- api_info, api_modify - add the ``use-interface-duid`` option for ``ipv6 dhcp-client`` path. This option prevents issues with Fritzbox modems and routers, when using virtual interfaces (like VLANs) may create duplicated records in hosts config, this breaks original "expose-host" function. Also add the ``script``, ``custom-duid`` and ``validate-server-duid`` as backport from 7.15 version update (https://github.com/ansible-collections/community.routeros/pull/341). - -v3.2.0 -====== - -Release Summary ---------------- - -Feature release. - -Minor Changes -------------- - -- api_info, api_modify - add support for the ``routing filter community-list`` path implemented by RouterOS 7 and newer (https://github.com/ansible-collections/community.routeros/pull/331). - -v3.1.0 -====== - -Release Summary ---------------- - -Bugfix and feature release. - -Minor Changes -------------- - -- api_info, api_modify - add missing fields ``comment``, ``next-pool`` to ``ip pool`` path (https://github.com/ansible-collections/community.routeros/pull/327). - -Bugfixes --------- - -- api_info, api_modify - fields ``log`` and ``log-prefix`` in paths ``ip firewall filter``, ``ip firewall mangle``, ``ip firewall nat``, ``ip firewall raw`` now have the correct default values (https://github.com/ansible-collections/community.routeros/pull/324). - v3.0.0 ====== diff --git a/README.md b/README.md index 7d4f4c3..f7b488d 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,7 @@ SPDX-License-Identifier: GPL-3.0-or-later --> # Community RouterOS Collection -[![Documentation](https://img.shields.io/badge/docs-brightgreen.svg)](https://docs.ansible.com/ansible/devel/collections/community/routeros/) -[![CI](https://github.com/ansible-collections/community.routeros/actions/workflows/nox.yml/badge.svg?branch=main)](https://github.com/ansible-collections/community.routeros/actions) +[![CI](https://github.com/ansible-collections/community.routeros/workflows/CI/badge.svg?event=push)](https://github.com/ansible-collections/community.routeros/actions) [![Codecov](https://img.shields.io/codecov/c/github/ansible-collections/community.routeros)](https://codecov.io/gh/ansible-collections/community.routeros) [![REUSE status](https://api.reuse.software/badge/github.com/ansible-collections/community.routeros)](https://api.reuse.software/info/github.com/ansible-collections/community.routeros) @@ -34,11 +33,11 @@ For more information about communication, see the [Ansible communication guide]( ## Tested with Ansible -Tested with the current ansible-core 2.15, ansible-core 2.16, ansible-core 2.17, ansible-core 2.18, and ansible-core 2.19 releases and the current development version of ansible-core. Ansible 2.9, ansible-base 2.10, and ansible-core versions before 2.15.0 are not supported. +Tested with the current ansible-core 2.15, ansible-core 2.16, ansible-core 2.17, and ansible-core 2.18 releases and the current development version of ansible-core. Ansible 2.9, ansible-base 2.10, and ansible-core versions before 2.15.0 are not supported. ## External requirements -The exact requirements for every module are listed in the module documentation. +The exact requirements for every module are listed in the module documentation. ### Supported connections @@ -208,4 +207,4 @@ See [LICENSES/GPL-3.0-or-later.txt](https://github.com/ansible-collections/commu Parts of the collection are licensed under the [BSD 2-Clause license](https://github.com/ansible-collections/community.routeros/blob/main/LICENSES/BSD-2-Clause.txt). -All files have a machine readable `SDPX-License-Identifier:` comment denoting its respective license(s) or an equivalent entry in an accompanying `.license` file. Only changelog fragments (which will not be part of a release) are covered by a blanket statement in `REUSE.toml`. This conforms to the [REUSE specification](https://reuse.software/spec/). +All files have a machine readable `SDPX-License-Identifier:` comment denoting its respective license(s) or an equivalent entry in an accompanying `.license` file. Only changelog fragments (which will not be part of a release) are covered by a blanket statement in `.reuse/dep5`. This conforms to the [REUSE specification](https://reuse.software/spec/). diff --git a/REUSE.toml b/REUSE.toml deleted file mode 100644 index ff95bb8..0000000 --- a/REUSE.toml +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) Ansible Project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -version = 1 - -[[annotations]] -path = "changelogs/fragments/**" -precedence = "aggregate" -SPDX-FileCopyrightText = "Ansible Project" -SPDX-License-Identifier = "GPL-3.0-or-later" diff --git a/antsibull-nox.toml b/antsibull-nox.toml deleted file mode 100644 index 72982fa..0000000 --- a/antsibull-nox.toml +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) Ansible Project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -[collection_sources] -"community.internal_test_tools" = "git+https://github.com/ansible-collections/community.internal_test_tools.git,main" -"community.netcommon" = "git+https://github.com/ansible-collections/ansible.netcommon.git,main" -"community.utils" = "git+https://github.com/ansible-collections/ansible.utils.git,main" - -[sessions] - -[sessions.lint] -run_isort = false -run_black = false -run_flake8 = false -run_pylint = false -run_yamllint = true -yamllint_config = ".yamllint" -yamllint_config_plugins = ".yamllint-docs" -yamllint_config_plugins_examples = ".yamllint-examples" -yamllint_config_extra_docs = ".yamllint-extra-docs" -run_mypy = false - -[sessions.docs_check] -validate_collection_refs="all" -codeblocks_restrict_types = [ - "ansible-output", - "ini", - "yaml", - "yaml+jinja", -] -codeblocks_restrict_type_exact_case = true -codeblocks_allow_without_type = false -codeblocks_allow_literal_blocks = false - -[sessions.license_check] - -[sessions.extra_checks] -run_no_unwanted_files = true -no_unwanted_files_module_extensions = [".py"] -no_unwanted_files_yaml_extensions = [".yml"] -run_action_groups = true -run_no_trailing_whitespace = true -no_trailing_whitespace_skip_directories = [ - "tests/unit/plugins/modules/fixtures/", -] -run_avoid_characters = true - -[[sessions.extra_checks.action_groups_config]] -name = "api" -pattern = "^api.*$" -exclusions = [] -doc_fragment = "community.routeros.attributes.actiongroup_api" - -[[sessions.extra_checks.avoid_character_group]] -name = "tab" -regex = "\\x09" - -[sessions.build_import_check] -run_galaxy_importer = true - -[sessions.ansible_test_sanity] -include_devel = true - -[sessions.ansible_test_units] -include_devel = true - -[sessions.ansible_test_integration_w_default_container] -include_devel = true -controller_python_versions_only = true - -[sessions.ansible_test_integration_w_default_container.core_python_versions] -"2.15" = ["2.7", "3.6", "3.7"] -"2.16" = ["3.10"] -"2.17" = ["3.8"] -"2.18" = ["3.9"] -"2.19" = ["3.11"] - -[[sessions.ee_check.execution_environments]] -name = "devel-ubi-9" -description = "ansible-core devel @ RHEL UBI 9" -test_playbooks = ["tests/ee/all.yml"] -config.images.base_image.name = "docker.io/redhat/ubi9:latest" -config.dependencies.ansible_core.package_pip = "https://github.com/ansible/ansible/archive/devel.tar.gz" -config.dependencies.ansible_runner.package_pip = "ansible-runner" -config.dependencies.python_interpreter.package_system = "python3.12 python3.12-pip python3.12-wheel python3.12-cryptography" -config.dependencies.python_interpreter.python_path = "/usr/bin/python3.12" -runtime_environment = {"ANSIBLE_PRIVATE_ROLE_VARS" = "true"} - -[[sessions.ee_check.execution_environments]] -name = "2.15-rocky-9" -description = "ansible-core 2.15 @ Rocky Linux 9" -test_playbooks = ["tests/ee/all.yml"] -config.images.base_image.name = "quay.io/rockylinux/rockylinux:9" -config.dependencies.ansible_core.package_pip = "https://github.com/ansible/ansible/archive/stable-2.15.tar.gz" -config.dependencies.ansible_runner.package_pip = "ansible-runner" -runtime_environment = {"ANSIBLE_PRIVATE_ROLE_VARS" = "true"} diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index df9d0e0..09b5b6d 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -809,138 +809,3 @@ releases: fragments: - 3.0.0.yml release_date: '2024-10-20' - 3.1.0: - changes: - bugfixes: - - api_info, api_modify - fields ``log`` and ``log-prefix`` in paths ``ip firewall - filter``, ``ip firewall mangle``, ``ip firewall nat``, ``ip firewall raw`` - now have the correct default values (https://github.com/ansible-collections/community.routeros/pull/324). - minor_changes: - - api_info, api_modify - add missing fields ``comment``, ``next-pool`` to - ``ip pool`` path (https://github.com/ansible-collections/community.routeros/pull/327). - release_summary: Bugfix and feature release. - fragments: - - 3.1.0.yml - - 324-fix-firewall-log-and-log-prefix.yaml - - 327-add-missing-ip-pool-fields.yml - release_date: '2024-12-02' - 3.2.0: - changes: - minor_changes: - - api_info, api_modify - add support for the ``routing filter community-list`` - path implemented by RouterOS 7 and newer (https://github.com/ansible-collections/community.routeros/pull/331). - release_summary: Feature release. - fragments: - - 3.2.0.yml - - 331-add-routing-filter-community-list.yml - release_date: '2024-12-30' - 3.3.0: - changes: - minor_changes: - - api_info, api_modify - add missing attribute ``require-message-auth`` for - the ``radius`` path which exists since RouterOS version 7.15 (https://github.com/ansible-collections/community.routeros/issues/338, - https://github.com/ansible-collections/community.routeros/pull/339). - - api_info, api_modify - add the ``interface 6to4`` path. Used to manage IPv6 - tunnels via tunnel-brokers like HE, where native IPv6 is not provided (https://github.com/ansible-collections/community.routeros/pull/342). - - api_info, api_modify - add the ``interface wireless access-list`` and ``interface - wireless connect-list`` paths (https://github.com/ansible-collections/community.routeros/issues/284, - https://github.com/ansible-collections/community.routeros/pull/340). - - api_info, api_modify - add the ``use-interface-duid`` option for ``ipv6 - dhcp-client`` path. This option prevents issues with Fritzbox modems and - routers, when using virtual interfaces (like VLANs) may create duplicated - records in hosts config, this breaks original "expose-host" function. Also - add the ``script``, ``custom-duid`` and ``validate-server-duid`` as backport - from 7.15 version update (https://github.com/ansible-collections/community.routeros/pull/341). - release_summary: Feature release. - fragments: - - 3.3.0.yml - - 339-add-require-message-auth-for-radius.yml - - 340-add-interface-wireless-access-and-connect-list.yml - - 341-add-dhcpv6-client-use-interface-duid.yml - - 342-add-interface-6to4.yml - release_date: '2025-01-27' - 3.4.0: - changes: - bugfixes: - - api_info, api_modify - remove the primary key ``action`` from the ``interface - wifi provisioning`` path, since RouterOS also allows to create completely - duplicate entries (https://github.com/ansible-collections/community.routeros/issues/344, - https://github.com/ansible-collections/community.routeros/pull/345). - minor_changes: - - api_info, api_modify - add support for the ``ip dns forwarders`` path implemented - by RouterOS 7.17 and newer (https://github.com/ansible-collections/community.routeros/pull/343). - release_summary: Feature and bugfix release. - fragments: - - 3.4.0.yml - - 343-add-ip-dns-forwarders.yml - - 345-interface-wifi-provisioning.yml - release_date: '2025-02-24' - 3.5.0: - changes: - minor_changes: - - api_info, api_modify - change default for ``/ip/cloud/ddns-enabled`` for - RouterOS 7.17 and newer from ``yes`` to ``auto`` (https://github.com/ansible-collections/community.routeros/pull/350). - release_summary: Feature release. - fragments: - - 3.5.0.yml - - 350-ip-cloud-ddns-enabled-auto.yml - release_date: '2025-03-22' - 3.6.0: - changes: - minor_changes: - - api_info, api_modify - add ``mdns-repeat-ifaces`` to ``ip dns`` for RouterOS - 7.16 and newer (https://github.com/ansible-collections/community.routeros/pull/358). - - api_info, api_modify - field name change in ``routing bgp connection`` path - implemented by RouterOS 7.19 and newer (https://github.com/ansible-collections/community.routeros/pull/360). - - api_info, api_modify - rename ``is-responder`` property in ``interface wireguard - peers`` to ``responder`` for RouterOS 7.17 and newer (https://github.com/ansible-collections/community.routeros/pull/364). - release_summary: Feature release. - fragments: - - 3.6.0.yml - - 358-mdns-repeat-ifaces.yml - - 360-bgp-connection-afi.yml - - 364-wireguard-responder.yml - release_date: '2025-04-21' - 3.7.0: - changes: - minor_changes: - - api_find_and_modify - allow to control whether ``dynamic`` and/or ``builtin`` - entries are ignored with the new ``ignore_dynamic`` and ``ignore_builtin`` - options (https://github.com/ansible-collections/community.routeros/issues/372, - https://github.com/ansible-collections/community.routeros/pull/373). - - api_info, api_modify - add ``port-cost-mode`` to ``interface bridge`` which - is supported since RouterOS 7.13 (https://github.com/ansible-collections/community.routeros/pull/371). - release_summary: Feature release. - fragments: - - 3.7.0.yml - - 371-add-bridge-port-cost-mode.yml - - 373-api_find_and_modify-dynamic-builtin.yml - release_date: '2025-05-31' - 3.8.0: - changes: - minor_changes: - - api_info, api_modify - add ``interface ethernet switch port-isolation`` - which is supported since RouterOS 6.43 (https://github.com/ansible-collections/community.routeros/pull/375). - - 'api_info, api_modify - add ``routing bfd configuration``. Officially stabilized - BFD support for BGP and OSPF is available since RouterOS 7.11 - - (https://github.com/ansible-collections/community.routeros/pull/375). - - ' - - api_modify, api_info - support API path ``ip ipsec mode-config`` (https://github.com/ansible-collections/community.routeros/pull/376). - release_summary: Feature release. - fragments: - - 3.8.0.yml - - 375-port_isolation-and-routing_bfd_configuration.yml - - 376-ipsec-mode-config.yml - release_date: '2025-06-14' - 3.8.1: - changes: - bugfixes: - - facts and api_facts modules - prevent deprecation warnings when used with - ansible-core 2.19 (https://github.com/ansible-collections/community.routeros/pull/384). - release_summary: Bugfix release. - fragments: - - 3.8.1.yml - - 384-warnings.yml - release_date: '2025-07-26' diff --git a/changelogs/config.yaml b/changelogs/config.yaml index 39b7120..a02e530 100644 --- a/changelogs/config.yaml +++ b/changelogs/config.yaml @@ -7,9 +7,9 @@ changelog_filename_template: ../CHANGELOG.rst changelog_filename_version_depth: 0 changes_file: changelog.yaml changes_format: combined -ignore_other_fragment_extensions: true keep_fragments: false mention_ancestor: true +flatmap: true new_plugins_after_name: removed_features notesdir: fragments output_formats: @@ -40,4 +40,3 @@ use_fqcn: true add_plugin_period: true changelog_nice_yaml: true changelog_sort: version -vcs: auto diff --git a/changelogs/fragments/3.9.0.yml b/changelogs/fragments/3.9.0.yml deleted file mode 100644 index 63e0654..0000000 --- a/changelogs/fragments/3.9.0.yml +++ /dev/null @@ -1 +0,0 @@ -release_summary: Feature release. \ No newline at end of file diff --git a/changelogs/fragments/380-ipv6-settings.yml b/changelogs/fragments/380-ipv6-settings.yml deleted file mode 100644 index a8c6e9b..0000000 --- a/changelogs/fragments/380-ipv6-settings.yml +++ /dev/null @@ -1,3 +0,0 @@ -minor_changes: - - api_info, api_modify - add ``disable-link-local-address`` and ``stale-neighbor-timeout`` fields to ``ipv6 settings`` (https://github.com/ansible-collections/community.routeros/pull/380). - - api_info, api_modify - adjust neighbor limit fields in ``ipv6 settings`` to match RouterOS 7.18 and newer (https://github.com/ansible-collections/community.routeros/pull/380). diff --git a/changelogs/fragments/381-logging-cef.yml b/changelogs/fragments/381-logging-cef.yml deleted file mode 100644 index 849f82a..0000000 --- a/changelogs/fragments/381-logging-cef.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - api_info, api modify - add ``remote-log-format``, ``remote-protocol``, and ``event-delimiter`` to ``system logging action`` (https://github.com/ansible-collections/community.routeros/pull/381). diff --git a/changelogs/fragments/382-mangle-passthrough.yml b/changelogs/fragments/382-mangle-passthrough.yml deleted file mode 100644 index 2123dfa..0000000 --- a/changelogs/fragments/382-mangle-passthrough.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - api_info, api_modify - set ``passthrough`` default in ``ip firewall mangle`` to ``true`` for RouterOS 7.19 and newer (https://github.com/ansible-collections/community.routeros/pull/382). diff --git a/changelogs/fragments/385-vrf-support-for-ovpn-server.yml b/changelogs/fragments/385-vrf-support-for-ovpn-server.yml deleted file mode 100644 index 4790a9d..0000000 --- a/changelogs/fragments/385-vrf-support-for-ovpn-server.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -minor_changes: - - api_info, api_modify - since RouterOS 7.17 VRF is supported for OVPN server. - It now supports multiple entries, while ``api_modify`` so far only accepted a single entry. - The ``interface ovpn-server server`` path now allows multiple entries - on RouterOS 7.17 and newer - (https://github.com/ansible-collections/community.routeros/pull/383). diff --git a/changelogs/fragments/386-fix-pattern-to-handle-long-identity.yml b/changelogs/fragments/386-fix-pattern-to-handle-long-identity.yml deleted file mode 100644 index b9a749f..0000000 --- a/changelogs/fragments/386-fix-pattern-to-handle-long-identity.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -bugfixes: - - routeros terminal plugin - fix ``terminal_stdout_re`` pattern to handle long system identities when connecting to RouterOS through SSH (https://github.com/ansible-collections/community.routeros/pull/386). diff --git a/docs/docsite/rst/api-guide.rst b/docs/docsite/rst/api-guide.rst index 9df17fc..6140d81 100644 --- a/docs/docsite/rst/api-guide.rst +++ b/docs/docsite/rst/api-guide.rst @@ -57,7 +57,7 @@ This results in the following output: } PLAY RECAP ******************************************************************************************************* - localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 + localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 Check out the documentation of the :ansplugin:`community.routeros.api module ` for details on the options. @@ -191,7 +191,7 @@ When this playbook completed successfully, you should be able to use the HTTPS a .. code-block:: yaml+jinja - community.routeros.api: - # ... + ... tls: true validate_certs: true validate_cert_hostname: true diff --git a/docs/docsite/rst/ssh-guide.rst b/docs/docsite/rst/ssh-guide.rst index ac1f65b..4b0eb20 100644 --- a/docs/docsite/rst/ssh-guide.rst +++ b/docs/docsite/rst/ssh-guide.rst @@ -66,22 +66,22 @@ With the above inventory, you can use the following playbook to execute ``/syste gather_facts: false tasks: - - name: Gather system resources - community.routeros.command: - commands: - - /system resource print - register: system_resource_print + - name: Gather system resources + community.routeros.command: + commands: + - /system resource print + register: system_resource_print - - name: Show system resources - debug: - var: system_resource_print.stdout_lines + - name: Show system resources + debug: + var: system_resource_print.stdout_lines - - name: Gather facts - community.routeros.facts: + - name: Gather facts + community.routeros.facts: - - name: Show a fact - debug: - msg: "First IP address: {{ ansible_net_all_ipv4_addresses[0] }}" + - name: Show a fact + debug: + msg: "First IP address: {{ ansible_net_all_ipv4_addresses[0] }}" This results in the following output: @@ -126,4 +126,4 @@ This results in the following output: } PLAY RECAP ******************************************************************************************************* - router : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 + router : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 diff --git a/galaxy.yml b/galaxy.yml index f2ba31a..1508467 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -7,7 +7,7 @@ namespace: community name: routeros -version: 3.9.0 +version: 3.0.0 readme: README.md authors: - Egor Zaitsev (github.com/heuels) @@ -16,7 +16,7 @@ authors: description: Modules and plugins for MikroTik RouterOS license: - GPL-3.0-or-later -# license_file: COPYING +#license_file: COPYING tags: - network - mikrotik diff --git a/noxfile.py b/noxfile.py deleted file mode 100644 index 16187f9..0000000 --- a/noxfile.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) Ansible Project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -# The following metadata allows Python runners and nox to install the required -# dependencies for running this Python script: -# -# /// script -# dependencies = ["nox>=2025.02.09", "antsibull-nox"] -# /// - -import os -import sys - -import nox - - -# We try to import antsibull-nox, and if that doesn't work, provide a more useful -# error message to the user. -try: - import antsibull_nox -except ImportError: - print("You need to install antsibull-nox in the same Python environment as nox.") - sys.exit(1) - - -IN_CI = os.environ.get("CI") == "true" - - -antsibull_nox.load_antsibull_nox_toml() - - -@nox.session(name="update-docs", default=True) -def update_docs_fragments(session: nox.Session) -> None: - """ - Update/check auto-generated parts of docs fragments. - """ - session.install("ansible-core") - prepare = antsibull_nox.sessions.prepare_collections( - session, install_in_site_packages=True - ) - if not prepare: - return - data = ["python", "tests/update-docs.py"] - if IN_CI: - data.append("--lint") - session.run(*data) - - -# Allow to run the noxfile with `python noxfile.py`, `pipx run noxfile.py`, or similar. -# Requires nox >= 2025.02.09 -if __name__ == "__main__": - nox.main() diff --git a/plugins/cliconf/routeros.py b/plugins/cliconf/routeros.py index dc2f4a9..412627b 100644 --- a/plugins/cliconf/routeros.py +++ b/plugins/cliconf/routeros.py @@ -5,14 +5,15 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -DOCUMENTATION = r""" +DOCUMENTATION = ''' +--- author: "Egor Zaitsev (@heuels)" name: routeros short_description: Use routeros cliconf to run command on MikroTik RouterOS platform description: - - This routeros plugin provides low level abstraction APIs for sending and receiving CLI commands from MikroTik RouterOS - network devices. -""" + - This routeros plugin provides low level abstraction apis for + sending and receiving CLI commands from MikroTik RouterOS network devices. +''' import re import json diff --git a/plugins/doc_fragments/api.py b/plugins/doc_fragments/api.py index 5b0b411..b0ad979 100644 --- a/plugins/doc_fragments/api.py +++ b/plugins/doc_fragments/api.py @@ -10,7 +10,7 @@ __metaclass__ = type class ModuleDocFragment(object): - DOCUMENTATION = r""" + DOCUMENTATION = r''' options: hostname: description: @@ -43,16 +43,17 @@ options: - ssl port: description: - - RouterOS API port. If O(tls) is set, port will apply to TLS/SSL connection. + - RouterOS api port. If O(tls) is set, port will apply to TLS/SSL connection. - Defaults are V(8728) for the HTTP API, and V(8729) for the HTTPS API. type: int force_no_cert: description: - Set to V(true) to connect without a certificate when O(tls=true). - See also O(validate_certs). - - B(Note:) this forces the use of anonymous Diffie-Hellman (ADH) ciphers. The protocol is susceptible to Man-in-the-Middle - attacks, because the keys used in the exchange are not authenticated. Instead of simply connecting without a certificate - to "make things work" have a look at O(validate_certs) and O(ca_path). + - B(Note:) this forces the use of anonymous Diffie-Hellman (ADH) ciphers. The protocol is susceptible + to Man-in-the-Middle attacks, because the keys used in the exchange are not authenticated. + Instead of simply connecting without a certificate to "make things work" have a look at + O(validate_certs) and O(ca_path). type: bool default: false version_added: 2.4.0 @@ -60,9 +61,10 @@ options: description: - Set to V(false) to skip validation of TLS certificates. - See also O(validate_cert_hostname). Only used when O(tls=true). - - B(Note:) instead of simply deactivating certificate validations to "make things work", please consider creating your - own CA certificate and using it to sign certificates used for your router. You can tell the module about your CA certificate - with the O(ca_path) option. + - B(Note:) instead of simply deactivating certificate validations to "make things work", + please consider creating your own CA certificate and using it to sign certificates used + for your router. You can tell the module about your CA certificate with the O(ca_path) + option. type: bool default: true version_added: 1.2.0 @@ -91,10 +93,10 @@ requirements: - Python >= 3.6 (for librouteros) seealso: - ref: ansible_collections.community.routeros.docsite.api-guide - description: How to connect to RouterOS devices with the RouterOS API. -""" + description: How to connect to RouterOS devices with the RouterOS API +''' - RESTRICT = r""" + RESTRICT = r''' options: restrict: type: list @@ -113,21 +115,24 @@ options: values: description: - The values of the field to limit to. - - 'Note that the types of the values are important. If you provide a string V("0"), and librouteros converts the - value returned by the API to the integer V(0), then this will not match. If you are not sure, better include both - variants: both the string and the integer.' + - >- + Note that the types of the values are important. If you provide a string V("0"), + and librouteros converts the value returned by the API to the integer V(0), + then this will not match. If you are not sure, better include both variants: + both the string and the integer. type: list elements: raw regex: description: - A regular expression matching values of the field to limit to. - Note that all values will be converted to strings before matching. - - It is not possible to match disabled values with regular expressions. Set O(restrict[].match_disabled=true) if - you also want to match disabled values. + - It is not possible to match disabled values with regular expressions. + Set O(restrict[].match_disabled=true) if you also want to match disabled values. type: str invert: description: - - Invert the condition. This affects O(restrict[].match_disabled), O(restrict[].values), and O(restrict[].regex). + - Invert the condition. This affects O(restrict[].match_disabled), O(restrict[].values), + and O(restrict[].regex). type: bool default: false -""" +''' diff --git a/plugins/doc_fragments/attributes.py b/plugins/doc_fragments/attributes.py index b8e68bf..e18a48f 100644 --- a/plugins/doc_fragments/attributes.py +++ b/plugins/doc_fragments/attributes.py @@ -11,102 +11,88 @@ __metaclass__ = type class ModuleDocFragment(object): # Standard documentation fragment - DOCUMENTATION = r""" + DOCUMENTATION = r''' options: {} attributes: - check_mode: - description: Can run in C(check_mode) and return changed status prediction without modifying target. - diff_mode: - description: Will return details on what has changed (or possibly needs changing in C(check_mode)), when in diff mode. - platform: - description: Target OS/families that can be operated against. - support: N/A - idempotent: - description: - - When run twice in a row outside check mode, with the same arguments, the second invocation indicates no change. - - This assumes that the system controlled/queried by the module has not changed in a relevant way. -""" - - # Should be used together with the standard fragment - IDEMPOTENT_NOT_MODIFY_STATE = r""" -options: {} -attributes: - idempotent: - support: full - details: - - This action does not modify state. -""" + check_mode: + description: Can run in C(check_mode) and return changed status prediction without modifying target. + diff_mode: + description: Will return details on what has changed (or possibly needs changing in C(check_mode)), when in diff mode. + platform: + description: Target OS/families that can be operated against. + support: N/A +''' # Should be used together with the standard fragment INFO_MODULE = r''' options: {} attributes: - check_mode: - support: full - details: - - This action does not modify state. - diff_mode: - support: N/A - details: - - This action does not modify state. + check_mode: + support: full + details: + - This action does not modify state. + diff_mode: + support: N/A + details: + - This action does not modify state. ''' ACTIONGROUP_API = r''' options: {} attributes: - action_group: - description: Use C(group/community.routeros.api) in C(module_defaults) to set defaults for this module. - support: full - membership: - - community.routeros.api + action_group: + description: Use C(group/community.routeros.api) in C(module_defaults) to set defaults for this module. + support: full + membership: + - community.routeros.api ''' - CONN = r""" + CONN = r''' options: {} attributes: - become: - description: Is usable alongside C(become) keywords. - connection: - description: Uses the target's configured connection information to execute code on it. - delegation: - description: Can be used in conjunction with C(delegate_to) and related keywords. -""" + become: + description: Is usable alongside C(become) keywords. + connection: + description: Uses the target's configured connection information to execute code on it. + delegation: + description: Can be used in conjunction with C(delegate_to) and related keywords. +''' - FACTS = r""" + FACTS = r''' options: {} attributes: - facts: - description: Action returns an C(ansible_facts) dictionary that will update existing host facts. -""" + facts: + description: Action returns an C(ansible_facts) dictionary that will update existing host facts. +''' # Should be used together with the standard fragment and the FACTS fragment FACTS_MODULE = r''' options: {} attributes: - check_mode: - support: full - details: - - This action does not modify state. - diff_mode: - support: N/A - details: - - This action does not modify state. - facts: - support: full + check_mode: + support: full + details: + - This action does not modify state. + diff_mode: + support: N/A + details: + - This action does not modify state. + facts: + support: full ''' - FILES = r""" + FILES = r''' options: {} attributes: - safe_file_operations: - description: Uses Ansible's strict file operation functions to ensure proper permissions and avoid data corruption. -""" + safe_file_operations: + description: Uses Ansible's strict file operation functions to ensure proper permissions and avoid data corruption. +''' - FLOW = r""" + FLOW = r''' options: {} attributes: - action: - description: Indicates this has a corresponding action plugin so some parts of the options can be executed on the controller. - async: - description: Supports being used with the C(async) keyword. -""" + action: + description: Indicates this has a corresponding action plugin so some parts of the options can be executed on the controller. + async: + description: Supports being used with the C(async) keyword. +''' diff --git a/plugins/filter/join.yml b/plugins/filter/join.yml index f25d739..9ff8a50 100644 --- a/plugins/filter/join.yml +++ b/plugins/filter/join.yml @@ -20,7 +20,6 @@ DOCUMENTATION: - Felix Fontein (@felixfontein) EXAMPLES: | - --- - name: Join arguments for a RouterOS CLI command ansible.builtin.set_fact: arguments: "{{ ['foo=bar', 'comment=foo is bar'] | community.routeros.join }}" diff --git a/plugins/filter/list_to_dict.yml b/plugins/filter/list_to_dict.yml index 7b7c5b1..6e7992d 100644 --- a/plugins/filter/list_to_dict.yml +++ b/plugins/filter/list_to_dict.yml @@ -30,7 +30,6 @@ DOCUMENTATION: - Felix Fontein (@felixfontein) EXAMPLES: | - --- - name: Convert a list to a dictionary ansible.builtin.set_fact: dictionary: "{{ ['foo=bar', 'comment=foo is bar'] | community.routeros.list_to_dict }}" diff --git a/plugins/filter/quote_argument.yml b/plugins/filter/quote_argument.yml index 477a15e..26a1f04 100644 --- a/plugins/filter/quote_argument.yml +++ b/plugins/filter/quote_argument.yml @@ -19,11 +19,9 @@ DOCUMENTATION: - Felix Fontein (@felixfontein) EXAMPLES: | - --- - name: Quote a RouterOS CLI command argument ansible.builtin.set_fact: - quoted: >- - {{ 'comment=this is a "comment"' | community.routeros.quote_argument }} + quoted: "{{ 'comment=this is a "comment"' | community.routeros.quote_argument }}" # Should result in 'comment="this is a \"comment\""' RETURN: diff --git a/plugins/filter/quote_argument_value.yml b/plugins/filter/quote_argument_value.yml index b4da246..839895b 100644 --- a/plugins/filter/quote_argument_value.yml +++ b/plugins/filter/quote_argument_value.yml @@ -19,11 +19,9 @@ DOCUMENTATION: - Felix Fontein (@felixfontein) EXAMPLES: | - --- - name: Quote a RouterOS CLI command argument's value ansible.builtin.set_fact: - quoted: >- - {{ 'this is a "comment"' | community.routeros.quote_argument_value }} + quoted: "{{ 'this is a "comment"' | community.routeros.quote_argument_value }}" # Should result in '"this is a \"comment\""' RETURN: diff --git a/plugins/filter/split.yml b/plugins/filter/split.yml index cb4ba88..5fc4b30 100644 --- a/plugins/filter/split.yml +++ b/plugins/filter/split.yml @@ -19,11 +19,9 @@ DOCUMENTATION: - Felix Fontein (@felixfontein) EXAMPLES: | - --- - name: Split command into list of arguments ansible.builtin.set_fact: - argument_list: >- - {{ 'foo=bar comment="foo is bar" baz' | community.routeros.split }} + argument_list: "{{ 'foo=bar comment="foo is bar" baz' | community.routeros.split }}" # Should result in ['foo=bar', 'comment=foo is bar', 'baz'] RETURN: diff --git a/plugins/module_utils/_api_data.py b/plugins/module_utils/_api_data.py index 2b44b8b..847f5b1 100644 --- a/plugins/module_utils/_api_data.py +++ b/plugins/module_utils/_api_data.py @@ -226,25 +226,6 @@ def join_path(path): # 3. All bold attributes go into the `primary_keys` list -- this is not always true! PATHS = { - ('interface', '6to4'): APIData( - unversioned=VersionedAPIData( - fully_understood=True, - primary_keys=('name', ), - fields={ - 'clamp-tcp-mss': KeyInfo(default=True), - 'comment': KeyInfo(can_disable=True, remove_value=''), - 'disabled': KeyInfo(default=False), - 'dont-fragment': KeyInfo(default=False), - 'dscp': KeyInfo(default='inherit'), - 'ipsec-secret': KeyInfo(can_disable=True), - 'keepalive': KeyInfo(default='10s,10', can_disable=True), - 'local-address': KeyInfo(default='0.0.0.0'), - 'mtu': KeyInfo(default='auto'), - 'name': KeyInfo(), - 'remote-address': KeyInfo(required=True), - } - ), - ), ('interface', 'bonding'): APIData( unversioned=VersionedAPIData( fully_understood=True, @@ -281,7 +262,6 @@ PATHS = { versioned_fields=[ ([('7.0', '<')], 'ingress-filtering', KeyInfo(default=False)), ([('7.0', '>=')], 'ingress-filtering', KeyInfo(default=True)), - ([('7.13', '>=')], 'port-cost-mode', KeyInfo(default='long')), ([('7.16', '>=')], 'forward-reserved-addresses', KeyInfo(default=False)), ([('7.16', '>=')], 'max-learned-entries', KeyInfo(default='auto')), ], @@ -652,22 +632,13 @@ PATHS = { ), ('ip', 'ipsec', 'mode-config'): APIData( unversioned=VersionedAPIData( - fully_understood=True, - primary_keys=('name', ), - versioned_fields=[ - ([('6.43', '>=')], 'responder', KeyInfo(default=False)), - ([('6.44', '>=')], 'address', KeyInfo(can_disable=True, remove_value='0.0.0.0')), - ], + unknown_mechanism=True, + # primary_keys=('default', ), fields={ - 'address-pool': KeyInfo(can_disable=True, remove_value='none'), - 'address-prefix-length': KeyInfo(), - 'comment': KeyInfo(can_disable=True, remove_value=''), + 'default': KeyInfo(), 'name': KeyInfo(), - 'split-dns': KeyInfo(can_disable=True, remove_value=''), - 'split-include': KeyInfo(can_disable=True, remove_value=''), - 'src-address-list': KeyInfo(can_disable=True, remove_value=''), - 'static-dns': KeyInfo(can_disable=True, remove_value=''), - 'system-dns': KeyInfo(default=False), + 'responder': KeyInfo(), + 'use-responder-dns': KeyInfo(), }, ), ), @@ -737,9 +708,7 @@ PATHS = { fully_understood=True, primary_keys=('name', ), fields={ - 'comment': KeyInfo(), 'name': KeyInfo(), - 'next-pool': KeyInfo(), 'ranges': KeyInfo(), }, ), @@ -924,20 +893,6 @@ PATHS = { )), ], ), - ('routing', 'filter', 'community-list'): APIData( - versioned=[ - ('7', '>=', VersionedAPIData( - fully_understood=True, - fields={ - 'list': KeyInfo(required=True), - 'comment': KeyInfo(can_disable=True, remove_value=''), - 'disabled': KeyInfo(can_disable=True), - 'communities': KeyInfo(can_disable=True), - 'regexp': KeyInfo(can_disable=True), - }, - )), - ], - ), ('routing', 'ospf', 'instance'): APIData( unversioned=VersionedAPIData( fully_understood=True, @@ -1553,19 +1508,13 @@ PATHS = { fully_understood=True, versioned_fields=[ ([('7.16', '>=')], 'multipath-hash-policy', KeyInfo(default='l3')), - ([('7.17', '>=')], 'disable-link-local-address', KeyInfo(default=False)), - ([('7.17', '>=')], 'stale-neighbor-timeout', KeyInfo(default=60)), - ([('7.18', '>=')], 'allow-fast-path', KeyInfo(default=True)), - ([('7.18', '<')], 'max-neighbor-entries', KeyInfo(default=8192)), - ([('7.18', '>=')], 'min-neighbor-entries', KeyInfo()), - ([('7.18', '>=')], 'soft-max-neighbor-entries', KeyInfo()), - ([('7.18', '>=')], 'max-neighbor-entries', KeyInfo()), ], fields={ 'accept-redirects': KeyInfo(default='yes-if-forwarding-disabled'), 'accept-router-advertisements': KeyInfo(default='yes-if-forwarding-disabled'), 'disable-ipv6': KeyInfo(default=False), 'forward': KeyInfo(default=True), + 'max-neighbor-entries': KeyInfo(default=8192), }, ), ), @@ -1664,46 +1613,23 @@ PATHS = { ), ), ('interface', 'ovpn-server', 'server'): APIData( - versioned=[ - ('7.17', '>=', VersionedAPIData( - fully_understood=True, - fields={ - 'auth': KeyInfo(), - 'cipher': KeyInfo(), - 'default-profile': KeyInfo(default='default'), - 'enabled': KeyInfo(default=False), - 'keepalive-timeout': KeyInfo(default=60), - 'mac-address': KeyInfo(), - 'max-mtu': KeyInfo(default=1500), - 'mode': KeyInfo(default='ip'), - 'name': KeyInfo(default=''), - 'netmask': KeyInfo(default=24), - 'port': KeyInfo(default=1194), - 'protocol': KeyInfo(default='tcp'), - 'require-client-certificate': KeyInfo(default=False), - 'vrf': KeyInfo(default='main'), - }, - )), - ('7.17', '<', VersionedAPIData( - single_value=True, - fully_understood=True, - fields={ - 'auth': KeyInfo(), - 'cipher': KeyInfo(), - 'default-profile': KeyInfo(default='default'), - 'enabled': KeyInfo(default=False), - 'keepalive-timeout': KeyInfo(default=60), - 'mac-address': KeyInfo(), - 'max-mtu': KeyInfo(default=1500), - 'mode': KeyInfo(default='ip'), - 'name': KeyInfo(default=''), - 'netmask': KeyInfo(default=24), - 'port': KeyInfo(default=1194), - 'protocol': KeyInfo(default='tcp'), - 'require-client-certificate': KeyInfo(default=False), - }, - )) - ] + unversioned=VersionedAPIData( + single_value=True, + fully_understood=True, + fields={ + 'auth': KeyInfo(), + 'cipher': KeyInfo(), + 'default-profile': KeyInfo(default='default'), + 'enabled': KeyInfo(default=False), + 'keepalive-timeout': KeyInfo(default=60), + 'mac-address': KeyInfo(), + 'max-mtu': KeyInfo(default=1500), + 'mode': KeyInfo(default='ip'), + 'netmask': KeyInfo(default=24), + 'port': KeyInfo(default=1194), + 'require-client-certificate': KeyInfo(default=False), + }, + ), ), ('interface', 'pppoe-server', 'server'): APIData( unversioned=VersionedAPIData( @@ -2076,6 +2002,7 @@ PATHS = { versioned=[ ('7.13', '>=', VersionedAPIData( fully_understood=True, + primary_keys=('action', ), fields={ 'action': KeyInfo(default='none'), 'address-ranges': KeyInfo(can_disable=True), @@ -2480,8 +2407,7 @@ PATHS = { }, versioned_fields=[ ([('7.15', '>=')], 'name', KeyInfo()), - ([('7.15', '>='), ('7.17', '<')], 'is-responder', KeyInfo()), - ([('7.17', '>=')], 'responder', KeyInfo()), + ([('7.15', '>=')], 'is-responder', KeyInfo()), ], ), ), @@ -2603,30 +2529,6 @@ PATHS = { }, ), ), - ('interface', 'wireless', 'access-list'): APIData( - unversioned=VersionedAPIData( - fully_understood=True, - fields={ - 'allow-signal-out-of-range': KeyInfo(default='10s'), - 'ap-tx-limit': KeyInfo(default=0), - 'authentication': KeyInfo(default=True), - 'client-tx-limit': KeyInfo(default=0), - 'comment': KeyInfo(can_disable=True, remove_value=''), - 'disabled': KeyInfo(default=False), - 'forwarding': KeyInfo(default=True), - 'interface': KeyInfo(default='any'), - 'mac-address': KeyInfo(default='00:00:00:00:00:00'), - 'management-protection-key': KeyInfo(default=''), - 'private-algo': KeyInfo(default='none'), - 'private-key': KeyInfo(default=''), - 'private-pre-shared-key': KeyInfo(default=''), - 'signal-range': KeyInfo(default='-120..120'), - 'time': KeyInfo(), - 'vlan-id': KeyInfo(default=1), - 'vlan-mode': KeyInfo(default='default'), - }, - ), - ), ('interface', 'wireless', 'cap'): APIData( unversioned=VersionedAPIData( single_value=True, @@ -2645,41 +2547,6 @@ PATHS = { }, ), ), - ('interface', 'wireless', 'connect-list'): APIData( - unversioned=VersionedAPIData( - fully_understood=True, - fields={ - '3gpp': KeyInfo(default=''), - 'allow-signal-out-of-range': KeyInfo(default='10s'), - 'area-prefix': KeyInfo(default=''), - 'comment': KeyInfo(can_disable=True, remove_value=''), - 'connect': KeyInfo(default=True), - 'disabled': KeyInfo(default=False), - 'interface': KeyInfo(required=True), - 'interworking': KeyInfo(default='any'), - 'iw-asra': KeyInfo(default='any'), - 'iw-authentication-types': KeyInfo(), - 'iw-connection-capabilities': KeyInfo(), - 'iw-esr': KeyInfo(default='any'), - 'iw-hessid': KeyInfo(default='00:00:00:00:00:00'), - 'iw-hotspot20': KeyInfo(default='any'), - 'iw-hotspot20-dgaf': KeyInfo(default='any'), - 'iw-internet': KeyInfo(default='any'), - 'iw-ipv4-availability': KeyInfo(default='any'), - 'iw-ipv6-availability': KeyInfo(default='any'), - 'iw-network-type': KeyInfo(default='wildcard'), - 'iw-realms': KeyInfo(), - 'iw-roaming-ois': KeyInfo(default=''), - 'iw-uesa': KeyInfo(default='any'), - 'iw-venue': KeyInfo(default='any'), - 'mac-address': KeyInfo(default='00:00:00:00:00:00'), - 'security-profile': KeyInfo(default='none'), - 'signal-range': KeyInfo(default='-120..120'), - 'ssid': KeyInfo(default=''), - 'wireless-protocol': KeyInfo(default='any'), - }, - ), - ), ('interface', 'wireless', 'security-profiles'): APIData( unversioned=VersionedAPIData( fully_understood=True, @@ -2817,11 +2684,8 @@ PATHS = { unversioned=VersionedAPIData( single_value=True, fully_understood=True, - versioned_fields=[ - ([('7.17', '<')], 'ddns-enabled', KeyInfo(default=False)), - ([('7.17', '>=')], 'ddns-enabled', KeyInfo(default='auto')), - ], fields={ + 'ddns-enabled': KeyInfo(default=False), 'ddns-update-interval': KeyInfo(default='none'), 'update-time': KeyInfo(default=True), }, @@ -3007,7 +2871,6 @@ PATHS = { ([('7.8', '>=')], 'doh-max-concurrent-queries', KeyInfo(default=50)), ([('7.8', '>=')], 'doh-max-server-connections', KeyInfo(default=5)), ([('7.8', '>=')], 'doh-timeout', KeyInfo(default='5s')), - ([('7.16', '>=')], 'mdns-repeat-ifaces', KeyInfo()), ], fields={ 'allow-remote-requests': KeyInfo(), @@ -3040,22 +2903,6 @@ PATHS = { )), ], ), - ('ip', 'dns', 'forwarders'): APIData( - versioned=[ - ('7.17', '>=', VersionedAPIData( - fully_understood=True, - required_one_of=[['dns-servers', 'doh-servers']], - fields={ - 'comment': KeyInfo(can_disable=True, remove_value=''), - 'disabled': KeyInfo(default=False), - 'dns-servers': KeyInfo(default=''), - 'doh-servers': KeyInfo(default=''), - 'name': KeyInfo(required=True), - 'verify-doh-cert': KeyInfo(default=True), - }, - )), - ], - ), ('ip', 'dns', 'static'): APIData( unversioned=VersionedAPIData( fully_understood=True, @@ -3137,8 +2984,8 @@ PATHS = { 'jump-target': KeyInfo(can_disable=True), 'layer7-protocol': KeyInfo(can_disable=True), 'limit': KeyInfo(can_disable=True), - 'log': KeyInfo(default=False), - 'log-prefix': KeyInfo(default=''), + 'log': KeyInfo(can_disable=True), + 'log-prefix': KeyInfo(can_disable=True), 'nth': KeyInfo(can_disable=True), 'out-bridge-port': KeyInfo(can_disable=True), 'out-bridge-port-list': KeyInfo(can_disable=True), @@ -3174,10 +3021,6 @@ PATHS = { unversioned=VersionedAPIData( fully_understood=True, stratify_keys=('chain', ), - versioned_fields=[ - ([('7.19', '<')], 'passthrough', KeyInfo(can_disable=True)), - ([('7.19', '>=')], 'passthrough', KeyInfo(default=True)), - ], fields={ 'action': KeyInfo(), 'address-list': KeyInfo(can_disable=True), @@ -3212,8 +3055,8 @@ PATHS = { 'jump-target': KeyInfo(can_disable=True), 'layer7-protocol': KeyInfo(can_disable=True), 'limit': KeyInfo(can_disable=True), - 'log': KeyInfo(default=False), - 'log-prefix': KeyInfo(default=''), + 'log': KeyInfo(can_disable=True), + 'log-prefix': KeyInfo(can_disable=True), 'new-connection-mark': KeyInfo(can_disable=True), 'new-dscp': KeyInfo(can_disable=True), 'new-mss': KeyInfo(can_disable=True), @@ -3229,6 +3072,7 @@ PATHS = { 'p2p': KeyInfo(can_disable=True), 'packet-mark': KeyInfo(can_disable=True), 'packet-size': KeyInfo(can_disable=True), + 'passthrough': KeyInfo(can_disable=True), 'per-connection-classifier': KeyInfo(can_disable=True), 'port': KeyInfo(can_disable=True), 'priority': KeyInfo(can_disable=True), @@ -3291,8 +3135,8 @@ PATHS = { 'jump-target': KeyInfo(can_disable=True), 'layer7-protocol': KeyInfo(can_disable=True), 'limit': KeyInfo(can_disable=True), - 'log': KeyInfo(default=False), - 'log-prefix': KeyInfo(default=''), + 'log': KeyInfo(can_disable=True), + 'log-prefix': KeyInfo(can_disable=True), 'nth': KeyInfo(can_disable=True), 'out-bridge-port': KeyInfo(can_disable=True), 'out-bridge-port-list': KeyInfo(can_disable=True), @@ -3354,8 +3198,8 @@ PATHS = { 'ipv4-options': KeyInfo(can_disable=True), 'jump-target': KeyInfo(can_disable=True), 'limit': KeyInfo(can_disable=True), - 'log': KeyInfo(default=False), - 'log-prefix': KeyInfo(default=''), + 'log': KeyInfo(can_disable=True), + 'log-prefix': KeyInfo(can_disable=True), 'nth': KeyInfo(can_disable=True), 'out-bridge-port': KeyInfo(can_disable=True), 'out-bridge-port-list': KeyInfo(can_disable=True), @@ -3625,14 +3469,6 @@ PATHS = { 'request': KeyInfo(), 'use-peer-dns': KeyInfo(default=True), }, - versioned_fields=[ - # Mikrotik does not provide exact version in official changelogs. - # The 7.15 version is the earliest, found option in router config backups: - ([('7.15', '>=')], 'script', KeyInfo(default='')), - ([('7.15', '>=')], 'custom-duid', KeyInfo(default='')), - ([('7.15', '>=')], 'use-interface-duid', KeyInfo(default=False)), - ([('7.15', '>=')], 'validate-server-duid', KeyInfo(default=True)), - ], ), ), ('ipv6', 'dhcp-server'): APIData( @@ -4166,9 +4002,6 @@ PATHS = { 'src-address': KeyInfo(default='0.0.0.0'), 'timeout': KeyInfo(default='300ms'), }, - versioned_fields=[ - ([('7.15', '>=')], 'require-message-auth', KeyInfo(default='yes-for-request-resp')), - ], ), ), ('radius', 'incoming'): APIData( @@ -4219,28 +4052,6 @@ PATHS = { }, ), ), - ('routing', 'bfd', 'configuration'): APIData( - versioned=[ - ('7.11', '>=', VersionedAPIData( - fully_understood=True, - fields={ - 'address-list': KeyInfo(), - 'addresses': KeyInfo(), - 'comment': KeyInfo(can_disable=True, remove_value=''), - 'copy-from': KeyInfo(), - 'disabled': KeyInfo(default=False), - 'forbid-bfd': KeyInfo(), - 'interfaces': KeyInfo(), - 'min-echo-rx': KeyInfo(), - 'min-rx': KeyInfo(), - 'min-tx': KeyInfo(), - 'multiplier': KeyInfo(), - 'place-before': KeyInfo(), - 'vrf': KeyInfo(), - }, - )) - ], - ), ('routing', 'bfd', 'interface'): APIData( unversioned=VersionedAPIData( unknown_mechanism=True, @@ -4957,18 +4768,6 @@ PATHS = { }, ), ), - ('interface', 'ethernet', 'switch', 'port-isolation'): APIData( - versioned=[ - ('6.43', '>=', VersionedAPIData( - primary_keys=('name', ), - fully_understood=True, - fields={ - 'forwarding-override': KeyInfo(), - 'name': KeyInfo(), - }, - )), - ], - ), ('ip', 'dhcp-client', 'option'): APIData( unversioned=VersionedAPIData( fixed_entries=True, @@ -5059,13 +4858,10 @@ PATHS = { ('routing', 'bgp', 'connection'): APIData( unversioned=VersionedAPIData( fully_understood=True, - versioned_fields=[ - ([('7.19', '<')], 'address-families', KeyInfo()), - ([('7.19', '>=')], 'afi', KeyInfo()), - ], fields={ 'as': KeyInfo(), 'add-path-out': KeyInfo(), + 'address-families': KeyInfo(), 'cisco-vpls-nlri-len-fmt': KeyInfo(), 'cluster-id': KeyInfo(), 'comment': KeyInfo(), @@ -5244,11 +5040,6 @@ PATHS = { unversioned=VersionedAPIData( fully_understood=True, primary_keys=('name',), - versioned_fields=[ - ([('7.18', '>=')], 'remote-log-format', KeyInfo(default='default')), - ([('7.18', '>=')], 'remote-protocol', KeyInfo(default='udp')), - ([('7.18', '>=')], 'cef-event-delimiter', KeyInfo(default='\r\n')), - ], fields={ 'bsd-syslog': KeyInfo(default=False), 'comment': KeyInfo(can_disable=True, remove_value=''), @@ -5351,7 +5142,7 @@ PATHS = { 'protocol': KeyInfo(default='all'), 'src-address': KeyInfo(), 'src-port': KeyInfo(default='any'), - # The template field ca not really be changed once the item is + # The template field can't really be changed once the item is # created. This config captures the behavior best as it can # i.e. template=yes is shown, template=no is hidden. 'template': KeyInfo(can_disable=True, remove_value=False), diff --git a/plugins/module_utils/api.py b/plugins/module_utils/api.py index aa3aa2e..6c276d7 100644 --- a/plugins/module_utils/api.py +++ b/plugins/module_utils/api.py @@ -77,7 +77,7 @@ def _ros_api_connect(module, username, password, host, port, use_tls, force_no_c elif not validate_cert_hostname: ctx.check_hostname = False else: - # Since librouteros does not pass server_hostname, + # Since librouteros doesn't pass server_hostname, # we have to do this ourselves: def wrap_context(*args, **kwargs): kwargs.pop('server_hostname', None) diff --git a/plugins/modules/api.py b/plugins/modules/api.py index d3ec089..4857a3c 100644 --- a/plugins/modules/api.py +++ b/plugins/modules/api.py @@ -8,17 +8,19 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -DOCUMENTATION = r""" +DOCUMENTATION = ''' +--- module: api author: "Nikolay Dachev (@NikolayDachev)" short_description: Ansible module for RouterOS API description: - Ansible module for RouterOS API with the Python C(librouteros) library. - - This module can add, remove, update, query, and execute arbitrary command in RouterOS through the API. + - This module can add, remove, update, query and execute arbitrary command in RouterOS via API. notes: - O(add), O(remove), O(update), O(cmd), and O(query) are mutually exclusive. - - Use the M(community.routeros.api_modify) and M(community.routeros.api_find_and_modify) modules for more specific modifications, - and the M(community.routeros.api_info) module for a more controlled way of returning all entries for a path. + - Use the M(community.routeros.api_modify) and M(community.routeros.api_find_and_modify) modules + for more specific modifications, and the M(community.routeros.api_info) module for a more controlled + way of returning all entries for a path. extends_documentation_fragment: - community.routeros.api - community.routeros.attributes @@ -33,15 +35,11 @@ attributes: platforms: RouterOS action_group: version_added: 2.1.0 - idempotent: - support: N/A - details: - - Whether the executed command is idempotent depends on the operation performed. options: path: description: - Main path for all other arguments. - - If other arguments are not set, the module will return all items in selected path. + - If other arguments are not set, api will return all items in selected path. - Example V(ip address). Equivalent of RouterOS CLI C(/ip address print). required: true type: str @@ -61,21 +59,20 @@ options: update: description: - Update config/value in RouterOS by '.id' in selected path. - - Example V(.id=*03 address=1.1.1.3/32) and path V(ip address) will replace the existing IP address with C(.id=*03). + - Example V(.id=*03 address=1.1.1.3/32) and path V(ip address) will replace existing ip address with C(.id=*03). - Equivalent in RouterOS CLI C(/ip address set address=1.1.1.3/32 numbers=1). - Note C(number) in RouterOS CLI is different from C(.id). type: str query: description: - - Query given path for selected query attributes from RouterOS API. + - Query given path for selected query attributes from RouterOS aip. - WHERE is key word which extend query. WHERE format is key operator value - with spaces. - WHERE valid operators are V(==) or V(eq), V(!=) or V(not), V(>) or V(more), V(<) or V(less). - - Example path V(ip address) and query V(.id address) will return only C(.id) and C(address) for all items in V(ip address) - path. - - Example path V(ip address) and query V(.id address WHERE address == 1.1.1.3/32). will return only C(.id) and C(address) - for items in V(ip address) path, where address is eq to 1.1.1.3/32. - - Example path V(interface) and query V(mtu name WHERE mut > 1400) will return only interfaces C(mtu,name) where mtu - is bigger than 1400. + - Example path V(ip address) and query V(.id address) will return only C(.id) and C(address) for all items in V(ip address) path. + - Example path V(ip address) and query V(.id address WHERE address == 1.1.1.3/32). + will return only C(.id) and C(address) for items in V(ip address) path, where address is eq to 1.1.1.3/32. + - Example path V(interface) and query V(mtu name WHERE mut > 1400) will + return only interfaces C(mtu,name) where mtu is bigger than 1400. - Equivalent in RouterOS CLI C(/interface print where mtu > 1400). type: str extended_query: @@ -94,8 +91,7 @@ options: where: description: - Allows to restrict the objects returned. - - The conditions here must all match. An O(extended_query.where[].or) condition needs at least one of its conditions - to match. + - The conditions here must all match. An O(extended_query.where[].or) condition needs at least one of its conditions to match. type: list elements: dict suboptions: @@ -109,8 +105,7 @@ options: description: - The operator to use for matching. - For equality use V(==) or V(eq). For less use V(<) or V(less). For more use V(>) or V(more). - - Use V(in) to check whether the value is part of a list. In that case, O(extended_query.where[].value) must - be a list. + - Use V(in) to check whether the value is part of a list. In that case, O(extended_query.where[].value) must be a list. - Either O(extended_query.where[].or) or all of O(extended_query.where[].attribute), O(extended_query.where[].is), and O(extended_query.where[].value) have to be specified. type: str @@ -138,8 +133,7 @@ options: description: - The operator to use for matching. - For equality use V(==) or V(eq). For less use V(<) or V(less). For more use V(>) or V(more). - - Use V(in) to check whether the value is part of a list. In that case, O(extended_query.where[].or[].value) - must be a list. + - Use V(in) to check whether the value is part of a list. In that case, O(extended_query.where[].or[].value) must be a list. type: str choices: ["==", "!=", ">", "<", "in", "eq", "not", "more", "less"] required: true @@ -156,15 +150,14 @@ options: type: str seealso: - ref: ansible_collections.community.routeros.docsite.quoting - description: How to quote and unquote commands and arguments. + description: How to quote and unquote commands and arguments - module: community.routeros.api_facts - module: community.routeros.api_find_and_modify - module: community.routeros.api_info - module: community.routeros.api_modify -""" +''' -EXAMPLES = r""" ---- +EXAMPLES = ''' - name: Get example - ip address print community.routeros.api: hostname: "{{ hostname }}" @@ -223,8 +216,8 @@ EXAMPLES = r""" - attribute: "network" is: "in" value: - - "10.20.36.0" - - "192.168.255.0" + - "10.20.36.0" + - "192.168.255.0" register: extended_queryout - name: Dump "Extended query example" output @@ -238,9 +231,9 @@ EXAMPLES = r""" username: "{{ username }}" path: "ip address" update: >- - .id=*14 - address=192.168.255.20/24 - comment={{ 'Update 192.168.255.10/24 to 192.168.255.20/24 on ether2' | community.routeros.quote_argument_value }} + .id=*14 + address=192.168.255.20/24 + comment={{ 'Update 192.168.255.10/24 to 192.168.255.20/24 on ether2' | community.routeros.quote_argument_value }} - name: Remove example - ether2 ip 192.168.255.20/24 with ".id = *14" community.routeros.api: @@ -262,17 +255,18 @@ EXAMPLES = r""" - name: Dump "Arbitrary command example" output ansible.builtin.debug: msg: '{{ arbitraryout }}' -""" +''' -RETURN = r""" +RETURN = ''' +--- message: - description: All outputs are in list with dictionary elements returned from RouterOS API. - sample: - - address: 1.2.3.4 - - address: 2.3.4.5 - type: list - returned: always -""" + description: All outputs are in list with dictionary elements returned from RouterOS api. + sample: + - address: 1.2.3.4 + - address: 2.3.4.5 + type: list + returned: always +''' from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.common.text.converters import to_native diff --git a/plugins/modules/api_facts.py b/plugins/modules/api_facts.py index 09c0fbb..296621e 100644 --- a/plugins/modules/api_facts.py +++ b/plugins/modules/api_facts.py @@ -9,27 +9,29 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -DOCUMENTATION = r""" +DOCUMENTATION = ''' +--- module: api_facts author: - - "Egor Zaitsev (@heuels)" - - "Nikolay Dachev (@NikolayDachev)" - - "Felix Fontein (@felixfontein)" + - "Egor Zaitsev (@heuels)" + - "Nikolay Dachev (@NikolayDachev)" + - "Felix Fontein (@felixfontein)" version_added: 2.1.0 short_description: Collect facts from remote devices running MikroTik RouterOS using the API description: - - Collects a base set of device facts from a remote device that is running RouterOS. This module prepends all of the base - network fact keys with C(ansible_net_). The facts module will always collect a base set of facts from the device + - Collects a base set of device facts from a remote device that + is running RouterOS. This module prepends all of the + base network fact keys with C(ansible_net_). The facts + module will always collect a base set of facts from the device and can enable or disable collection of additional facts. - - As opposed to the M(community.routeros.facts) module, it uses the RouterOS API, similar to the M(community.routeros.api) - module. + - As opposed to the M(community.routeros.facts) module, it uses the + RouterOS API, similar to the M(community.routeros.api) module. extends_documentation_fragment: - community.routeros.api - community.routeros.attributes - community.routeros.attributes.actiongroup_api - community.routeros.attributes.facts - community.routeros.attributes.facts_module - - community.routeros.attributes.idempotent_not_modify_state attributes: platform: support: full @@ -37,10 +39,12 @@ attributes: options: gather_subset: description: - - When supplied, this argument will restrict the facts collected to a given subset. Possible values for this argument - include V(all), V(hardware), V(interfaces), and V(routing). - - Can specify a list of values to include a larger subset. Values can also be used with an initial V(!) to specify that - a specific subset should not be collected. + - When supplied, this argument will restrict the facts collected + to a given subset. Possible values for this argument include + V(all), V(hardware), V(interfaces), and V(routing). + - Can specify a list of values to include a larger subset. + Values can also be used with an initial V(!) to specify that a + specific subset should not be collected. required: false default: - all @@ -52,10 +56,9 @@ seealso: - module: community.routeros.api_find_and_modify - module: community.routeros.api_info - module: community.routeros.api_modify -""" +''' -EXAMPLES = r""" ---- +EXAMPLES = """ - name: Collect all facts from the device community.routeros.api_facts: hostname: 192.168.88.1 @@ -72,7 +75,7 @@ EXAMPLES = r""" - "!hardware" """ -RETURN = r""" +RETURN = """ ansible_facts: description: "Dictionary of IP geolocation facts for a host's IP address." returned: always @@ -419,6 +422,8 @@ FACT_SUBSETS = dict( VALID_SUBSETS = frozenset(FACT_SUBSETS.keys()) +warnings = [] + def main(): argument_spec = dict( @@ -483,7 +488,7 @@ def main(): key = 'ansible_net_%s' % key ansible_facts[key] = value - module.exit_json(ansible_facts=ansible_facts) + module.exit_json(ansible_facts=ansible_facts, warnings=warnings) if __name__ == '__main__': diff --git a/plugins/modules/api_find_and_modify.py b/plugins/modules/api_find_and_modify.py index acdd125..176c943 100644 --- a/plugins/modules/api_find_and_modify.py +++ b/plugins/modules/api_find_and_modify.py @@ -8,7 +8,8 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -DOCUMENTATION = r""" +DOCUMENTATION = ''' +--- module: api_find_and_modify author: - "Felix Fontein (@felixfontein)" @@ -16,13 +17,13 @@ short_description: Find and modify information using the API version_added: 2.1.0 description: - Allows to find entries for a path by conditions and modify the values of these entries. - - Use the M(community.routeros.api_find_and_modify) module to set all entries of a path to specific values, or change multiple - entries in different ways in one step. + - Use the M(community.routeros.api_find_and_modify) module to set all entries of a path to specific values, + or change multiple entries in different ways in one step. notes: - - "If you want to change values based on their old values (like change all comments 'foo' to 'bar') and make sure that there - are at least N such values, you can use O(require_matches_min=N) together with O(allow_no_matches=true). This will make - the module fail if there are less than N such entries, but not if there is no match. The latter case is needed for idempotency - of the task: once the values have been changed, there should be no further match." + - "If you want to change values based on their old values (like change all comments 'foo' to 'bar') and make sure that + there are at least N such values, you can use O(require_matches_min=N) together with O(allow_no_matches=true). + This will make the module fail if there are less than N such entries, but not if there is no match. The latter case + is needed for idempotency of the task: once the values have been changed, there should be no further match." extends_documentation_fragment: - community.routeros.api - community.routeros.attributes @@ -35,8 +36,6 @@ attributes: platform: support: full platforms: RouterOS - idempotent: - support: full options: path: description: @@ -75,30 +74,14 @@ options: - Whether to allow that no match is found. - If not specified, this value is induced from whether O(require_matches_min) is 0 or larger. type: bool - ignore_dynamic: - description: - - Whether to ignore dynamic entries. - - By default, they are considered. If set to V(true), they are not considered. - - It is generally recommended to set this to V(true) unless when you really need to modify dynamic entries. - type: bool - default: false - version_added: 3.7.0 - ignore_builtin: - description: - - Whether to ignore builtin entries. - - By default, they are considered. If set to V(true), they are not considered. - - It is generally recommended to set this to V(true) unless when you really need to modify builtin entries. - type: bool - default: false - version_added: 3.7.0 seealso: - module: community.routeros.api - module: community.routeros.api_facts - module: community.routeros.api_modify - module: community.routeros.api_info -""" +''' -EXAMPLES = r""" +EXAMPLES = ''' --- - name: Rename bridge from 'bridge' to 'my-bridge' community.routeros.api_find_and_modify: @@ -110,10 +93,6 @@ EXAMPLES = r""" name: bridge values: name: my-bridge - # Always ignore dynamic and builtin entries - # (not relevant for this path, but generally recommended) - ignore_dynamic: true - ignore_builtin: true - name: Change IP address to 192.168.1.1 for interface bridge - assuming there is only one community.routeros.api_find_and_modify: @@ -129,58 +108,55 @@ EXAMPLES = r""" # exactly one is configured. require_matches_min: 1 require_matches_max: 1 - # Always ignore dynamic and builtin entries - # (not relevant for this path, but generally recommended) - ignore_dynamic: true - ignore_builtin: true -""" +''' -RETURN = r""" +RETURN = ''' +--- old_data: - description: - - A list of all elements for the current path before a change was made. - sample: - - '.id': '*1' - actual-interface: bridge - address: "192.168.88.1/24" - comment: defconf - disabled: false - dynamic: false - interface: bridge - invalid: false - network: 192.168.88.0 - type: list - elements: dict - returned: success + description: + - A list of all elements for the current path before a change was made. + sample: + - '.id': '*1' + actual-interface: bridge + address: "192.168.88.1/24" + comment: defconf + disabled: false + dynamic: false + interface: bridge + invalid: false + network: 192.168.88.0 + type: list + elements: dict + returned: success new_data: - description: - - A list of all elements for the current path after a change was made. - sample: - - '.id': '*1' - actual-interface: bridge - address: "192.168.1.1/24" - comment: awesome - disabled: false - dynamic: false - interface: bridge - invalid: false - network: 192.168.1.0 - type: list - elements: dict - returned: success + description: + - A list of all elements for the current path after a change was made. + sample: + - '.id': '*1' + actual-interface: bridge + address: "192.168.1.1/24" + comment: awesome + disabled: false + dynamic: false + interface: bridge + invalid: false + network: 192.168.1.0 + type: list + elements: dict + returned: success match_count: - description: - - The number of entries that matched the criteria in O(find). - sample: 1 - type: int - returned: success + description: + - The number of entries that matched the criteria in O(find). + sample: 1 + type: int + returned: success modify__count: - description: - - The number of entries that were modified. - sample: 1 - type: int - returned: success -""" + description: + - The number of entries that were modified. + sample: 1 + type: int + returned: success +''' from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.common.text.converters import to_native @@ -209,17 +185,6 @@ def compose_api_path(api, path): return api_path -def filter_entries(entries, ignore_dynamic=False, ignore_builtin=False): - result = [] - for entry in entries: - if ignore_dynamic and entry.get('dynamic', False): - continue - if ignore_builtin and entry.get('builtin', False): - continue - result.append(entry) - return result - - DISABLED_MEANS_EMPTY_STRING = ('comment', ) @@ -231,8 +196,6 @@ def main(): require_matches_min=dict(type='int', default=0), require_matches_max=dict(type='int'), allow_no_matches=dict(type='bool'), - ignore_dynamic=dict(type='bool', default=False), - ignore_builtin=dict(type='bool', default=False), ) module_args.update(api_argument_spec()) @@ -260,9 +223,6 @@ def main(): if key in values: module.fail_json(msg='`values` must not contain both "{key}" and "!{key}"!'.format(key=key)) - ignore_dynamic = module.params['ignore_dynamic'] - ignore_builtin = module.params['ignore_builtin'] - check_has_library(module) api = create_api(module) @@ -270,7 +230,7 @@ def main(): api_path = compose_api_path(api, path) - old_data = filter_entries(list(api_path), ignore_dynamic=ignore_dynamic, ignore_builtin=ignore_builtin) + old_data = list(api_path) new_data = [entry.copy() for entry in old_data] # Find matching entries @@ -339,7 +299,7 @@ def main(): error=to_native(e), ) ) - new_data = filter_entries(list(api_path), ignore_dynamic=ignore_dynamic, ignore_builtin=ignore_builtin) + new_data = list(api_path) # Produce return value more = {} diff --git a/plugins/modules/api_info.py b/plugins/modules/api_info.py index 1eb8a94..829fc17 100644 --- a/plugins/modules/api_info.py +++ b/plugins/modules/api_info.py @@ -8,7 +8,8 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -DOCUMENTATION = r""" +DOCUMENTATION = ''' +--- module: api_info author: - "Felix Fontein (@felixfontein)" @@ -17,18 +18,17 @@ version_added: 2.2.0 description: - Allows to retrieve information for a path using the API. - This can be used to backup a path to restore it with the M(community.routeros.api_modify) module. - - Entries are normalized, dynamic and builtin entries are not returned. Use the O(handle_disabled) and O(hide_defaults) - options to control normalization, the O(include_dynamic) and O(include_builtin) options to also return dynamic resp. builtin - entries, and use O(unfiltered) to return all fields including counters. - - B(Note) that this module is still heavily in development, and only supports B(some) paths. If you want to support new - paths, or think you found problems with existing paths, please first L(create an issue in the community.routeros Issue - Tracker,https://github.com/ansible-collections/community.routeros/issues/). + - Entries are normalized, dynamic and builtin entries are not returned. Use the O(handle_disabled) and + O(hide_defaults) options to control normalization, the O(include_dynamic) and O(include_builtin) options to also return + dynamic resp. builtin entries, and use O(unfiltered) to return all fields including counters. + - B(Note) that this module is still heavily in development, and only supports B(some) paths. + If you want to support new paths, or think you found problems with existing paths, please first + L(create an issue in the community.routeros Issue Tracker,https://github.com/ansible-collections/community.routeros/issues/). extends_documentation_fragment: - community.routeros.api - community.routeros.api.restrict - community.routeros.attributes - community.routeros.attributes.actiongroup_api - - community.routeros.attributes.idempotent_not_modify_state - community.routeros.attributes.info_module attributes: platform: @@ -43,232 +43,224 @@ options: type: str choices: # BEGIN PATH LIST - - caps-man aaa - - caps-man access-list - - caps-man channel - - caps-man configuration - - caps-man datapath - - caps-man manager - - caps-man manager interface - - caps-man provisioning - - caps-man security - - certificate settings - - interface 6to4 - - interface bonding - - interface bridge - - interface bridge mlag - - interface bridge port - - interface bridge port-controller - - interface bridge port-extender - - interface bridge settings - - interface bridge vlan - - interface detect-internet - - interface eoip - - interface ethernet - - interface ethernet poe - - interface ethernet switch - - interface ethernet switch port - - interface ethernet switch port-isolation - - interface gre - - interface gre6 - - interface l2tp-client - - interface l2tp-server server - - interface list - - interface list member - - interface ovpn-client - - interface ovpn-server server - - interface ppp-client - - interface pppoe-client - - interface pppoe-server server - - interface pptp-server server - - interface sstp-server server - - interface vlan - - interface vrrp - - interface wifi - - interface wifi aaa - - interface wifi access-list - - interface wifi cap - - interface wifi capsman - - interface wifi channel - - interface wifi configuration - - interface wifi datapath - - interface wifi interworking - - interface wifi provisioning - - interface wifi security - - interface wifi steering - - interface wifiwave2 - - interface wifiwave2 aaa - - interface wifiwave2 access-list - - interface wifiwave2 cap - - interface wifiwave2 capsman - - interface wifiwave2 channel - - interface wifiwave2 configuration - - interface wifiwave2 datapath - - interface wifiwave2 interworking - - interface wifiwave2 provisioning - - interface wifiwave2 security - - interface wifiwave2 steering - - interface wireguard - - interface wireguard peers - - interface wireless - - interface wireless access-list - - interface wireless align - - interface wireless cap - - interface wireless connect-list - - interface wireless security-profiles - - interface wireless sniffer - - interface wireless snooper - - iot modbus - - ip accounting - - ip accounting web-access - - ip address - - ip arp - - ip cloud - - ip cloud advanced - - ip dhcp-client - - ip dhcp-client option - - ip dhcp-relay - - ip dhcp-server - - ip dhcp-server config - - ip dhcp-server lease - - ip dhcp-server matcher - - ip dhcp-server network - - ip dhcp-server option - - ip dhcp-server option sets - - ip dns - - ip dns adlist - - ip dns forwarders - - ip dns static - - ip firewall address-list - - ip firewall connection tracking - - ip firewall filter - - ip firewall layer7-protocol - - ip firewall mangle - - ip firewall nat - - ip firewall raw - - ip firewall service-port - - ip hotspot service-port - - ip ipsec identity - - ip ipsec mode-config - - ip ipsec peer - - ip ipsec policy - - ip ipsec profile - - ip ipsec proposal - - ip ipsec settings - - ip neighbor discovery-settings - - ip pool - - ip proxy - - ip route - - ip route rule - - ip route vrf - - ip service - - ip settings - - ip smb - - ip socks - - ip ssh - - ip tftp settings - - ip traffic-flow - - ip traffic-flow ipfix - - ip traffic-flow target - - ip upnp - - ip upnp interfaces - - ip vrf - - ipv6 address - - ipv6 dhcp-client - - ipv6 dhcp-server - - ipv6 dhcp-server option - - ipv6 firewall address-list - - ipv6 firewall filter - - ipv6 firewall mangle - - ipv6 firewall nat - - ipv6 firewall raw - - ipv6 nd - - ipv6 nd prefix - - ipv6 nd prefix default - - ipv6 route - - ipv6 settings - - mpls - - mpls interface - - mpls ldp - - mpls ldp accept-filter - - mpls ldp advertise-filter - - mpls ldp interface - - port firmware - - port remote-access - - ppp aaa - - ppp profile - - ppp secret - - queue interface - - queue simple - - queue tree - - queue type - - radius - - radius incoming - - routing bfd configuration - - routing bgp aggregate - - routing bgp connection - - routing bgp instance - - routing bgp network - - routing bgp peer - - routing bgp template - - routing filter - - routing filter community-list - - routing filter num-list - - routing filter rule - - routing filter select-rule - - routing id - - routing igmp-proxy - - routing igmp-proxy interface - - routing mme - - routing ospf area - - routing ospf area range - - routing ospf instance - - routing ospf interface-template - - routing ospf static-neighbor - - routing pimsm instance - - routing pimsm interface-template - - routing rip - - routing ripng - - routing rule - - routing table - - snmp - - snmp community - - system clock - - system clock manual - - system health settings - - system identity - - system leds settings - - system logging - - system logging action - - system note - - system ntp client - - system ntp client servers - - system ntp server - - system package update - - system resource irq rps - - system routerboard settings - - system scheduler - - system script - - system upgrade mirror - - system ups - - system watchdog - - tool bandwidth-server - - tool e-mail - - tool graphing - - tool graphing interface - - tool graphing resource - - tool mac-server - - tool mac-server mac-winbox - - tool mac-server ping - - tool netwatch - - tool romon - - tool sms - - tool sniffer - - tool traffic-generator - - user - - user aaa - - user group - - user settings + - caps-man aaa + - caps-man access-list + - caps-man channel + - caps-man configuration + - caps-man datapath + - caps-man manager + - caps-man manager interface + - caps-man provisioning + - caps-man security + - certificate settings + - interface bonding + - interface bridge + - interface bridge mlag + - interface bridge port + - interface bridge port-controller + - interface bridge port-extender + - interface bridge settings + - interface bridge vlan + - interface detect-internet + - interface eoip + - interface ethernet + - interface ethernet poe + - interface ethernet switch + - interface ethernet switch port + - interface gre + - interface gre6 + - interface l2tp-client + - interface l2tp-server server + - interface list + - interface list member + - interface ovpn-client + - interface ovpn-server server + - interface ppp-client + - interface pppoe-client + - interface pppoe-server server + - interface pptp-server server + - interface sstp-server server + - interface vlan + - interface vrrp + - interface wifi + - interface wifi aaa + - interface wifi access-list + - interface wifi cap + - interface wifi capsman + - interface wifi channel + - interface wifi configuration + - interface wifi datapath + - interface wifi interworking + - interface wifi provisioning + - interface wifi security + - interface wifi steering + - interface wifiwave2 + - interface wifiwave2 aaa + - interface wifiwave2 access-list + - interface wifiwave2 cap + - interface wifiwave2 capsman + - interface wifiwave2 channel + - interface wifiwave2 configuration + - interface wifiwave2 datapath + - interface wifiwave2 interworking + - interface wifiwave2 provisioning + - interface wifiwave2 security + - interface wifiwave2 steering + - interface wireguard + - interface wireguard peers + - interface wireless + - interface wireless align + - interface wireless cap + - interface wireless security-profiles + - interface wireless sniffer + - interface wireless snooper + - iot modbus + - ip accounting + - ip accounting web-access + - ip address + - ip arp + - ip cloud + - ip cloud advanced + - ip dhcp-client + - ip dhcp-client option + - ip dhcp-relay + - ip dhcp-server + - ip dhcp-server config + - ip dhcp-server lease + - ip dhcp-server matcher + - ip dhcp-server network + - ip dhcp-server option + - ip dhcp-server option sets + - ip dns + - ip dns adlist + - ip dns static + - ip firewall address-list + - ip firewall connection tracking + - ip firewall filter + - ip firewall layer7-protocol + - ip firewall mangle + - ip firewall nat + - ip firewall raw + - ip firewall service-port + - ip hotspot service-port + - ip ipsec identity + - ip ipsec peer + - ip ipsec policy + - ip ipsec profile + - ip ipsec proposal + - ip ipsec settings + - ip neighbor discovery-settings + - ip pool + - ip proxy + - ip route + - ip route rule + - ip route vrf + - ip service + - ip settings + - ip smb + - ip socks + - ip ssh + - ip tftp settings + - ip traffic-flow + - ip traffic-flow ipfix + - ip traffic-flow target + - ip upnp + - ip upnp interfaces + - ip vrf + - ipv6 address + - ipv6 dhcp-client + - ipv6 dhcp-server + - ipv6 dhcp-server option + - ipv6 firewall address-list + - ipv6 firewall filter + - ipv6 firewall mangle + - ipv6 firewall nat + - ipv6 firewall raw + - ipv6 nd + - ipv6 nd prefix + - ipv6 nd prefix default + - ipv6 route + - ipv6 settings + - mpls + - mpls interface + - mpls ldp + - mpls ldp accept-filter + - mpls ldp advertise-filter + - mpls ldp interface + - port firmware + - port remote-access + - ppp aaa + - ppp profile + - ppp secret + - queue interface + - queue simple + - queue tree + - queue type + - radius + - radius incoming + - routing bgp aggregate + - routing bgp connection + - routing bgp instance + - routing bgp network + - routing bgp peer + - routing bgp template + - routing filter + - routing filter num-list + - routing filter rule + - routing filter select-rule + - routing id + - routing igmp-proxy + - routing igmp-proxy interface + - routing mme + - routing ospf area + - routing ospf area range + - routing ospf instance + - routing ospf interface-template + - routing ospf static-neighbor + - routing pimsm instance + - routing pimsm interface-template + - routing rip + - routing ripng + - routing rule + - routing table + - snmp + - snmp community + - system clock + - system clock manual + - system health settings + - system identity + - system leds settings + - system logging + - system logging action + - system note + - system ntp client + - system ntp client servers + - system ntp server + - system package update + - system resource irq rps + - system routerboard settings + - system scheduler + - system script + - system upgrade mirror + - system ups + - system watchdog + - tool bandwidth-server + - tool e-mail + - tool graphing + - tool graphing interface + - tool graphing resource + - tool mac-server + - tool mac-server mac-winbox + - tool mac-server ping + - tool netwatch + - tool romon + - tool sms + - tool sniffer + - tool traffic-generator + - user + - user aaa + - user group + - user settings # END PATH LIST unfiltered: description: @@ -324,9 +316,9 @@ seealso: - module: community.routeros.api_facts - module: community.routeros.api_find_and_modify - module: community.routeros.api_modify -""" +''' -EXAMPLES = r""" +EXAMPLES = ''' --- - name: Get IP addresses community.routeros.api_info: @@ -351,25 +343,26 @@ EXAMPLES = r""" - name: Print data for IP addresses ansible.builtin.debug: var: ip_addresses.result -""" +''' -RETURN = r""" +RETURN = ''' +--- result: - description: A list of all elements for the current path. - sample: - - '.id': '*1' - actual-interface: bridge - address: "192.168.88.1/24" - comment: defconf - disabled: false - dynamic: false - interface: bridge - invalid: false - network: 192.168.88.0 - type: list - elements: dict - returned: always -""" + description: A list of all elements for the current path. + sample: + - '.id': '*1' + actual-interface: bridge + address: "192.168.88.1/24" + comment: defconf + disabled: false + dynamic: false + interface: bridge + invalid: false + network: 192.168.88.0 + type: list + elements: dict + returned: always +''' from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.common.text.converters import to_native diff --git a/plugins/modules/api_modify.py b/plugins/modules/api_modify.py index dfa6fd5..4975dc4 100644 --- a/plugins/modules/api_modify.py +++ b/plugins/modules/api_modify.py @@ -8,7 +8,8 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -DOCUMENTATION = r""" +DOCUMENTATION = ''' +--- module: api_modify author: - "Felix Fontein (@felixfontein)" @@ -16,17 +17,17 @@ short_description: Modify data at paths with API version_added: 2.2.0 description: - Allows to modify information for a path using the API. - - Use the M(community.routeros.api_find_and_modify) module to modify one or multiple entries in a controlled way depending - on some search conditions. + - Use the M(community.routeros.api_find_and_modify) module to modify one or multiple entries in a controlled way + depending on some search conditions. - To make a backup of a path that can be restored with this module, use the M(community.routeros.api_info) module. - The module ignores dynamic and builtin entries. - - B(Note) that this module is still heavily in development, and only supports B(some) paths. If you want to support new - paths, or think you found problems with existing paths, please first L(create an issue in the community.routeros Issue - Tracker,https://github.com/ansible-collections/community.routeros/issues/). + - B(Note) that this module is still heavily in development, and only supports B(some) paths. + If you want to support new paths, or think you found problems with existing paths, please first + L(create an issue in the community.routeros Issue Tracker,https://github.com/ansible-collections/community.routeros/issues/). notes: - - If write-only fields are present in the path, the module is B(not idempotent) in a strict sense, since it is not able - to verify the current value of these fields. The behavior the module should assume can be controlled with the O(handle_write_only) - option. + - If write-only fields are present in the path, the module is B(not idempotent) in a strict sense, + since it is not able to verify the current value of these fields. The behavior the module should + assume can be controlled with the O(handle_write_only) option. requirements: - Needs L(ordereddict,https://pypi.org/project/ordereddict) for Python 2.6 extends_documentation_fragment: @@ -42,244 +43,233 @@ attributes: platform: support: full platforms: RouterOS - idempotent: - support: full options: path: description: - Path to query. - - An example value is V(ip address). This is equivalent to running modification commands in C(/ip address) in the RouterOS - CLI. + - An example value is V(ip address). This is equivalent to running modification commands in C(/ip address) in the RouterOS CLI. required: true type: str choices: # BEGIN PATH LIST - - caps-man aaa - - caps-man access-list - - caps-man channel - - caps-man configuration - - caps-man datapath - - caps-man manager - - caps-man manager interface - - caps-man provisioning - - caps-man security - - certificate settings - - interface 6to4 - - interface bonding - - interface bridge - - interface bridge mlag - - interface bridge port - - interface bridge port-controller - - interface bridge port-extender - - interface bridge settings - - interface bridge vlan - - interface detect-internet - - interface eoip - - interface ethernet - - interface ethernet poe - - interface ethernet switch - - interface ethernet switch port - - interface ethernet switch port-isolation - - interface gre - - interface gre6 - - interface l2tp-client - - interface l2tp-server server - - interface list - - interface list member - - interface ovpn-client - - interface ovpn-server server - - interface ppp-client - - interface pppoe-client - - interface pppoe-server server - - interface pptp-server server - - interface sstp-server server - - interface vlan - - interface vrrp - - interface wifi - - interface wifi aaa - - interface wifi access-list - - interface wifi cap - - interface wifi capsman - - interface wifi channel - - interface wifi configuration - - interface wifi datapath - - interface wifi interworking - - interface wifi provisioning - - interface wifi security - - interface wifi steering - - interface wifiwave2 - - interface wifiwave2 aaa - - interface wifiwave2 access-list - - interface wifiwave2 cap - - interface wifiwave2 capsman - - interface wifiwave2 channel - - interface wifiwave2 configuration - - interface wifiwave2 datapath - - interface wifiwave2 interworking - - interface wifiwave2 provisioning - - interface wifiwave2 security - - interface wifiwave2 steering - - interface wireguard - - interface wireguard peers - - interface wireless - - interface wireless access-list - - interface wireless align - - interface wireless cap - - interface wireless connect-list - - interface wireless security-profiles - - interface wireless sniffer - - interface wireless snooper - - iot modbus - - ip accounting - - ip accounting web-access - - ip address - - ip arp - - ip cloud - - ip cloud advanced - - ip dhcp-client - - ip dhcp-client option - - ip dhcp-relay - - ip dhcp-server - - ip dhcp-server config - - ip dhcp-server lease - - ip dhcp-server matcher - - ip dhcp-server network - - ip dhcp-server option - - ip dhcp-server option sets - - ip dns - - ip dns adlist - - ip dns forwarders - - ip dns static - - ip firewall address-list - - ip firewall connection tracking - - ip firewall filter - - ip firewall layer7-protocol - - ip firewall mangle - - ip firewall nat - - ip firewall raw - - ip firewall service-port - - ip hotspot service-port - - ip ipsec identity - - ip ipsec mode-config - - ip ipsec peer - - ip ipsec policy - - ip ipsec profile - - ip ipsec proposal - - ip ipsec settings - - ip neighbor discovery-settings - - ip pool - - ip proxy - - ip route - - ip route rule - - ip route vrf - - ip service - - ip settings - - ip smb - - ip socks - - ip ssh - - ip tftp settings - - ip traffic-flow - - ip traffic-flow ipfix - - ip traffic-flow target - - ip upnp - - ip upnp interfaces - - ip vrf - - ipv6 address - - ipv6 dhcp-client - - ipv6 dhcp-server - - ipv6 dhcp-server option - - ipv6 firewall address-list - - ipv6 firewall filter - - ipv6 firewall mangle - - ipv6 firewall nat - - ipv6 firewall raw - - ipv6 nd - - ipv6 nd prefix - - ipv6 nd prefix default - - ipv6 route - - ipv6 settings - - mpls - - mpls interface - - mpls ldp - - mpls ldp accept-filter - - mpls ldp advertise-filter - - mpls ldp interface - - port firmware - - port remote-access - - ppp aaa - - ppp profile - - ppp secret - - queue interface - - queue simple - - queue tree - - queue type - - radius - - radius incoming - - routing bfd configuration - - routing bgp aggregate - - routing bgp connection - - routing bgp instance - - routing bgp network - - routing bgp peer - - routing bgp template - - routing filter - - routing filter community-list - - routing filter num-list - - routing filter rule - - routing filter select-rule - - routing id - - routing igmp-proxy - - routing igmp-proxy interface - - routing mme - - routing ospf area - - routing ospf area range - - routing ospf instance - - routing ospf interface-template - - routing ospf static-neighbor - - routing pimsm instance - - routing pimsm interface-template - - routing rip - - routing ripng - - routing rule - - routing table - - snmp - - snmp community - - system clock - - system clock manual - - system health settings - - system identity - - system leds settings - - system logging - - system logging action - - system note - - system ntp client - - system ntp client servers - - system ntp server - - system package update - - system resource irq rps - - system routerboard settings - - system scheduler - - system script - - system upgrade mirror - - system ups - - system watchdog - - tool bandwidth-server - - tool e-mail - - tool graphing - - tool graphing interface - - tool graphing resource - - tool mac-server - - tool mac-server mac-winbox - - tool mac-server ping - - tool netwatch - - tool romon - - tool sms - - tool sniffer - - tool traffic-generator - - user - - user aaa - - user group - - user settings + - caps-man aaa + - caps-man access-list + - caps-man channel + - caps-man configuration + - caps-man datapath + - caps-man manager + - caps-man manager interface + - caps-man provisioning + - caps-man security + - certificate settings + - interface bonding + - interface bridge + - interface bridge mlag + - interface bridge port + - interface bridge port-controller + - interface bridge port-extender + - interface bridge settings + - interface bridge vlan + - interface detect-internet + - interface eoip + - interface ethernet + - interface ethernet poe + - interface ethernet switch + - interface ethernet switch port + - interface gre + - interface gre6 + - interface l2tp-client + - interface l2tp-server server + - interface list + - interface list member + - interface ovpn-client + - interface ovpn-server server + - interface ppp-client + - interface pppoe-client + - interface pppoe-server server + - interface pptp-server server + - interface sstp-server server + - interface vlan + - interface vrrp + - interface wifi + - interface wifi aaa + - interface wifi access-list + - interface wifi cap + - interface wifi capsman + - interface wifi channel + - interface wifi configuration + - interface wifi datapath + - interface wifi interworking + - interface wifi provisioning + - interface wifi security + - interface wifi steering + - interface wifiwave2 + - interface wifiwave2 aaa + - interface wifiwave2 access-list + - interface wifiwave2 cap + - interface wifiwave2 capsman + - interface wifiwave2 channel + - interface wifiwave2 configuration + - interface wifiwave2 datapath + - interface wifiwave2 interworking + - interface wifiwave2 provisioning + - interface wifiwave2 security + - interface wifiwave2 steering + - interface wireguard + - interface wireguard peers + - interface wireless + - interface wireless align + - interface wireless cap + - interface wireless security-profiles + - interface wireless sniffer + - interface wireless snooper + - iot modbus + - ip accounting + - ip accounting web-access + - ip address + - ip arp + - ip cloud + - ip cloud advanced + - ip dhcp-client + - ip dhcp-client option + - ip dhcp-relay + - ip dhcp-server + - ip dhcp-server config + - ip dhcp-server lease + - ip dhcp-server matcher + - ip dhcp-server network + - ip dhcp-server option + - ip dhcp-server option sets + - ip dns + - ip dns adlist + - ip dns static + - ip firewall address-list + - ip firewall connection tracking + - ip firewall filter + - ip firewall layer7-protocol + - ip firewall mangle + - ip firewall nat + - ip firewall raw + - ip firewall service-port + - ip hotspot service-port + - ip ipsec identity + - ip ipsec peer + - ip ipsec policy + - ip ipsec profile + - ip ipsec proposal + - ip ipsec settings + - ip neighbor discovery-settings + - ip pool + - ip proxy + - ip route + - ip route rule + - ip route vrf + - ip service + - ip settings + - ip smb + - ip socks + - ip ssh + - ip tftp settings + - ip traffic-flow + - ip traffic-flow ipfix + - ip traffic-flow target + - ip upnp + - ip upnp interfaces + - ip vrf + - ipv6 address + - ipv6 dhcp-client + - ipv6 dhcp-server + - ipv6 dhcp-server option + - ipv6 firewall address-list + - ipv6 firewall filter + - ipv6 firewall mangle + - ipv6 firewall nat + - ipv6 firewall raw + - ipv6 nd + - ipv6 nd prefix + - ipv6 nd prefix default + - ipv6 route + - ipv6 settings + - mpls + - mpls interface + - mpls ldp + - mpls ldp accept-filter + - mpls ldp advertise-filter + - mpls ldp interface + - port firmware + - port remote-access + - ppp aaa + - ppp profile + - ppp secret + - queue interface + - queue simple + - queue tree + - queue type + - radius + - radius incoming + - routing bgp aggregate + - routing bgp connection + - routing bgp instance + - routing bgp network + - routing bgp peer + - routing bgp template + - routing filter + - routing filter num-list + - routing filter rule + - routing filter select-rule + - routing id + - routing igmp-proxy + - routing igmp-proxy interface + - routing mme + - routing ospf area + - routing ospf area range + - routing ospf instance + - routing ospf interface-template + - routing ospf static-neighbor + - routing pimsm instance + - routing pimsm interface-template + - routing rip + - routing ripng + - routing rule + - routing table + - snmp + - snmp community + - system clock + - system clock manual + - system health settings + - system identity + - system leds settings + - system logging + - system logging action + - system note + - system ntp client + - system ntp client servers + - system ntp server + - system package update + - system resource irq rps + - system routerboard settings + - system scheduler + - system script + - system upgrade mirror + - system ups + - system watchdog + - tool bandwidth-server + - tool e-mail + - tool graphing + - tool graphing interface + - tool graphing resource + - tool mac-server + - tool mac-server mac-winbox + - tool mac-server ping + - tool netwatch + - tool romon + - tool sms + - tool sniffer + - tool traffic-generator + - user + - user aaa + - user group + - user settings # END PATH LIST data: description: @@ -307,12 +297,12 @@ options: default: ignore handle_entries_content: description: - - For a single entry in O(data), this describes how to handle fields that are not mentioned in that entry, but appear - in the actual config. + - For a single entry in O(data), this describes how to handle fields that are not mentioned + in that entry, but appear in the actual config. - If V(ignore), they are not modified. - If V(remove), they are removed. If at least one cannot be removed, the module will fail. - - If V(remove_as_much_as_possible), all that can be removed will be removed. The ones that cannot be removed will be - kept. + - If V(remove_as_much_as_possible), all that can be removed will be removed. The ones that + cannot be removed will be kept. - Note that V(remove) and V(remove_as_much_as_possible) do not apply to write-only fields. type: str choices: @@ -324,8 +314,8 @@ options: description: - How to handle values passed in for read-only fields. - If V(ignore), they are not passed to the API. - - If V(validate), the values are not passed for creation, and for updating they are compared to the value returned for - the object. If they differ, the module fails. + - If V(validate), the values are not passed for creation, and for updating they are compared to the value returned for the object. + If they differ, the module fails. - If V(error), the module will fail if read-only fields are provided. type: str choices: @@ -338,8 +328,9 @@ options: description: - How to handle values passed in for write-only fields. - If V(create_only), they are passed on creation, and ignored for updating. - - If V(always_update), they are always passed to the API. This means that if such a value is present, the module will - always result in C(changed) since there is no way to validate whether the value actually changed. + - If V(always_update), they are always passed to the API. This means that if such a value is present, + the module will always result in C(changed) since there is no way to validate whether the value + actually changed. - If V(error), the module will fail if write-only fields are provided. type: str choices: @@ -351,18 +342,20 @@ options: restrict: description: - Restrict operation to entries matching the following criteria. - - This can be useful together with O(handle_absent_entries=remove) to operate on a subset of the values. - - For example, for O(path=ip firewall filter), you can set O(restrict[].field=chain) and O(restrict[].values=input) - to restrict operation to the input chain, and ignore the forward and output chains. + - This can be useful together with O(handle_absent_entries=remove) to operate on a subset of + the values. + - For example, for O(path=ip firewall filter), you can set O(restrict[].field=chain) and + O(restrict[].values=input) to restrict operation to the input chain, and ignore the + forward and output chains. version_added: 2.18.0 seealso: - module: community.routeros.api - module: community.routeros.api_facts - module: community.routeros.api_find_and_modify - module: community.routeros.api_info -""" +''' -EXAMPLES = r""" +EXAMPLES = ''' --- - name: Setup DHCP server networks # Ensures that we have exactly two DHCP server networks (in the specified order) @@ -417,42 +410,43 @@ EXAMPLES = r""" data: - action: drop chain: input -""" +''' -RETURN = r""" +RETURN = ''' +--- old_data: - description: - - A list of all elements for the current path before a change was made. - sample: - - '.id': '*1' - actual-interface: bridge - address: "192.168.88.1/24" - comment: defconf - disabled: false - dynamic: false - interface: bridge - invalid: false - network: 192.168.88.0 - type: list - elements: dict - returned: always + description: + - A list of all elements for the current path before a change was made. + sample: + - '.id': '*1' + actual-interface: bridge + address: "192.168.88.1/24" + comment: defconf + disabled: false + dynamic: false + interface: bridge + invalid: false + network: 192.168.88.0 + type: list + elements: dict + returned: always new_data: - description: - - A list of all elements for the current path after a change was made. - sample: - - '.id': '*1' - actual-interface: bridge - address: "192.168.1.1/24" - comment: awesome - disabled: false - dynamic: false - interface: bridge - invalid: false - network: 192.168.1.0 - type: list - elements: dict - returned: always -""" + description: + - A list of all elements for the current path after a change was made. + sample: + - '.id': '*1' + actual-interface: bridge + address: "192.168.1.1/24" + comment: awesome + disabled: false + dynamic: false + interface: bridge + invalid: false + network: 192.168.1.0 + type: list + elements: dict + returned: always +''' from collections import defaultdict diff --git a/plugins/modules/command.py b/plugins/modules/command.py index 50abe49..bf74d47 100644 --- a/plugins/modules/command.py +++ b/plugins/modules/command.py @@ -7,77 +7,87 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -DOCUMENTATION = r""" +DOCUMENTATION = ''' +--- module: command author: "Egor Zaitsev (@heuels)" short_description: Run commands on remote devices running MikroTik RouterOS description: - - Sends arbitrary commands to an RouterOS node and returns the results read from the device. This module includes an argument - that will cause the module to wait for a specific condition before returning or timing out if the condition is not met. - - The module always indicates a (changed) status. You can use R(the changed_when task property,override_the_changed_result) - to determine whether a command task actually resulted in a change or not. + - Sends arbitrary commands to an RouterOS node and returns the results + read from the device. This module includes an + argument that will cause the module to wait for a specific condition + before returning or timing out if the condition is not met. + - The module always indicates a (changed) status. You can use + R(the changed_when task property,override_the_changed_result) to determine + whether a command task actually resulted in a change or not. extends_documentation_fragment: - community.routeros.attributes attributes: check_mode: support: none details: - - Before community.routeros 3.0.0, the module claimed to support check mode. It simply executed the command in check - mode. + - Before community.routeros 3.0.0, the module claimed to support check mode. + It simply executed the command in check mode. diff_mode: support: none platform: support: full platforms: RouterOS - idempotent: - support: N/A - details: - - Whether the executed command is idempotent depends on the command. options: commands: description: - - List of commands to send to the remote RouterOS device over the configured provider. The resulting output from the - command is returned. If the O(wait_for) argument is provided, the module is not returned until the condition is satisfied - or the number of retries has expired. + - List of commands to send to the remote RouterOS device over the + configured provider. The resulting output from the command + is returned. If the O(wait_for) argument is provided, the + module is not returned until the condition is satisfied or + the number of retries has expired. required: true type: list elements: str wait_for: description: - - List of conditions to evaluate against the output of the command. The task will wait for each condition to be true - before moving forward. If the conditional is not true within the configured number of retries, the task fails. See - examples. + - List of conditions to evaluate against the output of the + command. The task will wait for each condition to be true + before moving forward. If the conditional is not true + within the configured number of retries, the task fails. + See examples. type: list elements: str match: description: - - The O(match) argument is used in conjunction with the O(wait_for) argument to specify the match policy. Valid values - are V(all) or V(any). If the value is set to V(all) then all conditionals in the wait_for must be satisfied. If the - value is set to V(any) then only one of the values must be satisfied. + - The O(match) argument is used in conjunction with the + O(wait_for) argument to specify the match policy. Valid + values are V(all) or V(any). If the value is set to V(all) + then all conditionals in the wait_for must be satisfied. If + the value is set to V(any) then only one of the values must be + satisfied. default: all choices: ['any', 'all'] type: str retries: description: - - Specifies the number of retries a command should by tried before it is considered failed. The command is run on the - target device every retry and evaluated against the O(wait_for) conditions. + - Specifies the number of retries a command should by tried + before it is considered failed. The command is run on the + target device every retry and evaluated against the + O(wait_for) conditions. default: 10 type: int interval: description: - - Configures the interval in seconds to wait between retries of the command. If the command does not pass the specified - conditions, the interval indicates how long to wait before trying the command again. + - Configures the interval in seconds to wait between retries + of the command. If the command does not pass the specified + conditions, the interval indicates how long to wait before + trying the command again. default: 1 type: int seealso: - ref: ansible_collections.community.routeros.docsite.ssh-guide - description: How to connect to RouterOS devices with SSH. + description: How to connect to RouterOS devices with SSH - ref: ansible_collections.community.routeros.docsite.quoting - description: How to quote and unquote commands and arguments. -""" + description: How to quote and unquote commands and arguments +''' -EXAMPLES = r""" ---- +EXAMPLES = """ - name: Run command on remote devices community.routeros.command: commands: /system routerboard print @@ -103,19 +113,19 @@ EXAMPLES = r""" - result[1] contains ether1 """ -RETURN = r""" +RETURN = """ stdout: - description: The set of responses from the commands. + description: The set of responses from the commands returned: always apart from low level errors (such as action plugin) type: list sample: ['...', '...'] stdout_lines: - description: The value of stdout split into a list. + description: The value of stdout split into a list returned: always apart from low level errors (such as action plugin) type: list sample: [['...', '...'], ['...'], ['...']] failed_conditions: - description: The list of conditionals that have failed. + description: The list of conditionals that have failed returned: failed type: list sample: ['...', '...'] diff --git a/plugins/modules/facts.py b/plugins/modules/facts.py index e80143c..50f9a21 100644 --- a/plugins/modules/facts.py +++ b/plugins/modules/facts.py @@ -7,19 +7,21 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -DOCUMENTATION = r""" +DOCUMENTATION = ''' +--- module: facts author: "Egor Zaitsev (@heuels)" short_description: Collect facts from remote devices running MikroTik RouterOS description: - - Collects a base set of device facts from a remote device that is running RouterOS. This module prepends all of the base - network fact keys with C(ansible_net_). The facts module will always collect a base set of facts from the device + - Collects a base set of device facts from a remote device that + is running RouterOS. This module prepends all of the + base network fact keys with C(ansible_net_). The facts + module will always collect a base set of facts from the device and can enable or disable collection of additional facts. extends_documentation_fragment: - community.routeros.attributes - community.routeros.attributes.facts - community.routeros.attributes.facts_module - - community.routeros.attributes.idempotent_not_modify_state attributes: platform: support: full @@ -27,10 +29,12 @@ attributes: options: gather_subset: description: - - When supplied, this argument will restrict the facts collected to a given subset. Possible values for this argument - include V(all), V(hardware), V(config), V(interfaces), and V(routing). - - Can specify a list of values to include a larger subset. Values can also be used with an initial V(!) to specify that - a specific subset should not be collected. + - When supplied, this argument will restrict the facts collected + to a given subset. Possible values for this argument include + V(all), V(hardware), V(config), V(interfaces), and V(routing). + - Can specify a list of values to include a larger subset. + Values can also be used with an initial V(!) to specify that a + specific subset should not be collected. required: false default: - '!config' @@ -38,11 +42,10 @@ options: elements: str seealso: - ref: ansible_collections.community.routeros.docsite.ssh-guide - description: How to connect to RouterOS devices with SSH. -""" + description: How to connect to RouterOS devices with SSH +''' -EXAMPLES = r""" ---- +EXAMPLES = """ - name: Collect all facts from the device community.routeros.facts: gather_subset: all @@ -58,7 +61,7 @@ EXAMPLES = r""" - "!hardware" """ -RETURN = r""" +RETURN = """ ansible_facts: description: "Dictionary of IP geolocation facts for a host's IP address." returned: always @@ -126,9 +129,9 @@ ansible_facts: ansible_net_config_nonverbose: description: - The current active config from the device in minimal form. - - This value is idempotent in the sense that if the facts module is run twice and the device's config was not changed - between the runs, the value is identical. This is achieved by running C(/export) and stripping the timestamp from - the comment in the first line. + - This value is idempotent in the sense that if the facts module is run twice and the device's config + was not changed between the runs, the value is identical. This is achieved by running C(/export) + and stripping the timestamp from the comment in the first line. returned: O(gather_subset) contains V(config) type: str version_added: 1.2.0 @@ -589,6 +592,8 @@ FACT_SUBSETS = dict( VALID_SUBSETS = frozenset(FACT_SUBSETS.keys()) +warnings = list() + def main(): """main entry point for module execution @@ -651,7 +656,7 @@ def main(): key = 'ansible_net_%s' % key ansible_facts[key] = value - module.exit_json(ansible_facts=ansible_facts) + module.exit_json(ansible_facts=ansible_facts, warnings=warnings) if __name__ == '__main__': diff --git a/plugins/terminal/routeros.py b/plugins/terminal/routeros.py index 8a39561..9d50fa2 100644 --- a/plugins/terminal/routeros.py +++ b/plugins/terminal/routeros.py @@ -31,9 +31,7 @@ class TerminalModule(TerminalBase): terminal_stdout_re = [ re.compile(br"\x1b<"), - re.compile( - br"((\[[\w\-\.]+\@)|(\r\<(([\w\-\.]*\@)|)))" - br"[\w\s\-\.\/]+\] ?( ?$"), + re.compile(br"\[[\w\-\.]+\@[\w\s\-\.\/]+\] ?( ?$"), re.compile(br"Please press \"Enter\" to continue!"), re.compile(br"Do you want to see the software license\? \[Y\/n\]: ?"), ] diff --git a/tests/ee/roles/filter_quoting/tasks/main.yml b/tests/ee/roles/filter_quoting/tasks/main.yml index c80af59..e7a2d29 100644 --- a/tests/ee/roles/filter_quoting/tasks/main.yml +++ b/tests/ee/roles/filter_quoting/tasks/main.yml @@ -22,7 +22,7 @@ assert: that: - >- - "Unexpected end of string during escaped parameter" in result.msg + result.msg == "Unexpected end of string during escaped parameter" - name: "Test quote_argument filter" assert: diff --git a/tests/integration/requirements.yml b/tests/integration/requirements.yml index 559c301..6a22736 100644 --- a/tests/integration/requirements.yml +++ b/tests/integration/requirements.yml @@ -4,4 +4,4 @@ # SPDX-License-Identifier: GPL-3.0-or-later collections: - - ansible.netcommon +- ansible.netcommon diff --git a/tests/integration/targets/filter_quoting/tasks/main.yml b/tests/integration/targets/filter_quoting/tasks/main.yml index c80af59..e7a2d29 100644 --- a/tests/integration/targets/filter_quoting/tasks/main.yml +++ b/tests/integration/targets/filter_quoting/tasks/main.yml @@ -22,7 +22,7 @@ assert: that: - >- - "Unexpected end of string during escaped parameter" in result.msg + result.msg == "Unexpected end of string during escaped parameter" - name: "Test quote_argument filter" assert: diff --git a/tests/sanity/extra/extra-docs.json b/tests/sanity/extra/extra-docs.json new file mode 100644 index 0000000..9a28d17 --- /dev/null +++ b/tests/sanity/extra/extra-docs.json @@ -0,0 +1,13 @@ +{ + "include_symlinks": false, + "prefixes": [ + "docs/docsite/", + "plugins/", + "roles/" + ], + "output": "path-line-column-message", + "requirements": [ + "ansible-core", + "antsibull-docs" + ] +} diff --git a/tests/sanity/ignore-2.20.txt.license b/tests/sanity/extra/extra-docs.json.license similarity index 100% rename from tests/sanity/ignore-2.20.txt.license rename to tests/sanity/extra/extra-docs.json.license diff --git a/tests/sanity/extra/extra-docs.py b/tests/sanity/extra/extra-docs.py new file mode 100755 index 0000000..251e6d7 --- /dev/null +++ b/tests/sanity/extra/extra-docs.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later +"""Check extra collection docs with antsibull-docs.""" +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import sys +import subprocess + + +def main(): + """Main entry point.""" + env = os.environ.copy() + suffix = ':{env}'.format(env=env["ANSIBLE_COLLECTIONS_PATH"]) if 'ANSIBLE_COLLECTIONS_PATH' in env else '' + env['ANSIBLE_COLLECTIONS_PATH'] = '{root}{suffix}'.format(root=os.path.dirname(os.path.dirname(os.path.dirname(os.getcwd()))), suffix=suffix) + p = subprocess.run( + ['antsibull-docs', 'lint-collection-docs', '--plugin-docs', '--skip-rstcheck', '.'], + env=env, + check=False, + ) + if p.returncode not in (0, 3): + print('{0}:0:0: unexpected return code {1}'.format(sys.argv[0], p.returncode)) + + +if __name__ == '__main__': + main() diff --git a/tests/sanity/extra/licenses.json b/tests/sanity/extra/licenses.json new file mode 100644 index 0000000..50e47ca --- /dev/null +++ b/tests/sanity/extra/licenses.json @@ -0,0 +1,4 @@ +{ + "include_symlinks": false, + "output": "path-message" +} diff --git a/tests/sanity/extra/licenses.json.license b/tests/sanity/extra/licenses.json.license new file mode 100644 index 0000000..edff8c7 --- /dev/null +++ b/tests/sanity/extra/licenses.json.license @@ -0,0 +1,3 @@ +GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +SPDX-License-Identifier: GPL-3.0-or-later +SPDX-FileCopyrightText: Ansible Project diff --git a/tests/sanity/extra/licenses.py b/tests/sanity/extra/licenses.py new file mode 100755 index 0000000..80eb795 --- /dev/null +++ b/tests/sanity/extra/licenses.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python +# Copyright (c) 2022, Felix Fontein +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later +"""Prevent files without a correct license identifier from being added to the source tree.""" +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import glob +import sys + + +def format_license_list(licenses): + if not licenses: + return '(empty)' + return ', '.join(['"%s"' % license for license in licenses]) + + +def find_licenses(filename, relax=False): + spdx_license_identifiers = [] + other_license_identifiers = [] + has_copyright = False + try: + with open(filename, 'r', encoding='utf-8') as f: + for line in f: + line = line.rstrip() + if 'Copyright ' in line: + has_copyright = True + if 'Copyright: ' in line: + print('%s: found copyright line with "Copyright:". Please remove the colon.' % (filename, )) + if 'SPDX-FileCopyrightText: ' in line: + has_copyright = True + idx = line.find('SPDX-License-Identifier: ') + if idx >= 0: + lic_id = line[idx + len('SPDX-License-Identifier: '):] + spdx_license_identifiers.extend(lic_id.split(' OR ')) + if 'GNU General Public License' in line: + if 'v3.0+' in line: + other_license_identifiers.append('GPL-3.0-or-later') + if 'version 3 or later' in line: + other_license_identifiers.append('GPL-3.0-or-later') + if 'Simplified BSD License' in line: + other_license_identifiers.append('BSD-2-Clause') + if 'Apache License 2.0' in line: + other_license_identifiers.append('Apache-2.0') + if 'PSF License' in line or 'Python-2.0' in line: + other_license_identifiers.append('PSF-2.0') + if 'MIT License' in line: + other_license_identifiers.append('MIT') + except Exception as exc: + print('%s: error while processing file: %s' % (filename, exc)) + if len(set(spdx_license_identifiers)) < len(spdx_license_identifiers): + print('%s: found identical SPDX-License-Identifier values' % (filename, )) + if other_license_identifiers and set(other_license_identifiers) != set(spdx_license_identifiers): + print('%s: SPDX-License-Identifier yielded the license list %s, while manual guessing yielded the license list %s' % ( + filename, format_license_list(spdx_license_identifiers), format_license_list(other_license_identifiers))) + if not has_copyright and not relax: + print('%s: found no copyright notice' % (filename, )) + return sorted(spdx_license_identifiers) + + +def main(): + """Main entry point.""" + paths = sys.argv[1:] or sys.stdin.read().splitlines() + + # The following paths are allowed to have no license identifier + no_comments_allowed = [ + 'changelogs/fragments/*.yml', + 'changelogs/fragments/*.yaml', + ] + + # These files are completely ignored + ignore_paths = [ + '.ansible-test-timeout.json', + '.reuse/dep5', + 'LICENSES/*.txt', + 'COPYING', + ] + + no_comments_allowed = [fn for pattern in no_comments_allowed for fn in glob.glob(pattern)] + ignore_paths = [fn for pattern in ignore_paths for fn in glob.glob(pattern)] + + valid_licenses = [license_file[len('LICENSES/'):-len('.txt')] for license_file in glob.glob('LICENSES/*.txt')] + + for path in paths: + if path.startswith('./'): + path = path[2:] + if path in ignore_paths or path.startswith('tests/output/'): + continue + if os.stat(path).st_size == 0: + continue + if not path.endswith('.license') and os.path.exists(path + '.license'): + path = path + '.license' + valid_licenses_for_path = valid_licenses + if path.startswith('plugins/') and not path.startswith(('plugins/modules/', 'plugins/module_utils/')): + valid_licenses_for_path = [license for license in valid_licenses if license == 'GPL-3.0-or-later'] + licenses = find_licenses(path, relax=path in no_comments_allowed) + if not licenses: + if path not in no_comments_allowed: + print('%s: must have at least one license' % (path, )) + else: + for license in licenses: + if license not in valid_licenses_for_path: + print('%s: found not allowed license "%s", must be one of %s' % ( + path, license, format_license_list(valid_licenses_for_path))) + + +if __name__ == '__main__': + main() diff --git a/tests/sanity/extra/licenses.py.license b/tests/sanity/extra/licenses.py.license new file mode 100644 index 0000000..6c4958f --- /dev/null +++ b/tests/sanity/extra/licenses.py.license @@ -0,0 +1,3 @@ +GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +SPDX-License-Identifier: GPL-3.0-or-later +SPDX-FileCopyrightText: 2022, Felix Fontein diff --git a/tests/sanity/extra/no-unwanted-files.json b/tests/sanity/extra/no-unwanted-files.json new file mode 100644 index 0000000..c789a7f --- /dev/null +++ b/tests/sanity/extra/no-unwanted-files.json @@ -0,0 +1,7 @@ +{ + "include_symlinks": true, + "prefixes": [ + "plugins/" + ], + "output": "path-message" +} diff --git a/tests/sanity/extra/no-unwanted-files.json.license b/tests/sanity/extra/no-unwanted-files.json.license new file mode 100644 index 0000000..edff8c7 --- /dev/null +++ b/tests/sanity/extra/no-unwanted-files.json.license @@ -0,0 +1,3 @@ +GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +SPDX-License-Identifier: GPL-3.0-or-later +SPDX-FileCopyrightText: Ansible Project diff --git a/tests/sanity/extra/no-unwanted-files.py b/tests/sanity/extra/no-unwanted-files.py new file mode 100755 index 0000000..b39df83 --- /dev/null +++ b/tests/sanity/extra/no-unwanted-files.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later +"""Prevent unwanted files from being added to the source tree.""" +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import os.path +import sys + + +def main(): + """Main entry point.""" + paths = sys.argv[1:] or sys.stdin.read().splitlines() + + allowed_extensions = ( + '.cs', + '.ps1', + '.psm1', + '.py', + ) + + skip_paths = set([ + ]) + + skip_directories = ( + ) + + yaml_directories = ( + 'plugins/test/', + 'plugins/filter/', + ) + + for path in paths: + if path in skip_paths: + continue + + if any(path.startswith(skip_directory) for skip_directory in skip_directories): + continue + + if os.path.islink(path): + print('%s: is a symbolic link' % (path, )) + elif not os.path.isfile(path): + print('%s: is not a regular file' % (path, )) + + ext = os.path.splitext(path)[1] + + if ext in ('.yml', ) and any(path.startswith(yaml_directory) for yaml_directory in yaml_directories): + continue + + if ext not in allowed_extensions: + print('%s: extension must be one of: %s' % (path, ', '.join(allowed_extensions))) + + +if __name__ == '__main__': + main() diff --git a/tests/sanity/extra/update-docs.json b/tests/sanity/extra/update-docs.json new file mode 100644 index 0000000..3a63af8 --- /dev/null +++ b/tests/sanity/extra/update-docs.json @@ -0,0 +1,11 @@ +{ + "include_symlinks": false, + "prefixes": [ + "docs/docsite/rst/api-guide.rst", + "plugins/modules/" + ], + "output": "path-line-column-message", + "requirements": [ + "ansible-core" + ] +} diff --git a/tests/sanity/extra/update-docs.json.license b/tests/sanity/extra/update-docs.json.license new file mode 100644 index 0000000..edff8c7 --- /dev/null +++ b/tests/sanity/extra/update-docs.json.license @@ -0,0 +1,3 @@ +GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +SPDX-License-Identifier: GPL-3.0-or-later +SPDX-FileCopyrightText: Ansible Project diff --git a/tests/sanity/extra/update-docs.py b/tests/sanity/extra/update-docs.py new file mode 100644 index 0000000..6015512 --- /dev/null +++ b/tests/sanity/extra/update-docs.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later +"""Check whether update-docs.py modifies something.""" +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import sys +import subprocess + + +def main(): + """Main entry point.""" + p = subprocess.run([sys.executable, 'update-docs.py'], check=False) + if p.returncode not in (0, 1): + print('{0}:0:0: unexpected return code {1}'.format(sys.argv[0], p.returncode)) + + +if __name__ == '__main__': + main() diff --git a/tests/sanity/ignore-2.10.txt b/tests/sanity/ignore-2.10.txt index 50c92fb..ece5b29 100644 --- a/tests/sanity/ignore-2.10.txt +++ b/tests/sanity/ignore-2.10.txt @@ -1,9 +1,9 @@ docs/docsite/rst/api-guide.rst rstcheck docs/docsite/rst/quoting.rst rstcheck docs/docsite/rst/ssh-guide.rst rstcheck -tests/update-docs.py compile-2.6 -tests/update-docs.py compile-2.7 -tests/update-docs.py compile-3.5 -tests/update-docs.py future-import-boilerplate -tests/update-docs.py metaclass-boilerplate -tests/update-docs.py shebang +update-docs.py compile-2.6 +update-docs.py compile-2.7 +update-docs.py compile-3.5 +update-docs.py future-import-boilerplate +update-docs.py metaclass-boilerplate +update-docs.py shebang diff --git a/tests/sanity/ignore-2.11.txt b/tests/sanity/ignore-2.11.txt index 1c7f5da..876765a 100644 --- a/tests/sanity/ignore-2.11.txt +++ b/tests/sanity/ignore-2.11.txt @@ -1,6 +1,6 @@ -tests/update-docs.py compile-2.6 -tests/update-docs.py compile-2.7 -tests/update-docs.py compile-3.5 -tests/update-docs.py future-import-boilerplate -tests/update-docs.py metaclass-boilerplate -tests/update-docs.py shebang +update-docs.py compile-2.6 +update-docs.py compile-2.7 +update-docs.py compile-3.5 +update-docs.py future-import-boilerplate +update-docs.py metaclass-boilerplate +update-docs.py shebang diff --git a/tests/sanity/ignore-2.12.txt b/tests/sanity/ignore-2.12.txt index 65e5bca..ce635c3 100644 --- a/tests/sanity/ignore-2.12.txt +++ b/tests/sanity/ignore-2.12.txt @@ -1 +1 @@ -tests/update-docs.py shebang +update-docs.py shebang diff --git a/tests/sanity/ignore-2.13.txt b/tests/sanity/ignore-2.13.txt index 65e5bca..ce635c3 100644 --- a/tests/sanity/ignore-2.13.txt +++ b/tests/sanity/ignore-2.13.txt @@ -1 +1 @@ -tests/update-docs.py shebang +update-docs.py shebang diff --git a/tests/sanity/ignore-2.14.txt b/tests/sanity/ignore-2.14.txt index 65e5bca..ce635c3 100644 --- a/tests/sanity/ignore-2.14.txt +++ b/tests/sanity/ignore-2.14.txt @@ -1 +1 @@ -tests/update-docs.py shebang +update-docs.py shebang diff --git a/tests/sanity/ignore-2.15.txt b/tests/sanity/ignore-2.15.txt index 65e5bca..ce635c3 100644 --- a/tests/sanity/ignore-2.15.txt +++ b/tests/sanity/ignore-2.15.txt @@ -1 +1 @@ -tests/update-docs.py shebang +update-docs.py shebang diff --git a/tests/sanity/ignore-2.16.txt b/tests/sanity/ignore-2.16.txt index 65e5bca..ce635c3 100644 --- a/tests/sanity/ignore-2.16.txt +++ b/tests/sanity/ignore-2.16.txt @@ -1 +1 @@ -tests/update-docs.py shebang +update-docs.py shebang diff --git a/tests/sanity/ignore-2.17.txt b/tests/sanity/ignore-2.17.txt index 65e5bca..0a5234b 100644 --- a/tests/sanity/ignore-2.17.txt +++ b/tests/sanity/ignore-2.17.txt @@ -1 +1,2 @@ -tests/update-docs.py shebang +update-docs.py shebang +tests/unit/compat/mock.py pylint:use-yield-from # suggested construct does not work with Python 2 diff --git a/tests/sanity/ignore-2.18.txt b/tests/sanity/ignore-2.18.txt index 65e5bca..0a5234b 100644 --- a/tests/sanity/ignore-2.18.txt +++ b/tests/sanity/ignore-2.18.txt @@ -1 +1,2 @@ -tests/update-docs.py shebang +update-docs.py shebang +tests/unit/compat/mock.py pylint:use-yield-from # suggested construct does not work with Python 2 diff --git a/tests/sanity/ignore-2.19.txt b/tests/sanity/ignore-2.19.txt index 65e5bca..0a5234b 100644 --- a/tests/sanity/ignore-2.19.txt +++ b/tests/sanity/ignore-2.19.txt @@ -1 +1,2 @@ -tests/update-docs.py shebang +update-docs.py shebang +tests/unit/compat/mock.py pylint:use-yield-from # suggested construct does not work with Python 2 diff --git a/tests/sanity/ignore-2.20.txt b/tests/sanity/ignore-2.20.txt deleted file mode 100644 index 65e5bca..0000000 --- a/tests/sanity/ignore-2.20.txt +++ /dev/null @@ -1 +0,0 @@ -tests/update-docs.py shebang diff --git a/tests/sanity/ignore-2.9.txt b/tests/sanity/ignore-2.9.txt index 50c92fb..ece5b29 100644 --- a/tests/sanity/ignore-2.9.txt +++ b/tests/sanity/ignore-2.9.txt @@ -1,9 +1,9 @@ docs/docsite/rst/api-guide.rst rstcheck docs/docsite/rst/quoting.rst rstcheck docs/docsite/rst/ssh-guide.rst rstcheck -tests/update-docs.py compile-2.6 -tests/update-docs.py compile-2.7 -tests/update-docs.py compile-3.5 -tests/update-docs.py future-import-boilerplate -tests/update-docs.py metaclass-boilerplate -tests/update-docs.py shebang +update-docs.py compile-2.6 +update-docs.py compile-2.7 +update-docs.py compile-3.5 +update-docs.py future-import-boilerplate +update-docs.py metaclass-boilerplate +update-docs.py shebang diff --git a/tests/unit/compat/__init__.py b/tests/unit/compat/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/compat/builtins.py b/tests/unit/compat/builtins.py new file mode 100644 index 0000000..d548601 --- /dev/null +++ b/tests/unit/compat/builtins.py @@ -0,0 +1,20 @@ +# Copyright (c) 2014, Toshio Kuratomi +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +# +# Compat for python2.7 +# + +# One unittest needs to import builtins via __import__() so we need to have +# the string that represents it +try: + import __builtin__ # noqa: F401, pylint: disable=unused-import +except ImportError: + BUILTINS = 'builtins' +else: + BUILTINS = '__builtin__' diff --git a/tests/unit/compat/mock.py b/tests/unit/compat/mock.py new file mode 100644 index 0000000..bdbea94 --- /dev/null +++ b/tests/unit/compat/mock.py @@ -0,0 +1,109 @@ +# Copyright (c) 2014, Toshio Kuratomi +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +''' +Compat module for Python3.x's unittest.mock module +''' +import sys + +# Python 2.7 + +# Note: Could use the pypi mock library on python3.x as well as python2.x. It +# is the same as the python3 stdlib mock library + +try: + # Allow wildcard import because we really do want to import all of mock's + # symbols into this compat shim + # pylint: disable=wildcard-import,unused-wildcard-import + from unittest.mock import * # noqa: F401, pylint: disable=unused-import +except ImportError: + # Python 2 + # pylint: disable=wildcard-import,unused-wildcard-import + try: + from mock import * # noqa: F401, pylint: disable=unused-import + except ImportError: + print('You need the mock library installed on python2.x to run tests') + + +# Prior to 3.4.4, mock_open cannot handle binary read_data +if sys.version_info >= (3,) and sys.version_info < (3, 4, 4): + file_spec = None + + def _iterate_read_data(read_data): + # Helper for mock_open: + # Retrieve lines from read_data via a generator so that separate calls to + # readline, read, and readlines are properly interleaved + sep = b'\n' if isinstance(read_data, bytes) else '\n' + data_as_list = [l + sep for l in read_data.split(sep)] + + if data_as_list[-1] == sep: + # If the last line ended in a newline, the list comprehension will have an + # extra entry that's just a newline. Remove this. + data_as_list = data_as_list[:-1] + else: + # If there wasn't an extra newline by itself, then the file being + # emulated doesn't have a newline to end the last line remove the + # newline that our naive format() added + data_as_list[-1] = data_as_list[-1][:-1] + + for line in data_as_list: + yield line + + def mock_open(mock=None, read_data=''): + """ + A helper function to create a mock to replace the use of `open`. It works + for `open` called directly or used as a context manager. + + The `mock` argument is the mock object to configure. If `None` (the + default) then a `MagicMock` will be created for you, with the API limited + to methods or attributes available on standard file handles. + + `read_data` is a string for the `read` methoddline`, and `readlines` of the + file handle to return. This is an empty string by default. + """ + def _readlines_side_effect(*args, **kwargs): + if handle.readlines.return_value is not None: + return handle.readlines.return_value + return list(_data) + + def _read_side_effect(*args, **kwargs): + if handle.read.return_value is not None: + return handle.read.return_value + return type(read_data)().join(_data) + + def _readline_side_effect(): + if handle.readline.return_value is not None: + while True: + yield handle.readline.return_value + for line in _data: + yield line + + global file_spec + if file_spec is None: + import _io + file_spec = list(set(dir(_io.TextIOWrapper)).union(set(dir(_io.BytesIO)))) + + if mock is None: + mock = MagicMock(name='open', spec=open) + + handle = MagicMock(spec=file_spec) + handle.__enter__.return_value = handle + + _data = _iterate_read_data(read_data) + + handle.write.return_value = None + handle.read.return_value = None + handle.readline.return_value = None + handle.readlines.return_value = None + + handle.read.side_effect = _read_side_effect + handle.readline.side_effect = _readline_side_effect() + handle.readlines.side_effect = _readlines_side_effect + + mock.return_value = handle + return mock diff --git a/tests/unit/compat/unittest.py b/tests/unit/compat/unittest.py new file mode 100644 index 0000000..d50bab8 --- /dev/null +++ b/tests/unit/compat/unittest.py @@ -0,0 +1,25 @@ +# Copyright (c) 2014, Toshio Kuratomi +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +''' +Compat module for Python2.7's unittest module +''' + +import sys + +# Allow wildcard import because we really do want to import all of +# unittests's symbols into this compat shim +# pylint: disable=wildcard-import,unused-wildcard-import +if sys.version_info < (2, 7): + try: + # Need unittest2 on python2.6 + from unittest2 import * # noqa: F401, pylint: disable=unused-import + except ImportError: + print('You need unittest2 installed on python2.6.x to run tests') +else: + from unittest import * # noqa: F401, pylint: disable=unused-import diff --git a/tests/unit/plugins/modules/routeros_module.py b/tests/unit/plugins/modules/routeros_module.py index 4786da6..0ec44f7 100644 --- a/tests/unit/plugins/modules/routeros_module.py +++ b/tests/unit/plugins/modules/routeros_module.py @@ -9,7 +9,7 @@ __metaclass__ = type import os import json -from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase +from ansible_collections.community.routeros.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase fixture_path = os.path.join(os.path.dirname(__file__), 'fixtures') diff --git a/tests/unit/plugins/modules/test_api.py b/tests/unit/plugins/modules/test_api.py index 9a03825..4cfdeef 100644 --- a/tests/unit/plugins/modules/test_api.py +++ b/tests/unit/plugins/modules/test_api.py @@ -6,10 +6,9 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import patch, MagicMock -from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import set_module_args, AnsibleExitJson, AnsibleFailJson, ModuleTestCase - +from ansible_collections.community.routeros.tests.unit.compat.mock import patch, MagicMock from ansible_collections.community.routeros.tests.unit.plugins.modules.fake_api import FakeLibRouterosError, Key, Or, fake_ros_api +from ansible_collections.community.routeros.tests.unit.plugins.modules.utils import set_module_args, AnsibleExitJson, AnsibleFailJson, ModuleTestCase from ansible_collections.community.routeros.plugins.modules import api @@ -35,8 +34,8 @@ class TestRouterosApiModule(ModuleTestCase): def test_module_fail_when_required_args_missing(self): with self.assertRaises(AnsibleFailJson) as exc: - with set_module_args({}): - self.module.main() + set_module_args({}) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -44,8 +43,8 @@ class TestRouterosApiModule(ModuleTestCase): @patch('ansible_collections.community.routeros.plugins.modules.api.ROS_api_module.api_add_path', new=fake_ros_api.path) def test_api_path(self): with self.assertRaises(AnsibleExitJson) as exc: - with set_module_args(self.config_module_args.copy()): - self.module.main() + set_module_args(self.config_module_args.copy()) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -55,8 +54,8 @@ class TestRouterosApiModule(ModuleTestCase): with self.assertRaises(AnsibleExitJson) as exc: module_args = self.config_module_args.copy() module_args['add'] = "name=unit_test_brige" - with set_module_args(module_args): - self.module.main() + set_module_args(module_args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -66,8 +65,8 @@ class TestRouterosApiModule(ModuleTestCase): with self.assertRaises(AnsibleFailJson) as exc: module_args = self.config_module_args.copy() module_args['add'] = "name=unit_test_brige_exist" - with set_module_args(module_args): - self.module.main() + set_module_args(module_args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -78,8 +77,8 @@ class TestRouterosApiModule(ModuleTestCase): with self.assertRaises(AnsibleExitJson) as exc: module_args = self.config_module_args.copy() module_args['remove'] = "*A1" - with set_module_args(module_args): - self.module.main() + set_module_args(module_args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -89,8 +88,8 @@ class TestRouterosApiModule(ModuleTestCase): with self.assertRaises(AnsibleFailJson) as exc: module_args = self.config_module_args.copy() module_args['remove'] = "*A2" - with set_module_args(module_args): - self.module.main() + set_module_args(module_args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -101,8 +100,8 @@ class TestRouterosApiModule(ModuleTestCase): with self.assertRaises(AnsibleExitJson) as exc: module_args = self.config_module_args.copy() module_args['cmd'] = "add name=unit_test_brige_arbitrary" - with set_module_args(module_args): - self.module.main() + set_module_args(module_args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -112,8 +111,8 @@ class TestRouterosApiModule(ModuleTestCase): with self.assertRaises(AnsibleFailJson) as exc: module_args = self.config_module_args.copy() module_args['cmd'] = "add NONE_EXIST=unit_test_brige_arbitrary" - with set_module_args(module_args): - self.module.main() + set_module_args(module_args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -124,8 +123,8 @@ class TestRouterosApiModule(ModuleTestCase): with self.assertRaises(AnsibleExitJson) as exc: module_args = self.config_module_args.copy() module_args['update'] = ".id=*A1 name=unit_test_brige" - with set_module_args(module_args): - self.module.main() + set_module_args(module_args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -135,8 +134,8 @@ class TestRouterosApiModule(ModuleTestCase): with self.assertRaises(AnsibleFailJson) as exc: module_args = self.config_module_args.copy() module_args['update'] = ".id=*A2 name=unit_test_brige" - with set_module_args(module_args): - self.module.main() + set_module_args(module_args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -147,8 +146,8 @@ class TestRouterosApiModule(ModuleTestCase): with self.assertRaises(AnsibleExitJson) as exc: module_args = self.config_module_args.copy() module_args['query'] = ".id name" - with set_module_args(module_args): - self.module.main() + set_module_args(module_args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -163,8 +162,8 @@ class TestRouterosApiModule(ModuleTestCase): with self.assertRaises(AnsibleExitJson) as exc: module_args = self.config_module_args.copy() module_args['query'] = ".id other" - with set_module_args(module_args): - self.module.main() + set_module_args(module_args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -175,8 +174,8 @@ class TestRouterosApiModule(ModuleTestCase): with self.assertRaises(AnsibleExitJson) as exc: module_args = self.config_module_args.copy() module_args['query'] = ".id name WHERE name == dummy_bridge_A2" - with set_module_args(module_args): - self.module.main() + set_module_args(module_args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -189,8 +188,8 @@ class TestRouterosApiModule(ModuleTestCase): with self.assertRaises(AnsibleExitJson) as exc: module_args = self.config_module_args.copy() module_args['query'] = ".id name WHERE name != dummy_bridge_A2" - with set_module_args(module_args): - self.module.main() + set_module_args(module_args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -205,8 +204,8 @@ class TestRouterosApiModule(ModuleTestCase): module_args['extended_query'] = { 'attributes': ['.id', 'name'], } - with set_module_args(module_args): - self.module.main() + set_module_args(module_args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -223,8 +222,8 @@ class TestRouterosApiModule(ModuleTestCase): module_args['extended_query'] = { 'attributes': ['.id', 'other'], } - with set_module_args(module_args): - self.module.main() + set_module_args(module_args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -244,8 +243,8 @@ class TestRouterosApiModule(ModuleTestCase): }, ], } - with set_module_args(module_args): - self.module.main() + set_module_args(module_args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -267,8 +266,8 @@ class TestRouterosApiModule(ModuleTestCase): }, ], } - with set_module_args(module_args): - self.module.main() + set_module_args(module_args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -299,8 +298,8 @@ class TestRouterosApiModule(ModuleTestCase): }, ], } - with set_module_args(module_args): - self.module.main() + set_module_args(module_args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) diff --git a/tests/unit/plugins/modules/test_api_facts.py b/tests/unit/plugins/modules/test_api_facts.py index 7b019f9..64985f8 100644 --- a/tests/unit/plugins/modules/test_api_facts.py +++ b/tests/unit/plugins/modules/test_api_facts.py @@ -6,10 +6,9 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import patch, MagicMock -from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import set_module_args, AnsibleExitJson, AnsibleFailJson, ModuleTestCase - +from ansible_collections.community.routeros.tests.unit.compat.mock import patch, MagicMock from ansible_collections.community.routeros.tests.unit.plugins.modules.fake_api import FakeLibRouterosError, Key, fake_ros_api +from ansible_collections.community.routeros.tests.unit.plugins.modules.utils import set_module_args, AnsibleExitJson, AnsibleFailJson, ModuleTestCase from ansible_collections.community.routeros.plugins.modules import api_facts @@ -438,8 +437,8 @@ class TestRouterosApiFactsModule(ModuleTestCase): def test_module_fail_when_required_args_missing(self): with self.assertRaises(AnsibleFailJson) as exc: - with set_module_args({}): - self.module.main() + set_module_args({}) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -448,8 +447,8 @@ class TestRouterosApiFactsModule(ModuleTestCase): with self.assertRaises(AnsibleFailJson) as exc: module_args = self.config_module_args.copy() module_args['gather_subset'] = ['!foobar'] - with set_module_args(module_args): - self.module.main() + set_module_args(module_args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -457,8 +456,8 @@ class TestRouterosApiFactsModule(ModuleTestCase): def test_full_run(self): with self.assertRaises(AnsibleExitJson) as exc: - with set_module_args(self.config_module_args.copy()): - self.module.main() + set_module_args(self.config_module_args.copy()) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) diff --git a/tests/unit/plugins/modules/test_api_find_and_modify.py b/tests/unit/plugins/modules/test_api_find_and_modify.py index e700f9d..2f47af4 100644 --- a/tests/unit/plugins/modules/test_api_find_and_modify.py +++ b/tests/unit/plugins/modules/test_api_find_and_modify.py @@ -6,12 +6,11 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import patch, MagicMock -from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import set_module_args, AnsibleExitJson, AnsibleFailJson, ModuleTestCase - +from ansible_collections.community.routeros.tests.unit.compat.mock import patch, MagicMock from ansible_collections.community.routeros.tests.unit.plugins.modules.fake_api import ( FakeLibRouterosError, fake_ros_api, massage_expected_result_data, create_fake_path, ) +from ansible_collections.community.routeros.tests.unit.plugins.modules.utils import set_module_args, AnsibleExitJson, AnsibleFailJson, ModuleTestCase from ansible_collections.community.routeros.plugins.modules import api_find_and_modify @@ -94,52 +93,6 @@ START_IP_FIREWALL_FILTER = [ START_IP_FIREWALL_FILTER_OLD_DATA = massage_expected_result_data(START_IP_FIREWALL_FILTER, ('ip', 'firewall', 'filter'), keep_all=True) -START_IP_SERVICE = [ - # I removed all entryes not for 'api' and 'api-ssl' - { - "certificate": None, - "tls-version": None, - ".id": "*7", - "address": "", - "disabled": True, - "dynamic": False, - "invalid": True, - "name": "api", - "port": 8728, - "proto": "tcp", - "vrf": "main" - }, - { - ".id": "*9", - "address": "192.168.1.0/24", - "certificate": "mycert", - "dynamic": False, - "invalid": False, - "name": "api-ssl", - "port": 8729, - "proto": "tcp", - "tls-version": "only-1.2", - "vrf": "main" - }, - { - "address": None, - "certificate": None, - "max-sessions": None, - "tls-version": None, - ".id": "*13", - "connection": True, - "dynamic": True, - "invalid": False, - "local": "192.168.1.1", - "name": "api-ssl", - "port": 8729, - "proto": "tcp", - "remote": "192.168.1.2:12346" - } -] - -START_IP_SERVICE_OLD_DATA = massage_expected_result_data(START_IP_SERVICE, ('ip', 'service'), keep_all=True) - class TestRouterosApiFindAndModifyModule(ModuleTestCase): @@ -164,8 +117,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase): def test_module_fail_when_required_args_missing(self): with self.assertRaises(AnsibleFailJson) as exc: - with set_module_args({}): - self.module.main() + set_module_args({}) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -183,8 +136,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase): 'comment': 'bar', }, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -202,8 +155,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase): 'comment': 'bar', }, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -220,8 +173,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase): '!comment': None, }, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -237,8 +190,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase): '!comment': 'gone', }, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -259,8 +212,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase): }, 'require_matches_min': 10, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -281,8 +234,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase): }, 'require_matches_min': 10, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -303,8 +256,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase): }, 'require_matches_max': 1, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -324,8 +277,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase): 'name': 'bam', }, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -350,8 +303,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase): 'require_matches_min': 2, 'allow_no_matches': True, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -372,8 +325,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase): 'values': { }, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -396,8 +349,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase): 'comment': None, }, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -421,8 +374,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase): }, '_ansible_diff': True, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -497,8 +450,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase): 'comment': None, }, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -549,8 +502,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase): 'comment': '', }, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -600,8 +553,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase): '!comment': None, }, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -653,8 +606,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase): '!connection-state': None, }, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -667,8 +620,6 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase): 'comment': 'defconf', 'protocol': 'icmp', 'disabled': False, - 'log': False, - 'log-prefix': '', }, { '.id': '*3', @@ -676,8 +627,6 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase): 'chain': 'input', 'comment': 'defconf', 'disabled': False, - 'log': False, - 'log-prefix': '', }, { '.id': '*4', @@ -685,8 +634,6 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase): 'chain': 'input', 'comment': 'defconf', 'disabled': False, - 'log': False, - 'log-prefix': '', }, { '.id': '*7', @@ -695,8 +642,6 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase): 'comment': 'defconf', 'disabled': False, 'in-interface': 'wan', - 'log': False, - 'log-prefix': '', }, { '.id': '*8', @@ -705,8 +650,6 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase): 'comment': 'defconf', 'connection-state': 'established', 'disabled': False, - 'log': False, - 'log-prefix': '', }, { '.id': '*9', @@ -715,8 +658,6 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase): 'comment': 'defconf', 'connection-state': 'related', 'disabled': False, - 'log': False, - 'log-prefix': '', }, { '.id': '*A', @@ -725,35 +666,7 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase): 'comment': 'defconf', 'connection-status': 'invalid', 'disabled': False, - 'log': False, - 'log-prefix': '', }, ]) self.assertEqual(result['match_count'], 3) self.assertEqual(result['modify_count'], 2) - - @patch('ansible_collections.community.routeros.plugins.modules.api_find_and_modify.compose_api_path', - new=create_fake_path(('ip', 'service'), START_IP_SERVICE)) - def test_change_ignore_dynamic(self): - with self.assertRaises(AnsibleExitJson) as exc: - args = self.config_module_args.copy() - args.update({ - 'path': 'ip service', - 'find': { - 'name': 'api-ssl', - }, - 'values': { - 'address': '192.168.1.0/24', - }, - 'ignore_dynamic': True, - '_ansible_diff': True, - }) - with set_module_args(args): - self.module.main() - - result = exc.exception.args[0] - self.assertEqual(result['changed'], False) - self.assertEqual(result['old_data'], [entry for entry in START_IP_SERVICE_OLD_DATA if entry["dynamic"] is False]) - self.assertEqual(result['new_data'], [entry for entry in START_IP_SERVICE_OLD_DATA if entry["dynamic"] is False]) - self.assertEqual(result['match_count'], 1) - self.assertEqual(result['modify_count'], 0) diff --git a/tests/unit/plugins/modules/test_api_info.py b/tests/unit/plugins/modules/test_api_info.py index 967a854..1c9d014 100644 --- a/tests/unit/plugins/modules/test_api_info.py +++ b/tests/unit/plugins/modules/test_api_info.py @@ -6,12 +6,11 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import patch, MagicMock -from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import set_module_args, AnsibleExitJson, AnsibleFailJson, ModuleTestCase - +from ansible_collections.community.routeros.tests.unit.compat.mock import patch, MagicMock from ansible_collections.community.routeros.tests.unit.plugins.modules.fake_api import ( FAKE_ROS_VERSION, FakeLibRouterosError, Key, fake_ros_api, ) +from ansible_collections.community.routeros.tests.unit.plugins.modules.utils import set_module_args, AnsibleExitJson, AnsibleFailJson, ModuleTestCase from ansible_collections.community.routeros.plugins.modules import api_info @@ -42,8 +41,8 @@ class TestRouterosApiInfoModule(ModuleTestCase): def test_module_fail_when_required_args_missing(self): with self.assertRaises(AnsibleFailJson) as exc: - with set_module_args({}): - self.module.main() + set_module_args({}) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -54,8 +53,8 @@ class TestRouterosApiInfoModule(ModuleTestCase): args.update({ 'path': 'something invalid' }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -69,8 +68,8 @@ class TestRouterosApiInfoModule(ModuleTestCase): args.update({ 'path': 'ip dns static' }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -94,8 +93,8 @@ class TestRouterosApiInfoModule(ModuleTestCase): args.update({ 'path': 'caps-man aaa', }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -123,8 +122,8 @@ class TestRouterosApiInfoModule(ModuleTestCase): 'path': 'caps-man aaa', 'hide_defaults': False, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -156,8 +155,8 @@ class TestRouterosApiInfoModule(ModuleTestCase): 'path': 'caps-man aaa', 'unfiltered': True, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -190,8 +189,8 @@ class TestRouterosApiInfoModule(ModuleTestCase): 'path': 'ip firewall filter', 'handle_disabled': 'exclamation', }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -279,8 +278,8 @@ class TestRouterosApiInfoModule(ModuleTestCase): 'path': 'ip firewall filter', 'handle_disabled': 'null-value', }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -368,8 +367,8 @@ class TestRouterosApiInfoModule(ModuleTestCase): 'path': 'ip firewall filter', 'handle_disabled': 'omit', }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -403,8 +402,8 @@ class TestRouterosApiInfoModule(ModuleTestCase): 'handle_disabled': 'omit', 'include_dynamic': True, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -461,8 +460,8 @@ class TestRouterosApiInfoModule(ModuleTestCase): 'path': 'interface list', 'handle_disabled': 'omit', }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -514,8 +513,8 @@ class TestRouterosApiInfoModule(ModuleTestCase): 'handle_disabled': 'omit', 'include_builtin': True, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -606,8 +605,8 @@ class TestRouterosApiInfoModule(ModuleTestCase): 'path': 'ip dhcp-server lease', 'handle_disabled': 'omit', }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -689,8 +688,8 @@ class TestRouterosApiInfoModule(ModuleTestCase): args.update({ 'path': 'interface gre', }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -777,8 +776,8 @@ class TestRouterosApiInfoModule(ModuleTestCase): 'handle_disabled': 'omit', 'hide_defaults': False, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -848,8 +847,8 @@ class TestRouterosApiInfoModule(ModuleTestCase): 'handle_disabled': 'omit', 'restrict': [], }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -900,8 +899,8 @@ class TestRouterosApiInfoModule(ModuleTestCase): 'values': ['forward'], }], }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -971,8 +970,8 @@ class TestRouterosApiInfoModule(ModuleTestCase): }, ], }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) diff --git a/tests/unit/plugins/modules/test_api_modify.py b/tests/unit/plugins/modules/test_api_modify.py index 6edc046..f4aa777 100644 --- a/tests/unit/plugins/modules/test_api_modify.py +++ b/tests/unit/plugins/modules/test_api_modify.py @@ -6,12 +6,11 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import patch, MagicMock -from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import set_module_args, AnsibleExitJson, AnsibleFailJson, ModuleTestCase - +from ansible_collections.community.routeros.tests.unit.compat.mock import patch, MagicMock from ansible_collections.community.routeros.tests.unit.plugins.modules.fake_api import ( FAKE_ROS_VERSION, FakeLibRouterosError, fake_ros_api, massage_expected_result_data, create_fake_path, ) +from ansible_collections.community.routeros.tests.unit.plugins.modules.utils import set_module_args, AnsibleExitJson, AnsibleFailJson, ModuleTestCase from ansible_collections.community.routeros.plugins.modules import api_modify @@ -319,8 +318,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): def test_module_fail_when_required_args_missing(self): with self.assertRaises(AnsibleFailJson) as exc: - with set_module_args({}): - self.module.main() + set_module_args({}) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -332,8 +331,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'path': 'something invalid', 'data': [], }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -349,8 +348,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'foo': 'bar', }], }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -367,8 +366,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): '!comment': None, }], }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -384,8 +383,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): '!disabled': None, }], }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -401,8 +400,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): '!comment': 'foo', }], }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -417,8 +416,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'name': None, }], }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -433,8 +432,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'interface': 'eth0', }], }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -449,8 +448,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'address': '192.168.88.1', }], }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -467,8 +466,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'address': '192.168.88.1', }], }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['failed'], True) @@ -499,8 +498,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): }, ], }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -534,8 +533,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'handle_absent_entries': 'remove', 'handle_entries_content': 'remove', }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -557,8 +556,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): }, ], }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -592,8 +591,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): }, ], }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -657,8 +656,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): }, ], }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -723,8 +722,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): ], '_ansible_check_mode': True, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -786,8 +785,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'handle_absent_entries': 'remove', 'handle_entries_content': 'remove', }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -845,8 +844,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'handle_entries_content': 'remove', '_ansible_check_mode': True, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -904,8 +903,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'handle_absent_entries': 'remove', 'handle_entries_content': 'remove', }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -964,8 +963,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'handle_entries_content': 'remove', '_ansible_check_mode': True, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -1018,8 +1017,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'handle_absent_entries': 'remove', 'handle_entries_content': 'remove', }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -1078,8 +1077,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'handle_entries_content': 'remove', '_ansible_check_mode': True, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -1129,8 +1128,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'handle_absent_entries': 'remove', 'handle_entries_content': 'remove', }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -1165,8 +1164,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'handle_entries_content': 'remove', '_ansible_check_mode': True, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -1213,8 +1212,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'handle_entries_content': 'remove', 'ensure_order': True, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -1286,8 +1285,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'ensure_order': True, '_ansible_check_mode': True, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -1341,8 +1340,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): }, ], }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -1364,8 +1363,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): ], 'handle_entries_content': 'remove', }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -1387,8 +1386,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): }, ], }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -1427,8 +1426,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): ], '_ansible_check_mode': True, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -1467,8 +1466,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): ], 'handle_entries_content': 'remove', }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -1508,8 +1507,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'handle_entries_content': 'remove', '_ansible_check_mode': True, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -1552,8 +1551,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): }, ], }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -1588,8 +1587,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'handle_absent_entries': 'remove', 'handle_entries_content': 'remove', }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -1622,8 +1621,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'handle_absent_entries': 'remove', 'handle_entries_content': 'remove', }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -1677,8 +1676,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'handle_entries_content': 'remove', '_ansible_check_mode': True, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -1730,8 +1729,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'handle_entries_content': 'remove', 'ensure_order': True, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -1786,8 +1785,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'ensure_order': True, '_ansible_check_mode': True, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -1848,8 +1847,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'handle_entries_content': 'remove', 'ensure_order': True, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -1880,8 +1879,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'handle_entries_content': 'remove', 'ensure_order': True, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -1912,8 +1911,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'handle_entries_content': 'remove', 'ensure_order': True, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], True) @@ -1970,8 +1969,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'handle_absent_entries': 'remove', 'ensure_order': True, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) @@ -2006,8 +2005,8 @@ class TestRouterosApiModifyModule(ModuleTestCase): 'handle_absent_entries': 'remove', 'ensure_order': True, }) - with set_module_args(args): - self.module.main() + set_module_args(args) + self.module.main() result = exc.exception.args[0] self.assertEqual(result['changed'], False) diff --git a/tests/unit/plugins/modules/test_command.py b/tests/unit/plugins/modules/test_command.py index 06153f0..3fc5865 100644 --- a/tests/unit/plugins/modules/test_command.py +++ b/tests/unit/plugins/modules/test_command.py @@ -8,10 +8,9 @@ __metaclass__ = type import json -from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import patch -from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import set_module_args - +from ansible_collections.community.routeros.tests.unit.compat.mock import patch from ansible_collections.community.routeros.plugins.modules import command +from ansible_collections.community.routeros.tests.unit.plugins.modules.utils import set_module_args from .routeros_module import TestRouterosModule, load_fixture @@ -48,54 +47,54 @@ class TestRouterosCommandModule(TestRouterosModule): self.run_commands.side_effect = load_from_file def test_command_simple(self): - with set_module_args(dict(commands=['/system resource print'])): - result = self.execute_module(changed=True) + set_module_args(dict(commands=['/system resource print'])) + result = self.execute_module(changed=True) self.assertEqual(len(result['stdout']), 1) self.assertTrue('platform: "MikroTik"' in result['stdout'][0]) def test_command_multiple(self): - with set_module_args(dict(commands=['/system resource print', '/system resource print'])): - result = self.execute_module(changed=True) + set_module_args(dict(commands=['/system resource print', '/system resource print'])) + result = self.execute_module(changed=True) self.assertEqual(len(result['stdout']), 2) self.assertTrue('platform: "MikroTik"' in result['stdout'][0]) def test_command_wait_for(self): wait_for = 'result[0] contains "MikroTik"' - with set_module_args(dict(commands=['/system resource print'], wait_for=wait_for)): - self.execute_module(changed=True) + set_module_args(dict(commands=['/system resource print'], wait_for=wait_for)) + self.execute_module(changed=True) def test_command_wait_for_fails(self): wait_for = 'result[0] contains "test string"' - with set_module_args(dict(commands=['/system resource print'], wait_for=wait_for)): - self.execute_module(failed=True) + set_module_args(dict(commands=['/system resource print'], wait_for=wait_for)) + self.execute_module(failed=True) self.assertEqual(self.run_commands.call_count, 10) def test_command_retries(self): wait_for = 'result[0] contains "test string"' - with set_module_args(dict(commands=['/system resource print'], wait_for=wait_for, retries=2)): - self.execute_module(failed=True) + set_module_args(dict(commands=['/system resource print'], wait_for=wait_for, retries=2)) + self.execute_module(failed=True) self.assertEqual(self.run_commands.call_count, 2) def test_command_match_any(self): wait_for = ['result[0] contains "MikroTik"', 'result[0] contains "test string"'] - with set_module_args(dict(commands=['/system resource print'], wait_for=wait_for, match='any')): - self.execute_module(changed=True) + set_module_args(dict(commands=['/system resource print'], wait_for=wait_for, match='any')) + self.execute_module(changed=True) def test_command_match_all(self): wait_for = ['result[0] contains "MikroTik"', 'result[0] contains "RB1100"'] - with set_module_args(dict(commands=['/system resource print'], wait_for=wait_for, match='all')): - self.execute_module(changed=True) + set_module_args(dict(commands=['/system resource print'], wait_for=wait_for, match='all')) + self.execute_module(changed=True) def test_command_match_all_failure(self): wait_for = ['result[0] contains "MikroTik"', 'result[0] contains "test string"'] commands = ['/system resource print', '/system resource print'] - with set_module_args(dict(commands=commands, wait_for=wait_for, match='all')): - self.execute_module(failed=True) + set_module_args(dict(commands=commands, wait_for=wait_for, match='all')) + self.execute_module(failed=True) def test_command_wait_for_2(self): wait_for = 'result[0] contains "wireless"' - with set_module_args(dict(commands=['/system package print'], wait_for=wait_for)): - self.execute_module(changed=True) + set_module_args(dict(commands=['/system package print'], wait_for=wait_for)) + self.execute_module(changed=True) diff --git a/tests/unit/plugins/modules/test_facts.py b/tests/unit/plugins/modules/test_facts.py index 918f378..322fdda 100644 --- a/tests/unit/plugins/modules/test_facts.py +++ b/tests/unit/plugins/modules/test_facts.py @@ -6,10 +6,9 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import patch -from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import set_module_args - +from ansible_collections.community.routeros.tests.unit.compat.mock import patch from ansible_collections.community.routeros.plugins.modules import facts +from ansible_collections.community.routeros.tests.unit.plugins.modules.utils import set_module_args from .routeros_module import TestRouterosModule, load_fixture @@ -40,8 +39,8 @@ class TestRouterosFactsModule(TestRouterosModule): self.run_commands.side_effect = load_from_file def test_facts_default(self): - with set_module_args(dict(gather_subset='default')): - result = self.execute_module() + set_module_args(dict(gather_subset='default')) + result = self.execute_module() self.assertEqual( result['ansible_facts']['ansible_net_hostname'], 'MikroTik' ) @@ -62,8 +61,8 @@ class TestRouterosFactsModule(TestRouterosModule): ) def test_facts_hardware(self): - with set_module_args(dict(gather_subset='hardware')): - result = self.execute_module() + set_module_args(dict(gather_subset='hardware')) + result = self.execute_module() self.assertEqual( result['ansible_facts']['ansible_net_spacefree_mb'], 64921.6 ) @@ -78,8 +77,8 @@ class TestRouterosFactsModule(TestRouterosModule): ) def test_facts_config(self): - with set_module_args(dict(gather_subset='config')): - result = self.execute_module() + set_module_args(dict(gather_subset='config')) + result = self.execute_module() self.assertIsInstance( result['ansible_facts']['ansible_net_config'], str ) @@ -89,8 +88,8 @@ class TestRouterosFactsModule(TestRouterosModule): ) def test_facts_interfaces(self): - with set_module_args(dict(gather_subset='interfaces')): - result = self.execute_module() + set_module_args(dict(gather_subset='interfaces')) + result = self.execute_module() self.assertIn( result['ansible_facts']['ansible_net_all_ipv4_addresses'][0], ['10.37.129.3', '10.37.0.0', '192.168.88.1'] ) @@ -119,8 +118,8 @@ class TestRouterosFactsModule(TestRouterosModule): self.assertEqual(result, None) def test_facts_routing(self): - with set_module_args(dict(gather_subset='routing')): - result = self.execute_module() + set_module_args(dict(gather_subset='routing')) + result = self.execute_module() self.assertIn( result['ansible_facts']['ansible_net_bgp_peer']['iBGP_BRAS.TYRMA']['name'], ['iBGP_BRAS.TYRMA'] ) diff --git a/tests/unit/plugins/modules/utils.py b/tests/unit/plugins/modules/utils.py new file mode 100644 index 0000000..419eef1 --- /dev/null +++ b/tests/unit/plugins/modules/utils.py @@ -0,0 +1,54 @@ +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import json + +from ansible_collections.community.routeros.tests.unit.compat import unittest +from ansible_collections.community.routeros.tests.unit.compat.mock import patch +from ansible.module_utils import basic +from ansible.module_utils.common.text.converters import to_bytes + + +def set_module_args(args): + if '_ansible_remote_tmp' not in args: + args['_ansible_remote_tmp'] = '/tmp' + if '_ansible_keep_remote_files' not in args: + args['_ansible_keep_remote_files'] = False + + args = json.dumps({'ANSIBLE_MODULE_ARGS': args}) + basic._ANSIBLE_ARGS = to_bytes(args) + + +class AnsibleExitJson(Exception): + pass + + +class AnsibleFailJson(Exception): + pass + + +def exit_json(*args, **kwargs): + if 'changed' not in kwargs: + kwargs['changed'] = False + raise AnsibleExitJson(kwargs) + + +def fail_json(*args, **kwargs): + kwargs['failed'] = True + raise AnsibleFailJson(kwargs) + + +class ModuleTestCase(unittest.TestCase): + + def setUp(self): + self.mock_module = patch.multiple(basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json) + self.mock_module.start() + self.mock_sleep = patch('time.sleep') + self.mock_sleep.start() + set_module_args({}) + self.addCleanup(self.mock_module.stop) + self.addCleanup(self.mock_sleep.stop) diff --git a/tests/unit/requirements.yml b/tests/unit/requirements.yml index 107fe12..6a22736 100644 --- a/tests/unit/requirements.yml +++ b/tests/unit/requirements.yml @@ -4,4 +4,4 @@ # SPDX-License-Identifier: GPL-3.0-or-later collections: - - community.internal_test_tools +- ansible.netcommon diff --git a/tests/update-docs.py b/update-docs.py old mode 100644 new mode 100755 similarity index 62% rename from tests/update-docs.py rename to update-docs.py index 5f28f8e..05a9ee7 --- a/tests/update-docs.py +++ b/update-docs.py @@ -11,6 +11,9 @@ Updates DOCUMENTATION of modules using module_utils._api_data with the correct l import sys +# Ensure that we can import things from ansible_collections +sys.path.append('../../..') + from ansible_collections.community.routeros.plugins.module_utils._api_data import ( PATHS, join_path, @@ -23,34 +26,24 @@ MODULES = [ ] -def update_file(file: str, begin_line: str, end_line: str, choice_line: str, path_choices: list[str]) -> bool: +def update_file(file, begin_line, end_line, choice_line, path_choices): with open(file, 'r', encoding='utf-8') as f: lines = f.read().splitlines() begin_index = lines.index(begin_line) end_index = lines.index(end_line, begin_index + 1) new_lines = lines[:begin_index + 1] + [choice_line.format(choice=choice) for choice in path_choices] + lines[end_index:] - if lines == new_lines: - return False - print(f'{file} has been updated') - with open(file, 'w', encoding='utf-8') as f: - f.write('\n'.join(new_lines) + '\n') - return True + if lines != new_lines: + print(f'{file} has been updated') + with open(file, 'w', encoding='utf-8') as f: + f.write('\n'.join(new_lines) + '\n') -def main(args: list[str]) -> int: +def main(): path_choices = sorted([join_path(path) for path, path_info in PATHS.items() if path_info.fully_understood]) - changes = False for file in MODULES: - changes |= update_file(file, ' # BEGIN PATH LIST', ' # END PATH LIST', ' - {choice}', path_choices) - - lint = "--lint" in args - if not lint or not changes: - return 0 - - print("Run 'nox -Re update-docs'!") - return 1 + update_file(file, ' # BEGIN PATH LIST', ' # END PATH LIST', ' - {choice}', path_choices) if __name__ == '__main__': - sys.exit(main(sys.argv[1:])) + main()