Unified Assurance Event Generic Ticket Connector

Overview

The Unified Assurance Event Generic Ticket Connector allows for bi-directional integration with an external ticketing system, creating and synchronizing tickets in the ticketing system using information from Unified Assurance Events. For each event retrieved by the SQL query, a new ticket is opened using customizable rules. The application also synchronizes currently open tickets using a separate set of customizable rules.

Generic Ticket Connector Setup

The default query will process events that that have the "TicketFlag" field set to "1" and have a blank "TicketID" field. These values can be set manually in the event, using an event list tool, or by another application (for example, Unified Assurance Event Custom Action Policy Engine (CAPE)). The default rules were written to connect to a Wendia ticketing system and create an ticket with information from the event, then periodically check the ticket status to see if the issue has been resolved. This will need to be changed to connect to other ticketing systems.

  1. Review the query in the "SelectSQLFile" file to see what events will be selected for processing. Make changes to the events that are selected as needed.

  2. Review the processing that is done in the "OpenTicketRules" file to see the processing that is done to open a ticket. Make changes to the processing of new tickets as needed.

  3. Review the processing that is done in the "SyncTicketRules" file to see the processing that is done to keep the ticket status synchronized. Make changes to the synchronization of existing tickets as needed.

  4. Enable the default Service, unless a specific configuration option is needed.

    Configuration -> Broker Control -> Services

Default Service

Field Value
Package Name coreProcessing-app
Service Name Event Ticket Connector
Service Program bin/core/processing/GenericTicketConnectord
Service Arguments
Service Description Generic ticket connector
Failover Type Standalone (Supported: Standalone, Primary/Backup)
Status Disabled
Privileged (Checked)

Default Configuration

Name Value Possible Values Notes
BranchDir core/default Text, 255 characters Relative path to Rules directory.
IncludeRules processing/event/ticket/ticket.includes Text, 255 characters Relative path to Include Rules.
LoadRules processing/event/ticket/ticket.load Text, 255 characters Relative path to Load Rules
LogFile logs/EventTicketConnector.log Text, 255 characters Relative path to Log File.
LogLevel ERROR OFF, FATAL, ERROR, WARN, INFO, DEBUG Logging level used by application.
OpenTicketRules processing/event/ticket/open_ticket.rules Text, 255 characters Rules run upon finding any new events ready to be ticketed.
PollTime 60 Integer Interval in seconds where events in the TicketHash are processed by SyncTicketRules, and new events are queried using OpenTicketRules and processed by OpenTicketRules.
SelectSQLFile processing/event/ticket/events.sql Text, 255 characters Relative path to SQL file containing database query. (Ensure processed events are excluded after processing.)
ShardID 1 Integer Database shard to be used. - NO RELOAD CONFIG SUPPORT
SyncTicketRules processing/event/ticket/sync_ticket.rules Text, 255 characters Rules run at PollTime interval on Tickets saved in the $TicketHash for scheduled synchronization.
Threads 3 Integer Number of processing threads created.

Rules

This connector uses the Unified Assurance standard rules architecture. The rules are written in Perl syntax. Refer to the following guides for details on rules creation:

Tokens

The connector exposes the following tokens for rules processing.

Token Description
$AppConfig Hash reference to the application configuration name-value pairs that were configured. (i.e. use $AppConfig->{'Host'} to retrieve the set value for 'Host'.)
$EventRow Resulting data from query. Use $EventRow->{'FieldName'} to access the "FieldName" data.
$CustomHash Custom key, value cache available across all rules. Contents commonly defined in Load Rules then used in Base or other rules. NOTE: This variable is a shared object and any additional sub hashes or arrays must be shared before use or it will cause the error: "Invalid value for shared scalar". Instantiate the sub hash/array using '&share({})' e.g.
$CustomHash->{SubObject} = &share({});
$StorageHash Internal cache used as the StorageHash option when calling rules functions such as FindDeviceID(). NOTE: The structure of this cache is subject to change! Not recommended for custom global storage or manual manipulation; use $CustomHash.

Integration Example

SugarCRM

One of the many use cases for Unified Assurance is to create and/or update tickets in various ticketing systems. This example will demonstrate an integration with a SugarCRM system utilizing the Unified Assurance Generic Ticketing Connector and the SOAP API.

One possible use case is the monitoring of VPN tunnels between one or more sites. When a tunnel down event is received, Unified Assurance verifies the tunnel is down and then updates the "TicketFlag" field for the event. This is then caught by the ticketing connector, which opens a case in SugarCRM. After the case is opened in SugarCRM, the case ID is stored in memory, which is then used to check the SugarCRM case. If the SugarCRM case is closed, the event severity is changed to "0".

Dependencies

