1#!/usr/local/bin/python3.8
2# -*- coding: utf-8 -*-
3# Copyright: Ansible Project
4# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
5
6from __future__ import absolute_import, division, print_function
7__metaclass__ = type
8
9
10DOCUMENTATION = '''
11
12module: pagerduty_alert
13short_description: Trigger, acknowledge or resolve PagerDuty incidents
14description:
15    - This module will let you trigger, acknowledge or resolve a PagerDuty incident by sending events
16author:
17    - "Amanpreet Singh (@ApsOps)"
18requirements:
19    - PagerDuty API access
20options:
21    name:
22        type: str
23        description:
24            - PagerDuty unique subdomain. Obsolete. It is not used with PagerDuty REST v2 API.
25    service_id:
26        type: str
27        description:
28            - ID of PagerDuty service when incidents will be triggered, acknowledged or resolved.
29        required: true
30    service_key:
31        type: str
32        description:
33            - The GUID of one of your "Generic API" services. Obsolete. Please use I(integration_key).
34    integration_key:
35        type: str
36        description:
37            - The GUID of one of your "Generic API" services.
38            - This is the "integration key" listed on a "Integrations" tab of PagerDuty service.
39    state:
40        type: str
41        description:
42            - Type of event to be sent.
43        required: true
44        choices:
45            - 'triggered'
46            - 'acknowledged'
47            - 'resolved'
48    api_key:
49        type: str
50        description:
51            - The pagerduty API key (readonly access), generated on the pagerduty site.
52        required: true
53    desc:
54        type: str
55        description:
56            - For C(triggered) I(state) - Required. Short description of the problem that led to this trigger. This field (or a truncated version)
57              will be used when generating phone calls, SMS messages and alert emails. It will also appear on the incidents tables in the PagerDuty UI.
58              The maximum length is 1024 characters.
59            - For C(acknowledged) or C(resolved) I(state) - Text that will appear in the incident's log associated with this event.
60        required: false
61        default: Created via Ansible
62    incident_key:
63        type: str
64        description:
65            - Identifies the incident to which this I(state) should be applied.
66            - For C(triggered) I(state) - If there's no open (i.e. unresolved) incident with this key, a new one will be created. If there's already an
67              open incident with a matching key, this event will be appended to that incident's log. The event key provides an easy way to "de-dup"
68              problem reports.
69            - For C(acknowledged) or C(resolved) I(state) - This should be the incident_key you received back when the incident was first opened by a
70              trigger event. Acknowledge events referencing resolved or nonexistent incidents will be discarded.
71        required: false
72    client:
73        type: str
74        description:
75        - The name of the monitoring client that is triggering this event.
76        required: false
77    client_url:
78        type: str
79        description:
80        -  The URL of the monitoring client that is triggering this event.
81        required: false
82'''
83
84EXAMPLES = '''
85- name: Trigger an incident with just the basic options
86  community.general.pagerduty_alert:
87    name: companyabc
88    integration_key: xxx
89    api_key: yourapikey
90    service_id: PDservice
91    state: triggered
92    desc: problem that led to this trigger
93
94- name: Trigger an incident with more options
95  community.general.pagerduty_alert:
96    integration_key: xxx
97    api_key: yourapikey
98    service_id: PDservice
99    state: triggered
100    desc: problem that led to this trigger
101    incident_key: somekey
102    client: Sample Monitoring Service
103    client_url: http://service.example.com
104
105- name: Acknowledge an incident based on incident_key
106  community.general.pagerduty_alert:
107    integration_key: xxx
108    api_key: yourapikey
109    service_id: PDservice
110    state: acknowledged
111    incident_key: somekey
112    desc: "some text for incident's log"
113
114- name: Resolve an incident based on incident_key
115  community.general.pagerduty_alert:
116    integration_key: xxx
117    api_key: yourapikey
118    service_id: PDservice
119    state: resolved
120    incident_key: somekey
121    desc: "some text for incident's log"
122'''
123import json
124
125from ansible.module_utils.basic import AnsibleModule
126from ansible.module_utils.urls import fetch_url
127from ansible.module_utils.six.moves.urllib.parse import urlparse, urlencode, urlunparse
128
129
130def check(module, name, state, service_id, integration_key, api_key, incident_key=None, http_call=fetch_url):
131    url = 'https://api.pagerduty.com/incidents'
132    headers = {
133        "Content-type": "application/json",
134        "Authorization": "Token token=%s" % api_key,
135        'Accept': 'application/vnd.pagerduty+json;version=2'
136    }
137
138    params = {
139        'service_ids[]': service_id,
140        'sort_by': 'incident_number:desc',
141        'time_zone': 'UTC'
142    }
143    if incident_key:
144        params['incident_key'] = incident_key
145
146    url_parts = list(urlparse(url))
147    url_parts[4] = urlencode(params, True)
148
149    url = urlunparse(url_parts)
150
151    response, info = http_call(module, url, method='get', headers=headers)
152
153    if info['status'] != 200:
154        module.fail_json(msg="failed to check current incident status."
155                             "Reason: %s" % info['msg'])
156
157    incidents = json.loads(response.read())["incidents"]
158    msg = "No corresponding incident"
159
160    if len(incidents) == 0:
161        if state in ('acknowledged', 'resolved'):
162            return msg, False
163        return msg, True
164    elif state != incidents[0]["status"]:
165        return incidents[0], True
166
167    return incidents[0], False
168
169
170def send_event(module, service_key, event_type, desc,
171               incident_key=None, client=None, client_url=None):
172    url = "https://events.pagerduty.com/generic/2010-04-15/create_event.json"
173    headers = {
174        "Content-type": "application/json"
175    }
176
177    data = {
178        "service_key": service_key,
179        "event_type": event_type,
180        "incident_key": incident_key,
181        "description": desc,
182        "client": client,
183        "client_url": client_url
184    }
185
186    response, info = fetch_url(module, url, method='post',
187                               headers=headers, data=json.dumps(data))
188    if info['status'] != 200:
189        module.fail_json(msg="failed to %s. Reason: %s" %
190                         (event_type, info['msg']))
191    json_out = json.loads(response.read())
192    return json_out
193
194
195def main():
196    module = AnsibleModule(
197        argument_spec=dict(
198            name=dict(required=False),
199            service_id=dict(required=True),
200            service_key=dict(required=False, no_log=True),
201            integration_key=dict(required=False, no_log=True),
202            api_key=dict(required=True, no_log=True),
203            state=dict(required=True,
204                       choices=['triggered', 'acknowledged', 'resolved']),
205            client=dict(required=False, default=None),
206            client_url=dict(required=False, default=None),
207            desc=dict(required=False, default='Created via Ansible'),
208            incident_key=dict(required=False, default=None, no_log=False)
209        ),
210        supports_check_mode=True
211    )
212
213    name = module.params['name']
214    service_id = module.params['service_id']
215    integration_key = module.params['integration_key']
216    service_key = module.params['service_key']
217    api_key = module.params['api_key']
218    state = module.params['state']
219    client = module.params['client']
220    client_url = module.params['client_url']
221    desc = module.params['desc']
222    incident_key = module.params['incident_key']
223
224    if integration_key is None:
225        if service_key is not None:
226            integration_key = service_key
227            module.warn('"service_key" is obsolete parameter and will be removed.'
228                        ' Please, use "integration_key" instead')
229        else:
230            module.fail_json(msg="'integration_key' is required parameter")
231
232    state_event_dict = {
233        'triggered': 'trigger',
234        'acknowledged': 'acknowledge',
235        'resolved': 'resolve'
236    }
237
238    event_type = state_event_dict[state]
239
240    if event_type != 'trigger' and incident_key is None:
241        module.fail_json(msg="incident_key is required for "
242                             "acknowledge or resolve events")
243
244    out, changed = check(module, name, state, service_id,
245                         integration_key, api_key, incident_key)
246
247    if not module.check_mode and changed is True:
248        out = send_event(module, integration_key, event_type, desc,
249                         incident_key, client, client_url)
250
251    module.exit_json(result=out, changed=changed)
252
253
254if __name__ == '__main__':
255    main()
256