Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
lozzd committed Aug 22, 2011
0 parents commit 8b642a1
Show file tree
Hide file tree
Showing 4 changed files with 295 additions and 0 deletions.
24 changes: 24 additions & 0 deletions README
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Pagerduty PHP

- A PHP library of handy functions utilising the Pagerduty API.


* What's included?

pagerduty.php - The file which has all the functions that can be used. I say all, so far I only have two, and both relate to getting the current person on call.
pagerdutycron.php - A script designed to run from cron that will either ping irccat or send an email (or both) when the person on call in your rotations changes.
timeago.php - A support function provided by alex at nyoc dot net.

* How do I use it?

You need to enter some basic configuration information into pagerduty.php before you can use it, relating to your login and your schedules.

After you've done this, you are free to include pagerduty.php and use the functions in the file. Keep reading for info on other files.


* pagerdutycron.php

Setup is fairly simple; there are a few configuration options at the top of the file relating to whether you want to send an email, or send a message to irccat, or both, and where to send those messages.
After you've done that, run it by hand once to ensure everything works as planned, and then set it up in cron to run as often as you please (for example, 1 minute).

When it detects a change in rotation, it will perform your selected action to alert the relevant parties.
74 changes: 74 additions & 0 deletions pagerduty.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

# Feel free to tweak this to your preferred timezone or let your OS take care of it
date_default_timezone_set('America/New_York');
include_once('timeago.php');

# The base URL... Everyone has their own subdomain, set yours here.
$baseurl = "https://yourcompany.pagerduty.com/api/v1/schedules/";

# Each schedule has its own ID. Get yours by visting the Pagerduty website.
# When you look at your schedule, the URL will have something like:
# http://yourcompany.pagerduty.com/schedule/rotations/AB1ABCD
# The last part of this URL is the schedule ID.
# Add it in this array with a reasonable name to identify it.
$schedules = array ('ops' => 'AB1ABCD', 'dev' => 'CD1CDEFG');

# Your Pagerduty username is required for basic auth for access to the API.
# I suggest creating a new account just for this.
$username = "automationuser@yourcompany.com";
$password = "yourpassword";



function whoIsOnCall($schedule, $time = "") {
global $baseurl, $schedules, $username, $password;
if($time == "") $time = time();

if(!$scheduleid = array_key_exists($schedule, $schedules)) {
echo "Cannot find that schedule. Define in pagerduty.php";
die();
}

$context = stream_context_create(array(
'http' => array(
'header' => "Authorization: Basic " . base64_encode("$username:$password")
)
));

$time = date("c", $time);
$json = file_get_contents($baseurl . $schedules[$schedule] . "/entries?since={$time}&until={$time}&overflow=true", false, $context);

if (false == ($scheddata = json_decode($json))) {
echo "There was an error with the data from PagerDuty, please try again later.";
return false;
}

if ($scheddata->entries['0']->user->name == "") {
echo "No data from Pagerduty for that date, sorry.";
return false;
}

$oncalldetails = array();
$oncalldetails['person'] = $scheddata->entries['0']->user->name;
$oncalldetails['start'] = strtotime($scheddata->entries['0']->start);
$oncalldetails['end'] = strtotime($scheddata->entries['0']->end);

return $oncalldetails;

}

function printPersonOnCallNow($team) {
if($results = whoIsOnCall($team)) {
echo $results['person'] . " is on call until " . date("d-m-Y H:i:s", $results['end']);
}
}

function printPersonOnCallAt($team, $time) {
$time = strtotime($time);
if($results = whoIsOnCall($team, $time)) {
echo $results['person'] . " is on call from " . timeago($results['start']) . " until " . timeago($results['end']);
}
}

?>
151 changes: 151 additions & 0 deletions pagerdutycron.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
#!/usr/bin/php
<?

# A script designed to be run from cron at an interval (e.g. 1 minute) and then alerts you when changes
# to the on call rotation happen. For example, be emailed when a new person comes on call.

# Include the libs
include('pagerduty.php');
include_once('timeago.php');

if (!isset($argv[1])) {
echo "Must be run with an argument for which schedule you want to monitor, e.g. pagerdutycron.php ops\n";
die();
}


$schedule = $argv[1];

# CONFIG
#
# ** Remember to set up the base options in pagerduty.php **
#
# Choose your options here:
#
# $emailalert = This controls whether to send an email on change in the on call schedule
$emailalert = true;

# $emailrecipient = An array mapping of schedule names to email addresses. Leave blank to not email this schedule
$emailrecipient = array('ops' => "ops@yourcompany.com", 'dev' => "");

# $irccatalert = This controls whether to send a message to IRC on change in the on call schedule
$irccatalert = false;

# $ircchannel = An array mapping of schedules to irc channels. Leave blank to not irccat announce this schedule
$ircchannel = array('ops' => '#ops', 'dev' => '#dev');

# $irccathost = Hostname of the irccat server
$irccathost = "127.0.0.1";

# $schedulefile = Where you want to store the temporary state information for the schedule. Must be writeable!
$schedulefile = "/tmp/pagerduty-{$schedule}";


# END CONFIG
#
# Edit things under here for additional control.
#

logline("Starting run to check Pagerduty schedule {$schedule} for changes");