Steps

  1. Create new rules files.

    1. Via the UI, open the Rules UI:

      Configuration -> Rules

    2. Expand the following folder path:

      Core Rules (core) -> Default read-write branch (default) -> processing -> event

    3. Select on the "event" folder, then select "Add -> New Folder". In the form, enter "ticket-SugarCRM" , then "Submit" the new folder name. Enter a custom commit message, or click "Ok" to accept the default.

    4. Under the "ticket-SugarCRM" folder, create five new rules files that will be used by the connector. The default path for the default connector configuration is in the following folder:

      1. "sugar_events.sql" - This file contains the SQL query used by the connector to find events that will have a ticket opened.

      2. "sugar.load" - This file will contain any functions needed to pre-load the data when the connector starts. For this example, the file will be left blank, but still needs to be created.

      3. "sugar.includes" - This file will contain the path to any additional rules files that can be used by the connector when processing data. For this example, the file will be left blank, but still needs to be created.

      4. "sugar_open_ticket.rules" - This file contains the Perl logic used by the connector to create a new ticket when an event is detected.

      5. "sugar_sync_ticket.rules" - This file contains the Perl logic used by the connector to sync an existing ticket after an event is detected and a ticket is created.

    5. Copy the code from the "CODE EXAMPLE" below and paste it into the appropriate rules file, changing the following fields. Save each file after pasting and updating the code:

      • sugar_open_ticket.rules

        • (ASSURE1-SERVER-URL) - This should be the URL to a Assure1 presentation server.

        • (SUGAR-URL) - This should be the URL to a SugarCRM server.

        • (SUGAR-USERNAME)/(SUGAR-PASSWORD) - This should be the login credentials to the SugarCRM server with permissions to access the API.

        • (SUGAR-USER-ID) - This should be the SugarCRM User ID that will have the case associated to it.

      • sugar_sync_ticket.rules

        • (SUGAR-URL) - This should be the URL to a SugarCRM server.

        • (SUGAR-USERNAME)/(SUGAR-PASSWORD) - This should be the login credentials to the SugarCRM server with permissions to access the API.

  2. Create a new service using the following settings.

    Configuration -> Broker Control -> Services

    • Package Name => coreProcessing-app

    • Service Name => Event SugarCRM Ticket Connector

    • Service Program => bin/core/processing/GenericTicketConnectord

    • Failover Type => Standalone

    • Status => Enabled

    • Privileged => (Checked)

    • Configuration

      • BranchDir => core/default

      • IncludeRules => processing/event/ticket-SugarCRM/sugar.includes

      • LoadRules => processing/event/ticket-SugarCRM/sugar.load

      • LogFile => logs/EventSugarTicketConnector.log

      • LogLevel => DEBUG

      • OpenTicketRules => processing/event/ticket-SugarCRM/sugar_open_ticket.rules

      • PollTime => 60

      • SelectSQLFile => processing/event/ticket-SugarCRM/sugar_events.sql

      • ShardID => 1

      • SyncTicketRules => processing/event/ticket-SugarCRM/sugar_sync_ticket.rules

      • Threads => 3

  3. The broker will start the service the next time the self-check is done.

  4. Verify functionality.

Code Examples

sugar_events.sql

Note: Additional fields can be selected if they would be useful within the rules.

  SELECT EventID,   
         Node,
         Summary
    FROM Events
   WHERE TicketID = '' 
     AND TicketFlag = 1
ORDER BY LastReported desc
sugar_open_ticket.rules
######################### 
# $EventRow = Hash for record found
#########################
my $EventsDBH = DBConnect($Config, 'Event', {AutoCommit => 1}) || die 'Cannot open connection to database: ' . $DBI::errstr . "\n";
my $EventID = $EventRow->{EventID};
$Log->Message("INFO", " --> Open Ticket Rules --> Running Rules Starting [$EventID]");
$Log->Message("DEBUG", " --> Open Ticket Rules --> Event Dump\n--------------------------\n" . Dumper($EventRow) . "\n--------------------------\n");

# prepare a few variables for use
my $CaseNum        = 0;
my $CaseIdentifier = 0;

my ($ErrorFlag, $Message) = UpdateEvent({
    DBH     => \$EventsDBH,
    EventID => $EventID,
    ShardID => $AppConfig->{'ShardID'},
    Values  => {
        TicketFlag => 2,
        TicketID   => 'Pending'
    }
});

# set which tunnel was down based on the data within the event
if ( $EventRow->{Summary} =~ m/Neighbor 192.168.1.2/ ) {
    $VPNTunnel = "Datacenter <---> Destination 2";
}
elsif ( $EventRow->{Summary} =~ m/Neighbor 192.168.1.3/ ) {
    $VPNTunnel = "Datacenter <---> Destination 3";
}
else {
    $VPNTunnel = "Datacenter <---> Unknown";
}

