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
309
plugins/modules/icinga2_downtime.py
Normal file
309
plugins/modules/icinga2_downtime.py
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
#!/usr/bin/python
|
||||
#
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
# SPDX-FileCopyrightText: 2026 Christoph Fiehe <christoph.fiehe@gmail.com>
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
module: icinga2_downtime
|
||||
short_description: Manages Icinga 2 downtimes
|
||||
version_added: "12.4.0"
|
||||
description:
|
||||
- Manages downtimes in Icinga 2 through its REST API.
|
||||
- Options as described at U(https://icinga.com/docs/icinga-2/latest/doc/12-icinga2-api/#schedule-downtime).
|
||||
author:
|
||||
- Christoph Fiehe (@cfiehe)
|
||||
attributes:
|
||||
check_mode:
|
||||
support: none
|
||||
details:
|
||||
- In case of a complex filter expression, it may become very complex to decide
|
||||
whether downtime creation or removal will succeed and trigger a change.
|
||||
diff_mode:
|
||||
support: none
|
||||
options:
|
||||
all_services:
|
||||
description:
|
||||
- Whether downtimes should be created for all services of the matched host objects.
|
||||
- If omitted, Icinga 2 does not create downtimes for all services of the matched host objects by default.
|
||||
type: bool
|
||||
author:
|
||||
description:
|
||||
- Name of the author.
|
||||
type: str
|
||||
default: "Ansible"
|
||||
comment:
|
||||
description:
|
||||
- A descriptive comment.
|
||||
type: str
|
||||
default: Downtime scheduled by Ansible
|
||||
child_options:
|
||||
description:
|
||||
- Schedule child downtimes.
|
||||
type: str
|
||||
choices: ["DowntimeNoChildren", "DowntimeTriggeredChildren", "DowntimeNonTriggeredChildren"]
|
||||
duration:
|
||||
description:
|
||||
- Duration of the downtime.
|
||||
- Required in case of a flexible downtime.
|
||||
type: int
|
||||
end_time:
|
||||
description:
|
||||
- End time of the downtime as UNIX timestamp.
|
||||
type: int
|
||||
filter_vars:
|
||||
description:
|
||||
- Variable names and values used in the filter expression.
|
||||
type: dict
|
||||
filter:
|
||||
description:
|
||||
- Filter expression limiting the objects to operate on.
|
||||
type: str
|
||||
fixed:
|
||||
description:
|
||||
- Whether the downtime is fixed or flexible.
|
||||
- If omitted, Icinga 2 creates a fixed downtime by default.
|
||||
type: bool
|
||||
name:
|
||||
description:
|
||||
- Name of the downtime object.
|
||||
- This option has no effect for states other than V(absent).
|
||||
type: str
|
||||
object_type:
|
||||
description:
|
||||
- Use V(Host) for a host downtime and V(Service) for a service downtime.
|
||||
- Use V(Downtime) and give the name of the downtime object you want to remove.
|
||||
type: str
|
||||
choices: ["Service", "Host", "Downtime"]
|
||||
default: Host
|
||||
start_time:
|
||||
description:
|
||||
- Start time of the downtime as UNIX timestamp.
|
||||
type: int
|
||||
state:
|
||||
description:
|
||||
- State of the downtime.
|
||||
type: str
|
||||
choices: ["present", "absent"]
|
||||
default: present
|
||||
trigger_name:
|
||||
description:
|
||||
- Name of the downtime trigger.
|
||||
type: str
|
||||
extends_documentation_fragment:
|
||||
- community.general._icinga2_api
|
||||
- community.general.attributes
|
||||
- ansible.builtin.url
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
- name: Schedule a host downtime
|
||||
community.general.icinga2_downtime:
|
||||
url: "https://icinga2.example.com:5665"
|
||||
url_username: icingadmin
|
||||
url_password: secret
|
||||
state: present
|
||||
author: Ansible
|
||||
comment: Scheduled downtime for test purposes.
|
||||
all_services: true
|
||||
start_time: "{{ downtime_start_time }}"
|
||||
end_time: "{{ downtime_end_time }}"
|
||||
duration: "{{ downtime_duration }}"
|
||||
fixed: true
|
||||
object_type: Host
|
||||
filter: host.name=="host.example.com"
|
||||
delegate_to: localhost
|
||||
register: icinga2_downtime_response
|
||||
vars:
|
||||
downtime_start_time: "{{ ansible_date_time['epoch'] | int }}"
|
||||
downtime_end_time: "{{ downtime_start_time | int + 3600 }}"
|
||||
downtime_duration: "{{ downtime_end_time | int - downtime_start_time | int }}"
|
||||
|
||||
- name: Remove scheduled host downtime
|
||||
community.general.icinga2_downtime:
|
||||
url: "https://icinga2.example.com:5665"
|
||||
url_username: icingadmin
|
||||
url_password: secret
|
||||
state: absent
|
||||
author: Ansible
|
||||
object_type: Downtime
|
||||
name: "{{ icinga2_downtime_response.results[0].name }}"
|
||||
delegate_to: localhost
|
||||
when: icinga2_downtime_response.results | default([]) | length > 0
|
||||
"""
|
||||
|
||||
RETURN = r"""
|
||||
# Returns the results of downtime scheduling as a list of JSON dictionaries from the Icinga 2 API under the C(results) key.
|
||||
# Refer to https://icinga.com/docs/icinga-2/latest/doc/12-icinga2-api/#schedule-downtime for more details.
|
||||
results:
|
||||
description: Results of downtime scheduling or removal
|
||||
type: list
|
||||
returned: success
|
||||
elements: dict
|
||||
contains:
|
||||
code:
|
||||
description: Success or error code of downtime scheduling.
|
||||
returned: always
|
||||
type: int
|
||||
sample: 200
|
||||
legacy_id:
|
||||
description: Legacy id of the downtime object.
|
||||
returned: if a downtime was scheduled successfully
|
||||
type: int
|
||||
sample: 28911
|
||||
name:
|
||||
description: Name of the downtime object.
|
||||
returned: if a downtime was scheduled successfully
|
||||
type: str
|
||||
sample: host.example.com!e19c705a-54c2-49c5-8014-70ff624f9e51
|
||||
status:
|
||||
description: Human-readable message describing the result of downtime scheduling.
|
||||
returned: always
|
||||
type: str
|
||||
sample: Successfully scheduled downtime 'host.example.com!e19c705a-54c2-49c5-8014-70ff624f9e51' for object 'host.example.com'.
|
||||
sample:
|
||||
[
|
||||
{
|
||||
"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'.",
|
||||
}
|
||||
]
|
||||
error:
|
||||
description: Error message as JSON dictionary returned from the Icinga 2 API.
|
||||
type: dict
|
||||
returned: if downtime scheduling or removal did not succeed
|
||||
sample:
|
||||
{
|
||||
"error": 404,
|
||||
"status": "No objects found."
|
||||
}
|
||||
"""
|
||||
|
||||
import json
|
||||
from contextlib import suppress
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils._icinga2 import (
|
||||
Icinga2Client,
|
||||
icinga2_argument_spec,
|
||||
)
|
||||
from ansible_collections.community.general.plugins.module_utils.module_helper import StateModuleHelper
|
||||
|
||||
|
||||
class Icinga2Downtime(StateModuleHelper):
|
||||
argument_spec = icinga2_argument_spec()
|
||||
argument_spec.update(
|
||||
all_services=dict(type="bool"),
|
||||
author=dict(type="str", default="Ansible"),
|
||||
comment=dict(type="str", default="Downtime scheduled by Ansible"),
|
||||
child_options=dict(
|
||||
type="str",
|
||||
choices=[
|
||||
"DowntimeNoChildren",
|
||||
"DowntimeTriggeredChildren",
|
||||
"DowntimeNonTriggeredChildren",
|
||||
],
|
||||
),
|
||||
duration=dict(type="int"),
|
||||
end_time=dict(type="int"),
|
||||
filter_vars=dict(type="dict"),
|
||||
filter=dict(type="str"),
|
||||
fixed=dict(type="bool"),
|
||||
name=dict(type="str"),
|
||||
object_type=dict(type="str", choices=["Service", "Host", "Downtime"], default="Host"),
|
||||
start_time=dict(type="int"),
|
||||
state=dict(type="str", choices=["present", "absent"], default="present"),
|
||||
trigger_name=dict(type="str"),
|
||||
)
|
||||
module = dict(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=False,
|
||||
required_if=(
|
||||
(
|
||||
"state",
|
||||
"present",
|
||||
["comment", "start_time", "end_time", "filter"],
|
||||
),
|
||||
("fixed", False, ["duration"]),
|
||||
),
|
||||
required_one_of=[["filter", "name"]],
|
||||
)
|
||||
|
||||
def __init_module__(self) -> None:
|
||||
self.client = Icinga2Client(
|
||||
module=self.module, # type:ignore[arg-type]
|
||||
url=self.vars.url,
|
||||
ca_path=self.vars.ca_path,
|
||||
timeout=self.vars.timeout,
|
||||
)
|
||||
|
||||
def state_present(self) -> None:
|
||||
duration = self.vars.duration
|
||||
end_time = self.vars.end_time
|
||||
start_time = self.vars.start_time
|
||||
|
||||
if end_time <= start_time:
|
||||
self.do_raise(msg="The end time must be later than the start time.")
|
||||
|
||||
if duration is None:
|
||||
duration = end_time - start_time
|
||||
|
||||
response, info = self.client.actions.schedule_downtime(
|
||||
all_services=self.vars.all_services,
|
||||
author=self.vars.author,
|
||||
child_options=self.vars.child_options,
|
||||
comment=self.vars.comment,
|
||||
duration=duration,
|
||||
end_time=end_time,
|
||||
filter_vars=self.vars.filter_vars,
|
||||
filter=self.vars.filter,
|
||||
fixed=self.vars.fixed,
|
||||
object_type=self.vars.object_type,
|
||||
start_time=start_time,
|
||||
trigger_name=self.vars.trigger_name,
|
||||
)
|
||||
|
||||
status_code = info["status"]
|
||||
|
||||
if 200 <= status_code <= 299:
|
||||
self.vars.set("results", json.loads(response.read())["results"], output=True)
|
||||
self.vars.msg = "Successfully scheduled downtime."
|
||||
self.changed = True
|
||||
elif status_code >= 400:
|
||||
with suppress(KeyError, ValueError):
|
||||
self.vars.set("error", json.loads(info["body"])) # type:ignore[arg-type]
|
||||
|
||||
self.do_raise(msg="Unable to schedule downtime.")
|
||||
|
||||
def state_absent(self) -> None:
|
||||
response, info = self.client.actions.remove_downtime(
|
||||
filter_vars=self.vars.filter_vars,
|
||||
filter=self.vars.filter,
|
||||
name=self.vars.name,
|
||||
object_type=self.vars.object_type,
|
||||
)
|
||||
|
||||
status_code = info["status"]
|
||||
|
||||
if 200 <= status_code <= 299:
|
||||
self.vars.set("results", json.loads(response.read())["results"], output=True)
|
||||
self.vars.msg = "Successfully removed downtime."
|
||||
self.changed = True
|
||||
elif status_code == 404:
|
||||
self.vars.msg = "No matching downtime object found."
|
||||
elif status_code >= 400:
|
||||
with suppress(KeyError, ValueError):
|
||||
self.vars.set("error", json.loads(info["body"])) # type:ignore[arg-type]
|
||||
|
||||
self.do_raise(msg="Unable to remove downtime.")
|
||||
|
||||
|
||||
def main():
|
||||
Icinga2Downtime.execute()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue