]> git.proxmox.com Git - pve-manager.git/blobdiff - PVE/Status/Graphite.pm
bump version to 8.2.3
[pve-manager.git] / PVE / Status / Graphite.pm
index 849930f0c9dccd2cf57e9bab01543ea65ff61a54..1b196c2418f0796763fe8354919e6b0bf6c07468 100644 (file)
@@ -2,12 +2,18 @@ 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
 #
@@ -21,9 +27,22 @@ sub type {
 sub properties {
     return {
        path => {
-            type => 'string', format => 'graphite-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,
+       },
     };
 }
 
@@ -31,74 +50,109 @@ 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, $plugin_config, $node, $data, $ctime) = @_;
+    my ($class, $txn, $node, $data, $ctime) = @_;
 
-    write_graphite_hash($plugin_config, $data, $ctime, "nodes.$node");
+    return assemble($class, $txn, $data, $ctime, "nodes.$node");
 
 }
 
 sub update_qemu_status {
-    my ($class, $plugin_config, $vmid, $data, $ctime, $nodename) = @_;
-    write_graphite_hash($plugin_config, $data, $ctime, "qemu.$vmid");
+    my ($class, $txn, $vmid, $data, $ctime, $nodename) = @_;
+
+    return assemble($class, $txn, $data, $ctime, "qemu.$vmid");
 }
 
 sub update_lxc_status {
-    my ($class, $plugin_config, $vmid, $data, $ctime, $nodename) = @_;
+    my ($class, $txn, $vmid, $data, $ctime, $nodename) = @_;
 
-    write_graphite_hash($plugin_config, $data, $ctime, "lxc.$vmid");
+    return assemble($class, $txn, $data, $ctime, "lxc.$vmid");
 }
 
 sub update_storage_status {
-    my ($class, $plugin_config, $nodename, $storeid, $data, $ctime) = @_;
+    my ($class, $txn, $nodename, $storeid, $data, $ctime) = @_;
+
+    return assemble($class, $txn, $data, $ctime, "storages.$nodename.$storeid");
+}
 
-    write_graphite_hash($plugin_config, $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 write_graphite_hash {
-    my ($plugin_config, $d, $ctime, $object) = @_;
+sub _connect {
+    my ($class, $cfg) = @_;
 
-    my $host = $plugin_config->{server};
-    my $port = $plugin_config->{port} ? $plugin_config->{port} : 2003;
-    my $path = $plugin_config->{path} ? $plugin_config->{path} : 'proxmox';
+    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       => 'udp',
+       PeerAddr    => $host,
+       PeerPort    => $port,
+       Proto       => $proto,
+       Timeout     => $timeout,
     ) || die "couldn't create carbon socket [$host]:$port - $@\n";
 
-    write_graphite($carbon_socket, $d, $ctime, $path.".$object");
-
-    $carbon_socket->close() if $carbon_socket;
+    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 write_graphite {
-    my ($carbon_socket, $d, $ctime, $path) = @_;
+sub assemble {
+    my ($class, $txn, $data, $ctime, $object) = @_;
 
-    for my $key (keys %$d) {
+    my $path = $txn->{cfg}->{path} // 'proxmox';
+    $path .= ".$object";
 
-        my $value = $d->{$key};
-        my $oldpath = $path;
-        $key =~ s/\./-/g;
-        $path .= ".$key";
+    # we do not want boolean/state information to export to graphite
+    my $key_blacklist = {
+       'template' => 1,
+       'pid' => 1,
+       'agent' => 1,
+       'serial' => 1,
+    };
 
-        if ( defined $value ) {
-            if ( ref $value eq 'HASH' ) {
-                write_graphite($carbon_socket, $value, $ctime, $path);
-            }else {
-                $carbon_socket->send( "$path $value $ctime\n" );
-            }
-        }
-        $path = $oldpath;
-    }
+    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);
@@ -108,8 +162,8 @@ sub pve_verify_graphite_path {
     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 undef if $noerr;
+       die "value does not look like a valid graphite path\n";
     }
 
     return $path;