Custom Perl Script to Delete Cache entries for Individual Tickets in OTRS

Moderator: crythias

Post Reply
Jpio630
OTRS newbie
Posts: 12
Joined: 16 Aug 2019, 21:49
OTRS Version?: 4.0.32
Real Name: Jack Piotrowski
Company: MAT Holdings Inc.

Custom Perl Script to Delete Cache entries for Individual Tickets in OTRS

Post by Jpio630 » 19 Aug 2019, 20:09

Hi OtterHub,

I am working on Red Hat Enterprise Linux 7.6

OTRS Version: 4.0.32

I am working on creating a custom Perl script that I will store under "/opt/otrs/bin/otrs.Test.pl".

The idea for this script came from the fact that if we made direct updates to the database on tickets (to mitigate the amount of time Agents have to spend going through and changing some of the Dynamic Fields we have created for tickets) we were not seeing the changes appear in the UI until a user performed an action that would refresh the cache for that ticket (like responding to the ticket).

At first, I looked into running things like:
perl bin/otrs.Console.pl Maint::Cache::Delete

Or otrs.CleanUp.pl

However, it dawned on me that I have no idea what truncating the cache daily would mean for performance of the system. (I assume it would be a large detriment).

So I sought out to isolate individual cache entries for tickets.

I saw in the OTRS 5 (reminder that I am on OTRS 4.0.32) documentation that you could get individual CacheKeys by doing:

my $Value = $CacheObject->Get(
Type => 'ObjectName', # only [a-zA-Z0-9_] chars usable
Key => 'SomeKey',
);

So I copied a good portion of otrs.CleanUp.pl and attempted to have it isolate the cache file for one Ticket value.

Here is the contents of my script:

_____________________________________________________________________________________________________________________________
#!/usr/bin/perl
use strict;
use warnings;

use File::Basename;
use FindBin qw($RealBin);
use lib dirname($RealBin);
use lib dirname($RealBin) . '/Kernel/cpan-lib';
use lib dirname($RealBin) . '/Custom';

use Getopt::Long;

use Kernel::System::ObjectManager;

use lib qw(/opt/otrs/Kernel/System/);

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

# get options
Getopt::Long::Configure('no_ignore_case');
my %Opts;
GetOptions(
'expired|e' => \$Opts{e},
'all|a' => \$Opts{a},
'help|h' => \$Opts{h},
'type|t=s' => \$Opts{t},
);

my %Options;
if ( $Opts{e} ) {
$Options{Expired} = 1;
}
if ( $Opts{t} ) {
$Options{Type} = $Opts{t};
}


our @ObjectDependencies = (
'Kernel::Config',
'Kernel::Language',
'Kernel::System::XML',
'Kernel::System::Cache',
'Kernel::System::Log',
'Kernel::System::Type',
'Kernel::System::Ticket',
);

local $Kernel::OM = Kernel::System::ObjectManager->new(
'Kernel::System::Log' => {
LogPrefix => 'OTRS-otrs.Test.pl',
},
);

$Param{TicketID} = 195868;

my $CacheKey = "Ticket::ID::$Param{TicketID}::Kernel::System::Ticket";

# get cache object
my $CacheObject = $Kernel::OM->Get(
Type => 'Ticket',
Key => $CacheKey,
CacheInMemory => 0);

if(!CacheObject->CleanUp(%Options)) {
exit 1;
}

print "$CacheObject";

print "Done.\n";

exit;
_____________________________________________________________________________________________________________________________

However, I am currently getting these errors (even though I have ObjectDependencies declared in otrs.Test.pl:


[Mon Aug 19 12:58:48 2019] otrs.Test.pl: Type does not declare its object dependencies! at otrs.Test.pl line 59.
Type does not declare its object dependencies! at /opt/otrs/Kernel/System/ObjectManager.pm line 545.
Kernel::System::ObjectManager::_DieWithError('Kernel::System::ObjectManager=HASH(0x319b9c0)', 'Error', 'Type does not declare its object dependencies!') called at /opt/otrs/Kernel/System/ObjectManager.pm line 216
Kernel::System::ObjectManager::_ObjectBuild('Kernel::System::ObjectManager=HASH(0x319b9c0)', 'Package', 'Type') called at /opt/otrs/Kernel/System/ObjectManager.pm line 184
Kernel::System::ObjectManager::Get('Kernel::System::ObjectManager=HASH(0x319b9c0)', 'Type', 'Ticket', 'Key', 'Ticket::ID::195868::Kernel::System::Ticket', 'CacheInMemory', 0) called at otrs.Test.pl line 59
[root@drithlp10-matholdingsinc-com bin]# ^C
[root@drithlp10-matholdingsinc-com bin]#

The error messages also reference ObjectManager.pm not having ObjectDependencies declared for Type as well, so added them.

I'll include my ObjectManager.pm in its entirety in case there is more you should wish to see in there.

ObjectManager.pm:

_____________________________________________________________________________________________________________________________
# --
# Copyright (C) 2001-2018 OTRS AG, https://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
# --

package Kernel::System::ObjectManager;
## nofilter(TidyAll::Plugin::OTRS::Perl::LayoutObject)
## nofilter(TidyAll::Plugin::OTRS::Perl::PodSpelling)
## nofilter(TidyAll::Plugin::OTRS::Perl::Require)
## nofilter(TidyAll::Plugin::OTRS::Perl::SyntaxCheck)

use strict;
use warnings;

use Carp qw(carp confess);
use Scalar::Util qw(weaken);

# use the "standard" modules directly, so that persistent environments
# like mod_perl and FastCGI pre-load them at startup

use Kernel::Config;
use Kernel::Output::HTML::Layout;
use Kernel::System::Auth;
use Kernel::System::AuthSession;
use Kernel::System::Cache;
use Kernel::System::DB;
use Kernel::System::Encode;
use Kernel::System::Group;
use Kernel::System::Log;
use Kernel::System::Main;
use Kernel::System::Time;
use Kernel::System::Web::Request;
use Kernel::System::User;

# Contains the top-level object being retrieved;
# used to generate better error messages.
our $CurrentObject;

=head1 NAME

Kernel::System::ObjectManager - object and dependency manager

=head1 SYNOPSIS

The ObjectManager is the central place to create and access singleton OTRS objects.

=head2 How does it work?

It creates objects as late as possible and keeps references to them. Upon destruction the objects
are destroyed in the correct order, based on their dependencies (see below).

=head2 How to use it?

The ObjectManager must always be provided to OTRS by the toplevel script like this:

use Kernel::System::ObjectManager;
local $Kernel::OM = Kernel::System::ObjectManager->new(
# options for module constructors here
LogObject {
LogPrefix => 'OTRS-MyTestScript',
},
);

Then in the code any object can be retrieved that the ObjectManager can handle,
like Kernel::System::DB:

return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare('SELECT 1');


=head2 Which objects can be loaded?

The ObjectManager can load every object that declares its dependencies like this in the Perl package:

package Kernel::System::Valid;

use strict;
use warnings;

our @ObjectDependencies = (
'Kernel::System::Cache',
'Kernel::System::DB',
'Kernel::System::Log',
'Kernel::System::Type',
'Kernel::System::XML',
'Kernel::System::Ticket',
);

The C<@ObjectDependencies> is the list of objects that the current object will depend on. They will
be destroyed only after this object is destroyed.

If you want to signal that a package can NOT be loaded by the ObjectManager, you can use the
C<$ObjectManagerDisabled> flag:

package Kernel::System::MyBaseClass;

use strict;
use warnings;

$ObjectManagerDisabled = 1;

=head1 PUBLIC INTERFACE

=over 4

=item new()

Creates a new instance of Kernel::System::ObjectManager.

use Kernel::System::ObjectManager;
local $Kernel::OM = Kernel::System::ObjectManager->new();

Sometimes objects need parameters to be sent to their constructors,
these can also be passed to the ObjectManager's constructor like in the following example.
The hash reference will be flattened and passed to the constructor of the object(s).

local $Kernel::OM = Kernel::System::ObjectManager->new(
Kernel::System::Log => {
LogPrefix => 'OTRS-MyTestScript',
},
);

Alternatively, C<ObjectParamAdd()> can be used to set these parameters at runtime (but this
must happen before the object was created).

If the C<< Debug => 1 >> option is present, destruction of objects
is checked, and a warning is emitted if objects persist after the
attempt to destroy them.

=cut

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

$Self->{Debug} = delete $Param{Debug};

for my $Parameter ( sort keys %Param ) {
$Self->{Param}->{$Parameter} = $Param{$Parameter};
}

# Kernel::System::Encode->new() initializes the environment, so we need to
# already create an instance here to make sure it is always done and done
# at the beginning of things.
$Self->Get('Kernel::System::Encode');

return $Self;
}

=item Get()

Retrieves a singleton object, and if it not yet exists, implicitly creates one for you.

my $ConfigObject = $Kernel::OM->Get('Kernel::Config');

DEPRECATED: For backwards compatibility reasons, object aliases can be defined in L<Kernel::Config::Defaults>.
For example C<< ->Get('TicketObject') >> retrieves a L<Kernel::System::Ticket> object.

my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); # returns the same ConfigObject as above

=cut

sub Get {

# No param unpacking for increased performance
if ( $_[1] && $_[0]->{Objects}->{ $_[1] } ) {
return $_[0]->{Objects}->{ $_[1] };
}

if ( !$_[1] ) {
$_[0]->_DieWithError(
Error => "Error: Missing parameter (object name)",
);
}

# record the object we are about to retrieve to potentially
# build better error messages
# needs to be a statement-modifying 'if', otherwise 'local'
# is local to the scope of the 'if'-block
local $CurrentObject = $_[1] if !$CurrentObject;

return $_[0]->_ObjectBuild( Package => $_[1] );
}

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

