diff --git a/.github/workflows/ans-int-test-service_group.yaml b/.github/workflows/ans-int-test-service_group.yaml new file mode 100644 index 000000000..4e9cc352f --- /dev/null +++ b/.github/workflows/ans-int-test-service_group.yaml @@ -0,0 +1,65 @@ +# https://github.com/ansible-collections/collection_template/blob/main/.github/workflows/ansible-test.yml + +name: Ansible Integration Tests for Service Group Module +on: + workflow_dispatch: + pull_request: + branches: + - main + - devel + paths: + - 'plugins/modules/service_group.py' + +env: + NAMESPACE: tribe29 + COLLECTION_NAME: checkmk + +jobs: + +### +# Integration tests (RECOMMENDED) +# +# https://docs.ansible.com/ansible/latest/dev_guide/testing_integration.html + + integration: + runs-on: ubuntu-latest + name: Ⓐ${{ matrix.ansible }}+py${{ matrix.python }} + strategy: + fail-fast: false + matrix: + ansible: + - stable-2.12 + - stable-2.13 + - devel + python: + - '2.7' + - '3.6' + - '3.7' + - '3.8' + - '3.9' + - '3.10' + - '3.11' + exclude: + # Exclude unsupported sets. + - ansible: stable-2.12 + python: '3.11' + - ansible: stable-2.13 + python: '3.11' + + steps: + - name: Check out code + uses: actions/checkout@v3 + with: + path: ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: 3.9 + + - 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 integration test + run: ansible-test integration service_group -v --color --retry-on-error --continue-on-error --diff --python ${{ matrix.python }} --docker + working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f182882ed..60e16a129 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -86,10 +86,11 @@ Before running the action `Release Collection` against the `main` branch, the following needs to be done: 1. Update the collection version in `galaxy.yml` and `requirements.yml`. Look for `version:`. -2. Double check `changelogs/fragments` if all changes have a changelog. -3. After all changes have been performed, merge them into the `main` branch. -4. Release the collection by running the action `Release Collection` against the `main` branch. -5. Merge the automatically created pull request and update the `devel` branch from `main`. +2. Update the compatibility matrix in `SUPPORT.md`. +3. Double check `changelogs/fragments` if all changes have a changelog. +4. After all changes have been performed, merge them into the `main` branch. +5. Release the collection by running the action `Release Collection` against the `main` branch. +6. Merge the automatically created pull request and update the `devel` branch from `main`. ## Code of Conduct diff --git a/SUPPORT.md b/SUPPORT.md index 2ab726a9b..1c5e3b9db 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -6,3 +6,12 @@ This is just a side project and we can only work on this on a **part time and be Of course you can reach out in the [Checkmk Community (using the 'ansible' tag)](https://forum.checkmk.com/tag/ansible) or create [issues here](https://github.com/tribe29/ansible-collection-tribe29.checkmk/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc). + +# Compatibility Matrix +The following is a compatibility overview between this collection and Checkmk. We always try to track the most recent Checkmk version. This table will give you an overview which collection versions were tested against which Checkmk versions. +There is of course no guarantees, that there will be no issues at all, but we do our best in testing. On the other hand this obviously does not limit the collection versions to the Checkmk versions mentioned here. Most likely the collection will work with most current Checkmk versions. + +Collection Version | Checkmk Versions | Remarks +--- | --- | --- +0.12.0 | 2.1.0p11, 2.0.0p28 | None +0.13.0 | 2.1.0p17, 2.0.0p31 | None diff --git a/Vagrantfile b/Vagrantfile index de53078db..42c39cb4f 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -20,8 +20,8 @@ Vagrant.configure("2") do |config| $script = <<-SCRIPT apt-get update apt-get install -y python3-pip ca-certificates curl gnupg lsb-release - wget "https://download.checkmk.com/checkmk/2.1.0p13/check-mk-raw-2.1.0p13_0.focal_amd64.deb" -O /tmp/checkmk-stable.deb - wget "https://download.checkmk.com/checkmk/2.1.0p13/check-mk-raw-2.1.0p13_0.focal_amd64.deb" -O /tmp/checkmk-beta.deb + wget "https://download.checkmk.com/checkmk/2.1.0p17/check-mk-raw-2.1.0p17_0.focal_amd64.deb" -O /tmp/checkmk-stable.deb + wget "https://download.checkmk.com/checkmk/2.1.0p17/check-mk-raw-2.1.0p17_0.focal_amd64.deb" -O /tmp/checkmk-beta.deb apt-get install -y /tmp/checkmk-stable.deb omd create --admin-password 'd7589df1-01db-4eda-9858-dbcff8d0c361' stable apt-get install -y /tmp/checkmk-beta.deb diff --git a/changelogs/fragments/new-passwd-hashing.yml b/changelogs/fragments/new-passwd-hashing.yml new file mode 100644 index 000000000..78de6fb58 --- /dev/null +++ b/changelogs/fragments/new-passwd-hashing.yml @@ -0,0 +1,44 @@ +# https://docs.ansible.com/ansible/latest/community/development_process.html#changelogs-how-to + +minor_changes: + - Agent role - Now supports new password hashing according to L(Werk 14391,https://checkmk.com/werk/14391) + +## Line Format + +# When writing a changelog entry, use the following format: + +# - scope - description starting with a uppercase letter and ending with a period at the very end. Multiple sentences are allowed. + +# The scope is usually a module or plugin name or group of modules or plugins, for example, lookup plugins. While module names can (and should) be mentioned directly (foo_module), plugin names should always be followed by the type (foo inventory plugin). + +# For changes that are not really scoped (for example, which affect a whole collection), use the following format: + +# - Description starting with an uppercase letter and ending with a dot at the very end. Multiple sentences are allowed. + + +## Possible keys: + +# breaking_changes + +# Changes that break existing playbooks or roles. This includes any change to existing behavior that forces users to update tasks. Displayed in both the changelogs and the Porting Guides. +# major_changes + +# Major changes to Ansible itself. Generally does not include module or plugin changes. Displayed in both the changelogs and the Porting Guides. +# minor_changes + +# Minor changes to Ansible, modules, or plugins. This includes new features, new parameters added to modules, or behavior changes to existing parameters. +# deprecated_features + +# Features that have been deprecated and are scheduled for removal in a future release. Displayed in both the changelogs and the Porting Guides. +# removed_features + +# Features that were previously deprecated and are now removed. Displayed in both the changelogs and the Porting Guides. +# security_fixes + +# Fixes that address CVEs or resolve security concerns. Include links to CVE information. +# bugfixes + +# Fixes that resolve issues. +# known_issues + +# Known issues that are currently not fixed or will not be fixed. diff --git a/changelogs/fragments/service_group_module.yml b/changelogs/fragments/service_group_module.yml new file mode 100644 index 000000000..0ae6098b6 --- /dev/null +++ b/changelogs/fragments/service_group_module.yml @@ -0,0 +1,4 @@ +# https://docs.ansible.com/ansible/latest/community/development_process.html#changelogs-how-to + +major_changes: + - Add service_group module. diff --git a/galaxy.yml b/galaxy.yml index 8e1441bca..64514ef26 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -10,7 +10,7 @@ name: checkmk # The version of the collection. Must be compatible with semantic versioning -version: 0.12.0 +version: 0.13.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/meta/runtime.yml b/meta/runtime.yml index 5afb82055..4fff6d8d8 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -10,6 +10,7 @@ action_groups: - rule - tag_group - host_group + - service_group - contact_group # plugin_routing: diff --git a/playbooks/demo.yml b/playbooks/demo.yml index 8a2a4b417..58eb3356f 100644 --- a/playbooks/demo.yml +++ b/playbooks/demo.yml @@ -54,6 +54,19 @@ state: "fix_all" delegate_to: localhost + - name: "Create service groups." + service_group: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + name: "{{ item.name }}" + title: "{{ item.title | default(item.name) }}" + state: "present" + delegate_to: localhost + run_once: true + loop: "{{ checkmk_service_groups_create }}" + - name: "Pause to review first set of changes." ansible.builtin.pause: prompt: | @@ -129,6 +142,18 @@ delegate_to: localhost run_once: 'true' + - name: "Delete service groups." + service_group: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + name: "{{ item.name }}" + state: "absent" + delegate_to: localhost + run_once: true + loop: "{{ checkmk_service_groups_create }}" + - name: "Delete contact groups." contact_group: server_url: "{{ server_url }}" diff --git a/playbooks/hosts b/playbooks/hosts index 48de7c08f..268079509 100644 --- a/playbooks/hosts +++ b/playbooks/hosts @@ -7,4 +7,14 @@ test6.tld checkmk_folder_path="/bar/foo" test7.tld checkmk_folder_path="/" test8.tld checkmk_folder_path="/test" test9.tld checkmk_folder_path="/bar" -test10.tld checkmk_folder_path="/foo" \ No newline at end of file +test10.tld checkmk_folder_path="/foo" + +[vagrant] +ansibuntu ansible_host=192.168.56.61 +debsible ansible_host=192.168.56.62 +anstream ansible_host=192.168.56.63 +ansuse ansible_host=192.168.56.64 +ansles ansible_host=192.168.56.65 + +[vagrant:vars] +ansible_user=vagrant diff --git a/plugins/modules/service_group.py b/plugins/modules/service_group.py new file mode 100644 index 000000000..438ba2bf7 --- /dev/null +++ b/plugins/modules/service_group.py @@ -0,0 +1,501 @@ +#!/usr/bin/python +# -*- encoding: utf-8; py-indent-offset: 4 -*- + +# Copyright: (c) 2022, Michael Sekania & +# Robin Gierse +# 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: service_group + +short_description: Manage service groups in Checkmk (bulk version). + +# 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: "0.12.0" + +description: +- Manage service groups in Checkmk. + +extends_documentation_fragment: [tribe29.checkmk.common] + +options: + name: + description: The name of the service group to be created/modified/deleted. + type: str + title: + description: The title (alias) of your service group. If omitted defaults to the name. + type: str + groups: + description: + - instead of 'name', 'title' a list of dicts with elements of service group name and title (alias) to be created/modified/deleted. + If title is omitted in entry, it defaults to the service group name. + default: [] + type: raw + state: + description: The state of your service group. + type: str + default: present + choices: [present, absent] + validate_certs: + description: Whether to validate the SSL certificate of the Checkmk server. + default: true + type: bool + +author: + - Michael Sekania (@msekania) +""" + +EXAMPLES = r""" +# Create a single service group. +- name: "Create a single service group." + tribe29.checkmk.service_group: + server_url: "http://localhost/" + site: "my_site" + automation_user: "automation" + automation_secret: "$SECRET" + name: "my_service_group" + title: "My Service Group" + state: "present" + +# Create several service groups. +- name: "Create several service groups." + tribe29.checkmk.service_group: + server_url: "http://localhost/" + site: "my_site" + automation_user: "automation" + automation_secret: "$SECRET" + groups: + - name: "my_service_group_one" + title: "My Service Group One" + - name: "my_service_group_two" + title: "My Service Group Two" + - name: "my_service_group_test" + title: "My Test" + state: "present" + +# Create several service groups. +- name: "Create several service groups." + tribe29.checkmk.service_group: + server_url: "http://localhost/" + site: "my_site" + automation_user: "automation" + automation_secret: "$SECRET" + groups: + - name: "my_service_group_one" + title: "My Service Group One" + - name: "my_service_group_two" + - name: "my_service_group_test" + state: "present" + +# Delete a single service group. +- name: "Create a single service group." + tribe29.checkmk.service_group: + server_url: "http://localhost/" + site: "my_site" + automation_user: "automation" + automation_secret: "$SECRET" + name: "my_service_group" + state: "absent" + +# Delete several service groups. +- name: "Delete several service groups." + tribe29.checkmk.service_group: + server_url: "http://localhost/" + site: "my_site" + automation_user: "automation" + automation_secret: "$SECRET" + groups: + - name: "my_service_group_one" + - name: "my_service_group_two" + state: "absent" +""" + +RETURN = r""" +message: + description: The output message that the module generates. + type: str + returned: always + sample: 'Service group created.' +""" + +import json + +# https://docs.ansible.com/ansible/latest/dev_guide/testing/sanity/import.html +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.urls import fetch_url + + +def exit_failed(module, msg): + result = {"msg": msg, "changed": False, "failed": True} + module.fail_json(**result) + + +def exit_changed(module, msg): + result = {"msg": msg, "changed": True, "failed": False} + module.exit_json(**result) + + +def exit_ok(module, msg): + result = {"msg": msg, "changed": False, "failed": False} + module.exit_json(**result) + + +def get_current_single_service_group(module, base_url, headers): + current_state = "unknown" + current_title = "" + etag = "" + name = module.params["name"] + + api_endpoint = "/objects/service_group_config/" + name + url = base_url + api_endpoint + + response, info = fetch_url(module, url, data=None, headers=headers, method="GET") + + if info["status"] == 200: + body = json.loads(response.read()) + current_state = "present" + etag = info.get("etag", "") + current_title = body.get("title", name) + + elif info["status"] == 404: + current_state = "absent" + + else: + exit_failed( + module, + "Error calling API. HTTP code %d. Details: %s" + % (info["status"], info.get("body", "N/A")), + ) + + return current_state, current_title, etag + + +def get_current_service_groups(module, base_url, headers): + current_groups = [] + + api_endpoint = "/domain-types/service_group_config/collections/all" + url = base_url + api_endpoint + + response, info = fetch_url(module, url, headers=headers, method="GET") + + if info["status"] == 200: + body = json.loads(response.read()) + tmp = body.get("value", []) + current_groups = [ + { + "name": el.get("href").rsplit("/", 1)[-1], + "title": el.get("title", el.get("name")), + } + for el in tmp + ] + else: + exit_failed( + module, + "Error calling API in (collections). HTTP code %d. Details: %s" + % (info["status"], info.get("body", "N/A")), + ) + + return current_groups + + +def update_single_service_group(module, base_url, headers): + name = module.params["name"] + + api_endpoint = "/objects/service_group_config/" + name + params = { + "alias": module.params.get("title", name), + } + url = base_url + api_endpoint + + response, info = fetch_url( + module, url, module.jsonify(params), headers=headers, method="PUT" + ) + + if info["status"] != 200: + exit_failed( + module, + "Error calling API. HTTP code %d. Details: %s, " + % (info["status"], info["body"]), + ) + + +def update_service_groups(module, base_url, groups, headers): + api_endpoint = "/domain-types/service_group_config/actions/bulk-update/invoke" + params = { + "entries": [ + { + "name": el.get("name"), + "attributes": { + "alias": el.get("title", el.get("name")), + }, + } + for el in groups + ], + } + url = base_url + api_endpoint + + response, info = fetch_url( + module, url, module.jsonify(params), headers=headers, method="PUT" + ) + + if info["status"] != 200: + exit_failed( + module, + "Error calling API (bulk-update). HTTP code %d. Details: %s, " + % (info["status"], info["body"]), + ) + + +def create_single_service_group(module, base_url, headers): + name = module.params["name"] + + api_endpoint = "/domain-types/service_group_config/collections/all" + params = { + "name": name, + "alias": module.params.get("title", name), + } + url = base_url + api_endpoint + + response, info = fetch_url( + module, url, module.jsonify(params), headers=headers, method="POST" + ) + + if info["status"] != 200: + exit_failed( + module, + "Error calling API. HTTP code %d. Details: %s, " + % (info["status"], info["body"]), + ) + + +def create_service_groups(module, base_url, groups, headers): + api_endpoint = "/domain-types/service_group_config/actions/bulk-create/invoke" + params = { + "entries": [ + { + "name": el.get("name"), + "alias": el.get("title", el.get("name")), + } + for el in groups + ], + } + url = base_url + api_endpoint + + response, info = fetch_url( + module, url, module.jsonify(params), headers=headers, method="POST" + ) + + if info["status"] != 200: + exit_failed( + module, + "Error calling API (bulk-create). HTTP code %d. Details: %s, " + % (info["status"], info["body"]), + ) + + +def delete_single_service_group(module, base_url, headers): + api_endpoint = "/objects/service_group_config/" + module.params["name"] + url = base_url + api_endpoint + + response, info = fetch_url(module, url, data=None, headers=headers, method="DELETE") + + if info["status"] != 204: + exit_failed( + module, + "Error calling API. HTTP code %d. Details: %s, " + % (info["status"], info["body"]), + ) + + +def delete_service_groups(module, base_url, groups, headers): + api_endpoint = "/domain-types/service_group_config/actions/bulk-delete/invoke" + params = { + "entries": [el["name"] for el in groups], + } + url = base_url + api_endpoint + + response, info = fetch_url( + module, url, module.jsonify(params), headers=headers, method="POST" + ) + + if info["status"] != 204: + exit_failed( + module, + "Error calling API (bulk-delete). HTTP code %d. Details: %s, " + % (info["status"], info["body"]), + ) + + +def run_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), + name=dict(type="str", required=False), + title=dict(type="str", required=False), + groups=dict(type="raw", required=False), + state=dict(type="str", default="present", choices=["present", "absent"]), + ) + + module = AnsibleModule( + argument_spec=module_args, + mutually_exclusive=[ + ("groups", "name"), + ], + required_one_of=[ + ("groups", "name"), + ], + supports_check_mode=False, + ) + + # Use the parameters to initialize some common variables + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + "Authorization": "Bearer %s %s" + % ( + module.params.get("automation_user", ""), + module.params.get("automation_secret", ""), + ), + } + + base_url = "%s/%s/check_mk/api/1.0" % ( + module.params.get("server_url", ""), + module.params.get("site", ""), + ) + + # Determine desired state + state = module.params.get("state", "present") + + if ( + "groups" in module.params + and module.params.get("groups") + and len(module.params.get("groups", [])) > 0 + ): + if "title" in module.params and module.params.get("title", ""): + exit_failed( + module, + "'title' has only effect when 'name' is defined and not 'groups'", + ) + + groups = module.params.get("groups") + + # Determine which service groups do already exest + current_groups = get_current_service_groups(module, base_url, headers) + + # Determine intersection and difference with input, according to 'name' only + if len(set([el.get("name") for el in groups])) != len(groups): + exit_failed(module, "two or more entries with the same name!") + + listofnames = set([el.get("name") for el in current_groups]) + + intersection_list = [el for el in groups if el.get("name") in listofnames] + difference_list = [el for el in groups if not el.get("name") in listofnames] + + # Handle the service group accordingly to above findings and desired state + if state == "present": + # create service groups which do not exist (difference) + # update service groups that exist (intersection) + + msg_tokens = [] + + if len(difference_list) > 0: + create_service_groups(module, base_url, difference_list, headers) + msg_tokens.append( + "Service groups: " + + " ".join([el["name"] for el in difference_list]) + + " were created." + ) + + if len(intersection_list) > 0: + # determines difference between lists according to 'name' and 'title' pair + current_groups_dict = dict( + (el["name"], el["title"]) for el in current_groups + ) + remainings_list = [ + el + for el in intersection_list + if el.get("title") != current_groups_dict[el.get("name")] + ] + + if len(remainings_list) > 0: + changed = update_service_groups( + module, base_url, remainings_list, headers + ) + msg_tokens.append( + "Service groups: " + + " ".join([el["name"] for el in remainings_list]) + + " were updated." + ) + + if len(msg_tokens) >= 1: + exit_changed(module, " ".join(msg_tokens)) + else: + exit_ok(module, "Service groups already present.") + + elif state == "absent": + # delete service groups that exist (intersection_list) + if len(intersection_list) > 0: + # extra check if title-s (alias-es) match. + delete_service_groups(module, base_url, intersection_list, headers) + exit_changed( + module, + "Service groups: " + + " ".join([el["name"] for el in intersection_list]) + + " were deleted.", + ) + + else: + exit_failed(module, "Unknown error") + elif "name" in module.params and module.params.get("name", ""): + # Determine the current state of this particular service group + ( + current_state, + current_title, + etag, + ) = get_current_single_service_group(module, base_url, headers) + + # Handle the service group accordingly to above findings and desired state + if state == "present" and current_state == "present": + headers["If-Match"] = etag + msg_tokens = [] + + if current_title != module.params["title"]: + update_single_service_group(module, base_url, headers) + msg_tokens.append("Service group was updated.") + + if len(msg_tokens) >= 1: + exit_changed(module, " ".join(msg_tokens)) + else: + exit_ok(module, "Service group already present.") + + elif state == "present" and current_state == "absent": + create_single_service_group(module, base_url, headers) + exit_changed(module, "Service group created.") + + elif state == "absent" and current_state == "absent": + exit_ok(module, "Service group already absent.") + + elif state == "absent" and current_state == "present": + delete_single_service_group(module, base_url, headers) + exit_changed(module, "Service group deleted.") + + else: + exit_failed(module, "Unknown error") + else: + exit_failed(module, "One shoudl define either 'groups' or 'name'") + + +def main(): + run_module() + + +if __name__ == "__main__": + main() diff --git a/requirements.yml b/requirements.yml index 598ae2099..87e17d41d 100644 --- a/requirements.yml +++ b/requirements.yml @@ -1,4 +1,4 @@ --- collections: - name: tribe29.checkmk - version: 0.12.0 + version: 0.13.0 diff --git a/roles/server/defaults/main.yml b/roles/server/defaults/main.yml index 01703d0d2..1d1b07981 100644 --- a/roles/server/defaults/main.yml +++ b/roles/server/defaults/main.yml @@ -11,7 +11,7 @@ checkmk_server_server_stable_os: - RedHat-8 checkmk_server_edition: cre -checkmk_server_version: 2.1.0p13 +checkmk_server_version: 2.1.0p17 checkmk_server_verify_setup: 'true' checkmk_server_download_user: [] diff --git a/roles/server/molecule/2.0.0/group_vars/all.yml b/roles/server/molecule/2.0.0/group_vars/all.yml index 32f15d1fb..62ecd5d40 100644 --- a/roles/server/molecule/2.0.0/group_vars/all.yml +++ b/roles/server/molecule/2.0.0/group_vars/all.yml @@ -1,6 +1,6 @@ --- # General -checkmk_version: "2.0.0p28" +checkmk_version: "2.0.0p31" checkmk_edition: "cre" checkmk_site: "stable" server_url: "http://127.0.0.1/" 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 92a3e5c34..4c5dbea43 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_version: "2.1.0p11" +checkmk_version: "2.1.0p17" checkmk_edition: "cre" checkmk_site: "stable" server_url: "http://127.0.0.1/" diff --git a/roles/server/tasks/sites.yml b/roles/server/tasks/sites.yml index edd998897..f07f2c6b3 100644 --- a/roles/server/tasks/sites.yml +++ b/roles/server/tasks/sites.yml @@ -81,7 +81,7 @@ tags: - destroy-sites -- name: "Update Site Admin Password." +- name: "Update Site Admin Password for Checkmk < 2.1." become: true ansible.builtin.shell: | set -o pipefail @@ -90,6 +90,21 @@ executable: /bin/bash no_log: true loop: "{{ checkmk_server_sites }}" - when: item.state != "absent" + when: (item.state != "absent") and (item.version | regex_replace('p.*', '') is version('2.1', '<')) + tags: + - set-site-admin-pw + +# In the future this should be done with 'cmk-passwd' available from 2.1.0p16 (https://checkmk.com/werk/14389) +# To keep things simple, we do it in a more generic way here, which works in all 2.1 releases +- name: "Update Site Admin Password for Checkmk >= 2.1." + become: true + ansible.builtin.shell: | + set -o pipefail + echo '{{ item.admin_pw }}' | htpasswd -i -B -C 12 /omd/sites/{{ item.name }}/etc/htpasswd cmkadmin + args: + executable: /bin/bash + no_log: true + loop: "{{ checkmk_server_sites }}" + when: (item.state != "absent") and (item.version | regex_replace('p.*', '') is version('2.1', '>=')) tags: - set-site-admin-pw diff --git a/tests/integration/targets/activation/vars/main.yml b/tests/integration/targets/activation/vars/main.yml index aac4d7b05..46e07e9b2 100644 --- a/tests/integration/targets/activation/vars/main.yml +++ b/tests/integration/targets/activation/vars/main.yml @@ -1,8 +1,8 @@ --- checkmk_versions: - - version: "2.1.0p11" + - version: "2.1.0p17" site: "stable" - - version: "2.0.0p28" + - version: "2.0.0p31" site: "oldstable" download_url: "https://download.checkmk.com/checkmk/{{ item.version }}/check-mk-raw-{{ item.version }}_0.{{ ansible_distribution_release }}_amd64.deb" server_url: "http://127.0.0.1/" diff --git a/tests/integration/targets/contact_group/vars/main.yml b/tests/integration/targets/contact_group/vars/main.yml index dbadccec4..0e6d18384 100644 --- a/tests/integration/targets/contact_group/vars/main.yml +++ b/tests/integration/targets/contact_group/vars/main.yml @@ -1,8 +1,8 @@ --- checkmk_versions: - - version: "2.1.0p11" + - version: "2.1.0p17" site: "stable" - - version: "2.0.0p28" + - version: "2.0.0p31" site: "oldstable" download_url: "https://download.checkmk.com/checkmk/{{ item.version }}/check-mk-raw-{{ item.version }}_0.{{ ansible_distribution_release }}_amd64.deb" server_url: "http://127.0.0.1/" diff --git a/tests/integration/targets/discovery/vars/main.yml b/tests/integration/targets/discovery/vars/main.yml index aac4d7b05..46e07e9b2 100644 --- a/tests/integration/targets/discovery/vars/main.yml +++ b/tests/integration/targets/discovery/vars/main.yml @@ -1,8 +1,8 @@ --- checkmk_versions: - - version: "2.1.0p11" + - version: "2.1.0p17" site: "stable" - - version: "2.0.0p28" + - version: "2.0.0p31" site: "oldstable" download_url: "https://download.checkmk.com/checkmk/{{ item.version }}/check-mk-raw-{{ item.version }}_0.{{ ansible_distribution_release }}_amd64.deb" server_url: "http://127.0.0.1/" diff --git a/tests/integration/targets/downtime/vars/main.yml b/tests/integration/targets/downtime/vars/main.yml index aac4d7b05..46e07e9b2 100644 --- a/tests/integration/targets/downtime/vars/main.yml +++ b/tests/integration/targets/downtime/vars/main.yml @@ -1,8 +1,8 @@ --- checkmk_versions: - - version: "2.1.0p11" + - version: "2.1.0p17" site: "stable" - - version: "2.0.0p28" + - version: "2.0.0p31" site: "oldstable" download_url: "https://download.checkmk.com/checkmk/{{ item.version }}/check-mk-raw-{{ item.version }}_0.{{ ansible_distribution_release }}_amd64.deb" server_url: "http://127.0.0.1/" diff --git a/tests/integration/targets/folder/vars/main.yml b/tests/integration/targets/folder/vars/main.yml index 9306aa314..649e6ee5b 100644 --- a/tests/integration/targets/folder/vars/main.yml +++ b/tests/integration/targets/folder/vars/main.yml @@ -1,8 +1,8 @@ --- checkmk_versions: - - version: "2.1.0p11" + - version: "2.1.0p17" site: "stable" - - version: "2.0.0p28" + - version: "2.0.0p31" site: "oldstable" download_url: "https://download.checkmk.com/checkmk/{{ item.version }}/check-mk-raw-{{ item.version }}_0.{{ ansible_distribution_release }}_amd64.deb" server_url: "http://127.0.0.1/" diff --git a/tests/integration/targets/host/vars/main.yml b/tests/integration/targets/host/vars/main.yml index aac4d7b05..46e07e9b2 100644 --- a/tests/integration/targets/host/vars/main.yml +++ b/tests/integration/targets/host/vars/main.yml @@ -1,8 +1,8 @@ --- checkmk_versions: - - version: "2.1.0p11" + - version: "2.1.0p17" site: "stable" - - version: "2.0.0p28" + - version: "2.0.0p31" site: "oldstable" download_url: "https://download.checkmk.com/checkmk/{{ item.version }}/check-mk-raw-{{ item.version }}_0.{{ ansible_distribution_release }}_amd64.deb" server_url: "http://127.0.0.1/" diff --git a/tests/integration/targets/host_group/vars/main.yml b/tests/integration/targets/host_group/vars/main.yml index 6d12ec835..da9183649 100644 --- a/tests/integration/targets/host_group/vars/main.yml +++ b/tests/integration/targets/host_group/vars/main.yml @@ -1,8 +1,8 @@ --- checkmk_versions: - - version: "2.1.0p11" + - version: "2.1.0p17" site: "stable" - - version: "2.0.0p28" + - version: "2.0.0p31" site: "oldstable" download_url: "https://download.checkmk.com/checkmk/{{ item.version }}/check-mk-raw-{{ item.version }}_0.{{ ansible_distribution_release }}_amd64.deb" server_url: "http://127.0.0.1/" diff --git a/tests/integration/targets/rule/vars/main.yml b/tests/integration/targets/rule/vars/main.yml index d2f4f31bc..a9241e8e6 100644 --- a/tests/integration/targets/rule/vars/main.yml +++ b/tests/integration/targets/rule/vars/main.yml @@ -1,6 +1,6 @@ --- checkmk_versions: - - version: "2.1.0p10" + - version: "2.1.0p17" site: "stable" download_url: "https://download.checkmk.com/checkmk/{{ item.version }}/check-mk-raw-{{ item.version }}_0.{{ ansible_distribution_release }}_amd64.deb" site: "test" diff --git a/tests/integration/targets/service_group/tasks/main.yml b/tests/integration/targets/service_group/tasks/main.yml new file mode 100644 index 000000000..fade565a7 --- /dev/null +++ b/tests/integration/targets/service_group/tasks/main.yml @@ -0,0 +1,15 @@ +--- +- name: "Run preparations." + ansible.builtin.include_tasks: prep.yml + +- name: "Wait for site to be ready." + ansible.builtin.pause: + seconds: 5 + when: "'OVERALL 1' in item.stdout_lines" + loop: "{{ site_status.results }}" + +- name: "Testing." + ansible.builtin.include_tasks: test.yml + loop: "{{ checkmk_versions }}" + loop_control: + loop_var: outer_item diff --git a/tests/integration/targets/service_group/tasks/prep.yml b/tests/integration/targets/service_group/tasks/prep.yml new file mode 100644 index 000000000..0be779f8c --- /dev/null +++ b/tests/integration/targets/service_group/tasks/prep.yml @@ -0,0 +1,25 @@ +--- +- name: "Download Checkmk Versions." + ansible.builtin.get_url: + url: "{{ download_url }}" + dest: /tmp/checkmk-server-{{ item.site }}.deb + mode: 0640 + loop: "{{ checkmk_versions }}" + +- name: "Install Checkmk Versions." + ansible.builtin.apt: + deb: /tmp/checkmk-server-{{ item.site }}.deb + state: present + loop: "{{ checkmk_versions }}" + +- name: "Create Sites." + ansible.builtin.command: "omd create --no-tmpfs --admin-password {{ automation_secret }} {{ item.site }}" + args: + creates: "/omd/sites/{{ item.site }}" + loop: "{{ checkmk_versions }}" + +- name: "Start Sites." + ansible.builtin.shell: "omd status -b {{ item.site }} || omd start {{ item.site }}" + register: site_status + changed_when: site_status.rc == "0" + loop: "{{ checkmk_versions }}" diff --git a/tests/integration/targets/service_group/tasks/test.yml b/tests/integration/targets/service_group/tasks/test.yml new file mode 100644 index 000000000..245e87e57 --- /dev/null +++ b/tests/integration/targets/service_group/tasks/test.yml @@ -0,0 +1,195 @@ +--- +- name: "{{ outer_item.version }} - Create service groups." + service_group: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + name: "{{ item.name }}" + title: "{{ item.title | default(item.name) }}" + state: "present" + delegate_to: localhost + run_once: true + loop: "{{ checkmk_service_groups_create }}" + +- name: "{{ outer_item.version }} - Should fail because of 'groups', Create service groups." + service_group: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + name: "{{ item.name }}" + title: "{{ item.title | default(item.name) }}" + groups: checkmk_service_groups_create + state: "present" + delegate_to: localhost + run_once: true + loop: "{{ checkmk_service_groups_create }}" + ignore_errors: true + +- name: "{{ outer_item.version }} - Activate." + activation: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: true + sites: + - "{{ outer_item.site }}" + delegate_to: localhost + run_once: true + +- name: "{{ outer_item.version }} - Modify part of service groups (also checks for idempotency!)." + service_group: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + name: "{{ item.name | default(item.name) }}" + title: "{{ item.title }}" + state: "present" + delegate_to: localhost + run_once: true + loop: "{{ checkmk_service_groups_modify }}" + +- name: "{{ outer_item.version }} - Activate." + activation: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: true + sites: + - "{{ outer_item.site }}" + delegate_to: localhost + run_once: true + +- name: "{{ outer_item.version }} - Delete service groups." + service_group: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + name: "{{ item.name }}" + state: "absent" + delegate_to: localhost + run_once: true + loop: "{{ checkmk_service_groups_delete }}" + +- name: "{{ outer_item.version }} - Activate." + activation: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: true + sites: + - "{{ outer_item.site }}" + delegate_to: localhost + run_once: true + +- name: "{{ outer_item.version }} - Delete service groups that were created at the beginning (also check for idempotency)." + service_group: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + name: "{{ item.name }}" + state: "absent" + delegate_to: localhost + run_once: true + loop: "{{ checkmk_service_groups_create }}" + +- name: "{{ outer_item.version }} - Activate." + activation: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: true + sites: + - "{{ outer_item.site }}" + delegate_to: localhost + run_once: true + +- name: "{{ outer_item.version }} - Bulk create service groups." + service_group: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + groups: "{{ checkmk_service_groups_create }}" + state: "present" + delegate_to: localhost + run_once: true + +- name: "{{ outer_item.version }} - Should fail because of 'name', Bulk create service groups." + service_group: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + groups: "{{ checkmk_service_groups_create }}" + name: "test" + state: "present" + delegate_to: localhost + run_once: true + ignore_errors: true + +- name: "{{ outer_item.version }} - Should fail because of 'title', Bulk create service groups." + service_group: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + groups: "{{ checkmk_service_groups_create }}" + title: "Test" + state: "present" + delegate_to: localhost + run_once: true + ignore_errors: true + +- name: "{{ outer_item.version }} - Bulk modify part of service groups (also checks for idempotency!)." + service_group: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + groups: "{{ checkmk_service_groups_modify }}" + state: "present" + delegate_to: localhost + run_once: true + +- name: "{{ outer_item.version }} - Bulk delete service groups." + service_group: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + groups: "{{ checkmk_service_groups_delete }}" + state: "absent" + delegate_to: localhost + run_once: true + +- name: "{{ outer_item.version }} - Bulk delete service groups that were created at the beginning (also check for idempotency)." + service_group: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + groups: "{{ checkmk_service_groups_create }}" + state: "absent" + delegate_to: localhost + run_once: true + +- name: "{{ outer_item.version }} - Activate." + activation: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: true + sites: + - "{{ outer_item.site }}" + delegate_to: localhost + run_once: true diff --git a/tests/integration/targets/service_group/vars/main.yml b/tests/integration/targets/service_group/vars/main.yml new file mode 100644 index 000000000..0d4a572ff --- /dev/null +++ b/tests/integration/targets/service_group/vars/main.yml @@ -0,0 +1,37 @@ +--- +checkmk_versions: + - version: "2.1.0p17" + site: "stable" + - version: "2.0.0p31" + site: "oldstable" +download_url: "https://download.checkmk.com/checkmk/{{ item.version }}/check-mk-raw-{{ item.version }}_0.{{ ansible_distribution_release }}_amd64.deb" +server_url: "http://127.0.0.1/" +automation_user: "cmkadmin" +automation_secret: "d7589df1-01db-4eda-9858-dbcff8d0c361" + +checkmk_service_groups_create: + - name: "test1" + title: "Test_1" + - name: "test2" + - name: "test3" + - name: "test4" + title: "Test_4" + - name: "test5" + title: "Test_5" + +# 2 and 3 remains unchanged, good for idempotency check +checkmk_service_groups_modify: + - name: "test1" + title: "Test_one" + - name: "test2" + title: "Test_2" + - name: "test3" + title: "Test_3" + - name: "test4" + title: "Test_four" + - name: "test5" + title: "Test_five" + +checkmk_service_groups_delete: + - name: "test1" + - name: "test3" diff --git a/tests/integration/targets/tag_group/vars/main.yml b/tests/integration/targets/tag_group/vars/main.yml index a1789fd95..a70b2c27a 100644 --- a/tests/integration/targets/tag_group/vars/main.yml +++ b/tests/integration/targets/tag_group/vars/main.yml @@ -1,8 +1,8 @@ --- checkmk_versions: - - version: "2.1.0p11" + - version: "2.1.0p17" site: "stable" - - version: "2.0.0p28" + - version: "2.0.0p31" site: "oldstable" download_url: "https://download.checkmk.com/checkmk/{{ item.version }}/check-mk-raw-{{ item.version }}_0.{{ ansible_distribution_release }}_amd64.deb" server_url: "http://127.0.0.1/"