From e701a552c4c2e48770aeb4c0d42f28bb2ca80ce1 Mon Sep 17 00:00:00 2001 From: Joseph Hall Date: Mon, 6 Oct 2014 11:18:35 -0600 Subject: [PATCH] Move PagerDuty to use native API instead of pygerduty --- salt/modules/pagerduty.py | 162 +++++++++++--------------------------- salt/utils/pagerduty.py | 118 +++++++++++++++++++++++++++ 2 files changed, 162 insertions(+), 118 deletions(-) create mode 100644 salt/utils/pagerduty.py diff --git a/salt/modules/pagerduty.py b/salt/modules/pagerduty.py index 7ef4d569c0..a2ce0c3ff4 100644 --- a/salt/modules/pagerduty.py +++ b/salt/modules/pagerduty.py @@ -4,10 +4,9 @@ Module for Firing Events via PagerDuty .. versionadded:: 2014.1.0 -:depends: - pygerduty python module -:configuration: This module can be used by either passing a jid and password - directly to send_message, or by specifying the name of a configuration - profile in the minion config, minion pillar, or master config. +:configuration: This module can be used by specifying the name of a + configuration profile in the minion config, minion pillar, or master + config. For example: @@ -18,40 +17,23 @@ Module for Firing Events via PagerDuty pagerduty.subdomain: mysubdomain ''' -HAS_LIBS = False -try: - import pygerduty - HAS_LIBS = True -except ImportError: - pass +# Import python libs +import yaml +import json -__virtualname__ = 'pagerduty' +# Import salt libs +import salt.utils.pagerduty +from salt._compat import string_types def __virtual__(): ''' - Only load this module if pygerduty is installed on this minion. + No dependencies outside of what Salt itself requires ''' - if HAS_LIBS: - return __virtualname__ - return False + return True -def _get_pager(profile): - ''' - Return the pagerduty connection - ''' - creds = __salt__['config.option'](profile) - - pager = pygerduty.PagerDuty( - creds.get('pagerduty.subdomain'), - creds.get('pagerduty.api_key'), - ) - - return pager - - -def list_services(profile): +def list_services(profile=None, api_key=None): ''' List services belonging to this account @@ -59,35 +41,12 @@ def list_services(profile): pagerduty.list_services my-pagerduty-account ''' - pager = _get_pager(profile) - ret = {} - for service in pager.services.list(): - ret[service.name] = { - 'acknowledgement_timeout': service.acknowledgement_timeout, - 'auto_resolve_timeout': service.auto_resolve_timeout, - 'created_at': service.created_at, - 'deleted_at': service.deleted_at, - 'description': service.description, - 'email_filter_mode': service.email_filter_mode, - 'email_incident_creation': service.email_incident_creation, - 'id': service.id, - 'incident_counts': { - 'acknowledged': service.incident_counts.acknowledged, - 'resolved': service.incident_counts.resolved, - 'total': service.incident_counts.total, - 'triggered': service.incident_counts.triggered, - }, - 'last_incident_timestamp': service.last_incident_timestamp, - 'name': service.name, - 'service_key': service.service_key, - 'service_url': service.service_url, - 'status': service.status, - 'type': service.type, - } - return ret + return salt.utils.pagerduty.list_items( + 'services', 'name', profile, api_key, opts=__opts__ + ) -def list_incidents(profile): +def list_incidents(profile=None, api_key=None): ''' List services belonging to this account @@ -95,59 +54,13 @@ def list_incidents(profile): pagerduty.list_incidents my-pagerduty-account ''' - pager = _get_pager(profile) - ret = {} - for incident in pager.incidents.list(): - ret[incident.id] = { - 'status': incident.status, - 'service': { - 'deleted_at': incident.service.deleted_at, - 'id': incident.service.id, - 'name': incident.service.name, - 'html_url': incident.service.html_url, - }, - 'trigger_type': incident.trigger_type, - 'escalation_policy': { - 'id': incident.escalation_policy.id, - 'name': incident.escalation_policy.name, - }, - 'assigned_to_user': incident.assigned_to_user, - 'html_url': incident.html_url, - 'last_status_change_on': incident.last_status_change_on, - 'last_status_change_by': {}, - 'incident_key': incident.incident_key, - 'created_on': incident.created_on, - 'number_of_escalations': incident.number_of_escalations, - 'incident_number': incident.incident_number, - 'resolved_by_user': {}, - 'trigger_details_html_url': incident.trigger_details_html_url, - 'id': incident.id, - 'trigger_summary_data': { - 'subject': None, - }, - } - if hasattr(incident.trigger_summary_data, 'subject'): - ret[incident.id]['trigger_summary_data']['subject'] = \ - incident.trigger_summary_data.subject - if hasattr(incident, 'resolved_by_user'): - ret[incident.id]['resolved_by_user'] = { - 'id': incident.resolved_by_user.id, - 'name': incident.resolved_by_user.name, - 'html_url': incident.resolved_by_user.html_url, - 'email': incident.resolved_by_user.email, - } - if hasattr(incident.last_status_change_by, 'id'): - ret[incident.id]['last_status_change_by'] = { - 'id': incident.last_status_change_by.id, - 'name': incident.last_status_change_by.name, - 'html_url': incident.last_status_change_by.html_url, - 'email': incident.last_status_change_by.email, - } - return ret + return salt.utils.pagerduty.list_items( + 'incidents', 'id', profile, api_key, opts=__opts__ + ) -def create_event(service_key, description, details, incident_key=None, - profile=None): +def create_event(service_key=None, description=None, details=None, + incident_key=None, profile=None): ''' Create an event in PagerDuty. Designed for use in states. @@ -155,7 +68,7 @@ def create_event(service_key, description, details, incident_key=None, pagerduty.create_event
\ profile=my-pagerduty-account - +: The following parameters are required: service_key @@ -171,12 +84,25 @@ def create_event(service_key, description, details, incident_key=None, This refers to the configuration profile to use to connect to the PagerDuty service. ''' - pager = _get_pager(profile) - event = pager.create_event( - service_key=service_key, - description=description, - details=details, - event_type='trigger', - incident_key=incident_key, - ) - return {'incident_key': str(event)} + trigger_url = 'https://events.pagerduty.com/generic/2010-04-15/create_event.json' + + if isinstance(details, string_types): + details = yaml.safe_load(details) + if isinstance(details, string_types): + details = {'details': details} + + ret = json.loads(salt.utils.pagerduty.query( + method='POST', + profile=profile, + api_key=service_key, + data={ + 'service_key': service_key, + 'incident_key': incident_key, + 'event_type': 'trigger', + 'description': description, + 'details': details, + }, + url=trigger_url, + opts=__opts__ + )) + return ret diff --git a/salt/utils/pagerduty.py b/salt/utils/pagerduty.py new file mode 100644 index 0000000000..186a39b056 --- /dev/null +++ b/salt/utils/pagerduty.py @@ -0,0 +1,118 @@ +# -*- coding: utf-8 -*- +''' +Library for interacting with PagerDuty API + +.. versionadded:: 2014.7.0 + +:configuration: This module can be used by specifying the name of a + configuration profile in the minion config, minion pillar, or master + config. + + For example: + + .. code-block:: yaml + + my-pagerduty-account: + pagerduty.subdomain: mysubdomain + pagerduty.api_key: F3Rbyjbve43rfFWf2214 +''' + +import json +import requests +import logging +from salt.version import __version__ + +log = logging.getLogger(__name__) + + +def query(method='GET', profile=None, url=None, path='api/v1', + action=None, api_key=None, service=None, params=None, + data=None, subdomain=None, client_url=None, description=None, + opts=None, verify_ssl=True): + ''' + Query the PagerDuty API + ''' + user_agent = 'SaltStack {0}'.format(__version__) + + if opts is None: + opts = {} + + if profile is not None: + creds = opts.get(profile) + else: + creds = {} + + if api_key is not None: + creds['pagerduty.api_key'] = api_key + + if service is not None: + creds['pagerduty.service'] = service + + if subdomain is not None: + creds['pagerduty.subdomain'] = subdomain + + if client_url is None: + client_url = 'https://{0}.pagerduty.com'.format( + creds['pagerduty.subdomain'] + ) + + if url is None: + url = 'https://{0}.pagerduty.com/{1}/{2}'.format( + creds['pagerduty.subdomain'], + path, + action + ) + + if params is None: + params = {} + + if data is None: + data = {} + + data['client'] = user_agent + data['service_key'] = creds['pagerduty.service'] + data['client_url'] = client_url + if 'event_type' not in data: + data['event_type'] = 'trigger' + if 'description' not in data: + if not description: + data['description'] = 'SaltStack Event Triggered' + else: + data['description'] = description + + headers = { + 'User-Agent': user_agent, + 'Authorization': 'Token token={0}'.format(creds['pagerduty.api_key']) + } + if method == 'GET': + data = {} + else: + headers['Content-type'] = 'application/json' + + result = requests.request( + method, + url, + headers=headers, + params=params, + data=json.dumps(data), + verify=verify_ssl + ) + + return result.text + + +def list_items(action, key, profile=None, api_key=None, opts=None): + ''' + List items belonging to an API call. Used for list_services() and + list_incidents() + ''' + items = json.loads(query( + profile=profile, + api_key=api_key, + action=action, + opts=opts + )) + ret = {} + for item in items[action]: + ret[item[key]] = item + return ret