#!/usr/bin/python # # Copyright 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 DOCUMENTATION = r""" module: pagerduty short_description: Create PagerDuty maintenance windows description: - This module lets you create PagerDuty maintenance windows. author: - "Andrew Newdigate (@suprememoocow)" - "Dylan Silva (@thaumos)" - "Justin Johns (!UNKNOWN)" - "Bruce Pennypacker (@bpennypacker)" requirements: - PagerDuty API access extends_documentation_fragment: - community.general.attributes attributes: check_mode: support: none diff_mode: support: none options: state: type: str description: - Create a maintenance window or get a list of ongoing windows. required: true choices: ["running", "started", "ongoing", "absent"] name: type: str description: - PagerDuty unique subdomain. Obsolete. It is not used with PagerDuty REST v2 API. user: type: str description: - PagerDuty user ID. Obsolete. Please, use O(token) for authorization. token: type: str description: - A pagerduty token, generated on the pagerduty site. It is used for authorization. required: true requester_id: type: str description: - ID of user making the request. Only needed when creating a maintenance_window. service: type: list elements: str description: - A comma separated list of PagerDuty service IDs. aliases: [services] window_id: type: str description: - ID of maintenance window. Only needed when absent a maintenance_window. hours: type: str description: - Length of maintenance window in hours. default: '1' minutes: type: str description: - Maintenance window in minutes (this is added to the hours). default: '0' desc: type: str description: - Short description of maintenance window. default: Created by Ansible validate_certs: description: - If V(false), SSL certificates are not validated. This should only be used on personally controlled sites using self-signed certificates. type: bool default: true """ EXAMPLES = r""" - name: List ongoing maintenance windows using a token community.general.pagerduty: name: companyabc token: xxxxxxxxxxxxxx state: ongoing - name: Create a 1 hour maintenance window for service FOO123 community.general.pagerduty: name: companyabc user: example@example.com token: yourtoken state: running service: FOO123 - name: Create a 5 minute maintenance window for service FOO123 community.general.pagerduty: name: companyabc token: xxxxxxxxxxxxxx hours: 0 minutes: 5 state: running service: FOO123 - name: Create a 4 hour maintenance window for service FOO123 with the description "deployment" community.general.pagerduty: name: companyabc user: example@example.com state: running service: FOO123 hours: 4 desc: deployment register: pd_window - name: Delete the previous maintenance window community.general.pagerduty: name: companyabc user: example@example.com state: absent window_id: '{{ pd_window.result.maintenance_window.id }}' # Delete a maintenance window from a separate playbook than its creation, # and if it is the only existing maintenance window - name: Check community.general.pagerduty: requester_id: XXXXXXX token: yourtoken state: ongoing register: pd_window - name: Delete community.general.pagerduty: requester_id: XXXXXXX token: yourtoken state: absent window_id: "{{ pd_window.result.maintenance_windows[0].id }}" """ import datetime import json from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.urls import fetch_url from ansible_collections.community.general.plugins.module_utils.datetime import ( now, ) class PagerDutyRequest: def __init__(self, module, name, user, token): self.module = module self.name = name self.user = user self.token = token self.headers = { "Content-Type": "application/json", "Authorization": self._auth_header(), "Accept": "application/vnd.pagerduty+json;version=2", } def ongoing(self, http_call=fetch_url): url = "https://api.pagerduty.com/maintenance_windows?filter=ongoing" headers = dict(self.headers) response, info = http_call(self.module, url, headers=headers) if info["status"] != 200: self.module.fail_json(msg=f"failed to lookup the ongoing window: {info['msg']}") json_out = self._read_response(response) return False, json_out, False def create(self, requester_id, service, hours, minutes, desc, http_call=fetch_url): if not requester_id: self.module.fail_json(msg="requester_id is required when maintenance window should be created") url = "https://api.pagerduty.com/maintenance_windows" headers = dict(self.headers) headers.update({"From": requester_id}) start, end = self._compute_start_end_time(hours, minutes) services = self._create_services_payload(service) request_data = { "maintenance_window": {"start_time": start, "end_time": end, "description": desc, "services": services} } data = json.dumps(request_data) response, info = http_call(self.module, url, data=data, headers=headers, method="POST") if info["status"] != 201: self.module.fail_json(msg=f"failed to create the window: {info['msg']}") json_out = self._read_response(response) return False, json_out, True def _create_services_payload(self, service): if isinstance(service, list): return [{"id": s, "type": "service_reference"} for s in service] else: return [{"id": service, "type": "service_reference"}] def _compute_start_end_time(self, hours, minutes): now_t = now() later = now_t + datetime.timedelta(hours=int(hours), minutes=int(minutes)) start = now_t.strftime("%Y-%m-%dT%H:%M:%SZ") end = later.strftime("%Y-%m-%dT%H:%M:%SZ") return start, end def absent(self, window_id, http_call=fetch_url): url = f"https://api.pagerduty.com/maintenance_windows/{window_id}" headers = dict(self.headers) response, info = http_call(self.module, url, headers=headers, method="DELETE") if info["status"] != 204: self.module.fail_json(msg=f"failed to delete the window: {info['msg']}") json_out = self._read_response(response) return False, json_out, True def _auth_header(self): return f"Token token={self.token}" def _read_response(self, response): try: return json.loads(response.read()) except Exception: return "" def main(): module = AnsibleModule( argument_spec=dict( state=dict(required=True, choices=["running", "started", "ongoing", "absent"]), name=dict(), user=dict(), token=dict(required=True, no_log=True), service=dict(type="list", elements="str", aliases=["services"]), window_id=dict(), requester_id=dict(), hours=dict(default="1"), # @TODO change to int? minutes=dict(default="0"), # @TODO change to int? desc=dict(default="Created by Ansible"), validate_certs=dict(default=True, type="bool"), ) ) state = module.params["state"] name = module.params["name"] user = module.params["user"] service = module.params["service"] window_id = module.params["window_id"] hours = module.params["hours"] minutes = module.params["minutes"] token = module.params["token"] desc = module.params["desc"] requester_id = module.params["requester_id"] pd = PagerDutyRequest(module, name, user, token) if state == "running" or state == "started": if not service: module.fail_json(msg="service not specified") (rc, out, changed) = pd.create(requester_id, service, hours, minutes, desc) if rc == 0: changed = True if state == "ongoing": (rc, out, changed) = pd.ongoing() if state == "absent": (rc, out, changed) = pd.absent(window_id) if rc != 0: module.fail_json(msg="failed", result=out) module.exit_json(msg="success", result=out, changed=changed) if __name__ == "__main__": main()