Skip to content

Commit

Permalink
add support for simple calculations in columns
Browse files Browse the repository at this point in the history
for example:

  thruk r "/hosts?columns=num_services_crit+num_services_unknown+num_services_warn as num_services_problems"

summing up some numbers.
  • Loading branch information
sni committed Sep 20, 2024
1 parent 2885120 commit 7ef2135
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 3 deletions.
1 change: 1 addition & 0 deletions Changes
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ next:
ex.: /hosts/<name>/cmd/note
and /services/<host>/<service>/cmd/note
- add new endpoint /system/cmd/log
- add support for simple calculations in columns

3.18 Tue Sep 10 22:30:40 CEST 2024
- add optional oauth 2.1 pkce workflow
Expand Down
70 changes: 68 additions & 2 deletions lib/Thruk/Controller/rest_v1.pm
Original file line number Diff line number Diff line change
Expand Up @@ -1031,15 +1031,15 @@ sub _get_transformed_row_value {

my $val = $row->{$col->{'orig'}} // $row->{$col->{'column'}};
for my $f (@{$col->{'func'}}) {
$val = _apply_data_function($col, $f, $val);
$val = _apply_data_function($col, $f, $val, $row);
}

return $val;
}

##########################################################
sub _apply_data_function {
my($col, $f, $val) = @_;
my($col, $f, $val, $row) = @_;
my($name, $args) = @{$f};
if($name eq 'lower' || $name eq 'lc') {
return lc($val // '');
Expand Down Expand Up @@ -1091,12 +1091,73 @@ sub _apply_data_function {
return $val;
}

# more generic calculation function
if($name eq '_calc') {
return(&_apply_calc_column($args->[0], $row));
}

return $val if $aggregation_functions->{$name};
return $val if $disaggregation_functions->{$name};

die("unknown function: ".$name);
}

##########################################################
sub _apply_calc_column {
my($expr, $row) = @_;

# split expr into token
my @token = split(/\s*([\+\*\-\/])\s*/mx, $expr);
return 0 unless scalar @token > 0;
my $first = shift @token;
my $res = &_extract_val($first, $row);
while(scalar @token > 1) {
my $op = shift @token;
my $num = shift @token;
my $val = &_extract_val($num, $row);
if( $op eq '+') { $res += $val; }
elsif($op eq '-') { $res -= $val; }
elsif($op eq '*') { $res *= $val; }
elsif($op eq '/') { $res = $res / $val; }
else { die("unknown operator: $op in '$expr'"); }
}

die("missing value at end of expression in '$expr'") if scalar @token > 0;

return($res);
}

##########################################################
sub _extract_val {
my($name, $row) = @_;
return($row->{$name}) if exists $row->{$name};
return(0+$name) if $name =~ m/^(\d+)|(\d+\.\d+)$/mx;
return(0);
}

##########################################################
# returns columns required from calculations
sub _get_calc_required_columns {
my($c) = @_;

my $extra = [];
for my $col (@{get_request_columns($c, STRUCT) || []}) {
if(scalar @{$col->{'func'}} > 0 && $col->{'func'}->[0]->[0] eq '_calc') {
my $arg = $col->{'func'}->[0]->[1]->[0];
my @token = split(/\s*([\+\*\-\/])\s*/mx, $arg);
for my $t (@token) {
next if $t eq '+';
next if $t eq '-';
next if $t eq '*';
next if $t eq '/';
push @{$extra}, $t;
}
}
}

return $extra;
}

##########################################################
sub _apply_sort {
my($c, $data) = @_;
Expand Down Expand Up @@ -1196,6 +1257,7 @@ sub _livestatus_options {
}
if(scalar @{$columns} > 0) {
push @{$columns}, @{get_filter_columns($c)};
push @{$columns}, @{_get_calc_required_columns($c)};
$columns = Thruk::Base::array_uniq($columns);
my $ref_columns;
if($type eq 'hosts') {
Expand Down Expand Up @@ -2831,6 +2893,10 @@ sub _parse_columns_data {
$name = shift @arg;
push(@{$functions}, [$f, \@arg]);
}
if(scalar @{$functions} == 0 && $name =~ m/[\+\*\-\/]/gmx) {
my $f = "_calc";
push(@{$functions}, [$f, [$name]]);
}
# functions must be applied in reverse order
@{$functions} = reverse @{$functions};
push @{$columns}, {
Expand Down
4 changes: 4 additions & 0 deletions lib/Thruk/Utils/CLI/Rest.pm
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ sub _fetch_results {
_debug($opt->{'result'});
next;
}

# plus symbols from the command line are probably meant as plus
# is a space is meant, simply use a space or %20
$url =~ s/\+/%2B/gmx;
}

$url =~ s|^/||gmx;
Expand Down
10 changes: 9 additions & 1 deletion t/scenarios/rest_api/t/local/rest.t
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ BEGIN {
import TestUtils;
}

plan tests => 9;
plan tests => 13;

###########################################################
# test thruks script path
Expand All @@ -25,3 +25,11 @@ TestUtils::test_command({
like => ['/"critical_and_unhandled"/', '/"down_and_unhandled"/'],
});
};

###########################################################
{
TestUtils::test_command({
cmd => '/usr/bin/env thruk r "/hosts?columns=num_services_crit+num_services_unknown+num_services_warn as num_services_problems"',
like => ['/"num_services_problems" : 3/'],
});
};

0 comments on commit 7ef2135

Please sign in to comment.