[PATCH] Zabbix acknowledgement from OTRS

Александр Ефимов <[email protected]>
Newsgroups gmane.comp.otrs.devel
Organization Neoline LLC
Message-ID <[email protected]>
Hi All!

In our organization we use Zabbix as a monitoring solution for our
servers and network hardware. So, I wrote a small addon to the
SystemMonitoring module, which adds Zabbix acknowledgement functions to
this module.

Work sequence in general:
1. OTRS receives e-mail with text like "Event: 1234567".
2. PostMaster filter SystemMonitoring gets EventID by regexp and writes
it into TicketFreeText field.
3. Ticket Event stack calls ZabbixAcknowledge module.
4. ZabbixAcknowledge module fetches EventID from the ticket and sends
JSON request to Zabbix.

The attachments contain changed files. What was added:
1. In Kernel/System/PostMaster/Filter/SystemMonitoring.pm: additional
TicketFreeField (#3 by default) for storing Zabbix EventID.
2. Added Kernel/System/Ticket/Event/ZabbixAcknowledge.pm - filter
module, which sends acknowledgement to Zabbix server when TicketFreeText
with key='Event' and value=ZabbixEventID is added to the ticket.
3. In Kernel/Config/Files/SystemMonitoring.xml: options for the
ZabbixAcknowledge module.

Options description:
1. PostMaster::PreFilterModule###1-SystemMonitoring
Added fields:
 - FreeTextEvent - FreeText number for EventID
 - EventRegExp - RegExp to find EventID in the mail.
2. Ticket::EventModulePost###930-NagiosAcknowledge
Adds NagiosAcknowledge module to the EventModule stack for the Ticket.
3. Ticket::EventModulePost###931-ZabbixAcknowledge
Adds ZabbixAcknowledge module to the EventModule stack for the Ticket.
4. Zabbix::Acknowledge::Enable - self-explainory :)
5. Zabbix::Acknowledge::FreeField::Event - TicketFreeField for EventID.
6. Zabbix::Acknowledge::HTTP::URL, Zabbix::Acknowledge::HTTP::User,
Zabbix::Acknowledge::HTTP::Password - Zabbix JSON API URL, user name and
password.
7. Zabbix::Acknowledge::Message - message to post in Zabbix when
acknowledging. See NagiosAcknowledge, format is the same.

Zabbix configuration:
1. Add group with API access rights and GUI access set to "Internal".
2. Grant permissions to all the objects, events from which you want to
acknowledge.
3. Create a user and add it to the group. Set user's e-mail to one of
the OTRS mail addresses.
4. Configure Zabbix::Acknowledge::HTTP::User,
Zabbix::Acknowledge::HTTP::Password corresponding to this users parameters.
5. In Zabbix in Configuration -> Actions add new action to send mail
event reports to the user you created with text like:
Trigger: {TRIGGER.NAME}
Host: {HOSTNAME}
Service: {TRIGGER.KEY}
Severity: {TRIGGER.NSEVERITY} ({TRIGGER.SEVERITY})
State: {TRIGGER.STATUS}
Event: {EVENT.ID}
Last value: {ITEM.LASTVALUE}
URL:
http://zabbix.example.com/zabbix/tr_events.php?triggerid={TRIGGER.ID}&eventid={EVENT.ID}
6. Set "Restore message" with the same text.
7/ Save config and enjoy :)

_______________________________________________
OTRS mailing list: dev - Webpage: http://otrs.org/
Archive: http://lists.otrs.org/pipermail/dev
To unsubscribe: http://lists.otrs.org/cgi-bin/listinfo/dev
SystemMonitoring.pm (application/x-perl, 14.1 KB)
# --
# Kernel/System/PostMaster/Filter/SystemMonitoring.pm - Basic System Monitoring Interface
# Copyright (C) 2001-2010 OTRS AG, http://otrs.org/
# --
# $Id: SystemMonitoring.pm,v 1.10 2010/02/20 00:58:05 ub Exp $
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (AGPL). If you
# did not receive this file, see http://www.gnu.org/licenses/agpl.txt.
# --

package Kernel::System::PostMaster::Filter::SystemMonitoring;

use strict;
use warnings;

use Kernel::System::LinkObject;

use vars qw($VERSION);
$VERSION = qw($Revision: 1.10 $) [1];

