2015-08-17 14:18:07 +02:00
|
|
|
#! /usr/bin/env perl
|
2015-06-23 14:54:34 +02:00
|
|
|
|
|
|
|
use strict;
|
|
|
|
use utf8;
|
2021-03-20 06:57:37 +01:00
|
|
|
use Hydra::Helper::Nix;
|
2015-06-23 14:54:34 +02:00
|
|
|
use Net::Statsd;
|
2015-06-24 13:19:16 +02:00
|
|
|
use File::Slurp;
|
2015-06-23 14:54:34 +02:00
|
|
|
use JSON;
|
2021-03-20 09:12:02 -04:00
|
|
|
use Getopt::Long qw(:config gnu_getopt);
|
2015-06-23 14:54:34 +02:00
|
|
|
|
|
|
|
STDERR->autoflush(1);
|
|
|
|
binmode STDERR, ":encoding(utf8)";
|
|
|
|
|
2016-02-06 03:44:16 +01:00
|
|
|
my $config = getHydraConfig();
|
2021-03-20 06:57:37 +01:00
|
|
|
my $statsdConfig = getStatsdConfig($config);
|
2016-02-06 03:44:16 +01:00
|
|
|
$Net::Statsd::HOST = $statsdConfig->{'host'};
|
|
|
|
$Net::Statsd::PORT = $statsdConfig->{'port'};
|
|
|
|
|
2015-06-23 14:54:34 +02:00
|
|
|
sub gauge {
|
|
|
|
my ($name, $val) = @_;
|
|
|
|
die unless defined $val;
|
|
|
|
Net::Statsd::gauge($name, $val);
|
|
|
|
}
|
|
|
|
|
|
|
|
sub sendQueueRunnerStats {
|
|
|
|
my $s = `hydra-queue-runner --status`;
|
|
|
|
die "cannot get queue runner stats\n" if $? != 0;
|
|
|
|
|
|
|
|
my $json = decode_json($s) or die "cannot decode queue runner status";
|
|
|
|
|
2015-06-24 13:19:16 +02:00
|
|
|
gauge("hydra.queue.up", $json->{status} eq "up" ? 1 : 0);
|
|
|
|
|
2015-06-23 14:54:34 +02:00
|
|
|
return if $json->{status} ne "up";
|
|
|
|
|
|
|
|
gauge("hydra.queue.steps.active", $json->{nrActiveSteps});
|
|
|
|
gauge("hydra.queue.steps.building", $json->{nrStepsBuilding});
|
2015-06-24 13:19:16 +02:00
|
|
|
gauge("hydra.queue.steps.copying_to", $json->{nrStepsCopyingTo});
|
|
|
|
gauge("hydra.queue.steps.copying_from", $json->{nrStepsCopyingFrom});
|
2015-07-10 19:10:14 +02:00
|
|
|
gauge("hydra.queue.steps.waiting", $json->{nrStepsWaiting});
|
2015-06-23 14:54:34 +02:00
|
|
|
gauge("hydra.queue.steps.runnable", $json->{nrRunnableSteps});
|
|
|
|
gauge("hydra.queue.steps.unfinished", $json->{nrUnfinishedSteps});
|
|
|
|
gauge("hydra.queue.steps.finished", $json->{nrStepsDone});
|
|
|
|
gauge("hydra.queue.steps.retries", $json->{nrRetries});
|
2020-03-26 15:26:12 +01:00
|
|
|
gauge("hydra.queue.steps.unsupported", $json->{nrUnsupportedSteps});
|
2015-06-23 14:54:34 +02:00
|
|
|
gauge("hydra.queue.steps.max_retries", $json->{maxNrRetries});
|
|
|
|
if ($json->{nrStepsDone}) {
|
|
|
|
gauge("hydra.queue.steps.avg_total_time", $json->{avgStepTime});
|
|
|
|
gauge("hydra.queue.steps.avg_build_time", $json->{avgStepBuildTime});
|
|
|
|
}
|
2018-05-10 18:27:30 +01:00
|
|
|
foreach my $machine (keys %{$json->{machineTypes}}) {
|
|
|
|
my $machineType = $machine =~ s/:|,/_/r;
|
|
|
|
gauge("hydra.queue.$machineType.runnable", $json->{machineTypes}->{$machine}->{runnable});
|
|
|
|
gauge("hydra.queue.$machineType.running", $json->{machineTypes}->{$machine}->{running});
|
|
|
|
}
|
2015-06-23 14:54:34 +02:00
|
|
|
|
|
|
|
gauge("hydra.queue.builds.read", $json->{nrBuildsRead});
|
|
|
|
gauge("hydra.queue.builds.unfinished", $json->{nrQueuedBuilds});
|
|
|
|
gauge("hydra.queue.builds.finished", $json->{nrBuildsDone});
|
|
|
|
|
|
|
|
gauge("hydra.queue.checks", $json->{nrQueueWakeups});
|
2015-06-25 16:46:59 +02:00
|
|
|
|
|
|
|
gauge("hydra.queue.bytes_sent", $json->{bytesSent});
|
|
|
|
gauge("hydra.queue.bytes_received", $json->{bytesReceived});
|
2015-08-27 12:02:23 +02:00
|
|
|
|
2015-09-22 12:59:19 +02:00
|
|
|
gauge("hydra.queue.machines.total", scalar(grep { $_->{enabled} } (values %{$json->{machines}})));
|
2015-08-27 12:02:23 +02:00
|
|
|
gauge("hydra.queue.machines.in_use", scalar(grep { $_->{currentJobs} > 0 } (values %{$json->{machines}})));
|
2015-06-23 14:54:34 +02:00
|
|
|
}
|
|
|
|
|
2021-03-20 09:12:02 -04:00
|
|
|
|
|
|
|
sub showHelp {
|
|
|
|
print <<EOF;
|
|
|
|
Usage: $0 [--once]
|
|
|
|
|
|
|
|
Send stats to statsd. The --once flag can be used to send one round
|
|
|
|
of stats and then exit.
|
|
|
|
|
|
|
|
Example:
|
|
|
|
\$ $0 --once
|
|
|
|
EOF
|
|
|
|
exit 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
my $once = 0;
|
|
|
|
|
|
|
|
GetOptions("once" => \$once,
|
|
|
|
"help" => sub { showHelp() }
|
|
|
|
) or exit 1;
|
|
|
|
|
2015-06-23 14:54:34 +02:00
|
|
|
while (1) {
|
Improve handling of Perl's block eval errors
Taken from `Perl::Critic`:
A common idiom in perl for dealing with possible errors is to use `eval`
followed by a check of `$@`/`$EVAL_ERROR`:
eval {
...
};
if ($EVAL_ERROR) {
...
}
There's a problem with this: the value of `$EVAL_ERROR` (`$@`) can change
between the end of the `eval` and the `if` statement. The issue are object
destructors:
package Foo;
...
sub DESTROY {
...
eval { ... };
...
}
package main;
eval {
my $foo = Foo->new();
...
};
if ($EVAL_ERROR) {
...
}
Assuming there are no other references to `$foo` created, when the
`eval` block in `main` is exited, `Foo::DESTROY()` will be invoked,
regardless of whether the `eval` finished normally or not. If the `eval`
in `main` fails, but the `eval` in `Foo::DESTROY()` succeeds, then
`$EVAL_ERROR` will be empty by the time that the `if` is executed.
Additional issues arise if you depend upon the exact contents of
`$EVAL_ERROR` and both `eval`s fail, because the messages from both will
be concatenated.
Even if there isn't an `eval` directly in the `DESTROY()` method code,
it may invoke code that does use `eval` or otherwise affects
`$EVAL_ERROR`.
The solution is to ensure that, upon normal exit, an `eval` returns a
true value and to test that value:
# Constructors are no problem.
my $object = eval { Class->new() };
# To cover the possiblity that an operation may correctly return a
# false value, end the block with "1":
if ( eval { something(); 1 } ) {
...
}
eval {
...
1;
}
or do {
# Error handling here
};
Unfortunately, you can't use the `defined` function to test the result;
`eval` returns an empty string on failure.
Various modules have been written to take some of the pain out of
properly localizing and checking `$@`/`$EVAL_ERROR`. For example:
use Try::Tiny;
try {
...
} catch {
# Error handling here;
# The exception is in $_/$ARG, not $@/$EVAL_ERROR.
}; # Note semicolon.
"But we don't use DESTROY() anywhere in our code!" you say. That may be
the case, but do any of the third-party modules you use have them? What
about any you may use in the future or updated versions of the ones you
already use?
2020-05-26 10:56:24 +02:00
|
|
|
eval {
|
|
|
|
sendQueueRunnerStats();
|
|
|
|
1;
|
2020-06-02 15:30:42 +02:00
|
|
|
} or do { warn "$@"; };
|
2015-06-24 13:19:16 +02:00
|
|
|
|
|
|
|
my $meminfo = read_file("/proc/meminfo", err_mode => 'quiet') // "";
|
|
|
|
$meminfo =~ m/Dirty:\s*(\d+) kB/;
|
|
|
|
if (defined $1) {
|
|
|
|
my $dirty = $1 / (1024.0 * 1024.0);
|
|
|
|
gauge("hydra.mem.dirty", $dirty);
|
|
|
|
}
|
|
|
|
|
2021-03-20 09:12:02 -04:00
|
|
|
if ($once) {
|
|
|
|
last;
|
|
|
|
}
|
2015-06-23 14:54:34 +02:00
|
|
|
sleep(30);
|
|
|
|
}
|