Integrating PagerDuty with OTRS

Moderator: crythias

Locked
seberget2
Znuny newbie
Posts: 74
Joined: 17 May 2013, 09:30
Znuny Version: 6.0.30
Real Name: Stein Erik Berget

Integrating PagerDuty with OTRS

Post by seberget2 »

Hi there OTRS Community!
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:
  1. pagerdutystate to hold the PagerDuty state of type incident.resolve
    1. Name: pagerdutystate
    2. Label: PagerDuty State
    3. Field Order: (What you like it to be)
    4. Field Type: Text
    5. ObjectType: Ticket
    6. Default value: (blank)
    7. Show Link:

      Code: Select all

      http://<YOUR-IDENTIFYER>.pagerduty.com/incidents/$LQData{"DynamicField_pagerdutyid"}
      This makes a link that you can click on in OTRS and go directly to the PagerDuty incident
  2. pagerdutyid to hold the PagerDuty ID
    1. Name: pagerdutyid
    2. Label: PagerDuty Incident ID
    3. Field Order: (What you like it to be)
    4. Field Type: Text
    5. ObjectType: Ticket
    6. Default value: (blank)
    7. Show Link: (blank)
Next we would like to show the PagerDuty State somewhere. That can be configured using SysConfig in the OTRS admin interface. We would like to add the PagerDuty State on one different place.

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
Here you add one entry for pagerdutystate. The name of the field as key, and 1 as the content. The sort order are set by the Field order you set in the configuration of the Dynamic Fields.

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");
    }
  }
}
In PagerDuty you set up the webhook as described in their documentation which you can find on their site.

Next is to send alerts into their system. That I've set up by using Notifications (Event)
Notifications (Event).png
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
Event based execution.png
This way we can change our mind, without touching the Webhook code.
You do not have the required permissions to view the files attached to this post.
OTRS 6.0.12 on Ubuntu with MySQL DB, and various plug-ins and a hack or two :-D
Locked