sub new {
    my ( $Type, %Param ) = @_;

    # allocate new hash for object
    my $Self = {};
    bless( $Self, $Type );

    $Self->{Debug} = $Param{Debug} || 0;

    # get needed objects
    for my $Object (
        qw(DBObject ConfigObject LogObject MainObject EncodeObject TicketObject TimeObject)
        )
    {
        $Self->{$Object} = $Param{$Object} || die "Got no $Object!";
    }

    # create additional objects
    $Self->{LinkObject} = Kernel::System::LinkObject->new( %{$Self} );

    # check if CI incident state should be set automatically
    # this requires the ITSMConfigurationManagement module to be installed
    if ( $Self->{ConfigObject}->Get('SystemMonitoring::SetIncidentState') ) {

        # require the general catalog module
        if ( $Self->{MainObject}->Require('Kernel::System::GeneralCatalog') ) {

            # create general catalog object
            $Self->{GeneralCatalogObject} = Kernel::System::GeneralCatalog->new( %{$Self} );
        }

        # require the config item module
        if ( $Self->{MainObject}->Require('Kernel::System::ITSMConfigItem') ) {

            # create config item object
            $Self->{ConfigItemObject} = Kernel::System::ITSMConfigItem->new( %{$Self} );
        }
    }

    # Default Settings
    $Self->{Config} = {
        StateRegExp       => '\s*State:\s+(\S+)',
        FromAddressRegExp => '[email protected]',
        NewTicketRegExp   => 'CRITICAL|DOWN',
        CloseTicketRegExp => 'OK|UP',
        CloseActionState  => 'closed successful',
        ClosePendingTime  => 60 * 60 * 24 * 2,                          # 2 days
        HostRegExp        => '\s*Address:\s+(\d+\.\d+\.\d+\.\d+)\s*',
        FreeTextHost      => '1',
        FreeTextService   => '2',
        FreeTextState     => '1',
        FreeTextEvent     => '3',
        ServiceRegExp     => '\s*Service:\s+(.*)\s*',
        EventRegExp       => '\s*EventID:\s+(\d*)\s*',
        DefaultService    => 'Host',
        SenderType        => 'system',
        ArticleType       => 'note-report',
    };

    return $Self;
}

