Estimate disk usage.pl
Aus VDR Wiki
Version vom 25. Oktober 2008, 23:51 Uhr von 85.178.233.215 (Diskussion)
Berechnung, wann der Plattenplatz voraussichtlich zur Neige gehen wird.
Das nachfolgende Skript habe ich auf meinem VDR Server in /etc/cron.hourly liegen. Es schreibt eine Nagios Statusmeldung raus.
$PATH/estimate_disk_usage.pl
#!/usr/bin/perl use strict; use warnings; use Time::Local; use Data::Dumper; # VDR's video directory my $vdrdir = "/scratch/vdr"; my $debug = 1; my $backlogdir = "$vdrdir/.estimate_disk_usage_backlog"; my $fps = 25; my $worst_case_ratio = 1.05; my $nagios_status_file = "/var/spool/nagios-status/vdr_disk_usage_estimation"; my $warning_ndays = 1.0; my $critical_ndays = 0.8; if ( ! -d $backlogdir ) { system("install", "-d", "-m", "700", $backlogdir) and die $!; } # zuerst die Backlogs erstellen (wir speichern für jede aufgenommene # Sendung deren mittlere Bitrate mitsamt dem Transponder-Code) my $fh; open ( $fh, "-|", "find", $vdrdir, "-iname", "index.vdr", "-mmin", "+60" ) or die $!; while (<$fh>) { chomp; my $index_vdr_fn = $_; my ( $channel_id, $md5sum, $lmod ) = &get_t ( index_vdr => $index_vdr_fn ); my ( $nframes, $bytesize, $mbitrate ) = &get_size ( index_vdr => $index_vdr_fn ); my %data = ( nframes => $nframes, bytesize => $bytesize, mbitrate => $mbitrate, md5sum => $md5sum, lmod => $lmod, channel_id => $channel_id, ); &store_bitrate ( %data ); } close $fh or die $!; # dann bestimmen wir die maximale Bitrate der letzten N Aufzeichnungen pro Transponder-Code # aus den Backlogs my $last_n_recs = 10; my %max_mbitrate = (); open ( $fh, "-|", "find", $backlogdir, "-mindepth", "1", "-maxdepth", "1", "-type", "d", "-printf", '%P\n' ) or die $!; while (<$fh>) { chomp; my $channel_id = $_; if ( $debug ) { print "$channel_id\n"; } my $max = 0; my $fh2; open ( $fh2, "find \"$backlogdir/$channel_id\" -mindepth 1 -maxdepth 1 -type f -printf \%P\\\\n | sort -n | tail -n $last_n_recs |" ) or die $!; while (<$fh2>) { chomp; my $timestamp = $_; if ( $debug ) { print "$timestamp\n"; } my $fh3; open ( $fh3, "<", "$backlogdir/$channel_id/$timestamp" ) or die $!; chomp ( my $mbitrate = <$fh3> ); close $fh3 or die $!; if ( $mbitrate > $max ) { $max = $mbitrate; } } close $fh2 or die $!; $max_mbitrate{$channel_id} = $max; } close $fh or die $!; if ( $debug ) { print Dumper \%max_mbitrate; } # für den Fall, daß wir keine Erfahrungswerte bei einem speziellen Kanal haben, nehmen wir # eine globale Bitrate my $global_max_mbitrate = 0.1; foreach my $r ( keys %max_mbitrate ) { if ( $max_mbitrate{$r} > $global_max_mbitrate ) { $global_max_mbitrate = $max_mbitrate{$r}; } } if ( $debug ) { print "global max mbit rate = $global_max_mbitrate\n"; } # Timer-Liste einlesen und parsen my @timers = (); open ( $fh, "-|", 'echo -e "lstt id\nquit" | netcat localhost 2001' ) or die $!; while (<$fh>) { chomp; # 250-10 1:T-8468-258-14:2008-06-26:2350:0130:50:99:Aufgemerkt! Pelzig unterh�lt sich~Comedytalk Deutschland 2008:<epgsearch><channel>1 - ARD</channel><searchtimer>aufgemerkt pelzig</searchtimer><start>1214517000</start><stop>1214523000</stop><s-id>27</s-id><eventid>59954</eventid></epgsearch> my $timer = $_; if ( $timer =~ /^250.\d+\s+\d+:(.*?):(\d+)-(\d+)-(\d+):(\d{2,2})(\d{2,2}):(\d{2,2})(\d{2,2}):/ ) { my ( $channel_id, $y, $m, $d, $hhs, $mms, $hhe, $mme ) = ( $1, $2, $3, $4, $5, $6, $7, $8 ); my $start = timelocal( 0, $mms, $hhs, $d, $m-1, $y-1900 ); my $end = timelocal( 0, $mme, $hhe, $d, $m-1, $y-1900 ); if ( $end < $start ) { $end += 86400; } if ( $end < $start ) { die; } if ( $debug ) { print "timer = $timer\n"; print "channel_id = $channel_id\n"; print "Date = " . localtime($start) . "\n"; } my %timer = ( start => $start, channel_id => $channel_id, end => $end, ); push @timers, \%timer; } } close $fh or die $!; if ( $debug ) { print Dumper \@timers; } # und schließlich gehen wir sequentiell die Timer-Liste durch, um festzustellen, wann der # Speicherplatz knapp werden wird. # (bei gleichzeitig laufenden Timern/Aufzeichnungen gehen wir davon aus, daß es ausreichend ist, # die geplanten Aufzeichnungen sequentiell nach ihren Aufzeichnungsstarts durchzugehen und den # benötigten Plattenplatz sequentiell zu akkumulieren, anstatt die Timer parallel durch # Partitionierung an den Überschneidungsstellen durchzugehen) @timers = sort { $a->{start} <=> $b->{start} } @timers; if ( $debug ) { print Dumper \@timers; } my $free = &get_free_diskspace(); my $disk_full_time = 0; foreach my $t ( @timers ) { my $estimated_size_kb = ( $t->{end} - $t->{start} ) * $worst_case_ratio * &get_mbitrate($t->{channel_id}) * 1024 / 8; die if ($estimated_size_kb < 0); if ( $debug ) { print Dumper $t; print "estimated size = $estimated_size_kb kb\n"; } if ( $free < $estimated_size_kb && !$disk_full_time ) { $disk_full_time = int($t->{start} + ($t->{end} - $t->{start}) * ($free/$estimated_size_kb)); if ( $debug ) { print "disk full time = $disk_full_time\n"; } } $free -= $estimated_size_kb; } my $delta = $disk_full_time - time(); if ( $debug ) { print "delta = $delta (local time = ".localtime(time()).", disk full time = ".localtime($disk_full_time)."\n"; } if ( $delta < 0 ) { &write_nagios_status_file ( "0:est. time of disk full is inf, min. free disk space after all scheduled recordings is ".int($free/1024)." MB" ); } elsif ( $delta <= $critical_ndays * 86400 ) { &write_nagios_status_file ( "2:est. time of disk full is ".localtime($disk_full_time).", min. free disk space after all scheduled recordings is ".int($free/1024)." MB" ); } elsif ( $delta <= $warning_ndays * 86400 ) { &write_nagios_status_file ( "1:est. time of disk full is ".localtime($disk_full_time).", min. free disk space after all scheduled recordings is ".int($free/1024)." MB" ); } else { &write_nagios_status_file ( "0:est. time of disk full is ".localtime($disk_full_time).", min. free disk space after all scheduled recordings is ".int($free/1024)." MB" ); } if ( $debug ) { print "estimated free disk space after all scheduled recordings = $free kb\n"; } exit 0; sub write_nagios_status_file { my ( $errmsg ) = @_; if ( $debug ) { print "nagios msg = $errmsg\n"; } my $fh; open ( $fh, ">", $nagios_status_file ) or die $!; print $fh "$errmsg\n"; close $fh or die $!; } sub get_mbitrate { my ( $channel_id ) = @_; if ( exists $max_mbitrate{$channel_id} ) { return $max_mbitrate{$channel_id}; } return $global_max_mbitrate; } sub get_free_diskspace { my $fh; my $free = -1; open ( $fh, "-|", "df", "-k", $vdrdir ) or die $!; while ( <$fh> ) { chomp; if ( /^\S+\s+\d+\s+\d+\s+(\d+)\s+/ ) { $free = $1; } } close $fh or die $!; if ( $free < 0 ) { die; } if ( $debug ) { print "free disk space = $free kb\n"; } return $free; } sub store_bitrate { my ( %p ) = @_; my $dir = "$backlogdir/$p{channel_id}"; system("install", "-d", "-m", "700", $dir) and die $!; my $fh; open ( $fh, ">", "$dir/$p{lmod}" ) or die $!; print $fh $p{mbitrate}; close $fh or die $!; } sub get_size { my ( %p ) = @_; my $fn = $p{index_vdr}; my $dirname = $fn; $dirname =~ s/index\.vdr$//; my $nframes = (stat $fn)[7] / 8; if ( $debug ) { print "get_size():nframes=\"$nframes\"\n"; print "get_size():dirname=\"$dirname\"\n"; } if ( $nframes < 1 ) { return ( 0, 0 ); } my $fh; my $totalsize = 0; open ( $fh, "-|", "find", $dirname, "-type", "f" ) or die $!; while (<$fh>) { chomp; if ( /\/\d+\.vdr/i ) { $totalsize += (stat $_)[7]; } } close $fh or die $!; my $mbitrate = ( 8 * $totalsize / ( $nframes / $fps ) ) / 1048576; if ( $debug ) { print "get_size():totalsize=\"$totalsize\"\n"; print "get_size():bitrate=$mbitrate Mbit/s\n"; } return ( $nframes, $totalsize, $mbitrate ); } sub get_t { my ( %p ) = @_; my $fn = $p{index_vdr}; $fn =~ s/index\.vdr$/info.vdr/i; my $fh; my $result = ""; my $lmod = (stat $fn)[9]; open ( $fh, "<", $fn ) or die $!; while ( <$fh> ) { chomp; if ( /^C\s+([^\s]+)\s*$/ ) { $result = $1; } } close $fh or die $!; if ( $debug ) { print "get_t()=\"$result\"\n"; } if ( $result eq "" ) { die; } my $md5sum = `md5sum -b "$fn"`; die $! if $?; chomp ( $md5sum ); $md5sum =~ s/\s+.*$//; return ( $result, $md5sum, $lmod ); }