2020-12-15 19:50:42 +01:00
|
|
|
#!/usr/bin/python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
2022-08-05 12:28:29 +02:00
|
|
|
# Copyright (c) 2020, Datadog, Inc
|
|
|
|
# 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
|
2020-12-15 19:50:42 +01:00
|
|
|
|
|
|
|
from __future__ import absolute_import, division, print_function
|
|
|
|
|
|
|
|
__metaclass__ = type
|
|
|
|
|
|
|
|
DOCUMENTATION = """
|
|
|
|
---
|
|
|
|
module: datadog_downtime
|
|
|
|
short_description: Manages Datadog downtimes
|
|
|
|
version_added: 2.0.0
|
|
|
|
description:
|
|
|
|
- Manages downtimes within Datadog.
|
2023-06-19 18:01:16 +02:00
|
|
|
- Options as described on U(https://docs.datadoghq.com/api/v1/downtimes/).
|
2020-12-15 19:50:42 +01:00
|
|
|
author:
|
|
|
|
- Datadog (@Datadog)
|
|
|
|
requirements:
|
|
|
|
- datadog-api-client
|
|
|
|
- Python 3.6+
|
Add attributes to consul, datadog, jenkins, pagerduty, rundeck, and sensu modules (#5957)
* Add attributes to consul, datadog, jenkins, pagerduty, rundeck, and sensu modules.
* Fix error.
2023-02-24 09:23:56 +01:00
|
|
|
extends_documentation_fragment:
|
|
|
|
- community.general.attributes
|
|
|
|
attributes:
|
|
|
|
check_mode:
|
|
|
|
support: none
|
|
|
|
diff_mode:
|
|
|
|
support: none
|
2020-12-15 19:50:42 +01:00
|
|
|
options:
|
|
|
|
api_key:
|
|
|
|
description:
|
|
|
|
- Your Datadog API key.
|
|
|
|
required: true
|
|
|
|
type: str
|
|
|
|
api_host:
|
|
|
|
description:
|
|
|
|
- The URL to the Datadog API.
|
|
|
|
- This value can also be set with the C(DATADOG_HOST) environment variable.
|
|
|
|
required: false
|
|
|
|
default: https://api.datadoghq.com
|
|
|
|
type: str
|
|
|
|
app_key:
|
|
|
|
description:
|
|
|
|
- Your Datadog app key.
|
|
|
|
required: true
|
|
|
|
type: str
|
|
|
|
state:
|
|
|
|
description:
|
|
|
|
- The designated state of the downtime.
|
|
|
|
required: false
|
|
|
|
choices: ["present", "absent"]
|
|
|
|
default: present
|
|
|
|
type: str
|
|
|
|
id:
|
|
|
|
description:
|
|
|
|
- The identifier of the downtime.
|
|
|
|
- If empty, a new downtime gets created, otherwise it is either updated or deleted depending of the C(state).
|
|
|
|
- To keep your playbook idempotent, you should save the identifier in a file and read it in a lookup.
|
|
|
|
type: int
|
|
|
|
monitor_tags:
|
|
|
|
description:
|
|
|
|
- A list of monitor tags to which the downtime applies.
|
|
|
|
- The resulting downtime applies to monitors that match ALL provided monitor tags.
|
|
|
|
type: list
|
|
|
|
elements: str
|
|
|
|
scope:
|
|
|
|
description:
|
|
|
|
- A list of scopes to which the downtime applies.
|
|
|
|
- The resulting downtime applies to sources that matches ALL provided scopes.
|
|
|
|
type: list
|
|
|
|
elements: str
|
|
|
|
monitor_id:
|
|
|
|
description:
|
|
|
|
- The ID of the monitor to mute. If not provided, the downtime applies to all monitors.
|
|
|
|
type: int
|
|
|
|
downtime_message:
|
|
|
|
description:
|
|
|
|
- A message to include with notifications for this downtime.
|
|
|
|
- Email notifications can be sent to specific users by using the same "@username" notation as events.
|
|
|
|
type: str
|
|
|
|
start:
|
|
|
|
type: int
|
|
|
|
description:
|
|
|
|
- POSIX timestamp to start the downtime. If not provided, the downtime starts the moment it is created.
|
|
|
|
end:
|
|
|
|
type: int
|
|
|
|
description:
|
|
|
|
- POSIX timestamp to end the downtime. If not provided, the downtime is in effect until you cancel it.
|
|
|
|
timezone:
|
|
|
|
description:
|
|
|
|
- The timezone for the downtime.
|
|
|
|
type: str
|
|
|
|
rrule:
|
|
|
|
description:
|
|
|
|
- The C(RRULE) standard for defining recurring events.
|
|
|
|
- For example, to have a recurring event on the first day of each month,
|
|
|
|
select a type of rrule and set the C(FREQ) to C(MONTHLY) and C(BYMONTHDAY) to C(1).
|
|
|
|
- Most common rrule options from the iCalendar Spec are supported.
|
|
|
|
- Attributes specifying the duration in C(RRULE) are not supported (e.g. C(DTSTART), C(DTEND), C(DURATION)).
|
|
|
|
type: str
|
|
|
|
"""
|
|
|
|
|
|
|
|
EXAMPLES = """
|
|
|
|
- name: Create a downtime
|
|
|
|
register: downtime_var
|
|
|
|
community.general.datadog_downtime:
|
|
|
|
state: present
|
|
|
|
monitor_tags:
|
|
|
|
- "foo:bar"
|
|
|
|
downtime_message: "Downtime for foo:bar"
|
|
|
|
scope: "test"
|
|
|
|
api_key: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
|
|
|
app_key: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
|
|
|
# Lookup the id in the file and ignore errors if the file doesn't exits, so downtime gets created
|
|
|
|
id: "{{ lookup('file', inventory_hostname ~ '_downtime_id.txt', errors='ignore') }}"
|
|
|
|
- name: Save downtime id to file for later updates and idempotence
|
|
|
|
delegate_to: localhost
|
|
|
|
copy:
|
|
|
|
content: "{{ downtime.downtime.id }}"
|
|
|
|
dest: "{{ inventory_hostname ~ '_downtime_id.txt' }}"
|
|
|
|
"""
|
|
|
|
|
|
|
|
RETURN = """
|
|
|
|
# Returns the downtime JSON dictionary from the API response under the C(downtime) key.
|
|
|
|
# See https://docs.datadoghq.com/api/v1/downtimes/#schedule-a-downtime for more details.
|
|
|
|
downtime:
|
|
|
|
description: The downtime returned by the API.
|
|
|
|
type: dict
|
|
|
|
returned: always
|
|
|
|
sample: {
|
|
|
|
"active": true,
|
|
|
|
"canceled": null,
|
|
|
|
"creator_id": 1445416,
|
|
|
|
"disabled": false,
|
|
|
|
"downtime_type": 2,
|
|
|
|
"end": null,
|
|
|
|
"id": 1055751000,
|
|
|
|
"message": "Downtime for foo:bar",
|
|
|
|
"monitor_id": null,
|
|
|
|
"monitor_tags": [
|
|
|
|
"foo:bar"
|
|
|
|
],
|
|
|
|
"parent_id": null,
|
|
|
|
"recurrence": null,
|
|
|
|
"scope": [
|
|
|
|
"test"
|
|
|
|
],
|
|
|
|
"start": 1607015009,
|
|
|
|
"timezone": "UTC",
|
|
|
|
"updater_id": null
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
|
|
|
|
import traceback
|
|
|
|
|
|
|
|
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
|
|
|
# Import Datadog
|
|
|
|
|
|
|
|
DATADOG_IMP_ERR = None
|
|
|
|
HAS_DATADOG = True
|
|
|
|
try:
|
|
|
|
from datadog_api_client.v1 import Configuration, ApiClient, ApiException
|
|
|
|
from datadog_api_client.v1.api.downtimes_api import DowntimesApi
|
|
|
|
from datadog_api_client.v1.model.downtime import Downtime
|
|
|
|
from datadog_api_client.v1.model.downtime_recurrence import DowntimeRecurrence
|
|
|
|
except ImportError:
|
|
|
|
DATADOG_IMP_ERR = traceback.format_exc()
|
|
|
|
HAS_DATADOG = False
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
module = AnsibleModule(
|
|
|
|
argument_spec=dict(
|
|
|
|
api_key=dict(required=True, no_log=True),
|
|
|
|
api_host=dict(required=False, default="https://api.datadoghq.com"),
|
|
|
|
app_key=dict(required=True, no_log=True),
|
|
|
|
state=dict(required=False, choices=["present", "absent"], default="present"),
|
|
|
|
monitor_tags=dict(required=False, type="list", elements="str"),
|
|
|
|
scope=dict(required=False, type="list", elements="str"),
|
|
|
|
monitor_id=dict(required=False, type="int"),
|
|
|
|
downtime_message=dict(required=False, no_log=True),
|
|
|
|
start=dict(required=False, type="int"),
|
|
|
|
end=dict(required=False, type="int"),
|
|
|
|
timezone=dict(required=False, type="str"),
|
|
|
|
rrule=dict(required=False, type="str"),
|
|
|
|
id=dict(required=False, type="int"),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
# Prepare Datadog
|
|
|
|
if not HAS_DATADOG:
|
|
|
|
module.fail_json(msg=missing_required_lib("datadog-api-client"), exception=DATADOG_IMP_ERR)
|
|
|
|
|
|
|
|
configuration = Configuration(
|
|
|
|
host=module.params["api_host"],
|
|
|
|
api_key={
|
|
|
|
"apiKeyAuth": module.params["api_key"],
|
|
|
|
"appKeyAuth": module.params["app_key"]
|
|
|
|
}
|
|
|
|
)
|
|
|
|
with ApiClient(configuration) as api_client:
|
|
|
|
api_client.user_agent = "ansible_collection/community_general (module_name datadog_downtime) {0}".format(
|
|
|
|
api_client.user_agent
|
|
|
|
)
|
|
|
|
api_instance = DowntimesApi(api_client)
|
|
|
|
|
|
|
|
# Validate api and app keys
|
|
|
|
try:
|
|
|
|
api_instance.list_downtimes(current_only=True)
|
|
|
|
except ApiException as e:
|
|
|
|
module.fail_json(msg="Failed to connect Datadog server using given app_key and api_key: {0}".format(e))
|
|
|
|
|
|
|
|
if module.params["state"] == "present":
|
|
|
|
schedule_downtime(module, api_client)
|
|
|
|
elif module.params["state"] == "absent":
|
|
|
|
cancel_downtime(module, api_client)
|
|
|
|
|
|
|
|
|
|
|
|
def _get_downtime(module, api_client):
|
|
|
|
api = DowntimesApi(api_client)
|
|
|
|
downtime = None
|
|
|
|
if module.params["id"]:
|
|
|
|
try:
|
|
|
|
downtime = api.get_downtime(module.params["id"])
|
|
|
|
except ApiException as e:
|
|
|
|
module.fail_json(msg="Failed to retrieve downtime with id {0}: {1}".format(module.params["id"], e))
|
|
|
|
return downtime
|
|
|
|
|
|
|
|
|
|
|
|
def build_downtime(module):
|
|
|
|
downtime = Downtime()
|
|
|
|
if module.params["monitor_tags"]:
|
|
|
|
downtime.monitor_tags = module.params["monitor_tags"]
|
|
|
|
if module.params["scope"]:
|
|
|
|
downtime.scope = module.params["scope"]
|
|
|
|
if module.params["monitor_id"]:
|
|
|
|
downtime.monitor_id = module.params["monitor_id"]
|
|
|
|
if module.params["downtime_message"]:
|
|
|
|
downtime.message = module.params["downtime_message"]
|
|
|
|
if module.params["start"]:
|
|
|
|
downtime.start = module.params["start"]
|
|
|
|
if module.params["end"]:
|
|
|
|
downtime.end = module.params["end"]
|
|
|
|
if module.params["timezone"]:
|
|
|
|
downtime.timezone = module.params["timezone"]
|
|
|
|
if module.params["rrule"]:
|
|
|
|
downtime.recurrence = DowntimeRecurrence(
|
|
|
|
rrule=module.params["rrule"]
|
|
|
|
)
|
|
|
|
return downtime
|
|
|
|
|
|
|
|
|
|
|
|
def _post_downtime(module, api_client):
|
|
|
|
api = DowntimesApi(api_client)
|
|
|
|
downtime = build_downtime(module)
|
|
|
|
try:
|
|
|
|
resp = api.create_downtime(downtime)
|
|
|
|
module.params["id"] = resp.id
|
|
|
|
module.exit_json(changed=True, downtime=resp.to_dict())
|
|
|
|
except ApiException as e:
|
|
|
|
module.fail_json(msg="Failed to create downtime: {0}".format(e))
|
|
|
|
|
|
|
|
|
|
|
|
def _equal_dicts(a, b, ignore_keys):
|
|
|
|
ka = set(a).difference(ignore_keys)
|
|
|
|
kb = set(b).difference(ignore_keys)
|
|
|
|
return ka == kb and all(a[k] == b[k] for k in ka)
|
|
|
|
|
|
|
|
|
|
|
|
def _update_downtime(module, current_downtime, api_client):
|
|
|
|
api = DowntimesApi(api_client)
|
|
|
|
downtime = build_downtime(module)
|
|
|
|
try:
|
|
|
|
if current_downtime.disabled:
|
|
|
|
resp = api.create_downtime(downtime)
|
|
|
|
else:
|
|
|
|
resp = api.update_downtime(module.params["id"], downtime)
|
|
|
|
if _equal_dicts(
|
|
|
|
resp.to_dict(),
|
|
|
|
current_downtime.to_dict(),
|
|
|
|
["active", "creator_id", "updater_id"]
|
|
|
|
):
|
|
|
|
module.exit_json(changed=False, downtime=resp.to_dict())
|
|
|
|
else:
|
|
|
|
module.exit_json(changed=True, downtime=resp.to_dict())
|
|
|
|
except ApiException as e:
|
|
|
|
module.fail_json(msg="Failed to update downtime: {0}".format(e))
|
|
|
|
|
|
|
|
|
|
|
|
def schedule_downtime(module, api_client):
|
|
|
|
downtime = _get_downtime(module, api_client)
|
|
|
|
if downtime is None:
|
|
|
|
_post_downtime(module, api_client)
|
|
|
|
else:
|
|
|
|
_update_downtime(module, downtime, api_client)
|
|
|
|
|
|
|
|
|
|
|
|
def cancel_downtime(module, api_client):
|
|
|
|
downtime = _get_downtime(module, api_client)
|
|
|
|
api = DowntimesApi(api_client)
|
|
|
|
if downtime is None:
|
|
|
|
module.exit_json(changed=False)
|
|
|
|
try:
|
|
|
|
api.cancel_downtime(downtime["id"])
|
|
|
|
except ApiException as e:
|
|
|
|
module.fail_json(msg="Failed to create downtime: {0}".format(e))
|
|
|
|
|
|
|
|
module.exit_json(changed=True)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|