$CaseSubject = 'A Tunnel has been down for more than 1 minute - ' . $VPNTunnel;

#############
# NOTE: UNKNOWN IF A URL TO A1 CAN BE DONE AT THIS TIME.
#############
$CaseBody    = "A Tunnel is down.

Node = $EventRow->{Node}

VPN Tunnel = $VPNTunnel

Down Alert Information:
    Raw Message = $EventRow->{Summary}
    Event = https://(Assure1-SERVER-URL)/events/tools/GenericBaseTools.php?Tool=AlarmInfo;EventID=$EventID
";

# needed if sugar server https cert is self-signed
BEGIN { $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0; }

use SOAP::Lite;
use Data::Dumper;

# establish the connection to the soap server
my $SugarConnection = SOAP::Lite->new(
    'readable' => 1,
    'uri'      => 'http://www.sugarcrm.com/sugarcrm',
    'proxy'    => 'https://(SUGAR-URL)/soap.php'
);

# prepare some variables that will be used for logging into sugar
my $SugarUsername = SOAP::Data->name('user_name' => '(SUGAR-USERNAME)');
my $SugarPassword = SOAP::Data->name('password'  => '(SUGAR-PASSWORD)');

# prepare the login request data
my $LoginRequest = SOAP::Data
    ->prefix('tns')
    ->type('user_auth')
    ->name('user_auth' => \SOAP::Data->value(
        $SugarUsername,
        $SugarPassword
    ))
;

my $Result;
my $SessionID;

eval {
    # execute the login request
    $Result = $SugarConnection->login($LoginRequest);
    $Log->Message("DEBUG", "Login Returned:" . Dumper($Result->result));

    # if logged in successfully...
    if (defined $Result) {

        # get the session id
        $Session   = SOAP::Data->name('session' => $Result->result->{'id'});
        $SessionID = $Result->result->{'id'};
        $Log->Message("DEBUG", "Session ID = " . $SessionID);

        # get the user id
        $Result = $SugarConnection->get_user_id($Session);
        $UserID = $Result->result;
        $Log->Message("DEBUG", "User ID = " . $UserID);

        # create the case in Sugar
        $Result = $SugarConnection->set_entry( 
            $SessionID, 
            "Cases", 
            [ 
                { name => 'contact_id',              value => $UserID }, 
                { name => 'status',                  value => "New" }, 
                { name => 'priority',                value => 5 }, 
                { name => 'account_id',              value => "(SUGAR-USER-ID)" }, 
                { name => 'name',                    value => $CaseSubject }, 
                { name => 'description',             value => $CaseBody }, 
                { name => 'case_request_type_dom_c', value => "issue" }, 
                { name => 'type',                    value => "support_desk" }
            ] 
        );
        $Log->Message("DEBUG", "Case create returned = " . Dumper($Result->result));

        # get the case ID
        $CaseID = $Result->result->{'id'};
        $Log->Message("DEBUG", "Case ID = " . $CaseID);

        my $get_entry_listRequest = SOAP::Data->value(
            SOAP::Data->name('session'       => $SessionID),
            SOAP::Data->name('module_name'   => 'Cases'),
            SOAP::Data->name('query'         => 'cases.id = \'' . $CaseID . '\''),
            SOAP::Data->name('order_by'      => ''),
            SOAP::Data->name('offset'        => ''),
            SOAP::Data->name('select_fields' => ''),
            SOAP::Data->name('max_results'   => ''),
            SOAP::Data->name('deleted'       => '')
        );

        # get the data from sugar
        $Result = $SugarConnection->get_entry_list($get_entry_listRequest);
        $Log->Message("DEBUG", "Get case data returned = " . Dumper($Result->result));

        # get the case number
        $CaseNumber = $Result->result->{'entry_list'}[0]->{'name_value_list'}[12]->{'value'};
        $Log->Message("DEBUG", "Case Number = " . $CaseNumber);

        # logout
        $Result = $SugarConnection->logout($Session);
        $Log->Message("DEBUG", "Logout returned = " . Dumper($Result->result));
    }
};

if ($@) {
    $Log->Message("ERROR", "An error occurred: " . $@);
}

# if the ticket was opened...
if ( $CaseNumber > 0 ) {
    # ... update event with ticket number
    my ($ErrorFlag, $Message) = UpdateEvent({
        DBH     => \$EventsDBH,
        EventID => $EventID,
        ShardID => $AppConfig->{'ShardID'},
        Values  => {
            TicketFlag => 2,
            TicketID   => $CaseNumber
        }
    });
}
else {
    # ... or update event with errors
    my ($ErrorFlag, $Message) = UpdateEvent({
        DBH     => \$EventsDBH,
        EventID => $EventID,
        ShardID => $AppConfig->{'ShardID'},
        Values  => {
            TicketFlag => 'ERROR',
            TicketID   => 'ERROR'
        }
    });
}

