Merge branch 'collectd-5.8'
[collectd.git] / bindings / perl / lib / Collectd / Plugins / OpenVZ.pm
1 #
2 # collectd - OpenVZ collectd plugin
3 # Copyright (C) 2009  Jonathan Kolb
4 #
5 # This program is free software; you can redistribute it and/or modify it under
6 # the terms of the GNU General Public License as published by the Free Software
7 # Foundation; either version 2 of the License, or (at your option) any later
8 # version.
9 #
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 # General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License along
16 # with this program; if not, write to the Free Software Foundation, Inc.,
17 # 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18 #
19 # Author:
20 #   Jonathan Kolb <jon at b0g.us>
21 #
22
23 package Collectd::Plugins::OpenVZ;
24
25 use strict;
26 use warnings;
27
28 use Collectd qw( :all );
29
30 my $vzctl = '/usr/sbin/vzctl';
31 my $vzlist = '/usr/sbin/vzlist';
32
33 # Since OpenVZ is container based, all guests see all the host's CPUs,
34 # and would report the same data. So we disable CPU by default.
35 my $enable_interface = 1;
36 my $enable_cpu       = 0;
37 my $enable_df        = 1;
38 my $enable_load      = 1;
39 my $enable_processes = 1;
40 my $enable_users     = 1;
41
42 # We probably don't care about loopback transfer
43 my @ignored_interfaces = ( "lo" );
44
45 sub interface_read {
46     my ($veid, $name) = @_;
47     my @rx_fields = qw(if_octets if_packets if_errors drop fifo frame compressed multicast);
48     my @tx_fields = qw(if_octets if_packets if_errors drop fifo frame compressed);
49     my %v = _build_report_hash($name);
50
51     my @lines = `$vzctl exec $veid cat /proc/net/dev`;
52
53     for my $line (@lines) {
54         # skip explanatory text
55         next if $line !~ /:/;
56
57         $line =~ s/^\s+|\s+$//g;
58
59         my ($iface, %rx, %tx);
60
61         # read /proc/net/dev fields
62         ($iface, @rx{@rx_fields}, @tx{@tx_fields}) = split /[: ]+/, $line;
63
64         # Skip this interface if it is in the ignored list
65         next if grep { $iface eq $_ } @ignored_interfaces;
66
67         for my $instance (qw(if_octets if_packets if_errors)) {
68             plugin_dispatch_values({
69                 'plugin'          => 'interface',
70                 'plugin_instance' => $iface,
71                 'type'            => $instance,
72                 'values'          => [ $rx{$instance}, $tx{$instance} ],
73                 %v,
74             });
75         }
76     }
77 }
78
79 sub cpu_read {
80     my $veid = shift;
81     my $name = shift;
82     my ($key, $val, $i, @lines, @counters);
83     my @cpu_instances = ('user', 'nice', 'system', 'idle', 'wait', 'interrupt', 'softirq', 'steal');
84     my $last_stat = {};
85     my %v = _build_report_hash($name);
86
87     $v{'plugin'} = 'cpu';
88     $v{'type'} = 'cpu';
89
90     $i = 0;
91     @lines = split(/\n/, `$vzctl exec $veid cat /proc/stat`);
92     foreach (@lines) {
93         next if (!/^cpu[0-9]/);
94
95         @counters = split(/ +/);
96         shift(@counters);
97
98         # Remove once OpenVZ bug 1376 is resolved
99         if (48485 == $counters[3]) {
100             $counters[3] = $last_stat->{"$veid-$i-idle"};
101             $counters[4] = $last_stat->{"$veid-$i-wait"};
102         }
103         else {
104             $last_stat->{"$veid-$i-idle"} = $counters[3];
105             $last_stat->{"$veid-$i-wait"} = $counters[4];
106         }
107
108         $v{'plugin_instance'} = $i++;
109         for ($key = 0; $key <= $#counters; ++$key) {
110             $v{'type_instance'} = $cpu_instances[$key];
111             $v{'values'} = [ $counters[$key] ];
112             plugin_dispatch_values(\%v);
113     }
114 }
115 }
116
117 sub df_read {
118     my $veid = shift;
119     my $name = shift;
120     my ($key, $val, @lines, @parts);
121     my %v = _build_report_hash($name);
122
123     $v{'plugin'} = 'df';
124     delete $v{'plugin_instance'};
125     $v{'type'} = 'df';
126
127     $val = join(' ', map { (split)[1] } split(/\n/, `$vzctl exec $veid cat /proc/mounts`));
128     @lines = split(/\n/, `$vzctl exec $veid stat -tf $val`);
129     foreach (@lines) {
130         @parts = split(/ /);
131         next if (0 == $parts[7]);
132
133         $val = substr($parts[0], 1);
134         $val = 'root' if ($val =~ /^$/);
135         $val =~ s#/#-#g;
136
137         $v{'type_instance'} = $val;
138         $v{'values'} = [ $parts[5] * ($parts[6] - $parts[7]), $parts[5] * $parts[7] ];
139         plugin_dispatch_values(\%v);
140 }
141 }
142
143 sub load_read {
144     my $veid = shift;
145     my $name = shift;
146     my ($key, $val, @lines, @parts);
147     my %v = _build_report_hash($name);
148
149     $v{'plugin'} = 'load';
150     delete $v{'plugin_instance'};
151     $v{'type'} = 'load';
152     delete $v{'type_instance'};
153
154     @parts = split(/ +/, `$vzctl exec $veid cat /proc/loadavg`);
155     $v{'values'} = [ $parts[0], $parts[1], $parts[2] ];
156     plugin_dispatch_values(\%v);
157 }
158
159 sub processes_read {
160     my $veid = shift;
161     my $name = shift;
162     my ($key, $val, @lines);
163     my %v = _build_report_hash($name);
164
165     my $ps_states = { 'paging' => 0, 'blocked' => 0, 'zombies' => 0, 'stopped' => 0,
166         'running' => 0, 'sleeping' => 0 };
167     my $state_map = { 'R' => 'running', 'S' => 'sleeping', 'D' => 'blocked',
168         'Z' => 'zombies', 'T' => 'stopped', 'W' => 'paging' };
169
170     $v{'plugin'} = 'processes';
171     delete $v{'plugin_instance'};
172     $v{'type'} = 'ps_state';
173
174     @lines = map { (split)[2] } split(/\n/, `$vzctl exec $veid cat '/proc/[0-9]*/stat'`);
175     foreach $key (@lines) {
176         ++$ps_states->{$state_map->{$key}};
177     }
178
179     foreach $key (keys %{$ps_states}) {
180         $v{'type_instance'} = $key;
181         $v{'values'} = [ $ps_states->{$key} ];
182         plugin_dispatch_values(\%v);
183 }
184 }
185
186 sub users_read {
187     my $veid = shift;
188     my $name = shift;
189     my ($key, $val, @lines);
190     my %v = _build_report_hash($name);
191
192     $v{'plugin'} = 'users';
193     delete $v{'plugin_instance'};
194     $v{'type'} = 'users';
195     delete $v{'type_instance'};
196
197     @lines = split(/\n/, `$vzctl exec $veid w -h`);
198     $v{'values'} = [ scalar(@lines) ];
199     plugin_dispatch_values(\%v);
200 }
201
202 sub _build_report_hash {
203     my $name = shift;
204     return (time => time(), interval => plugin_get_interval(), host => $name);
205 }
206
207 sub openvz_read {
208     my (@veids, $veid, $name);
209
210     @veids = map { s/ //g; $_; } split(/\n/, `$vzlist -Ho veid`);
211
212     foreach $veid (@veids) {
213         ($name = `$vzlist -Ho name $veid`) =~ s/^\s*(.*?)\s*$/$1/;
214         ($name = `$vzlist -Ho hostname $veid`) =~ s/^\s*(.*?)\s*$/$1/ if($name =~ /^-$/);
215         $name = $veid if ($name =~ /^-$/);
216
217         if($enable_interface) {
218             interface_read($veid, $name);
219         }
220
221         if($enable_cpu) {
222             cpu_read($veid, $name);
223         }
224
225         if($enable_df) {
226             df_read($veid, $name);
227         }
228
229         if($enable_load) {
230             load_read($veid, $name);
231         }
232
233         if($enable_processes) {
234             processes_read($veid, $name);
235         }
236
237         if($enable_users) {
238             users_read($veid, $name);
239         }
240
241         return 1;
242     }
243 }
244
245 plugin_register(TYPE_READ, 'OpenVZ', 'openvz_read');
246
247 return 1;