#!/usr/bin/perl # example zfs_pool_alert script # ########################################################################### # To be copied into /etc/zfs/zfs_pool_alert (and made executable) if you plan # on using automatic recovery on vdev failure. # ########################################################################### # zfs_pool_alert : this command is executed everytime a pool passes from the # state HEALTHY to DEGRADED, or the other way around. # It receives only the pool name as argument. # Here this script tries to : # - if the new state is degraded # - if there are some spares available, try to use 1 # - otherwise send a mail to tell a vdev is failing for this pool # - if the new state is healthy # - if there are still some spares in use, execute zpool clear, spares are # usually removed quite some time after that without sending any command. use strict; my $zpool = "zpool"; my $pool = shift @ARGV; my $status = `$zpool status $pool`; my ($state) = $status =~ /state: (.+)/; if ($state eq "DEGRADED") { # get the 1st avail spare my ($spare) = $status =~ /[ \t]+(.+)[ \t]+AVAIL/; my ($dev, $cur, $comment) = $status =~ /[ \t]+(.+)[ \t]+(UNAVAIL|FAULTED)[ \t\d]+(.+)/; if ($spare) { $spare = "/dev/$spare" if ($spare !~ /\//); if ($dev) { print "dev :$dev:\n"; if ($comment =~ /was (.+)/) { $dev = $1; } $dev = "/dev/$dev" if ($dev !~ /\//); system("$zpool replace $pool $dev $spare"); } else { print "no dev $dev,$cur,$comment\n"; } } else { # a vdev is failing and there is no spare -> send a mail ? system("echo automatic mail generated by zfs_pool_alert | ". "mail -s 'vdev $dev is failing for pool $pool' root"); } } else { my ($spare) = $status =~ /[ \t]+(.+)[ \t]+INUSE/; if ($spare) { # There is a spare in use, but the pool is already healthy, be sure to clear # the errors then. Notice : the spare is not removed immediately then, and it # can be quite long. Don't know why ! sleep 3; system("$zpool clear $pool"); } }