App-Netdisco
view release on metacpan or search on metacpan
lib/App/Netdisco/Web/Metrics.pm view on Meta::CPAN
package App::Netdisco::Web::Metrics;
use Dancer ':syntax';
use Dancer::Plugin::DBIC;
use App::Netdisco::Util::Permission 'acl_matches';
use POSIX 'floor';
use Try::Tiny;
sub _header {
my ($name, $help) = @_;
return sprintf("# HELP %s %s\n# TYPE %s gauge\n", $name, $help, $name);
}
sub _sample {
my ($name, $value, %labels) = @_;
my $label_str = '';
if (%labels) {
$label_str = '{'. join(',', map { qq($_="$labels{$_}") } sort keys %labels) .'}';
}
return sprintf("%s%s %s\n", $name, $label_str, $value // 0);
}
if (my $metrics_path = setting('metrics_path')) {
# Both restrictions below are optional, so this endpoint can be published with
# neither. Warns rather than dies: a misconfiguration should not become an
# outage.
warning 'metrics_path is set with neither metrics_allow nor metrics_token,'
. ' so the metrics endpoint is served to anyone who can reach it'
unless (setting('metrics_allow') and scalar @{ setting('metrics_allow') })
or setting('metrics_token');
get $metrics_path => sub {
# Optional IP range restriction
my $allow = setting('metrics_allow');
if ($allow and ref $allow eq ref []) {
my $remote = request->remote_address;
unless (acl_matches($remote, $allow)) {
status 403;
return 'Forbidden';
}
}
# Optional bearer token auth
my $token = setting('metrics_token');
if ($token) {
my $auth = request->header('Authorization') // '';
unless ($auth eq "Bearer $token") {
status 401;
header 'WWW-Authenticate' => 'Bearer realm="netdisco metrics"';
return 'Unauthorized';
}
}
content_type 'text/plain; version=0.0.4; charset=utf-8';
my @tenants = ('netdisco');
if (my $tdbs = setting('tenant_databases')) {
push @tenants, map { $_->{'tag'} } @$tdbs;
}
my $output = '';
# -- Statistics metrics (one row per tenant) -------------------------------
my @stat_metrics = (
[ netdisco_devices => 'device_count', 'Total number of discovered devices' ],
[ netdisco_device_ips => 'device_ip_count', 'Total number of device IP addresses' ],
[ netdisco_device_links => 'device_link_count', 'Total number of layer2 links between devices' ],
[ netdisco_device_ports => 'device_port_count', 'Total number of device ports' ],
[ netdisco_device_ports_up => 'device_port_up_count','Total number of device ports with up/up status' ],
[ netdisco_ip_table => 'ip_table_count', 'Total number of IP table entries' ],
[ netdisco_ip_active => 'ip_active_count', 'Total number of active IP entries' ],
[ netdisco_nodes => 'node_table_count', 'Total number of node entries' ],
[ netdisco_nodes_active => 'node_active_count', 'Total number of active nodes' ],
[ netdisco_phones => 'phone_count', 'Total number of discovered VoIP phones' ],
[ netdisco_waps => 'wap_count', 'Total number of discovered wireless access points' ],
);
foreach my $m (@stat_metrics) {
my ($metric, $col, $help) = @$m;
$output .= _header($metric, $help);
foreach my $tenant (@tenants) {
my $stats = try {
schema($tenant)->resultset('Statistics')
->search(undef, { order_by => { -desc => 'day' }, rows => 1 })->first;
};
next unless $stats;
$output .= _sample($metric, $stats->$col // 0, tenant => $tenant);
}
( run in 0.783 second using v1.01-cache-2.11-cpan-e623d60df62 )