fb397d4cd41775ad85ada96f600adc4ab49a25df
[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 sub interface_read($$) {
43     my $veid = shift;
44     my $name = shift;
45     my ($key, $val, @lines, @parts, @counters);
46     my @if_instances = ('if_octets', 'if_packets', 'if_errors');
47     my %v = _build_report_hash($name);
48
49     $v{'plugin'} = 'interface';
50     delete $v{'plugin_instance'};
51
52     @lines = split(/\n/, `$vzctl exec $veid cat /proc/net/dev`);
53     foreach (@lines) {
54         next if (!/:/);
55
56         @parts = split(/:/);
57         ($key = $parts[0]) =~ s/^\s*(.*?)\s*$/$1/;
58         ($val = $parts[1]) =~ s/^\s*(.*?)\s*$/$1/;
59         @counters = split(/ +/, $val);
60
61         $v{'type_instance'} = $key;
62         for ($key = 0; $key <= $#if_instances; ++$key) {
63             $v{'type'} = $if_instances[$key];
64             $v{'values'} = [ $counters[$key], $counters[$key + 8] ];
65             plugin_dispatch_values(\%v);
66     }
67 }
68 }
69
70 sub cpu_read($$) {
71     my $veid = shift;
72     my $name = shift;
73     my ($key, $val, $i, @lines, @counters);
74     my @cpu_instances = ('user', 'nice', 'system', 'idle', 'wait', 'interrupt', 'softirq', 'steal');
75     my $last_stat = {};
76     my %v = _build_report_hash($name);
77
78     $v{'plugin'} = 'cpu';
79     $v{'type'} = 'cpu';
80
81     $i = 0;
82     @lines = split(/\n/, `$vzctl exec $veid cat /proc/stat`);
83     foreach (@lines) {
84         next if (!/^cpu[0-9]/);
85
86         @counters = split(/ +/);
87         shift(@counters);
88
89         # Remove once OpenVZ bug 1376 is resolved
90         if (48485 == $counters[3]) {
91             $counters[3] = $last_stat->{"$veid-$i-idle"};
92             $counters[4] = $last_stat->{"$veid-$i-wait"};
93         }
94         else {
95             $last_stat->{"$veid-$i-idle"} = $counters[3];
96             $last_stat->{"$veid-$i-wait"} = $counters[4];
97         }
98
99         $v{'plugin_instance'} = $i++;
100         for ($key = 0; $key <= $#counters; ++$key) {
101             $v{'type_instance'} = $cpu_instances[$key];
102             $v{'values'} = [ $counters[$key] ];
103             plugin_dispatch_values(\%v);
104     }
105 }
106 }
107
108 sub df_read($$) {
109     my $veid = shift;
110     my $name = shift;
111     my ($key, $val, @lines, @parts);
112     my %v = _build_report_hash($name);
113
114     $v{'plugin'} = 'df';
115     delete $v{'plugin_instance'};
116     $v{'type'} = 'df';
117
118     $val = join(' ', map { (split)[1] } split(/\n/, `$vzctl exec $veid cat /proc/mounts`));
119     @lines = split(/\n/, `$vzctl exec $veid stat -tf $val`);
120     foreach (@lines) {
121         @parts = split(/ /);
122         next if (0 == $parts[7]);
123
124         $val = substr($parts[0], 1);
125         $val = 'root' if ($val =~ /^$/);
126         $val =~ s#/#-#g;
127
128         $v{'type_instance'} = $val;
129         $v{'values'} = [ $parts[5] * ($parts[6] - $parts[7]), $parts[5] * $parts[7] ];
130         plugin_dispatch_values(\%v);
131 }
132 }
133
134 sub load_read($$) {
135     my $veid = shift;
136     my $name = shift;
137     my ($key, $val, @lines, @parts);
138     my %v = _build_report_hash($name);
139
140     $v{'plugin'} = 'load';
141     delete $v{'plugin_instance'};
142     $v{'type'} = 'load';
143     delete $v{'type_instance'};
144
145     @parts = split(/ +/, `$vzctl exec $veid cat /proc/loadavg`);
146     $v{'values'} = [ $parts[0], $parts[1], $parts[2] ];
147     plugin_dispatch_values(\%v);
148 }
149
150 sub processes_read($$) {
151     my $veid = shift;
152     my $name = shift;
153     my ($key, $val, @lines);
154     my %v = _build_report_hash($name);
155
156     my $ps_states = { 'paging' => 0, 'blocked' => 0, 'zombies' => 0, 'stopped' => 0,
157         'running' => 0, 'sleeping' => 0 };
158     my $state_map = { 'R' => 'running', 'S' => 'sleeping', 'D' => 'blocked',
159         'Z' => 'zombies', 'T' => 'stopped', 'W' => 'paging' };
160
161     $v{'plugin'} = 'processes';
162     delete $v{'plugin_instance'};
163     $v{'type'} = 'ps_state';
164
165     @lines = map { (split)[2] } split(/\n/, `$vzctl exec $veid cat '/proc/[0-9]*/stat'`);
166     foreach $key (@lines) {
167         ++$ps_states->{$state_map->{$key}};
168     }
169
170     foreach $key (keys %{$ps_states}) {
171         $v{'type_instance'} = $key;
172         $v{'values'} = [ $ps_states->{$key} ];
173         plugin_dispatch_values(\%v);
174 }
175 }
176
177 sub users_read($$) {
178     my $veid = shift;
179     my $name = shift;
180     my ($key, $val, @lines);
181     my %v = _build_report_hash($name);
182
183     $v{'plugin'} = 'users';
184     delete $v{'plugin_instance'};
185     $v{'type'} = 'users';
186     delete $v{'type_instance'};
187
188     @lines = split(/\n/, `$vzctl exec $veid w -h`);
189     $v{'values'} = [ scalar(@lines) ];
190     plugin_dispatch_values(\%v);
191 }
192
193 sub _build_report_hash($) {
194     my $name = shift;
195     return (time => time(), interval => plugin_get_interval(), host => $name);
196 }
197
198 sub openvz_read {
199     my (@veids, $veid, $name);
200
201     @veids = map { s/ //g; $_; } split(/\n/, `$vzlist -Ho veid`);
202
203     foreach $veid (@veids) {
204         ($name = `$vzlist -Ho name $veid`) =~ s/^\s*(.*?)\s*$/$1/;
205         ($name = `$vzlist -Ho hostname $veid`) =~ s/^\s*(.*?)\s*$/$1/ if($name =~ /^-$/);
206         $name = $veid if ($name =~ /^-$/);
207
208         if($enable_interface) {
209             interface_read($veid, $name);
210         }
211
212         if($enable_cpu) {
213             cpu_read($veid, $name);
214         }
215
216         if($enable_df) {
217             df_read($veid, $name);
218         }
219
220         if($enable_load) {
221             load_read($veid, $name);
222         }
223
224         if($enable_processes) {
225             processes_read($veid, $name);
226         }
227
228         if($enable_users) {
229             users_read($veid, $name);
230         }
231
232         return 1;
233     }
234 }
235
236 plugin_register(TYPE_READ, 'OpenVZ', 'openvz_read');
237
238 return 1;