if(!is_readable($schedulefile)) { # If the schedule file isn't readable, it probably never existed. Populate it.
logline("Schedule file is unreadable... Attempting to initial populate");
if (!is_writable($schedulefile)) {
if($results = whoIsOnCall($schedule)) {
$fh = fopen($schedulefile, "w");
fwrite($fh, $results['person']);
logline("Wrote initial data to schedule file. Person on call is currently {$results['person']}");
fclose($fh);
} else {
logline("Pagerduty call failed; I cannot proceed. Try again later.");
die();
}
} else {
logline("Your schedule file is unwriteable. Please choose a different path.");
die();
}
}

unset($results);
logline("Checking schedule for updates...");

# Basic checking of the file... Don't want any errors.
if (!is_readable($schedulefile)) {
logline("Schedule file is no longer readable, bailing.");
die();
}
if (!is_writable($schedulefile)) {
logline("Schedule file is not writeable, bailing. ");
die();
}

if($results = whoIsOnCall($schedule)) {
logline("Person on call is now {$results['person']}");
$fh = fopen($schedulefile, "r");
$lastoncall = fgets($fh);
fclose($fh);
if ($lastoncall == $results['person']) { # No change in on call rotation
logline("Person on call last == person on call now ({$lastoncall} == {$results['person']}), nothing to do");
} else { # A change has occured! Alert the relevant places and write the new person to our temp file
logline("Person on call has changed! Was {$lastoncall} is now {$results['person']}.");
if ($irccatalert) {
irccat("On call person for {$schedule} is now {$results['person']} until " . timeago($results['end']));
irccat("Thanks to {$lastoncall}, you are now free. Remember to tip your on call guy/gal!");
}

if ($emailalert) {
$emailmessage = "Hi! \n\nThis is an automated email to let you know that the on call person for schedule {$schedule} has changed.\n\n";
$emailmessage .= "The on call person for {$schedule} is now {$results['person']} until " . date("r", $results['end']) . " (" . timeago($results['end']) . ")\n\n";
$emailmessage .= "Thanks to {$lastoncall} who is now relieved from duty. \n\n";
$emailmessage .= "Thank you!\nPagerduty email bot";
email($emailmessage);
}
logline("Writing new on call person to log file...");
$fh = fopen($schedulefile, "w");
fwrite($fh, $results['person']);
fclose($fh);
}
} else {
logline("Pagerduty call failed, trying again later.");
}
logline("Run completed");




function logline($message) {
echo date(DATE_RFC822) . " " . $message . "\n";
}

function irccat($message) {
global $ircchannel, $schedule, $irccathost;
if ($ircchannel[$schedule] == "") {
logline("Bailing on IRC, no channel set for this schedule");
return false;
}
echo "IRC MESSAGE: {$message} \n";
$irccat = fsockopen($irccathost, 12345);
if ($irccat) {
fwrite($irccat, $ircchannel[$schedule] . " " . $message);
fclose($irccat);
}
}

function email($message) {
global $emailrecipient, $schedule;
if ($emailrecipient[$schedule] == "") {
logline("Bailing on sending email, no recipient set for this schedule");
return false;
}
$emailsubject = "New on-call person for {$schedule}";
if (mail($emailrecipient[$schedule], $emailsubject, $message)) {
logline("Email sent successfully");
return true;
} else {
logline("Email sending failed");
return false;
}

}

?>
46 changes: 46 additions & 0 deletions timeago.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php
## alex at nyoc dot net
## Feel free to better for your needs

function timeago($referencedate=0, $timepointer='', $measureby='', $autotext=true){ ## Measureby can be: s, m, h, d, or y
if($timepointer == '') $timepointer = time();
$Raw = $timepointer-$referencedate; ## Raw time difference
$Clean = abs($Raw);
$calcNum = array(array('s', 60), array('m', 60*60), array('h', 60*60*60), array('d', 60*60*60*24), array('mo', 60*60*60*24*30)); ## Used for calculating
$calc = array('s' => array(1, 'second'), 'm' => array(60, 'minute'), 'h' => array(60*60, 'hour'), 'd' => array(60*60*24, 'day'), 'mo' => array(60*60*24*30, 'month')); ## Used for units and determining actual differences per unit (there probably is a more efficient way to do this)


if($measureby == ''){ ## Only use if nothing is referenced in the function parameters
$usemeasure = 's'; ## Default unit

for($i=0; $i<count($calcNum); $i++){ ## Loop through calcNum until we find a low enough unit
if($Clean <= $calcNum[$i][1]){ ## Checks to see if the Raw is less than the unit, uses calcNum b/c system is based on seconds being 60
$usemeasure = $calcNum[$i][0]; ## The if statement okayed the proposed unit, we will use this friendly key to output the time left
$i = count($calcNum); ## Skip all other units by maxing out the current loop position
}
}
}else{
$usemeasure = $measureby; ## Used if a unit is provided
}

$datedifference = floor($Clean/$calc[$usemeasure][0]); ## Rounded date difference

if($autotext==true && ($timepointer==time())){
if($Raw < 0){
$prospect = 'from now';
}else{
$prospect = 'ago';
}
}

if($referencedate != 0){ ## Check to make sure a date in the past was supplied
if($datedifference == 1){ ## Checks for grammar (plural/singular)
return $datedifference . ' ' . $calc[$usemeasure][1] . ' ' . $prospect;
}else{
return $datedifference . ' ' . $calc[$usemeasure][1] . 's ' . $prospect;
}
}else{
return 'No input time referenced.';
}
}
?>

0 comments on commit 8b642a1

Please sign in to comment.