pve-manager/PVE/Status/Graphite.pm
2024-12-09 18:54:50 +03:00

174 lines
3.8 KiB
Perl

package PVE::Status::Graphite;
use strict;
use warnings;
use IO::Socket::IP;
use Socket qw(SOL_SOCKET SO_SNDTIMEO SO_RCVTIMEO);
use PVE::Status::Plugin;
use PVE::JSONSchema;
# example config (/etc/pve/status.cfg)
#graphite:
# server test
# port 2003
# proto udp
# path proxmox.mycluster
# disable 0
#
use base('PVE::Status::Plugin');
sub type {
return 'graphite';
}
sub properties {
return {
path => {
type => 'string', format => 'graphite-path',
description => "root graphite path (ex: proxmox.mycluster.mykey)",
},
timeout => {
type => 'integer',
description => "graphite TCP socket timeout (default=1)",
minimum => 0,
default => 1,
optional => 1
},
proto => {
type => 'string',
enum => ['udp', 'tcp'],
description => "Protocol to send graphite data. TCP or UDP (default)",
optional => 1,
},
};
}
sub options {
return {
server => {},
port => { optional => 1 },
mtu => { optional => 1 },
proto => { optional => 1 },
timeout => { optional => 1 },
path => { optional => 1 },
disable => { optional => 1 },
};
}
# Plugin implementation
sub update_node_status {
my ($class, $txn, $node, $data, $ctime) = @_;
return assemble($class, $txn, $data, $ctime, "nodes.$node");
}
sub update_qemu_status {
my ($class, $txn, $vmid, $data, $ctime, $nodename) = @_;
return assemble($class, $txn, $data, $ctime, "qemu.$vmid");
}
sub update_lxc_status {
my ($class, $txn, $vmid, $data, $ctime, $nodename) = @_;
return assemble($class, $txn, $data, $ctime, "lxc.$vmid");
}
sub update_storage_status {
my ($class, $txn, $nodename, $storeid, $data, $ctime) = @_;
return assemble($class, $txn, $data, $ctime, "storages.$nodename.$storeid");
}
sub _send_batch_size {
my ($class, $cfg) = @_;
my $proto = $cfg->{proto} || 'udp';
if ($proto eq 'tcp') {
return 56000;
}
return $class->SUPER::_send_batch_size($cfg);
}
sub _connect {
my ($class, $cfg) = @_;
my $host = $cfg->{server};
my $port = $cfg->{port} || 2003;
my $proto = $cfg->{proto} || 'udp';
my $timeout = $cfg->{timeout} // 1;
my $carbon_socket = IO::Socket::IP->new(
PeerAddr => $host,
PeerPort => $port,
Proto => $proto,
Timeout => $timeout,
) || die "couldn't create carbon socket [$host]:$port - $@\n";
if ($proto eq 'tcp') {
# seconds and µs
my $timeout_struct = pack( 'l!l!', $timeout, 0);
setsockopt($carbon_socket, SOL_SOCKET, SO_SNDTIMEO, $timeout_struct);
setsockopt($carbon_socket, SOL_SOCKET, SO_RCVTIMEO, $timeout_struct);
}
return $carbon_socket;
}
sub assemble {
my ($class, $txn, $data, $ctime, $object) = @_;
my $path = $txn->{cfg}->{path} // 'proxmox';
$path .= ".$object";
# we do not want boolean/state information to export to graphite
my $key_blacklist = {
'template' => 1,
'pid' => 1,
'agent' => 1,
'serial' => 1,
};
my $assemble_graphite_data;
$assemble_graphite_data = sub {
my ($metric, $path) = @_;
for my $key (sort keys %$metric) {
my $value = $metric->{$key};
next if !defined($value);
$key =~ s/\./-/g;
my $metricpath = $path . ".$key";
if (ref($value) eq 'HASH') {
$assemble_graphite_data->($value, $metricpath);
} elsif ($value =~ m/^[+-]?[0-9]*\.?[0-9]+$/ && !$key_blacklist->{$key}) {
$class->add_metric_data($txn, "$metricpath $value $ctime\n");
}
}
};
$assemble_graphite_data->($data, $path);
$assemble_graphite_data = undef; # avoid cyclic reference!
}
PVE::JSONSchema::register_format('graphite-path', \&pve_verify_graphite_path);
sub pve_verify_graphite_path {
my ($path, $noerr) = @_;
my $regex = "([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?)";
if ($path !~ /^(${regex}\.)*${regex}$/) {
return undef if $noerr;
die "value does not look like a valid graphite path\n";
}
return $path;
}
1;