# --
# Copyright (C) 2001-2019 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::CloudService::Backend::Run;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Cache',
'Kernel::System::Encode',
'Kernel::System::JSON',
'Kernel::System::Log',
'Kernel::System::OTRSBusiness',
'Kernel::System::SystemData',
'Kernel::System::WebUserAgent',
);
=head1 NAME
Kernel::System::CloudService::Backend::Run - cloud service lib
=head1 DESCRIPTION
All functions for cloud service communication.
=head1 PUBLIC INTERFACE
=head2 new()
create a CloudService object. Do not use it directly, instead use:
my $CloudServiceObject = $Kernel::OM->Get('Kernel::System::CloudService::Backend::Run');
=cut
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
# set system registration data
%{ $Self->{RegistrationData} } =
$Kernel::OM->Get('Kernel::System::SystemData')->SystemDataGroupGet(
Group => 'Registration',
UserID => 1,
);
# set URL for calling cloud services
$Self->{CloudServiceURL} = 'https://cloud.otrs.com/otrs/public.pl';
return $Self;
}
=head2 Request()
perform a cloud service communication and return result data
my $RequestResult = $CloudServiceObject->Request(
OTRSIDAuth => { # will be send encoded as JSON
OTRSID => '',
Password => '',
},
UniqueIDAuth => { # will send encoded as JSON
UniqueID => '',
APIKey => '',
},
RequestData => { # this complex structure will be send encoded as JSON
CloudServiceTest => [
{
InstanceName = 'AnyName', # optional
Operation => "ConfigurationSet",
Data => {
# ... request operation data ...
},
},
{
Operation => "SomeOperation",
Data => {
# ... request operation data ...
},
},
# ... other entries may follow ...
],
FeatureAddonManagement => [
{
Operation => "FAOListAssigned",
Data => {
# ... request operation data ...
},
},
{
InstanceName = 'InstanceNameOne', # optional
Operation => "FAOGet",
Data => {
# ... request operation data ...
},
},
{
InstanceName = 'InstanceNameTwo', # optional
Operation => "FAOGet",
Data => {
# ... request operation data ...
},
},
# ... other entries may follow ...
],
# ... other entries may follow ...
},
Timeout => 15, # optional, timeout
Proxy => 'proxy.example.com', # optional, proxy
);
Returns:
$RequestResult {
Success => 1,
ErrorMessage => '...', # optional
Results => {
CloudServiceTest => [
{
Success => 1, # 1 or 0
ErrorMessage => '...', # optional
InstanceName = 'AnyName', # optional
Operation => "ConfigurationSet",
Data => {
# ... response operation data ..
},
},
{
Success => 0, # 1 or 0
ErrorMessage => '...', # optional
Operation => "SomeOperation",
Data => {
# ... response operation data ...
},
},
],
FeatureAddonManagement => [
{
Success => 1, # 1 or 0
ErrorMessage => '...', # optional
Operation => "FAOListAssigned",
Data => {
# ... response operation data ..
},
},
{
Success => 1, # 1 or 0
ErrorMessage => '...', # optional
InstanceName = 'InstanceNameOne', # optional
Operation => "FaoGet",
Data => {
# ... response operation data ...
},
},
{
Success => 0, # 1 or 0
ErrorMessage => '...', # optional
InstanceName = 'InstanceNameTwo', # optional
Operation => "FaoGet",
Data => {
# ... response operation data ...
},
},
],
},
};
=cut
sub Request {
my ( $Self, %Param ) = @_;
# create config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
# If OTRSSTORM package is installed, system is able to do a Cloud request even if CloudService is disabled.
if ( !$Kernel::OM->Get('Kernel::System::OTRSBusiness')->OTRSSTORMIsInstalled() ) {
# check if cloud services are disabled
my $CloudServicesDisabled = $ConfigObject->Get('CloudServices::Disabled');
return if $CloudServicesDisabled;
}
# check needed stuff
if ( !defined $Param{RequestData} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need RequestData!"
);
return;
}
# RequestData might be a hash
if ( !IsHashRefWithData( $Param{RequestData} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Wrong structure for RequestData, it might be an hash with data!",
);
return;
}
# check in detail each request data
for my $CloudService ( sort keys %{ $Param{RequestData} } ) {
# check if CloudService is defined and not empty
if ( !$CloudService ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "A CloudService name is needed!",
);
return;
}
# check if CloudService is defined and not empty
if ( !IsArrayRefWithData( $Param{RequestData}->{$CloudService} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "CloudService::$CloudService data structure is not valid!",
);
return;
}
for my $Instance ( @{ $Param{RequestData}->{$CloudService} } ) {
# check if Operation is present
if ( !$Instance->{Operation} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Each operation request needs a Operation name!",
);
return;
}
# if Data is present it might be a hash
if ( $Instance->{Data} && ref $Instance->{Data} ne 'HASH' ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Request Data needs to be a hash structure!",
);
return;
}
}
}
# create json object
my $JSONObject = $Kernel::OM->Get('Kernel::System::JSON');
# get UniqueIDAut structure if needed
my $UniqueIDAuth;
if (
defined $Self->{RegistrationData}->{State}
&& $Self->{RegistrationData}->{State} eq 'registered'
)
{
# create cache object
my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
# check cache
my $CacheKey = "APIKey::" . ( $Self->{RegistrationData}->{APIKey} || '' )
. "::UniqueID::" . ( $Self->{RegistrationData}->{UniqueID} || '' );
my $CacheContent = $CacheObject->Get(
Type => 'RequestUniqueIDAuth',
Key => $CacheKey,
);
if ( defined $CacheContent ) {
$UniqueIDAuth = $CacheContent;
}
else {
# create JSON string
$UniqueIDAuth = $JSONObject->Encode(
Data => {
APIKey => $Self->{RegistrationData}->{APIKey} || '',
UniqueID => $Self->{RegistrationData}->{UniqueID} || '',
},
);
$CacheObject->Set(
Type => 'RequestUniqueIDAuth',
Key => $CacheKey,
Value => $UniqueIDAuth || '',
TTL => 3 * 24 * 60 * 60,
);
}
}
# get OTRSIDAuth structure if needed
my $OTRSIDAuth = '';
if ( $Param{OTRSID} && $Param{Password} ) {
$OTRSIDAuth = $JSONObject->Encode(
Data => {
OTRSID => $Param{OTRSID},
Password => $Param{Password},
},
);
}
my $RequestTimeout = $Param{Timeout} || $ConfigObject->Get('WebUserAgent::Timeout') || 15;
my $RequestProxy = $Param{Proxy} || $ConfigObject->Get('WebUserAgent::Proxy') || '';
# set timeout setting for packages
if ( grep {/^Package/i} sort keys %{ $Param{RequestData} } ) {
$RequestTimeout = $ConfigObject->Get('Package::Timeout') || 120;
}
# create JSON string
my $RequestData = $JSONObject->Encode(
Data => $Param{RequestData},
);
# add params to webuseragent object
$Kernel::OM->ObjectParamAdd(
'Kernel::System::WebUserAgent' => {
Timeout => $RequestTimeout,
Proxy => $RequestProxy,
},
);
# Perform web service request.
my %Response;
TRY:
for my $Try ( 1 .. 3 ) {
%Response = $Kernel::OM->Get('Kernel::System::WebUserAgent')->Request(
Type => 'POST',
URL => $Self->{CloudServiceURL},
Data => {
Action => 'PublicCloudService',
RequestData => $RequestData,
UniqueIDAuth => $UniqueIDAuth,
OTRSIDAuth => $OTRSIDAuth,
},
);
last TRY if %Response
&& $Response{Status} eq '200 OK'
&& $Response{Content}
&& ref $Response{Content} eq 'SCALAR';
}
# test if the web response was successful
if ( !%Response || $Response{Status} ne '200 OK' ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'notice',
Message => "PublicCloudService - Can't connect to server - $Response{Status}",
);
return;
}
# check if we have content as a scalar ref
if ( !$Response{Content} || ref $Response{Content} ne 'SCALAR' ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'notice',
Message =>
"PublicCloudService - No content received from public cloud service. Please try again later.'",
);
return;
}
# convert internal used charset
$Kernel::OM->Get('Kernel::System::Encode')->EncodeInput(
$Response{Content},
);
# decode JSON data
my $ResponseData = $JSONObject->Decode(
Data => ${ $Response{Content} },
);
# check response structure
if ( !IsHashRefWithData($ResponseData) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Can't decode JSON data: 'PublicCloudService'!",
);
return;
}
# check if we have an error
if ( !$ResponseData->{Success} ) {
my $ResponseErrorMessage = $ResponseData->{ErrorMessage} || 'No error message available.';
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'notice',
Message => "PublicCloudService - Error from server: $ResponseErrorMessage !",
);
return;
}
# return data from server if defined
return $ResponseData->{Results} if defined $ResponseData->{Results};
return;
}
=head2 OperationResultGet()
my $OperationResult = $CloudServiceObject->OperationResultGet(
CloudService => 'Test',
Operation => 'test',
InstanceName => 'AnyName', # optional
RequestResult => {
Success => 1,
Results => {
Test => [
{
Success => 1,
InstanceName => 'AnyName',
Operation => 'Test',
Data => {
# ... response operation data ..
},
},
{
Success => 0,
ErrorMessage => 'some message',
Operation => 'SomeOperation',
Data => {
# ... response operation data ...
},
},
],
},
};
);
Returns:
$OperationResult {
Success => 1,
ErrorMessage => 'a message' # optional
InstanceName => 'AnyName',
Operation => "Test",
Data => {
# ... response operation data ..
},
},
=cut
sub OperationResultGet {
my ( $Self, %Param ) = @_;
# check needed
for my $Needed (qw(RequestResult CloudService Operation)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return {
Success => 0,
};
}
}
# check RequestResult internals
if ( !IsHashRefWithData( $Param{RequestResult} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "The format of the request result is invalid!",
);
return {
Success => 0,
};
}
if ( !$Param{RequestResult}->{ $Param{CloudService} } ) {
my $Message = "No CloudService:'$Param{CloudService}' found in the request result!";
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "$Message",
);
return {
Success => 0,
ErrorMessage => $Message,
};
}
if ( !IsArrayRefWithData( $Param{RequestResult}->{ $Param{CloudService} } ) ) {
my $Message = "Results from CloudService:'$Param{CloudService}' are invalid!";
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "$Message",
);
return {
Success => 0,
ErrorMessage => $Message,
};
}
# create a shortcut for actual cloud-service results
my @CloudServiceResults = @{ $Param{RequestResult}->{ $Param{CloudService} } };
RESULT:
for my $OperationResult (@CloudServiceResults) {
next RESULT if $OperationResult->{Operation} ne $Param{Operation};
if ( !$Param{InstanceName} ) {
next RESULT if defined $OperationResult->{InstanceName};
}
else {
if (
!defined $OperationResult->{InstanceName}
|| $OperationResult->{InstanceName} ne $Param{InstanceName}
)
{
next RESULT;
}
}
return $OperationResult;
}
# if not found return an error
my $Message = "No Results from CloudService:'$Param{CloudService}' Operation:'$Param{Operation}'";
if ( $Param{InstanceName} ) {
$Message .= " InstanceName:'$Param{InstanceName}'!";
}
else {
$Message .= " Without InstanceName!";
}
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => $Message,
);
return {
Success => 0,
Message => "No results found!",
};
}
1;
=head1 TERMS AND CONDITIONS
This software is part of the OTRS project (L).
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.
=cut