]>
Commit | Line | Data |
---|---|---|
eb32152f DM |
1 | package PVE::Status::Graphite; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
fa6f3716 | 5 | |
228f017e | 6 | use IO::Socket::IP; |
8dcf2cac | 7 | use Socket qw(SOL_SOCKET SO_SNDTIMEO SO_RCVTIMEO); |
eb32152f | 8 | |
228f017e TL |
9 | use PVE::Status::Plugin; |
10 | use PVE::JSONSchema; | |
11 | ||
eb32152f DM |
12 | # example config (/etc/pve/status.cfg) |
13 | #graphite: | |
58541b94 | 14 | # server test |
567bb543 | 15 | # port 2003 |
2c927f11 | 16 | # proto udp |
567bb543 | 17 | # path proxmox.mycluster |
eb32152f DM |
18 | # disable 0 |
19 | # | |
20 | ||
21 | use base('PVE::Status::Plugin'); | |
22 | ||
23 | sub type { | |
24 | return 'graphite'; | |
25 | } | |
26 | ||
27 | sub properties { | |
28 | return { | |
567bb543 | 29 | path => { |
5c90e08a | 30 | type => 'string', format => 'graphite-path', |
567bb543 | 31 | description => "root graphite path (ex: proxmox.mycluster.mykey)", |
eb32152f | 32 | }, |
2c927f11 MV |
33 | timeout => { |
34 | type => 'integer', | |
87be2c19 TL |
35 | description => "graphite TCP socket timeout (default=1)", |
36 | minimum => 0, | |
37 | default => 1, | |
2c927f11 MV |
38 | optional => 1 |
39 | }, | |
40 | proto => { | |
41 | type => 'string', | |
42 | enum => ['udp', 'tcp'], | |
87be2c19 | 43 | description => "Protocol to send graphite data. TCP or UDP (default)", |
2c927f11 MV |
44 | optional => 1, |
45 | }, | |
eb32152f DM |
46 | }; |
47 | } | |
48 | ||
49 | sub options { | |
50 | return { | |
58541b94 | 51 | server => {}, |
567bb543 | 52 | port => { optional => 1 }, |
0fc553eb | 53 | mtu => { optional => 1 }, |
2c927f11 MV |
54 | proto => { optional => 1 }, |
55 | timeout => { optional => 1 }, | |
567bb543 | 56 | path => { optional => 1 }, |
eb32152f | 57 | disable => { optional => 1 }, |
5c90e08a | 58 | }; |
eb32152f DM |
59 | } |
60 | ||
61 | # Plugin implementation | |
62 | sub update_node_status { | |
87be2c19 | 63 | my ($class, $txn, $node, $data, $ctime) = @_; |
567bb543 | 64 | |
5c77a34f | 65 | return assemble($class, $txn, $data, $ctime, "nodes.$node"); |
eb32152f | 66 | |
eb32152f DM |
67 | } |
68 | ||
69 | sub update_qemu_status { | |
87be2c19 | 70 | my ($class, $txn, $vmid, $data, $ctime, $nodename) = @_; |
5c77a34f TL |
71 | |
72 | return assemble($class, $txn, $data, $ctime, "qemu.$vmid"); | |
eb32152f DM |
73 | } |
74 | ||
75 | sub update_lxc_status { | |
87be2c19 | 76 | my ($class, $txn, $vmid, $data, $ctime, $nodename) = @_; |
eb32152f | 77 | |
5c77a34f | 78 | return assemble($class, $txn, $data, $ctime, "lxc.$vmid"); |
eb32152f DM |
79 | } |
80 | ||
81 | sub update_storage_status { | |
87be2c19 | 82 | my ($class, $txn, $nodename, $storeid, $data, $ctime) = @_; |
567bb543 | 83 | |
5c77a34f | 84 | return assemble($class, $txn, $data, $ctime, "storages.$nodename.$storeid"); |
567bb543 AD |
85 | } |
86 | ||
1d5c5ba1 TL |
87 | sub _send_batch_size { |
88 | my ($class, $cfg) = @_; | |
89 | my $proto = $cfg->{proto} || 'udp'; | |
90 | if ($proto eq 'tcp') { | |
91 | return 56000; | |
92 | } | |
93 | return $class->SUPER::_send_batch_size($cfg); | |
94 | } | |
95 | ||
68f58b5d TL |
96 | sub _connect { |
97 | my ($class, $cfg) = @_; | |
567bb543 | 98 | |
68f58b5d TL |
99 | my $host = $cfg->{server}; |
100 | my $port = $cfg->{port} || 2003; | |
101 | my $proto = $cfg->{proto} || 'udp'; | |
102 | my $timeout = $cfg->{timeout} // 1; | |
567bb543 AD |
103 | |
104 | my $carbon_socket = IO::Socket::IP->new( | |
5c90e08a DC |
105 | PeerAddr => $host, |
106 | PeerPort => $port, | |
2c927f11 MV |
107 | Proto => $proto, |
108 | Timeout => $timeout, | |
f014da61 | 109 | ) || die "couldn't create carbon socket [$host]:$port - $@\n"; |
567bb543 | 110 | |
68f58b5d | 111 | if ($proto eq 'tcp') { |
8dcf2cac TL |
112 | # seconds and µs |
113 | my $timeout_struct = pack( 'l!l!', $timeout, 0); | |
114 | setsockopt($carbon_socket, SOL_SOCKET, SO_SNDTIMEO, $timeout_struct); | |
115 | setsockopt($carbon_socket, SOL_SOCKET, SO_RCVTIMEO, $timeout_struct); | |
2c927f11 | 116 | } |
68f58b5d TL |
117 | |
118 | return $carbon_socket; | |
119 | } | |
120 | ||
87be2c19 | 121 | sub assemble { |
5c77a34f | 122 | my ($class, $txn, $data, $ctime, $object) = @_; |
68f58b5d | 123 | |
87be2c19 TL |
124 | my $path = $txn->{cfg}->{path} // 'proxmox'; |
125 | $path .= ".$object"; | |
567bb543 | 126 | |
108e0c8b TL |
127 | # we do not want boolean/state information to export to graphite |
128 | my $key_blacklist = { | |
129 | 'template' => 1, | |
130 | 'pid' => 1, | |
131 | 'agent' => 1, | |
132 | 'serial' => 1, | |
133 | }; | |
134 | ||
108e0c8b TL |
135 | my $assemble_graphite_data; |
136 | $assemble_graphite_data = sub { | |
137 | my ($metric, $path) = @_; | |
138 | ||
139 | for my $key (sort keys %$metric) { | |
1e4ae7d4 | 140 | my $value = $metric->{$key}; |
87be2c19 | 141 | next if !defined($value); |
108e0c8b TL |
142 | |
143 | $key =~ s/\./-/g; | |
144 | my $metricpath = $path . ".$key"; | |
145 | ||
146 | if (ref($value) eq 'HASH') { | |
147 | $assemble_graphite_data->($value, $metricpath); | |
148 | } elsif ($value =~ m/^[+-]?[0-9]*\.?[0-9]+$/ && !$key_blacklist->{$key}) { | |
5c77a34f | 149 | $class->add_metric_data($txn, "$metricpath $value $ctime\n"); |
5a5aed73 | 150 | } |
5c90e08a | 151 | } |
108e0c8b | 152 | }; |
87be2c19 | 153 | $assemble_graphite_data->($data, $path); |
db2ce488 TL |
154 | |
155 | $assemble_graphite_data = undef; # avoid cyclic reference! | |
567bb543 AD |
156 | } |
157 | ||
158 | PVE::JSONSchema::register_format('graphite-path', \&pve_verify_graphite_path); | |
159 | sub pve_verify_graphite_path { | |
160 | my ($path, $noerr) = @_; | |
161 | ||
162 | my $regex = "([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?)"; | |
163 | ||
164 | if ($path !~ /^(${regex}\.)*${regex}$/) { | |
5c90e08a DC |
165 | return undef if $noerr; |
166 | die "value does not look like a valid graphite path\n"; | |
567bb543 AD |
167 | } |
168 | ||
169 | return $path; | |
170 | } | |
171 | ||
172 | ||
eb32152f | 173 | 1; |