diff --git a/.ansible-lint b/.ansible-lint index 426ba1149..61faac655 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -32,7 +32,7 @@ exclude_paths: # requirements, like avoiding python identifiers. To disable add `var-naming` # to skip_list. # var_naming_pattern: "^[a-z_][a-z0-9_]*$" -var_naming_pattern: "^checkmk_(server|agent|var)_.*$" +var_naming_pattern: "^(__)?checkmk_(server|agent|var)_.*$" use_default_rules: true # Load custom rules from this specific folder diff --git a/.github/workflows/ans-int-test-activation.yaml b/.github/workflows/ans-int-test-activation.yaml index c0dc91265..ea58c6ee2 100644 --- a/.github/workflows/ans-int-test-activation.yaml +++ b/.github/workflows/ans-int-test-activation.yaml @@ -29,6 +29,7 @@ on: push: paths: - 'plugins/modules/activation.py' + - '.github/workflows/ans-int-test-activation.yaml' jobs: diff --git a/.github/workflows/ans-int-test-bakery.yaml b/.github/workflows/ans-int-test-bakery.yaml index a9151f67f..bbf84d097 100644 --- a/.github/workflows/ans-int-test-bakery.yaml +++ b/.github/workflows/ans-int-test-bakery.yaml @@ -28,6 +28,7 @@ on: push: paths: - 'plugins/modules/bakery.py' + - '.github/workflows/ans-int-test-bakery.yaml' jobs: diff --git a/.github/workflows/ans-int-test-contact_group.yaml b/.github/workflows/ans-int-test-contact_group.yaml index d1227d3ba..d06d94bd1 100644 --- a/.github/workflows/ans-int-test-contact_group.yaml +++ b/.github/workflows/ans-int-test-contact_group.yaml @@ -28,6 +28,7 @@ on: push: paths: - 'plugins/modules/contact_group.py' + - '.github/workflows/ans-int-test-contact_group.yaml' jobs: diff --git a/.github/workflows/ans-int-test-discovery.yaml b/.github/workflows/ans-int-test-discovery.yaml index 609cadf03..d14c89704 100644 --- a/.github/workflows/ans-int-test-discovery.yaml +++ b/.github/workflows/ans-int-test-discovery.yaml @@ -28,6 +28,7 @@ on: push: paths: - 'plugins/modules/discovery.py' + - '.github/workflows/ans-int-test-discovery.yaml' jobs: diff --git a/.github/workflows/ans-int-test-downtime.yaml b/.github/workflows/ans-int-test-downtime.yaml index eeafbe5a3..1b238390b 100644 --- a/.github/workflows/ans-int-test-downtime.yaml +++ b/.github/workflows/ans-int-test-downtime.yaml @@ -28,6 +28,7 @@ on: push: paths: - 'plugins/modules/downtime.py' + - '.github/workflows/ans-int-test-downtime.yaml' jobs: diff --git a/.github/workflows/ans-int-test-folder.yaml b/.github/workflows/ans-int-test-folder.yaml index 6bedab035..a94e9c164 100644 --- a/.github/workflows/ans-int-test-folder.yaml +++ b/.github/workflows/ans-int-test-folder.yaml @@ -28,6 +28,7 @@ on: push: paths: - 'plugins/modules/folder.py' + - '.github/workflows/ans-int-test-folder.yaml' jobs: diff --git a/.github/workflows/ans-int-test-host.yaml b/.github/workflows/ans-int-test-host.yaml index d64921454..c9d136233 100644 --- a/.github/workflows/ans-int-test-host.yaml +++ b/.github/workflows/ans-int-test-host.yaml @@ -28,6 +28,7 @@ on: push: paths: - 'plugins/modules/host.py' + - '.github/workflows/ans-int-test-host.yaml' jobs: diff --git a/.github/workflows/ans-int-test-host_group.yaml b/.github/workflows/ans-int-test-host_group.yaml index 98b3ee54f..1f77fe1aa 100644 --- a/.github/workflows/ans-int-test-host_group.yaml +++ b/.github/workflows/ans-int-test-host_group.yaml @@ -28,6 +28,7 @@ on: push: paths: - 'plugins/modules/host_group.py' + - '.github/workflows/ans-int-test-host_group.yaml' jobs: diff --git a/.github/workflows/ans-int-test-lkp-bakery.yaml b/.github/workflows/ans-int-test-lkp-bakery.yaml index 25886b102..e991625cb 100644 --- a/.github/workflows/ans-int-test-lkp-bakery.yaml +++ b/.github/workflows/ans-int-test-lkp-bakery.yaml @@ -28,6 +28,7 @@ on: push: paths: - 'plugins/lookup/bakery.py' + - '.github/workflows/ans-int-test-lkp-bakery.yaml' jobs: diff --git a/.github/workflows/ans-int-test-lkp-folder.yaml b/.github/workflows/ans-int-test-lkp-folder.yaml index f8831f6a8..8c62396bf 100644 --- a/.github/workflows/ans-int-test-lkp-folder.yaml +++ b/.github/workflows/ans-int-test-lkp-folder.yaml @@ -26,6 +26,7 @@ on: push: paths: - 'plugins/lookup/folder.py' + - '.github/workflows/ans-int-test-lkp-folder.yaml' jobs: diff --git a/.github/workflows/ans-int-test-lkp-folders.yaml b/.github/workflows/ans-int-test-lkp-folders.yaml index 95b75cf14..315f31b35 100644 --- a/.github/workflows/ans-int-test-lkp-folders.yaml +++ b/.github/workflows/ans-int-test-lkp-folders.yaml @@ -26,6 +26,7 @@ on: push: paths: - 'plugins/lookup/folders.py' + - '.github/workflows/ans-int-test-lkp-folders.yaml' jobs: diff --git a/.github/workflows/ans-int-test-lkp-host.yaml b/.github/workflows/ans-int-test-lkp-host.yaml index 1693f0157..3546da5ef 100644 --- a/.github/workflows/ans-int-test-lkp-host.yaml +++ b/.github/workflows/ans-int-test-lkp-host.yaml @@ -26,6 +26,7 @@ on: push: paths: - 'plugins/lookup/host.py' + - '.github/workflows/ans-int-test-lkp-host.yaml' jobs: diff --git a/.github/workflows/ans-int-test-lkp-hosts.yaml b/.github/workflows/ans-int-test-lkp-hosts.yaml index 9ad9f9be5..7541547a4 100644 --- a/.github/workflows/ans-int-test-lkp-hosts.yaml +++ b/.github/workflows/ans-int-test-lkp-hosts.yaml @@ -26,6 +26,7 @@ on: push: paths: - 'plugins/lookup/hosts.py' + - '.github/workflows/ans-int-test-lkp-hosts.yaml' jobs: diff --git a/.github/workflows/ans-int-test-lkp-rules.yaml b/.github/workflows/ans-int-test-lkp-rules.yaml index 7556c7c98..02aa4c0ef 100644 --- a/.github/workflows/ans-int-test-lkp-rules.yaml +++ b/.github/workflows/ans-int-test-lkp-rules.yaml @@ -28,6 +28,7 @@ on: paths: - 'plugins/lookup/rule.py' - 'plugins/lookup/rules.py' + - '.github/workflows/ans-int-test-lkp-rules.yaml' jobs: diff --git a/.github/workflows/ans-int-test-lkp-rulesets.yaml b/.github/workflows/ans-int-test-lkp-rulesets.yaml index 8a2936874..ad0ffd155 100644 --- a/.github/workflows/ans-int-test-lkp-rulesets.yaml +++ b/.github/workflows/ans-int-test-lkp-rulesets.yaml @@ -28,6 +28,7 @@ on: paths: - 'plugins/lookup/ruleset.py' - 'plugins/lookup/rulesets.py' + - '.github/workflows/ans-int-test-lkp-rulesets.yaml' jobs: diff --git a/.github/workflows/ans-int-test-lkp-site.yaml b/.github/workflows/ans-int-test-lkp-site.yaml new file mode 100644 index 000000000..c5d2b3bc0 --- /dev/null +++ b/.github/workflows/ans-int-test-lkp-site.yaml @@ -0,0 +1,76 @@ +# README: +# - When changing the module name, it needs to be changed in 'env:MODULE_NAME' and in 'on:pull_requests:path'! +# +# Resources: +# - Template for this file: https://github.com/ansible-collections/collection_template/blob/main/.github/workflows/ansible-test.yml +# - About Ansible integration tests: https://docs.ansible.com/ansible/latest/dev_guide/testing_integration.html + +env: + NAMESPACE: checkmk + COLLECTION_NAME: general + MODULE_NAME: lookup_site + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +name: Ansible Integration Tests for Site Lookup Module +on: + workflow_dispatch: + pull_request: + branches: + - main + - devel + paths: + - 'plugins/lookup/site.py' + push: + paths: + - 'plugins/lookup/site.py' + - '.github/workflows/ans-int-test-lkp-site.yaml' + +jobs: + + integration: + runs-on: ubuntu-22.04 + name: Ⓐ${{ matrix.ansible }}+py${{ matrix.python }} + strategy: + fail-fast: false + matrix: + ansible: + - stable-2.15 + - stable-2.16 + - stable-2.17 + - devel + python: + - '3.9' + - '3.10' + - '3.11' + - '3.12' + exclude: + # Exclude unsupported sets. + - ansible: stable-2.15 + python: '3.12' + + 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-base (${{ matrix.ansible }}) + run: pip install https://github.com/ansible/ansible/archive/${{ matrix.ansible }}.tar.gz --disable-pip-version-check + + - name: Provide secrets file + run: echo "${{ secrets.CHECKMK_DOWNLOAD_PW }}" > ./tests/integration/files/.dl-secret + working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + env: + CHECKMK_DOWNLOAD_PW: ${{ secrets.CHECKMK_DOWNLOAD_PW }} + + - name: Run integration test + run: ansible-test integration ${{env.MODULE_NAME}} -v --color --retry-on-error --continue-on-error --diff --python ${{ matrix.python }} --docker default + working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} diff --git a/.github/workflows/ans-int-test-lkp-sites.yaml b/.github/workflows/ans-int-test-lkp-sites.yaml new file mode 100644 index 000000000..6f3333530 --- /dev/null +++ b/.github/workflows/ans-int-test-lkp-sites.yaml @@ -0,0 +1,76 @@ +# README: +# - When changing the module name, it needs to be changed in 'env:MODULE_NAME' and in 'on:pull_requests:path'! +# +# Resources: +# - Template for this file: https://github.com/ansible-collections/collection_template/blob/main/.github/workflows/ansible-test.yml +# - About Ansible integration tests: https://docs.ansible.com/ansible/latest/dev_guide/testing_integration.html + +env: + NAMESPACE: checkmk + COLLECTION_NAME: general + MODULE_NAME: lookup_sites + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +name: Ansible Integration Tests for Sites Lookup Module +on: + workflow_dispatch: + pull_request: + branches: + - main + - devel + paths: + - 'plugins/lookup/sites.py' + push: + paths: + - 'plugins/lookup/sites.py' + - '.github/workflows/ans-int-test-lkp-sites.yaml' + +jobs: + + integration: + runs-on: ubuntu-22.04 + name: Ⓐ${{ matrix.ansible }}+py${{ matrix.python }} + strategy: + fail-fast: false + matrix: + ansible: + - stable-2.15 + - stable-2.16 + - stable-2.17 + - devel + python: + - '3.9' + - '3.10' + - '3.11' + - '3.12' + exclude: + # Exclude unsupported sets. + - ansible: stable-2.15 + python: '3.12' + + 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-base (${{ matrix.ansible }}) + run: pip install https://github.com/ansible/ansible/archive/${{ matrix.ansible }}.tar.gz --disable-pip-version-check + + - name: Provide secrets file + run: echo "${{ secrets.CHECKMK_DOWNLOAD_PW }}" > ./tests/integration/files/.dl-secret + working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + env: + CHECKMK_DOWNLOAD_PW: ${{ secrets.CHECKMK_DOWNLOAD_PW }} + + - name: Run integration test + run: ansible-test integration ${{env.MODULE_NAME}} -v --color --retry-on-error --continue-on-error --diff --python ${{ matrix.python }} --docker default + working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} diff --git a/.github/workflows/ans-int-test-lkp-version.yaml b/.github/workflows/ans-int-test-lkp-version.yaml index d16579afb..88dd4b420 100644 --- a/.github/workflows/ans-int-test-lkp-version.yaml +++ b/.github/workflows/ans-int-test-lkp-version.yaml @@ -28,6 +28,7 @@ on: push: paths: - 'plugins/lookup/version.py' + - '.github/workflows/ans-int-test-lkp-version.yaml' jobs: diff --git a/.github/workflows/ans-int-test-password.yaml b/.github/workflows/ans-int-test-password.yaml index 3db9d308b..5f8be9b52 100644 --- a/.github/workflows/ans-int-test-password.yaml +++ b/.github/workflows/ans-int-test-password.yaml @@ -28,6 +28,7 @@ on: push: paths: - 'plugins/modules/password.py' + - '.github/workflows/ans-int-test-password.yaml' jobs: diff --git a/.github/workflows/ans-int-test-rule.yaml b/.github/workflows/ans-int-test-rule.yaml index 17275b760..6e415998b 100644 --- a/.github/workflows/ans-int-test-rule.yaml +++ b/.github/workflows/ans-int-test-rule.yaml @@ -28,6 +28,7 @@ on: push: paths: - 'plugins/modules/rule.py' + - '.github/workflows/ans-int-test-rule.yaml' jobs: diff --git a/.github/workflows/ans-int-test-service_group.yaml b/.github/workflows/ans-int-test-service_group.yaml index 1db6e05ec..d562830c0 100644 --- a/.github/workflows/ans-int-test-service_group.yaml +++ b/.github/workflows/ans-int-test-service_group.yaml @@ -28,6 +28,7 @@ on: push: paths: - 'plugins/modules/service_group.py' + - '.github/workflows/ans-int-test-service_group.yaml' jobs: diff --git a/.github/workflows/ans-int-test-site.yaml b/.github/workflows/ans-int-test-site.yaml new file mode 100644 index 000000000..1add01bf0 --- /dev/null +++ b/.github/workflows/ans-int-test-site.yaml @@ -0,0 +1,78 @@ +# README: +# - When changing the module name, it needs to be changed in 'env:MODULE_NAME' and in 'on:pull_requests:path'! +# +# Resources: +# - Template for this file: https://github.com/ansible-collections/collection_template/blob/main/.github/workflows/ansible-test.yml +# - About Ansible integration tests: https://docs.ansible.com/ansible/latest/dev_guide/testing_integration.html + +env: + NAMESPACE: checkmk + COLLECTION_NAME: general + MODULE_NAME: site + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +name: Ansible Integration Tests for Site Management Module +on: + workflow_dispatch: + schedule: + - cron: '0 0 * * 0' + pull_request: + branches: + - main + - devel + paths: + - 'plugins/modules/site.py' + push: + paths: + - 'plugins/modules/site.py' + - '.github/workflows/ans-int-test-site.yaml' + +jobs: + + integration: + runs-on: ubuntu-22.04 + name: Ⓐ${{ matrix.ansible }}+py${{ matrix.python }} + strategy: + fail-fast: false + matrix: + ansible: + - stable-2.15 + - stable-2.16 + - stable-2.17 + - devel + python: + - '3.9' + - '3.10' + - '3.11' + - '3.12' + exclude: + # Exclude unsupported sets. + - ansible: stable-2.15 + python: '3.12' + + 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-base (${{ matrix.ansible }}) + run: pip install https://github.com/ansible/ansible/archive/${{ matrix.ansible }}.tar.gz --disable-pip-version-check + + - name: Provide secrets file + run: echo "${{ secrets.CHECKMK_DOWNLOAD_PW }}" > ./tests/integration/files/.dl-secret + working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + env: + CHECKMK_DOWNLOAD_PW: ${{ secrets.CHECKMK_DOWNLOAD_PW }} + + - name: Run integration test + run: ansible-test integration ${{env.MODULE_NAME}} -v --color --retry-on-error --continue-on-error --diff --python ${{ matrix.python }} --docker default + working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} diff --git a/.github/workflows/ans-int-test-tag_group.yaml b/.github/workflows/ans-int-test-tag_group.yaml index 37b7184a6..c3ae50f32 100644 --- a/.github/workflows/ans-int-test-tag_group.yaml +++ b/.github/workflows/ans-int-test-tag_group.yaml @@ -28,6 +28,7 @@ on: push: paths: - 'plugins/modules/tag_group.py' + - '.github/workflows/ans-int-test-tag_group.yaml' jobs: diff --git a/.github/workflows/ans-int-test-timeperiod.yaml b/.github/workflows/ans-int-test-timeperiod.yaml index f18f2a63d..f214e6bc5 100644 --- a/.github/workflows/ans-int-test-timeperiod.yaml +++ b/.github/workflows/ans-int-test-timeperiod.yaml @@ -28,6 +28,7 @@ on: push: paths: - 'plugins/modules/timeperiod.py' + - '.github/workflows/ans-int-test-timeperiod.yaml' jobs: diff --git a/.github/workflows/ans-int-test-user.yaml b/.github/workflows/ans-int-test-user.yaml index a758985ef..c5f1201a1 100644 --- a/.github/workflows/ans-int-test-user.yaml +++ b/.github/workflows/ans-int-test-user.yaml @@ -28,6 +28,7 @@ on: push: paths: - 'plugins/modules/user.py' + - '.github/workflows/ans-int-test-user.yaml' jobs: diff --git a/.github/workflows/ans-unit-test-inventory.yaml b/.github/workflows/ans-unit-test-inventory.yaml new file mode 100644 index 000000000..5d4bd9c65 --- /dev/null +++ b/.github/workflows/ans-unit-test-inventory.yaml @@ -0,0 +1,61 @@ +# https://github.com/ansible-collections/collection_template/blob/main/.github/workflows/ansible-test.yml + +env: + NAMESPACE: checkmk + COLLECTION_NAME: general + MODULE_NAME: checkmk + TESTPATH: tests/unit/plugins/inventory/test_checkmk.py + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +name: Ansible Unit Test for Inventory Module +on: + workflow_dispatch: + schedule: + - cron: '0 0 * * 0' + pull_request: + branches: + - main + - devel + paths: + - 'plugins/inventory/checkmk.py' + push: + paths: + - 'plugins/inventory/checkmk.py' + +jobs: + + units: + runs-on: ubuntu-22.04 + name: Units (Ⓐ${{ matrix.ansible }}) + strategy: + fail-fast: true # false? + matrix: + ansible: + - stable-2.15 + - stable-2.16 + - stable-2.17 + - devel + python: + - '3.10' + - '3.11' + + 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-base (${{ matrix.ansible }}) + run: pip install https://github.com/ansible/ansible/archive/${{ matrix.ansible }}.tar.gz --disable-pip-version-check + + - name: Run unit test + run: ansible-test units ${{env.TESTPATH}} -v --color --python ${{ matrix.python }} --docker default + working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} diff --git a/.github/workflows/ansible-lint.yaml b/.github/workflows/ansible-lint.yaml index 1d51054aa..a3b5ec59b 100644 --- a/.github/workflows/ansible-lint.yaml +++ b/.github/workflows/ansible-lint.yaml @@ -9,20 +9,21 @@ env: name: Ansible Linting on: workflow_dispatch: - push: + pull_request: branches: - main - devel paths: - 'roles/**' - 'playbooks/**' - pull_request: + push: branches: - main - devel paths: - 'roles/**' - 'playbooks/**' + - '.github/workflows/ansible-lint.yaml' jobs: diff --git a/.github/workflows/cla.yaml b/.github/workflows/cla.yaml index 54d78d640..0b916e00d 100644 --- a/.github/workflows/cla.yaml +++ b/.github/workflows/cla.yaml @@ -18,7 +18,7 @@ jobs: steps: - name: 'CLA Assistant' if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA or my organization already has a signed CLA.') || github.event_name == 'pull_request_target' - uses: contributor-assistant/github-action@v2.4.0 + uses: contributor-assistant/github-action@v2.6.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # the below token should have repo scope and must be manually added by you in the repository's secret diff --git a/.github/workflows/molecule-role-agent.yaml b/.github/workflows/molecule-role-agent.yaml index 5eff148ad..e43c7d178 100644 --- a/.github/workflows/molecule-role-agent.yaml +++ b/.github/workflows/molecule-role-agent.yaml @@ -19,6 +19,8 @@ on: - devel paths: - 'roles/agent/**' + - '.github/workflows/molecule-role-agent.yaml' + jobs: build: diff --git a/.github/workflows/molecule-role-server.yaml b/.github/workflows/molecule-role-server.yaml index 8568ee7d7..c0813d0ee 100644 --- a/.github/workflows/molecule-role-server.yaml +++ b/.github/workflows/molecule-role-server.yaml @@ -19,6 +19,7 @@ on: - devel paths: - 'roles/server/**' + - '.github/workflows/molecule-role-server.yaml' jobs: build: diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 921f86aa8..5830a4fde 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -111,7 +111,7 @@ jobs: run: antsibull-docs collection --use-current --squash-hierarchy --fail-on-error --dest-dir ./docs/ ${{env.NAMESPACE}}.${{env.COLLECTION_NAME}} - name: "Create Pull Request for Docs and Changelog against devel branch" - uses: peter-evans/create-pull-request@v6 + uses: peter-evans/create-pull-request@v7 with: commit-message: Update Docs and Changelogs upon Release signoff: false diff --git a/CODEOWNERS b/CODEOWNERS index 7ae68dc25..cc45e98cc 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,13 +1,19 @@ * @robin-checkmk -/tests/ @robin-checkmk -/roles/ @robin-checkmk -/playbooks/ @robin-checkmk -/plugins/lookup/version.py @lgetwan +*.md @robin-checkmk +/plugins/ @lgetwan +/plugins/doc_fragments/ @robin-checkmk +/plugins/lookup/ @lgetwan +/plugins/lookup/bakery.py @Max-checkmk +/plugins/module_utils/ @lgetwan /plugins/modules/ @lgetwan -/plugins/module_utils/ @godspeed-you -/plugins/modules/user.py @lgetwan /plugins/modules/bakery.py @Max-checkmk +/plugins/modules/contact_group.py @lgetwan +/plugins/modules/discovery.py @Max-checkmk +/plugins/modules/host_group.py @lgetwan +/plugins/modules/tag_group.py @Max-checkmk +/plugins/modules/timeperiod.py @Max-checkmk /plugins/modules/password.py @Max-checkmk +/plugins/modules/service_group.py @lgetwan /roles/agent/tasks/Windows.yml @Max-checkmk /roles/agent/vars/Windows.yml @Max-checkmk /roles/agent/tasks/Win32NT.yml @Max-checkmk \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6bfe54ae0..15693e8a0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -141,7 +141,16 @@ To test our roles, we use [Molecule](https://www.jeffgeerling.com/blog/2018/test ### Unit -There are currently no unit tests. +Currently we're only unit testing the inventory plugin. +Like with integration tests, we recommend testing inside docker, so modification of your local system isn't necessary. + +To run all tests locally, you can use following command: + + ansible-test units --docker + +For a single unit test, this can be started with this command: + + ansible-test units --docker tests/unit/plugins/inventory/test_checkmk.py ## Releasing this collection diff --git a/Makefile b/Makefile index 69669c829..97c75bc47 100644 --- a/Makefile +++ b/Makefile @@ -45,6 +45,13 @@ help: @echo " publish - Make files available, update git and announce" @echo "" +build: + @echo "Building Collection from current working directory. This can take a while." + @ansible-galaxy collection build --force ./ + +install: + @ansible-galaxy collection install -f ./checkmk-general-$(VERSION).tar.gz + release: version # gh workflow run release.yaml --ref main # https://cli.github.com/manual/gh_workflow_run @@ -151,15 +158,12 @@ tests-sanity: vm cd $(COLLECTION_ROOT) && \ ansible-test sanity --docker" -tests-integration: vm +tests-units: vm @vagrant ssh collection -c "\ cd $(COLLECTION_ROOT) && \ - ansible-test integration --docker" + ansible-test units --docker" -tests-integration-custom: vm container +tests-integration: vm @vagrant ssh collection -c "\ cd $(COLLECTION_ROOT) && \ - docker load -i ansible-checkmk-test-latest-image.tar.gz && \ - ansible-test integration --docker-privileged --python 3.10 --docker ansible-checkmk-test && \ - ansible-test integration --docker-privileged --python 3.11 --docker ansible-checkmk-test && \ - ansible-test integration --docker-privileged --python 3.12 --docker ansible-checkmk-test" + ansible-test integration --docker" diff --git a/README.md b/README.md index 891e52658..83a665cdd 100644 --- a/README.md +++ b/README.md @@ -38,11 +38,11 @@ For any form of support queries or requests refer to [SUPPORT.md](SUPPORT.md). You can find playbooks, demonstrating the content of this collection in the folder [playbooks/demo/](playbooks/demo/). - +### Inventory plugins + +Name | Description | Tests +--- | --- | --- +[checkmk.general.checkmk](https://github.com/Checkmk/ansible-collection-checkmk.general/blob/main/plugins/inventory/checkmk.py)|Dynamic Inventory Source for Checkmk | [![Ansible Unit Test for Inventory Module](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-unit-test-inventory.yaml/badge.svg)](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-unit-test-inventory.yaml) ### Lookup plugins Click on the lookup plugin name below, to get detailed documentation about it. @@ -59,6 +59,8 @@ Name | Description | Tests [checkmk.general.rules](https://github.com/Checkmk/ansible-collection-checkmk.general/blob/main/plugins/lookup/rules.py)|Look up all rules.|[![Integration Tests for Rules Lookup Module](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-int-test-lkp-rules.yaml/badge.svg)](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-int-test-lkp-rules.yaml) [checkmk.general.ruleset](https://github.com/Checkmk/ansible-collection-checkmk.general/blob/main/plugins/lookup/ruleset.py)|Look up ruleset attributes.|[![Integration Tests for Ruleset Lookup Module](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-int-test-lkp-rulesets.yaml/badge.svg)](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-int-test-lkp-rulesets.yaml) [checkmk.general.rulesets](https://github.com/Checkmk/ansible-collection-checkmk.general/blob/main/plugins/lookup/rulesets.py)|Look up all rulesets.|[![Integration Tests for Rulesets Lookup Module](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-int-test-lkp-rulesets.yaml/badge.svg)](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-int-test-lkp-rulesets.yaml) +[checkmk.general.site](https://github.com/Checkmk/ansible-collection-checkmk.general/blob/main/plugins/lookup/site.py)|Look up site attributes.|[![Integration Tests for Site Lookup Module](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-int-test-lkp-site.yaml/badge.svg)](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-int-test-lkp-site.yaml) +[checkmk.general.sites](https://github.com/Checkmk/ansible-collection-checkmk.general/blob/main/plugins/lookup/sites.py)|Look up all sites.|[![Integration Tests for Sites Lookup Module](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-int-test-lkp-sites.yaml/badge.svg)](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-int-test-lkp-sites.yaml) [checkmk.general.version](https://github.com/Checkmk/ansible-collection-checkmk.general/blob/main/plugins/lookup/version.py)|Look up version and edition information.|[![Integration Tests for Version Lookup Module](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-int-test-lkp-version.yaml/badge.svg)](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-int-test-lkp-version.yaml) ### Modules @@ -76,6 +78,7 @@ Name | Description | Tests [checkmk.general.host](https://github.com/Checkmk/ansible-collection-checkmk.general/blob/main/plugins/modules/host.py)|Manage hosts.|[![Integration Tests for Host Module](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-int-test-host.yaml/badge.svg)](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-int-test-host.yaml) [checkmk.general.rule](https://github.com/Checkmk/ansible-collection-checkmk.general/blob/main/plugins/modules/rule.py)|Manage rules.|[![Integration Tests for Rule Module](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-int-test-rule.yaml/badge.svg)](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-int-test-rule.yaml) [checkmk.general.service_group](https://github.com/Checkmk/ansible-collection-checkmk.general/blob/main/plugins/modules/service_group.py)|Manage service groups.|[![Integration Tests for Service Group Module](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-int-test-service_group.yaml/badge.svg)](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-int-test-service_group.yaml) +[checkmk.general.site](https://github.com/Checkmk/ansible-collection-checkmk.general/blob/main/plugins/modules/site.py)|Manage sites.|[![Integration Tests for Site Module](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-int-test-site.yaml/badge.svg)](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-int-test-site.yaml) [checkmk.general.tag_group](https://github.com/Checkmk/ansible-collection-checkmk.general/blob/main/plugins/modules/tag_group.py)|Manage tag groups.|[![Integration Tests for Tag Group Module](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-int-test-tag_group.yaml/badge.svg)](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-int-test-tag_group.yaml) [checkmk.general.user](https://github.com/Checkmk/ansible-collection-checkmk.general/blob/main/plugins/modules/user.py)|Manage users.|[![Integration Tests for User Module](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-int-test-user.yaml/badge.svg)](https://github.com/Checkmk/ansible-collection-checkmk.general/actions/workflows/ans-int-test-user.yaml) @@ -101,20 +104,17 @@ Please refer to the [official Ansible documentation](https://docs.ansible.com/an ## Using this collection -You can either call modules by their Fully Qualified Collection Namespace (FQCN), -such as `checkmk.general.activation`, or you can call modules by their short name -if you list the `checkmk.general` collection in the playbook's [`collections`](https://docs.ansible.com/ansible/devel/user_guide/collections_using.html#using-collections-in-playbooks) keyword: +We encourage you - in accordance with Ansible Best Practices - +to always use FQCNs (Fully Qualified Collection Names) as seen below. +This ensures, that you always know, which module is at play. ```yaml --- - hosts: all - collections: - - checkmk.general - tasks: - name: "Run activation." - activation: + checkmk.general.activation: server_url: "http://myserver/" site: "mysite" automation_user: "myuser" @@ -149,12 +149,14 @@ Please do **not** consider it a concrete planning document! - Modules - Monitoring - Acknowledgement + - Business Intelligence + - Event Console - Setup - Agents - - BI - - Distributed Monitoring + - Business Intelligence + - Dynamic host management (DCD) + - Event Console - Notification Rules -- Dynamic Inventory - OMD Module ## More information about Ansible diff --git a/SUPPORT.md b/SUPPORT.md index 2a255bdd8..56c22f5c3 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -57,3 +57,4 @@ Collection Version | Checkmk Versions | Ansible Versions | Remarks 5.1.0 | 2.1.0p44, 2.2.0p27, 2.3.0p6 | 2.15, 2.16, 2.17 | None 5.2.0 | 2.1.0p46, 2.2.0p31, 2.3.0p11 | 2.15, 2.16, 2.17 | None 5.2.1 | 2.1.0p46, 2.2.0p32, 2.3.0p12 | 2.15, 2.16, 2.17 | None +5.3.0 | 2.1.0p48, 2.2.0p35, 2.3.0p18 | 2.15, 2.16, 2.17 | None diff --git a/changelogs/fragments/checkmk.yml b/changelogs/fragments/checkmk.yml new file mode 100644 index 000000000..adf4e8c70 --- /dev/null +++ b/changelogs/fragments/checkmk.yml @@ -0,0 +1,2 @@ +major_changes: + - Inventory module - Add module for creating a dynamic inventory from Checkmk. diff --git a/changelogs/fragments/folder.yml b/changelogs/fragments/folder.yml new file mode 100644 index 000000000..861a79194 --- /dev/null +++ b/changelogs/fragments/folder.yml @@ -0,0 +1,3 @@ +bugfixes: + - Folder module - Fix an issue, where the folder module would create an + uppercase folder but would not be able to find said folder. diff --git a/changelogs/fragments/rule.yml b/changelogs/fragments/rule.yml new file mode 100644 index 000000000..becc04d91 --- /dev/null +++ b/changelogs/fragments/rule.yml @@ -0,0 +1,2 @@ +minor_changes: + - Rule module - Return 'content' and 'http_code', which includes the 'rule_id'. diff --git a/changelogs/fragments/site.yml b/changelogs/fragments/site.yml new file mode 100644 index 000000000..b2714588b --- /dev/null +++ b/changelogs/fragments/site.yml @@ -0,0 +1,12 @@ +major_changes: + - Site module - Add module for distributed monitoring. + Refer to the module documentation for further details. + - Site lookup module - Add module to lookup details of a single site. + - Sites lookup module - Add module to lookup all sites and their details + in a distributed monitoring setup. + +known_issues: + - Site module - To completely enable a site, the livestatus certificate + needs to be trusted. This cannot be done with the site module. + As of now, there is no automatic way to do this, so you need to log into + the site and add the certificate to the trusted certificates manually. diff --git a/galaxy.yml b/galaxy.yml index 8dfa2acca..57b401ee9 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -10,7 +10,7 @@ name: general # The version of the collection. Must be compatible with semantic versioning -version: 5.2.1 +version: 5.3.0 # The path to the Markdown (.md) readme file. This path is relative to the root of the collection readme: README.md diff --git a/playbooks/demo/lookup.yml b/playbooks/demo/lookup.yml index f84939682..366f6c7d0 100644 --- a/playbooks/demo/lookup.yml +++ b/playbooks/demo/lookup.yml @@ -13,14 +13,31 @@ msg: "Version is {{ checkmk_var_version }}" vars: checkmk_var_version: "{{ lookup('checkmk.general.version', - checkmk_var_server_url + '/' + checkmk_var_site, - validate_certs=False, + server_url=checkmk_var_server_url, + site=checkmk_var_site, automation_user=checkmk_var_automation_user, - automation_secret=checkmk_var_automation_secret) + automation_secret=checkmk_var_automation_secret, + validate_certs=False) }}" delegate_to: localhost run_once: true # noqa run-once[task] + - name: "Get the attributes of the main folder." + ansible.builtin.debug: + msg: "Attributes of main folder: {{ checkmk_var_attributes }}" + vars: + checkmk_var_attributes: "{{ + lookup('checkmk.general.folder', + '~', + server_url=checkmk_var_server_url, + site=checkmk_var_site, + automation_user=checkmk_var_automation_user, + automation_secret=checkmk_var_automation_secret, + validate_certs=False) + }}" + delegate_to: localhost + run_once: true # noqa run-once[task] + - name: "Get all subfolders of the main folder recursively" ansible.builtin.debug: msg: "Folder tree: {{ item.id }}" @@ -40,41 +57,85 @@ delegate_to: localhost run_once: true # noqa run-once[task] -## TODO: @lgetwan: Please take a look at these tasks and fix them. Bonus: Add the new lookup modules. :) + - name: "Get all hosts of the main directory recursively." + ansible.builtin.debug: + msg: "Host found in {{ item.0.id }}: {{ item.1.title }}" + vars: + checkmk_var_looping: "{{ + lookup('checkmk.general.folders', + '~', + show_hosts=True, + recursive=True, + server_url=checkmk_var_server_url, + site=checkmk_var_site, + automation_user=checkmk_var_automation_user, + automation_secret=checkmk_var_automation_secret, + validate_certs=False) + }}" + loop: "{{ checkmk_var_looping | subelements('members.hosts.value') }}" + loop_control: + label: "{{ item.0.id }}" + delegate_to: localhost + run_once: true # noqa run-once[task] + + ## TODO: @lgetwan: Please take a look at these tasks and beautify/fix them. Bonus: Add the new lookup modules. :) + + # - name: "Get all Checkmk Sites." + # ansible.builtin.debug: + # msg: "Sites found: {{ checkmk_var_sites }}" + # vars: + # checkmk_var_sites: "{{ lookup('checkmk.general.sites', + # server_url=checkmk_var_server_url, + # site=checkmk_var_site, + # automation_user=checkmk_var_automation_user, + # automation_secret=checkmk_var_automation_secret, + # validate_certs=False) + # }}" + # delegate_to: localhost + # run_once: true # noqa run-once[task] -# - name: "Get all hosts of the folder /test recursively" -# ansible.builtin.debug: -# msg: "Host found in {{ item.0.id }}: {{ item.1.title }}" -# vars: -# checkmk_var_looping: "{{ -# lookup('checkmk.general.folders', -# '~tests', -# show_hosts=True, -# recursive=True, -# server_url=checkmk_var_server_url, -# site=checkmk_var_site, -# automation_user=checkmk_var_automation_user, -# automation_secret=checkmk_var_automation_secret, -# validate_certs=False) -# }}" -# loop: "{{ checkmk_var_looping | subelements('members.hosts.value') }}" -# loop_control: -# label: "{{ item.0.id }}" -# delegate_to: localhost -# run_once: true # noqa run-once[task] + # - name: "Get a single site." + # ansible.builtin.debug: + # msg: "Site: {{ checkmk_var_looping }}" + # vars: + # checkmk_var_looping: "{{ + # lookup('checkmk.general.site', + # 'mysite', + # show_hosts=True, + # recursive=True, + # server_url=checkmk_var_server_url, + # site=checkmk_var_site, + # automation_user=checkmk_var_automation_user, + # automation_secret=checkmk_var_automation_secret, + # validate_certs=False) + # }}" + # delegate_to: localhost + # run_once: true # noqa run-once[task] -# - name: "Get the attributes of folder /tests" -# ansible.builtin.debug: -# msg: "Attributes of folder /network: {{ checkmk_var_attributes }}" -# vars: -# checkmk_var_attributes: "{{ -# lookup('checkmk.general.folder', -# '~tests', -# server_url=checkmk_var_server_url, -# site=checkmk_var_site, -# automation_user=checkmk_var_automation_user, -# automation_secret=checkmk_var_automation_secret, -# validate_certs=False) -# }}" -# delegate_to: localhost -# run_once: true # noqa run-once[task] + # - name: "Get all sites recursively." + # ansible.builtin.debug: + # msg: "Site ID {{ item.0.id }}: {{ item.1.title }}" + # vars: + # checkmk_var_sites: "{{ lookup('checkmk.general.sites', + # server_url=checkmk_var_server_url, + # site=checkmk_var_site, + # automation_user=checkmk_var_automation_user, + # automation_secret=checkmk_var_automation_secret, + # validate_certs=False) + # }}" + # checkmk_var_looping: "{{ + # lookup('checkmk.general.site', + # item.0.id, + # show_hosts=True, + # recursive=True, + # server_url=checkmk_var_server_url, + # site=checkmk_var_site, + # automation_user=checkmk_var_automation_user, + # automation_secret=checkmk_var_automation_secret, + # validate_certs=False) + # }}" + # loop: "{{ checkmk_var_sites }}" + # loop_control: + # label: "{{ item.0.id }}" + # delegate_to: localhost + # run_once: true # noqa run-once[task] diff --git a/playbooks/usecases/remote-registration.yml b/playbooks/usecases/remote-registration.yml index 3c6c47aad..191bf58c4 100644 --- a/playbooks/usecases/remote-registration.yml +++ b/playbooks/usecases/remote-registration.yml @@ -1,15 +1,15 @@ --- # This playbook uses the inventory from the 'playbooks/hosts' file and expects -# an expects an existing site with the below configuration and hosts of the group -# 'vagrant'. +# an existing site with the below configuration and hosts of the group +# 'linux'. - name: "Register hosts against a remote site. Both for updates and TLS." - hosts: vagrant + hosts: linux strategy: linear vars: # Basic server and authentication information. # You have to provide the distributed setup yourself. - checkmk_agent_version: "2.3.0p6" + checkmk_agent_version: "2.3.0p14" checkmk_agent_edition: "cre" checkmk_agent_user: "cmkadmin" checkmk_agent_pass: "password" diff --git a/playbooks/usecases/setup-distributed-monitoring.yml b/playbooks/usecases/setup-distributed-monitoring.yml new file mode 100644 index 000000000..b60480e65 --- /dev/null +++ b/playbooks/usecases/setup-distributed-monitoring.yml @@ -0,0 +1,81 @@ +--- +# This playbook uses the inventory from the 'playbooks/hosts' file and expects +# The VM 'debsible' to be running. + +- name: "Install a Central and Remote Site and connect them." + hosts: debsible + strategy: linear + vars: + checkmk_server_version: "2.3.0p14" + checkmk_server_edition: "cre" + checkmk_server_admin_pass: "password" + checkmk_server_sites: + - name: central + version: "{{ checkmk_server_version }}" + state: started + admin_pw: "{{ checkmk_server_admin_pass }}" + update_conflict_resolution: install + - name: remote + version: "{{ checkmk_server_version }}" + state: started + admin_pw: "{{ checkmk_server_admin_pass }}" + update_conflict_resolution: install + omd_auto_restart: 'true' + omd_config: + - var: LIVESTATUS_TCP + value: "on" + - var: LIVESTATUS_TCP_TLS + value: "on" + - var: LIVESTATUS_TCP_PORT + value: "6558" + tasks: + - name: "Install Checkmk and create Sites." + ansible.builtin.import_role: + name: checkmk.general.server + - name: "Connect Central Site to Remote Site." + checkmk.general.site: + server_url: "http://localhost/" + site: "central" + automation_user: "cmkadmin" + automation_secret: "{{ checkmk_server_admin_pass }}" + site_id: "remote" + site_connection: + site_config: + status_connection: + connection: + socket_type: tcp + port: 6558 + encrypted: true + host: localhost + verify: true + proxy: + use_livestatus_daemon: "direct" + connect_timeout: 2 + status_host: + status_host_set: "disabled" + url_prefix: "/remote/" + configuration_connection: + enable_replication: true + url_of_remote_site: "http://localhost/remote/check_mk/" + basic_settings: + site_id: "remote" + alias: "My Remote Site" + state: "present" + - name: "Log in to Remote Site." + checkmk.general.site: + server_url: "http://localhost/" + site: "central" + automation_user: "cmkadmin" + automation_secret: "{{ checkmk_server_admin_pass }}" + site_id: "remote" + site_connection: + authentication: + username: "cmkadmin" + password: "{{ checkmk_server_admin_pass }}" + state: "login" + - name: "Activate Changes on all Sites." + checkmk.general.activation: + server_url: "http://localhost/" + site: "central" + automation_user: "cmkadmin" + automation_secret: "{{ checkmk_server_admin_pass }}" diff --git a/plugins/doc_fragments/site_options.py b/plugins/doc_fragments/site_options.py new file mode 100644 index 000000000..8c6ce12e6 --- /dev/null +++ b/plugins/doc_fragments/site_options.py @@ -0,0 +1,326 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class ModuleDocFragment(object): + DOCUMENTATION = r""" + options: + state: + description: + - The desired state of this site connection. + type: str + choices: ['present', 'absent', 'login', 'logout'] + default: present + site_id: + description: + - The site ID to manage. + required: true + type: str + site_connection: + description: + - The settings of the site. + type: dict + suboptions: + authentication: + description: + - The authentication data for a configuration connection. + - Only required when the O(state) is V(login). + type: dict + suboptions: + username: + description: + - A user with administrative permissions. + type: str + password: + description: + - The password for the username provided. + type: str + site_config: + description: + - A site's connection. + - Only required when that O(state) is V(present). + type: dict + suboptions: + status_connection: + description: + - A site's status connection. + type: dict + suboptions: + connection: + description: + - When connecting to sites on remote servers, please + - make sure that Livestatus over TCP is activated there. + - You can use UNIX sockets to connect to foreign sites on + - localhost. + type: dict + suboptions: + socket_type: + description: + - The connection type. + type: str + choices: ['tcp', 'tcp6', 'unix', 'local'] + port: + description: + - The Livestatus TCP port to connect to. + type: int + encrypted: + description: + - Enable encryption for the connection. + type: bool + verify: + description: + - Verify remote site's certificate. + type: bool + host: + description: + - The IP or domain name of the host + - running the remote site. + type: str + path: + description: + - When the connection name is unix, + - this is the path to the unix socket. + type: str + proxy: + description: + - The Livestatus Proxy Daemon configuration attributes. + type: dict + suboptions: + use_livestatus_daemon: + description: + - Use Livestatus daemon with direct connection + - or with Livestatus proxy. + type: str + choices: [with_proxy, direct] + global_settings: + description: + - When O(site_connection.site_config.status_connection.proxy.use_livestatus_daemon) + - is set to V(with_proxy), + - you can set this to V(true) to use global setting or + - V(false) to use custom parameters. + type: bool + tcp: + description: + - Allow access to Livestatus via TCP. + type: dict + suboptions: + port: + description: + - The TCP port to open. + type: int + only_from: + description: + - Restrict access to these IP addresses. + type: list + elements: str + tls: + description: + - Encrypt TCP Livestatus connections. + type: bool + default: false + params: + description: + - The Livestatus Proxy Daemon parameters. + type: dict + suboptions: + channels: + description: + - The number of channels to keep open. + type: int + default: 5 + heartbeat: + description: + - The heartbeat interval and timeout + - configuration. + type: dict + suboptions: + interval: + description: + - The heartbeat interval + - for the TCP connection. + type: int + default: 5 + timeout: + description: + - The heartbeat timeout + - for the TCP connection. + type: int + default: 2 + channel_timeout: + description: + - The timeout waiting for a free channel. + type: int + default: 3 + query_timeout: + description: + - The total query timeout. + type: int + default: 120 + connect_retry: + description: + - The cooling period after failed + - connect or heartbeat. + type: int + default: 4 + cache: + description: + - Enable caching of several non-status queries. + type: bool + default: true + connect_timeout: + description: + - The time that the GUI waits for a connection to the site + - to be established before the site is considered to be + - unreachable. + type: int + default: 2 + persistent_connection: + description: + - If you enable persistent connections then Multisite + - will try to keep open a number of connections + - to the remote sites. + type: bool + default: false + url_prefix: + description: + - The URL prefix will be prepended to links of addons like + - NagVis when a link to such applications points to a host + - or service on that site. + type: str + status_host: + description: + - By specifying a status host for each non-local connection + - you prevent Multisite from running into timeouts when + - remote sites do not respond. + - I(This setting can be omitted, when) + - O(site_connection.site_config.status_connection.proxy.use_livestatus_daemon) + - I(is set to) V(with_proxy)! + type: dict + suboptions: + status_host_set: + description: + - enabled for 'use the following status host' and + - disabled for 'no status host' + type: str + choices: [enabled, disabled] + default: disabled + site: + description: + - The site ID of the status host. + type: str + host: + description: + - The host name of the status host. + type: str + disable_in_status_gui: + description: + - If you disable a connection, then no data of this site will + - be shown in the status GUI. The replication is not affected + - by this, however. + type: bool + default: false + configuration_connection: + description: + - A site's configuration connection. + type: dict + suboptions: + enable_replication: + description: + - Replication allows you to manage several monitoring sites + - with a logically centralized setup. + - Remote sites receive their configuration + - from the central sites. + type: bool + default: false + url_of_remote_site: + description: + - URL of the remote site including C(/check_mk/). + - This URL can be the same as the URL prefix of the status + - connection, but with C(/check_mk/) appended. + - Here it must always be an absolute URL, though. + - Unfortunately, this field is required by the REST API, + - even if there is no configuration connection enabled. + type: str + default: http://localhost/nonexistant/check_mk/ + disable_remote_configuration: + description: + - It is a good idea to disable access to Setup completely on + - the remote site. Otherwise a user who does not now about + - the replication could make local changes that are overridden + - at the next configuration activation. + type: bool + default: true + ignore_tls_errors: + description: + - This might be needed to make the synchronization accept + - problems with SSL certificates when using an SSL secured + - connection. We encourage you to always understand TLS issues + - and fix them, though! + type: bool + default: false + direct_login_to_web_gui_allowed: + description: + - When enabled, this site is marked for synchronization every + - time a web GUI related option is changed and users are + - allowed to login to the web GUI of this site. + type: bool + default: true + user_sync: + description: + - By default the users are synchronized automatically in + - the interval configured in the connection. For example + - the LDAP connector synchronizes the users every five minutes + - by default. The interval can be changed for each connection + - individually in the connection settings. Please note that + - the synchronization is only performed on the master site + - in distributed setups by default. + type: dict + suboptions: + sync_with_ldap_connections: + description: + - Sync with ldap connections. + type: str + choices: ['ldap', 'all', 'disabled'] + default: all + ldap_connections: + description: + - A list of ldap connections to synchronize. + type: list + elements: str + replicate_event_console: + description: + - This option enables the distribution of global settings and + - rules of the Event Console to the remote site. Any change in + - the local Event Console settings will mark the site as needing + - to sync. A synchronization will automatically reload + - the Event Console of the remote site. + type: bool + default: true + replicate_extensions: + description: + - If you enable the replication of MKPs then during each + - activation of changes MKPs that are installed on your central site + - and all other files below the C($OMD_ROOT/local/) directory will be + - transferred to the remote site. All other MKPs and + - files below C($OMD_ROOT/local/) on the remote site will be removed. + type: bool + default: true + basic_settings: + description: + - A site's basic settings. + type: dict + suboptions: + alias: + description: + - The alias of the site. + type: str + customer: + description: + - The customer of the site (Managed Edition - CME only). + type: str + site_id: + description: + - The site ID. + type: str + """ diff --git a/plugins/inventory/checkmk.py b/plugins/inventory/checkmk.py new file mode 100644 index 000000000..3a3ef5cd3 --- /dev/null +++ b/plugins/inventory/checkmk.py @@ -0,0 +1,269 @@ +# Copyright: (c) 2024, Max Sickora +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +DOCUMENTATION = """ + name: checkmk + author: Max Sickora (@max-checkmk) + short_description: Dynamic Inventory Source or Checkmk + description: + - Get hosts from any checkmk site. + - Generate groups based on tag groups or sites in Checkmk. + + extends_documentation_fragment: [checkmk.general.common] + + options: + plugin: + description: Name of the plugin. Should always be C(checkmk.general.checkmk). + type: string + required: true + choices: ['checkmk.general.checkmk'] + groupsources: + description: + - List of sources for grouping + - Possible sources are C(sites) and C(hosttags) + type: list + elements: str + required: false +""" + +EXAMPLES = """ +# To get started, you need to create a file called `checkmk.yml`, which contains +# one of the example blocks below and use it as your inventory source. +# E.g., with `ansible-inventory -i checkmk.yml --graph`. + +# Group all hosts based on both tag groups and sites: +plugin: checkmk.general.checkmk +server_url: "http://hostname/" +site: "sitename" +automation_user: "cmkadmin" +automation_secret: "******" +validate_certs: False +groupsources: ["hosttags", "sites"] +""" + +import json +import re + +from ansible.errors import AnsibleError, AnsibleParserError +from ansible.plugins.inventory import BaseInventoryPlugin +from ansible.utils.display import Display +from ansible_collections.checkmk.general.plugins.module_utils.lookup_api import ( + CheckMKLookupAPI, +) + +display = Display() + + +class InventoryModule(BaseInventoryPlugin): + """Host inventory parser for ansible using Checkmk as source.""" + + NAME = "checkmk.general.checkmk" + + def __init__(self): + super(InventoryModule, self).__init__() + + self.plugin = None + self.server_url = None + self.site = None + self.user = None + self.secret = None + self.validate_certs = None + self.groupsources = [] + self.hosttaggroups = [] + self.tags = [] + self.groups = [] + self.hosts = [] + self.sites = [] + + def convertname(self, name): + """Removes empty space and changes bad chars to _""" + regex = r"[^A-Za-z0-9\_]" + return re.sub(regex, "_", name.replace(" ", "")) + + def verify_file(self, path): + """return true/false if this is possibly a valid file for this plugin to consume""" + valid = False + if super(InventoryModule, self).verify_file(path): + # base class verifies that file exists and is readable by current user + if path.endswith(("checkmk.yaml", "checkmk.yml")): + self.display.vvv("Inventory source file verified") + valid = True + else: + self.display.vvv( + "Inventory source file doesn't end with 'checkmk.yaml' or 'checkmk.yml'" + ) + return valid + + def _generate_groups(self): + if self.groupsources: + if "hosttags" in self.groupsources: + hosttags = [] + for hosttaggroups in self.hosttaggroups: + if len(hosttaggroups.get("tags")) > 1: + hosttags += [ + ( + "tag_" + hosttaggroups.get("id") + "_" + tag.get("id") + if tag.get("id") + else "tag_" + hosttaggroups.get("id") + "_None" + ) + for tag in (hosttaggroups.get("tags")) + ] + else: + # If the tag group has only one choice, we have to generate TWO groups, + # one for hosts that have this tag set and one for hosts that have it unset + hosttags.append("tag_" + hosttaggroups.get("id") + "_None") + hosttags.append( + "tag_" + + hosttaggroups.get("id") + + "_" + + hosttaggroups.get("tags")[0].get("id") + ) + self.groups.extend(hosttags) + + if "sites" in self.groupsources: + sites = ["site_" + site.get("id") for site in self.sites] + self.groups.extend(sites) + + def parse(self, inventory, loader, path, cache=False): + super(InventoryModule, self).parse(inventory, loader, path, cache) + + config = self._read_config_data(path) + + try: + self.plugin = self.get_option("plugin") + self.server_url = self.get_option("server_url") + self.site = self.get_option("site") + self.user = self.get_option("automation_user") + self.secret = self.get_option("automation_secret") + self.validate_certs = self.get_option("validate_certs") + self.groupsources = self.get_option("groupsources") + except Exception as e: + raise AnsibleParserError("All correct options required: {}".format(e)) + + api = CheckMKLookupAPI( + site_url=self.get_option("server_url") + "/" + self.get_option("site"), + user=self.get_option("automation_user"), + secret=self.get_option("automation_secret"), + validate_certs=self.get_option("validate_certs"), + ) + + self.hosttaggroups = self._get_taggroups(api) + self.tags = [("tag_" + tag.get("id")) for tag in self.hosttaggroups] + self.sites = self._get_sites(api) + + self.hosts = self._get_hosts(api) + + self._generate_groups() + + self._populate() + + def _populate(self): + """Return the hosts and groups""" + + for group in self.groups: + self.inventory.add_group(self.convertname(group)) + + for host in self.hosts: + self.inventory.add_host(host["id"]) + self.inventory.set_variable(host["id"], "ipaddress", host["ipaddress"]) + self.inventory.set_variable(host["id"], "folder", host["folder"]) + + if self.groupsources: + if "hosttags" in self.groupsources: + for host in self.hosts: + for tag in self.tags: + if host.get("tags").get(tag): + self.inventory.add_child( + tag + "_" + self.convertname(host.get("tags").get(tag)), + host.get("id"), + ) + else: + self.inventory.add_child(tag + "_None", host.get("id")) + if "sites" in self.groupsources: + for host in self.hosts: + self.inventory.add_child( + "site_" + self.convertname(host.get("site")), host.get("id") + ) + + def _get_hosts(self, api): + response = json.loads( + api.get( + "/domain-types/host_config/collections/all", + {"effective_attributes": True}, + ) + ) + if "code" in response: + raise AnsibleError( + "Received error for %s - %s: %s" + % ( + response.get("url", ""), + response.get("code", ""), + response.get("msg", ""), + ) + ) + + hosts = [ + { + "id": host.get("id"), + "title": host.get("extensions").get("title"), + "ipaddress": host.get("extensions").get("attributes").get("ipaddress"), + "folder": host.get("extensions").get("folder"), + "site": host.get("extensions").get("effective_attributes").get("site"), + "tags": { + taggroup: tag + for taggroup, tag in host.get("extensions") + .get("effective_attributes") + .items() + if taggroup in self.tags + }, + } + for host in (response.get("value")) + ] + + return hosts + + def _get_taggroups(self, api): + response = json.loads(api.get("/domain-types/host_tag_group/collections/all")) + if "code" in response: + raise AnsibleError( + "Received error for %s - %s: %s" + % ( + response.get("url", ""), + response.get("code", ""), + response.get("msg", ""), + ) + ) + + hosttaggroups = [ + { + "id": hosttaggroup.get("id"), + "tags": hosttaggroup.get("extensions").get("tags"), + } + for hosttaggroup in (response.get("value")) + ] + + return hosttaggroups + + def _get_sites(self, api): + response = json.loads(api.get("/domain-types/site_connection/collections/all")) + if "code" in response: + raise AnsibleError( + "Received error for %s - %s: %s" + % ( + response.get("url", ""), + response.get("code", ""), + response.get("msg", ""), + ) + ) + + sites = [ + {"id": site.get("id"), "customer": site.get("extensions").get("customer")} + for site in (response.get("value")) + ] + + return sites diff --git a/plugins/lookup/site.py b/plugins/lookup/site.py new file mode 100644 index 000000000..962000cff --- /dev/null +++ b/plugins/lookup/site.py @@ -0,0 +1,170 @@ +# Copyright: (c) 2023, Lars Getwan +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ + name: site + author: Lars Getwan (@lgetwan) + version_added: "5.3.0" + + short_description: Show the configuration of a site + + description: + - Returns the configuration of a distributed monitoring site + + options: + + _terms: + description: site ID + required: True + + server_url: + description: URL of the Checkmk server. + required: True + vars: + - name: ansible_lookup_checkmk_server_url + env: + - name: ANSIBLE_LOOKUP_CHECKMK_SERVER_URL + ini: + - section: checkmk_lookup + key: server_url + + site: + description: Site name for REST API access. + required: True + vars: + - name: ansible_lookup_checkmk_site + env: + - name: ANSIBLE_LOOKUP_CHECKMK_SITE + ini: + - section: checkmk_lookup + key: site + + automation_user: + description: Automation user for the REST API access. + required: True + vars: + - name: ansible_lookup_checkmk_automation_user + env: + - name: ANSIBLE_LOOKUP_CHECKMK_AUTOMATION_USER + ini: + - section: checkmk_lookup + key: automation_user + + automation_secret: + description: Automation secret for the REST API access. + required: True + vars: + - name: ansible_lookup_checkmk_automation_secret + env: + - name: ANSIBLE_LOOKUP_CHECKMK_AUTOMATION_SECRET + ini: + - section: checkmk_lookup + key: automation_secret + + validate_certs: + description: Whether or not to validate TLS certificates. + type: boolean + required: False + default: True + vars: + - name: ansible_lookup_checkmk_validate_certs + env: + - name: ANSIBLE_LOOKUP_CHECKMK_VALIDATE_CERTS + ini: + - section: checkmk_lookup + key: validate_certs + + notes: + - Like all lookups, this runs on the Ansible controller and is unaffected by other keywords such as 'become'. + If you need to use different permissions, you must change the command or run Ansible as another user. + - Alternatively, you can use a shell/command task that runs against localhost and registers the result. + - The directory of the play is used as the current working directory. + - It is B(NOT) possible to assign other variables to the variables mentioned in the C(vars) section! + This is a limitation of Ansible itself. +""" + +EXAMPLES = """ +- name: Get a site with a particular site id + ansible.builtin.debug: + msg: "site: {{ extensions }}" + vars: + extensions: "{{ + lookup('checkmk.general.site', + 'my_remote_site', + server_url=server_url, + site=site, + automation_user=automation_user, + automation_secret=automation_secret, + validate_certs=False + ) + }}" + +- name: "Use variables outside the module call." + ansible.builtin.debug: + msg: "site: {{ extensions }}" + vars: + ansible_lookup_checkmk_server_url: "http://myserver/" + ansible_lookup_checkmk_site: "mysite" + ansible_lookup_checkmk_automation_user: "myuser" + ansible_lookup_checkmk_automation_secret: "mysecret" + ansible_lookup_checkmk_validate_certs: false + attributes: "{{ lookup('checkmk.general.site', 'my_remote_site') }}" +""" + +RETURN = """ + _list: + description: + - The details of a particular site + type: list + elements: str +""" + +import json + +from ansible.errors import AnsibleError +from ansible.plugins.lookup import LookupBase +from ansible_collections.checkmk.general.plugins.module_utils.lookup_api import ( + CheckMKLookupAPI, +) + + +class LookupModule(LookupBase): + def run(self, terms, variables, **kwargs): + self.set_options(var_options=variables, direct=kwargs) + server_url = self.get_option("server_url") + site = self.get_option("site") + user = self.get_option("automation_user") + secret = self.get_option("automation_secret") + validate_certs = self.get_option("validate_certs") + + site_url = server_url + "/" + site + + api = CheckMKLookupAPI( + site_url=site_url, + user=user, + secret=secret, + validate_certs=validate_certs, + ) + + ret = [] + + for term in terms: + response = json.loads(api.get("/objects/site_connection/" + term)) + + if "code" in response: + raise AnsibleError( + "Received error for %s - %s: %s" + % ( + response.get("url", ""), + response.get("code", ""), + response.get("msg", ""), + ) + ) + + ret.append(response.get("extensions", {})) + + return ret diff --git a/plugins/lookup/sites.py b/plugins/lookup/sites.py new file mode 100644 index 000000000..2c51feb54 --- /dev/null +++ b/plugins/lookup/sites.py @@ -0,0 +1,153 @@ +# Copyright: (c) 2023, Lars Getwan +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ + name: sites + author: Lars Getwan (@lgetwan) + version_added: "5.3.0" + + short_description: Get a list of all sites + + description: + - Returns a list of all sites and their configuration. + + options: + + server_url: + description: URL of the Checkmk server. + required: True + vars: + - name: ansible_lookup_checkmk_server_url + env: + - name: ANSIBLE_LOOKUP_CHECKMK_SERVER_URL + ini: + - section: checkmk_lookup + key: server_url + + site: + description: Site name for REST API access. + required: True + vars: + - name: ansible_lookup_checkmk_site + env: + - name: ANSIBLE_LOOKUP_CHECKMK_SITE + ini: + - section: checkmk_lookup + key: site + + automation_user: + description: Automation user for the REST API access. + required: True + vars: + - name: ansible_lookup_checkmk_automation_user + env: + - name: ANSIBLE_LOOKUP_CHECKMK_AUTOMATION_USER + ini: + - section: checkmk_lookup + key: automation_user + + automation_secret: + description: Automation secret for the REST API access. + required: True + vars: + - name: ansible_lookup_checkmk_automation_secret + env: + - name: ANSIBLE_LOOKUP_CHECKMK_AUTOMATION_SECRET + ini: + - section: checkmk_lookup + key: automation_secret + + validate_certs: + description: Whether or not to validate TLS certificates. + type: boolean + required: False + default: True + vars: + - name: ansible_lookup_checkmk_validate_certs + env: + - name: ANSIBLE_LOOKUP_CHECKMK_VALIDATE_CERTS + ini: + - section: checkmk_lookup + key: validate_certs + + notes: + - Like all lookups, this runs on the Ansible controller and is unaffected by other keywords such as 'become'. + If you need to use different permissions, you must change the command or run Ansible as another user. + - Alternatively, you can use a shell/command task that runs against localhost and registers the result. + - The directory of the play is used as the current working directory. + - It is B(NOT) possible to assign other variables to the variables mentioned in the C(vars) section! + This is a limitation of Ansible itself. +""" + +EXAMPLES = """ +- name: Get all sites defined in the environment. + ansible.builtin.debug: + msg: "Site: {{ item.extensions }}" + loop: "{{ + lookup('checkmk.general.sites', + server_url=server_url, + site=site, + automation_user=automation_user, + automation_secret=automation_secret, + validate_certs=False + ) + }}" + loop_control: + label: "{{ item.id }}" +""" + +RETURN = """ + _list: + description: + - A list of all sites + type: list + elements: str +""" + +import json + +from ansible.errors import AnsibleError +from ansible.plugins.lookup import LookupBase +from ansible_collections.checkmk.general.plugins.module_utils.lookup_api import ( + CheckMKLookupAPI, +) + + +class LookupModule(LookupBase): + def run(self, terms, variables, **kwargs): + regex_params = {} + self.set_options(var_options=variables, direct=kwargs) + server_url = self.get_option("server_url") + site = self.get_option("site") + user = self.get_option("automation_user") + secret = self.get_option("automation_secret") + validate_certs = self.get_option("validate_certs") + + site_url = server_url + "/" + site + + api = CheckMKLookupAPI( + site_url=site_url, + user=user, + secret=secret, + validate_certs=validate_certs, + ) + + response = json.loads(api.get("/domain-types/site_connection/collections/all")) + + if "code" in response: + raise AnsibleError( + "Received error for %s - %s: %s" + % ( + response.get("url", ""), + response.get("code", ""), + response.get("msg", ""), + ) + ) + + site_list = response.get("value") + + return [site_list] diff --git a/plugins/module_utils/api.py b/plugins/module_utils/api.py index bdb01927b..55855aeb0 100644 --- a/plugins/module_utils/api.py +++ b/plugins/module_utils/api.py @@ -41,7 +41,7 @@ def __init__(self, module): } self.current = {} self.required = {} - # may be "present", "abesent" or an individual one + # may be "present", "absent" or an individual one self.state = "" def _fetch(self, code_mapping="", endpoint="", data=None, method="GET"): diff --git a/plugins/module_utils/logger.py b/plugins/module_utils/logger.py new file mode 100644 index 000000000..96df3c70b --- /dev/null +++ b/plugins/module_utils/logger.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +# -*- encoding: utf-8; py-indent-offset: 4 -*- + +# Copyright: (c) 2024, Lars Getwan +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class Logger: + def __init__(self): + self.output = [] + self.loglevel = 0 + + def set_loglevel(self, loglevel): + self.loglevel = loglevel + + def warn(self, msg): + self.output.append("WARN: %s" % msg) + + def info(self, msg): + if self.loglevel >= 1: + self.output.append("INFO: %s" % msg) + + def debug(self, msg): + if self.loglevel >= 2: + self.output.append("DEBUG: %s" % msg) + + def trace(self, msg): + if self.loglevel >= 3: + self.output.append("TRACE: %s" % msg) + + def get_log(self): + return "\n".join(self.output) diff --git a/plugins/module_utils/site.py b/plugins/module_utils/site.py new file mode 100644 index 000000000..868b2c270 --- /dev/null +++ b/plugins/module_utils/site.py @@ -0,0 +1,384 @@ +#!/usr/bin/env python +# -*- encoding: utf-8; py-indent-offset: 4 -*- + +# Copyright: (c) 2024, Lars Getwan +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class TargetAPI: + GET = "get" + CREATE = "create" + LOGIN = "login" + LOGOUT = "logout" + UPDATE = "update" + DELETE = "delete" + + +class SiteHTTPCodes: + # http_code: (changed, failed, "Message") + get = { + 200: (False, False, "Site connection found, nothing changed"), + 404: (False, False, "Site connection not found"), + } + + create = {200: (True, False, "Site connection created")} + update = {200: (True, False, "Site connection modified")} + delete = {204: (True, False, "Site connection deleted")} + login = {204: (True, False, "Logged in to site")} + logout = {204: (True, False, "Logged out from site")} + + +class SiteEndpoints: + default = "/objects/site_connection" + create = "/domain-types/site_connection/collections/all" + + +class SiteConnection: + """Represents a particular site connection""" + + def __init__( + self, + authentication=None, + site_config=None, + state="absent", + site_id=None, + ): + self.site_id = site_id + self.state = state + self.site_config = site_config + self.authentication = authentication + + @classmethod + def from_module_params(cls, params): + site_connection = params.get("site_connection") + state = params.get("state") + site_id = params.get("site_id") + if site_connection: + authentication = site_connection.get("authentication") + site_config = site_connection.get("site_config") + else: + authentication = None + site_config = None + + return cls( + site_config=site_config, + authentication=authentication, + state=state, + site_id=site_id, + ) + + @classmethod + def from_api(cls, api_data): + + if not api_data: + return None + + return cls( + site_config=api_data.content.get("extensions"), + site_id=api_data.content.get("id"), + state="present", + ) + + def equals(self, site_connection): + return self.site_config == site_connection.site_config + + def _diff(self, d, u): + differences = [] + for k, v in u.items(): + if isinstance(v, dict): + differences += self._diff(d.get(k, {}), v) + else: + if d.get(k) != v: + differences += [k] + return differences + + def diff(self, site_connection): + return self._diff(self.site_config, site_connection.site_config) + + def logged_in(self): + if self.site_config and self.site_config.get("secret"): + return True + + def _update(self, d, u): + for k, v in u.items(): + if isinstance(v, dict): + d[k] = self._update(d.get(k, {}), v) + else: + d[k] = v + return d + + def merge_with(self, site_connection): + self._update(self.site_config, site_connection.site_config) + + def get_api_data(self, target_api): + + t = TargetAPI() + if target_api in [t.CREATE, t.UPDATE]: + return {"site_config": self.site_config} + + if target_api in [t.LOGIN]: + return self.authentication + + +# Define available arguments/parameters a user can pass to the module +module_args = dict( + server_url=dict(type="str", required=True), + site=dict(type="str", required=True), + validate_certs=dict(type="bool", required=False, default=True), + automation_user=dict(type="str", required=True), + automation_secret=dict(type="str", required=True, no_log=True), + state=dict( + type="str", + default="present", + choices=["present", "absent", "login", "logout"], + ), + site_id=dict(type="str", required=True), + site_connection=dict( + type="dict", + mutually_exclusive=[ + ("authentication", "site_config"), + ], + options=dict( + authentication=dict( + type="dict", + options=dict( + username=dict(type="str"), + password=dict(type="str", no_log=True), + ), + ), + site_config=dict( + type="dict", + options=dict( + status_connection=dict( + type="dict", + apply_defaults=True, + options=dict( + connection=dict( + type="dict", + apply_defaults=False, + options=dict( + socket_type=dict( + type="str", + choices=["tcp", "tcp6", "unix", "local"], + ), + port=dict( + type="int", + ), + encrypted=dict( + type="bool", + ), + verify=dict( + type="bool", + ), + host=dict( + type="str", + ), + path=dict( + type="str", + ), + ), + required_if=[ + ( + "socket_type", + "tcp", + ("port", "encrypted", "host"), + ), + ( + "socket_type", + "tcp6", + ( + "port", + "encrypted", + "host", + ), + ), + ("socket_type", "unix", ("path",)), + ], + mutually_exclusive=[ + ("host", "path"), + ("port", "path"), + ("encrypted", "path"), + ("verify", "path"), + ], + ), + proxy=dict( + type="dict", + apply_defaults=False, + options=dict( + use_livestatus_daemon=dict( + type="str", + choices=["with_proxy", "direct"], + ), + global_settings=dict( + type="bool", + ), + tcp=dict( + type="dict", + options=dict( + port=dict( + type="int", + ), + only_from=dict( + type="list", + elements="str", + ), + tls=dict( + type="bool", + default=False, + ), + ), + ), + params=dict( + type="dict", + apply_defaults=False, + options=dict( + channels=dict( + type="int", + default=5, + ), + heartbeat=dict( + type="dict", + options=dict( + interval=dict( + type="int", + default=5, + ), + timeout=dict( + type="int", + default=2, + ), + ), + ), + channel_timeout=dict( + type="int", + default=3, + ), + query_timeout=dict( + type="int", + default=120, + ), + connect_retry=dict( + type="int", + default=4, + ), + cache=dict( + type="bool", + default=True, + ), + ), + ), + ), + ), + connect_timeout=dict( + type="int", + default=2, + ), + persistent_connection=dict( + type="bool", + default=False, + ), + url_prefix=dict( + type="str", + ), + status_host=dict( + type="dict", + apply_defaults=True, + options=dict( + status_host_set=dict( + type="str", + default="disabled", + choices=["enabled", "disabled"], + ), + site=dict( + type="str", + ), + host=dict( + type="str", + ), + ), + ), + disable_in_status_gui=dict( + type="bool", + default="False", + ), + ), + ), + configuration_connection=dict( + type="dict", + apply_defaults=True, + options=dict( + enable_replication=dict( + type="bool", + default=False, + ), + url_of_remote_site=dict( + type="str", + default="http://localhost/nonexistant/check_mk/", + ), + disable_remote_configuration=dict( + type="bool", + default=True, + ), + ignore_tls_errors=dict( + type="bool", + default=False, + ), + direct_login_to_web_gui_allowed=dict( + type="bool", + default=True, + ), + user_sync=dict( + type="dict", + apply_defaults=True, + required_if=[ + ( + "sync_with_ldap_connections", + "ldap", + ("ldap_connections",), + ) + ], + options=dict( + sync_with_ldap_connections=dict( + type="str", + choices=["ldap", "all", "disabled"], + default="all", + ), + ldap_connections=dict( + type="list", + elements="str", + ), + ), + ), + replicate_event_console=dict( + type="bool", + default=True, + ), + replicate_extensions=dict( + default=True, + type="bool", + ), + ), + ), + basic_settings=dict( + type="dict", + options=dict( + alias=dict( + type="str", + ), + customer=dict( + type="str", + ), + site_id=dict( + type="str", + ), + ), + ), + ), + ), + ), + ), +) diff --git a/plugins/module_utils/utils.py b/plugins/module_utils/utils.py index 5412e3df8..47f846329 100644 --- a/plugins/module_utils/utils.py +++ b/plugins/module_utils/utils.py @@ -10,6 +10,8 @@ __metaclass__ = type +from ansible_collections.checkmk.general.plugins.module_utils.types import RESULT + def result_as_dict(result): return { @@ -19,6 +21,61 @@ def result_as_dict(result): } +def merge_results(results): + """Merges two or more results. Call like this: + over_all_result = merge_results({"created": create_result, "moved": move_result})""" + + return RESULT( + http_code=list(results.values())[-1].http_code, + msg=", ".join( + ["%s (%d)" % (results[k].msg, results[k].http_code) for k in results.keys()] + ), + content=list(results.values())[-1].content, + etag=list(results.values())[-1].etag, + failed=any(r.failed for r in list(results.values())), + changed=any(r.changed for r in list(results.values())), + ) + + +def remove_null_value_keys(params): + """Takes the module.params and removes all parameters that are set to 'null'. + This unsually removes all parameters that are neither explicitly set + nor provided in the ansible task""" + + for k in list(params.keys()): + if isinstance(params[k], dict): + remove_null_value_keys(params[k]) + elif params[k] is None: + del params[k] + + +def exit_module( + module, + result=None, + http_code=0, + msg="", + content="{}", + etag="", + failed=False, + changed=False, + logger=None, +): + if not result: + result = RESULT( + http_code=http_code, + msg=msg, + content=content, + etag=etag, + failed=failed, + changed=changed, + ) + + result_as_dict = result._asdict() + if logger: + result_as_dict["debug"] = logger.get_log() + module.exit_json(**result_as_dict) + + GENERIC_HTTP_CODES = { 200: (True, False, "OK: The operation was done successfully"), 204: (True, False, "Operation done successfully. No further output."), diff --git a/plugins/modules/folder.py b/plugins/modules/folder.py index 929a2f61e..4c45acc6c 100644 --- a/plugins/modules/folder.py +++ b/plugins/modules/folder.py @@ -279,7 +279,7 @@ def _normalize_path(self, path): p = Path(path) if not p.is_absolute(): p = Path("/").joinpath(p) - return str(p.parent).lower(), p.name + return str(p.parent), p.name def _urlize_path(self, path): return path.replace("/", "~").replace("~~", "~") diff --git a/plugins/modules/rule.py b/plugins/modules/rule.py index 28f732a82..7733a0791 100644 --- a/plugins/modules/rule.py +++ b/plugins/modules/rule.py @@ -547,7 +547,7 @@ def __init__(self, module): if self.rule_id: # Get the current rule from the API and set some parameters - (self.current, self.state) = self._get_current() + (self.current, self.state, self.result) = self._get_current() if self.state == "present": self._changed_items = self._detect_changes() @@ -560,7 +560,7 @@ def _verify_location(self): neighbour_id = self.params.get("rule", {}).get("location", {}).get("neighbour") if neighbour_id: - (neighbour, state) = self._get_rule_by_id(neighbour_id) + (neighbour, state, result) = self._get_rule_by_id(neighbour_id) if state == "absent": self.module.warn( @@ -723,7 +723,7 @@ def _get_rule_by_id(self, rule_id): if key in CURRENT_RULE_KEYS } - return (current, state) + return (current, state, result) def _get_current(self): return self._get_rule_by_id(self.rule_id) @@ -738,6 +738,9 @@ def _check_output(self, mode): changed=True, ) + def get_retrieve_result(self): + return self.result + def needs_update(self): return len(self._changed_items) > 0 @@ -951,9 +954,11 @@ def run_module(): if current_rule.needs_update(): result = current_rule.edit() else: + result = current_rule.get_retrieve_result() result = result._replace( msg="Rule already exists with the desired parameters." ) + elif rule_id: # There is no rule with the given rule_id result = result._replace( diff --git a/plugins/modules/site.py b/plugins/modules/site.py new file mode 100644 index 000000000..ee227fa0c --- /dev/null +++ b/plugins/modules/site.py @@ -0,0 +1,335 @@ +#!/usr/bin/python +# -*- encoding: utf-8; py-indent-offset: 4 -*- + +# Copyright: (c) 2024, Lars Getwan +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = r""" +--- +module: site + +short_description: Manage distributed monitoring in Checkmk. + +# If this is part of a collection, you need to use semantic versioning, +# i.e. the version is of the form "2.5.0" and not "2.4". +version_added: "5.3.0" + +description: + - Manage distributed monitoring within Checkmk. + +extends_documentation_fragment: + - checkmk.general.common + - checkmk.general.site_options + +author: + - Lars Getwan (@lgetwan) +""" + +EXAMPLES = r""" +- name: "Add a remote site with configuration replication." + checkmk.general.site: + server_url: "http://myserver/" + site: "mysite" + automation_user: "myuser" + automation_secret: "mysecret" + site_id: "myremotesite" + site_connection: + site_config: + status_connection: + connection: + socket_type: tcp + port: 6557 + encrypted: true + host: localhost + verify: true + proxy: + use_livestatus_daemon: "direct" + connect_timeout: 2 + status_host: + status_host_set: "disabled" + url_prefix: "/myremotesite/" + configuration_connection: + enable_replication: true + url_of_remote_site: "http://localhost/myremotesite/check_mk/" + basic_settings: + site_id: "myremotesite" + customer: "provider" + alias: "My Remote Site" + state: "present" + +- name: "Log into a remote site." + checkmk.general.site: + server_url: "http://myserver/" + site: "mysite" + automation_user: "myuser" + automation_secret: "mysecret" + site_id: "myremotesite" + site_connection: + authentication: + username: "myremote_admin" + password: "highly_secret" + state: "login" + +- name: "Log out from a remote site." + checkmk.general.site: + server_url: "http://myserver/" + site: "mysite" + automation_user: "myuser" + automation_secret: "mysecret" + site_id: "myremotesite" + state: "logout" + +- name: "Delete a remote site." + checkmk.general.site: + server_url: "http://myserver/" + site: "mysite" + automation_user: "myuser" + automation_secret: "mysecret" + site_id: "myremotesite" + state: "absent" +""" + +RETURN = r""" +message: + description: The output message that the module generates. Contains the API response details in case of an error. + type: str + returned: always + sample: 'Site connection created.' +""" + +import json + +# https://docs.ansible.com/ansible/latest/dev_guide/testing/sanity/import.html +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.checkmk.general.plugins.module_utils.api import CheckmkAPI +from ansible_collections.checkmk.general.plugins.module_utils.logger import Logger +from ansible_collections.checkmk.general.plugins.module_utils.site import ( + SiteConnection, + SiteEndpoints, + SiteHTTPCodes, + TargetAPI, + module_args, +) +from ansible_collections.checkmk.general.plugins.module_utils.types import RESULT +from ansible_collections.checkmk.general.plugins.module_utils.utils import ( + exit_module, + remove_null_value_keys, +) +from ansible_collections.checkmk.general.plugins.module_utils.version import ( + CheckmkVersion, +) + + +class SiteAPI(CheckmkAPI): + def __init__(self, module): + super().__init__(module) + + self._verify_compatibility() + + self.module = module + self.params = self.module.params + self.state = self.params.get("state") + + def _get_endpoint(self, target_api, site_id=""): + if target_api == TargetAPI.CREATE: + return SiteEndpoints.create + + if target_api in [TargetAPI.GET, TargetAPI.UPDATE]: + return "%s/%s" % (SiteEndpoints.default, site_id) + + if target_api in [TargetAPI.LOGIN]: + return "%s/%s/actions/login/invoke" % ( + SiteEndpoints.default, + site_id, + ) + + if target_api in [TargetAPI.LOGOUT]: + return "%s/%s/actions/logout/invoke" % ( + SiteEndpoints.default, + site_id, + ) + + if target_api in [TargetAPI.DELETE]: + return "%s/%s/actions/delete/invoke" % ( + SiteEndpoints.default, + site_id, + ) + + def get(self, site_id): + logger.debug( + "get endpoint: %s" % self._get_endpoint(TargetAPI.GET, site_id=site_id) + ) + result = self._fetch( + code_mapping=SiteHTTPCodes.get, + endpoint=self._get_endpoint(TargetAPI.GET, site_id=site_id), + ) + + logger.debug("get data: %s" % str(result)) + + if result.http_code == 404: + return None + + result = result._replace(content=json.loads(result.content)) + return result + + def create(self, site_connection): + logger.debug("create endpoint: %s" % self._get_endpoint(TargetAPI.CREATE)) + logger.debug("create data: %s" % site_connection.get_api_data(TargetAPI.CREATE)) + return self._fetch( + code_mapping=SiteHTTPCodes.create, + endpoint=self._get_endpoint(TargetAPI.CREATE), + data=site_connection.get_api_data(TargetAPI.CREATE), + method="POST", + ) + + def update(self, site_connection, desired_site_connection): + vorher = site_connection.site_config + site_connection.merge_with(desired_site_connection) + nachher = site_connection.site_config + logger.debug("update endpoint: %s" % self._get_endpoint(TargetAPI.UPDATE)) + logger.debug("update data: %s" % site_connection.get_api_data(TargetAPI.UPDATE)) + return self._fetch( + code_mapping=SiteHTTPCodes.update, + endpoint=self._get_endpoint( + TargetAPI.UPDATE, site_id=site_connection.site_id + ), + data=site_connection.get_api_data(TargetAPI.UPDATE), + method="PUT", + ) + + def login(self, site_connection): + logger.debug( + "login endpoint: %s" + % self._get_endpoint(TargetAPI.LOGIN, site_id=site_connection.site_id) + ) + logger.debug("login data: %s" % site_connection.get_api_data(TargetAPI.LOGIN)) + return self._fetch( + code_mapping=SiteHTTPCodes.login, + endpoint=self._get_endpoint( + TargetAPI.LOGIN, site_id=site_connection.site_id + ), + data=site_connection.get_api_data(TargetAPI.LOGIN), + method="POST", + ) + + def logout(self, site_connection): + logger.debug( + "logout endpoint: %s" + % self._get_endpoint(TargetAPI.LOGOUT, site_id=site_connection.site_id) + ) + logger.debug("logout data: %s" % site_connection.get_api_data(TargetAPI.LOGOUT)) + return self._fetch( + code_mapping=SiteHTTPCodes.logout, + endpoint=self._get_endpoint( + TargetAPI.LOGOUT, site_id=site_connection.site_id + ), + method="POST", + ) + + def delete(self, site_connection): + logger.debug( + "delete endpoint: %s" + % self._get_endpoint(TargetAPI.DELETE, site_id=site_connection.site_id) + ) + logger.debug("delete data: %s" % site_connection.get_api_data(TargetAPI.DELETE)) + return self._fetch( + code_mapping=SiteHTTPCodes.delete, + endpoint=self._get_endpoint( + TargetAPI.DELETE, site_id=site_connection.site_id + ), + method="POST", + ) + + def _verify_compatibility(self): + if self.getversion() <= CheckmkVersion("2.2.0"): + exit_module( + msg="Site management is only available for Checkmk versions starting with 2.2.0.", + failed=True, + ) + + +logger = Logger() + + +def run_module(): + # define available arguments/parameters a user can pass to the module + + module = AnsibleModule(argument_spec=module_args, supports_check_mode=True) + logger.set_loglevel(module._verbosity) + remove_null_value_keys(module.params) + site_id = module.params.get("site_id") + + site_api = SiteAPI(module) + desired_site_connection = SiteConnection.from_module_params(module.params) + existing_site_connection = SiteConnection.from_api(site_api.get(site_id)) + + if desired_site_connection.state == "present": + if existing_site_connection and existing_site_connection.state == "present": + differences = existing_site_connection.diff(desired_site_connection) + if differences: + result = site_api.update( + existing_site_connection, desired_site_connection + ) + + result = result._replace( + msg="%s\nUpdated: %s" % (result.msg, ", ".join(differences)) + ) + else: + result = RESULT( + http_code=0, + msg="Site connection already exists with the desired parameters.", + content="", + etag="", + failed=False, + changed=False, + ) + + else: + result = site_api.create(desired_site_connection) + + exit_module(module, result=result) + + elif desired_site_connection.state == "absent": + if existing_site_connection and existing_site_connection.state == "present": + result = site_api.delete(existing_site_connection) + exit_module(module, result=result) + else: + exit_module(module, msg="Site connection already absent.") + + elif desired_site_connection.state == "login": + if not existing_site_connection: + exit_module(module, msg="Site does not exist", failed=True) + + if not existing_site_connection.logged_in(): + result = site_api.login(desired_site_connection) + exit_module(module, result=result) + else: + exit_module(module, msg="Already logged in to site.") + + elif desired_site_connection.state == "logout": + if not existing_site_connection: + exit_module(module, msg="Site does not exist", failed=True) + + if existing_site_connection.logged_in(): + result = site_api.logout(desired_site_connection) + exit_module(module, result=result) + else: + exit_module(module, msg="Already logged out from site.") + + else: + exit_module( + module, + msg="Unexpected target state %s" % desired_site_connection.state, + failed=True, + ) + + +def main(): + run_module() + + +if __name__ == "__main__": + main() diff --git a/roles/agent/README.md b/roles/agent/README.md index 11a34009b..475a6f832 100644 --- a/roles/agent/README.md +++ b/roles/agent/README.md @@ -16,7 +16,7 @@ Please make sure it is installed on your system and available for Ansible. ## Role Variables - checkmk_agent_version: "2.3.0p12" + checkmk_agent_version: "2.3.0p18" The Checkmk version of the site your agents will talk to. @@ -49,7 +49,7 @@ Whether to validate the SSL certificate of the Checkmk server. The port of the web interface of your Checkmk server. Defaults to port 80 for http and port 443 for https. - checkmk_agent_site: mysite + checkmk_agent_site: 'mysite' The name of your Checkmk site. @@ -61,21 +61,21 @@ The server you want to use for registration tasks (Agent updates and TLS encrypt The site you want to use for registration tasks (Agent updates and TLS encryption). Defaults to `{{ checkmk_agent_site }}`. - checkmk_agent_user: myuser + checkmk_agent_user: 'myuser' The user used to authenticate against your Checkmk site. - checkmk_agent_pass: mysecret + checkmk_agent_pass: 'mypass' The password for the normal user used to authenticate against your Checkmk site, both for API calls and agent updates. This is mutually exclusive with `checkmk_agent_secret`. - checkmk_agent_secret: mysecret + checkmk_agent_secret: 'mysecret' The secret for the automation user used to authenticate against your Checkmk site, both for API calls and agent updates. This is mutually exclusive with `checkmk_agent_pass`. - checkmk_agent_port: 6556 + checkmk_agent_port: '6556' Configure the port the agent listens on. We recommend to stick to the default. **This does not change the agent configuration! It merely tells Ansible which port to talk to.** @@ -97,7 +97,7 @@ Automatically add the host where the agent was installed on to Checkmk. Define the hostname which will be used to add the host to Checkmk. - checkmk_agent_folder: "/" + checkmk_agent_folder: '/' The folder into which the automatically created host will be placed. @@ -152,7 +152,7 @@ This means, downgrades become possible and unverified packages would be installe Enable this to automatically install `xinetd` on hosts with systemd prior to version 220. - checkmk_agent_delegate_api_calls: localhost + checkmk_agent_delegate_api_calls: 'localhost' Configure the host to which Checkmk API calls are delegated to. Typically this would be your Ansible host, hence the default `localhost`. @@ -161,7 +161,7 @@ Typically this would be your Ansible host, hence the default `localhost`. Configure the host to which downloads are delegated to. After download the files are transferred to the remote node, when the remote node didn't do the download itself. - checkmk_agent_mode: pull + checkmk_agent_mode: 'pull' The mode the agent operates in. For most deployments, this will be the `pull` mode. If you are uncertain, what you are using, this is most likely your mode. diff --git a/roles/agent/defaults/main.yml b/roles/agent/defaults/main.yml index ad14e376e..bb449691d 100644 --- a/roles/agent/defaults/main.yml +++ b/roles/agent/defaults/main.yml @@ -1,16 +1,16 @@ --- -checkmk_agent_version: "2.3.0p12" -checkmk_agent_edition: cre -checkmk_agent_server_protocol: http -checkmk_agent_server: localhost -checkmk_agent_site: mysite +checkmk_agent_version: "2.3.0p18" +checkmk_agent_edition: 'cre' +checkmk_agent_server_protocol: 'http' +checkmk_agent_server: 'localhost' +checkmk_agent_site: 'mysite' checkmk_agent_registration_server_protocol: "{{ checkmk_agent_server_protocol }}" checkmk_agent_registration_server: "{{ checkmk_agent_server }}" checkmk_agent_registration_site: "{{ checkmk_agent_site }}" checkmk_agent_server_validate_certs: 'true' checkmk_agent_server_port: "{% if checkmk_agent_server_protocol == 'https' %}443{% else %}80{% endif %}" checkmk_agent_user: "{{ automation_user | default('automation') }}" -checkmk_agent_port: 6556 +checkmk_agent_port: '6556' # Depending on which user you will be using, set the password or secret: # checkmk_agent_pass: "{{ checkmk_var_automation_secret }}" @@ -19,7 +19,7 @@ checkmk_agent_port: 6556 checkmk_agent_auto_activate: 'false' checkmk_agent_add_host: 'false' checkmk_agent_discover: 'false' -checkmk_agent_discover_max_parallel_tasks: 0 +checkmk_agent_discover_max_parallel_tasks: '0' checkmk_agent_force_foreign_changes: 'false' checkmk_agent_update: 'false' checkmk_agent_tls: 'false' @@ -28,13 +28,13 @@ checkmk_agent_configure_firewall_zone: 'public' checkmk_agent_server_ips: [] checkmk_agent_force_install: 'false' checkmk_agent_prep_legacy: 'false' -checkmk_agent_delegate_api_calls: localhost +checkmk_agent_delegate_api_calls: 'localhost' checkmk_agent_delegate_download: "{{ inventory_hostname }}" checkmk_agent_host_name: "{{ inventory_hostname }}" checkmk_agent_folder: "{{ checkmk_var_folder_path | default('/') }}" checkmk_agent_host_attributes: ipaddress: "{{ checkmk_agent_host_ip | default(omit) }}" -checkmk_agent_mode: pull +checkmk_agent_mode: 'pull' checkmk_agent_no_log: 'true' # If you trust your local hostnames, you could also use the following diff --git a/roles/agent/molecule/2.1.0/group_vars/all.yml b/roles/agent/molecule/2.1.0/group_vars/all.yml index fde0ebf8e..06fc431cd 100644 --- a/roles/agent/molecule/2.1.0/group_vars/all.yml +++ b/roles/agent/molecule/2.1.0/group_vars/all.yml @@ -1,6 +1,6 @@ --- # General -checkmk_var_version: "2.1.0p46" +checkmk_var_version: "2.1.0p48" checkmk_var_edition: "cre" checkmk_var_checkmk_site: "mysite" checkmk_var_automation_user: "cmkadmin" diff --git a/roles/agent/molecule/2.1.0/molecule.yml b/roles/agent/molecule/2.1.0/molecule.yml index b093f6725..b529173f0 100644 --- a/roles/agent/molecule/2.1.0/molecule.yml +++ b/roles/agent/molecule/2.1.0/molecule.yml @@ -5,17 +5,6 @@ dependency: driver: name: docker platforms: - ## Disable for now, as something between Docker, Ubuntu and the UFW Ansible module does not seem to get along. - # - name: ubuntu2004 - # image: geerlingguy/docker-ubuntu2004-ansible - # command: ${MOLECULE_DOCKER_COMMAND:-""} - # cgroupns_mode: host - # tmpfs: - # - /run - # volumes: - # - /sys/fs/cgroup:/sys/fs/cgroup:rw - # privileged: true - # pre_build_image: true - name: ubuntu2204 image: geerlingguy/docker-ubuntu2204-ansible command: ${MOLECULE_DOCKER_COMMAND:-""} diff --git a/roles/agent/molecule/2.2.0/group_vars/all.yml b/roles/agent/molecule/2.2.0/group_vars/all.yml index 7f4c94d44..d46edd3dd 100644 --- a/roles/agent/molecule/2.2.0/group_vars/all.yml +++ b/roles/agent/molecule/2.2.0/group_vars/all.yml @@ -1,6 +1,6 @@ --- # General -checkmk_var_version: "2.2.0p32" +checkmk_var_version: "2.2.0p35" checkmk_var_edition: "cre" checkmk_var_checkmk_site: "mysite" checkmk_var_automation_user: "cmkadmin" diff --git a/roles/agent/molecule/2.2.0/molecule.yml b/roles/agent/molecule/2.2.0/molecule.yml index c95ee9721..92c099135 100644 --- a/roles/agent/molecule/2.2.0/molecule.yml +++ b/roles/agent/molecule/2.2.0/molecule.yml @@ -5,17 +5,6 @@ dependency: driver: name: docker platforms: - ## Disable for now, as something between Docker, Ubuntu and the UFW Ansible module does not seem to get along. - # - name: ubuntu2004 - # image: geerlingguy/docker-ubuntu2004-ansible - # command: ${MOLECULE_DOCKER_COMMAND:-""} - # cgroupns_mode: host - # tmpfs: - # - /run - # volumes: - # - /sys/fs/cgroup:/sys/fs/cgroup:rw - # privileged: true - # pre_build_image: true - name: ubuntu2204 image: geerlingguy/docker-ubuntu2204-ansible command: ${MOLECULE_DOCKER_COMMAND:-""} diff --git a/roles/agent/molecule/2.3.0/group_vars/all.yml b/roles/agent/molecule/2.3.0/group_vars/all.yml index 28d4a071f..827d1d1f8 100644 --- a/roles/agent/molecule/2.3.0/group_vars/all.yml +++ b/roles/agent/molecule/2.3.0/group_vars/all.yml @@ -1,6 +1,6 @@ --- # General -checkmk_var_version: "2.3.0p12" +checkmk_var_version: "2.3.0p18" checkmk_var_edition: "cre" checkmk_var_checkmk_site: "mysite" checkmk_var_automation_user: "cmkadmin" diff --git a/roles/agent/molecule/2.3.0/molecule.yml b/roles/agent/molecule/2.3.0/molecule.yml index c95ee9721..92c099135 100644 --- a/roles/agent/molecule/2.3.0/molecule.yml +++ b/roles/agent/molecule/2.3.0/molecule.yml @@ -5,17 +5,6 @@ dependency: driver: name: docker platforms: - ## Disable for now, as something between Docker, Ubuntu and the UFW Ansible module does not seem to get along. - # - name: ubuntu2004 - # image: geerlingguy/docker-ubuntu2004-ansible - # command: ${MOLECULE_DOCKER_COMMAND:-""} - # cgroupns_mode: host - # tmpfs: - # - /run - # volumes: - # - /sys/fs/cgroup:/sys/fs/cgroup:rw - # privileged: true - # pre_build_image: true - name: ubuntu2204 image: geerlingguy/docker-ubuntu2204-ansible command: ${MOLECULE_DOCKER_COMMAND:-""} diff --git a/roles/server/README.md b/roles/server/README.md index 5daff0e77..367e2a67e 100644 --- a/roles/server/README.md +++ b/roles/server/README.md @@ -25,12 +25,12 @@ To learn about the distributions used in automated tests, inspect the correspond ## Role Variables - checkmk_server_version: "2.3.0p12" + checkmk_server_version: "2.3.0p18" The global Checkmk version. This is used for installing Checkmk. To manage sites and their version, see `checkmk_server_sites`. - checkmk_server_edition: cre + checkmk_server_edition: 'cre' The edition you want to use. Valid values are `cre`, `cfe`, `cee`, `cce` and `cme`. @@ -73,15 +73,15 @@ Whether to allow downgrading a site's version. Note: this is not a recommended procedure, and will not be supported for enterprise customers. checkmk_server_sites: - - name: mysite + - name: 'mysite' version: "{{ checkmk_server_version }}" - update_conflict_resolution: abort - state: started - admin_pw: mypw + update_conflict_resolution: 'abort' + state: 'started' + admin_pw: 'mypass' omd_auto_restart: 'false' omd_config: - var: AUTOSTART - value: on + value: 'on' A dictionary of sites, containing the desired version, admin password and state. There are also advanced settings, which will be outlined below. @@ -106,7 +106,7 @@ on an existing site. Whether to back up sites when updating between versions. Only disable this if you plan on taking manual backups. - checkmk_server_backup_dir: /tmp + checkmk_server_backup_dir: '/tmp' Directory to backup sites to when updating between versions. Of course `/tmp/` is not a sane backup location, so change it! diff --git a/roles/server/defaults/main.yml b/roles/server/defaults/main.yml index aab5e8759..bfc98cacd 100644 --- a/roles/server/defaults/main.yml +++ b/roles/server/defaults/main.yml @@ -25,8 +25,8 @@ checkmk_server_server_stable_os: - Ubuntu-22 - Ubuntu-24 -checkmk_server_version: "2.3.0p12" -checkmk_server_edition: cre +checkmk_server_version: "2.3.0p18" +checkmk_server_edition: 'cre' checkmk_server_verify_setup: 'true' checkmk_server_download_user: [] @@ -41,14 +41,14 @@ checkmk_server_sites: [] # omd_auto_restart: 'false' # omd_config: # - var: LIVESTATUS_TCP -# value: "on" +# value: 'on' # - var: LIVESTATUS_TCP_PORT -# value: "6557" +# value: '6557' checkmk_server_configure_firewall: 'true' checkmk_server_backup_on_update: 'true' # Not recommended to disable this option -checkmk_server_backup_dir: /tmp +checkmk_server_backup_dir: '/tmp' checkmk_server_backup_opts: '--no-past' checkmk_server_allow_downgrades: 'false' diff --git a/roles/server/molecule/2.1.0/group_vars/all.yml b/roles/server/molecule/2.1.0/group_vars/all.yml index 3bbc9d767..f06b68d4f 100644 --- a/roles/server/molecule/2.1.0/group_vars/all.yml +++ b/roles/server/molecule/2.1.0/group_vars/all.yml @@ -1,6 +1,6 @@ --- # General -checkmk_var_version: "2.1.0p46" +checkmk_var_version: "2.1.0p48" checkmk_var_edition: "cre" checkmk_server_verify_setup: 'true' checkmk_var_server_url: "http://127.0.0.1/" diff --git a/roles/server/molecule/2.2.0/group_vars/all.yml b/roles/server/molecule/2.2.0/group_vars/all.yml index 30262e8d8..423c2118d 100644 --- a/roles/server/molecule/2.2.0/group_vars/all.yml +++ b/roles/server/molecule/2.2.0/group_vars/all.yml @@ -1,6 +1,6 @@ --- # General -checkmk_var_version: "2.2.0p32" +checkmk_var_version: "2.2.0p35" checkmk_var_edition: "cre" checkmk_server_verify_setup: 'true' checkmk_var_server_url: "http://127.0.0.1/" diff --git a/roles/server/molecule/2.3.0/group_vars/all.yml b/roles/server/molecule/2.3.0/group_vars/all.yml index 49b2d148a..082b49ca3 100644 --- a/roles/server/molecule/2.3.0/group_vars/all.yml +++ b/roles/server/molecule/2.3.0/group_vars/all.yml @@ -1,6 +1,6 @@ --- # General -checkmk_var_version: "2.3.0p12" +checkmk_var_version: "2.3.0p18" checkmk_var_edition: "cre" checkmk_server_verify_setup: 'true' checkmk_var_server_url: "http://127.0.0.1/" diff --git a/roles/server/tasks/sites.yml b/roles/server/tasks/sites.yml index ba035d0f4..0fa28d7b4 100644 --- a/roles/server/tasks/sites.yml +++ b/roles/server/tasks/sites.yml @@ -43,6 +43,7 @@ if [ "$(omd config {{ item.name }} show AUTOSTART)" != "on" ] then omd config {{ item.name }} set AUTOSTART on + omd enable "{{ item.name }}" echo "Autostart enabled." fi args: @@ -91,6 +92,7 @@ if [ "$(omd config {{ item.name }} show AUTOSTART)" != "off" ] then omd config {{ item.name }} set AUTOSTART off + omd disable {{ item.name }} echo "Autostart disabled." fi args: @@ -163,7 +165,7 @@ msg: - "Just a trigger." loop: "{{ __checkmk_server_sites_created.results }}" - when: __checkmk_server_sites_created.changed | bool and not item.item.admin_pw is defined + when: __checkmk_server_sites_created.changed | bool and not item.item.admin_pw is defined and item.item.state != "absent" changed_when: true notify: Warn site admin password no_log: "{{ checkmk_server_no_log | bool }}" diff --git a/scripts/release.sh b/scripts/release.sh index ec42142a8..00ad7cebb 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -15,9 +15,9 @@ script_dir=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) collection_dir="${script_dir%/*}" # Update these as necessary: -checkmk_ancient="2.1.0p46" -checkmk_oldstable="2.2.0p32" -checkmk_stable="2.3.0p12" +checkmk_ancient="2.1.0p48" +checkmk_oldstable="2.2.0p35" +checkmk_stable="2.3.0p18" while getopts 's:t:' OPTION; do case "$OPTION" in diff --git a/tests/integration/targets/activation/vars/main.yml b/tests/integration/targets/activation/vars/main.yml index e5d4f52a2..c1af07873 100644 --- a/tests/integration/targets/activation/vars/main.yml +++ b/tests/integration/targets/activation/vars/main.yml @@ -1,15 +1,15 @@ --- test_sites: - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cre" site: "stable_cre" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cee" site: "stable_cee" - - version: "2.2.0p32" + - version: "2.2.0p35" edition: "cre" site: "old_cre" - # - version: "2.1.0p46" + # - version: "2.1.0p48" # edition: "cre" # site: "ancient_cre" diff --git a/tests/integration/targets/bakery/vars/main.yml b/tests/integration/targets/bakery/vars/main.yml index 9a2b4ab7b..2609aab9c 100644 --- a/tests/integration/targets/bakery/vars/main.yml +++ b/tests/integration/targets/bakery/vars/main.yml @@ -1,12 +1,12 @@ --- test_sites: - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cee" site: "stable_cee" - - version: "2.2.0p32" + - version: "2.2.0p35" edition: "cee" site: "old_cee" - # - version: "2.1.0p46" + # - version: "2.1.0p48" # edition: "cee" # site: "ancient_cee" diff --git a/tests/integration/targets/contact_group/vars/main.yml b/tests/integration/targets/contact_group/vars/main.yml index 91cd97521..8b2cb5a02 100644 --- a/tests/integration/targets/contact_group/vars/main.yml +++ b/tests/integration/targets/contact_group/vars/main.yml @@ -1,18 +1,18 @@ --- test_sites: - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cme" site: "stable_cme" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cee" site: "stable_cee" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cre" site: "stable_cre" - - version: "2.2.0p32" + - version: "2.2.0p35" edition: "cre" site: "old_cre" - # - version: "2.1.0p46" + # - version: "2.1.0p48" # edition: "cre" # site: "ancient_cre" diff --git a/tests/integration/targets/discovery/vars/main.yml b/tests/integration/targets/discovery/vars/main.yml index a6d7c4936..e3b749bdb 100644 --- a/tests/integration/targets/discovery/vars/main.yml +++ b/tests/integration/targets/discovery/vars/main.yml @@ -1,15 +1,15 @@ --- test_sites: - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cee" site: "stable_cee" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cre" site: "stable_cre" - - version: "2.2.0p32" + - version: "2.2.0p35" edition: "cre" site: "old_cre" - # - version: "2.1.0p46" + # - version: "2.1.0p48" # edition: "cre" # site: "ancient_cre" diff --git a/tests/integration/targets/downtime/vars/main.yml b/tests/integration/targets/downtime/vars/main.yml index fd377db58..e3ee87acf 100644 --- a/tests/integration/targets/downtime/vars/main.yml +++ b/tests/integration/targets/downtime/vars/main.yml @@ -1,15 +1,15 @@ --- test_sites: - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cee" site: "stable_cee" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cre" site: "stable_cre" - - version: "2.2.0p32" + - version: "2.2.0p35" edition: "cre" site: "old_cre" - # - version: "2.1.0p46" + # - version: "2.1.0p48" # edition: "cre" # site: "ancient_cre" diff --git a/tests/integration/targets/folder/vars/main.yml b/tests/integration/targets/folder/vars/main.yml index a07327a23..795aa0274 100644 --- a/tests/integration/targets/folder/vars/main.yml +++ b/tests/integration/targets/folder/vars/main.yml @@ -1,15 +1,15 @@ --- test_sites: - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cee" site: "stable_cee" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cre" site: "stable_cre" - - version: "2.2.0p32" + - version: "2.2.0p35" edition: "cre" site: "old_cre" - # - version: "2.1.0p46" + # - version: "2.1.0p48" # edition: "cre" # site: "ancient_cre" diff --git a/tests/integration/targets/host/vars/main.yml b/tests/integration/targets/host/vars/main.yml index 520da9632..2f39e7600 100644 --- a/tests/integration/targets/host/vars/main.yml +++ b/tests/integration/targets/host/vars/main.yml @@ -1,15 +1,15 @@ --- test_sites: - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cre" site: "stable_cre" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cee" site: "stable_cee" - - version: "2.2.0p32" + - version: "2.2.0p35" edition: "cre" site: "old_cre" - # - version: "2.1.0p46" + # - version: "2.1.0p48" # edition: "cre" # site: "ancient_cre" diff --git a/tests/integration/targets/host_group/vars/main.yml b/tests/integration/targets/host_group/vars/main.yml index f6644fd59..371cb2282 100644 --- a/tests/integration/targets/host_group/vars/main.yml +++ b/tests/integration/targets/host_group/vars/main.yml @@ -1,18 +1,18 @@ --- test_sites: - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cme" site: "stable_cme" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cee" site: "stable_cee" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cre" site: "stable_cre" - - version: "2.2.0p32" + - version: "2.2.0p35" edition: "cre" site: "old_cre" - # - version: "2.1.0p46" + # - version: "2.1.0p48" # edition: "cre" # site: "ancient_cre" diff --git a/tests/integration/targets/lookup_bakery/vars/main.yml b/tests/integration/targets/lookup_bakery/vars/main.yml index 72e0fb20d..ee2f9b6e3 100644 --- a/tests/integration/targets/lookup_bakery/vars/main.yml +++ b/tests/integration/targets/lookup_bakery/vars/main.yml @@ -1,11 +1,11 @@ --- test_sites: - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cee" site: "stable_cee" - - version: "2.2.0p32" + - version: "2.2.0p35" edition: "cee" site: "old_cee" - # - version: "2.1.0p46" + # - version: "2.1.0p48" # edition: "cee" # site: "ancient_cee" diff --git a/tests/integration/targets/lookup_folder/vars/main.yml b/tests/integration/targets/lookup_folder/vars/main.yml index 67bf8af88..7f7ceb6d7 100644 --- a/tests/integration/targets/lookup_folder/vars/main.yml +++ b/tests/integration/targets/lookup_folder/vars/main.yml @@ -1,15 +1,15 @@ --- test_sites: - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cee" site: "stable_cee" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cre" site: "stable_cre" - - version: "2.2.0p32" + - version: "2.2.0p35" edition: "cre" site: "old_cre" - # - version: "2.1.0p46" + # - version: "2.1.0p48" # edition: "cre" # site: "ancient_cre" diff --git a/tests/integration/targets/lookup_folders/vars/main.yml b/tests/integration/targets/lookup_folders/vars/main.yml index 105c9e313..074b8752b 100644 --- a/tests/integration/targets/lookup_folders/vars/main.yml +++ b/tests/integration/targets/lookup_folders/vars/main.yml @@ -1,15 +1,15 @@ --- test_sites: - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cee" site: "stable_cee" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cre" site: "stable_cre" - - version: "2.2.0p32" + - version: "2.2.0p35" edition: "cre" site: "old_cre" - # - version: "2.1.0p46" + # - version: "2.1.0p48" # edition: "cre" # site: "ancient_cre" diff --git a/tests/integration/targets/lookup_host/vars/main.yml b/tests/integration/targets/lookup_host/vars/main.yml index 111e29959..96923e19b 100644 --- a/tests/integration/targets/lookup_host/vars/main.yml +++ b/tests/integration/targets/lookup_host/vars/main.yml @@ -1,15 +1,15 @@ --- test_sites: - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cee" site: "stable_cee" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cre" site: "stable_cre" - - version: "2.2.0p32" + - version: "2.2.0p35" edition: "cre" site: "old_cre" - # - version: "2.1.0p46" + # - version: "2.1.0p48" # edition: "cre" # site: "ancient_cre" diff --git a/tests/integration/targets/lookup_hosts/vars/main.yml b/tests/integration/targets/lookup_hosts/vars/main.yml index 4cfdf791c..5f4bd1fe1 100644 --- a/tests/integration/targets/lookup_hosts/vars/main.yml +++ b/tests/integration/targets/lookup_hosts/vars/main.yml @@ -1,15 +1,15 @@ --- test_sites: - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cee" site: "stable_cee" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cre" site: "stable_cre" - - version: "2.2.0p32" + - version: "2.2.0p35" edition: "cre" site: "old_cre" - # - version: "2.1.0p46" + # - version: "2.1.0p48" # edition: "cre" # site: "ancient_cre" diff --git a/tests/integration/targets/lookup_rules/vars/main.yml b/tests/integration/targets/lookup_rules/vars/main.yml index 10de491cb..724557188 100644 --- a/tests/integration/targets/lookup_rules/vars/main.yml +++ b/tests/integration/targets/lookup_rules/vars/main.yml @@ -1,15 +1,15 @@ --- test_sites: - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cee" site: "stable_cee" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cre" site: "stable_cre" - - version: "2.2.0p32" + - version: "2.2.0p35" edition: "cre" site: "old_cre" - # - version: "2.1.0p46" + # - version: "2.1.0p48" # edition: "cre" # site: "ancient_cre" diff --git a/tests/integration/targets/lookup_rulesets/vars/main.yml b/tests/integration/targets/lookup_rulesets/vars/main.yml index 754963c1f..d4c034077 100644 --- a/tests/integration/targets/lookup_rulesets/vars/main.yml +++ b/tests/integration/targets/lookup_rulesets/vars/main.yml @@ -1,15 +1,15 @@ --- test_sites: - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cee" site: "stable_cee" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cre" site: "stable_cre" - - version: "2.2.0p32" + - version: "2.2.0p35" edition: "cre" site: "old_cre" - # - version: "2.1.0p46" + # - version: "2.1.0p48" # edition: "cre" # site: "ancient_cre" diff --git a/tests/integration/targets/lookup_site/tasks/main.yml b/tests/integration/targets/lookup_site/tasks/main.yml new file mode 100644 index 000000000..bbb3963bc --- /dev/null +++ b/tests/integration/targets/lookup_site/tasks/main.yml @@ -0,0 +1,13 @@ +--- +- name: "Include Global Variables." + ansible.builtin.include_vars: /root/ansible_collections/checkmk/general/tests/integration/files/includes/vars/global.yml + +- name: "Run Preparations." + ansible.builtin.include_tasks: /root/ansible_collections/checkmk/general/tests/integration/files/includes/tasks/prep.yml + +- name: "Testing." + ansible.builtin.include_tasks: test.yml + loop: "{{ test_sites }}" + loop_control: + loop_var: outer_item + when: (download_pass is defined and download_pass | length) or outer_item.edition == "cre" diff --git a/tests/integration/targets/lookup_site/tasks/test.yml b/tests/integration/targets/lookup_site/tasks/test.yml new file mode 100644 index 000000000..3cdc18eaf --- /dev/null +++ b/tests/integration/targets/lookup_site/tasks/test.yml @@ -0,0 +1,31 @@ +--- +- name: " {{ outer_item.version }} - {{ outer_item.edition | upper }} - Get config of the site." + ansible.builtin.debug: + #msg: "Config of site {{ extensions.basic_settings.site_id }} is {{ extensions }}" + msg: "Config of site {{ extensions.basic_settings.site_id }}: {{ extensions }}" + vars: + extensions: "{{ lookup('checkmk.general.site', + outer_item.site, + server_url=checkmk_var_server_url, + site=outer_item.site, + validate_certs=False, + automation_user=checkmk_var_automation_user, + automation_secret=checkmk_var_automation_secret) + }}" + delegate_to: localhost + run_once: true # noqa run-once[task] + +- name: "{{ outer_item.version }} - {{ outer_item.edition | upper }} - Verify local connection." + ansible.builtin.assert: + that: "extensions.status_connection.connection.socket_type == 'local'" + vars: + extensions: "{{ lookup('checkmk.general.site', + outer_item.site, + server_url=checkmk_var_server_url, + site=outer_item.site, + validate_certs=False, + automation_user=checkmk_var_automation_user, + automation_secret=checkmk_var_automation_secret) + }}" + delegate_to: localhost + run_once: true # noqa run-once[task] diff --git a/tests/integration/targets/lookup_site/vars/main.yml b/tests/integration/targets/lookup_site/vars/main.yml new file mode 100644 index 000000000..b7c363879 --- /dev/null +++ b/tests/integration/targets/lookup_site/vars/main.yml @@ -0,0 +1,11 @@ +--- +test_sites: + - version: "2.3.0p18" + edition: "cee" + site: "stable_cee" + - version: "2.3.0p18" + edition: "cre" + site: "stable_cre" + - version: "2.2.0p35" + edition: "cre" + site: "old_cre" diff --git a/tests/integration/targets/lookup_sites/tasks/main.yml b/tests/integration/targets/lookup_sites/tasks/main.yml new file mode 100644 index 000000000..bbb3963bc --- /dev/null +++ b/tests/integration/targets/lookup_sites/tasks/main.yml @@ -0,0 +1,13 @@ +--- +- name: "Include Global Variables." + ansible.builtin.include_vars: /root/ansible_collections/checkmk/general/tests/integration/files/includes/vars/global.yml + +- name: "Run Preparations." + ansible.builtin.include_tasks: /root/ansible_collections/checkmk/general/tests/integration/files/includes/tasks/prep.yml + +- name: "Testing." + ansible.builtin.include_tasks: test.yml + loop: "{{ test_sites }}" + loop_control: + loop_var: outer_item + when: (download_pass is defined and download_pass | length) or outer_item.edition == "cre" diff --git a/tests/integration/targets/lookup_sites/tasks/test.yml b/tests/integration/targets/lookup_sites/tasks/test.yml new file mode 100644 index 000000000..296cbca8f --- /dev/null +++ b/tests/integration/targets/lookup_sites/tasks/test.yml @@ -0,0 +1,43 @@ +--- +- name: " {{ outer_item.version }} - {{ outer_item.edition | upper }} - Get config of the site." + ansible.builtin.debug: + msg: "Config of site {{ item.id }} is {{ item.extensions }}" + loop: "{{ lookup('checkmk.general.sites', + server_url=checkmk_var_server_url, + site=outer_item.site, + validate_certs=False, + automation_user=checkmk_var_automation_user, + automation_secret=checkmk_var_automation_secret) + }}" + loop_control: + label: "{{ item.id }}" + delegate_to: localhost + run_once: true # noqa run-once[task] + +- name: "{{ outer_item.version }} - {{ outer_item.edition | upper }} - Verify local connection." + ansible.builtin.debug: + msg: "{{ extensions }}" + vars: + extensions: "{{ lookup('checkmk.general.sites', + server_url=checkmk_var_server_url, + site=outer_item.site, + validate_certs=False, + automation_user=checkmk_var_automation_user, + automation_secret=checkmk_var_automation_secret) + }}" + delegate_to: localhost + run_once: true # noqa run-once[task] + +- name: "{{ outer_item.version }} - {{ outer_item.edition | upper }} - Verify local connection." + ansible.builtin.assert: + that: "sites[0].extensions.status_connection.connection.socket_type == 'local'" + vars: + sites: "{{ lookup('checkmk.general.sites', + server_url=checkmk_var_server_url, + site=outer_item.site, + validate_certs=False, + automation_user=checkmk_var_automation_user, + automation_secret=checkmk_var_automation_secret) + }}" + delegate_to: localhost + run_once: true # noqa run-once[task] diff --git a/tests/integration/targets/lookup_sites/vars/main.yml b/tests/integration/targets/lookup_sites/vars/main.yml new file mode 100644 index 000000000..b7c363879 --- /dev/null +++ b/tests/integration/targets/lookup_sites/vars/main.yml @@ -0,0 +1,11 @@ +--- +test_sites: + - version: "2.3.0p18" + edition: "cee" + site: "stable_cee" + - version: "2.3.0p18" + edition: "cre" + site: "stable_cre" + - version: "2.2.0p35" + edition: "cre" + site: "old_cre" diff --git a/tests/integration/targets/lookup_version/vars/main.yml b/tests/integration/targets/lookup_version/vars/main.yml index 766766053..9d4b0b940 100644 --- a/tests/integration/targets/lookup_version/vars/main.yml +++ b/tests/integration/targets/lookup_version/vars/main.yml @@ -1,14 +1,14 @@ --- test_sites: - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cee" site: "stable_cee" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cre" site: "stable_cre" - - version: "2.2.0p32" + - version: "2.2.0p35" edition: "cre" site: "old_cre" - # - version: "2.1.0p46" + # - version: "2.1.0p48" # edition: "cre" # site: "ancient_cre" diff --git a/tests/integration/targets/password/vars/main.yml b/tests/integration/targets/password/vars/main.yml index f5bd53349..80c8b7084 100644 --- a/tests/integration/targets/password/vars/main.yml +++ b/tests/integration/targets/password/vars/main.yml @@ -1,18 +1,18 @@ --- test_sites: - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cme" site: "stable_cme" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cee" site: "stable_cee" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cre" site: "stable_cre" - - version: "2.2.0p32" + - version: "2.2.0p35" edition: "cre" site: "old_cre" - # - version: "2.1.0p46" + # - version: "2.1.0p48" # edition: "cre" # site: "ancient_cre" diff --git a/tests/integration/targets/rule/vars/main.yml b/tests/integration/targets/rule/vars/main.yml index 766766053..9d4b0b940 100644 --- a/tests/integration/targets/rule/vars/main.yml +++ b/tests/integration/targets/rule/vars/main.yml @@ -1,14 +1,14 @@ --- test_sites: - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cee" site: "stable_cee" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cre" site: "stable_cre" - - version: "2.2.0p32" + - version: "2.2.0p35" edition: "cre" site: "old_cre" - # - version: "2.1.0p46" + # - version: "2.1.0p48" # edition: "cre" # site: "ancient_cre" diff --git a/tests/integration/targets/service_group/vars/main.yml b/tests/integration/targets/service_group/vars/main.yml index f1e51a23e..63301d2b2 100644 --- a/tests/integration/targets/service_group/vars/main.yml +++ b/tests/integration/targets/service_group/vars/main.yml @@ -1,18 +1,18 @@ --- test_sites: - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cme" site: "stable_cme" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cee" site: "stable_cee" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cre" site: "stable_cre" - - version: "2.2.0p32" + - version: "2.2.0p35" edition: "cre" site: "old_cre" - # - version: "2.1.0p46" + # - version: "2.1.0p48" # edition: "cre" # site: "ancient_cre" diff --git a/tests/integration/targets/site/tasks/main.yml b/tests/integration/targets/site/tasks/main.yml new file mode 100644 index 000000000..45a262645 --- /dev/null +++ b/tests/integration/targets/site/tasks/main.yml @@ -0,0 +1,14 @@ +--- +- name: "Include Global Variables." + ansible.builtin.include_vars: /root/ansible_collections/checkmk/general/tests/integration/files/includes/vars/global.yml + +- name: "Run Preparations." + ansible.builtin.include_tasks: /root/ansible_collections/checkmk/general/tests/integration/files/includes/tasks/prep.yml + +- name: "Testing." + ansible.builtin.include_tasks: test.yml + loop: "{{ test_sites }}" + loop_control: + loop_var: outer_item + label: "{{ outer_item.site }}" + when: (download_pass is defined and download_pass | length) or outer_item.edition == "cre" diff --git a/tests/integration/targets/site/tasks/test.yml b/tests/integration/targets/site/tasks/test.yml new file mode 100644 index 000000000..49a0070d1 --- /dev/null +++ b/tests/integration/targets/site/tasks/test.yml @@ -0,0 +1,213 @@ +--- +- name: "{{ outer_item.version }} - {{ outer_item.edition | upper }} - Set customer when needed." + ansible.builtin.set_fact: + customer: "provider" + when: (outer_item.edition == "cme") or (outer_item.edition == "cce") + +- name: "{{ outer_item.version }} - {{ outer_item.edition | upper }} - Unset customer when needed." + ansible.builtin.set_fact: + customer: null + when: not ((outer_item.edition == "cme") or (outer_item.edition == "cce")) + +- name: "{{ outer_item.version }} - {{ outer_item.edition | upper }} - Create remote sites." + ansible.builtin.command: "omd -V {{ outer_item.version }}.{{ outer_item.edition }} create --no-tmpfs --admin-password {{ checkmk_var_automation_secret }} {{ item.site_id }}" + args: + creates: "/omd/sites/{{ item.site_id }}" + when: (download_pass is defined and download_pass | length) or outer_item.edition == "cre" + loop: "{{ outer_item.remote_sites }}" + loop_control: + label: "{{ item.site_id }}" + +- name: "{{ outer_item.version }} - {{ outer_item.edition | upper }} - Configure Sites." # noqa no-changed-when + become: true + ansible.builtin.shell: | + set -o pipefail + omd config {{ item.site_id }} set LIVEPROXYD on + omd config {{ item.site_id }} set LIVESTATUS_TCP_PORT {{ item.site_config.status_connection.connection.port }} + omd config {{ item.site_id }} set LIVESTATUS_TCP_TLS off + args: + executable: /bin/bash + when: (download_pass is defined and download_pass | length) or outer_item.edition == "cre" + loop: "{{ outer_item.remote_sites }}" + loop_control: + label: "{{ item.site_id }}" + +- name: "{{ outer_item.version }} - {{ outer_item.edition | upper }} - Start Sites." + ansible.builtin.shell: "omd status -b {{ item.site_id }} || omd start {{ item.site_id }}" + register: site_status + changed_when: site_status.rc == "0" + when: (download_pass is defined and download_pass | length) or outer_item.edition == "cre" + loop: "{{ outer_item.remote_sites }}" + loop_control: + label: "{{ item.site_id }}" + +- name: "{{ outer_item.version }} - {{ outer_item.edition | upper }} - Wait for site to be ready." + ansible.builtin.pause: + seconds: 5 + when: | + ((download_pass is defined and download_pass | length) or outer_item.edition == 'cre') + and (outer_item.stdout_lines is defined and 'OVERALL 1' in outer_item.stdout_lines) + +- name: "{{ outer_item.version }} - {{ outer_item.edition | upper }} - Create site connection." + site: + server_url: "{{ checkmk_var_server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ checkmk_var_automation_user }}" + automation_secret: "{{ checkmk_var_automation_secret }}" + site_id: "{{ item.site_id }}" + site_connection: + site_config: "{{ item.site_config }}" + state: "present" + delegate_to: localhost + loop: "{{ outer_item.remote_sites }}" + loop_control: + label: "{{ item.site_id }}" + run_once: true # noqa run-once[task] + +- name: "{{ outer_item.version }} - {{ outer_item.edition | upper }} - Create site connection again." + site: + server_url: "{{ checkmk_var_server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ checkmk_var_automation_user }}" + automation_secret: "{{ checkmk_var_automation_secret }}" + site_id: "{{ item.site_id }}" + site_connection: + site_config: "{{ item.site_config }}" + state: "present" + delegate_to: localhost + loop: "{{ outer_item.remote_sites }}" + loop_control: + label: "{{ item.site_id }}" + run_once: true # noqa run-once[task] + +- name: "{{ outer_item.version }} - {{ outer_item.edition | upper }} - Log in to remote site." + site: + server_url: "{{ checkmk_var_server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ checkmk_var_automation_user }}" + automation_secret: "{{ checkmk_var_automation_secret }}" + site_id: "{{ item.site_id }}" + site_connection: + authentication: "{{ item.authentication }}" + state: "login" + delegate_to: localhost + loop: "{{ outer_item.remote_sites }}" + loop_control: + label: "{{ item.site_id }}" + run_once: true # noqa run-once[task] + +- name: "{{ outer_item.version }} - {{ outer_item.edition | upper }} - Log in to remote site again." + site: + server_url: "{{ checkmk_var_server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ checkmk_var_automation_user }}" + automation_secret: "{{ checkmk_var_automation_secret }}" + site_id: "{{ item.site_id }}" + site_connection: + authentication: "{{ item.authentication }}" + state: "login" + delegate_to: localhost + loop: "{{ outer_item.remote_sites }}" + loop_control: + label: "{{ item.site_id }}" + run_once: true # noqa run-once[task] + register: result + failed_when: result.changed | bool + +- name: "{{ outer_item.version }} - {{ outer_item.edition | upper }} - Update remote site." + site: + server_url: "{{ checkmk_var_server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ checkmk_var_automation_user }}" + automation_secret: "{{ checkmk_var_automation_secret }}" + site_id: "{{ item.site_id }}" + site_connection: + site_config: + basic_settings: + alias: "{{ item.site_id }} with new alias" + state: "present" + delegate_to: localhost + loop: "{{ outer_item.remote_sites }}" + loop_control: + label: "{{ item.site_id }}" + run_once: true # noqa run-once[task] + +- name: "{{ outer_item.version }} - {{ outer_item.edition | upper }} - Update remote site again. " + site: + server_url: "{{ checkmk_var_server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ checkmk_var_automation_user }}" + automation_secret: "{{ checkmk_var_automation_secret }}" + site_id: "{{ item.site_id }}" + site_connection: + site_config: + basic_settings: + alias: "{{ item.site_id }} with new alias" + state: "present" + delegate_to: localhost + loop: "{{ outer_item.remote_sites }}" + loop_control: + label: "{{ item.site_id }}" + run_once: true # noqa run-once[task] + register: result + failed_when: result.changed | bool + +- name: "{{ outer_item.version }} - {{ outer_item.edition | upper }} - Log out from remote site." + site: + server_url: "{{ checkmk_var_server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ checkmk_var_automation_user }}" + automation_secret: "{{ checkmk_var_automation_secret }}" + site_id: "{{ item.site_id }}" + state: "logout" + delegate_to: localhost + loop: "{{ outer_item.remote_sites }}" + loop_control: + label: "{{ item.site_id }}" + run_once: true # noqa run-once[task] + +- name: "{{ outer_item.version }} - {{ outer_item.edition | upper }} - Log out from remote site again." + site: + server_url: "{{ checkmk_var_server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ checkmk_var_automation_user }}" + automation_secret: "{{ checkmk_var_automation_secret }}" + site_id: "{{ item.site_id }}" + state: "logout" + delegate_to: localhost + loop: "{{ outer_item.remote_sites }}" + loop_control: + label: "{{ item.site_id }}" + run_once: true # noqa run-once[task] + register: result + failed_when: result.changed | bool + +- name: "{{ outer_item.version }} - {{ outer_item.edition | upper }} - Delete remote site." + site: + server_url: "{{ checkmk_var_server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ checkmk_var_automation_user }}" + automation_secret: "{{ checkmk_var_automation_secret }}" + site_id: "{{ item.site_id }}" + state: "absent" + delegate_to: localhost + loop: "{{ outer_item.remote_sites }}" + loop_control: + label: "{{ item.site_id }}" + run_once: true # noqa run-once[task] + +- name: "{{ outer_item.version }} - {{ outer_item.edition | upper }} - Delete remote site again." + site: + server_url: "{{ checkmk_var_server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ checkmk_var_automation_user }}" + automation_secret: "{{ checkmk_var_automation_secret }}" + site_id: "{{ item.site_id }}" + state: "absent" + delegate_to: localhost + loop: "{{ outer_item.remote_sites }}" + loop_control: + label: "{{ item.site_id }}" + run_once: true # noqa run-once[task] + register: result + failed_when: result.changed | bool diff --git a/tests/integration/targets/site/vars/main.yml b/tests/integration/targets/site/vars/main.yml new file mode 100644 index 000000000..a6c381b35 --- /dev/null +++ b/tests/integration/targets/site/vars/main.yml @@ -0,0 +1,207 @@ +--- +test_sites: + - version: "2.3.0p18" + edition: "cme" + site: "stable_cme" + remote_sites: + - site_id: "stable_cme_r1" + site_config: + status_connection: + connection: + socket_type: "tcp" + port: 6561 + encrypted: true + host: "localhost" + verify: false + proxy: + use_livestatus_daemon: "direct" + connect_timeout: 2 + status_host: + status_host_set: "disabled" + url_prefix: "/stable_cme_r1/" + configuration_connection: + enable_replication: true + url_of_remote_site: "http://localhost/stable_cme_r1/check_mk/" + basic_settings: + site_id: "stable_cme_r1" + customer: "provider" + alias: "stable_cme remote site 1" + authentication: + username: "cmkadmin" + password: "{{ checkmk_var_automation_secret }}" + - site_id: "stable_cme_r2" + site_config: + status_connection: + connection: + socket_type: "tcp" + port: 6562 + encrypted: true + host: "localhost" + verify: false + proxy: + use_livestatus_daemon: "with_proxy" + global_settings: true + connect_timeout: 2 + status_host: + status_host_set: "disabled" + url_prefix: "/stable_cme_r2/" + configuration_connection: + enable_replication: true + url_of_remote_site: "http://localhost/stable_cme_r2/check_mk/" + basic_settings: + site_id: "stable_cme_r2" + customer: "provider" + alias: "stable_cme remote site 2" + authentication: + username: "cmkadmin" + password: "{{ checkmk_var_automation_secret }}" + - site_id: "stable_cme_r3" + site_config: + status_connection: + connection: + socket_type: "tcp" + port: 6563 + encrypted: true + host: "localhost" + verify: false + proxy: + use_livestatus_daemon: "with_proxy" + global_settings: false + tcp: + port: 6663 + only_from: [] + tls: true + params: + channels: 6 + heartbeat: + interval: 10 + timeout: 4 + channel_timeout: 6 + query_timeout: 240 + connect_retry: 5 + cache: true + connect_timeout: 2 + status_host: + status_host_set: "disabled" + url_prefix: "/stable_cme_r3/" + configuration_connection: + enable_replication: true + url_of_remote_site: "http://localhost/stable_cme_r3/check_mk/" + disable_remote_configuration: true + ignore_tls_errors: false + direct_login_to_web_gui_allowed: false + replicate_event_console: true + replicate_extensions: false + basic_settings: + site_id: "stable_cme_r3" + customer: "provider" + alias: "stable_cme remote site 3" + authentication: + username: "cmkadmin" + password: "{{ checkmk_var_automation_secret }}" + - version: "2.3.0p18" + edition: "cee" + site: "stable_cee" + remote_sites: + - site_id: "stable_cee_r" + site_config: + status_connection: + connection: + socket_type: "tcp" + port: 6559 + encrypted: true + host: "localhost" + verify: "true" + proxy: + use_livestatus_daemon: "direct" + connect_timeout: 2 + status_host: + status_host_set: "disabled" + url_prefix: "/stable_cee_r/" + configuration_connection: + enable_replication: true + url_of_remote_site: "http://localhost/stable_cee_r/check_mk/" + basic_settings: + site_id: "stable_cee_r" + alias: "stable_cee remote site" + authentication: + username: "cmkadmin" + password: "{{ checkmk_var_automation_secret }}" + - version: "2.3.0p18" + edition: "cre" + site: "stable_cre" + remote_sites: + - site_id: "stable_cre_r1" + site_config: + status_connection: + connection: + socket_type: "tcp" + port: 6564 + encrypted: true + host: "localhost" + verify: false + proxy: + use_livestatus_daemon: "with_proxy" + global_settings: false + tcp: + port: 6664 + only_from: [] + tls: true + params: + channels: 6 + heartbeat: + interval: 10 + timeout: 4 + channel_timeout: 6 + query_timeout: 240 + connect_retry: 5 + cache: true + connect_timeout: 2 + status_host: + status_host_set: "disabled" + url_prefix: "/stable_cre_r1/" + configuration_connection: + enable_replication: true + url_of_remote_site: "http://localhost/stable_cre_r1/check_mk/" + disable_remote_configuration: true + ignore_tls_errors: false + direct_login_to_web_gui_allowed: false + replicate_event_console: true + replicate_extensions: false + basic_settings: + site_id: "stable_cre_r1" + alias: "stable_cre remote site 1" + authentication: + username: "cmkadmin" + password: "{{ checkmk_var_automation_secret }}" + - version: "2.2.0p35" + edition: "cre" + site: "old_cre" + remote_sites: + - site_id: "old_cre_r" + site_config: + status_connection: + connection: + socket_type: "tcp" + port: 6559 + encrypted: true + host: "localhost" + verify: "true" + proxy: + use_livestatus_daemon: "direct" + connect_timeout: 2 + status_host: + status_host_set: "disabled" + url_prefix: "/old_cre_r/" + configuration_connection: + enable_replication: true + url_of_remote_site: "http://localhost/old_cre_r/check_mk/" + basic_settings: + site_id: "old_cre_r" + alias: "old_cre remote site" + authentication: + username: "cmkadmin" + password: "{{ checkmk_var_automation_secret }}" + # - version: "2.1.0p48" + # edition: "cre" + # site: "ancient_cre" diff --git a/tests/integration/targets/tag_group/vars/main.yml b/tests/integration/targets/tag_group/vars/main.yml index 387992c56..92765f2ec 100644 --- a/tests/integration/targets/tag_group/vars/main.yml +++ b/tests/integration/targets/tag_group/vars/main.yml @@ -1,18 +1,18 @@ --- test_sites: - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cme" site: "stable_cme" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cee" site: "stable_cee" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cre" site: "stable_cre" - - version: "2.2.0p32" + - version: "2.2.0p35" edition: "cre" site: "old_cre" - # - version: "2.1.0p46" + # - version: "2.1.0p48" # edition: "cre" # site: "ancient_cre" diff --git a/tests/integration/targets/timeperiod/vars/main.yml b/tests/integration/targets/timeperiod/vars/main.yml index 278b978f4..6d8d5ab8e 100644 --- a/tests/integration/targets/timeperiod/vars/main.yml +++ b/tests/integration/targets/timeperiod/vars/main.yml @@ -1,15 +1,15 @@ --- test_sites: - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cee" site: "stable_cee" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cre" site: "stable_cre" - - version: "2.2.0p32" + - version: "2.2.0p35" edition: "cre" site: "old_cre" - # - version: "2.1.0p46" + # - version: "2.1.0p48" # edition: "cre" # site: "ancient_cre" diff --git a/tests/integration/targets/user/vars/main.yml b/tests/integration/targets/user/vars/main.yml index 01d9bd68c..e3fd919a1 100644 --- a/tests/integration/targets/user/vars/main.yml +++ b/tests/integration/targets/user/vars/main.yml @@ -1,18 +1,18 @@ --- test_sites: - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cme" site: "stable_cme" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cee" site: "stable_cee" - - version: "2.3.0p12" + - version: "2.3.0p18" edition: "cre" site: "stable_cre" - - version: "2.2.0p32" + - version: "2.2.0p35" edition: "cre" site: "old_cre" - # - version: "2.1.0p46" + # - version: "2.1.0p48" # edition: "cre" # site: "ancient_cre" diff --git a/tests/unit/plugins/inventory/conftest.py b/tests/unit/plugins/inventory/conftest.py new file mode 100644 index 000000000..b408c4671 --- /dev/null +++ b/tests/unit/plugins/inventory/conftest.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +import sys +import importlib + +fake_lookupapi = importlib.import_module( + "ansible_collections.checkmk.general.tests.unit.plugins.module_utils.lookup_api" +) +sys.modules[ + "ansible_collections.checkmk.general.plugins.module_utils.lookup_api" +] = fake_lookupapi diff --git a/tests/unit/plugins/inventory/test_checkmk.py b/tests/unit/plugins/inventory/test_checkmk.py new file mode 100644 index 000000000..970278fb2 --- /dev/null +++ b/tests/unit/plugins/inventory/test_checkmk.py @@ -0,0 +1,183 @@ +# Copyright: (c) 2024, Max Sickora +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +import pytest + +from ansible_collections.checkmk.general.plugins.inventory.checkmk import ( + InventoryModule, +) +from ansible_collections.checkmk.general.plugins.module_utils.lookup_api import ( + CheckMKLookupAPI, +) +from ansible.inventory.data import InventoryData + + +@pytest.fixture(scope="module") +def inventory(): + r = InventoryModule() + r.inventory = InventoryData() + return r + + +def test_verify_file(tmp_path, inventory): + file = tmp_path / "checkmk.yml" + file.touch() + assert inventory.verify_file(str(file)) is True + + +def test_verify_file_bad_config(inventory): + assert inventory.verify_file("wrongname.yml") is False + + +def get_option(opts): + def fn(option): + default = opts.get("default", False) + return opts.get(option, default) + + return fn + + +def test_populate_allgroups(inventory, mocker): + inventory.plugin = "checkmk.general.checkmk" + inventory.server_url = "http://127.0.0.1/" + inventory.site = "stable" + inventory.user = "cmkadmin" + inventory.secret = "cmk" + inventory.validate_certs = False + inventory.groupsources = ["hosttags", "sites"] + + opts = { + "server_url": inventory.server_url, + "site": inventory.site, + "automation_user": inventory.user, + "automation_secret": inventory.secret, + "validate_certs": inventory.validate_certs, + } + + inventory.get_option = mocker.MagicMock(side_effect=get_option(opts)) + + api = CheckMKLookupAPI( + site_url=inventory.get_option("server_url") + + "/" + + inventory.get_option("site"), + user=inventory.get_option("automation_user"), + secret=inventory.get_option("automation_secret"), + validate_certs=inventory.get_option("validate_certs"), + ) + + inventory.hosttaggroups = inventory._get_taggroups(api) + inventory.tags = [("tag_" + tag.get("id")) for tag in inventory.hosttaggroups] + inventory.sites = inventory._get_sites(api) + inventory.hosts = inventory._get_hosts(api) + + inventory._generate_groups() + inventory._populate() + + # Test if testhost2 exists + assert inventory.inventory.get_host("testhost2") + + # Test of sites exist as groups + for site in ["site_maintestsite", "site_remotetestsite"]: + assert site in inventory.inventory.groups + + # Test of some tags exist as groups + for site in [ + "tag_criticality_offline", + "tag_networking_lan", + "tag_piggyback_auto_piggyback", + "tag_snmp_ds_snmp_v2", + "tag_agent_cmk_agent", + ]: + assert site in inventory.inventory.groups + + # Test if testhost1,4,5 are in group tag_criticality_test + tag_criticality_test_group = inventory.inventory.groups["tag_criticality_test"] + host_testhost1 = inventory.inventory.get_host("testhost1") + host_testhost4 = inventory.inventory.get_host("testhost4") + host_testhost5 = inventory.inventory.get_host("testhost5") + assert tag_criticality_test_group.hosts == [ + host_testhost1, + host_testhost4, + host_testhost5, + ] + + # Test if testhost3 is in group tag_testtaggroup_None (id = NoneType) + tag_testtaggroup_none_group = inventory.inventory.groups["tag_testtaggroup_None"] + host_testhost3 = inventory.inventory.get_host("testhost3") + assert tag_testtaggroup_none_group.hosts == [host_testhost3] + + # Test if testhost1 is in folder /main + assert host_testhost1.get_vars()["folder"] == "/main" + + # Test if testhost4 and 5 are not in group site_maintestsite, tag_lonelytag_lonelytag or ungrouped + for node in ["testhost4", "testhost5"]: + assert node not in inventory.inventory.get_groups_dict()["site_maintestsite"] + assert node not in inventory.inventory.get_groups_dict()["ungrouped"] + assert ( + node not in inventory.inventory.get_groups_dict()["tag_lonelytag_lonelytag"] + ) + + # Test if testhost6 is in group tag_lonelytag_lonelytag + tag_lonelytag_lonelytag_group = inventory.inventory.groups[ + "tag_lonelytag_lonelytag" + ] + host_testhost6 = inventory.inventory.get_host("testhost6") + assert tag_lonelytag_lonelytag_group.hosts == [host_testhost6] + + +def test_populate_nogroups(inventory, mocker): + inventory.plugin = "checkmk.general.checkmk" + inventory.server_url = "http://127.0.0.1/" + inventory.site = "stable" + inventory.user = "cmkadmin" + inventory.secret = "cmk" + inventory.validate_certs = False + inventory.groupsources = [] + + opts = { + "server_url": inventory.server_url, + "site": inventory.site, + "automation_user": inventory.user, + "automation_secret": inventory.secret, + "validate_certs": inventory.validate_certs, + } + + inventory.get_option = mocker.MagicMock(side_effect=get_option(opts)) + + api = CheckMKLookupAPI( + site_url=inventory.get_option("server_url") + + "/" + + inventory.get_option("site"), + user=inventory.get_option("automation_user"), + secret=inventory.get_option("automation_secret"), + validate_certs=inventory.get_option("validate_certs"), + ) + + inventory.hosttaggroups = inventory._get_taggroups(api) + inventory.tags = [("tag_" + tag.get("id")) for tag in inventory.hosttaggroups] + inventory.sites = inventory._get_sites(api) + inventory.hosts = inventory._get_hosts(api) + + inventory._generate_groups() + inventory._populate() + + # Test if testhost1 exists + assert inventory.inventory.get_host("testhost1") + + # Test if sites don't exist as groups + for site in ["site_maintestsite", "site_remotetestsite"]: + assert site not in inventory.inventory.groups + + # Test of some tags don't exist as groups + for tag in [ + "tag_criticality_offline", + "tag_networking_lan", + "tag_piggyback_auto_piggyback", + "tag_snmp_ds_snmp_v2", + "tag_agent_cmk_agent", + ]: + assert tag not in inventory.inventory.groups diff --git a/tests/unit/plugins/module_utils/lookup_api.py b/tests/unit/plugins/module_utils/lookup_api.py new file mode 100644 index 000000000..2c73eba75 --- /dev/null +++ b/tests/unit/plugins/module_utils/lookup_api.py @@ -0,0 +1,1467 @@ +#!/usr/bin/env python +# -*- encoding: utf-8; py-indent-offset: 4 -*- + +# Copyright: (c) 2023, Lars Getwan +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +# This is a fake lookup_api to create static response for unit testing modules of the checkmk collection + + +class CheckMKLookupAPI: + """Base class to contact a Checkmk server for ~Lookup calls""" + + def __init__(self, site_url, user, secret, validate_certs=True): + self.site_url = site_url + self.user = user + self.secret = secret + self.validate_certs = validate_certs + self.url = "%s/check_mk/api/1.0" % site_url + self.headers = { + "Accept": "application/json", + "Content-Type": "application/json", + "Authorization": "Bearer %s %s" % (user, secret), + } + + def get(self, endpoint="", parameters=None): + if endpoint == "/domain-types/host_tag_group/collections/all": + # Example response from the endpoint "Show all host tag groups" + host_tag_group = """{ + "domainType": "host_tag_group", + "id": "host_tag", + "links": [ + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/domain-types/host_tag_group/collections/all", + "method": "GET", + "rel": "self", + "type": "application/json" + } + ], + "value": [ + { + "domainType": "host_tag_group", + "extensions": { + "tags": [ + { + "aux_tags": [], + "id": "prod", + "title": "Productive system" + }, + { + "aux_tags": [], + "id": "critical", + "title": "Business critical" + }, + { + "aux_tags": [], + "id": "test", + "title": "Test system" + }, + { + "aux_tags": [], + "id": "offline", + "title": "Do not monitor this host" + } + ], + "topic": "Tags" + }, + "id": "criticality", + "links": [ + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/criticality", + "method": "GET", + "rel": "self", + "type": "application/json" + }, + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/criticality", + "method": "PUT", + "rel": "urn:org.restfulobjects:rels/update", + "type": "application/json" + }, + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/criticality", + "method": "DELETE", + "rel": "urn:org.restfulobjects:rels/delete", + "type": "application/json" + } + ], + "members": { + "title": { + "format": "string", + "id": "title", + "links": [ + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/criticality/properties/title", + "method": "GET", + "rel": "self", + "type": "application/json" + } + ], + "memberType": "property", + "title": null, + "value": "Criticality" + } + }, + "title": "Criticality" + }, + { + "domainType": "host_tag_group", + "extensions": { + "tags": [ + { + "aux_tags": [], + "id": "lan", + "title": "Local network (low latency)" + }, + { + "aux_tags": [], + "id": "wan", + "title": "WAN (high latency)" + }, + { + "aux_tags": [], + "id": "dmz", + "title": "DMZ (low latency, secure access)" + } + ], + "topic": "Tags" + }, + "id": "networking", + "links": [ + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/networking", + "method": "GET", + "rel": "self", + "type": "application/json" + }, + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/networking", + "method": "PUT", + "rel": "urn:org.restfulobjects:rels/update", + "type": "application/json" + }, + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/networking", + "method": "DELETE", + "rel": "urn:org.restfulobjects:rels/delete", + "type": "application/json" + } + ], + "members": { + "title": { + "format": "string", + "id": "title", + "links": [ + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/networking/properties/title", + "method": "GET", + "rel": "self", + "type": "application/json" + } + ], + "memberType": "property", + "title": null, + "value": "Networking Segment" + } + }, + "title": "Networking Segment" + }, + { + "domainType": "host_tag_group", + "extensions": { + "tags": [ + { + "aux_tags": [], + "id": "tag1", + "title": "First tag" + }, + { + "aux_tags": [], + "id": null, + "title": "None tag" + }, + { + "aux_tags": [], + "id": "tag3", + "title": "Just another tag" + } + ], + "topic": "Tags" + }, + "id": "testtaggroup", + "links": [ + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/testtaggroup", + "method": "GET", + "rel": "self", + "type": "application/json" + }, + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/testtaggroup", + "method": "PUT", + "rel": "urn:org.restfulobjects:rels/update", + "type": "application/json" + }, + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/testtaggroup", + "method": "DELETE", + "rel": "urn:org.restfulobjects:rels/delete", + "type": "application/json" + } + ], + "members": { + "title": { + "format": "string", + "id": "title", + "links": [ + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/testtaggroup/properties/title", + "method": "GET", + "rel": "self", + "type": "application/json" + } + ], + "memberType": "property", + "title": null, + "value": "testtaggroup" + } + }, + "title": "testtaggroup" + }, + { + "domainType": "host_tag_group", + "extensions": { + "tags": [ + { + "aux_tags": [], + "id": "lonelytag", + "title": "yes" + } + ], + "topic": "Tags" + }, + "id": "lonelytag", + "links": [ + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/lonelytag", + "method": "GET", + "rel": "self", + "type": "application/json" + }, + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/lonelytag", + "method": "PUT", + "rel": "urn:org.restfulobjects:rels/update", + "type": "application/json" + }, + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/lonelytag", + "method": "DELETE", + "rel": "urn:org.restfulobjects:rels/delete", + "type": "application/json" + } + ], + "members": { + "title": { + "format": "string", + "id": "title", + "links": [ + { + "domainType": "link", + "href": "http://192.168.125.61/maintestsite/check_mk/api/1.0/objects/host_tag_group/lonelytag/properties/title", + "method": "GET", + "rel": "self", + "type": "application/json" + } + ], + "memberType": "property", + "title": null, + "value": "lonelytag" + } + }, + "title": "lonelytag" + }, + { + "domainType": "host_tag_group", + "extensions": { + "tags": [ + { + "aux_tags": [ + "tcp", + "checkmk-agent" + ], + "id": "cmk-agent", + "title": "API integrations if configured, else Checkmk agent" + }, + { + "aux_tags": [ + "tcp", + "checkmk-agent" + ], + "id": "all-agents", + "title": "Configured API integrations and Checkmk agent" + }, + { + "aux_tags": [ + "tcp" + ], + "id": "special-agents", + "title": "Configured API integrations, no Checkmk agent" + }, + { + "aux_tags": [], + "id": "no-agent", + "title": "No API integrations, no Checkmk agent" + } + ], + "topic": "Monitoring agents" + }, + "id": "agent", + "links": [ + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/agent", + "method": "GET", + "rel": "self", + "type": "application/json" + }, + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/agent", + "method": "PUT", + "rel": "urn:org.restfulobjects:rels/update", + "type": "application/json" + }, + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/agent", + "method": "DELETE", + "rel": "urn:org.restfulobjects:rels/delete", + "type": "application/json" + } + ], + "members": { + "title": { + "format": "string", + "id": "title", + "links": [ + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/agent/properties/title", + "method": "GET", + "rel": "self", + "type": "application/json" + } + ], + "memberType": "property", + "title": null, + "value": "Checkmk agent / API integrations" + } + }, + "title": "Checkmk agent / API integrations" + }, + { + "domainType": "host_tag_group", + "extensions": { + "help": "By default, each host has a piggyback data source. ...", + "tags": [ + { + "aux_tags": [], + "id": "auto-piggyback", + "title": "Use piggyback data from other hosts if present" + }, + { + "aux_tags": [], + "id": "piggyback", + "title": "Always use and expect piggyback data" + }, + { + "aux_tags": [], + "id": "no-piggyback", + "title": "Never use piggyback data" + } + ], + "topic": "Monitoring agents" + }, + "id": "piggyback", + "links": [ + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/piggyback", + "method": "GET", + "rel": "self", + "type": "application/json" + }, + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/piggyback", + "method": "PUT", + "rel": "urn:org.restfulobjects:rels/update", + "type": "application/json" + }, + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/piggyback", + "method": "DELETE", + "rel": "urn:org.restfulobjects:rels/delete", + "type": "application/json" + } + ], + "members": { + "title": { + "format": "string", + "id": "title", + "links": [ + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/piggyback/properties/title", + "method": "GET", + "rel": "self", + "type": "application/json" + } + ], + "memberType": "property", + "title": null, + "value": "Piggyback" + } + }, + "title": "Piggyback" + }, + { + "domainType": "host_tag_group", + "extensions": { + "tags": [ + { + "aux_tags": [], + "id": "no-snmp", + "title": "No SNMP" + }, + { + "aux_tags": [ + "snmp" + ], + "id": "snmp-v2", + "title": "SNMP v2 or v3" + }, + { + "aux_tags": [ + "snmp" + ], + "id": "snmp-v1", + "title": "SNMP v1" + } + ], + "topic": "Monitoring agents" + }, + "id": "snmp_ds", + "links": [ + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/snmp_ds", + "method": "GET", + "rel": "self", + "type": "application/json" + }, + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/snmp_ds", + "method": "PUT", + "rel": "urn:org.restfulobjects:rels/update", + "type": "application/json" + }, + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/snmp_ds", + "method": "DELETE", + "rel": "urn:org.restfulobjects:rels/delete", + "type": "application/json" + } + ], + "members": { + "title": { + "format": "string", + "id": "title", + "links": [ + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/snmp_ds/properties/title", + "method": "GET", + "rel": "self", + "type": "application/json" + } + ], + "memberType": "property", + "title": null, + "value": "SNMP" + } + }, + "title": "SNMP" + }, + { + "domainType": "host_tag_group", + "extensions": { + "tags": [ + { + "aux_tags": [ + "ip-v4" + ], + "id": "ip-v4-only", + "title": "IPv4 only" + }, + { + "aux_tags": [ + "ip-v6" + ], + "id": "ip-v6-only", + "title": "IPv6 only" + }, + { + "aux_tags": [ + "ip-v4", + "ip-v6" + ], + "id": "ip-v4v6", + "title": "IPv4/IPv6 dual-stack" + }, + { + "aux_tags": [], + "id": "no-ip", + "title": "No IP" + } + ], + "topic": "Address" + }, + "id": "address_family", + "links": [ + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/address_family", + "method": "GET", + "rel": "self", + "type": "application/json" + }, + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/address_family", + "method": "PUT", + "rel": "urn:org.restfulobjects:rels/update", + "type": "application/json" + }, + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/address_family", + "method": "DELETE", + "rel": "urn:org.restfulobjects:rels/delete", + "type": "application/json" + } + ], + "members": { + "title": { + "format": "string", + "id": "title", + "links": [ + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/host_tag_group/address_family/properties/title", + "method": "GET", + "rel": "self", + "type": "application/json" + } + ], + "memberType": "property", + "title": null, + "value": "IP address family" + } + }, + "title": "IP address family" + } + ] + }""" + return host_tag_group + + # Example response from the endpoint "Show all site connections" + if endpoint == "/domain-types/site_connection/collections/all": + site_connection = """{ + "domainType": "site_connection", + "extensions": {}, + "id": "site_connection", + "links": [ + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/domain-types/site_connection/collections/all", + "method": "GET", + "rel": "self", + "type": "application/json" + } + ], + "value": [ + { + "domainType": "site_connection", + "extensions": { + "basic_settings": { + "alias": "Local site maintestsite", + "customer": "provider", + "site_id": "maintestsite" + }, + "configuration_connection": { + "direct_login_to_web_gui_allowed": true, + "disable_remote_configuration": true, + "enable_replication": false, + "ignore_tls_errors": false, + "replicate_event_console": false, + "replicate_extensions": false, + "url_of_remote_site": "", + "user_sync": { + "sync_with_ldap_connections": "all" + } + }, + "status_connection": { + "connect_timeout": 5, + "connection": { + "socket_type": "local" + }, + "disable_in_status_gui": false, + "persistent_connection": false, + "proxy": { + "use_livestatus_daemon": "direct" + }, + "status_host": { + "status_host_set": "disabled" + }, + "url_prefix": "/maintestsite/" + } + }, + "id": "maintestsite", + "links": [ + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/site_connection/maintestsite", + "method": "GET", + "rel": "self", + "type": "application/json" + }, + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/site_connection/maintestsite", + "method": "PUT", + "rel": "urn:org.restfulobjects:rels/update", + "type": "application/json" + }, + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/site_connection/maintestsite", + "method": "DELETE", + "rel": "urn:org.restfulobjects:rels/delete", + "type": "application/json" + } + ], + "members": {}, + "title": "Local site maintestsite" + }, + { + "domainType": "site_connection", + "extensions": { + "basic_settings": { + "alias": "remotetestsite", + "customer": "provider", + "site_id": "remotetestsite" + }, + "configuration_connection": { + "direct_login_to_web_gui_allowed": true, + "disable_remote_configuration": true, + "enable_replication": true, + "ignore_tls_errors": false, + "replicate_event_console": true, + "replicate_extensions": true, + "url_of_remote_site": "http://192.168.11.12/remotetestsite/check_mk/", + "user_sync": { + "sync_with_ldap_connections": "all" + } + }, + "status_connection": { + "connect_timeout": 2, + "connection": { + "encrypted": true, + "host": "192.168.11.12", + "port": 6557, + "socket_type": "tcp", + "verify": true + }, + "disable_in_status_gui": false, + "persistent_connection": false, + "proxy": { + "global_settings": true, + "use_livestatus_daemon": "with_proxy" + }, + "status_host": { + "status_host_set": "disabled" + }, + "url_prefix": "" + } + }, + "id": "remotetestsite", + "links": [ + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/site_connection/remotetestsite", + "method": "GET", + "rel": "self", + "type": "application/json" + }, + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/site_connection/remotetestsite", + "method": "PUT", + "rel": "urn:org.restfulobjects:rels/update", + "type": "application/json" + }, + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/objects/site_connection/remotetestsite", + "method": "DELETE", + "rel": "urn:org.restfulobjects:rels/delete", + "type": "application/json" + } + ], + "members": {}, + "title": "remotetestsite" + } + ] + }""" + return site_connection + + # Example response from the endpoint "Show all hosts" + if endpoint == "/domain-types/host_config/collections/all": + host_config = """{ + "domainType": "host_config", + "id": "host", + "links": [ + { + "domainType": "link", + "href": "http://192.168.11.11/maintestsite/check_mk/api/1.0/domain-types/host_config/collections/all", + "method": "GET", + "rel": "self", + "type": "application/json" + } + ], + "value": [ + { + "domainType": "host_config", + "extensions": { + "attributes": { + "ipaddress": "192.168.11.11", + "meta_data": { + "created_at": "2024-08-28T07:32:19.415298+00:00", + "created_by": "cmkadmin", + "updated_at": "2024-09-05T07:25:47.164888+00:00" + }, + "site": "maintestsite", + "tag_agent": "cmk-agent" + }, + "cluster_nodes": null, + "effective_attributes": { + "additional_ipv4addresses": [], + "additional_ipv6addresses": [], + "alias": "", + "bake_agent_package": true, + "cmk_agent_connection": "pull-agent", + "contactgroups": { + "groups": [], + "recurse_perms": false, + "recurse_use": false, + "use": false, + "use_for_services": false + }, + "inventory_failed": false, + "ipaddress": "192.168.11.11", + "ipv6address": "", + "labels": {}, + "locked_attributes": [], + "locked_by": { + "instance_id": "", + "program_id": "", + "site_id": "maintestsite" + }, + "management_address": "", + "management_ipmi_credentials": null, + "management_protocol": "none", + "management_snmp_community": null, + "meta_data": { + "created_at": "2024-08-28T07:32:19.415298+00:00", + "created_by": "cmkadmin", + "updated_at": "2024-09-05T07:25:47.164888+00:00" + }, + "network_scan": { + "addresses": [], + "exclude_addresses": [], + "run_as": "cmkadmin", + "scan_interval": 86400, + "set_ip_address": true, + "tag_criticality": "offline", + "time_allowed": [ + { + "end": "23:59:59", + "start": "00:00:00" + } + ] + }, + "network_scan_result": { + "end": null, + "output": "", + "start": null, + "state": "running" + }, + "parents": [], + "site": "maintestsite", + "snmp_community": null, + "tag_address_family": "ip-v4-only", + "tag_agent": "cmk-agent", + "tag_criticality": "prod", + "tag_lonelytag": null, + "tag_networking": "lan", + "tag_piggyback": "auto-piggyback", + "tag_snmp_ds": "no-snmp", + "tag_testtaggroup": "tag1" + }, + "folder": "/main", + "is_cluster": false, + "is_offline": false + }, + "id": "checkmk-server-main", + "links": [], + "members": {}, + "title": "checkmk-server-main" + }, + { + "domainType": "host_config", + "extensions": { + "attributes": { + "ipaddress": "192.168.111.111", + "meta_data": { + "created_at": "2024-09-05T07:24:25.593371+00:00", + "created_by": "cmkadmin", + "updated_at": "2024-09-05T07:25:47.164953+00:00" + }, + "tag_criticality": "test" + }, + "cluster_nodes": null, + "effective_attributes": { + "additional_ipv4addresses": [], + "additional_ipv6addresses": [], + "alias": "", + "bake_agent_package": true, + "cmk_agent_connection": "pull-agent", + "contactgroups": { + "groups": [], + "recurse_perms": false, + "recurse_use": false, + "use": false, + "use_for_services": false + }, + "inventory_failed": false, + "ipaddress": "192.168.111.111", + "ipv6address": "", + "labels": {}, + "locked_attributes": [], + "locked_by": { + "instance_id": "", + "program_id": "", + "site_id": "maintestsite" + }, + "management_address": "", + "management_ipmi_credentials": null, + "management_protocol": "none", + "management_snmp_community": null, + "meta_data": { + "created_at": "2024-09-05T07:24:25.593371+00:00", + "created_by": "cmkadmin", + "updated_at": "2024-09-05T07:25:47.164953+00:00" + }, + "network_scan": { + "addresses": [], + "exclude_addresses": [], + "run_as": "cmkadmin", + "scan_interval": 86400, + "set_ip_address": true, + "tag_criticality": "offline", + "time_allowed": [ + { + "end": "23:59:59", + "start": "00:00:00" + } + ] + }, + "network_scan_result": { + "end": null, + "output": "", + "start": null, + "state": "running" + }, + "parents": [], + "site": "maintestsite", + "snmp_community": null, + "tag_address_family": "ip-v4-only", + "tag_agent": "cmk-agent", + "tag_criticality": "test", + "tag_lonelytag": null, + "tag_networking": "lan", + "tag_piggyback": "auto-piggyback", + "tag_snmp_ds": "no-snmp", + "tag_testtaggroup": "tag1" + }, + "folder": "/main", + "is_cluster": false, + "is_offline": false + }, + "id": "testhost1", + "links": [], + "members": {}, + "title": "testhost1" + }, + { + "domainType": "host_config", + "extensions": { + "attributes": { + "ipaddress": "192.168.111.222", + "meta_data": { + "created_at": "2024-09-05T07:24:40+00:00", + "created_by": "cmkadmin", + "updated_at": "2024-09-05T07:25:47.164986+00:00" + }, + "tag_agent": "cmk-agent", + "tag_snmp_ds": "snmp-v2" + }, + "cluster_nodes": null, + "effective_attributes": { + "additional_ipv4addresses": [], + "additional_ipv6addresses": [], + "alias": "", + "bake_agent_package": true, + "cmk_agent_connection": "pull-agent", + "contactgroups": { + "groups": [], + "recurse_perms": false, + "recurse_use": false, + "use": false, + "use_for_services": false + }, + "inventory_failed": false, + "ipaddress": "192.168.111.222", + "ipv6address": "", + "labels": {}, + "locked_attributes": [], + "locked_by": { + "instance_id": "", + "program_id": "", + "site_id": "maintestsite" + }, + "management_address": "", + "management_ipmi_credentials": null, + "management_protocol": "none", + "management_snmp_community": null, + "meta_data": { + "created_at": "2024-09-05T07:24:40+00:00", + "created_by": "cmkadmin", + "updated_at": "2024-09-05T07:25:47.164986+00:00" + }, + "network_scan": { + "addresses": [], + "exclude_addresses": [], + "run_as": "cmkadmin", + "scan_interval": 86400, + "set_ip_address": true, + "tag_criticality": "offline", + "time_allowed": [ + { + "end": "23:59:59", + "start": "00:00:00" + } + ] + }, + "network_scan_result": { + "end": null, + "output": "", + "start": null, + "state": "running" + }, + "parents": [], + "site": "maintestsite", + "snmp_community": null, + "tag_address_family": "ip-v4-only", + "tag_agent": "cmk-agent", + "tag_criticality": "prod", + "tag_lonelytag": null, + "tag_networking": "lan", + "tag_piggyback": "auto-piggyback", + "tag_snmp_ds": "snmp-v2", + "tag_testtaggroup": "tag1" + }, + "folder": "/main", + "is_cluster": false, + "is_offline": false + }, + "id": "testhost2", + "links": [], + "members": {}, + "title": "testhost2" + }, + { + "domainType": "host_config", + "extensions": { + "attributes": { + "meta_data": { + "created_at": "2024-09-05T07:25:47.154584+00:00", + "created_by": "cmkadmin", + "updated_at": "2024-09-05T07:25:47.165017+00:00" + }, + "tag_agent": "special-agents", + "tag_criticality": "critical" + }, + "cluster_nodes": null, + "effective_attributes": { + "additional_ipv4addresses": [], + "additional_ipv6addresses": [], + "alias": "", + "bake_agent_package": true, + "cmk_agent_connection": "pull-agent", + "contactgroups": { + "groups": [], + "recurse_perms": false, + "recurse_use": false, + "use": false, + "use_for_services": false + }, + "inventory_failed": false, + "ipaddress": "", + "ipv6address": "", + "labels": {}, + "locked_attributes": [], + "locked_by": { + "instance_id": "", + "program_id": "", + "site_id": "maintestsite" + }, + "management_address": "", + "management_ipmi_credentials": null, + "management_protocol": "none", + "management_snmp_community": null, + "meta_data": { + "created_at": "2024-09-05T07:25:47.154584+00:00", + "created_by": "cmkadmin", + "updated_at": "2024-09-05T07:25:47.165017+00:00" + }, + "network_scan": { + "addresses": [], + "exclude_addresses": [], + "run_as": "cmkadmin", + "scan_interval": 86400, + "set_ip_address": true, + "tag_criticality": "offline", + "time_allowed": [ + { + "end": "23:59:59", + "start": "00:00:00" + } + ] + }, + "network_scan_result": { + "end": null, + "output": "", + "start": null, + "state": "running" + }, + "parents": [], + "site": "maintestsite", + "snmp_community": null, + "tag_address_family": "ip-v4-only", + "tag_agent": "special-agents", + "tag_criticality": "critical", + "tag_lonelytag": null, + "tag_networking": "lan", + "tag_piggyback": "auto-piggyback", + "tag_snmp_ds": "no-snmp", + "tag_testtaggroup": null + }, + "folder": "/main", + "is_cluster": false, + "is_offline": false + }, + "id": "testhost3", + "links": [], + "members": {}, + "title": "testhost3" + }, + { + "domainType": "host_config", + "extensions": { + "attributes": { + "ipaddress": "192.168.11.15", + "meta_data": { + "created_at": "2024-08-28T07:36:30.805379+00:00", + "created_by": "cmkadmin", + "updated_at": "2024-09-05T07:27:18.569235+00:00" + }, + "site": "remotetestsite", + "tag_agent": "cmk-agent" + }, + "cluster_nodes": null, + "effective_attributes": { + "additional_ipv4addresses": [], + "additional_ipv6addresses": [], + "alias": "", + "bake_agent_package": true, + "cmk_agent_connection": "pull-agent", + "contactgroups": { + "groups": [], + "recurse_perms": false, + "recurse_use": false, + "use": false, + "use_for_services": false + }, + "inventory_failed": false, + "ipaddress": "192.168.11.15", + "ipv6address": "", + "labels": {}, + "locked_attributes": [], + "locked_by": { + "instance_id": "", + "program_id": "", + "site_id": "maintestsite" + }, + "management_address": "", + "management_ipmi_credentials": null, + "management_protocol": "none", + "management_snmp_community": null, + "meta_data": { + "created_at": "2024-08-28T07:36:30.805379+00:00", + "created_by": "cmkadmin", + "updated_at": "2024-09-05T07:27:18.569235+00:00" + }, + "network_scan": { + "addresses": [], + "exclude_addresses": [], + "run_as": "cmkadmin", + "scan_interval": 86400, + "set_ip_address": true, + "tag_criticality": "offline", + "time_allowed": [ + { + "end": "23:59:59", + "start": "00:00:00" + } + ] + }, + "network_scan_result": { + "end": null, + "output": "", + "start": null, + "state": "running" + }, + "parents": [], + "site": "remotetestsite", + "snmp_community": null, + "tag_address_family": "ip-v4-only", + "tag_agent": "cmk-agent", + "tag_criticality": "prod", + "tag_lonelytag": null, + "tag_networking": "lan", + "tag_piggyback": "auto-piggyback", + "tag_snmp_ds": "no-snmp", + "tag_testtaggroup": "tag1" + }, + "folder": "/remote", + "is_cluster": false, + "is_offline": false + }, + "id": "checkmk-server-remote", + "links": [], + "members": {}, + "title": "checkmk-server-remote" + }, + { + "domainType": "host_config", + "extensions": { + "attributes": { + "ipaddress": "192.168.222.11", + "meta_data": { + "created_at": "2024-09-05T07:26:23.703024+00:00", + "created_by": "cmkadmin", + "updated_at": "2024-09-05T07:27:18.569319+00:00" + }, + "site": "remotetestsite", + "tag_criticality": "test" + }, + "cluster_nodes": null, + "effective_attributes": { + "additional_ipv4addresses": [], + "additional_ipv6addresses": [], + "alias": "", + "bake_agent_package": true, + "cmk_agent_connection": "pull-agent", + "contactgroups": { + "groups": [], + "recurse_perms": false, + "recurse_use": false, + "use": false, + "use_for_services": false + }, + "inventory_failed": false, + "ipaddress": "192.168.222.11", + "ipv6address": "", + "labels": {}, + "locked_attributes": [], + "locked_by": { + "instance_id": "", + "program_id": "", + "site_id": "maintestsite" + }, + "management_address": "", + "management_ipmi_credentials": null, + "management_protocol": "none", + "management_snmp_community": null, + "meta_data": { + "created_at": "2024-09-05T07:26:23.703024+00:00", + "created_by": "cmkadmin", + "updated_at": "2024-09-05T07:27:18.569319+00:00" + }, + "network_scan": { + "addresses": [], + "exclude_addresses": [], + "run_as": "cmkadmin", + "scan_interval": 86400, + "set_ip_address": true, + "tag_criticality": "offline", + "time_allowed": [ + { + "end": "23:59:59", + "start": "00:00:00" + } + ] + }, + "network_scan_result": { + "end": null, + "output": "", + "start": null, + "state": "running" + }, + "parents": [], + "site": "remotetestsite", + "snmp_community": null, + "tag_address_family": "ip-v4-only", + "tag_agent": "cmk-agent", + "tag_criticality": "test", + "tag_lonelytag": null, + "tag_networking": "lan", + "tag_piggyback": "auto-piggyback", + "tag_snmp_ds": "no-snmp", + "tag_testtaggroup": "tag1" + }, + "folder": "/remote", + "is_cluster": false, + "is_offline": false + }, + "id": "testhost4", + "links": [], + "members": {}, + "title": "testhost4" + }, + { + "domainType": "host_config", + "extensions": { + "attributes": { + "meta_data": { + "created_at": "2024-09-05T07:26:49+00:00", + "created_by": "cmkadmin", + "updated_at": "2024-09-05T07:27:18.569355+00:00" + }, + "parents": [ + "testhost4" + ], + "site": "remotetestsite", + "tag_criticality": "test" + }, + "cluster_nodes": null, + "effective_attributes": { + "additional_ipv4addresses": [], + "additional_ipv6addresses": [], + "alias": "", + "bake_agent_package": true, + "cmk_agent_connection": "pull-agent", + "contactgroups": { + "groups": [], + "recurse_perms": false, + "recurse_use": false, + "use": false, + "use_for_services": false + }, + "inventory_failed": false, + "ipaddress": "", + "ipv6address": "", + "labels": {}, + "locked_attributes": [], + "locked_by": { + "instance_id": "", + "program_id": "", + "site_id": "maintestsite" + }, + "management_address": "", + "management_ipmi_credentials": null, + "management_protocol": "none", + "management_snmp_community": null, + "meta_data": { + "created_at": "2024-09-05T07:26:49+00:00", + "created_by": "cmkadmin", + "updated_at": "2024-09-05T07:27:18.569355+00:00" + }, + "network_scan": { + "addresses": [], + "exclude_addresses": [], + "run_as": "cmkadmin", + "scan_interval": 86400, + "set_ip_address": true, + "tag_criticality": "offline", + "time_allowed": [ + { + "end": "23:59:59", + "start": "00:00:00" + } + ] + }, + "network_scan_result": { + "end": null, + "output": "", + "start": null, + "state": "running" + }, + "parents": [ + "testhost4" + ], + "site": "remotetestsite", + "snmp_community": null, + "tag_address_family": "ip-v4-only", + "tag_agent": "cmk-agent", + "tag_criticality": "test", + "tag_lonelytag": null, + "tag_networking": "lan", + "tag_piggyback": "auto-piggyback", + "tag_snmp_ds": "no-snmp", + "tag_testtaggroup": "tag1" + }, + "folder": "/remote", + "is_cluster": false, + "is_offline": false + }, + "id": "testhost5", + "links": [], + "members": {}, + "title": "testhost5" + }, + { + "domainType": "host_config", + "extensions": { + "attributes": { + "meta_data": { + "created_at": "2024-09-05T07:27:18.560244+00:00", + "created_by": "cmkadmin", + "updated_at": "2024-09-05T07:27:18.569386+00:00" + }, + "site": "remotetestsite", + "tag_criticality": "critical" + }, + "cluster_nodes": null, + "effective_attributes": { + "additional_ipv4addresses": [], + "additional_ipv6addresses": [], + "alias": "", + "bake_agent_package": true, + "cmk_agent_connection": "pull-agent", + "contactgroups": { + "groups": [], + "recurse_perms": false, + "recurse_use": false, + "use": false, + "use_for_services": false + }, + "inventory_failed": false, + "ipaddress": "", + "ipv6address": "", + "labels": {}, + "locked_attributes": [], + "locked_by": { + "instance_id": "", + "program_id": "", + "site_id": "maintestsite" + }, + "management_address": "", + "management_ipmi_credentials": null, + "management_protocol": "none", + "management_snmp_community": null, + "meta_data": { + "created_at": "2024-09-05T07:27:18.560244+00:00", + "created_by": "cmkadmin", + "updated_at": "2024-09-05T07:27:18.569386+00:00" + }, + "network_scan": { + "addresses": [], + "exclude_addresses": [], + "run_as": "cmkadmin", + "scan_interval": 86400, + "set_ip_address": true, + "tag_criticality": "offline", + "time_allowed": [ + { + "end": "23:59:59", + "start": "00:00:00" + } + ] + }, + "network_scan_result": { + "end": null, + "output": "", + "start": null, + "state": "running" + }, + "parents": [], + "site": "remotetestsite", + "snmp_community": null, + "tag_address_family": "ip-v4-only", + "tag_agent": "cmk-agent", + "tag_criticality": "critical", + "tag_lonelytag": "lonelytag", + "tag_networking": "lan", + "tag_piggyback": "auto-piggyback", + "tag_snmp_ds": "no-snmp", + "tag_testtaggroup": "tag1" + }, + "folder": "/remote", + "is_cluster": false, + "is_offline": false + }, + "id": "testhost6", + "links": [], + "members": {}, + "title": "testhost6" + } + ] + }""" + return host_config