diff --git a/.ansible-lint b/.ansible-lint index f76898413..776d394bf 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -40,6 +40,7 @@ use_default_rules: true # This makes linter to fully ignore rules/tags listed below skip_list: - experimental + - name[template] # https://ansible-lint.readthedocs.io/rules/name/ # # Any rule that has the 'opt-in' tag will not be loaded unless its 'id' is # # mentioned in the enable_list: diff --git a/.github/workflows/ans-int-test-host_group.yaml b/.github/workflows/ans-int-test-host_group.yaml new file mode 100644 index 000000000..0690b07a1 --- /dev/null +++ b/.github/workflows/ans-int-test-host_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 Host Group Module +on: + workflow_dispatch: + pull_request: + branches: + - main + - devel + paths: + - 'plugins/modules/host_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 host_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/.github/workflows/ans-int-test-tag_group.yaml b/.github/workflows/ans-int-test-tag_group.yaml new file mode 100644 index 000000000..d44eb8292 --- /dev/null +++ b/.github/workflows/ans-int-test-tag_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 Tag Group Module +on: + workflow_dispatch: + pull_request: + branches: + - main + - devel + paths: + - 'plugins/modules/tag_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 tag_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/.github/workflows/molecule-role-server.yaml b/.github/workflows/molecule-role-server.yaml index 095567a12..1546acb3f 100644 --- a/.github/workflows/molecule-role-server.yaml +++ b/.github/workflows/molecule-role-server.yaml @@ -49,7 +49,7 @@ jobs: path: ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} - name: Set up Python ${{ matrix.python }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} diff --git a/Vagrantfile b/Vagrantfile index d43bf1861..de53078db 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -19,17 +19,25 @@ Vagrant.configure("2") do |config| end $script = <<-SCRIPT apt-get update - apt-get install -y python3-pip - wget "https://download.checkmk.com/checkmk/2.1.0p10/check-mk-raw-2.1.0p10_0.focal_amd64.deb" -O /tmp/checkmk-stable.deb - wget "https://download.checkmk.com/checkmk/2.1.0p10/check-mk-raw-2.1.0p10_0.focal_amd64.deb" -O /tmp/checkmk-beta.deb + 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 apt-get install -y /tmp/checkmk-stable.deb - omd create --admin-password 'cmk' stable + omd create --admin-password 'd7589df1-01db-4eda-9858-dbcff8d0c361' stable apt-get install -y /tmp/checkmk-beta.deb - omd create --admin-password 'cmk' beta + omd create --admin-password 'd7589df1-01db-4eda-9858-dbcff8d0c361' beta omd status -b stable || omd start stable omd status -b beta || omd start beta pip install -r /vagrant/requirements.txt sudo -u vagrant ansible-galaxy collection install -f -r /vagrant/requirements.yml + mkdir -p /home/vagrant/ansible_collections/tribe29/checkmk + rsync -avzh /vagrant/* /home/vagrant/ansible_collections/tribe29/checkmk/ + mkdir -p /etc/apt/keyrings + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg + echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null + apt-get update + apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin + usermod -aG docker vagrant SCRIPT srv.vm.provision "shell", inline: $script end @@ -90,4 +98,18 @@ Vagrant.configure("2") do |config| inline: "zypper --quiet up -y" end +# SLES15 + config.vm.define "ansles", autostart: false , primary: false do |srv| + srv.vm.box = "saltstack/cicd-sles15" + srv.vm.network "private_network", ip: "192.168.56.65" + srv.ssh.insert_key = false + srv.vm.provider "virtualbox" do |v| + v.name = 'ansuse' + v.memory = 2048 + v.cpus = 2 + end + srv.vm.provision "shell", + inline: "zypper --quiet up -y" +end + end diff --git a/changelogs/fragments/agent_role.yml b/changelogs/fragments/agent_role.yml new file mode 100644 index 000000000..563014b82 --- /dev/null +++ b/changelogs/fragments/agent_role.yml @@ -0,0 +1,45 @@ +minor_changes: + - Agent role - (Actually in v0.10.0) Fix authentication handling, where several tasks would fail, when using a secret. + - Agent role - Add support for CME. + +# https://docs.ansible.com/ansible/latest/community/development_process.html#changelogs-how-to + +## 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/host_group_module.yml b/changelogs/fragments/host_group_module.yml new file mode 100644 index 000000000..3dfb57fea --- /dev/null +++ b/changelogs/fragments/host_group_module.yml @@ -0,0 +1,4 @@ +# https://docs.ansible.com/ansible/latest/community/development_process.html#changelogs-how-to + +major_changes: + - Add host_group module. diff --git a/changelogs/fragments/tag_group_module.yml b/changelogs/fragments/tag_group_module.yml new file mode 100644 index 000000000..616913a02 --- /dev/null +++ b/changelogs/fragments/tag_group_module.yml @@ -0,0 +1,4 @@ +# https://docs.ansible.com/ansible/latest/community/development_process.html#changelogs-how-to + +major_changes: + - Add tag_group module. diff --git a/galaxy.yml b/galaxy.yml index 8ec30196c..311638ecc 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.10.0 +version: 0.11.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 0741d92a4..fac7fe05a 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -8,6 +8,8 @@ action_groups: - host - downtime - rule + - tag_group + - host_group # plugin_routing: # modules: diff --git a/plugins/modules/host_group.py b/plugins/modules/host_group.py new file mode 100644 index 000000000..9a3855e2f --- /dev/null +++ b/plugins/modules/host_group.py @@ -0,0 +1,507 @@ +#!/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: host_group + +short_description: Manage host 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.11.0" + +description: +- Manage host groups in Checkmk. + +extends_documentation_fragment: [tribe29.checkmk.common] + +options: + host_group_name: + description: The name of the host group to be created/modified/deleted. + type: str + title: + description: The title (alias) of your host group. If omitted defaults to the host_group_name. + type: str + host_groups: + description: + - instead of 'host_group_name', 'title' a list of dicts with elements of host group name and title (alias) to be created/modified/deleted. + If title is omitted in entry, it defaults to the host group name. + default: [] + type: raw + state: + description: The state of your host 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 host group. +- name: "Create a single host group." + tribe29.checkmk.host_group: + server_url: "http://localhost/" + site: "my_site" + automation_user: "automation" + automation_secret: "$SECRET" + host_group_name: "my_host_group" + title: "My Host Group" + state: "present" + +# Create several host groups. +- name: "Create several host groups." + tribe29.checkmk.host_group: + server_url: "http://localhost/" + site: "my_site" + automation_user: "automation" + automation_secret: "$SECRET" + host_groups: + - name: "my_host_group_one" + title: "My Host Group One" + - name: "my_host_group_two" + title: "My Host Group Two" + - name: "my_host_group_test" + title: "My Test" + state: "present" + +# Create several host groups. +- name: "Create several host groups." + tribe29.checkmk.host_group: + server_url: "http://localhost/" + site: "my_site" + automation_user: "automation" + automation_secret: "$SECRET" + host_groups: + - name: "my_host_group_one" + title: "My Host Group One" + - name: "my_host_group_two" + - name: "my_host_group_test" + state: "present" + +# Delete a single host group. +- name: "Create a single host group." + tribe29.checkmk.host_group: + server_url: "http://localhost/" + site: "my_site" + automation_user: "automation" + automation_secret: "$SECRET" + host_group_name: "my_host_group" + state: "absent" + +# Delete several host groups. +- name: "Delete several host groups." + tribe29.checkmk.host_group: + server_url: "http://localhost/" + site: "my_site" + automation_user: "automation" + automation_secret: "$SECRET" + host_groups: + - name: "my_host_group_one" + - name: "my_host_group_two" + state: "absent" +""" + +RETURN = r""" +message: + description: The output message that the module generates. + type: str + returned: always + sample: 'Host 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_host_group(module, base_url, headers): + current_state = "unknown" + current_title = "" + etag = "" + host_group_name = module.params["host_group_name"] + + api_endpoint = "/objects/host_group_config/" + host_group_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", host_group_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_host_groups(module, base_url, headers): + current_groups = [] + + api_endpoint = "/domain-types/host_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_host_group(module, base_url, headers): + host_group_name = module.params["host_group_name"] + + api_endpoint = "/objects/host_group_config/" + host_group_name + params = { + "alias": module.params.get("title", host_group_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_host_groups(module, base_url, host_groups, headers): + api_endpoint = "/domain-types/host_group_config/actions/bulk-update/invoke" + params = { + "entries": [ + { + "name": el.get("name"), + "attributes": { + "alias": el.get("title", el.get("name")), + }, + } + for el in host_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_host_group(module, base_url, headers): + host_group_name = module.params["host_group_name"] + + api_endpoint = "/domain-types/host_group_config/collections/all" + params = { + "name": host_group_name, + "alias": module.params.get("title", host_group_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_host_groups(module, base_url, host_groups, headers): + api_endpoint = "/domain-types/host_group_config/actions/bulk-create/invoke" + params = { + "entries": [ + { + "name": el.get("name"), + "alias": el.get("title", el.get("name")), + } + for el in host_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_host_group(module, base_url, headers): + api_endpoint = "/objects/host_group_config/" + module.params["host_group_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_host_groups(module, base_url, host_groups, headers): + api_endpoint = "/domain-types/host_group_config/actions/bulk-delete/invoke" + params = { + "entries": [el["name"] for el in host_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), + host_group_name=dict(type="str", required=False), + title=dict(type="str", required=False), + host_groups=dict(type="raw", required=False), + state=dict(type="str", default="present", choices=["present", "absent"]), + ) + + module = AnsibleModule( + argument_spec=module_args, + mutually_exclusive=[ + ("host_groups", "host_group_name"), + ], + required_one_of=[ + ("host_groups", "host_group_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 ( + "host_groups" in module.params + and module.params.get("host_groups") + and len(module.params.get("host_groups", [])) > 0 + ): + if "title" in module.params and module.params.get("title", ""): + exit_failed( + module, + "'title' has only effect when 'host_group_name' is defined and not 'host_groups'", + ) + + host_groups = module.params.get("host_groups") + + # Determine which host groups do already exest + current_groups = get_current_host_groups(module, base_url, headers) + + # Determine intersection and difference with input, according to 'name' only + if len(set([el.get("name") for el in host_groups])) != len(host_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 host_groups if el.get("name") in listofnames] + difference_list = [ + el for el in host_groups if not el.get("name") in listofnames + ] + + # Handle the host group accordingly to above findings and desired state + if state == "present": + # create host groups which do not exist (difference) + # update host groups that exist (intersection) + + msg_tokens = [] + + if len(difference_list) > 0: + create_host_groups(module, base_url, difference_list, headers) + msg_tokens.append( + "Host 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_host_groups( + module, base_url, remainings_list, headers + ) + msg_tokens.append( + "Host 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, "Host groups already present.") + + elif state == "absent": + # delete host groups that exist (intersection_list) + if len(intersection_list) > 0: + # extra check if title-s (alias-es) match. + delete_host_groups(module, base_url, intersection_list, headers) + exit_changed( + module, + "Host groups: " + + " ".join([el["name"] for el in intersection_list]) + + " were deleted.", + ) + + else: + exit_failed(module, "Unknown error") + elif "host_group_name" in module.params and module.params.get( + "host_group_name", "" + ): + # Determine the current state of this particular host group + ( + current_state, + current_title, + etag, + ) = get_current_single_host_group(module, base_url, headers) + + # Handle the host 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_host_group(module, base_url, headers) + msg_tokens.append("Host group was updated.") + + if len(msg_tokens) >= 1: + exit_changed(module, " ".join(msg_tokens)) + else: + exit_ok(module, "Host group already present.") + + elif state == "present" and current_state == "absent": + create_single_host_group(module, base_url, headers) + exit_changed(module, "Host group created.") + + elif state == "absent" and current_state == "absent": + exit_ok(module, "Host group already absent.") + + elif state == "absent" and current_state == "present": + delete_single_host_group(module, base_url, headers) + exit_changed(module, "Host group deleted.") + + else: + exit_failed(module, "Unknown error") + else: + exit_failed( + module, "One shoudl define either 'host_groups' or 'host_group_name'" + ) + + +def main(): + run_module() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/tag_group.py b/plugins/modules/tag_group.py new file mode 100644 index 000000000..26eba2c8d --- /dev/null +++ b/plugins/modules/tag_group.py @@ -0,0 +1,400 @@ +#!/usr/bin/python +# -*- encoding: utf-8; py-indent-offset: 4 -*- + +# Copyright: (c) 2022, Stefan Mühling & +# 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: tag_group + +short_description: Manage tag_group within 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: "0.11.0" + +description: +- Manage tag_group within Checkmk. + +extends_documentation_fragment: [tribe29.checkmk.common] + +options: + id: + description: The id of the tag_group to be created/ + modified/deleted. + default: "" + type: str + title: + description: The title of the tag_group + default: "" + type: str + topic: + description: The topic of the tag_group + default: "" + type: str + choices: + description: The list of the tags for the tag_group as dicts. + default: [] + type: list + elements: dict + state: + description: The desired state + default: "present" + choices: ["present", "absent"] + type: str + +author: + - Stefan Mühling (@muehlings) +""" + +EXAMPLES = r""" +- name: "Create tag_group" + tribe29.checkmk.tag_group: + server_url: "https://localhost/" + site: "my_site" + automation_user: "automation" + automation_secret: "$SECRET" + id: Virtualization + title: Virtualization + topic: My_Tags + choices: + - id: No_Virtualization + title: No Virtualization + - id: ESXi + title: ESXi + - id: vCenter + title: vCenter + - id: HyperV + title: HyperV + - id: KVM + title: KVM + state: present +""" + +RETURN = r""" +http_code: + description: The HTTP code the Checkmk API returns. + type: int + returned: always + sample: '200' +message: + description: The output message that the module generates. + type: str + returned: always + sample: 'OK' +""" + + +import json + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.urls import fetch_url + + +def read_tag_group(module, base_url, headers): + result = dict( + changed=False, failed=False, http_code="", msg="", current_tag_group={}, etag="" + ) + + current_state = "unknown" + current_tag_group = dict(title="", topic="", tags=[], ident="") + etag = "" + + ident = module.params.get("id", "") + + api_endpoint = "/objects/host_tag_group/" + ident + url = base_url + api_endpoint + + response, info = fetch_url(module, url, data=None, headers=headers, method="GET") + + response_content = "" + + http_code = info["status"] + try: + response_content = response.read() + detail = str(json.loads(info["body"])["detail"]), str( + json.loads(info["body"])["fields"] + ) + except Exception: + detail = response_content + msg = info["msg"] + + if info["status"] == 200: + current_state = "present" + etag = info.get("etag", "") + + extensions = json.loads(response_content).get("extensions", {}) + current_tag_group["tags"] = extensions["tags"] + current_tag_group["topic"] = extensions["topic"] + current_tag_group["title"] = json.loads(response_content).get("title", "") + current_tag_group["ident"] = json.loads(response_content).get("id", "") + + for d in current_tag_group["tags"]: + d["ident"] = d.pop("id") + d.pop("aux_tags") + + elif info["status"] == 404: + current_state = "absent" + + else: + failed = True + + result["current_tag_group"] = current_tag_group + result["msg"] = str(http_code) + " - " + msg + result["http_code"] = http_code + result["state"] = current_state + result["etag"] = etag + + return result + + +def create_tag_group(module, base_url, headers): + result = dict(changed=False, failed=False, http_code="", msg="") + + changed = False + failed = False + http_code = "" + + ident = module.params.get("id", "") + tag_group = {} + tag_group["ident"] = ident + tag_group["title"] = module.params.get("title", "") + tag_group["topic"] = module.params.get("topic", "") + tag_group["tags"] = module.params.get("choices", "") + for d in tag_group["tags"]: + d["ident"] = d.pop("id") + + api_endpoint = "/domain-types/host_tag_group/collections/all" + url = base_url + api_endpoint + response, info = fetch_url( + module, url, module.jsonify(tag_group), headers=headers, method="POST" + ) + + http_code = info["status"] + try: + response_content = response.read() + detail = str(json.loads(info["body"])["detail"]), str( + json.loads(info["body"])["fields"] + ) + except Exception: + detail = "" + msg = info["msg"] + + if info["status"] != 200: + failed = True + + result["msg"] = str(http_code) + " - " + msg + result["changed"] = changed + result["failed"] = failed + result["http_code"] = http_code + result["info"] = info + + return result + + +def update_tag_group(module, base_url, headers, etag): + result = dict(changed=False, failed=False, http_code="", msg="") + + changed = False + failed = False + http_code = "" + + ident = module.params.get("id", "") + tag_group = {} + tag_group["repair"] = True + tag_group["title"] = module.params.get("title", "") + tag_group["topic"] = module.params.get("topic", "") + tag_group["tags"] = module.params.get("choices", "") + for d in tag_group["tags"]: + d["ident"] = d.pop("id") + + api_endpoint = "/objects/host_tag_group/" + ident + url = base_url + api_endpoint + headers["If-Match"] = etag + response, info = fetch_url( + module, url, module.jsonify(tag_group), headers=headers, method="PUT" + ) + + http_code = info["status"] + try: + response_content = response.read() + detail = str(json.loads(info["body"])["detail"]), str( + json.loads(info["body"])["fields"] + ) + except Exception: + detail = "" + msg = info["msg"] + + if info["status"] != 200: + failed = True + + result["msg"] = str(http_code) + " - " + msg + result["changed"] = changed + result["failed"] = failed + result["http_code"] = http_code + result["info"] = info + + return result + + +def delete_tag_group(module, base_url, headers, etag): + result = dict(changed=False, failed=False, http_code="", msg="") + + changed = False + failed = False + http_code = "" + + ident = module.params.get("id") + tag_group = {} + tag_group["repair"] = True + tag_group["title"] = module.params.get("title", "") + tag_group["topic"] = module.params.get("topic", "") + tag_group["tags"] = module.params.get("choices", "") + for d in tag_group["tags"]: + d["ident"] = d.pop("id") + + api_endpoint = "/objects/host_tag_group/" + ident + url = base_url + api_endpoint + headers["If-Match"] = etag + response, info = fetch_url( + module, url, module.jsonify(tag_group), headers=headers, method="DELETE" + ) + + http_code = info["status"] + try: + response_content = response.read() + detail = str(json.loads(info["body"])["detail"]), str( + json.loads(info["body"])["fields"] + ) + except Exception: + detail = "" + msg = info["msg"] + + if info["status"] != 204: + failed = True + + result["msg"] = str(http_code) + " - " + msg + result["changed"] = changed + result["failed"] = failed + result["http_code"] = http_code + result["info"] = info + + return result + + +def run_module(): + # 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), + automation_user=dict(type="str", required=True), + automation_secret=dict(type="str", required=True, no_log=True), + title=dict(type="str", default=""), + id=dict(type="str", default=""), + topic=dict(type="str", default=""), + choices=dict(type="list", elements="dict", default=[]), + state=dict(type="str", default="present", choices=["present", "absent"]), + ) + + module = AnsibleModule(argument_spec=module_args, supports_check_mode=False) + + # Declare headers including authentication to send to the Checkmk API + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + "Authorization": "Bearer %s %s" + % ( + module.params.get("automation_user", ""), + module.params.get("automation_secret", ""), + ), + } + + state = module.params.get("state", "present") + + base_url = "%s/%s/check_mk/api/1.0" % ( + module.params.get("server_url", ""), + module.params.get("site", ""), + ) + + # read current state (GET) + result = read_tag_group(module, base_url, headers) + msg_tokens = [] + + # tag_group is "present" + if result["etag"] != "": + # tag_group needs to be deleted (DELETE) + if module.params.get("state") == "absent": + result = delete_tag_group(module, base_url, headers, result["etag"]) + msg_tokens.append("Tag group deleted.") + result["changed"] = True + # tag_group needs to be updated (PUT) + elif module.params.get("state") == "present": + choices = module.params.get("choices") + current_choices = result["current_tag_group"]["tags"] + for d in current_choices: + d["id"] = d.pop("ident") + pairs = zip(choices, current_choices) + current_len = len(current_choices) + current_etag = result["etag"] + current_title = result["current_tag_group"]["title"] + current_topic = result["current_tag_group"]["topic"] + changed_len = False + changed_content = False + changed_title = False + changed_topic = False + if not all(a == b for a, b in pairs): + changed_content = True + msg_tokens.append("Content of choices changed.") + if len(module.params.get("choices")) != current_len: + changed_len = True + msg_tokens.append("Number of choices changed.") + if module.params.get("title") != current_title: + changed_title = True + msg_tokens.append("Title changed.") + if module.params.get("topic") != current_topic: + changed_topic = True + msg_tokens.append("Topic changed.") + if ( + changed_content is True + or changed_len is True + or changed_title is True + or changed_topic is True + ): + result = update_tag_group(module, base_url, headers, current_etag) + result["changed"] = True + else: + msg_tokens.append("Tag group as desired. Nothing to do.") + + else: + # tag_group is "absent" + if module.params.get("state") == "absent": + # nothing to do + msg_tokens.append("Nothing to do.") + result["changed"] = False + elif module.params.get("state") == "present": + # tag_group needs to be created (POST) + result = create_tag_group(module, base_url, headers) + msg_tokens.append("Tag group created.") + result["changed"] = True + + if len(msg_tokens) > 0: + result["msg"] = " ".join(msg_tokens) + + if result["failed"]: + module.fail_json(**result) + + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == "__main__": + main() diff --git a/requirements.yml b/requirements.yml index 7ac26996d..e159f2e0e 100644 --- a/requirements.yml +++ b/requirements.yml @@ -1,4 +1,4 @@ --- collections: - name: tribe29.checkmk - version: 0.10.0 + version: 0.11.0 diff --git a/roles/agent/README.md b/roles/agent/README.md index fe22d3a63..389ebcdbd 100644 --- a/roles/agent/README.md +++ b/roles/agent/README.md @@ -12,14 +12,19 @@ None. - checkmk_agent_version: "2.1.0p1" + checkmk_agent_version: "2.1.0p13" The Checkmk version of your site. checkmk_agent_edition: cre -The edition you are using. Valid values are `cre` and `cee`. -Note, that `cee` is not implemented yet. +The edition you are using. Valid values are `cre`, `cfe`, `cee` and `cme`. +- `cre`: Raw Edition, fully open source. +- `cfe`: Free Edition, enterprise features, but limited hosts. +- `cee`: Enterprise Edition, full enterprise features. +- `cme`: Managed Edition, for service providers. + +For details about the editions see: https://checkmk.com/product/editions checkmk_agent_protocol: http @@ -47,13 +52,13 @@ The user used to authenticate against your Checkmk site. checkmk_agent_pass: "{{ automation_secret }}" -The password for the normal user used to authenticate against your Checkmk site. -This is mutually exclusive with `checkmk_agent_secret`! +The password for the normal user used to authenticate against your Checkmk site. +This is mutually exclusive with `checkmk_agent_secret`. checkmk_agent_secret: "{{ automation_secret }}" -The secret for the automation user used to authenticate against your Checkmk site. -This is mutually exclusive with `checkmk_agent_pass`! +The secret for the automation user used to authenticate against your Checkmk site. +This is mutually exclusive with `checkmk_agent_pass`. checkmk_agent_add_host: 'false' diff --git a/roles/agent/defaults/main.yml b/roles/agent/defaults/main.yml index ee24e7cf5..ff51c5b4a 100644 --- a/roles/agent/defaults/main.yml +++ b/roles/agent/defaults/main.yml @@ -1,5 +1,5 @@ --- -checkmk_agent_version: "2.1.0p11" +checkmk_agent_version: "2.1.0p13" checkmk_agent_edition: cre checkmk_agent_protocol: http checkmk_agent_server: localhost diff --git a/roles/agent/tasks/Debian.yml b/roles/agent/tasks/Debian.yml index c9c050459..74e581b9b 100644 --- a/roles/agent/tasks/Debian.yml +++ b/roles/agent/tasks/Debian.yml @@ -8,7 +8,10 @@ headers: Authorization: "Bearer {{ checkmk_agent_user }} {{ checkmk_agent_auth }}" Accept: "application/octet-stream" - when: checkmk_agent_edition == "cee" or checkmk_agent_edition == "cfe" + when: | + checkmk_agent_edition == "cee" or + checkmk_agent_edition == "cfe" or + checkmk_agent_edition == "cme" register: checkmk_agent_download_state # This task may fail, as we fall back to the generic agent in that case failed_when: 'false' @@ -23,7 +26,9 @@ force: "{{ checkmk_agent_force_install | bool }}" state: present when: | - (checkmk_agent_edition == "cee" or checkmk_agent_edition == "cfe") + (checkmk_agent_edition == "cee" or + checkmk_agent_edition == "cfe" or + checkmk_agent_edition == "cme") and checkmk_agent_download_state.status == 200 tags: - install-package @@ -38,7 +43,9 @@ Authorization: "Bearer {{ checkmk_agent_user }} {{ checkmk_agent_auth }}" Accept: "application/octet-stream" when: | - (checkmk_agent_edition == "cee" or checkmk_agent_edition == "cfe") + (checkmk_agent_edition == "cee" or + checkmk_agent_edition == "cfe" or + checkmk_agent_edition == "cme") and checkmk_agent_download_state.status != 200 retries: 3 tags: @@ -51,7 +58,9 @@ force: "{{ checkmk_agent_force_install | bool }}" state: present when: | - (checkmk_agent_edition == "cee" or checkmk_agent_edition == "cfe") + (checkmk_agent_edition == "cee" or + checkmk_agent_edition == "cfe" or + checkmk_agent_edition == "cme") and checkmk_agent_download_state.status != 200 tags: - install-package diff --git a/roles/agent/tasks/RedHat.yml b/roles/agent/tasks/RedHat.yml index 37b050d58..da2dda48e 100644 --- a/roles/agent/tasks/RedHat.yml +++ b/roles/agent/tasks/RedHat.yml @@ -8,7 +8,10 @@ headers: Authorization: "Bearer {{ checkmk_agent_user }} {{ checkmk_agent_auth }}" Accept: "application/octet-stream" - when: checkmk_agent_edition == "cee" or checkmk_agent_edition == "cfe" + when: | + checkmk_agent_edition == "cee" or + checkmk_agent_edition == "cfe" or + checkmk_agent_edition == "cme" register: checkmk_agent_download_state # This task may fail, as we fall back to the generic agent in that case failed_when: 'false' @@ -23,7 +26,9 @@ state: present disable_gpg_check: true when: | - (checkmk_agent_edition == "cee" or checkmk_agent_edition == "cfe") + (checkmk_agent_edition == "cee" or + checkmk_agent_edition == "cfe" or + checkmk_agent_edition == "cme") and checkmk_agent_download_state.status == 200 tags: - install-package @@ -38,7 +43,9 @@ Authorization: "Bearer {{ checkmk_agent_user }} {{ checkmk_agent_auth }}" Accept: "application/octet-stream" when: | - (checkmk_agent_edition == "cee" or checkmk_agent_edition == "cfe") + (checkmk_agent_edition == "cee" or + checkmk_agent_edition == "cfe" or + checkmk_agent_edition == "cme") and checkmk_agent_download_state.status != 200 retries: 3 tags: @@ -61,7 +68,9 @@ state: present disable_gpg_check: true when: | - (checkmk_agent_edition == "cee" or checkmk_agent_edition == "cfe") + (checkmk_agent_edition == "cee" or + checkmk_agent_edition == "cfe" or + checkmk_agent_edition == "cme") and checkmk_agent_download_state.status != 200 tags: - install-package diff --git a/roles/agent/tasks/Suse.yml b/roles/agent/tasks/Suse.yml index 06acdf2c6..7ff31082d 100644 --- a/roles/agent/tasks/Suse.yml +++ b/roles/agent/tasks/Suse.yml @@ -8,7 +8,10 @@ headers: Authorization: "Bearer {{ checkmk_agent_user }} {{ checkmk_agent_auth }}" Accept: "application/octet-stream" - when: checkmk_agent_edition == "cee" or checkmk_agent_edition == "cfe" + when: | + checkmk_agent_edition == "cee" or + checkmk_agent_edition == "cfe" or + checkmk_agent_edition == "cme" register: checkmk_agent_download_state # This task may fail, as we fall back to the generic agent in that case failed_when: 'false' @@ -24,7 +27,9 @@ state: present disable_gpg_check: true when: | - (checkmk_agent_edition == "cee" or checkmk_agent_edition == "cfe") + (checkmk_agent_edition == "cee" or + checkmk_agent_edition == "cfe" or + checkmk_agent_edition == "cme") and checkmk_agent_download_state.status == 200 tags: - install-package @@ -39,7 +44,9 @@ Authorization: "Bearer {{ checkmk_agent_user }} {{ checkmk_agent_auth }}" Accept: "application/octet-stream" when: | - (checkmk_agent_edition == "cee" or checkmk_agent_edition == "cfe") + (checkmk_agent_edition == "cee" or + checkmk_agent_edition == "cfe" or + checkmk_agent_edition == "cme") and checkmk_agent_download_state.status != 200 retries: 3 tags: @@ -64,7 +71,9 @@ state: present disable_gpg_check: true when: | - (checkmk_agent_edition == "cee" or checkmk_agent_edition == "cfe") + (checkmk_agent_edition == "cee" or + checkmk_agent_edition == "cfe" or + checkmk_agent_edition == "cme") and checkmk_agent_download_state.status != 200 tags: - install-package diff --git a/roles/agent/tasks/main.yml b/roles/agent/tasks/main.yml index cf17a8c4b..6b0cccbb0 100644 --- a/roles/agent/tasks/main.yml +++ b/roles/agent/tasks/main.yml @@ -68,7 +68,9 @@ -U {{ checkmk_agent_user }} -P {{ checkmk_agent_auth }} register: checkmk_agent_update_state when: | - (checkmk_agent_edition == "cee" or checkmk_agent_edition == "cfe") + (checkmk_agent_edition == "cee" or + checkmk_agent_edition == "cfe" or + checkmk_agent_edition == "cme") and checkmk_agent_updater_binary.stat.exists | bool and checkmk_agent_update | bool and (checkmk_agent_pass is defined and checkmk_agent_pass | length) @@ -82,7 +84,9 @@ -U {{ checkmk_agent_user }} -S {{ checkmk_agent_auth }} register: checkmk_agent_update_state when: | - (checkmk_agent_edition == "cee" or checkmk_agent_edition == "cfe") + (checkmk_agent_edition == "cee" or + checkmk_agent_edition == "cfe" or + checkmk_agent_edition == "cme") and checkmk_agent_updater_binary.stat.exists | bool and checkmk_agent_update | bool and (checkmk_agent_secret is defined and checkmk_agent_secret | length) @@ -95,7 +99,9 @@ -U {{ checkmk_agent_user }} -P {{ checkmk_agent_auth }} --trust-cert register: checkmk_agent_tls_state when: | - (checkmk_agent_edition == "cee" or checkmk_agent_edition == "cfe") + (checkmk_agent_edition == "cee" or + checkmk_agent_edition == "cfe" or + checkmk_agent_edition == "cme") and checkmk_agent_controller_binary.stat.exists | bool and checkmk_agent_tls | bool and (checkmk_agent_auth is defined and checkmk_agent_auth | length) diff --git a/roles/agent/vars/main.yml b/roles/agent/vars/main.yml index 658340bdc..f6a712bf6 100644 --- a/roles/agent/vars/main.yml +++ b/roles/agent/vars/main.yml @@ -1,4 +1,4 @@ --- checkmk_agent_site_url: "{{ checkmk_agent_protocol }}://{{ checkmk_agent_server }}:{{ checkmk_agent_port }}/{{ checkmk_agent_site }}" -checkmk_agent_auth: "{% if checkmk_agent_secret is defined and checkmk_agent_secret | length %}{{ checkmk_agent_secret }}{% else %}{{ checkmk_agent_pass }}{% endif %}" # noqa yaml[line-length] +checkmk_agent_auth: "{% if checkmk_agent_secret is defined and checkmk_agent_secret | length %}{{ checkmk_agent_secret }}{% else %}{{ checkmk_agent_pass }}{% endif %}" # noqa yaml[line-length] diff --git a/roles/server/defaults/main.yml b/roles/server/defaults/main.yml index 77201d181..01703d0d2 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.0p11 +checkmk_server_version: 2.1.0p13 checkmk_server_verify_setup: 'true' checkmk_server_download_user: [] diff --git a/roles/server/tasks/RedHat.yml b/roles/server/tasks/RedHat.yml index 78a7d9777..7efb1f206 100644 --- a/roles/server/tasks/RedHat.yml +++ b/roles/server/tasks/RedHat.yml @@ -1,16 +1,8 @@ --- -- name: "Enable repositories on RHEL 8." +- name: "Enable repositories on CentOS & RHEL 8." block: - - name: "Enable CodeReady Linux Builder repo on RHEL 8." - become: true - community.general.rhsm_repository: - name: "codeready-builder-for-rhel-{{ ansible_facts.distribution_major_version }}-x86_64-rpms" - state: enabled - tags: - - enable-repos - - - name: "Download EPEL GPG Key on RHEL 8." + - name: "Download EPEL GPG Key on CentOS & RHEL 8." ansible.builtin.get_url: url: "{{ checkmk_server_epel_gpg_key }}" dest: "/tmp/EPEL-pubkey.gpg" @@ -30,7 +22,7 @@ - import-gpg-key - enable-repos - - name: "Install epel-release from URL on RHEL 8." + - name: "Install epel-release from URL on CentOS & RHEL 8." become: true ansible.builtin.yum: name: "{{ checkmk_server_epel_url }}" @@ -39,6 +31,17 @@ tags: - enable-repos + when: | + (ansible_facts.distribution == "RedHat" or ansible_facts.distribution == "CentOS") + and ansible_facts.distribution_major_version == "8" + +- name: "Enable CodeReady Linux Builder repo on RHEL 8." + become: true + community.general.rhsm_repository: + name: "codeready-builder-for-rhel-{{ ansible_facts.distribution_major_version }}-x86_64-rpms" + state: enabled + tags: + - enable-repos when: | ansible_facts.distribution == "RedHat" and ansible_facts.distribution_major_version == "8" diff --git a/roles/server/tasks/sites.yml b/roles/server/tasks/sites.yml index e5abc1834..edd998897 100644 --- a/roles/server/tasks/sites.yml +++ b/roles/server/tasks/sites.yml @@ -29,7 +29,7 @@ tags: - update-sites -- name: "Include update site tasks." +- name: "Include update site tasks." # noqa no-handler ansible.builtin.include_tasks: update-site.yml loop: "{{ checkmk_server_sites_versions.results }}" when: "item.changed" diff --git a/tests/integration/targets/host_group/tasks/main.yml b/tests/integration/targets/host_group/tasks/main.yml new file mode 100644 index 000000000..fade565a7 --- /dev/null +++ b/tests/integration/targets/host_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/host_group/tasks/prep.yml b/tests/integration/targets/host_group/tasks/prep.yml new file mode 100644 index 000000000..0be779f8c --- /dev/null +++ b/tests/integration/targets/host_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/host_group/tasks/test.yml b/tests/integration/targets/host_group/tasks/test.yml new file mode 100644 index 000000000..8fbc3f9dc --- /dev/null +++ b/tests/integration/targets/host_group/tasks/test.yml @@ -0,0 +1,195 @@ +--- +- name: "{{ outer_item.version }} - Create host groups." + host_group: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_group_name: "{{ item.name }}" + title: "{{ item.title | default(item.name) }}" + state: "present" + delegate_to: localhost + run_once: true + loop: "{{ checkmk_host_groups_create }}" + +- name: "{{ outer_item.version }} - Should fail because of 'host_groups', Create host groups." + host_group: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_group_name: "{{ item.name }}" + title: "{{ item.title | default(item.name) }}" + host_groups: checkmk_host_groups_create + state: "present" + delegate_to: localhost + run_once: true + loop: "{{ checkmk_host_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 host groups (also checks for idempotency!)." + host_group: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_group_name: "{{ item.name | default(item.name) }}" + title: "{{ item.title }}" + state: "present" + delegate_to: localhost + run_once: true + loop: "{{ checkmk_host_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 host groups." + host_group: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_group_name: "{{ item.name }}" + state: "absent" + delegate_to: localhost + run_once: true + loop: "{{ checkmk_host_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 host groups that were created at the beginning (also check for idempotency)." + host_group: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_group_name: "{{ item.name }}" + state: "absent" + delegate_to: localhost + run_once: true + loop: "{{ checkmk_host_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 host groups." + host_group: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_groups: "{{ checkmk_host_groups_create }}" + state: "present" + delegate_to: localhost + run_once: true + +- name: "{{ outer_item.version }} - Should fail because of 'host_group_name', Bulk create host groups." + host_group: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_groups: "{{ checkmk_host_groups_create }}" + host_group_name: "test" + state: "present" + delegate_to: localhost + run_once: true + ignore_errors: True + +- name: "{{ outer_item.version }} - Should fail because of 'title', Bulk create host groups." + host_group: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_groups: "{{ checkmk_host_groups_create }}" + title: "Test" + state: "present" + delegate_to: localhost + run_once: true + ignore_errors: True + +- name: "{{ outer_item.version }} - Bulk modify part of host groups (also checks for idempotency!)." + host_group: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_groups: "{{ checkmk_host_groups_modify }}" + state: "present" + delegate_to: localhost + run_once: true + +- name: "{{ outer_item.version }} - Bulk delete host groups." + host_group: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_groups: "{{ checkmk_host_groups_delete }}" + state: "absent" + delegate_to: localhost + run_once: true + +- name: "{{ outer_item.version }} - Bulk delete host groups that were created at the beginning (also check for idempotency)." + host_group: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_groups: "{{ checkmk_host_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/host_group/vars/main.yml b/tests/integration/targets/host_group/vars/main.yml new file mode 100644 index 000000000..6d12ec835 --- /dev/null +++ b/tests/integration/targets/host_group/vars/main.yml @@ -0,0 +1,37 @@ +--- +checkmk_versions: + - version: "2.1.0p11" + site: "stable" + - version: "2.0.0p28" + 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_host_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_host_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_host_groups_delete: + - name: "test1" + - name: "test3" diff --git a/tests/integration/targets/tag_group/tasks/main.yml b/tests/integration/targets/tag_group/tasks/main.yml new file mode 100644 index 000000000..fade565a7 --- /dev/null +++ b/tests/integration/targets/tag_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/tag_group/tasks/prep.yml b/tests/integration/targets/tag_group/tasks/prep.yml new file mode 100644 index 000000000..0be779f8c --- /dev/null +++ b/tests/integration/targets/tag_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/tag_group/tasks/test.yml b/tests/integration/targets/tag_group/tasks/test.yml new file mode 100644 index 000000000..89ffd3785 --- /dev/null +++ b/tests/integration/targets/tag_group/tasks/test.yml @@ -0,0 +1,65 @@ +--- +- name: "{{ outer_item.version }} - Create tag_group." + tribe29.checkmk.tag_group: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + id: Virtualization + title: Virtualization + topic: My_Tag_Group + choices: + - id: No_Virtualization + title: No Virtualization + - id: ESXi + title: ESXi + - id: vCenter + title: vCenter + - id: HyperV + title: Hyper + - id: KVM + title: KVM + state: "present" + delegate_to: localhost + +- name: "{{ outer_item.version }} - Update tag_group." + tribe29.checkmk.tag_group: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + id: Virtualization + title: Hypervisors + topic: My_Tag_Group + choices: + - id: No_Virtualization + title: No Virtualization + - id: ESXi + title: ESXi + - id: vCenter + title: vCenter + - id: HyperV + title: Hyper + state: "present" + delegate_to: localhost + +- name: "{{ outer_item.version }} - Delete tag_group." + tribe29.checkmk.tag_group: + server_url: "{{ server_url }}" + site: "{{ outer_item.site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + id: Virtualization + title: Hypervisors + topic: My_Tag_Group + choices: + - id: No_Virtualization + title: No Virtualization + - id: ESXi + title: ESXi + - id: vCenter + title: vCenter + - id: HyperV + title: Hyper + state: "absent" + delegate_to: localhost diff --git a/tests/integration/targets/tag_group/vars/main.yml b/tests/integration/targets/tag_group/vars/main.yml new file mode 100644 index 000000000..a1789fd95 --- /dev/null +++ b/tests/integration/targets/tag_group/vars/main.yml @@ -0,0 +1,10 @@ +--- +checkmk_versions: + - version: "2.1.0p11" + site: "stable" + - version: "2.0.0p28" + 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"