sub Run {
    my ( $Self, %Param ) = @_;

    # to store the log message
    my $LogMessage;

    # get config options, use defaults unless value specified
    if ( $Param{JobConfig} && ref $Param{JobConfig} eq 'HASH' ) {
        KEY:
        for my $Key ( keys( %{ $Param{JobConfig} } ) ) {
            next KEY if !$Self->{Config}->{$Key};
            $Self->{Config}->{$Key} = $Param{JobConfig}->{$Key};
        }
    }

    # check if sender is of interest
    return 1 if !$Param{GetParam}->{From};
    return 1 if $Param{GetParam}->{From} !~ /$Self->{Config}->{FromAddressRegExp}/i;

    # Try to get State, Host and Service from email subject
    my @SubjectLines = split /\n/, $Param{GetParam}->{Subject};
    for my $Line (@SubjectLines) {
        for (qw(State Host Service Event)) {
            if ( $Line =~ /$Self->{Config}->{ $_ . 'RegExp' }/ ) {
                $Self->{$_} = $1;
            }
        }
    }

    # Try to get State, Host and Service from email body
    my @BodyLines = split /\n/, $Param{GetParam}->{Body};
    for my $Line (@BodyLines) {
        for (qw(State Host Service Event)) {
            if ( $Line =~ /$Self->{Config}->{ $_ . 'RegExp' }/ ) {
                $Self->{$_} = $1;
            }
        }
    }

    # we need State and Host to proceed
    if ( !$Self->{State} || !$Self->{Host} ) {

        $Self->{LogObject}->Log(
            Priority => 'notice',
            Message  => 'SystemMonitoring Mail: '
                . 'SystemMonitoring: Could not find host address '
                . 'and/or state in mail => Ignoring',
        );
        return 1;
    }

    # Check for Service
    $Self->{Service} ||= $Self->{Config}->{DefaultService};

    # define log message
    $LogMessage = " - "
        . "Host: $Self->{Host}, "
        . "State: $Self->{State}, "
        . "Service: $Self->{Service}, "
        . "Event: $Self->{Event}";

    # Is there a ticket for this Host/Service pair?
    my %Query = (
        Result    => 'ARRAY',
        Limit     => 1,
        UserID    => 1,
        StateType => 'Open',
    );
    for my $Type (qw(Host Service)) {
        $Query{ 'TicketFreeKey' . $Self->{Config}->{ 'FreeText' . $Type } } = $Type;
        $Query{ 'TicketFreeText' . $Self->{Config}->{ 'FreeText' . $Type } }
            = $Self->{$Type};
    }

    # search tickets
    my @TicketIDs = $Self->{TicketObject}->TicketSearch(%Query);

    # get the first and only ticket id
    my $TicketID = shift @TicketIDs;

    # OK, found ticket to deal with
    if ($TicketID) {

        # get ticket number
        my $TicketNumber = $Self->{TicketObject}->TicketNumberLookup(
            TicketID => $TicketID,
            UserID   => 1,
        );

        # build subject
        $Param{GetParam}->{Subject} = $Self->{TicketObject}->TicketSubjectBuild(
            TicketNumber => $TicketNumber,
            Subject      => $Param{GetParam}->{Subject},
        );

        # set sender type and article type
        $Param{GetParam}->{'X-OTRS-FollowUp-SenderType'}  = $Self->{Config}->{SenderType};
        $Param{GetParam}->{'X-OTRS-FollowUp-ArticleType'} = $Self->{Config}->{ArticleType};

        # Set Article Free Field for State
        my $ArticleFreeTextNumber = $Self->{Config}->{'FreeTextState'};
        $Param{GetParam}->{ 'X-OTRS-FollowUp-ArticleKey' . $ArticleFreeTextNumber }
            = 'State';
        $Param{GetParam}->{ 'X-OTRS-FollowUp-ArticleValue' . $ArticleFreeTextNumber }
            = $Self->{State};

        if ( $Self->{State} =~ /$Self->{Config}->{CloseTicketRegExp}/ ) {

            # Close Ticket Condition -> Take Close Action
            if ( $Self->{Config}->{CloseActionState} ne 'OLD' ) {
                $Param{GetParam}->{'X-OTRS-FollowUp-State'} = $Self->{Config}->{CloseActionState};

                my $TimeStamp = $Self->{TimeObject}->SystemTime2TimeStamp(
                    SystemTime => $Self->{TimeObject}->SystemTime()
                        + $Self->{Config}->{ClosePendingTime},
                );
                $Param{GetParam}->{'X-OTRS-State-PendingTime'} = $TimeStamp;
            }

            # set log message
            $LogMessage = 'Recovered' . $LogMessage;

            # if the CI incident state should be set
            if ( $Self->{ConfigObject}->Get('SystemMonitoring::SetIncidentState') ) {

                # set the CI incident state to 'Operational'
                $Self->_SetIncidentState(
                    Name          => $Self->{Host},
                    IncidentState => 'Operational',
                );
            }
        }
        else {

            # Attach note to existing ticket
            $LogMessage = 'New Notice' . $LogMessage;
        }

        # link ticket with CI, this is only possible if the ticket already exists,
        # e.g. in a subsequent email request, because we need a ticket id
        if ( $Self->{ConfigObject}->Get('SystemMonitoring::LinkTicketWithCI') ) {

            # link ticket with CI
            $Self->_LinkTicketWithCI(
                Name     => $Self->{Host},
                TicketID => $TicketID,
            );
        }

    }
    elsif ( $Self->{State} =~ /$Self->{Config}->{NewTicketRegExp}/ ) {

        # Create Ticket Condition -> Create new Ticket and record Host and Service
        for (qw(Host Service Event)) {

            # get the freetext number from config
            my $TicketFreeTextNumber = $Self->{Config}->{ 'FreeText' . $_ };

            $Param{GetParam}->{ 'X-OTRS-TicketKey' . $TicketFreeTextNumber }   = $_;
            $Param{GetParam}->{ 'X-OTRS-TicketValue' . $TicketFreeTextNumber } = $Self->{$_};
        }

        # Set Article Free Field for State
        my $ArticleFreeTextNumber = $Self->{Config}->{'FreeTextState'};
        $Param{GetParam}->{ 'X-OTRS-ArticleKey' . $ArticleFreeTextNumber }   = 'State';
        $Param{GetParam}->{ 'X-OTRS-ArticleValue' . $ArticleFreeTextNumber } = $Self->{State};

        # set sender type and article type
        $Param{GetParam}->{'X-OTRS-SenderType'}  = $Self->{Config}->{SenderType};
        $Param{GetParam}->{'X-OTRS-ArticleType'} = $Self->{Config}->{ArticleType};

        # set log message
        $LogMessage = 'New Ticket' . $LogMessage;

        # if the CI incident state should be set
        if ( $Self->{ConfigObject}->Get('SystemMonitoring::SetIncidentState') ) {

            # set the CI incident state to 'Incident'
            $Self->_SetIncidentState(
                Name          => $Self->{Host},
                IncidentState => 'Incident',
            );
        }
    }
    else {

        # No existing ticket and no open condition -> drop silently
        $Param{GetParam}->{'X-OTRS-Ignore'} = 'yes';
        $LogMessage = 'Mail Dropped, no matching ticket found,'
            . ' no open on this state ' . $LogMessage;
    }

    # logging
    if ($LogMessage) {
        $Self->{LogObject}->Log(
            Priority => 'notice',
            Message  => 'SystemMonitoring Mail: ' . $LogMessage,
        );
    }

    return 1;
}

