mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-03-22 05:09:12 +00:00
New module icinga2_downtime (#11462)
* feat: Icinga 2 downtime module added allowing to schedule and remove downtimes through its REST API. Signed-off-by: Fiehe Christoph <c.fiehe@eurodata.de> * ensure compatibility with ModuleTestCase feat: errors raised from MH now contain the changed flag ref: move module exit out of the decorated run method Signed-off-by: Fiehe Christoph <c.fiehe@eurodata.de> * revised module ref: module refactored using StateModuleHelper now ref: suggested changes by reviewer added Signed-off-by: Fiehe Christoph <c.fiehe@eurodata.de> * revert change regarding changed flag in MH Signed-off-by: Fiehe Christoph <c.fiehe@eurodata.de> * refactoring and set changed flag explicitly on error Signed-off-by: Fiehe Christoph <c.fiehe@eurodata.de> * Check whether there was a state change on module failure removed. Signed-off-by: Fiehe Christoph <c.fiehe@eurodata.de> * ref: test cases migrated to the new feature that allows passing through exceptions Signed-off-by: Fiehe Christoph <c.fiehe@eurodata.de> * Update plugins/module_utils/icinga2.py Co-authored-by: Felix Fontein <felix@fontein.de> * Update plugins/module_utils/icinga2.py Co-authored-by: Felix Fontein <felix@fontein.de> * Update plugins/modules/icinga2_downtime.py Co-authored-by: Felix Fontein <felix@fontein.de> * ref: make module helper private Signed-off-by: Fiehe Christoph <c.fiehe@eurodata.de> * fix: ensure that all non-null values are added to the request otherwise a `false` value is dropped Signed-off-by: Fiehe Christoph <c.fiehe@eurodata.de> * ref: module description extended with the note that check mode is not supported Signed-off-by: Fiehe Christoph <c.fiehe@eurodata.de> * Update plugins/modules/icinga2_downtime.py Co-authored-by: Felix Fontein <felix@fontein.de> * fix: documentation updated Signed-off-by: Fiehe Christoph <c.fiehe@eurodata.de> * ref: documentation updated ref: doc fragment added Signed-off-by: Fiehe Christoph <c.fiehe@eurodata.de> * Update plugins/doc_fragments/icinga2_api.py Co-authored-by: Felix Fontein <felix@fontein.de> * ref: doc fragment renamed to `_icinga2_api.py` Signed-off-by: Fiehe Christoph <c.fiehe@eurodata.de> * ref: maintainer to doc fragment in BOTMETA.yml added Signed-off-by: Fiehe Christoph <c.fiehe@eurodata.de> * Update plugins/modules/icinga2_downtime.py Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> * Update plugins/modules/icinga2_downtime.py Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> * Update plugins/modules/icinga2_downtime.py Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> --------- Signed-off-by: Fiehe Christoph <c.fiehe@eurodata.de> Co-authored-by: Fiehe Christoph <c.fiehe@eurodata.de> Co-authored-by: Felix Fontein <felix@fontein.de> Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
This commit is contained in:
parent
cb91ff424f
commit
ce7cb4e914
5 changed files with 696 additions and 0 deletions
224
tests/unit/plugins/modules/test_icinga2_downtime.py
Normal file
224
tests/unit/plugins/modules/test_icinga2_downtime.py
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
# Copyright (c) Ansible project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from unittest.mock import MagicMock, patch
|
||||
from urllib.error import HTTPError
|
||||
|
||||
from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import (
|
||||
AnsibleExitJson,
|
||||
AnsibleFailJson,
|
||||
ModuleTestCase,
|
||||
set_module_args,
|
||||
)
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.deco import no_handle_exceptions
|
||||
from ansible_collections.community.general.plugins.modules import icinga2_downtime
|
||||
|
||||
|
||||
class TestIcinga2Downtime(ModuleTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.module = icinga2_downtime
|
||||
|
||||
@patch("ansible_collections.community.general.plugins.modules.icinga2_downtime.Icinga2Client")
|
||||
def test_schedule_downtime_successfully(self, client_mock):
|
||||
module_args = {
|
||||
"url": "http://icinga2.example.com:5665",
|
||||
"url_username": "icingaadmin",
|
||||
"url_password": "secret",
|
||||
"author": "Ansible",
|
||||
"comment": "This is a test comment.",
|
||||
"state": "present",
|
||||
"start_time": 1769954400,
|
||||
"end_time": 1769958000,
|
||||
"duration": 3600,
|
||||
"fixed": True,
|
||||
"object_type": "Host",
|
||||
"filter": 'host.name=="host.example.com"',
|
||||
}
|
||||
with set_module_args(module_args):
|
||||
info = {
|
||||
"content-type": "application/json",
|
||||
"server": "Icinga/r2.15.1-1",
|
||||
"status": 200,
|
||||
"url": "https://icinga2.example.com:5665/v1/actions/schedule-downtime",
|
||||
}
|
||||
response = {
|
||||
"results": [
|
||||
{
|
||||
"code": 200,
|
||||
"legacy_id": 28911,
|
||||
"name": "host.example.com!e19c705a-54c2-49c5-8014-70ff624f9e51",
|
||||
"status": "Successfully scheduled downtime 'host.example.com!e19c705a-54c2-49c5-8014-70ff624f9e51' for object 'host.example.com'.",
|
||||
}
|
||||
]
|
||||
}
|
||||
response_read_mock = MagicMock(return_value=json.dumps(response))
|
||||
response_mock = MagicMock(read=response_read_mock)
|
||||
schedule_downtime_mock = MagicMock(return_value=(response_mock, info))
|
||||
actions_mock = MagicMock(schedule_downtime=schedule_downtime_mock)
|
||||
client_mock.return_value = MagicMock(actions=actions_mock)
|
||||
|
||||
with no_handle_exceptions(AnsibleExitJson, AnsibleFailJson):
|
||||
with self.assertRaises(AnsibleExitJson) as result:
|
||||
self.module.main()
|
||||
|
||||
self.assertFalse(result.exception.args[0]["failed"])
|
||||
self.assertTrue(result.exception.args[0]["changed"])
|
||||
self.assertEqual(result.exception.args[0]["results"], response["results"])
|
||||
schedule_downtime_mock.assert_called_once_with(
|
||||
all_services=None,
|
||||
author=module_args["author"],
|
||||
child_options=None,
|
||||
comment=module_args["comment"],
|
||||
duration=module_args["duration"],
|
||||
end_time=module_args["end_time"],
|
||||
filter=module_args["filter"],
|
||||
filter_vars=None,
|
||||
fixed=module_args["fixed"],
|
||||
object_type=module_args["object_type"],
|
||||
start_time=module_args["start_time"],
|
||||
trigger_name=None,
|
||||
)
|
||||
|
||||
@patch("ansible_collections.community.general.plugins.modules.icinga2_downtime.Icinga2Client")
|
||||
def test_schedule_downtime_failed(self, client_mock):
|
||||
module_args = {
|
||||
"url": "http://icinga2.example.com:5665",
|
||||
"url_username": "icingaadmin",
|
||||
"url_password": "secret",
|
||||
"author": "Ansible",
|
||||
"comment": "This is a test comment.",
|
||||
"state": "present",
|
||||
"start_time": 1769954400,
|
||||
"end_time": 1769958000,
|
||||
"duration": 3600,
|
||||
"fixed": True,
|
||||
"object_type": "Host",
|
||||
"filter": 'host.name=="unknown.example.com"',
|
||||
}
|
||||
with set_module_args(module_args):
|
||||
info = {
|
||||
"body": json.dumps({"error": 404, "status": "No objects found."}),
|
||||
"content-length": "42",
|
||||
"content-type": "application/json",
|
||||
"msg": "HTTP Error 404: Not Found",
|
||||
"server": "Icinga/r2.15.1-1",
|
||||
"status": 404,
|
||||
"url": "https://icinga2.example.com:5665/v1/actions/remove-downtime",
|
||||
}
|
||||
response = HTTPError(url=info["url"], code=404, msg=info["msg"], hdrs={}, fp=None)
|
||||
schedule_downtime_mock = MagicMock(return_value=(response, info))
|
||||
actions_mock = MagicMock(schedule_downtime=schedule_downtime_mock)
|
||||
client_mock.return_value = MagicMock(actions=actions_mock)
|
||||
|
||||
with no_handle_exceptions(AnsibleExitJson, AnsibleFailJson):
|
||||
with self.assertRaises(AnsibleFailJson) as result:
|
||||
self.module.main()
|
||||
|
||||
self.assertTrue(result.exception.args[0]["failed"])
|
||||
self.assertEqual(
|
||||
result.exception.args[0]["error"],
|
||||
{"error": 404, "status": "No objects found."},
|
||||
)
|
||||
schedule_downtime_mock.assert_called_once_with(
|
||||
all_services=None,
|
||||
author=module_args["author"],
|
||||
child_options=None,
|
||||
comment=module_args["comment"],
|
||||
duration=module_args["duration"],
|
||||
end_time=module_args["end_time"],
|
||||
filter=module_args["filter"],
|
||||
filter_vars=None,
|
||||
fixed=module_args["fixed"],
|
||||
object_type=module_args["object_type"],
|
||||
start_time=module_args["start_time"],
|
||||
trigger_name=None,
|
||||
)
|
||||
|
||||
@patch("ansible_collections.community.general.plugins.modules.icinga2_downtime.Icinga2Client")
|
||||
def test_remove_existing_downtime(self, client_mock):
|
||||
module_args = {
|
||||
"url": "http://icinga2.example.com:5665",
|
||||
"url_username": "icingaadmin",
|
||||
"url_password": "secret",
|
||||
"state": "absent",
|
||||
"name": "host.example.com!e19c705a-54c2-49c5-8014-70ff624f9e51",
|
||||
"object_type": "Downtime",
|
||||
}
|
||||
with set_module_args(module_args):
|
||||
info = {
|
||||
"content-type": "application/json",
|
||||
"server": "Icinga/r2.15.1-1",
|
||||
"status": 200,
|
||||
"url": "https://icinga2.example.com:5665/v1/actions/remove-downtime",
|
||||
}
|
||||
response = {
|
||||
"results": [
|
||||
{
|
||||
"code": 200,
|
||||
"status": "Successfully removed downtime 'host.example.com!e19c705a-54c2-49c5-8014-70ff624f9e51' and 0 child downtimes.",
|
||||
}
|
||||
]
|
||||
}
|
||||
response_read_mock = MagicMock(return_value=json.dumps(response))
|
||||
response_mock = MagicMock(read=response_read_mock)
|
||||
remove_downtime_mock = MagicMock(return_value=(response_mock, info))
|
||||
actions_mock = MagicMock(remove_downtime=remove_downtime_mock)
|
||||
client_mock.return_value = MagicMock(actions=actions_mock)
|
||||
|
||||
with no_handle_exceptions(AnsibleExitJson, AnsibleFailJson):
|
||||
with self.assertRaises(AnsibleExitJson) as result:
|
||||
self.module.main()
|
||||
|
||||
self.assertFalse(result.exception.args[0]["failed"])
|
||||
self.assertTrue(result.exception.args[0]["changed"])
|
||||
self.assertEqual(result.exception.args[0]["results"], response["results"])
|
||||
remove_downtime_mock.assert_called_once_with(
|
||||
filter=None,
|
||||
filter_vars=None,
|
||||
name=module_args["name"],
|
||||
object_type=module_args["object_type"],
|
||||
)
|
||||
|
||||
@patch("ansible_collections.community.general.plugins.modules.icinga2_downtime.Icinga2Client")
|
||||
def test_remove_non_existing_downtime(self, client_mock):
|
||||
module_args = {
|
||||
"url": "http://icinga2.example.com:5665",
|
||||
"url_username": "icingaadmin",
|
||||
"url_password": "secret",
|
||||
"state": "absent",
|
||||
"name": "unknown.example.com!e19c705a-54c2-49c5-8014-70ff624f9e51",
|
||||
"object_type": "Downtime",
|
||||
}
|
||||
with set_module_args(module_args):
|
||||
info = {
|
||||
"body": json.dumps({"error": 404, "status": "No objects found."}),
|
||||
"content-length": "42",
|
||||
"content-type": "application/json",
|
||||
"msg": "HTTP Error 404: Not Found",
|
||||
"server": "Icinga/r2.15.1-1",
|
||||
"status": 404,
|
||||
"url": "https://icinga2.example.com:5665/v1/actions/remove-downtime",
|
||||
}
|
||||
response = HTTPError(url=info["url"], code=404, msg=info["msg"], hdrs={}, fp=None)
|
||||
remove_downtime_mock = MagicMock(return_value=(response, info))
|
||||
actions_mock = MagicMock(remove_downtime=remove_downtime_mock)
|
||||
client_mock.return_value = MagicMock(actions=actions_mock)
|
||||
|
||||
with no_handle_exceptions(AnsibleExitJson, AnsibleFailJson):
|
||||
with self.assertRaises(AnsibleExitJson) as result:
|
||||
self.module.main()
|
||||
|
||||
self.assertFalse(result.exception.args[0]["failed"])
|
||||
self.assertFalse(result.exception.args[0]["changed"])
|
||||
remove_downtime_mock.assert_called_once_with(
|
||||
filter=None,
|
||||
filter_vars=None,
|
||||
name=module_args["name"],
|
||||
object_type=module_args["object_type"],
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue