Git Product home page Git Product logo

pygerduty's Introduction

image

Pygerduty

Python Library for PagerDuty's REST API and Events API. This library was originally written to support v1 and is currently being updated to be compatible with v2 of the API. See "Migrating from v1 to v2" for more details.

This library is currently evolving and backwards compatibility cannot always be guaranteed at this time.

Installation

You can install with pip install pygerduty.

If you want to install from source, then python setup.py install.

Requirements

Tested on Python 2.7, or >= 3.6.

Known to work on Python 2.6.

Documentation

Pygerduty is a thin wrapper around PagerDuty's APIs. You will need to refer to the the PagerDuty Documentation for all available parameters to pass and all available attributes on responses.

The main methods available to resources are list, show, create, update, and delete. Not all resources have endpoints for all of the above methods. Again, refer to the PagerDuty Documentation to see all available endpoints.

Top level resources will be accessible via the PagerDuty object and nested resources available on containers returned from their parent resource.

Migrating from v1 to v2

In order to allow for a smooth transition between versions 1 and 2 of the library, version 1 library remains in the file called __init__.py inside of the pygerduty directory. Also in that directory you will see four other files:

  • v2.py — This file contains all updated logic compatible with v2 of the API.
  • events.py — PygerDuty also provides an Events API which is separate from the REST API that has had the recent update. Since the logic is mostly disjoint, we have created a new module for logic related to the Events API.
  • common.py — This file contains all common functions used by both v2.py and events.py.
  • version.py — Contains version info.

See the examples below to see how this affects how you will instantiate a client in v2.

Examples

Instantiating a client:

Version 1:

import pygerduty
pager = pygerduty.PagerDuty("foobar", "SOMEAPIKEY123456")

Version 2:

import pygerduty.v2
pager = pygerduty.v2.PagerDuty("SOMEAPIKEY123456")

Listing a resource:

for schedule in pager.schedules.list():
    print(schedule.id, schedule.name)

# PX7F8S3 Primary
# PJ48C0S Tertiary
# PCJ94SK Secondary

Getting all schedules associated with a user:

user = pager.users.show('PDSKF08')
for schedule in user.schedules.list():
    print(schedule.id, schedule.name)

# PDSARUD Ops
# PTDSKJH Support

Getting a resource by ID:

schedule = pager.schedules.show("PX7F8S3")

Creating a resource:

user = next(pager.users.list(query="gary", limit=1))
override = schedule.overrides.create(
    start="2012-12-16", end="2012-12-17", user_id=user.id)

Delete a resource:

schedule.overrides.delete(override.id)

Updating a resource:

pagerduty.users.update(user.id, name="Gary Example")

Acknowledging a group by incidents:

me = next(pager.users.list(query="[email protected]", limit=1))
for incident in pagerduty.incidents.list(status='triggered'):
    incident.acknowledge(requester_id=me.id)

pygerduty's People

Contributors

adnam avatar akhilg avatar amckenna-pinterest avatar ariscn avatar axinojolais avatar benjaminp avatar chris-martin avatar cugini-dbx avatar dcheckoway avatar gmjosack avatar jamezpolley avatar jeekl avatar khardsonhurley avatar laurent-indeed avatar laurentkol avatar leon-depop avatar mattrobenolt avatar mesozoic avatar nduthoit avatar nharkins avatar pawka avatar rlongair-dbx avatar roguelazer avatar satori avatar sofuture avatar thieman avatar tmancill avatar wbyoung avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pygerduty's Issues

getting event details from log_entries

I am new to pygerduty and still exploring.

I have a case were i need to get the event details from a log entry...

I know get_trigger_log_entry() gets me the details but the return i am getting is just
<LogEntry: contexts=[], created_at=u'2016-05-02T11:42:42Z', agent=<Container: type=u'service'>, type=u'trigger', id=u'someID', channel=<Container: type=u'email', summary=u'SomeSummary'>>

Is there a way i can completely get this info with pygerduty. The return of incidents.show has the link to
the log entry but does not give me the content of it.

Is there an attribute to get this content that I am missing.

Can some one please help me with my requirement. Thanks in advance.

Issue on python3.4

On python3.4 with the latest version of pygerduty, acknowledging an incident gives the following error. It works fine with python2.7

Traceback (most recent call last):
  File "oncall.py", line 35, in <module>
    incident.acknowledge(requester_id=me.id)
  File "/home/derek/pagerduty/lib/python3.4/site-packages/pygerduty/__init__.py", line 396, in acknowledge
    self._do_action('acknowledge', requester_id=requester_id)
  File "/home/derek/pagerduty/lib/python3.4/site-packages/pygerduty/__init__.py", line 390, in _do_action
    self.pagerduty.request("PUT", path, data=json.dumps(data))
  File "/home/derek/pagerduty/lib/python3.4/site-packages/pygerduty/__init__.py", line 654, in request
    return self.execute_request(request)
  File "/home/derek/pagerduty/lib/python3.4/site-packages/pygerduty/__init__.py", line 589, in execute_request
    response = (urllib.request.urlopen(request, timeout=self.timeout).
  File "/usr/lib64/python3.4/urllib/request.py", line 161, in urlopen
    return opener.open(url, data, timeout)
  File "/usr/lib64/python3.4/urllib/request.py", line 461, in open
    req = meth(req)
  File "/usr/lib64/python3.4/urllib/request.py", line 1112, in do_request_
    raise TypeError(msg)
TypeError: POST data should be bytes or an iterable of bytes. It cannot be of type str.

Generator functions bail on retry-able exceptions

For tracking purposes.

The original v1 version of Requester included an additive backoff/retry loop for execute_request(). This logic does not appear to have been ported to the v2 Requester class. Unfortunately, HTTPError(429) and URLError(Timeout) both can raise exceptions that will interrupt an iteration in progress. Because we want to allow uninterrupted iteration over things like Incident.list(), any applicable retries should occur before raising these exceptions.

This retry logic is especially pertinent for multi-threading, as (for example) a parent thread may iterate over incidents, while child threads iterate over log_entries. There should be an expectation that rapid requests from the child, causing HTTP 429, should not raise an exception in the parent thread iterator as it does today.

Passing array values for parameters (example: ?include[]=escalation_policy) in requests

Can we add support for array parameters in requests?

pd = pygerduty.PagerDuty(NAME, API_KEY)
pd.services.show(service_id, include='escalation_policy')

I need a request to pass a url that looks like:
https://subdomain.pagerduty.com/api/v1/services/XXXXXXX?include[]=escalation_policy

Keyword arguments do not support brackets so the request becomes malformed:
https://subdomain.pagerduty.com/api/v1/services/XXXXXXX?include=escalation_policy

pygerduty.BadRequest: Invalid Parameters (99999): {u'include': [u'Include must be an Array']}
    def request(self, method, path, query_params=None, data=None,
                extra_headers=None):
# lines omitted
        if query_params is not None:
            query_params = urllib.urlencode(query_params)

From the PagerDuty API docs, it looks like there are 3 array parameters: escalation_policies, include, and schedule_layers.

Perhaps, we could add brackets with a regexp?

        if query_params is not None:
            # Add brackets for array keys
            query_params = re.sub(r'(escalation_policies|include|schedule_layers)', 
                                  r'\1[]', urllib.urlencode(query_params))

PagerClient not found

Appears as though the PagerClient method was removed from the commit?

>>> import pygerduty
>>> pager = pygerduty.PagerClient("foobar", "SOMEAPIKEY123456")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'PagerClient'
>>>

After updating to v2, `schedules.show(id)` hits `TypeError: a float is required`

This simple code to load a schedule succeeds in v1 but fails in v2:

import pygerduty
import pygerduty.v2

pagerduty_key = getTheSecretKey() #redacted

pager_v1 = pygerduty.PagerDuty("asana", pagerduty_key)
pager_v2 = pygerduty.v2.PagerDuty("asana", pagerduty_key)

schedule_id = "some_id"

# the following succeeds
pager_v1.schedules.show(schedule_id)

# the following fails
pager_v2.schedules.show(schedule_id)

Here's the stack trace:

  File "/usr/local/lib/python2.7/dist-packages/pygerduty/v2.py", line 693, in request
    return self.requester.execute_request(request)
  File "/usr/local/lib/python2.7/dist-packages/pygerduty/common.py", line 50, in execute_request
    response = (self.opener.open(request, timeout=self.timeout).
  File "/usr/lib/python2.7/urllib2.py", line 429, in open
    response = self._open(req, data)
  File "/usr/lib/python2.7/urllib2.py", line 447, in _open
    '_open', req)
  File "/usr/lib/python2.7/urllib2.py", line 407, in _call_chain
    result = func(*args)
  File "/usr/lib/python2.7/urllib2.py", line 1241, in https_open
    context=self._context)
  File "/usr/lib/python2.7/urllib2.py", line 1195, in do_open
    h.request(req.get_method(), req.get_selector(), req.data, headers)
  File "/usr/lib/python2.7/httplib.py", line 1057, in request
    self._send_request(method, url, body, headers)
  File "/usr/lib/python2.7/httplib.py", line 1097, in _send_request
    self.endheaders(body)
  File "/usr/lib/python2.7/httplib.py", line 1053, in endheaders
    self._send_output(message_body)
  File "/usr/lib/python2.7/httplib.py", line 897, in _send_output
    self.send(msg)
  File "/usr/lib/python2.7/httplib.py", line 859, in send
    self.connect()
  File "/usr/lib/python2.7/httplib.py", line 1270, in connect
    HTTPConnection.connect(self)
  File "/usr/lib/python2.7/httplib.py", line 836, in connect
    self.timeout, self.source_address)
  File "/usr/lib/python2.7/socket.py", line 563, in create_connection
    sock.settimeout(timeout)
  File "/usr/lib/python2.7/socket.py", line 228, in meth
    return getattr(self._sock,name)(*args)
TypeError: a float is required

I've tried messing with the source and can't figure out what changed between v1 and v2 that would cause the bug.

I'm using pygerduty version 0.38.0 and python version 2.7.12.

Discussion: Ability to move from object to object

So if the title doesn't make sense let me explain. I would like the ability to go from a parent object to a child object. For instance Team -> Service -> Escalation Policy -> Escalation Rules -> Schedules.

So first off I am only opening this for discussion since I think it would be pretty spiffy to do this since they link together. So let me know your thoughts if this is worth pursuing.

New 'self' fields in PagerDuty REST API responses trigger TypeErrors

PagerDuty have started returning new self fields in their responses. These fields self-reference the canonical URI of the resource in the response, e.g.:

{
  [...]
  "self": "https://contoso.pagerduty.com/api/v1/escalation_policies/PABCDEF"
}

This causes obvious problems in pygerduty when the key-values from the response payload are passed to a Container constructor. The problem typically manifests with a stack trace not unlike:

  File "/Users/saj/.pex/install/pygerduty-0.28-py2-none-any.whl.ba6cbb714c785594762b7f0a3df561c0fae3a825/pygerduty-0.28-py2-none-any.whl/pygerduty/__init__.py", line 84, in create
    return self.container(self, **response.get(self.sname, {}))
TypeError: __init__() got multiple values for keyword argument 'self'

Incorrect arguments supplied to json.JSONEncoder.default

This happens when I try to trigger an incident with a non JSON serializable object in the 'details' dictionary:

Traceback (most recent call last):
  File "/home/mgrandi/rabbitmq_monitor/app/rabbitmq_monitor/pagerduty.py", line 56, in _trigger_incident
    client=self.client_name)
  File "/home/mgrandi/.pyenv/versions/rabbitmq_monitor_venv/lib/python3.5/site-packages/pygerduty/__init__.py", line 626, in trigger_incident
    client=client, client_url=client_url, contexts=contexts)
  File "/home/mgrandi/.pyenv/versions/rabbitmq_monitor_venv/lib/python3.5/site-packages/pygerduty/__init__.py", line 588, in create_event
    data=_json_dumper(data).encode('utf-8'),
  File "/home/mgrandi/.pyenv/versions/3.5.2/lib/python3.5/json/__init__.py", line 237, in dumps
    **kw).encode(obj)
  File "/home/mgrandi/.pyenv/versions/3.5.2/lib/python3.5/json/encoder.py", line 198, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/home/mgrandi/.pyenv/versions/3.5.2/lib/python3.5/json/encoder.py", line 256, in iterencode
    return _iterencode(o, 0)
  File "/home/mgrandi/.pyenv/versions/rabbitmq_monitor_venv/lib/python3.5/site-packages/pygerduty/__init__.py", line 747, in _datetime_encoder
    return json.JSONEncoder.default(obj)
TypeError: default() missing 1 required positional argument: 'o'

While I realize that the error is because i'm doing something wrong, there seems to be a bug where the json.JSONEncoder.default(obj) call is not using the right arguments. The exception should be (for example)

>>> import uuid, json
>>> json.dumps({"lol": uuid.uuid4()})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/json/__init__.py", line 230, in dumps
    return _default_encoder.encode(obj)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/json/encoder.py", line 198, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/json/encoder.py", line 256, in iterencode
    return _iterencode(o, 0)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/json/encoder.py", line 179, in default
    raise TypeError(repr(o) + " is not JSON serializable")
TypeError: UUID('9c5c7b30-def2-4497-ba84-f49b409b072c') is not JSON serializable
>>>

It seems the bug is that since you are calling json.JSONEncoder.default, but you you aren't directly subclassing JSONEncoder, you need to pass 'self' in as well as 'o'

https://docs.python.org/3.5/library/json.html#json.JSONEncoder.default

Improve and/or document ability to create mock Incident objects

Howdy!

Thanks for sharing this! I'm trying to write some unit test coverage (which is running in CI, so I'm mocking out the calls to pagerduty) but am having a rough time figuring out how to generate a mocked out Incident object. For a list of Incidents, I was trying something along the lines of:

 Incidents(Incident(html_url='pagerduty.com/theincident', incident_key='this was a page'))

Since it looks like when you request a list of incidents they are wrapped in an Incidents (aka a Collection). I'm curious if the community already has a pattern for doing this, but if not I'll come back around in a few weeks with a path- either a test fixture, example, or something depending on what everyone prefers.

Thanks!
Joe

Python3 issues

Not-necessarily-exhaustive list of Python 3 issues:

  • urllib2 module is gone - is replaced by urllib.request and urllib.error
  • urlparse module is renamed to urllib.parse
  • dict.iteritems is gone, replaced by items. This exists in python 2, but with nonlazy semantics, not a breaking change to switch.
  • % syntax for formatting strings is gone. Replace '... %s ...' % x with '... {} ...'.format(x) - Unfortunately this was added in 2.6, so this is a breaking change for 2.5
  • Relative imports must be explicit. from version import ... changes to from .version import ...

Iterator failures in py3.8 with RuntimeError

Python 3.8.0 (default, Oct 28 2019, 16:14:01)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pygerduty.v2
>>> p = pygerduty.v2.PagerDuty(<api key>)
>>> for sched in p.schedules.list():
...   print(sched)
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/amckenna/code/venv3/lib/python3.8/site-packages/pygerduty/v2.py", line 159, in list
    this_paginated_result = self._list_no_pagination(**these_kwargs)
  File "/home/amckenna/code/venv3/lib/python3.8/site-packages/pygerduty/v2.py", line 137, in _list_no_pagination
    response = self.pagerduty.request("GET", path, query_params=kwargs)
  File "/home/amckenna/code/venv3/lib/python3.8/site-packages/pygerduty/v2.py", line 673, in request
    return self.requester.execute_request(request)
  File "/home/amckenna/code/venv3/lib/python3.8/site-packages/pygerduty/common.py", line 50, in execute_request
    response = clean_response(response)
  File "/home/amckenna/code/venv3/lib/python3.8/site-packages/pygerduty/common.py", line 68, in clean_response
    clean_response(response[key])
  File "/home/amckenna/code/venv3/lib/python3.8/site-packages/pygerduty/common.py", line 60, in clean_response
    clean_response(elem)
  File "/home/amckenna/code/venv3/lib/python3.8/site-packages/pygerduty/common.py", line 62, in clean_response
    for key, val in response.items():