my $Package = $Param{Package};
my $FileName = $Package;
$FileName =~ s{::}{/}g;
$FileName .= '.pm';
eval {
require $FileName;
};
if ($@) {
if ( $CurrentObject && $CurrentObject ne $Package ) {
$Self->_DieWithError(
Error => "$CurrentObject depends on $Package, but $Package could not be loaded: $@",
);
}
else {
$Self->_DieWithError(
Error => "$Package could not be loaded: $@",
);
}
}

# Kernel::Config does not declare its dependencies (they would have to be in
# Kernel::Config::Defaults), so assume [] in this case.
my $Dependencies = [];

if ( $Package ne 'Kernel::Config' ) {
no strict 'refs'; ## no critic
if ( !exists ${ $Package . '::' }{ObjectDependencies} ) {
$Self->_DieWithError( Error => "$Package does not declare its object dependencies!" );
}
$Dependencies = \@{ $Package . '::ObjectDependencies' };

if ( ${ $Package . '::ObjectManagerDisabled' } ) {
$Self->_DieWithError( Error => "$Package cannot be loaded via ObjectManager!" );
}

use strict 'refs';
}
$Self->{ObjectDependencies}->{$Package} = $Dependencies;

my $NewObject = $Package->new(
%{ $Self->{Param}->{$Package} // {} }
);

if ( !defined $NewObject ) {
if ( $CurrentObject && $CurrentObject ne $Package ) {
$Self->_DieWithError(
Error =>
"$CurrentObject depends on $Package, but the constructor of $Package returned undef.",
);
}
else {
$Self->_DieWithError(
Error => "The constructor of $Package returned undef.",
);
}
}

$Self->{Objects}->{$Package} = $NewObject;

return $NewObject;
}

=item ObjectInstanceRegister()

Adds an existing object instance to the ObjectManager so that it can be accessed by other objects.

This should only be used on special circumstances, e. g. in the unit tests to pass $Self to the
ObjectManager so that it is also available from there as 'Kernel::System::UnitTest'.

$Kernel::OM->ObjectInstanceRegister(
Package => 'Kernel::System::UnitTest',
Object => $UnitTestObject,
Dependencies => [], # optional, specify OM-managed packages that the object might depend on
);

=cut

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

if ( !$Param{Package} || !$Param{Object} ) {
$Self->_DieWithError( Error => 'Need Package and Object.' );
}

if ( defined $Self->{Objects}->{ $Param{Package} } ) {
$Self->_DieWithError( Error => 'Need $Param{Package} is already registered.' );
}

$Self->{Objects}->{ $Param{Package} } = $Param{Object};
$Self->{ObjectDependencies}->{ $Param{Package} } = $Param{Dependencies} // [];

return 1;
}

=item ObjectParamAdd()

Adds arguments that will be passed to constructors of classes
when they are created, in the same format as the C<new()> method
receives them.

$Kernel::OM->ObjectParamAdd(
'Kernel::System::Ticket' => {
Key => 'Value',
},
);

To remove a key again, send undef as a value:

$Kernel::OM->ObjectParamAdd(
'Kernel::System::Ticket' => {
Key => undef, # this will remove the key from the hash
},
);

=cut

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

for my $Package ( sort keys %Param ) {
if ( ref( $Param{$Package} ) eq 'HASH' ) {
for my $Key ( sort keys %{ $Param{$Package} } ) {
if ( defined $Key ) {
$Self->{Param}->{$Package}->{$Key} = $Param{$Package}->{$Key};
}
else {
delete $Self->{Param}->{$Package}->{$Key};
}
}
}
else {
$Self->{Param}->{$Package} = $Param{$Package};
}
}
return;
}

=item ObjectsDiscard()

Discards internally stored objects, so that the next access to objects
creates them newly. If a list of object names is passed, only
the supplied objects and their recursive dependencies are destroyed.
If no list of object names is passed, all stored objects are destroyed.

$Kernel::OM->ObjectsDiscard();

$Kernel::OM->ObjectsDiscard(
Objects => ['Kernel::System::Ticket', 'Kernel::System::Queue'],

# optional
# forces the packages to be reloaded from the file system
# sometimes necessary with mod_perl when running CodeUpgrade during a package upgrade
# if no list of object names is passed, all stored objects are destroyed
# and forced to be reloaded
ForcePackageReload => 1,
);

Mostly used for tests that rely on fresh objects, or to avoid large
memory consumption in long-running processes.

Note that if you pass a list of objects to be destroyed, they are destroyed
in in the order they were passed; otherwise they are destroyed in reverse
dependency order.

=cut

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