sub _SetIncidentState {
    my ( $Self, %Param ) = @_;

    # check needed stuff
    for my $Argument (qw(Name IncidentState )) {
        if ( !$Param{$Argument} ) {
            $Self->{LogObject}->Log(
                Priority => 'error',
                Message  => "Need $Argument!",
            );
            return;
        }
    }

    # check configitem object
    return if !$Self->{ConfigItemObject};

    # search configitem
    my $ConfigItemIDs = $Self->{ConfigItemObject}->ConfigItemSearchExtended(
        Name => $Param{Name},
    );

    # if no config item with this name was found
    if ( !$ConfigItemIDs || ref $ConfigItemIDs ne 'ARRAY' || !@{$ConfigItemIDs} ) {

        # log error
        $Self->{LogObject}->Log(
            Priority => 'error',
            Message  => "Could not find any CI with the name '$Param{Name}'. ",
        );
        return;
    }

    # if more than one config item with this name was found
    if ( scalar @{$ConfigItemIDs} > 1 ) {

        # log error
        $Self->{LogObject}->Log(
            Priority => 'error',
            Message  => "Can not set incident state for CI with the name '$Param{Name}'. "
                . "More than one CI with this name was found!",
        );
        return;
    }

    # we only found one config item
    my $ConfigItemID = shift @{$ConfigItemIDs};

    # get config item
    my $ConfigItem = $Self->{ConfigItemObject}->ConfigItemGet(
        ConfigItemID => $ConfigItemID,
    );

    # get latest version data of config item
    my $Version = $Self->{ConfigItemObject}->VersionGet(
        ConfigItemID => $ConfigItemID,
    );

    return if !$Version;
    return if ref $Version ne 'HASH';

    # get incident state list
    my $InciStateList = $Self->{GeneralCatalogObject}->ItemList(
        Class => 'ITSM::Core::IncidentState',
    );

    return if !$InciStateList;
    return if ref $InciStateList ne 'HASH';

    # reverse the incident state list
    my %ReverseInciStateList = reverse %{$InciStateList};

    # check if incident state is valid
    if ( !$ReverseInciStateList{ $Param{IncidentState} } ) {

        # log error
        $Self->{LogObject}->Log(
            Priority => 'error',
            Message  => "Invalid incident state '$Param{IncidentState}'!",
        );
        return;
    }

    # add a new version with the new incident state
    my $VersionID = $Self->{ConfigItemObject}->VersionAdd(
        %{$Version},
        InciStateID => $ReverseInciStateList{ $Param{IncidentState} },
        UserID      => 1,
    );

    return $VersionID;
}

sub _LinkTicketWithCI {
    my ( $Self, %Param ) = @_;

    # check needed stuff
    for my $Argument (qw(Name TicketID )) {
        if ( !$Param{$Argument} ) {
            $Self->{LogObject}->Log(
                Priority => 'error',
                Message  => "Need $Argument!",
            );
            return;
        }
    }

    # check configitem object
    return if !$Self->{ConfigItemObject};

    # search configitem
    my $ConfigItemIDs = $Self->{ConfigItemObject}->ConfigItemSearchExtended(
        Name => $Param{Name},
    );

    # if no config item with this name was found
    if ( !$ConfigItemIDs || ref $ConfigItemIDs ne 'ARRAY' || !@{$ConfigItemIDs} ) {

        # log error
        $Self->{LogObject}->Log(
            Priority => 'error',
            Message  => "Could not find any CI with the name '$Param{Name}'. ",
        );
        return;
    }

    # if more than one config item with this name was found
    if ( scalar @{$ConfigItemIDs} > 1 ) {

        # log error
        $Self->{LogObject}->Log(
            Priority => 'error',
            Message  => "Can not set incident state for CI with the name '$Param{Name}'. "
                . "More than one CI with this name was found!",
        );
        return;
    }

    # we only found one config item
    my $ConfigItemID = shift @{$ConfigItemIDs};

    # link the ticket with the CI
    my $LinkResult = $Self->{LinkObject}->LinkAdd(
        SourceObject => 'Ticket',
        SourceKey    => $Param{TicketID},
        TargetObject => 'ITSMConfigItem',
        TargetKey    => $ConfigItemID,
        Type         => 'RelevantTo',
        State        => 'Valid',
        UserID       => 1,
    );

    return $LinkResult;
}

1;

=back

=head1 TERMS AND CONDITIONS

