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 23package Collectd::Plugins::OpenVZ; 24 25use strict; 26use warnings; 27 28use Collectd qw( :all ); 29 30my $vzctl = '/usr/sbin/vzctl'; 31my $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. 35my $enable_interface = 1; 36my $enable_cpu = 0; 37my $enable_df = 1; 38my $enable_load = 1; 39my $enable_processes = 1; 40my $enable_users = 1; 41 42# We probably don't care about loopback transfer 43my @ignored_interfaces = ( "lo" ); 44 45sub 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 79sub 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 117sub 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 143sub 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 159sub 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 186sub 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 202sub _build_report_hash { 203 my $name = shift; 204 return (time => time(), interval => plugin_get_interval(), host => $name); 205} 206 207sub 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 245plugin_register(TYPE_READ, 'OpenVZ', 'openvz_read'); 246 247return 1; 248