この記事はPagerDuty Advent Calendar 2023の23日目の記事です。
どうも、manji0です。 今年は所属してる会社が合併したり、交際0日婚約 & 出会って2ヶ月で入籍したりと中々激動の一年でした。
今日はPagerDutyのAPIの使い方について、いくつか例を上げたいと思います。PDのAPIを使いこなすための導入記事として読んでいただければ嬉しいです。
PagerDuty API
まずはお手元にPagerDuty APIのドキュメントを用意しておいてください。
Service, Escalation PolicyやScheduleなど、かなり広い対象にAPIが用意されていることが見てとれると思います。ただ、ほとんどのユーザはEvent API v2(=Incidentを作成するためのAPI)以外のAPIを使ったことがないのではないでしょうか?
個人的には、PDのAPIは少し使い方に癖があるように思えます。やりたいことを実現する方法をすぐに思いつけるようになるのに多少の慣れが必要だな、という感覚です。
例えば、Get a scheduleをQuery param無しでコールしてみます。
ドキュメントのResponse sampleでは以下のように final_schedule
が返ってきますね。
{
"schedule": {
"id": "PI7DH85",
"type": "schedule",
"省略": "",
"final_schedule": {
"name": "Final Schedule",
"rendered_schedule_entries": [
{
"start": "2015-11-10T08:00:00-05:00",
"end": "2015-11-10T17:00:00-05:00",
"user": {
"id": "PXPGF42",
"type": "user_reference",
"summary": "Regina Phalange",
"self": "https://api.pagerduty.com/users/PXPGF42",
"html_url": "https://subdomain.pagerduty.com/users/PXPGF42"
}
}
],
"rendered_coverage_percentage": 37.5
}
}
}
ところが実際に叩いてみると、final_scheduleは空だったりします。
"overrides_subschedule": {
"name": "Overrides",
"rendered_schedule_entries": [],
"rendered_coverage_percentage": null
},
"final_schedule": {
"name": "Final Schedule",
"rendered_schedule_entries": [],
"rendered_coverage_percentage": null
},
これが何故か分かる方は、正直この記事を読まなくてもOKです。
そうでない方がPDのAPIを便利に使えるようになるために、「あると少し便利だな」というユースケースを例に挙げ、仕様を交えながらAPIの呼び出しフローを説明していきます。
ユースケース
任意のEscalation Policyから現在の担当者のリストを取得する
用途: Slackにその週の担当者を通知する、など
まとめ:
- Get an escalation policyを使って
escalation_rules.0.targets
を取得 - targetsにtype:schedule_referenceが含まれる場合はGet a scheduleを使って
final_schedule
を取得し、担当者を特定する
目的に沿ってそうな名前のAPIはGet an escalation policyですね。response exampleを見てみましょう。
{
"escalation_policy": {
"id": "PT20YPA",
"type": "escalation_policy",
"summary": "Another Escalation Policy",
"on_call_handoff_notifications": "if_has_services",
"self": "https://api.pagerduty.com/escalation_policies/PT20YPA",
"html_url": "https://subdomain.pagerduty.com/escalation_policies/PT20YPA",
"name": "Another Escalation Policy",
"escalation_rules": [
{
"id": "PGHDV41",
"escalation_delay_in_minutes": 30,
"targets": [
{
"id": "PAM4FGS",
"summary": "Kyler Kuhn",
"type": "user_reference",
"self": "https://api.pagerduty.com/users/PAM4FGS",
"html_url": "https://subdomain.pagerduty.com/users/PAM4FGS"
},
{
"id": "PI7DH85",
"summary": "Daily Engineering Rotation",
"type": "schedule_reference",
"self": "https://api.pagerduty.com/schedules/PI7DH85",
"html_url": "https://subdomain.pagerduty.com/schedules/PI7DH85"
}
]
}
],
"services": [
{
"id": "PIJ90N7",
"type": "service_reference",
"summary": "My Mail Service",
"self": "https://api.pagerduty.com/services/PIJ90N7",
"html_url": "https://subdomain.pagerduty.com/service-directory/PIJ90N7"
}
],
"num_loops": 2,
"teams": [
{
"id": "PQ9K7I8",
"type": "team_reference",
"summary": "Engineering",
"self": "https://api.pagerduty.com/teams/PQ9K7I8",
"html_url": "https://subdomain.pagerduty.com/teams/PQ9K7I8"
}
],
"description": "This is yet another escalation policy"
}
}
escalation_rules
が目的の情報のようですが、このままでは使えませんね。Kylerさんが担当者なのは判明していますが...
{
"id": "PI7DH85",
"summary": "Daily Engineering Rotation",
"type": "schedule_reference",
"self": "https://api.pagerduty.com/schedules/PI7DH85",
"html_url": "https://subdomain.pagerduty.com/schedules/PI7DH85"
}
このAPIから得られる情報では、sheduleの担当者は明らかになっていません。
Scheduleで定義された担当者を含めた完全なリストを生成するには、Scheduleの情報も取得する必要があります。
Get a scheduleについて
Get a scheduleを使ってみましょう。担当者はfinal_schedule
に記載されるはずです。
...しかし、(恐らく)ほとんどの方はここが空になって返ってくるのではないでしょうか。僕の試した限り、final_schedule
を得るためにはsince
かuntil
を指定する必要があります。
そして、ドキュメントにある通りsinceかuntilの片方だけを指定した場合にはそこを起点とした2週間分の情報が出力されます。
例えば、1週間で担当者が切り替わるスケジュールである場合は最大3週分の情報が出力されます。
curl -H "Authorization: Token token=xxx" https://api.pagerduty.com/schedules/yyy\?since\=2023-12-23T00:00:00 | jq
# 省略
"final_schedule": {
"name": "Final Schedule",
"rendered_schedule_entries": [
{
"start": "2023-12-23T00:00:00+09:00",
"end": "2023-12-29T20:00:00+09:00",
"user": {
"id": "YYY",
"type": "user_reference",
"summary": "Alice Carrol",
"self": "https://api.pagerduty.com/users/YYY",
"html_url": "https://verda.pagerduty.com/users/YYY"
},
"id": "Q114LVW901RIEH"
},
{
"start": "2023-12-29T20:00:00+09:00",
"end": "2024-01-05T20:00:00+09:00",
"user": {
"id": "XXX",
"type": "user_reference",
"summary": "Bob Iris",
"self": "https://api.pagerduty.com/users/XXX",
"html_url": "https://verda.pagerduty.com/users/XXX"
},
"id": "Q0BY6F774SDSHE"
},
{
"start": "2024-01-05T20:00:00+09:00",
"end": "2024-01-06T00:00:00+09:00",
"user": {
"id": "ZZZ",
"type": "user_reference",
"summary": "Adam Smith",
"self": "https://api.pagerduty.com/users/ZZZ",
"html_url": "https://verda.pagerduty.com/users/ZZZ"
},
"id": "HOGEHOGE"
}
],
"rendered_coverage_percentage": 100.0
}
こうして何個も出力されてしまったら、「現在の日付とrendered_schedule_entriesのそれぞれの要素の.startや.endを比較して適切なユーザを抽出する」みたいなとても面倒な手順が必要になってしまいます。
これを避けるには、since
とuntil
に同じように現在のtimestampを指定してください。
curl -H "Authorization: Token token=xxx" https://api.pagerduty.com/schedules/yyy\?since\=2023-12-23T00:00:00\&until\=2023-12-23T00:00:00 | jq
"final_schedule": {
"name": "Final Schedule",
"rendered_schedule_entries": [
{
"start": "2023-12-22T20:00:00+09:00",
"end": "2023-12-29T20:00:00+09:00",
"user": {
"id": "YYY",
"type": "user_reference",
"summary": "Alice Carrol",
"self": "https://api.pagerduty.com/users/YYY",
"html_url": "https://verda.pagerduty.com/users/YYY"
},
"id": "HOGEHOGE"
}
],
"rendered_coverage_percentage": 100.0
}
これで担当者が全員明らかになりました。使い方が少し分かってきましたでしょうか。
一定期間内に発生したアラートを全て取得する
用途: インシデントをローカルのpandasなどでより詳細に分析するなど
まとめ:
- List incidentsから指定期間内のIncident一覧を取得
- Incident毎にList alerts for an incidentでAlert一覧を取得してマージ
- (
resolved_at
はAlertではなくIncidentにしか定義されてないので注意)
PagerDutyではIncidentとAlertは別の概念です。具体的には、1つのIncidentには複数のAlertが含まれる場合があります。
通常は1Incident=1Alertですが、上位プランでIncident Alert Groupingを使っていたり、PDのEvent API v2を叩くモジュール(Alertmanagerなど)でdedup keyを制御したりしていると、この関係が崩れることがあります。
(ちなみに、Event API v2でPOSTしたときに作成されるのは(僕の認識が正しければ)Alertです。Incidentが作成されるか、既存のIncidentにマージされるかはPOST後に判断されます。)
ということで、手元のシステムから発行されるアラートなどについて件数などを正確に分析したい場合は、すべてのIncidentからAlertの一覧を抽出する必要があります。
List incidentsから仕様を確認していきましょう。
- Query parameterにはGet a scheduleと同じく
since
とuntil
があるので、期間はここで指定します。 - 対象のサービスを絞りたい場合には、
services
にサービスIDのリストを指定します。チーム管理をきっちりしてる場合にはteams
で絞ってもよいです。 -
more:true
の場合にはoffset
を使って追加でincident listを取得しましょう。
Response Exampleを見る限り、incidents[].id
以外の情報は無視していいですね。
次に、List alerts for an incidentを使ってAlertのリストを取得します。最後にそれをマージしていけばAlertのリストの完成です。
さて、List alerts for an incidentやGet an alertのResponse Exampleを見てみると、created_at
はありますがresolved_at
がないことが分かります。
実は、PagerDutyではresolved_at
はIncidentにしか定義されていません。まあIncidentがオンコールイベントなので自然ですね。
とはいえ、このように「PDに投げたアラートをすべて管理したい」とか「PDのAnalysis機能以上の分析をしたい」という場合には引っかかりやすい仕様だと思います。このあたり、自分たちの要望は発生次第しっかり担当の営業さんに伝えて実現方法を一緒に考えてもらいましょうね。
結構長くなったのでこの辺で。直前に書いたことにも繋がりますが、一般論として、APIやドキュメントが整備されてるからといって自分たちで何でも考えようとすると後から詰まったりするものです。PagerDutyは契約してたらきちんと担当営業さんがついてくれるので、逐一相談してより快適なオンコール生活を送りましょう。 Merry Xmas and Have a happy new year!
余談: 実はIncidentのリストはWebUIからならCSVで落とせたりします。
分析のためにPandasにかけるならCSVで落とした方が圧倒的に楽なので、このAPIを開放してください! クリスマスプレゼントってことで!