RuntimeError: dictionary keys changed during iteration

Issue Parsing Results Returned from PagerDuty

With pygerduty 0.35.2 / python 2.7.10, when I request schedules, v1 api's return valid results but list or str calls to results end up with following error:

pager = pygerduty.PagerDuty(####,####)
schedules = pager.schedules.show(####)
 File "/Users/tarik/Desktop/devs/lib/python2.7/site-packages/pygerduty/__init__.py", line 176, in show
    return self.container(self, **response.get(self.sname, {}))
TypeError: __init__() got multiple values for keyword argument 'self'

Could it be PagerDuty changed api responses not in a backward-compatible way? Thx

Need Events API v2 Support

Need to support Events API v2 (not to be confused with REST API v2). New Events API comes with new Common Event Format payload requirements (PD-CEF). Need to create a Events() implementation to handle the new requirements.

README should also be updated for reference to the PD-CEF & Events API v2 documentation.

Bad URL: /incidents/{ID}/log_entries/count

For tracking purposes.

The v2 REST API no longer officially supports the /count path. Although empirical evidence would suggest it still exists for /incidents/count, there is no documentation supporting this, and plenty of support conversations suggesting otherwise. This is especially true for sub-container elements such as Incident.log_entries

To be v2 compliant, all count() calls should be changed to use the documented total=true parameter on a single paginated request. The use of limit=1 should theoretically allow for the smallest possible request, though some testing on older incidents seems to suggest that this must be limit=2 or higher to avoid HTTP errors.

Escalation Policies not loading Escalation Rules

I can fetch the escalation Policy correctly however it does not load the rules properly.

u'escalation_rules': <pygerduty.EscalationRulesobjectat0x10b7f21d0>

I try to loop on it and get the following:

Traceback (most recent call last):
  File "./verify.py", line 30, in <module>
    for rule in escalation_policy.escalation_rules:
TypeError: 'EscalationRules' object is not iterable

If I try and print the rules I get back the main escalation policy rather than the rules

print escalation_policy.escalation_rules.__dict__

Manually fetching the escalation policy via curl returns all the information:

TOKEN="YOUR-TOKEN-HERE"
COMPANY="YOUR-COMPANY-NAME-HERE"
curl -H "Content-type: application/json" -H "Authorization: Token token=${TOKEN}" -X GET -G \
    --data-urlencode "include[]=teams" \
    --data-urlencode "query=API Escalation" \
    "https://${COMPANY}.pagerduty.com/api/v1/escalation_policies"

So from what I can tell its having issues converting the rules.

Running v0.29.0

response object can sometimes not have error field

It looks like sometimes the entire error field can be missing from pagerduty's response.

Traceback (most recent call last):
  File "/srv/www/myapp/releases/20131126024533/pager_on_error.py", line 17, in pager_on_failure
    pager.page(service_key, description, incident_key=incident_key, details=details)
  File "/var/www/myapp/env/local/lib/python2.7/site-packages/pygerduty.py", line 479, in trigger_incident
    details, incident_key)
  File "/var/www/myapp/env/local/lib/python2.7/site-packages/pygerduty.py", line 445, in create_event
    response = self.execute_request(request)
  File "/var/www/myapp/env/local/lib/python2.7/site-packages/pygerduty.py", line 488, in execute_request
    raise BadRequest(json.loads(err.read()))
  File "/var/www/myapp/env/local/lib/python2.7/site-packages/pygerduty.py", line 37, in __init__
    self.code = payload["error"].get('code', 99999)
KeyError: 'error'

I have this happen every once in a while.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.