############################### 
# Setup Ticket Sync
############################### 
if ( ( ! exists $TicketHash->{$EventID} ) && ( int($CaseID) > 0 ) ) {
    $TicketHash->{$EventID} = &share({});
    $TicketHash->{$EventID}->{CaseNumber} = $CaseNumber;
    $TicketHash->{$EventID}->{CaseID}     = $CaseID;
}

$Log->Message("INFO", " --> Open Ticket Rules --> Rules Running Complete [$EventID]");
$EventsDBH->disconnect;
sugar_sync_ticket.rules
######################### 
# $EventID = Event Found
#########################

$Log->Message("INFO", " --> Sync Ticket Rules --> Running Rules Starting [$EventID]");

# grab data from hash and log it...
my $CaseNumber = $TicketHash->{$EventID}->{CaseNumber};
my $CaseID     = $TicketHash->{$EventID}->{CaseID};
$Log->Message('INFO', [
    '#===============================================',
    '# Sync Unified Assurance Event with Sugar',
    '# EventID = '    . $EventID,
    '# CaseNumber = ' . $CaseNumber,
    '# CaseID = '     . $CaseID,
    '#===============================================',
]);

# connect to the events databases
my $EventsDBH = DBConnect($Config, 'Event', {AutoCommit => 1}) || die 'Cannot open connection to database: ' . $DBI::errstr . "\n";

# needed if sugar server https cert is self-signed
BEGIN { $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0; }

use SOAP::Lite;
use Data::Dumper;

# establish the connection to the soap server
my $SugarConnection = SOAP::Lite->new(
    'readable' => 1,
    'uri'      => 'http://www.sugarcrm.com/sugarcrm',
    'proxy'    => 'https://(SUGAR-URL)/soap.php'
);

# prepare some variables that will be used for logging into sugar
my $SugarUsername = SOAP::Data->name('user_name' => '(SUGAR-USERNAME)');
my $SugarPassword = SOAP::Data->name('password'  => '(SUGAR-PASSWORD)');

# prepare the login request data
my $LoginRequest = SOAP::Data
    ->prefix('tns')
    ->type('user_auth')
    ->name('user_auth' => \SOAP::Data->value(
        $SugarUsername,
        $SugarPassword
    ))
;

my $Result;
my $SessionID;

eval {
    # execute the login request
    $Result = $SugarConnection->login($LoginRequest);
    $Log->Message("DEBUG", "Login Returned:" . Dumper($Result->result));

    # if logged in successfully...
    if (defined $Result) {

        # get the session id
        $Session   = SOAP::Data->name('session' => $Result->result->{'id'});
        $SessionID = $Result->result->{'id'};
        $Log->Message("DEBUG", "Session ID = " . $SessionID);

        # get the case data from sugar
        my $get_entry_listRequest = SOAP::Data->value(
            SOAP::Data->name('session'       => $SessionID),
            SOAP::Data->name('module_name'   => 'Cases'),
            SOAP::Data->name('query'         => 'cases.id = \'' . $CaseID . '\''),
            SOAP::Data->name('order_by'      => ''),
            SOAP::Data->name('offset'        => ''),
            SOAP::Data->name('select_fields' => ''),
            SOAP::Data->name('max_results'   => ''),
            SOAP::Data->name('deleted'       => '')
        );
        $Result = $SugarConnection->get_entry_list($get_entry_listRequest);
        $Log->Message("DEBUG", "Get case data returned = " . Dumper($Result->result));

        # get the case status
        $CaseStatus = $Result->result->{'entry_list'}[0]->{'name_value_list'}[14]->{'value'};
        $Log->Message("DEBUG", "Case Status = " . $CaseStatus);

        # if the case is closed in sugar, update the event severity to "clear" and remove the data from the sync'ed ticket hash
        if ( $CaseStatus eq 'Closed' ) {
            my ($ErrorFlag, $Message) = UpdateEvent({
                DBH     => \$EventsDBH,
                EventID => $EventID,
                ShardID => $AppConfig->{'ShardID'},
                Values  => {
                    Severity => 0
                }
            });
            delete $TicketHash->{$EventID};
        }

        # logout
        $Result = $SugarConnection->logout($Session);
        $Log->Message("DEBUG", "Logout returned = " . Dumper($Result->result));
    }
};

if ($@) {
    $Log->Message("ERROR", "An error occurred: " . $@);
}

# disconnect to the events databases
$EventsDBH->disconnect;

$Log->Message("INFO", " --> Sync Ticket Rules --> Rules Running Complete [$EventID]");

Administration Details

The following list shows the technical details needed for advanced administration of the application: