We use OTRS for our Service Desk, and we are now using PagerDuty for Incident alerts. As it says on their web page Alert the right person, every time. Naturally we would like to integrate those two systems in a good way. Typically we would like to have a field or two in OTRS. So here is how we have integrated the two systems:
- pagerdutystate to hold the PagerDuty state of type incident.resolve
- Name: pagerdutystate
- Label: PagerDuty State
- Field Order: (What you like it to be)
- Field Type: Text
- ObjectType: Ticket
- Default value: (blank)
- Show Link: This makes a link that you can click on in OTRS and go directly to the PagerDuty incident
Code: Select all
http://<YOUR-IDENTIFYER>.pagerduty.com/incidents/$LQData{"DynamicField_pagerdutyid"}
- pagerdutyid to hold the PagerDuty ID
- Name: pagerdutyid
- Label: PagerDuty Incident ID
- Field Order: (What you like it to be)
- Field Type: Text
- ObjectType: Ticket
- Default value: (blank)
- Show Link: (blank)
To show the values of the field in the Ticket Information if they have content. That is done on the following SysConfig category:
Code: Select all
Ticket::Frontend::AgentTicketZoom###DynamicField
When we have finished with configuring PagerDuty you will now see a clickable link to PagerDuty in the Ticket Information window. Next we need to get the state out of PagerDuty for those tickets.
Here I got inspiration from https://github.com/rttag/RTT-JiraWebhook. Basically we are going to use the Web Hook call back feature of PagerDuty http://developer.pagerduty.com/document ... t/webhooks. To use that we need to set up a thing on the OTRS server to handle the POST request from PagerDuty.
The following is the content of the file
Code: Select all
<OTRS-INSTALL-DIR>/bin/cgi-bin/pagerduty.pl
Code: Select all
#!/usr/bin/perl
#
# RTT-JiraWebhook
#
# Copyright (C) 2013-2014 Realtime Technology AG, http://rtt.ag/
#
# Author: Martin Gross <martin.gross@rtt.ag>
# License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, November 2007
#
# THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
# KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
# PARTICULAR PURPOSE.
#
# Amended by: Stein Erik Berget, seberget@esenic.com
#
use strict;
use warnings;
#use ../../ as lib location
use FindBin qw($Bin);
use lib "$Bin/../..";
use lib "$Bin/../../Kernel/cpan -lib";
use lib "$Bin/../../Custom";
#:use Getopt::Long;
use Encode qw(encode);
use Kernel::Config;
use Kernel::System::Encode;
use Kernel::System::Log;
use Kernel::System::Time;
use Kernel::System::DB;
use Kernel::System::Main;
use Kernel::System::Ticket;
#use Data::Dumper;
use CGI;
use JSON::XS;
use POSIX;
# create common objects
my %CommonObject = ();
$CommonObject{ConfigObject} = Kernel::Config->new();
$CommonObject{EncodeObject} = Kernel::System::Encode->new(%CommonObject);
$CommonObject{LogObject} = Kernel::System::Log->new(
LogPrefix => 'PagerDuty Webhook',
%CommonObject,
);
$CommonObject{MainObject} = Kernel::System::Main->new(%CommonObject);
$CommonObject{TimeObject} = Kernel::System::Time->new(%CommonObject);
$CommonObject{DBObject} = Kernel::System::DB->new(%CommonObject);
$CommonObject{TicketObject} = Kernel::System::Ticket->new(%CommonObject);
my $q = new CGI;
my @fields = $q->param();
print $q->header();
my $jsondata;
if (!defined($q->param('POSTDATA'))) {
$CommonObject{LogObject}->Log( Priority => 'error', Message => "No POSTDATA" );
exit('No Postdata!');
}
$CommonObject{LogObject}->Log( Priority => 'info', Message => "We have postdata!" );
my $json_utf8 = encode('UTF-8', $q->param('POSTDATA'));
$jsondata = JSON->new->utf8->decode($json_utf8);
foreach my $messages ($jsondata->{messages} ) {
foreach my $item (@$messages) {
#Which incident webhook message we are dealing with
my $type = $item->{'type'};
#Get the OTRS ID out of subject
my $subject = $item->{'data'}->{'incident'}->{'trigger_summary_data'}->{'subject'};
$subject =~ m/(\d+)/;
my $ticketNumber = $1;
#Search for the given OTRS ticket
my @TicketIDs = $CommonObject{TicketObject}->TicketSearch (
Result => 'ARRAY',
TicketNumber => $ticketNumber,
UserID => '1',
Permission => 'rw',
CacheTTL => '1',
);
my $ticketId = $TicketIDs[0];
my $oldState;
#get the pagerdutystate in the ticket
$oldState = $CommonObject{TicketObject}->{DynamicFieldBackendObject}->ValueGet(
DynamicFieldConfig => $CommonObject{TicketObject}->{DynamicFieldObject}->DynamicFieldGet(Name => "pagerdutystate"),
ObjectID => $ticketId,
);
#Set the pagerdutystate if not changed
if ($type ne $oldState) {
$CommonObject{TicketObject}->{DynamicFieldBackendObject}->ValueSet(
DynamicFieldConfig => $CommonObject{TicketObject}->{DynamicFieldObject}->DynamicFieldGet(Name => "pagerdutystate"),
ObjectID => $ticketId,
Value => $type,
UserID => 1,
);
}
if ($type eq 'incident.trigger') {
$CommonObject{LogObject}->Log( Priority => 'info', Message => "Incident triggered; TicketNumber: $ticketNumber");
#Set PagerDuty Id in OTRS
$CommonObject{TicketObject}->{DynamicFieldBackendObject}->ValueSet(
DynamicFieldConfig => $CommonObject{TicketObject}->{DynamicFieldObject}->DynamicFieldGet(Name => "pagerdutyid"),
ObjectID => $ticketId,
Value => $item->{'data'}->{'incident'}->{'id'},
UserID => 1,
);
my @tmp = split("@", $item->{'data'}->{'incident'}->{'assigned_to_user'}->{'email'}, 2);
my $owner = $tmp[0];
#Assign it to the correct 'Agent' in OTRS
my $success = $CommonObject{TicketObject}->TicketOwnerSet(
TicketID => $ticketId,
NewUser => $owner,
UserID => 1,
);
$CommonObject{LogObject}->Log( Priority => 'info', Message => "New owner set to '$owner', and it was a '$success'");
}
elsif ($type eq 'incident.acknowledge') {
$CommonObject{LogObject}->Log( Priority => 'info', Message => "Incident acknowledged; TicketNumber: $ticketNumber");
my @tmp = split("@", $item->{'data'}->{'incident'}->{'assigned_to_user'}->{'email'}, 2);
my $owner = $tmp[0];
#Assign it to the correct 'Agent' in OTRS
my $success = $CommonObject{TicketObject}->TicketOwnerSet(
TicketID => $ticketId,
NewUser => $owner,
UserID => 1,
);
$CommonObject{LogObject}->Log( Priority => 'info', Message => "New owner set to '$owner', and it was a '$success'");
}
elsif ($type eq 'incident.unacknowledge') {
$CommonObject{LogObject}->Log( Priority => 'info', Message => "Incident unacknowledged; TicketNumber: $ticketNumber");
}
elsif ($type eq 'incident.resolve') {
$CommonObject{LogObject}->Log( Priority => 'info', Message => "Incident resolved; TicketNumber: $ticketNumber");
my @tmp = split("@", $item->{'data'}->{'incident'}->{'resolved_by_user'}->{'email'}, 2);
my $owner = $tmp[0];
#Assign it to the correct 'Agent' in OTRS
my $success = $CommonObject{TicketObject}->TicketOwnerSet(
TicketID => $ticketId,
NewUser => $owner,
UserID => 1,
);
$CommonObject{LogObject}->Log( Priority => 'info', Message => "New owner set to '$owner', and it was a '$success'");
}
elsif ($type eq 'incident.assign') {
$CommonObject{LogObject}->Log( Priority => 'info', Message => "Incident assigned; TicketNumber: $ticketNumber");
}
elsif ($type eq 'incident.escalate') {
$CommonObject{LogObject}->Log( Priority => 'info', Message => "Incident escalated; TicketNumber: $ticketNumber");
}
elsif ($type eq 'incident.delegate') {
$CommonObject{LogObject}->Log( Priority => 'info', Message => "Incident delegated; TicketNumber: $ticketNumber");
}
else {
$CommonObject{LogObject}->Log( Priority => 'error', Message => "Action for type '$type' undefined; TicketNumber: $ticketNumber");
}
}
}
Next is to send alerts into their system. That I've set up by using Notifications (Event) as you can see in the image.
Select whatever trigger you would like, but the important area is to actually send an email to your Services email address given by PagerDuty.
The rest of the 'alerting' configuring in OTRS is basically done on GenericAgent Event based execution This way we can change our mind, without touching the Webhook code.