[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [OSSTEST PATCH 4/5] cr-disk-report: New script



This generates a report on the disk usage in the logs directory.
---
 .gitignore     |    2 +
 cr-disk-report |  336 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 338 insertions(+)
 create mode 100755 cr-disk-report

diff --git a/.gitignore b/.gitignore
index 8dad470..5f3c626 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,3 +28,5 @@ id_rsa_osstest
 id_rsa_osstest.pub
 overlay-local
 images
+diskusage-[A-Z]*.html
+diskusage-[A-Z]*.png
diff --git a/cr-disk-report b/cr-disk-report
new file mode 100755
index 0000000..f9b842d
--- /dev/null
+++ b/cr-disk-report
@@ -0,0 +1,336 @@
+#!/usr/bin/perl -w
+#
+# Generate a report on the disk space used by the logs
+
+# This is part of "osstest", an automated testing framework for Xen.
+# Copyright (C) 2009-2013 Citrix Inc.
+# 
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+# 
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# usage: ./cr-disk-report [<options>] cfgbase
+
+use strict qw(vars);
+use Osstest;
+use Osstest::Management qw(:logs);
+
+use HTML::Entities;
+use URI::Escape;
+use POSIX;
+
+use Data::Dumper;
+
+our $outdir;
+our $output;
+our $du_output;
+our $graphs_px=0;
+our $graphs_py=0;
+open DEBUG, ">/dev/null" or die $!;
+
+our @blessings = qw(real real-bisect);
+# for these blessings column is       "<blessing> <branch>"
+# for other blessings column is       "<intended> [<blessing>]"
+
+while (@ARGV && $ARGV[0] =~ m/^\-/) {
+    $_= shift @ARGV;
+    last if m/^--$/;
+    if (m/^--html=(.+)$/) {
+       $output= $1;
+    } elsif (m/^--html-dir=(.+)$/) {
+       $outdir= $1;
+    } elsif (m/^--blessings=(.+)$/) {
+       @blessings = split /,/, $1;
+    } elsif (m/^--graphs(?:=(\d*)x(\d*))?$/) {
+       $graphs_px = $1 || 50;
+       $graphs_py = $1 || 20;
+    } elsif (m/^--debug$/) {
+       open DEBUG, ">&STDERR" or die $!;
+    } elsif (m/^--du-output=(.*)$/) {
+       $du_output = $1;
+    } else {
+       die "$_ ?";
+    }
+}
+
+die unless @ARGV==1;
+die unless !!$graphs_px == !!$graphs_py;
+die if defined $outdir && defined $output;
+
+our ($cfgbase) = @ARGV;
+
+csreadconfig();
+
+logs_select $cfgbase or exit 4;
+
+$outdir //= ".";
+$output //= "$outdir/diskusage-$cfgbase.html";
+
+my $flightq = $dbh_tests->prepare(<<END);
+    SELECT started,blessing,branch,intended FROM flights WHERE flight=?
+END
+
+our $now = time;
+
+our ($totaldata, %coldata, %rowdata, %celldata);
+
+if (!defined $du_output) {
+    open P, "-|", onloghost "du -sk $logdir/*/*" or die $!;
+} else {
+    open P, "<", $du_output or die "$du_output $!";
+}
+
+my $frow;
+
+our @alldata;
+our (%totalinfo, %rowheadsinfo, %colheadsinfo, %bulkinfo);
+
+sub perinfo ($) {
+    my ($fn) = @_;
+    $fn->( \%totalinfo    );
+    $fn->( \%colheadsinfo );
+    $fn->( \%rowheadsinfo );
+    $fn->( \%bulkinfo     );
+}
+
+sub perdatum ($$) {
+    my ($fn, $datum) = @_;
+    my ($rowhead, $colhead, $kb, $age) = @$datum;
+    $fn->( \%totalinfo,    \$totaldata,                    $kb, $age );
+    $fn->( \%colheadsinfo, \$coldata{$colhead},            $kb, $age );
+    $fn->( \%rowheadsinfo, \$rowdata{$rowhead},            $kb, $age );
+    $fn->( \%bulkinfo,     \$celldata{$rowhead}{$colhead}, $kb, $age );
+}
+
+sub prepinfo ($) {
+    my ($info) = @_;
+    $info->{MaxAge} = 1;
+    $info->{MaxCellKb} = 0;
+}
+
+perinfo \&prepinfo;
+
+sub updatemax ($$) {
+    my ($maxref, $val) = @_;
+    $$maxref = $val if $val > $$maxref;
+}
+
+sub updatecell ($$$) {
+    my ($info, $cellrefref, $kb, $age) = @_;
+    $$cellrefref //= { Kb => 0, N => 0, MaxAge => 0 };
+    my $cellref = $$cellrefref;
+    $cellref->{Kb} += $kb;
+    $cellref->{N}++;
+    # MaxAge is actually necessarily the same for every \%info, but
+    # we retain this code structure because it is more orthogonal, and
+    # it would make it easier to scale each cell differently.
+    updatemax \ $info->{MaxAge}, $age;
+    updatemax \ $info->{MaxCellKb}, $cellref->{Kb};
+}
+
+while (<P>) {
+    m#^(\d+)\s+\S+/([^/]+)/([^/]+)\n$# or die "$_ ?";
+    my ($kb, $flight, $job) = ($1, $2, $3);
+
+    print DEBUG "kb=$kb flight=$flight job=$job\n";
+
+    if (!(defined $frow->{flight} && $frow->{flight} eq $flight)) {
+       $frow = undef;
+       if ($flight =~ m/^\d+$/) {
+           $flightq->execute($flight);
+           $frow = $flightq->fetchrow_hashref();
+       }
+    }
+
+    my $age = $now - ($frow->{started} || $now);
+
+    my $colhead;
+    if (!defined $frow->{blessing}) {
+       $colhead = "?";
+    } elsif (grep { $_ eq $frow->{blessing} } @blessings) {
+       $colhead = "$frow->{blessing} $frow->{branch}";
+    } else {
+       $colhead = $frow->{intended};
+       $colhead .= " [$frow->{blessing}]"
+           if $frow->{blessing} ne $frow->{intended};
+    }
+
+    my $rowhead = $job;
+
+    my @datum = ($rowhead, $colhead, $kb, $age);
+    push @alldata, \@datum if $graphs_px;
+    perdatum \&updatecell, \@datum;
+}
+
+my $minexpire = logcfg('MinExpireAge');
+
+sub graphscale ($) {
+    my ($info) = @_;
+
+    $info->{SecondsPerPixel} = ($info->{MaxAge} + 1) / $graphs_px;
+    $info->{KbPerPixel} = ($info->{MaxCellKb} + 1) / $graphs_py;
+    $info->{MinExpireBucket} =
+       floor($minexpire / $info->{SecondsPerPixel});
+}
+
+sub agebucket ($$$$) {
+    my ($info, $cellrefref, $kb, $age) = @_;
+    my $bucket = floor($age / $info->{SecondsPerPixel});
+#    $bucket++ if $age > $minexpire;
+#    $bucket = $graphs_px-1 if $bucket >= $graphs_px;
+    my $cellref = $$cellrefref;                              
+    $cellref->{Ages} //= [ (0) x $graphs_px ];
+    $cellref->{Ages}[$bucket] += $kb;
+}
+
+print DEBUG Dumper(\%coldata, \%rowdata, \%celldata);
+
+sub dumpinfo ($) {
+    my ($info) = @_;
+    print DEBUG Dumper($info);
+}
+
+if ($graphs_px) {
+    perinfo \&graphscale;
+    foreach my $datum (@alldata) {
+       perdatum \&agebucket, $datum;
+    }
+}
+
+perinfo \&dumpinfo;
+
+$!=0; $?=0; close P or die "$? $!";
+
+my @cols = sort { $coldata{$b}{Kb} <=> $coldata{$a}{Kb} } keys %coldata;
+my @rows = sort { $rowdata{$b}{Kb} <=> $rowdata{$a}{Kb} } keys %rowdata;
+
+our @o = '';
+
+sub headcell ($) {
+    my ($text) = @_;
+    return "<th>$text</th>";
+}
+
+our $span2 = $graphs_px?"colspan=2":"";
+
+sub cheadcell ($) {
+    my ($text) = @_;
+    return "<th $span2>$text</th>";
+}
+
+sub prow {
+    push @o, "<thead><tr>";
+    push @o, @_;
+    push @o, "</tr></thead>\n";
+}
+
+sub cell_graph_fn ($$$$) {
+    my ($ginfo,$rowk,$colk,$cellref) = @_;
+    my $fn = $output;
+    $fn =~ s/\.html?$//i;
+    foreach my $k ($rowk, $colk) {
+       $_ = $k;
+       s{\W}{ sprintf "=%02x", ord $& }ge;
+       $fn .= "_$_";
+    }
+    flush STDOUT or die $!;
+    $fn .= ".png";
+    my $pid = open PBM, "|-"; defined $pid or die $!;
+    my $cmd = 'pnmflip -cw -tb | pnmtopng --quiet';
+    if (!length $rowk && !length $colk) {
+       $cmd = "tee $fn.pbm | $cmd";
+    }
+    if (!$pid) {
+       open STDOUT, ">", "$fn.tmp" or die $!;
+       exec qw(bash -ec), "set -o pipefail; $cmd";
+       die $!;
+    }
+    print PBM "P3\n$graphs_py $graphs_px 7\n" or die $!;
+    my $kbthisyoung=0;
+    for (my $bucket=0; $bucket < $graphs_px; $bucket++) {
+       $kbthisyoung += $cellref->{Ages}[$bucket];
+       my $fullpixels = floor($kbthisyoung / $ginfo->{KbPerPixel});
+       my ($fullcolour,$emptycolour) =
+           ($bucket <= $ginfo->{MinExpireBucket}
+            ? ('7 0 0 ', '5 5 7 ')
+            : ('0 0 0 ', '7 7 7 '));
+
+       print PBM ($fullcolour) x $fullpixels or die $!;
+       print PBM ($emptycolour) x ($graphs_py - $fullpixels) or die $!;
+       print PBM "\n" or die $!;
+    }
+    $?=0; $!=0; close PBM or die "$? $!";
+    rename "$fn.tmp", "$fn" or die "$fn $!";
+    $fn =~ s#.*/##;
+    return $fn;
+}
+
+sub spcell ($$$$) {
+    my ($ginfo,$rowk,$colk,$cellref) = @_;
+    return "<td $span2></td>" unless $cellref;
+    my $s = '';
+    $s .= "<td align='right'>";
+    $s .= sprintf "<em>%d</em><br>%d",
+        ceil($cellref->{Kb} / 1024), $cellref->{N};
+    $s .= "</td>";
+    if ($graphs_px) {
+       my $fn = cell_graph_fn($ginfo,$rowk,$colk,$cellref);
+       $s .= "<td><img src='".uri_escape($fn)."'></td>";
+    }
+    $s .= "\n";
+    return $s;
+}
+
+sub prowdata ($$$$$) {
+    my ($rowk,$head1,$head2, $ginfo,$dataref) = @_;
+    my @data;
+    foreach my $col (@cols) {
+       my $cellref = $dataref->{$col};
+       push @data, spcell($ginfo,$rowk,$col,$cellref);
+    }
+    prow($head1,$head2,@data);
+}
+
+push @o, <<END;
+<html><head>
+<title>Disk report for $cfgbase</title>
+</head><body>
+<table rules=groups>
+END
+
+push @o, "<colgroup></colgroup>";
+my $g = sprintf "<colgroup span=%d></colgroup>", ($graphs_px ? 2 : 1);
+push @o, $g;
+push @o, $g foreach @cols;
+push @o, "\n";
+
+prow(headcell('[<em>MiB</em><br>count]'),
+     cheadcell('Branch / blessing'),
+     map { cheadcell(encode_entities($_)) } @cols);
+prowdata('', headcell('Job'), spcell(\%totalinfo, '','',$totaldata),
+        \%colheadsinfo, \%coldata);
+
+foreach my $row (@rows) {
+    print DEBUG "###$row\n";
+    prowdata($row, headcell(encode_entities($row)),
+            spcell(\%rowheadsinfo, $row,'',$rowdata{$row}),
+            \%bulkinfo, $celldata{$row});
+}
+
+push @o, <<END;
+</table>
+END
+
+open O, ">", "$output.tmp" or die "$output $!";
+print O @o or die $!;
+close O or die $!;
+rename "$output.tmp", "$output" or die "$output $!";
-- 
1.7.10.4


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.