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