#!/usr/bin/env perl
#########################
use strict;
use Test::More;
use IO::Socket::UNIX qw( SOCK_STREAM SOMAXCONN );
use Data::Dumper;
use JSON::XS;
BEGIN {
eval {require threads;};
if ( $@ ) {
plan skip_all => 'need threads support for testing a real socket'
}
elsif( $^O eq 'MSWin32' ) {
plan skip_all => 'no sockets on windows';
}
else{
plan tests => 109
}
}
use File::Temp;
BEGIN { use_ok('Monitoring::Livestatus') };
#########################
# Normal Querys
#########################
my $line_seperator = 10;
my $column_seperator = 0;
my $test_data = [ ["alias","name","contacts"], # table header
["alias1","host1","contact1"], # row 1
["alias2","host2","contact2"], # row 2
["alias3","host3","contact3"], # row 3
];
my $test_hostgroups = [['']]; # test one row with no data
# expected results
my $selectall_arrayref1 = [ [ 'alias1', 'host1', 'contact1' ],
[ 'alias2', 'host2', 'contact2' ],
[ 'alias3', 'host3', 'contact3' ]
];
my $selectall_arrayref2 = [
{ 'contacts' => 'contact1', 'name' => 'host1', 'alias' => 'alias1' },
{ 'contacts' => 'contact2', 'name' => 'host2', 'alias' => 'alias2' },
{ 'contacts' => 'contact3', 'name' => 'host3', 'alias' => 'alias3' }
];
my $selectall_hashref = {
'host1' => { 'contacts' => 'contact1', 'name' => 'host1', 'alias' => 'alias1' },
'host2' => { 'contacts' => 'contact2', 'name' => 'host2', 'alias' => 'alias2' },
'host3' => { 'contacts' => 'contact3', 'name' => 'host3', 'alias' => 'alias3' }
};
my $selectcol_arrayref1 = [ 'alias1', 'alias2', 'alias3' ];
my $selectcol_arrayref2 = [ 'alias1', 'host1', 'alias2', 'host2', 'alias3', 'host3' ];
my $selectcol_arrayref3 = [ 'alias1', 'host1', 'contact1', 'alias2', 'host2', 'contact2', 'alias3', 'host3', 'contact3' ];
my @selectrow_array = ( 'alias1', 'host1', 'contact1' );
my $selectrow_arrayref = [ 'alias1', 'host1', 'contact1' ];
my $selectrow_hashref = { 'contacts' => 'contact1', 'name' => 'host1', 'alias' => 'alias1' };
#########################
# Single Querys
#########################
my $single_statement = "GET hosts\nColumns: alias\nFilter: name = host1";
my $selectscalar_value = 'alias1';
#########################
# Stats Querys
#########################
my $stats_statement = "GET services\nStats: state = 0\nStats: state = 1\nStats: state = 2\nStats: state = 3";
my $stats_data = [[4297,13,9,0]];
# expected results
my $stats_selectall_arrayref1 = [ [4297,13,9,0] ];
my $stats_selectall_arrayref2 = [ { 'state = 0' => '4297', 'state = 1' => '13', 'state = 2' => '9', 'state = 3' => 0 } ];
my $stats_selectcol_arrayref = [ '4297' ];
my @stats_selectrow_array = ( '4297', '13', '9', '0' );
my $stats_selectrow_arrayref = [ '4297', '13', '9', '0' ];
my $stats_selectrow_hashref = { 'state = 0' => '4297', 'state = 1' => '13', 'state = 2' => '9', 'state = 3' => 0 };
#########################
# Empty Querys
#########################
my $empty_statement = "GET services\nFilter: description = empty";
# expected results
my $empty_selectall_arrayref = [];
my $empty_selectcol_arrayref = [];
my @empty_selectrow_array;
my $empty_selectrow_arrayref;
my $empty_selectrow_hashref;
#########################
# get a temp file from File::Temp and replace it with our socket
my $fh = File::Temp->new(UNLINK => 0);
my $socket_path = $fh->filename;
unlink($socket_path);
my $thr1 = threads->create('create_socket', 'unix');
#########################
# get a temp file from File::Temp and replace it with our socket
my $server = 'localhost:32987';
my $thr2 = threads->create('create_socket', 'inet');
sleep(1);
#########################
my $objects_to_test = {
# create unix object with hash args
'unix_hash_args' => Monitoring::Livestatus->new(
verbose => 0,
socket => $socket_path,
line_seperator => $line_seperator,
column_seperator => $column_seperator,
),
# create unix object with a single arg
'unix_single_arg' => Monitoring::Livestatus::UNIX->new( $socket_path ),
# create inet object with hash args
'inet_hash_args' => Monitoring::Livestatus->new(
verbose => 0,
server => $server,
line_seperator => $line_seperator,
column_seperator => $column_seperator,
),
# create inet object with a single arg
'inet_single_arg' => Monitoring::Livestatus::INET->new( $server ),
};
for my $key (keys %{$objects_to_test}) {
my $ml = $objects_to_test->{$key};
isa_ok($ml, 'Monitoring::Livestatus');
# we dont need warnings for testing
$ml->warnings(0);
##################################################
# test settings
my $rt = $ml->verbose(1);
is($rt, '0', 'enable verbose');
$rt = $ml->verbose(0);
is($rt, '1', 'disable verbose');
$rt = $ml->errors_are_fatal(0);
is($rt, '1', 'disable errors_are_fatal');
$rt = $ml->errors_are_fatal(1);
is($rt, '0', 'enable errors_are_fatal');
##################################################
# do some sample querys
my $statement = "GET hosts";
#########################
my $ary_ref = $ml->selectall_arrayref($statement);
is_deeply($ary_ref, $selectall_arrayref1, 'selectall_arrayref($statement)')
or diag("got: ".Dumper($ary_ref)."\nbut expected ".Dumper($selectall_arrayref1));
#########################
$ary_ref = $ml->selectall_arrayref($statement, { Slice => {} });
is_deeply($ary_ref, $selectall_arrayref2, 'selectall_arrayref($statement, { Slice => {} })')
or diag("got: ".Dumper($ary_ref)."\nbut expected ".Dumper($selectall_arrayref2));
#########################
my $hash_ref = $ml->selectall_hashref($statement, 'name');
is_deeply($hash_ref, $selectall_hashref, 'selectall_hashref($statement, "name")')
or diag("got: ".Dumper($hash_ref)."\nbut expected ".Dumper($selectall_hashref));
#########################
$ary_ref = $ml->selectcol_arrayref($statement);
is_deeply($ary_ref, $selectcol_arrayref1, 'selectcol_arrayref($statement)')
or diag("got: ".Dumper($ary_ref)."\nbut expected ".Dumper($selectcol_arrayref1));
#########################
$ary_ref = $ml->selectcol_arrayref($statement, { Columns=>[1,2] });
is_deeply($ary_ref, $selectcol_arrayref2, 'selectcol_arrayref($statement, { Columns=>[1,2] })')
or diag("got: ".Dumper($ary_ref)."\nbut expected ".Dumper($selectcol_arrayref2));
$ary_ref = $ml->selectcol_arrayref($statement, { Columns=>[1,2,3] });
is_deeply($ary_ref, $selectcol_arrayref3, 'selectcol_arrayref($statement, { Columns=>[1,2,3] })')
or diag("got: ".Dumper($ary_ref)."\nbut expected ".Dumper($selectcol_arrayref3));
#########################
my @row_ary = $ml->selectrow_array($statement);
is_deeply(\@row_ary, \@selectrow_array, 'selectrow_array($statement)')
or diag("got: ".Dumper(\@row_ary)."\nbut expected ".Dumper(\@selectrow_array));
#########################
$ary_ref = $ml->selectrow_arrayref($statement);
is_deeply($ary_ref, $selectrow_arrayref, 'selectrow_arrayref($statement)')
or diag("got: ".Dumper($ary_ref)."\nbut expected ".Dumper($selectrow_arrayref));
#########################
$hash_ref = $ml->selectrow_hashref($statement);
is_deeply($hash_ref, $selectrow_hashref, 'selectrow_hashref($statement)')
or diag("got: ".Dumper($hash_ref)."\nbut expected ".Dumper($selectrow_hashref));
##################################################
# stats querys
##################################################
$ary_ref = $ml->selectall_arrayref($stats_statement);
is_deeply($ary_ref, $stats_selectall_arrayref1, 'selectall_arrayref($stats_statement)')
or diag("got: ".Dumper($ary_ref)."\nbut expected ".Dumper($stats_selectall_arrayref1));
$ary_ref = $ml->selectall_arrayref($stats_statement, { Slice => {} });
is_deeply($ary_ref, $stats_selectall_arrayref2, 'selectall_arrayref($stats_statement, { Slice => {} })')
or diag("got: ".Dumper($ary_ref)."\nbut expected ".Dumper($stats_selectall_arrayref2));
$ary_ref = $ml->selectcol_arrayref($stats_statement);
is_deeply($ary_ref, $stats_selectcol_arrayref, 'selectcol_arrayref($stats_statement)')
or diag("got: ".Dumper($ary_ref)."\nbut expected ".Dumper($stats_selectcol_arrayref));
@row_ary = $ml->selectrow_array($stats_statement);
is_deeply(\@row_ary, \@stats_selectrow_array, 'selectrow_arrayref($stats_statement)')
or diag("got: ".Dumper(\@row_ary)."\nbut expected ".Dumper(\@stats_selectrow_array));
$ary_ref = $ml->selectrow_arrayref($stats_statement);
is_deeply($ary_ref, $stats_selectrow_arrayref, 'selectrow_arrayref($stats_statement)')
or diag("got: ".Dumper($ary_ref)."\nbut expected ".Dumper($stats_selectrow_arrayref));
$hash_ref = $ml->selectrow_hashref($stats_statement);
is_deeply($hash_ref, $stats_selectrow_hashref, 'selectrow_hashref($stats_statement)')
or diag("got: ".Dumper($hash_ref)."\nbut expected ".Dumper($stats_selectrow_hashref));
my $scal = $ml->selectscalar_value($single_statement);
is($scal, $selectscalar_value, 'selectscalar_value($single_statement)')
or diag("got: ".Dumper($scal)."\nbut expected ".Dumper($selectscalar_value));
##################################################
# empty querys
##################################################
$ary_ref = $ml->selectall_arrayref($empty_statement);
is_deeply($ary_ref, $empty_selectall_arrayref, 'selectall_arrayref($empty_statement)')
or diag("got: ".Dumper($ary_ref)."\nbut expected ".Dumper($empty_selectall_arrayref));
$ary_ref = $ml->selectcol_arrayref($empty_statement);
is_deeply($ary_ref, $empty_selectcol_arrayref, 'selectcol_arrayref($empty_statement)')
or diag("got: ".Dumper($ary_ref)."\nbut expected ".Dumper($empty_selectcol_arrayref));
@row_ary = $ml->selectrow_array($empty_statement);
is_deeply(\@row_ary, \@empty_selectrow_array, 'selectrow_arrayref($empty_statement)')
or diag("got: ".Dumper(\@row_ary)."\nbut expected ".Dumper(\@empty_selectrow_array));
$ary_ref = $ml->selectrow_arrayref($empty_statement);
is_deeply($ary_ref, $empty_selectrow_arrayref, 'selectrow_arrayref($empty_statement)')
or diag("got: ".Dumper($ary_ref)."\nbut expected ".Dumper($empty_selectrow_arrayref));
$hash_ref = $ml->selectrow_hashref($empty_statement);
is_deeply($hash_ref, $empty_selectrow_hashref, 'selectrow_hashref($empty_statement)')
or diag("got: ".Dumper($hash_ref)."\nbut expected ".Dumper($empty_selectrow_hashref));
##################################################
# empty rows and columns
##################################################
my $empty_hostgroups_stm = "GET hostgroups\nColumns: members";
$ary_ref = $ml->selectall_arrayref($empty_hostgroups_stm);
is_deeply($ary_ref, $test_hostgroups, 'selectall_arrayref($empty_hostgroups_stm)')
or diag("got: ".Dumper($ary_ref)."\nbut expected ".Dumper($test_hostgroups));
}
##################################################
# exit threads
$thr1->kill('KILL')->detach();
$thr2->kill('KILL')->detach();
exit;
#########################
# SUBS
#########################
# test socket server
sub create_socket {
my $type = shift;
my $listener;
$SIG{'KILL'} = sub { threads->exit(); };
if($type eq 'unix') {
print "creating unix socket\n";
$listener = IO::Socket::UNIX->new(
Type => SOCK_STREAM,
Listen => SOMAXCONN,
Local => $socket_path,
) or die("failed to open $socket_path as test socket: $!");
}
elsif($type eq 'inet') {
print "creating tcp socket\n";
$listener = IO::Socket::INET->new(
LocalAddr => $server,
Proto => 'tcp',
Listen => 1,
Reuse => 1,
) or die("failed to listen on $server: $!");
} else {
die("unknown type");
}
while( my $socket = $listener->accept() or die('cannot accept: $!') ) {
my $recv = "";
while(<$socket>) { $recv .= $_; last if $_ eq "\n" }
my $data;
my $status = 200;
if($recv =~ m/^GET .*?\s+Filter:.*?empty/m) {
$data = '';
}
elsif($recv =~ m/^GET hosts\s+Columns: alias/m) {
my @data = @{$test_data}[1..3];
$data = encode_json(\@data)."\n";
}
elsif($recv =~ m/^GET hosts\s+Columns: name/m) {
$data = encode_json(\@{$test_data}[1..3])."\n";
}
elsif($recv =~ m/^GET hosts/) {
$data = encode_json($test_data)."\n";
}
elsif($recv =~ m/^GET hostgroups/) {
$data = encode_json(\@{$test_hostgroups})."\n";
}
elsif($recv =~ m/^GET services/ and $recv =~ m/Stats:/m) {
$data = encode_json(\@{$stats_data})."\n";
}
my $content_length = sprintf("%11s", length($data));
print $socket $status." ".$content_length."\n";
print $socket $data;
close($socket);
}
unlink($socket_path);
}