# fire outstanding events before destroying anything
my $HasQueuedTransactions;
EVENTS:
for my $Counter ( 1 .. 10 ) {
$HasQueuedTransactions = 0;
EVENTHANDLERS:
for my $EventHandler ( @{ $Self->{EventHandlers} } ) {

# since the event handlers are weak references,
# they might be undef by now.
next EVENTHANDLERS if !defined $EventHandler;
if ( $EventHandler->EventHandlerHasQueuedTransactions() ) {
$HasQueuedTransactions = 1;
$EventHandler->EventHandlerTransaction();
}
}
if ( !$HasQueuedTransactions ) {
last EVENTS;
}
}
if ($HasQueuedTransactions) {
warn "Unable to handle all pending events in 10 iterations";
}
delete $Self->{EventHandlers};

# destroy objects before their dependencies are destroyed

# first step: get the dependencies into a single hash,
# so that the topological sorting goes faster
my %ReverseDependencies;
my @AllObjects;
for my $Object ( sort keys %{ $Self->{Objects} } ) {
my $Dependencies = $Self->{ObjectDependencies}->{$Object};

for my $Dependency (@$Dependencies) {

# undef happens to be the value that uses the least amount
# of memory in Perl, and we are only interested in the keys
$ReverseDependencies{$Dependency}->{$Object} = undef;
}
push @AllObjects, $Object;
}

# During an OTRS package upgrade the packagesetup code module has just
# recently been copied to it's location in the file system.
# In a persistent Perl environment an old version of the module might still be loaded,
# as watchdogs like Apache2::Reload haven't had a chance to reload it.
# So we need to make sure that the new version is being loaded.
# Kernel::System::Main::Require() checks the relative file path, so we need to remove that from %INC.
# This is only needed in persistent Perl environment, but does no harm in a CGI environment.
if ( $Param{ForcePackageReload} ) {

my @Objects;
if ( $Param{Objects} && @{ $Param{Objects} } ) {
@Objects = @{ $Param{Objects} };
}
else {
@Objects = @AllObjects;
}

for my $Object (@Objects) {

# convert :: to / in order to build a file system path name
my $ObjectPath = $Object;
$ObjectPath =~ s/::/\//g;

# attach .pm as file extension
$ObjectPath .= '.pm';

# delete from global %INC hash
delete $INC{$ObjectPath};
}
}

# second step: post-order recursive traversal
my %Seen;
my @OrderedObjects;
my $Traverser;
$Traverser = sub {
my ($Object) = @_;
return if $Seen{$Object}++;
for my $ReverseDependency ( sort keys %{ $ReverseDependencies{$Object} } ) {
$Traverser->($ReverseDependency);
}
push @OrderedObjects, $Object;
};

if ( $Param{Objects} ) {
for my $Object ( @{ $Param{Objects} } ) {
$Traverser->($Object);
}
}
else {
for my $Object (@AllObjects) {
$Traverser->($Object);
}
}
undef $Traverser;

# third step: destruction
if ( $Self->{Debug} ) {

# If there are undeclared dependencies between objects, destruction
# might not work in the order that we calculated, but might still work
# out in the end.
my %DestructionFailed;
for my $Object (@OrderedObjects) {
my $Checker = $Self->{Objects}->{$Object};
weaken($Checker);
delete $Self->{Objects}->{$Object};

if ( defined $Checker ) {
$DestructionFailed{$Object} = $Checker;
weaken( $DestructionFailed{$Object} );
}
}
for my $Object ( sort keys %DestructionFailed ) {
if ( defined $DestructionFailed{$Object} ) {
warn "DESTRUCTION OF $Object FAILED!\n";
if ( eval { require Devel::Cycle; 1 } ) {
Devel::Cycle::find_cycle( $DestructionFailed{$Object} );
}
else {
warn "To get more debugging information, please install Devel::Cycle.";
}
}
}
}
else {
for my $Object (@OrderedObjects) {
delete $Self->{Objects}{$Object};
}
}

# if an object requests an already destroyed object
# in its DESTROY method, we might hold it again, and must try again
# (but not infinitely)
if ( !$Param{Objects} && keys %{ $Self->{Objects} } ) {
if ( $Self->{DestroyAttempts} && $Self->{DestroyAttempts} > 3 ) {
$Self->_DieWithError( Error => "Loop while destroying objects!" );
}

$Self->{DestroyAttempts}++;
$Self->ObjectsDiscard();
$Self->{DestroyAttempts}--;
}

return 1;
}

=item ObjectRegisterEventHandler()

Registers an object that can handle asynchronous events.

$Kernel::OM->ObjectRegisterEventHandler(
EventHandler => $EventHandlerObject,
);

The C<EventHandler> object should inherit from L<Kernel::System::EventHandler>.
The object manager will call that object's C<EventHandlerHasQueuedTransactions>
method, and if that returns a true value, calls its C<EventHandlerTransaction> method.

=cut

