########################################################################### # $Id$ ########################################################################### # $Log$ # ########################################################################### ########################################################################### # zz-zfs: Output states of ZFS pools and datasets # # Detail Levels: # 0: Output list of pools and capacities # 5: Output full pool status (automatic if any pools are not healthy) # ########################################################################### ####################################################### ## Copyright 2011 Cloyce D. Spradling ## Covered under the included MIT/X-Consortium License: ## http://www.opensource.org/licenses/mit-license.php ## All modifications and contributions by other persons to ## this script are assumed to have been donated to the ## Logwatch project and thus assume the above copyright ## and licensing terms. If you want to make contributions ## under your own copyright or a different license this ## must be explicitly stated in the contribution an the ## Logwatch project reserves the right to not accept such ## contributions. If you have made significant ## contributions to this script and want to claim ## copyright please contact logwatch-devel@lists.sourceforge.net. ######################################################### use strict; use vars qw($logwatch_hostname $DebugCounter); use POSIX; # Keep the pipes hot $| = 1; $ENV{PRINTING} = "y"; my $debug = $ENV{'LOGWATCH_DEBUG'} || 0; my $detail = $ENV{'LOGWATCH_DETAIL_LEVEL'} || $ENV{'zfs_detail'} || 0; my ($os_name, $host, $os_release, $version, $machine) = POSIX::uname(); # Check to see if we should even be looking at this... $host =~ s/\..*//; # Trim domain (if any) if ($ENV{'LOGWATCH_ONLY_HOSTNAME'} && ($logwatch_hostname ne $host)) { exit 0; } my $pathto_zpool = $ENV{'pathto_zpool'} || '/usr/sbin/zpool'; my $pathto_zfs = $ENV{'pathto_zfs'} || '/usr/sbin/zfs'; my $summary_only = $ENV{'summary_only'} || ($detail < 5); my $detail_only = $ENV{'detail_only'} || 0; if (!-x $pathto_zpool) { # Doesn't support ZFS exit 0; } if ( $debug >= 5 ) { print STDERR "\n\nDEBUG: Inside zz-zfs\n\n"; $DebugCounter = 1; } my @pools = (); my %poolinfo = (); my %counts = ( 'mounted' => 0, 'filesystem' => 0, 'snapshot' => 0, 'volume' => 0, ); my $total_pools = 0; # Table for converting things to kibibytes my %units = ( 'P' => 1024 * 1024 * 1024 * 1024, 'T' => 1024 * 1024 * 1024, 'G' => 1024 * 1024, 'M' => 1024, 'K' => 1, ); my $unit_re = '['.join('', keys %units).']'; # Discover the pools open POOLS, '-|', $pathto_zpool, qw(list -H -o name,size,allocated,free,dedupratio,capacity,health) or die "Error running 'zpool list': $!\n"; while() { chomp; my ($name, $size, $used, $avail, $dedup, $cap, $health) = split(/\s+/); next unless $name ne ''; $size = convert_to_kb($size); $used = convert_to_kb($used); $avail = convert_to_kb($avail); print STDERR "\nPOOLS: \"$_\" name=\"$name\" size=$size used=$used avail=$avail dedup=$dedup cap=\"$cap\" health=\"$health\"\n" if ($debug); push @pools, [ $name, $size, $used, $avail, $dedup, $cap, $health ]; } close(POOLS); exit 0 unless @pools; # Nothing to do? # Read filesystem information for each pool foreach my $type (qw( filesystem volume snapshot ) ) { foreach my $pool (map { $_->[0] } @pools) { open POOLINFO, '-|', $pathto_zfs, qw(list -H -t), $type, qw(-o name,referenced,available,mountpoint -r), $pool or die "Error running 'zfs list': $!\n"; while() { next if /no datasets available/i; chomp; my ($name, $used, $avail, $mountpoint) = split(/\s+/); next unless $name ne ''; $used = convert_to_kb($used); $avail = convert_to_kb($avail); print STDERR "\nPOOLINFO: \"$_\" name=\"$name\" used=$used avail=$avail mountpoint=\"$mountpoint\"\n" if ($debug); push @{$poolinfo{$pool}->{$type}}, [ $name, $used, $avail, $mountpoint ]; $counts{$type}++; $counts{'mounted'}++ if ($type eq 'filesystem' && $mountpoint ne 'none'); } close(POOLINFO) } } print "Total ZFS pools: ".(@pools+0)."\n"; print "Total filesystems: $counts{'filesystem'} ($counts{'mounted'} mounted)\n"; print "Total snapshots: $counts{'snapshot'}\n"; print "Total volumes: $counts{'volume'}\n"; if (!$detail_only) { my $pool_format = "%2s%-15s %-10s %-10s %-10s %-10s %s\n"; print "\n------------------- ZFS Pool Summary -------------------\n\n"; printf $pool_format, '', 'Pool Name', 'Size (MiB)', 'Used (MiB)', 'Free (MiB)', 'Dedup', ''; foreach my $poolref (@pools) { my ($name, $size, $used, $avail, $dedup, $cap, $health) = @{$poolref}; my $badflag = ($health eq 'ONLINE') ? '' : '!!'; $detail = 1000 if $badflag ne ''; # Show status if something's wrong printf $pool_format, $badflag, $name, convert_to_mb($size, 10), convert_to_mb($used, 10), convert_to_mb($avail, 10), $dedup, $cap; } print "\n--------------------------------------------------------\n\n"; } if (!$summary_only || $detail > 999) { print "\n------------------- ZFS Pool Status -------------------\n\n"; foreach my $pool (map { $_->[0] } @pools) { system $pathto_zpool, 'status', $pool; } print "\n-------------------------------------------------------\n\n"; } sub convert_to_kb { my ($val) = @_; my ($num, $unit) = ($val =~ /([\.\d]+)\s*($unit_re)?/io); return 0 if $num eq ''; $unit = uc($unit); return $num * $units{$unit}; } sub convert_to_mb { my ($kb, $width) = @_; $width = 0 unless defined($width); $kb /= 1024; $kb = int(($kb * 10) + 0.5) / 10; return sprintf '%*.1f', $width, $kb; } # vi: shiftwidth=3 tabstop=3 syntax=perl et