#!/usr/bin/perl -w # Draw bandwidth pie graphs. # Copyright 2002, 2003 Adam Sampson # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. use strict; use GD; # Set this to 1 to generate fake data (so you can see how the output will # look). my $simulate = 0; my $statfile = 'net_stats'; my $ingraphfile = 'pie_in.png'; my $outgraphfile = 'pie_out.png'; my $ttffont = '/opt/lib/X11/fonts/TTF/luxisr.ttf'; my $width = 120; my $height = 120; open IN, "<$statfile" or die "can't open stats file"; my $last_time = int ; my %last_in = (); my %last_out = (); while () { chomp; /^(\w+) (\d+) (\d+)$/ or die "bad line in stats file"; $last_in{$1} = int $2; $last_out{$1} = int $3; } close IN; my $time = time(); my %in = (); my %out = (); if ($simulate) { foreach ("cartman", "vindaloo", "naria") { my $period = $time - $last_time; unless (exists $last_in{$_}) { $last_in{$_} = $last_out{$_} = 0; } $in{$_} = $last_in{$_} + int rand(40000) * $period; $out{$_} = $last_out{$_} + int rand(10000) * $period; } } else { my $current_chain = undef; my @lines = `iptables -vnxL`; foreach (@lines) { chomp; s/\s+/ /g; s/^ //; s/ $//; if (/^Chain (\w+) /) { $current_chain = $1; } elsif (/^$/) { # skip } elsif (/^pkts/) { # skip } else { my @bits = split / /; if ($bits[2] eq "RETURN" && $current_chain =~ /^(.+)_(in|out)$/) { if ($2 eq "in") { $in{$1} = int $bits[1]; } else { $out{$1} = int $bits[1]; } } } } } my @machs = sort keys %in; open OUT, ">$statfile" or die "can't open stats file for writing"; print OUT "$time\n"; foreach my $mach (@machs) { print OUT "$mach $in{$mach} $out{$mach}\n"; } close OUT; my $period = $time - $last_time; my %delta_in = (); my %delta_out = (); foreach my $mach (@machs) { unless (defined $last_in{$mach}) { $last_in{$mach} = 0; $last_out{$mach} = 0; } $delta_in{$mach} = 1.0 * ($in{$mach} - $last_in{$mach}); $delta_out{$mach} = 1.0 * ($out{$mach} - $last_out{$mach}); } sub gd_center { my ($im, $size, $x, $y, $string, $color) = @_; my @b = GD::Image->stringFT($color, $ttffont, $size, 0, 0, 0, $string); my $w = $b[2] - $b[0]; my $h = $b[3] - $b[5]; $x -= $b[0]; $y -= $b[1]; $im->stringFT($color, $ttffont, $size, 0, $x - ($w / 2), $y + ($h / 2), $string); } sub graph { my ($machs, $delta, $period, $label, $file) = @_; my $im = new GD::Image($width, $height); my $white = $im->colorAllocate(255, 255, 255); my $black = $im->colorAllocate(0, 0, 0); $im->transparent($white); my %bother = (); my $total = 0; my %cols = (); my $i = 0; foreach my $mach (@$machs) { if ($delta->{$mach} > 1024) { $i = ($i + 1) % 6; my $j = $i + 1; $cols{$mach} = $im->colorAllocate(221 + 31 * ($j & 1), 221 + 31 * (($j >> 1) & 1), 221 + 31 * (($j >> 2) & 1)); $total += $delta->{$mach}; $bother{$mach} = 1; } else { $bother{$mach} = 0; } } my $cx = $width / 2; my $cy = $height / 2; my $xradius = ($width / 2) * 1.0; my $yradius = ($height / 2) * 1.0; my $factor = (2 * 3.1415927) / $total; my $pos = 0.0; foreach my $mach (@$machs) { next unless $bother{$mach}; my $phi = $pos * $factor; my $phi_end = ($pos + $delta->{$mach}) * $factor; my $poly = new GD::Polygon; $poly->addPt($cx, $cy); my $frag = 1.0 / 200; for (my $i = $phi; $i < $phi_end; $i += $frag) { $poly->addPt($cx + cos($i) * $xradius, $cy + sin($i) * $yradius); } $im->filledPolygon($poly, $cols{$mach}); $pos += $delta->{$mach}; } $pos = 0.0; foreach my $mach (@$machs) { next unless $bother{$mach}; my $phi = ($pos + $delta->{$mach} / 2) * $factor; my $x = $cx + cos($phi) * ($xradius * 0.6); my $y = $cy + sin($phi) * ($yradius * 0.6); &gd_center($im, 8, $x, $y - 5, "$mach", $black); &gd_center($im, 7, $x, $y + 5, sprintf("%.1fK", $delta->{$mach} / ($period * 1024)), $black); $pos += $delta->{$mach}; } &gd_center($im, 10, $cx, $cy, $label, $black); open OUT, ">$file" or die "can't open image file $file"; print OUT $im->png; close OUT; } &graph(\@machs, \%delta_in, $period, "in", $ingraphfile); &graph(\@machs, \%delta_out, $period, "out", $outgraphfile);