sub ObjectRegisterEventHandler {
my ( $Self, %Param ) = @_;
if ( !$Param{EventHandler} ) {
die "Missing parameter EventHandler";
}
push @{ $Self->{EventHandlers} }, $Param{EventHandler};
weaken( $Self->{EventHandlers}[-1] );
return 1;
}

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

if ( $Self->{Objects}->{'Kernel::System::Log'} ) {
$Self->{Objects}->{'Kernel::System::Log'}->Log(
Priority => 'Error',
Message => $Param{Error},
);
confess $Param{Error}; # this will die()
}

carp $Param{Error};
confess $Param{Error};
}

sub DESTROY {
my ($Self) = @_;

# Make sure $Kernel::OM is still available in the destructor
local $Kernel::OM = $Self;
$Self->ObjectsDiscard();
}

=back

=head1 TERMS AND CONDITIONS

This software is part of the OTRS project (L<https://otrs.org/>).

This software comes with ABSOLUTELY NO WARRANTY. For details, see
the enclosed file COPYING for license information (GPL). If you
did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.

=cut

1;
_____________________________________________________________________________________________________________________________

Questions:

1. Is it possible to isolate entries in the cache by ticket on OTRS 4.0.32?

2. I added Object Dependencies to both my otrs.Test.pl and ObjectManager.pm but it is still erroring out when I run otrs.Test.pl, stating that the ObjectDependencies are not declared. If both have the dependencies declared as you see above, why else might my script error out in this fashion?

3. Is this the correct way to call the CacheKey for individual tickets in OTRS's cache?

my $CacheKey = "Ticket::ID::$Param{TicketID}::Kernel::System::Ticket";

Any input is greatly appreciated.

Best regards,

Jpio630

jojo
Moderator
Posts: 14571
Joined: 26 Jan 2007, 14:50
OTRS Version?: Git Master
Contact:

Re: Custom Perl Script to Delete Cache entries for Individual Tickets in OTRS

Post by jojo » 19 Aug 2019, 21:09

OTRS 4 is not just outdated but also unsecure as there are several unfixed security bugs which will not be fixed and can also exploited by email.

From a design perspective you should consider using webservices instead of database inserts.
"Production": OTRS™ 6, STORM powered by OTRS
"Testing": ((OTRS Community Edition)) git Master

Never change Defaults.pm! :: Blog
Professional Services:: http://www.otrs.com :: enjoy@otrs.com :: Share your ideas

Jpio630
OTRS newbie
Posts: 12
Joined: 16 Aug 2019, 21:49
OTRS Version?: 4.0.32
Real Name: Jack Piotrowski
Company: MAT Holdings Inc.

Re: Custom Perl Script to Delete Cache entries for Individual Tickets in OTRS

Post by Jpio630 » 19 Aug 2019, 23:15

Hi jojo,

"From a design perspective you should consider using webservices instead of database inserts."

What webservices are you referring to to use for this functionality?

Is there an OTRS API for OTRS 4.0.32 that I should be using via REST calls for this functionality?

I appreciate your input,

Jpio630

root
Moderator
Posts: 1758
Joined: 18 Dec 2007, 12:23
OTRS Version?: 4/5/6
Real Name: Roy Kaldung
Company: Znuny Inc.
Contact:

Re: Custom Perl Script to Delete Cache entries for Individual Tickets in OTRS

Post by root » 20 Aug 2019, 07:28

Jack,

Use the GenericInterface for this. If offers you REST and SOAP to work with tickets.

- Roy
OTRS 4/5/6 CentOS / RHEL / Debian / SLES / MySQL / PostgreSQL / Oracle / OpenLDAP / Active Directory / SSO

You need professional services? Check out https://www.znuny.com/

Jpio630
OTRS newbie
Posts: 12
Joined: 16 Aug 2019, 21:49
OTRS Version?: 4.0.32
Real Name: Jack Piotrowski
Company: MAT Holdings Inc.

Re: Custom Perl Script to Delete Cache entries for Individual Tickets in OTRS

Post by Jpio630 » 22 Aug 2019, 16:01

Hi root,

I'm sorry I am late to responding to this ticket. I have been sick and away from my work for a couple days.

Okay, I will look into using the GenericInterface with REST calls to facilitate my functionality. Do you know if it can refresh cache entries for tickets via REST for OTRS 4.0.32? I'm trying to find documentation for OTRS 4 related to this, but not a lot is coming up.

Someone posted earlier stating that they could possibly provide a PHP or PowerShell example for this. I am fairly proficient in PHP if that offer is still on the table?

I appreciate all of your input, and I'm sorry I have not responded due to illness.

Best regards,

Jpio630

root
Moderator
Posts: 1758
Joined: 18 Dec 2007, 12:23
OTRS Version?: 4/5/6
Real Name: Roy Kaldung
Company: Znuny Inc.
Contact:

Re: Custom Perl Script to Delete Cache entries for Individual Tickets in OTRS

Post by root » 22 Aug 2019, 16:15

Jack,

Using the API via GenericInterface REST also keep the cache in shape.

You will find a PHP example here: https://github.com/rkaldung/otrs-gi-rest-php

- Roy
OTRS 4/5/6 CentOS / RHEL / Debian / SLES / MySQL / PostgreSQL / Oracle / OpenLDAP / Active Directory / SSO

You need professional services? Check out https://www.znuny.com/

Jpio630
OTRS newbie
Posts: 12
Joined: 16 Aug 2019, 21:49
OTRS Version?: 4.0.32
Real Name: Jack Piotrowski
Company: MAT Holdings Inc.

Re: Custom Perl Script to Delete Cache entries for Individual Tickets in OTRS

Post by Jpio630 » 22 Aug 2019, 16:59

Root,

Thank you for your help.

I'll take a look and report back if I have any questions once I have a decent idea of how to work with the technology.

Best regards,

Jpio630

Jpio630
OTRS newbie
Posts: 12
Joined: 16 Aug 2019, 21:49
OTRS Version?: 4.0.32
Real Name: Jack Piotrowski
Company: MAT Holdings Inc.

Re: Custom Perl Script to Delete Cache entries for Individual Tickets in OTRS

Post by Jpio630 » 22 Aug 2019, 21:02

Hi,

I cloned this repository:

https://github.com/rkaldung/otrs-gi-rest-php

And installed 'mashape' via Composer.

I have replaced the FQDN (if my server is 'ABC123' on Domain 'TestDomain.com then my FQDN is ABC123.TestDomain.com) to my appropriate server name and domain.

And changed the username and password to valid credentials that I am able to use to login to OTRS's Front-end.

I am getting the following errors though:

Fatal error: Uncaught exception 'Unirest\Exception' with message 'Failed to connect to ABC123.TestDomain.com(real name) port 443: Connection refused' in C:\wamp64\www\OTRS_REST_API\vendor\mashape\unirest-php\src\Unirest\Request.php on line 476

And,

Unirest\Exception: Failed to connect to ABC123.TestDomain.com(real name) port 443: Connection refused in C:\wamp64\www\OTRS_REST_API\vendor\mashape\unirest-php\src\Unirest\Request.php on line 476

I ran "sudo netstat -tulpn" to list the open ports on my server and the process that owns them:

[root@ABC123.TestDomain.com(real name) ~]# sudo netstat -tulpn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN 1714/mysqld
tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN 1/systemd
tcp 0 0 192.*.*.*:53 0.0.0.0:* LISTEN 5219/dnsmasq
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1531/sshd
tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN 1524/cupsd
tcp6 0 0 :::111 :::* LISTEN 1/systemd
tcp6 0 0 :::80 :::* LISTEN 1541/httpd
tcp6 0 0 :::22 :::* LISTEN 1531/sshd
tcp6 0 0 ::1:631 :::* LISTEN 1524/cupsd
udp 0 0 192.*.*.*:53 0.0.0.0:* 5219/dnsmasq
udp 0 0 0.0.0.0:67 0.0.0.0:* 5219/dnsmasq
udp 0 0 0.0.0.0:111 0.0.0.0:* 1/systemd
udp 0 0 127.0.0.1:323 0.0.0.0:* 997/chronyd
udp 0 0 0.0.0.0:722 0.0.0.0:* 975/rpcbind
udp 0 0 0.0.0.0:50338 0.0.0.0:* 988/avahi-daemon: r
udp 0 0 0.0.0.0:5353 0.0.0.0:* 988/avahi-daemon: r
udp6 0 0 :::111 :::* 1/systemd
udp6 0 0 ::1:323 :::* 997/chronyd
udp6 0 0 :::722 :::* 975/rpcbind

But I am not seeing a reference to Port 443.

I noticed in the Github README.md that "The web service can be configured with the web service configuration from Github. Upload this file to create a new REST web service."

So I followed the link and found the GenericTicketConnectorREST.yml

I edited the Framework Version to mine and added it to my root directory for the project.

Where do I upload this file to obtain the REST web service?

Any help is much appreciated.

Best regards,

Jpio630

I expect this is why I do not see Port 443 in the list of ports.

Jpio630
OTRS newbie
Posts: 12
Joined: 16 Aug 2019, 21:49
OTRS Version?: 4.0.32
Real Name: Jack Piotrowski
Company: MAT Holdings Inc.

Re: Custom Perl Script to Delete Cache entries for Individual Tickets in OTRS

Post by Jpio630 » 22 Aug 2019, 21:45

I found the Web Services section in the Admin section OTRS. I'm trying to figure that out now.

Just posting this small note in case someone is trying to respond to my previous response already.

Best regards,

Jpio630

root
Moderator
Posts: 1758
Joined: 18 Dec 2007, 12:23
OTRS Version?: 4/5/6
Real Name: Roy Kaldung
Company: Znuny Inc.
Contact:

Re: Custom Perl Script to Delete Cache entries for Individual Tickets in OTRS

Post by root » 22 Aug 2019, 22:08

Jack,

Port 443 is https, from your netstat I just see port 80. Change https to http in the script.

- Roy
OTRS 4/5/6 CentOS / RHEL / Debian / SLES / MySQL / PostgreSQL / Oracle / OpenLDAP / Active Directory / SSO

You need professional services? Check out https://www.znuny.com/

root
Moderator
Posts: 1758
Joined: 18 Dec 2007, 12:23
OTRS Version?: 4/5/6
Real Name: Roy Kaldung
Company: Znuny Inc.
Contact:

Re: Custom Perl Script to Delete Cache entries for Individual Tickets in OTRS

Post by root » 22 Aug 2019, 22:11

Jpio630 wrote:
22 Aug 2019, 21:02


So I followed the link and found the GenericTicketConnectorREST.yml
This is the link for OTRS 4: https://github.com/OTRS/otrs/blob/rel-4 ... orREST.yml

- Roy
OTRS 4/5/6 CentOS / RHEL / Debian / SLES / MySQL / PostgreSQL / Oracle / OpenLDAP / Active Directory / SSO

You need professional services? Check out https://www.znuny.com/

Jpio630
OTRS newbie
Posts: 12
Joined: 16 Aug 2019, 21:49
OTRS Version?: 4.0.32
Real Name: Jack Piotrowski
Company: MAT Holdings Inc.

Re: Custom Perl Script to Delete Cache entries for Individual Tickets in OTRS

Post by Jpio630 » 22 Aug 2019, 22:34

Thank you Root!

Best regards,

Jpio630

Jpio630
OTRS newbie
Posts: 12
Joined: 16 Aug 2019, 21:49
OTRS Version?: 4.0.32
Real Name: Jack Piotrowski
Company: MAT Holdings Inc.

Re: Custom Perl Script to Delete Cache entries for Individual Tickets in OTRS

Post by Jpio630 » 22 Aug 2019, 23:48

Hi root/other forum users,

I took the YAML file for OTRS 4 that you provided me and I imported it into the GenericInterface Web Service Management section in the UI.

I created an Invoker for the WebService named Test::Test (Not sure if I need an invoker since I think I only need Provider Mode).

I also activated GenericInterface::Invoker::Module###Test::Test in SysConfig.

The WebService lists all of the different available operations appropriately (Create a Session, Create a Ticket, etc...)

However, Client.php with the BaseURL: https://ABC123.TestDomain.com(real FQDN)/otrs/nph-genericinterface.pl/Webservice/GenericTicketConnectorREST is still returning with "Failed to connect" errors.

Do you have any ideas as to what else may be configured incorrectly and preventing me from connecting to the WebService?

I wish I could include screenshots for all of this to make it easier.

If you should wish to have a better understanding via screenshots please private message me an e-mail address to send the screenshots to.

Grateful for your help,

Jpio630

root
Moderator
Posts: 1758
Joined: 18 Dec 2007, 12:23
OTRS Version?: 4/5/6
Real Name: Roy Kaldung
Company: Znuny Inc.
Contact:

Re: Custom Perl Script to Delete Cache entries for Individual Tickets in OTRS

Post by root » 23 Aug 2019, 00:24

Jack,

As I mentioned earliert: if you host does not serve https switch http in the URL

- Roy
OTRS 4/5/6 CentOS / RHEL / Debian / SLES / MySQL / PostgreSQL / Oracle / OpenLDAP / Active Directory / SSO

You need professional services? Check out https://www.znuny.com/

Jpio630
OTRS newbie
Posts: 12
Joined: 16 Aug 2019, 21:49
OTRS Version?: 4.0.32
Real Name: Jack Piotrowski
Company: MAT Holdings Inc.

Re: Custom Perl Script to Delete Cache entries for Individual Tickets in OTRS

Post by Jpio630 » 23 Aug 2019, 15:50

Roy,

Sorry I must have missed that.

After switching from https to http I received my first valid response from the API:

C:\wamp64\www\OTRS_REST_API\client.php:109:
object(Unirest\Response)[3]
public 'code' => int 200
public 'raw_body' => string '{"TicketID":["205268","205267","205266","205265","205264","205263","205262","205261","205260","205259","205258","205257","205256","205255","205254","205253","205252","205251","205250","205249","205248","205247","205246","205245","205244","205243","205242","205241","205240","205239","205238","205237","205236","205235","205234","205233","205232","205231","205230","205229","205228","205227","205226","205225","205224","205223","205222","205221","205220","205219","205218","205217","205216","205215","205214","205'... (length=4514)
public 'body' =>
object(stdClass)[5]
public 'TicketID' =>
array (size=500)
0 => string '205268' (length=6)
1 => string '205267' (length=6)
2 => string '205266' (length=6)
3 => string '205265' (length=6)
4 => string '205264' (length=6)
5 => string '205263' (length=6)
6 => string '205262' (length=6)
7 => string '205261' (length=6)
8 => string '205260' (length=6)
9 => string '205259' (length=6)
10 => string '205258' (length=6)
11 => string '205257' (length=6)
12 => string '205256' (length=6)
13 => string '205255' (length=6)
14 => string '205254' (length=6)
15 => string '205253' (length=6)
16 => string '205252' (length=6)
17 => string '205251' (length=6)
18 => string '205250' (length=6)
19 => string '205249' (length=6)
20 => string '205248' (length=6)
21 => string '205247' (length=6)
22 => string '205246' (length=6)
23 => string '205245' (length=6)
24 => string '205244' (length=6)
25 => string '205243' (length=6)
26 => string '205242' (length=6)
27 => string '205241' (length=6)
28 => string '205240' (length=6)
29 => string '205239' (length=6)
30 => string '205238' (length=6)
31 => string '205237' (length=6)
32 => string '205236' (length=6)
33 => string '205235' (length=6)
34 => string '205234' (length=6)
35 => string '205233' (length=6)
36 => string '205232' (length=6)
37 => string '205231' (length=6)
38 => string '205230' (length=6)
39 => string '205229' (length=6)
40 => string '205228' (length=6)
41 => string '205227' (length=6)
42 => string '205226' (length=6)
43 => string '205225' (length=6)
44 => string '205224' (length=6)
45 => string '205223' (length=6)
46 => string '205222' (length=6)
47 => string '205221' (length=6)
48 => string '205220' (length=6)
49 => string '205219' (length=6)
50 => string '205218' (length=6)
51 => string '205217' (length=6)
52 => string '205216' (length=6)
53 => string '205215' (length=6)
54 => string '205214' (length=6)
55 => string '205213' (length=6)
56 => string '205212' (length=6)
57 => string '205211' (length=6)
58 => string '205210' (length=6)
59 => string '205209' (length=6)
60 => string '205208' (length=6)
61 => string '205207' (length=6)
62 => string '205206' (length=6)
63 => string '205205' (length=6)
64 => string '205204' (length=6)
65 => string '205203' (length=6)
66 => string '205202' (length=6)
67 => string '205201' (length=6)
68 => string '205200' (length=6)
69 => string '205199' (length=6)
70 => string '205198' (length=6)
71 => string '205197' (length=6)
72 => string '205196' (length=6)
73 => string '205195' (length=6)
74 => string '205194' (length=6)
75 => string '205193' (length=6)
76 => string '205192' (length=6)
77 => string '205191' (length=6)
78 => string '205190' (length=6)
79 => string '205189' (length=6)
80 => string '205188' (length=6)
81 => string '205187' (length=6)
82 => string '205186' (length=6)
83 => string '205185' (length=6)
84 => string '205184' (length=6)
85 => string '205183' (length=6)
86 => string '205182' (length=6)
87 => string '205181' (length=6)
88 => string '205180' (length=6)
89 => string '205179' (length=6)
90 => string '205178' (length=6)
91 => string '205177' (length=6)
92 => string '205176' (length=6)
93 => string '205175' (length=6)
94 => string '205174' (length=6)
95 => string '205173' (length=6)
96 => string '205172' (length=6)
97 => string '205171' (length=6)
98 => string '205170' (length=6)
99 => string '205169' (length=6)
100 => string '205168' (length=6)
101 => string '205167' (length=6)
102 => string '205166' (length=6)
103 => string '205165' (length=6)
104 => string '205164' (length=6)
105 => string '205163' (length=6)
106 => string '205162' (length=6)
107 => string '205161' (length=6)
108 => string '205160' (length=6)
109 => string '205159' (length=6)
110 => string '205158' (length=6)
111 => string '205157' (length=6)
112 => string '205156' (length=6)
113 => string '205155' (length=6)
114 => string '205154' (length=6)
115 => string '205153' (length=6)
116 => string '205152' (length=6)
117 => string '205151' (length=6)
118 => string '205150' (length=6)
119 => string '205149' (length=6)
120 => string '205148' (length=6)
121 => string '205147' (length=6)
122 => string '205146' (length=6)
123 => string '205145' (length=6)
124 => string '205144' (length=6)
125 => string '205143' (length=6)
126 => string '205142' (length=6)
127 => string '205141' (length=6)
more elements...
public 'headers' =>
array (size=4)
0 => string 'HTTP/1.1 200 OK' (length=15)
'Content-Type' => string 'application/json; charset=UTF-8' (length=31)
'Content-Length' => string '4514' (length=4)
'Connection' => string 'close' (length=5)


Thank you Roy!

I'm going to try and toy around with the API now for a bit.

All of my functionality should be facilitated via ticket updates.

If I get really stuck I'll post another question.

You have been invaluably helpful.

Have a great weekend,

Jpio630

Post Reply