patternMinor
Perl script to check disk usage and report via email
Viewed 0 times
scriptdiskperlemailusagereportviaandcheck
Problem
Due to lack of central configuration management I am avoiding any Perl modules which would need to be installed on each of a great many servers. Result is more code than would normally be required in order to format HTML and send email as well as make system calls which are less desirable.
```
#!/usr/bin/perl
use strict;
use warnings;
use Sys::Hostname;
use POSIX qw(uname);
my (%fsSize, %fsFree, %fsPct, %overrides);
my ($thresh, $fh);
my $repFile = "/tmp/chkDiskResults.txt";
my $send = 0;
my $hostname = hostname();
my @uname = uname();
# Determine the OS and set the 'df' command appropriately
my $df;
if ($uname[0] =~ 'AIX') {
$df = "df -tg";
} elsif ($uname[0] =~ 'Linux') {
$df = "df -h";
}
# Check for an override file loading it if it exists and running
# simple checks to ensure the values are valid.
my $overrideFile = "/etc/override";
if (-e $overrideFile) {
open($fh, ') {
my @split = split /\s+/, $line;
unless (!$split[1] || $split[1] !~ /^[0-9]+$/) {
$overrides{$split[0]} = $split[1];
}
}
close($fh);
}
# Execute the system 'df' command ignoring anything that isn't a
# real filesystem
# $cols[1] => Total space in GB
# $cols[3] => Free space in GB
# $cols[4] => Percent used column
# $cols[5] => Mounted on column
foreach my $line (qx[$df |grep -E -v "(Filesystem|proc|tmpfs)"]) {
my @cols = split /\s+/, $line;
chop($cols[1]);
chomp($cols[3]);
chop($cols[4]);
# set threshold based on disk size; A 1TB disk doesn't need
# to alert when 100GB are available
if ($cols[1] >= 800) {
$thresh = 98;
} elsif ($cols[1] = 400) {
$thresh = 96;
} elsif ($cols[1] = 200) {
$thresh = 94;
} elsif ($cols[1] = 100) {
$thresh = 92;
} else {
$thresh = 90;
}
$fsSize{$cols[5]} = $cols[1];# . "G";
$fsFree{$cols[5]} = $cols[3];# . "G";
$fsPct{$cols[5]} = $cols[4];
}
# Do the needful; override the thresholds if necessary; write
# offending filesystems to /tmp/chkDiskResults.txt as HTML
# since Outlook mang
```
#!/usr/bin/perl
use strict;
use warnings;
use Sys::Hostname;
use POSIX qw(uname);
my (%fsSize, %fsFree, %fsPct, %overrides);
my ($thresh, $fh);
my $repFile = "/tmp/chkDiskResults.txt";
my $send = 0;
my $hostname = hostname();
my @uname = uname();
# Determine the OS and set the 'df' command appropriately
my $df;
if ($uname[0] =~ 'AIX') {
$df = "df -tg";
} elsif ($uname[0] =~ 'Linux') {
$df = "df -h";
}
# Check for an override file loading it if it exists and running
# simple checks to ensure the values are valid.
my $overrideFile = "/etc/override";
if (-e $overrideFile) {
open($fh, ') {
my @split = split /\s+/, $line;
unless (!$split[1] || $split[1] !~ /^[0-9]+$/) {
$overrides{$split[0]} = $split[1];
}
}
close($fh);
}
# Execute the system 'df' command ignoring anything that isn't a
# real filesystem
# $cols[1] => Total space in GB
# $cols[3] => Free space in GB
# $cols[4] => Percent used column
# $cols[5] => Mounted on column
foreach my $line (qx[$df |grep -E -v "(Filesystem|proc|tmpfs)"]) {
my @cols = split /\s+/, $line;
chop($cols[1]);
chomp($cols[3]);
chop($cols[4]);
# set threshold based on disk size; A 1TB disk doesn't need
# to alert when 100GB are available
if ($cols[1] >= 800) {
$thresh = 98;
} elsif ($cols[1] = 400) {
$thresh = 96;
} elsif ($cols[1] = 200) {
$thresh = 94;
} elsif ($cols[1] = 100) {
$thresh = 92;
} else {
$thresh = 90;
}
$fsSize{$cols[5]} = $cols[1];# . "G";
$fsFree{$cols[5]} = $cols[3];# . "G";
$fsPct{$cols[5]} = $cols[4];
}
# Do the needful; override the thresholds if necessary; write
# offending filesystems to /tmp/chkDiskResults.txt as HTML
# since Outlook mang
Solution
-
If you have a machine with something like a dvd reader/writer and a disk inserted, the use % will always be 100%. This is actually more common than one might think if, for example, you have a virtual machine with VirtualBox and the guest additions CD is mounted.
-
On my own machine (Ubuntu 14.04), the output of
Which will not be parsed correctly because of the
As for the code itself:
These lines:
can be replaced by
This part:
Is actually just:
This is part of the script how I would write it. It's not complete, because it's missing the override and the sending part, but I'm sure you can figure out how to do that.
If you have a machine with something like a dvd reader/writer and a disk inserted, the use % will always be 100%. This is actually more common than one might think if, for example, you have a virtual machine with VirtualBox and the guest additions CD is mounted.
-
On my own machine (Ubuntu 14.04), the output of
df -h is
udev 3,9G 4,0K 3,9G 1% /dev
none 3,9G 147M 3,8G 4% /run/shmWhich will not be parsed correctly because of the
, instead of the .As for the code itself:
- You're not using a proper temp file. I understand not wanting to use external modules, but
File::Tempis a core module and it will be there unless your perl environment is broken.
- You don't have even a single sub and this makes the code a lot less readable.
- You have a double negation in
unless (!$split[1] || $split[1] !~ /^[0-9]+$/) {. This is the same asif ($split[1] || $split[1] =~ /^[0-9]+$/)which I think is more readable (btw,unlessis the same asif not, but it's not exactly the same asif !because of precedence).
These lines:
my @cols = split /\s+/, $line;
chop($cols[1]);
chomp($cols[3]);
chop($cols[4]);can be replaced by
my @cols = map { substr($_, 0, length($_)-1) || 0 } split /\s+/, $line;This part:
if ($cols[1] >= 800) {
$thresh = 98;
} elsif ($cols[1] = 400) {
$thresh = 96;
} elsif ($cols[1] = 200) {
$thresh = 94;
} elsif ($cols[1] = 100) {
$thresh = 92;
} else {
$thresh = 90;
}Is actually just:
if ($cols[1] >= 800) {
$thresh = 98;
} elsif ($cols[1] >= 400) {
$thresh = 96;
} elsif ($cols[1] >= 200) {
$thresh = 94;
} elsif ($cols[1] >= 100) {
$thresh = 92;
} else {
$thresh = 90;
}- There is no reason to have `
and` in a separate print.
- You're not differentiating thresholds by filesystem, you have only one even if the filesystems have different sizes.
- I think it would be more readable if you used only one hash to store everything and distinguish by key.
This is part of the script how I would write it. It's not complete, because it's missing the override and the sending part, but I'm sure you can figure out how to do that.
#!/usr/bin/perl
use strict;
use warnings;
use File::Temp qw(tempfile);
use Sys::Hostname;
use POSIX qw(uname);
use Data::Dumper;
my %overrides;
my $send = 0;
my $filename = write_report_file( get_fs_data() );
print Dumper $filename; # This is the file you can send
sub get_df_command {
my $df;
my @uname = uname();
if ( $uname[0] =~ 'AIX' ) {
$df = "df -tg";
}
elsif ( $uname[0] =~ 'Linux' ) {
$df = "df -h";
}
return $df;
}
sub get_fs_data {
my %fs_data;
my $df = get_df_command();
foreach my $line (qx[$df |grep -E -v "(Filesystem|proc|tmpfs)"]) {
$line =~ s/,/\./g;
my @cols = map { substr( $_, 0, length($_) - 1 ) || 0 } split(/\s+/, $line);
my $thresh;
if ( $cols[1] >= 800 ) {
$thresh = 98;
}
elsif ( $cols[1] >= 400 ) {
$thresh = 96;
}
elsif ( $cols[1] >= 200 ) {
$thresh = 94;
}
elsif ( $cols[1] >= 100 ) {
$thresh = 92;
}
else {
$thresh = 90;
}
$fs_data{ $cols[5] } = {
total => $cols[1],
free => $cols[3],
percent_used => $cols[4],
threshold => $thresh,
};
}
return \%fs_data;
}
sub write_report_file {
my ($data) = @_;
my %fs_data = %{$data};
my $hostname = hostname();
my ( $rep_fh, $rep_filename ) = tempfile( UNLINK => 0 ) or die "Unable to open temp file: $!";
print $rep_fh
Disk usage report for $hostname
Filesystem
Size
Free
Percent Used
EOF
foreach my $filesystem ( keys(%fs_data) ) {
my $thresh = $overrides{$filesystem} // $fs_data{$filesystem}->{threshold};
if ( $fs_data{$filesystem}->{percent_used} >= $thresh ) {
$send = 1;
print $rep_fh
$filesystem
$fs_data{$filesystem}->{total}G
$fs_data{$filesystem}->{free}G
$fs_data{$filesystem}->{percent_used}%
EOF
}
}
print $rep_fh
EOF
close($rep_fh);
return $rep_filename;
}Code Snippets
my @cols = split /\s+/, $line;
chop($cols[1]);
chomp($cols[3]);
chop($cols[4]);if ($cols[1] >= 800) {
$thresh = 98;
} elsif ($cols[1] < 800 && $cols[1] >= 400) {
$thresh = 96;
} elsif ($cols[1] < 400 && $cols[1] >= 200) {
$thresh = 94;
} elsif ($cols[1] < 200 && $cols[1] >= 100) {
$thresh = 92;
} else {
$thresh = 90;
}if ($cols[1] >= 800) {
$thresh = 98;
} elsif ($cols[1] >= 400) {
$thresh = 96;
} elsif ($cols[1] >= 200) {
$thresh = 94;
} elsif ($cols[1] >= 100) {
$thresh = 92;
} else {
$thresh = 90;
}#!/usr/bin/perl
use strict;
use warnings;
use File::Temp qw(tempfile);
use Sys::Hostname;
use POSIX qw(uname);
use Data::Dumper;
my %overrides;
my $send = 0;
my $filename = write_report_file( get_fs_data() );
print Dumper $filename; # This is the file you can send
sub get_df_command {
my $df;
my @uname = uname();
if ( $uname[0] =~ 'AIX' ) {
$df = "df -tg";
}
elsif ( $uname[0] =~ 'Linux' ) {
$df = "df -h";
}
return $df;
}
sub get_fs_data {
my %fs_data;
my $df = get_df_command();
foreach my $line (qx[$df |grep -E -v "(Filesystem|proc|tmpfs)"]) {
$line =~ s/,/\./g;
my @cols = map { substr( $_, 0, length($_) - 1 ) || 0 } split(/\s+/, $line);
my $thresh;
if ( $cols[1] >= 800 ) {
$thresh = 98;
}
elsif ( $cols[1] >= 400 ) {
$thresh = 96;
}
elsif ( $cols[1] >= 200 ) {
$thresh = 94;
}
elsif ( $cols[1] >= 100 ) {
$thresh = 92;
}
else {
$thresh = 90;
}
$fs_data{ $cols[5] } = {
total => $cols[1],
free => $cols[3],
percent_used => $cols[4],
threshold => $thresh,
};
}
return \%fs_data;
}
sub write_report_file {
my ($data) = @_;
my %fs_data = %{$data};
my $hostname = hostname();
my ( $rep_fh, $rep_filename ) = tempfile( UNLINK => 0 ) or die "Unable to open temp file: $!";
print $rep_fh <<"EOF";
<html>
<body>
<h1>Disk usage report for $hostname</h1>
<table width="500">
<tr>
<th align="left">Filesystem</th>
<th>Size</th>
<th>Free</th>
<th>Percent Used</th>
</tr>
EOF
foreach my $filesystem ( keys(%fs_data) ) {
my $thresh = $overrides{$filesystem} // $fs_data{$filesystem}->{threshold};
if ( $fs_data{$filesystem}->{percent_used} >= $thresh ) {
$send = 1;
print $rep_fh <<"EOF";
<tr>
<td>$filesystem</td>
<td align="center">$fs_data{$filesystem}->{total}G</td>
<td align="center">$fs_data{$filesystem}->{free}G</td>
<td align="center">$fs_data{$filesystem}->{percent_used}%</td>
</tr>
EOF
}
}
print $rep_fh <<"EOF";
</table>
</body>
</html>
EOF
close($rep_fh);
return $rep_filename;
}Context
StackExchange Code Review Q#160384, answer score: 2
Revisions (0)
No revisions yet.