#!/usr/bin/env perl ######################### use strict; use Test::More; use Data::Dumper; if ( ! defined $ENV{TEST_SOCKET} or !defined $ENV{TEST_SERVER} ) { my $msg = 'Author test. Set $ENV{TEST_SOCKET} and $ENV{TEST_SERVER} to run'; plan( skip_all => $msg ); } else { plan( tests => 727 ); } # set an alarm my $lastquery; $SIG{ALRM} = sub { my @caller = caller; print STDERR 'last query: '.$lastquery if defined $lastquery; die "timeout reached:".Dumper(\@caller)."\n" }; alarm(120); use_ok('Monitoring::Livestatus'); ######################### my $line_seperator = 10; my $column_seperator = 0; my $objects_to_test = { # UNIX # create unix object with a single arg # '01 unix_single_arg' => Monitoring::Livestatus::UNIX->new( $ENV{TEST_SOCKET} ), # create unix object with hash args '02 unix_few_args' => Monitoring::Livestatus->new( #verbose => 1, socket => $ENV{TEST_SOCKET}, line_seperator => $line_seperator, column_seperator => $column_seperator, ), # create unix object with hash args '03 unix_keepalive' => Monitoring::Livestatus->new( verbose => 0, socket => $ENV{TEST_SOCKET}, keepalive => 1, ), # TCP # create inet object with a single arg '04 inet_single_arg' => Monitoring::Livestatus::INET->new( $ENV{TEST_SERVER} ), # create inet object with hash args '05 inet_few_args' => Monitoring::Livestatus->new( verbose => 0, server => $ENV{TEST_SERVER}, line_seperator => $line_seperator, column_seperator => $column_seperator, ), # create inet object with keepalive '06 inet_keepalive' => Monitoring::Livestatus->new( verbose => 0, server => $ENV{TEST_SERVER}, keepalive => 1, ), # create multi single args '07 multi_keepalive' => Monitoring::Livestatus->new( [ $ENV{TEST_SERVER}, $ENV{TEST_SOCKET} ] ), # create multi object with keepalive '08 multi_keepalive_hash_args' => Monitoring::Livestatus->new( verbose => 0, peer => [ $ENV{TEST_SERVER}, $ENV{TEST_SOCKET} ], keepalive => 1, ), # create multi object without keepalive '09 multi_no_keepalive' => Monitoring::Livestatus->new( peer => [ $ENV{TEST_SERVER}, $ENV{TEST_SOCKET} ], keepalive => 0, ), # create multi object without threads '10 multi_no_threads' => Monitoring::Livestatus->new( peer => [ $ENV{TEST_SERVER}, $ENV{TEST_SOCKET} ], use_threads => 0, ), # create multi object with only one peer '11 multi_one_peer' => Monitoring::Livestatus::MULTI->new( peer => $ENV{TEST_SERVER}, ), # create multi object without threads '12 multi_two_peers' => Monitoring::Livestatus::MULTI->new( peer => [ $ENV{TEST_SERVER}, $ENV{TEST_SOCKET} ], ), }; my $expected_keys = { 'columns' => [ 'description','name','table','type' ], 'commands' => [ 'line','name' ], 'comments' => [ '__all_from_hosts__', '__all_from_services__', 'author','comment','entry_time','entry_type','expire_time','expires', 'id','persistent', 'source','type' ], 'contacts' => [ 'address1','address2','address3','address4','address5','address6','alias', 'can_submit_commands','custom_variable_names','custom_variable_values','email', 'host_notification_period','host_notifications_enabled','in_host_notification_period', 'in_service_notification_period','name','modified_attributes','modified_attributes_list', 'pager','service_notification_period','service_notifications_enabled' ], 'contactgroups' => [ 'name', 'alias', 'members' ], 'downtimes' => [ '__all_from_hosts__', '__all_from_services__', 'author','comment','duration','end_time','entry_time','fixed','id','start_time', 'triggered_by','type' ], 'hostgroups' => [ 'action_url','alias','members','name','members_with_state','notes','notes_url','num_hosts','num_hosts_down', 'num_hosts_pending','num_hosts_unreach','num_hosts_up','num_services','num_services_crit', 'num_services_hard_crit','num_services_hard_ok','num_services_hard_unknown', 'num_services_hard_warn','num_services_ok','num_services_pending','num_services_unknown', 'num_services_warn','worst_host_state','worst_service_hard_state','worst_service_state' ], 'hosts' => [ 'accept_passive_checks','acknowledged','acknowledgement_type','action_url','action_url_expanded', 'active_checks_enabled','address','alias','check_command','check_freshness','check_interval', 'check_options','check_period','check_type','checks_enabled','childs','comments','comments_with_info', 'contacts','current_attempt','current_notification_number','custom_variable_names', 'custom_variable_values','display_name','downtimes','downtimes_with_info','event_handler_enabled', 'execution_time','first_notification_delay','flap_detection_enabled','groups','hard_state','has_been_checked', 'high_flap_threshold','icon_image','icon_image_alt','icon_image_expanded','in_check_period', 'in_notification_period','initial_state','is_executing','is_flapping','last_check','last_hard_state', 'last_hard_state_change','last_notification','last_state','last_state_change','latency','last_time_down', 'last_time_unreachable','last_time_up','long_plugin_output','low_flap_threshold','max_check_attempts','name', 'modified_attributes','modified_attributes_list','next_check', 'next_notification','notes','notes_expanded','notes_url','notes_url_expanded','notification_interval', 'notification_period','notifications_enabled','num_services','num_services_crit','num_services_hard_crit', 'num_services_hard_ok','num_services_hard_unknown','num_services_hard_warn','num_services_ok', 'num_services_pending','num_services_unknown','num_services_warn','obsess_over_host','parents', 'pending_flex_downtime','percent_state_change','perf_data','plugin_output', 'process_performance_data','retry_interval','scheduled_downtime_depth','services','services_with_state', 'state','state_type','statusmap_image','total_services','worst_service_hard_state','worst_service_state', 'x_3d','y_3d','z_3d' ], 'hostsbygroup' => [ '__all_from_hosts__', '__all_from_hostgroups__' ], 'log' => [ '__all_from_hosts__','__all_from_services__','__all_from_contacts__','__all_from_commands__', 'attempt','class','command_name','comment','contact_name','host_name','lineno','message','options', 'plugin_output','service_description','state','state_type','time','type' ], 'servicegroups' => [ 'action_url','alias','members','name','members_with_state','notes','notes_url','num_services','num_services_crit', 'num_services_hard_crit','num_services_hard_ok','num_services_hard_unknown', 'num_services_hard_warn','num_services_ok','num_services_pending','num_services_unknown', 'num_services_warn','worst_service_state' ], 'servicesbygroup' => [ '__all_from_services__', '__all_from_hosts__', '__all_from_servicegroups__' ], 'services' => [ '__all_from_hosts__', 'accept_passive_checks','acknowledged','acknowledgement_type','action_url','action_url_expanded', 'active_checks_enabled','check_command','check_interval','check_options','check_period', 'check_type','checks_enabled','comments','comments_with_info','contacts','current_attempt', 'current_notification_number','custom_variable_names','custom_variable_values', 'description','display_name','downtimes','downtimes_with_info','event_handler','event_handler_enabled', 'execution_time','first_notification_delay','flap_detection_enabled','groups', 'has_been_checked','high_flap_threshold','icon_image','icon_image_alt','icon_image_expanded','in_check_period', 'in_notification_period','initial_state','is_executing','is_flapping','last_check', 'last_hard_state','last_hard_state_change','last_notification','last_state', 'last_state_change','latency','last_time_critical','last_time_ok','last_time_unknown','last_time_warning', 'long_plugin_output','low_flap_threshold','max_check_attempts','modified_attributes','modified_attributes_list', 'next_check','next_notification','notes','notes_expanded','notes_url','notes_url_expanded', 'notification_interval','notification_period','notifications_enabled','obsess_over_service', 'percent_state_change','perf_data','plugin_output','process_performance_data','retry_interval', 'scheduled_downtime_depth','state','state_type' ], 'servicesbyhostgroup' => [ '__all_from_services__', '__all_from_hosts__', '__all_from_hostgroups__' ], 'status' => [ 'accept_passive_host_checks','accept_passive_service_checks','cached_log_messages', 'check_external_commands','check_host_freshness','check_service_freshness','connections', 'connections_rate','enable_event_handlers','enable_flap_detection','enable_notifications', 'execute_host_checks','execute_service_checks','forks','forks_rate','host_checks','host_checks_rate','interval_length', 'last_command_check','last_log_rotation','livestatus_version','log_messages','log_messages_rate','nagios_pid','neb_callbacks', 'neb_callbacks_rate','obsess_over_hosts','obsess_over_services','process_performance_data', 'program_start','program_version','requests','requests_rate','service_checks','service_checks_rate' ], 'timeperiods' => [ 'in', 'name', 'alias' ], }; my $author = 'Monitoring::Livestatus test'; for my $key (sort keys %{$objects_to_test}) { my $ml = $objects_to_test->{$key}; isa_ok($ml, 'Monitoring::Livestatus') or BAIL_OUT("no need to continue without a proper Monitoring::Livestatus object: ".$key); # dont die on errors $ml->errors_are_fatal(0); $ml->warnings(0); ######################### # set downtime for a host and service my $downtimes = $ml->selectall_arrayref("GET downtimes\nColumns: id"); my $num_downtimes = 0; $num_downtimes = scalar @{$downtimes} if defined $downtimes; my $firsthost = $ml->selectscalar_value("GET hosts\nColumns: name\nLimit: 1"); isnt($firsthost, undef, 'get test hostname') or BAIL_OUT($key.': got not test hostname'); $ml->do('COMMAND ['.time().'] SCHEDULE_HOST_DOWNTIME;'.$firsthost.';'.time().';'.(time()+300).';1;0;300;'.$author.';perl test: '.$0); my $firstservice = $ml->selectscalar_value("GET services\nColumns: description\nFilter: host_name = $firsthost\nLimit: 1"); isnt($firstservice, undef, 'get test servicename') or BAIL_OUT('got not test servicename'); $ml->do('COMMAND ['.time().'] SCHEDULE_SVC_DOWNTIME;'.$firsthost.';'.$firstservice.';'.time().';'.(time()+300).';1;0;300;'.$author.';perl test: '.$0); # sometimes it takes while till the downtime is accepted my $waited = 0; while(scalar @{$ml->selectall_arrayref("GET downtimes\nColumns: id")} < $num_downtimes + 2) { print "waiting for the downtime...\n"; sleep(1); $waited++; BAIL_OUT('waited 30 seconds for the downtime...') if $waited > 30; } ######################### ######################### # check tables my $data = $ml->selectall_hashref("GET columns\nColumns: table", 'table'); my @tables = sort keys %{$data}; my @expected_tables = sort keys %{$expected_keys}; is_deeply(\@tables, \@expected_tables, $key.' tables') or BAIL_OUT("got tables:\n".join(', ', @tables)."\nbut expected\n".join(', ', @expected_tables)); ######################### # check keys for my $type (keys %{$expected_keys}) { my $filter = ""; $filter = "Filter: time > ".(time() - 86400)."\n" if $type eq 'log'; $filter .= "Filter: time < ".(time())."\n" if $type eq 'log'; my $expected_keys = get_expected_keys($type); my $statement = "GET $type\n".$filter."Limit: 1"; $lastquery = $statement; my $hash_ref = $ml->selectrow_hashref($statement ); undef $lastquery; is(ref $hash_ref, 'HASH', $type.' keys are a hash') or BAIL_OUT($type.'keys are not in hash format, got '.Dumper($hash_ref)); my @keys = sort keys %{$hash_ref}; is_deeply(\@keys, $expected_keys, $key.' '.$type.' table columns') or BAIL_OUT("got $type keys:\n".join(', ', @keys)."\nbut expected\n".join(', ', @{$expected_keys})); } my $statement = "GET hosts\nColumns: name as hostname state\nLimit: 1"; $lastquery = $statement; my $hash_ref = $ml->selectrow_hashref($statement); undef $lastquery; isnt($hash_ref, undef, $key.' test column alias'); is($Monitoring::Livestatus::ErrorCode, 0, $key.' test column alias') or diag('got error: '.$Monitoring::Livestatus::ErrorMessage); ######################### # send a test command # commands still dont work and breaks livestatus my $rt = $ml->do('COMMAND ['.time().'] SAVE_STATE_INFORMATION'); is($rt, '1', $key.' test command'); ######################### # check for errors #$ml->{'verbose'} = 1; $statement = "GET hosts\nLimit: 1"; $lastquery = $statement; $hash_ref = $ml->selectrow_hashref($statement ); undef $lastquery; isnt($hash_ref, undef, $key.' test error 200 body'); is($Monitoring::Livestatus::ErrorCode, 0, $key.' test error 200 status') or diag('got error: '.$Monitoring::Livestatus::ErrorMessage); $statement = "BLAH hosts"; $lastquery = $statement; $hash_ref = $ml->selectrow_hashref($statement ); undef $lastquery; is($hash_ref, undef, $key.' test error 401 body'); is($Monitoring::Livestatus::ErrorCode, '401', $key.' test error 401 status') or diag('got error: '.$Monitoring::Livestatus::ErrorMessage); $statement = "GET hosts\nLimit: "; $lastquery = $statement; $hash_ref = $ml->selectrow_hashref($statement ); undef $lastquery; is($hash_ref, undef, $key.' test error 403 body'); is($Monitoring::Livestatus::ErrorCode, '403', $key.' test error 403 status') or diag('got error: '.$Monitoring::Livestatus::ErrorMessage); $statement = "GET unknowntable\nLimit: 1"; $lastquery = $statement; $hash_ref = $ml->selectrow_hashref($statement ); undef $lastquery; is($hash_ref, undef, $key.' test error 404 body'); is($Monitoring::Livestatus::ErrorCode, '404', $key.' test error 404 status') or diag('got error: '.$Monitoring::Livestatus::ErrorMessage); $statement = "GET hosts\nColumns: unknown"; $lastquery = $statement; $hash_ref = $ml->selectrow_hashref($statement ); undef $lastquery; is($hash_ref, undef, $key.' test error 405 body'); TODO: { local $TODO = 'livestatus returns wrong status'; is($Monitoring::Livestatus::ErrorCode, '405', $key.' test error 405 status') or diag('got error: '.$Monitoring::Livestatus::ErrorMessage); }; ######################### # some more broken statements $statement = "GET "; $lastquery = $statement; $hash_ref = $ml->selectrow_hashref($statement); undef $lastquery; is($hash_ref, undef, $key.' test error 403 body'); is($Monitoring::Livestatus::ErrorCode, '403', $key.' test error 403 status: GET ') or diag('got error: '.$Monitoring::Livestatus::ErrorMessage); $statement = "GET hosts\nColumns: name, name"; $lastquery = $statement; $hash_ref = $ml->selectrow_hashref($statement ); undef $lastquery; is($hash_ref, undef, $key.' test error 405 body'); is($Monitoring::Livestatus::ErrorCode, '405', $key.' test error 405 status: GET hosts\nColumns: name, name') or diag('got error: '.$Monitoring::Livestatus::ErrorMessage); $statement = "GET hosts\nColumns: "; $lastquery = $statement; $hash_ref = $ml->selectrow_hashref($statement ); undef $lastquery; is($hash_ref, undef, $key.' test error 405 body'); is($Monitoring::Livestatus::ErrorCode, '405', $key.' test error 405 status: GET hosts\nColumns: ') or diag('got error: '.$Monitoring::Livestatus::ErrorMessage); ######################### # some forbidden headers $statement = "GET hosts\nKeepAlive: on"; $lastquery = $statement; $hash_ref = $ml->selectrow_hashref($statement ); undef $lastquery; is($hash_ref, undef, $key.' test error 496 body'); is($Monitoring::Livestatus::ErrorCode, '496', $key.' test error 496 status: KeepAlive: on') or diag('got error: '.$Monitoring::Livestatus::ErrorMessage); $statement = "GET hosts\nResponseHeader: fixed16"; $lastquery = $statement; $hash_ref = $ml->selectrow_hashref($statement ); undef $lastquery; is($hash_ref, undef, $key.' test error 495 body'); is($Monitoring::Livestatus::ErrorCode, '495', $key.' test error 495 status: ResponseHeader: fixed16') or diag('got error: '.$Monitoring::Livestatus::ErrorMessage); $statement = "GET hosts\nColumnHeaders: on"; $lastquery = $statement; $hash_ref = $ml->selectrow_hashref($statement ); undef $lastquery; is($hash_ref, undef, $key.' test error 494 body'); is($Monitoring::Livestatus::ErrorCode, '494', $key.' test error 494 status: ColumnHeader: on') or diag('got error: '.$Monitoring::Livestatus::ErrorMessage); $statement = "GET hosts\nOuputFormat: json"; $lastquery = $statement; $hash_ref = $ml->selectrow_hashref($statement ); undef $lastquery; is($hash_ref, undef, $key.' test error 493 body'); is($Monitoring::Livestatus::ErrorCode, '493', $key.' test error 493 status: OutputForma: json') or diag('got error: '.$Monitoring::Livestatus::ErrorMessage); $statement = "GET hosts\nSeparators: 0 1 2 3"; $lastquery = $statement; $hash_ref = $ml->selectrow_hashref($statement ); undef $lastquery; is($hash_ref, undef, $key.' test error 492 body'); is($Monitoring::Livestatus::ErrorCode, '492', $key.' test error 492 status: Seperators: 0 1 2 3') or diag('got error: '.$Monitoring::Livestatus::ErrorMessage); ######################### # check some fancy stats queries my $stats_query = "GET services Stats: state = 0 as all_ok Stats: state = 1 as all_warning Stats: state = 2 as all_critical Stats: state = 3 as all_unknown Stats: state = 4 as all_pending Stats: host_state != 0 Stats: state = 1 StatsAnd: 2 as all_warning_on_down_hosts Stats: host_state != 0 Stats: state = 2 StatsAnd: 2 as all_critical_on_down_hosts Stats: host_state != 0 Stats: state = 3 StatsAnd: 2 as all_unknown_on_down_hosts Stats: host_state != 0 Stats: state = 3 Stats: active_checks_enabled = 1 StatsAnd: 3 as all_unknown_active_on_down_hosts Stats: state = 3 Stats: active_checks_enabled = 1 StatsOr: 2 as all_active_or_unknown"; $lastquery = $stats_query; $hash_ref = $ml->selectrow_hashref($stats_query ); undef $lastquery; isnt($hash_ref, undef, $key.' test fancy stats query') or diag('got error: '.Dumper($hash_ref)); } # generate expected keys sub get_expected_keys { my $type = shift; my $skip = shift; my @keys = @{$expected_keys->{$type}}; my @new_keys; for my $key (@keys) { my $replaced = 0; for my $replace_with (keys %{$expected_keys}) { if($key eq '__all_from_'.$replace_with.'__') { $replaced = 1; next if $skip; my $prefix = $replace_with.'_'; if($replace_with eq "hosts") { $prefix = 'host_'; } if($replace_with eq "services") { $prefix = 'service_'; } if($replace_with eq "commands") { $prefix = 'command_'; } if($replace_with eq "contacts") { $prefix = 'contact_'; } if($replace_with eq "servicegroups") { $prefix = 'servicegroup_'; } if($replace_with eq "hostgroups") { $prefix = 'hostgroup_'; } if($type eq "log") { $prefix = 'current_'.$prefix; } if($type eq "servicesbygroup" and $replace_with eq 'services') { $prefix = ''; } if($type eq "servicesbyhostgroup" and $replace_with eq 'services') { $prefix = ''; } if($type eq "hostsbygroup" and $replace_with eq 'hosts') { $prefix = ''; } my $replace_keys = get_expected_keys($replace_with, 1); for my $key2 (@{$replace_keys}) { push @new_keys, $prefix.$key2; } } } if($replaced == 0) { push @new_keys, $key; } } # has been fixed in 1.1.1rc #if($type eq 'log') { # my %keys = map { $_ => 1 } @new_keys; # delete $keys{'current_contact_can_submit_commands'}; # delete $keys{'current_contact_host_notifications_enabled'}; # delete $keys{'current_contact_in_host_notification_period'}; # delete $keys{'current_contact_in_service_notification_period'}; # delete $keys{'current_contact_service_notifications_enabled'}; # @new_keys = keys %keys; #} my @return = sort @new_keys; return(\@return); }