This software is part of the OTRS project (http://otrs.org/).

This software comes with ABSOLUTELY NO WARRANTY. For details, see
the enclosed file COPYING for license information (AGPL). If you
did not receive this file, see http://www.gnu.org/licenses/agpl.txt.

=cut

=head1 VERSION

$Revision: 1.10 $ $Date: 2010/02/20 00:58:05 $

=cut
SystemMonitoring.xml (text/xml, 12.3 KB)
<?xml version="1.0" encoding="iso-8859-1" ?>
<otrs_config version="1.0" init="Application">
    <CVS>$Id: SystemMonitoring.xml,v 1.18 2010/11/12 12:40:38 mb Exp $</CVS>
    <ConfigItem Name="PostMaster::PreFilterModule###1-SystemMonitoring" Required="0" Valid="1">
        <Description Translatable="1">Basic mail interface to System Monitoring Suites. Use this block if the filter should run AFTER PostMasterFilter.</Description>
        <Group>SystemMonitoring</Group>
        <SubGroup>Core::PostMaster</SubGroup>
        <Setting>
            <Hash>
                <Item Key="Module">Kernel::System::PostMaster::Filter::SystemMonitoring</Item>
                <Item Key="FromAddressRegExp">[email protected]</Item>
                <Item Key="StateRegExp">\s*State:\s+(\S+)</Item>
                <Item Key="HostRegExp">\s*Host:\s+(.*)\s*</Item>
                <Item Key="ServiceRegExp">\s*Service:\s+(.*)\s*</Item>
                <Item Key="NewTicketRegExp">CRITICAL|DOWN</Item>
                <Item Key="CloseTicketRegExp">OK|UP</Item>
                <Item Key="CloseActionState">closed successful</Item>
                <Item Key="ClosePendingTime">172800</Item>
                <Item Key="DefaultService">Host</Item>
                <Item Key="FreeTextHost">1</Item>
                <Item Key="FreeTextService">2</Item>
                <Item Key="SenderType">system</Item>
                <Item Key="ArticleType">note-report</Item>
                <Item Key="FreeTextState">1</Item>
                <Item Key="FreeTextEvent">3</Item>
                <Item Key="EventRegExp">\s*Event:\s+(\d+)\s*</Item>
            </Hash>
        </Setting>
    </ConfigItem>
    <ConfigItem Name="Ticket::EventModulePost###930-NagiosAcknowledge" Required="0" Valid="0">
        <Description Translatable="1">Load Nagios acknowledge module</Description>
        <Group>SystemMonitoring</Group>
        <SubGroup>Core::Ticket</SubGroup>
        <Setting>
            <Hash>
                <Item Key="Module">Kernel::System::Ticket::Event::NagiosAcknowledge</Item>
                <Item Key="Event">TicketFreeTextUpdate</Item>
            </Hash>
        </Setting>
    </ConfigItem>
    <ConfigItem Name="Ticket::EventModulePost###931-ZabbixAcknowledge" Required="0" Valid="0">
        <Description Translatable="1">Load Zabbix acknowledge</Description>
        <Group>SystemMonitoring</Group>
        <SubGroup>Core::Ticket</SubGroup>
        <Setting>
            <Hash>
                <Item Key="Module">Kernel::System::Ticket::Event::ZabbixAcknowledge</Item>
                <Item Key="Event">TicketFreeTextUpdate</Item>
            </Hash>
        </Setting>
    </ConfigItem>
    

    <ConfigItem Name="PostMaster::PreFilterModule###00-SystemMonitoring" Required="0" Valid="0">
        <Description Translatable="1">Basic mail interface to System Monitoring Suites. Use this block if the filter should run BEFORE PostMasterFilter.</Description>
        <Group>SystemMonitoring</Group>
        <SubGroup>Core::PostMaster</SubGroup>
        <Setting>
            <Hash>
                <Item Key="Module">Kernel::System::PostMaster::Filter::SystemMonitoring</Item>
                <Item Key="FromAddressRegExp">[email protected]</Item>
                <Item Key="StateRegExp">\s*State:\s+(\S+)</Item>
                <Item Key="HostRegExp">\s*Host:\s+(.*)\s*</Item>
                <Item Key="ServiceRegExp">\s*Service:\s+(.*)\s*</Item>
                <Item Key="NewTicketRegExp">CRITICAL|DOWN</Item>
                <Item Key="CloseTicketRegExp">OK|UP</Item>
                <Item Key="CloseActionState">closed successful</Item>
                <Item Key="ClosePendingTime">172800</Item>
                <Item Key="DefaultService">Host</Item>
                <Item Key="FreeTextHost">1</Item>
                <Item Key="FreeTextService">2</Item>
                <Item Key="SenderType">system</Item>
                <Item Key="ArticleType">note-report</Item>
                <Item Key="FreeTextState">1</Item>
                <Item Key="FreeTextEvent">3</Item>
                <Item Key="EventRegExp">\s*Event:\s+(\d+)\s*</Item>
            </Hash>
        </Setting>
    </ConfigItem>
    <ConfigItem Name="SystemMonitoring::SetIncidentState" Required="0" Valid="1">
        <Description Translatable="1">Set the incident state of a CI automatically when a system monitoring email arrives.</Description>
        <Group>SystemMonitoring</Group>
        <SubGroup>Core::ConfigItem</SubGroup>
        <Setting>
            <Option SelectedID="0">
                <Item Key="0">No</Item>
                <Item Key="1">Yes</Item>
            </Option>
        </Setting>
    </ConfigItem>
    <ConfigItem Name="SystemMonitoring::LinkTicketWithCI" Required="0" Valid="1">
        <Description Translatable="1">Link an already opened incident ticket with the affected CI. This is only possible when a subsequent system monitoring email arrives.</Description>
        <Group>SystemMonitoring</Group>
        <SubGroup>Core::ConfigItem</SubGroup>
        <Setting>
            <Option SelectedID="0">
                <Item Key="0">No</Item>
                <Item Key="1">Yes</Item>
            </Option>
        </Setting>
    </ConfigItem>
    <ConfigItem Name="Nagios::Acknowledge::FreeField::Host" Required="1" Valid="1">
        <Description Translatable="1">Name of TicketFreeField for Host.</Description>
        <Group>SystemMonitoring</Group>
        <SubGroup>Nagios::Acknowledge</SubGroup>
        <Setting>
            <String Regex="">TicketFreeText1</String>
        </Setting>
    </ConfigItem>
    <ConfigItem Name="Nagios::Acknowledge::FreeField::Service" Required="1" Valid="1">
        <Description Translatable="1">Name of TicketFreeField for Service.</Description>
        <Group>SystemMonitoring</Group>
        <SubGroup>Nagios::Acknowledge</SubGroup>
        <Setting>
            <String Regex="">TicketFreeText2</String>
        </Setting>
    </ConfigItem>
    <ConfigItem Name="Nagios::Acknowledge::Type" Required="0" Valid="1">
        <Description Translatable="1">Define Nagios acknowledge type.</Description>
        <Group>SystemMonitoring</Group>
        <SubGroup>Nagios::Acknowledge</SubGroup>
        <Setting>
            <Option SelectedID="">
                <Item Key="">-</Item>
                <Item Key="pipe">pipe</Item>
                <Item Key="http">http</Item>
            </Option>
        </Setting>
    </ConfigItem>
    <ConfigItem Name="Nagios::Acknowledge::NamedPipe::CMD" Required="0" Valid="1">
        <Description Translatable="1">Named pipe acknowledge command.</Description>
        <Group>SystemMonitoring</Group>
        <SubGroup>Nagios::Acknowledge</SubGroup>
        <Setting>
            <String Regex="">echo '&lt;OUTPUTSTRING&gt;' > /usr/local/nagios/var/rw/nagios.cmd</String>
        </Setting>
    </ConfigItem>
    <ConfigItem Name="Nagios::Acknowledge::NamedPipe::Host" Required="0" Valid="1">
        <Description Translatable="1">Named pipe acknowledge format for host.</Description>
        <Group>SystemMonitoring</Group>
        <SubGroup>Nagios::Acknowledge</SubGroup>
        <Setting>
            <String Regex="">[&lt;UNIXTIME&gt;] ACKNOWLEDGE_HOST_PROBLEM;&lt;HOST_NAME&gt;;1;1;1;&lt;LOGIN&gt;;&lt;a href="&lt;CONFIG_HttpType&gt;://&lt;CONFIG_FQDN&gt;/&lt;CONFIG_ScriptAlias&gt;index.pl?Action=AgentTicketZoom&amp;TicketID=&lt;TicketID&gt;"&gt;&lt;CONFIG_Ticket::Hook&gt;&lt;TicketNumber&gt;&lt;/a&gt;</String>
        </Setting>
    </ConfigItem>
    <ConfigItem Name="Nagios::Acknowledge::NamedPipe::Service" Required="0" Valid="1">
        <Description Translatable="1">Named pipe acknowledge format for service.</Description>
        <Group>SystemMonitoring</Group>
        <SubGroup>Nagios::Acknowledge</SubGroup>
        <Setting>
            <String Regex="">[&lt;UNIXTIME&gt;] ACKNOWLEDGE_SVC_PROBLEM;&lt;HOST_NAME&gt;;&lt;SERVICE_NAME&gt;;1;1;1;&lt;LOGIN&gt;;&lt;a href="&lt;CONFIG_HttpType&gt;://&lt;CONFIG_FQDN&gt;/&lt;CONFIG_ScriptAlias&gt;index.pl?Action=AgentTicketZoom&amp;TicketID=&lt;TicketID&gt;"&gt;&lt;CONFIG_Ticket::Hook&gt;&lt;TicketNumber&gt;&lt;/a&gt;</String>
        </Setting>
    </ConfigItem>
    <ConfigItem Name="Ticket::EventModulePost###9-NagiosAcknowledge" Required="1" Valid="1">
        <Description Translatable="1">Ticket event module to send an acknowlage to Nagios.</Description>
        <Group>SystemMonitoring</Group>
        <SubGroup>Nagios::Acknowledge</SubGroup>
        <Setting>
            <Hash>
                <Item Key="Module">Kernel::System::Ticket::Event::NagiosAcknowledge</Item>
                <Item Key="Event">TicketLockUpdate</Item>
            </Hash>
        </Setting>
    </ConfigItem>
    <ConfigItem Name="Nagios::Acknowledge::HTTP::URL" Required="0" Valid="1">
        <Description Translatable="1">The http acknowledge url.</Description>
        <Group>SystemMonitoring</Group>
        <SubGroup>Nagios::Acknowledge</SubGroup>
        <Setting>
            <String Regex="">http://nagios.example.com/nagios/cgi-bin/cmd.cgi?cmd_typ=&lt;CMD_TYP&gt;&amp;cmd_mod=2&amp;host=&lt;HOST_NAME&gt;&amp;service=&lt;SERVICE_NAME&gt;&amp;sticky_ack=on&amp;send_notification=on&amp;persistent=on&amp;com_data=&lt;TicketNumber&gt;&amp;btnSubmit=Commit</String>
        </Setting>
    </ConfigItem>
    <ConfigItem Name="Nagios::Acknowledge::HTTP::User" Required="0" Valid="1">
        <Description Translatable="1">The http acknowledge user.</Description>
        <Group>SystemMonitoring</Group>
        <SubGroup>Nagios::Acknowledge</SubGroup>
        <Setting>
            <String Regex="">John</String>
        </Setting>
    </ConfigItem>
    <ConfigItem Name="Nagios::Acknowledge::HTTP::Password" Required="0" Valid="1">
        <Description Translatable="1">The http acknowledge password.</Description>
        <Group>SystemMonitoring</Group>
        <SubGroup>Nagios::Acknowledge</SubGroup>
        <Setting>
            <String Regex="">some_pass</String>
        </Setting>
    </ConfigItem>
    
    <ConfigItem Name="Zabbix::Acknowledge::Enable" Required="1" Valid="1">
        <Description Translatable="1">Enable Zabbix events acknowledgement on ticket creation</Description>
        <Group>SystemMonitoring</Group>
        <SubGroup>Zabbix::Acknowledge</SubGroup>
        <Setting>
            <Option SelectedID="0">
                <Item Key="0">No</Item>
                <Item Key="1">Yes</Item>
            </Option>
        </Setting>
    </ConfigItem>

    <ConfigItem Name="Zabbix::Acknowledge::FreeField::Event" Required="1" Valid="1">
        <Description Translatable="1">Name of TicketFreeField for Event.</Description>
        <Group>SystemMonitoring</Group>
        <SubGroup>Zabbix::Acknowledge</SubGroup>
        <Setting>
            <String Regex="">TicketFreeText3</String>
        </Setting>
    </ConfigItem>
    <ConfigItem Name="Zabbix::Acknowledge::HTTP::URL" Required="1" Valid="1">
        <Description Translatable="1">The http acknowledge url.</Description>
        <Group>SystemMonitoring</Group>
        <SubGroup>Zabbix::Acknowledge</SubGroup>
        <Setting>
            <String Regex="">http://zabbix.example.com/zabbix/api_jsonrpc.php</String>
        </Setting>
    </ConfigItem>
    <ConfigItem Name="Zabbix::Acknowledge::HTTP::User" Required="1" Valid="1">
        <Description Translatable="1">The http acknowledge user.</Description>
        <Group>SystemMonitoring</Group>
        <SubGroup>Zabbix::Acknowledge</SubGroup>
        <Setting>
            <String Regex="">John</String>
        </Setting>
    </ConfigItem>
    <ConfigItem Name="Zabbix::Acknowledge::HTTP::Password" Required="1" Valid="1">
        <Description Translatable="1">The http acknowledge password.</Description>
        <Group>SystemMonitoring</Group>
        <SubGroup>Zabbix::Acknowledge</SubGroup>
        <Setting>
            <String Regex="">some_pass</String>
        </Setting>
    </ConfigItem>
    <ConfigItem Name="Zabbix::Acknowledge::Message" Required="1" Valid="1">
        <Description Translatable="1">Named pipe acknowledge format for service.</Description>
        <Group>SystemMonitoring</Group>
        <SubGroup>Zabbix::Acknowledge</SubGroup>
        <Setting>
            <String Regex="">OTRS Ticket: &lt;a href="&lt;CONFIG_HttpType&gt;://&lt;CONFIG_FQDN&gt;/&lt;CONFIG_ScriptAlias&gt;index.pl?Action=AgentTicketZoom&amp;TicketID=&lt;TicketID&gt;"&gt;&lt;CONFIG_Ticket::Hook&gt;&lt;TicketNumber&gt;&lt;/a&gt;</String>
        </Setting>
    </ConfigItem>
</otrs_config>
ZabbixAcknowledge.pm (application/x-perl, 4.8 KB)
# --
# Kernel/System/Ticket/Event/NagiosAcknowledge.pm - acknowlege nagios tickets
# Copyright (C) 2001-2010 OTRS AG, http://otrs.org/
# --
# $Id: NagiosAcknowledge.pm,v 1.9 2010/02/15 18:16:06 ub Exp $
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (AGPL). If you
# did not receive this file, see http://www.gnu.org/licenses/agpl.txt.
# --

package Kernel::System::Ticket::Event::ZabbixAcknowledge;

use strict;
use warnings;
use JSON;
use JSON::RPC::Client;

use vars qw($VERSION);
$VERSION = qw($Revision: 1.9 $) [1];

sub new {
    my ( $Type, %Param ) = @_;

    # allocate new hash for object
    my $Self = {};
    bless( $Self, $Type );

    # get needed objects
    for (
        qw(ConfigObject TicketObject LogObject UserObject CustomerUserObject SendmailObject TimeObject EncodeObject UserObject)
        )
    {
        $Self->{$_} = $Param{$_} || die "Got no $_!";
    }

    # get correct FreeFields
    $Self->{Fevent}    = $Self->{ConfigObject}->Get('Zabbix::Acknowledge::FreeField::Event');

    return $Self;
}

sub Run {
    my ( $Self, %Param ) = @_;

    # check needed stuff
    for (qw(TicketID Event Config)) {
        if ( !$Param{$_} ) {
            $Self->{LogObject}->Log( Priority => 'error', Message => "Need $_!" );
            return;
        }
    }
    
    # Set UserID to root if we didn't get it
    my $UserID = 1;
    if ($Param{UserID}){
	$UserID = $Param{UserID};
    };

    # check if acknowledge is active
    my $Enable = $Self->{ConfigObject}->Get('Zabbix::Acknowledge::Enable');
    if (!$Enable){
	$Self->{LogObject}->Log( Priority => 'debug', Message => "Zabbix acknowledge not enabled" );
	return 1;
    };

    # check if it's a Zabbix related ticket
    my %Ticket = $Self->{TicketObject}->TicketGet( TicketID => $Param{TicketID} );
    if ( !$Ticket{ $Self->{Fevent} } ) {
        $Self->{LogObject}->Log( Priority => 'debug', Message => "No Event ID in the ticket!" );
        return 1;
    }
    
    # Get ticket event ID
    $Self->{EventID} = $Ticket{$Self->{Fevent}};
    
    my $Msg   = $Self->{ConfigObject}->Get('Zabbix::Acknowledge::Message');

    # replace ticket tags
    for my $Key ( keys %Ticket ) {
        next if !defined $Ticket{$Key};

        # strip not allowd chars
        $Ticket{$Key} =~ s/'//g;
        $Ticket{$Key} =~ s/;//g;
        $Msg          =~ s/<$Key>/$Ticket{$Key}/g;
    }

    # replace config tags
    $Msg =~ s{<CONFIG_(.+?)>}{$Self->{ConfigObject}->Get($1)}egx;
    
    # Replace newlines
    

    my $Return = $Self->_JSON_Zabbix(Ticket => \%Ticket, Message => "$Msg");

    if (!$Return) {
        $Self->{TicketObject}->HistoryAdd(
            TicketID     => $Param{TicketID},
            HistoryType  => 'Misc',
            Name         => "Sent Acknowledge to Zabbix.",
            CreateUserID => $UserID,
        );
        return;
    }
    else {
        $Self->{TicketObject}->HistoryAdd(
            TicketID     => $Param{TicketID},
            HistoryType  => 'Misc',
            Name         => "Was not able to send Acknowledge to Zabbix!",
            CreateUserID => $UserID,
        );
        return 1;
    }
    return;
}

sub _JSON_Zabbix {
    my ( $Self, %Param ) = @_;

    # check needed stuff
    for (qw(Ticket Message)) {
        if ( !$Param{$_} ) {
            $Self->{LogObject}->Log( Priority => 'error', Message => "Need $_!" );
            return 1;
        }
    }
    # Get parameters
    my %Ticket = %{ $Param{Ticket} };
    my $URL   = $Self->{ConfigObject}->Get('Zabbix::Acknowledge::HTTP::URL');
    
    
    # RPC start
    my $id=0;
    my $rpc = new JSON::RPC::Client;
    
    # Auth first
    $id = $id + 1;
    my $auth = {
	'jsonrpc' => '2.0',
	'method' => 'user.authenticate',
	'id' => $id,
	'params' => {
	    'user' => $Self->{ConfigObject}->Get('Zabbix::Acknowledge::HTTP::User'),
	    'password' => $Self->{ConfigObject}->Get('Zabbix::Acknowledge::HTTP::Password'),
	},
    };
    my $rpc_result=$rpc->call($URL, $auth);
    if ($rpc_result->is_error){
        $Self->{LogObject}->Log(
            Priority => 'error',
            Message  => "Can't authenticate $URL: " . "$rpc_result->jsontext",
        );
        return 1;
    };
    # Get auth hash
    $auth = $rpc_result->result;
    
    # Post RPC query and get result
    $id = $id + 1;
    my $request = {
	'jsonrpc' => '2.0',
	'method' => 'event.acknowledge',
	'id' => $id,
	'params' => {
	    'eventids' => [ "$Self->{EventID}" ],
	    'message' => "$Param{Message}"
	},
        'auth' => $auth,
    };
    $rpc_result=$rpc->call($URL, $request);
    if ($rpc_result->is_error){
        $Self->{LogObject}->Log(
            Priority => 'error',
            Message  => "Can't request $URL: " . "$rpc_result->jsontext",
        );
        return 1;
    };
    return;
}
1;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.