From 636d2bc385af29f3f773eb0cdc8fed8992f367fd Mon Sep 17 00:00:00 2001 From: Ryan O'Hara <rohara@redhat.com> Date: Mon, 27 Sep 2010 10:50:12 -0500 Subject: [PATCH] fence_scsi_test: rewrite to more accurately test The old fence_scsi_test tool was far too simple to adequately test it a devices was capable of being used with fence_scsi. This rewrite should adequately test all requirements, such as support for "preempt-and-abort" subcommand. This script will also verify that each command worked as expected. Resolves: rhbz#603838 Signed-off-by: Ryan O'Hara <rohara@redhat.com> --- fence/agents/scsi/fence_scsi_test.pl | 748 ++++++++++++++++++++++++++-------- fence/man/fence_scsi_test.8 | 68 +++ 2 files changed, 653 insertions(+), 163 deletions(-) create mode 100644 fence/man/fence_scsi_test.8 diff --git a/fence/agents/scsi/fence_scsi_test.pl b/fence/agents/scsi/fence_scsi_test.pl index e24ef61..25db6ba 100755 --- a/fence/agents/scsi/fence_scsi_test.pl +++ b/fence/agents/scsi/fence_scsi_test.pl @@ -1,272 +1,694 @@ #!/usr/bin/perl -use POSIX; -use IPC::Open3; -use XML::LibXML; -use Getopt::Std; - -my @devices; -my %results; - #BEGIN_VERSION_GENERATION $FENCE_RELEASE_NAME=""; $REDHAT_COPYRIGHT=""; $BUILD_DATE=""; #END_VERSION_GENERATION -sub get_scsi_block_devices +################################################################################ + +use File::Basename; +use Term::ANSIColor; +use Getopt::Long; +use XML::LibXML; + +################################################################################ + +my @devices = (); +my %devices_name = (); +my %devices_path = (); +my %options = (); +my %results = (); + +################################################################################ + +sub do_action_on (\@$$) { - print "[debug] get_scsi_block_devices\n" if ($opt_d); + my @devices = @{(shift)}; + my ($dev, $node_key) = @_; + my $err = 0; - my $block_dir = "/sys/block"; + if (defined $options{'v'}) { + $self = (caller(0))[3]; + print " $self (dev=$dev node_key=$node_key)\n"; + } - opendir(DIR, $block_dir) or die "$!\n"; + foreach (@devices) { + if (do_reset ($_) != 0) { + } + if (do_register_ignore ($_, $node_key) != 0) { + $err++; + } + } - my @block_devices = grep { /^sd*/ } readdir(DIR); + if (scalar (get_keys_reserve ($dev)) == 0) { + if (do_reserve ($dev, $node_key) != 0) { + $err = 0; + } + } - closedir(DIR); + return ($err); +} - for $block_dev (@block_devices) - { - print "[debug] \t device = $block_dev\n" if ($opt_d); +sub do_action_off ($$$) +{ + my ($dev, $node_key, $host_key) = @_; + my $err = 0; - push(@devices, "/dev/" . $block_dev); + if (defined $options{'v'}) { + $self = (caller(0))[3]; + print " $self (dev=$dev node_key=$node_key host_key=$host_key)\n"; } + + if (do_preempt_abort ($dev, $host_key, $node_key) != 0) { + $err++; + } + + return ($err); } -sub get_cluster_vol_devices +sub do_verify_on (\@$$) { - print "[debug] get_cluster_vol_devices\n" if ($opt_d); + my @devices = @{(shift)}; + my ($dev, $node_key) = @_; + my $err = 0; + + if (defined $options{'v'}) { + $self = (caller(0))[3]; + print " $self (dev=$dev node_key=$node_key)\n"; + } + + @keys = grep { /^$node_key$/ } get_keys_register ($dev); - my ($in, $out, $err); + if (scalar (@keys) != scalar (@devices)) { + $err++; + } - my $cmd = "vgs --config 'global { locking_type = 0 }'" . - " --noheadings --separator : -o vg_attr,pv_name"; + @keys = get_keys_reserve ($dev); - my $pid = open3($in, $out, $err, $cmd) or die "$!\n"; + if (scalar (@keys) == 0) { + $err++; + } - waitpid($pid, 0); + return ($err); +} - die "[error] unable to execute vgs command.\n" if WEXITSTATUS($?); +sub do_verify_off ($$$) +{ + my ($dev, $node_key, $host_key) = @_; + my $err = 0; - while (<$out>) - { - chomp; + if (defined $options{'v'}) { + $self = (caller(0))[3]; + print " $self (dev=$dev node_key=$node_key host_key=$host_key)\n"; + } - my ($vg_attr, $pv_name) = split(/:/, $_); + @keys = grep { /^$node_key$/ } get_keys_register ($dev); - if ($vg_attr =~ /.*c$/) - { - print "[debug] \t device = $pv_name\n" if ($opt_d); + if (scalar (@keys) != 0) { + $err++; + } - push(@devices, $pv_name); - } + @keys = grep { /^$host_key$/ } get_keys_reserve ($dev); + + if (scalar (@keys) == 0) { + $err++; } - close($in); - close($out); - close($err); + return ($err); } -sub register_device +sub do_key_write ($) { - my ($dev, $key) = @_; - my ($in, $out, $err); + my $key = shift; - print "[debug] register_device (device=$dev, key=$key)\n" if ($opt_d); + open (\*FILE, ">/tmp/.fence_scsi_test") or die "$!\n"; + print (FILE "$key\n"); + close (FILE); - my $cmd = "sg_persist -n -d $dev -o -G -S $key"; + if (defined $options{'v'}) { + $self = (caller(0))[3]; + print " $self (key=$key)\n"; + } - print "[debug] \t cmd = $cmd\n" if ($opt_d); + return; +} - my $pid = open3($in, $out, $err, $cmd) or die "$!\n"; +sub do_key_read ($) +{ + my $key = shift; - waitpid($pid, 0); + open (\*FILE, "</tmp/.fence_scsi_test") or die "$!\n"; + chomp ($$key = <FILE>); + close (FILE); - $results{$dev}[0] = WEXITSTATUS($?); + if (defined $options{'v'}) { + $self = (caller(0))[3]; + print " $self (key=$$key)\n"; + } - close($in); - close($out); - close($err); + return; } -sub unregister_device +sub do_register ($$) { - my ($dev, $key) = @_; - my ($in, $out, $err); + my ($dev, $sark) = @_; - print "[debug] unregister_device (device=$dev, key=$key)\n" if ($opt_d); + my $cmd = "sg_persist -n -o -G -S $sark -d $dev"; + my @out = qx { $cmd 2> /dev/null }; + my $err = ($?>>8); - my $cmd = "sg_persist -n -d $dev -o -G -K $key -S 0"; + if (defined $options{'v'}) { + $self = (caller(0))[3]; + print " $self (dev=$dev sark=$sark)\n"; + print " cmd=$cmd\n"; + print " err=$err\n"; + } - print "[debug] \t cmd = $cmd\n" if ($opt_d); + return ($err); +} - my $pid = open3($in, $out, $err, $cmd) or die "$!\n"; +sub do_register_ignore ($$) +{ + my ($dev, $sark) = @_; - waitpid($pid, 0); + my $cmd = "sg_persist -n -o -I -S $sark -d $dev"; + my @out = qx { $cmd 2> /dev/null }; + my $err = ($?>>8); - $results{$dev}[1] = WEXITSTATUS($?); + if (defined $options{'v'}) { + $self = (caller(0))[3]; + print " $self (dev=$dev sark=$sark)\n"; + print " cmd=$cmd\n"; + print " err=$err\n"; + } - close($in); - close($out); - close($err); + return ($err); } -sub test_devices +sub do_reserve ($$) { - my $key = "0x1"; + my ($dev, $rk) = @_; + + my $cmd = "sg_persist -n -o -R -T 5 -K $rk -d $dev"; + my @out = qx { $cmd 2> /dev/null }; + my $err = ($?>>8); - foreach $dev (@devices) - { - register_device($dev, $key); - unregister_device($dev, $key); + if (defined $options{'v'}) { + $self = (caller(0))[3]; + print " $self (dev=$dev rk=$rk)\n"; + print " cmd=$cmd\n"; + print " err=$err\n"; } + + return ($err); } -sub check_config_fence +sub do_release ($$) { - my $xml = XML::LibXML->new(); - my $tree = $xml->parse_file("/etc/cluster/cluster.conf"); - my $root = "//cluster/fencedevices/fencedevice"; + my ($dev, $rk) = @_; - my $xpath_fence = "count(${root}[\@agent='fence_scsi'])"; + my $cmd = "sg_persist -n -o -L -T 5 -K $rk -d $dev"; + my @out = qx { $cmd 2> /dev/null }; + my $err = ($?>>8); + + if (defined $options{'v'}) { + $self = (caller(0))[3]; + print " $self (dev=$dev rk=$rk)\n"; + print " cmd=$cmd\n"; + print " err=$err\n"; + } - return ( ! $tree->findvalue($xpath_fence)); + return ($err); } -sub check_config_nodes +sub do_preempt ($$$) { - my $xml = XML::LibXML->new(); - my $tree = $xml->parse_file("/etc/cluster/cluster.conf"); - my $root = "//cluster/clusternodes/clusternode"; + my ($dev, $rk, $sark) = @_; - my $xpath_name = "count(${root}/\@name)"; - my $xpath_nodeid = "count(${root}/\@nodeid)"; + my $cmd = "sg_persist -n -o -P -T 5 -K $rk -S $sark -d $dev"; + my @out = qx { $cmd 2> /dev/null }; + my $err = ($?>>8); + + if (defined $options{'v'}) { + $self = (caller(0))[3]; + print " $self (dev=$dev rk=$rk sark=$sark)\n"; + print " cmd=$cmd\n"; + print " err=$err\n"; + } - return ($tree->findvalue($xpath_name) != $tree->findvalue($xpath_nodeid)); + return ($err); } -sub print_results +sub do_preempt_abort ($$$) { - my $device_count = scalar(@devices); + my ($dev, $rk, $sark) = @_; - my $failure_count = 0; - my $success_count = 0; + my $cmd = "sg_persist -n -o -A -T 5 -K $rk -S $sark -d $dev"; + my @out = qx { $cmd 2> /dev/null }; + my $err = ($?>>8); - print "\nAttempted to register with devices:\n"; - print "-------------------------------------\n"; + if (defined $options{'v'}) { + $self = (caller(0))[3]; + print " $self (dev=$dev rk=$rk sark=$sark)\n"; + print " cmd=$cmd\n"; + print " err=$err\n"; + } - for $dev (@devices) - { - print "\t$dev\t"; - if ($results{$dev}[0] == 0) - { - $success_count++; - print "Success\n"; - } - else - { - $failure_count++; - print "Failure\n"; - } + return ($err); +} + +sub do_clear ($$) +{ + my ($dev, $rk) = @_; + + my $cmd = "sg_persist -n -o -C -K $rk -d $dev"; + my @out = qx { $cmd 2> /dev/null }; + my $err = ($?>>8); + + if (defined $options{'v'}) { + $self = (caller(0))[3]; + print " $self (dev=$dev rk=$rk)\n"; + print " cmd=$cmd\n"; + print " err=$err\n"; } - print "-------------------------------------\n"; - print "Number of devices tested: $device_count\n"; - print "Number of devices passed: $success_count\n"; - print "Number of devices failed: $failure_count\n\n"; + return ($err); +} + +sub do_reset ($) +{ + my ($dev) = @_; + + my $cmd = "sg_turs $dev"; + my @out = qx { $cmd 2> /dev/null }; + my $err = ($?>>8); - if ($failure_count != 0) - { - exit(1); + if (defined $options{'v'}) { + $self = (caller(0))[3]; + print " $self (dev=$dev)\n"; + print " cmd=$cmd\n"; + print " err=$err\n"; } + + return ($err); } -sub print_usage +sub get_options () { - print "\nUsage: fence_scsi_test [-c|-s] [-d] [-h]\n\n"; + my @opts = ("d|device=s", + "h|help", + "k|key=s", + "o|action=s", + "t|test=s", + "v|verbose"); - print "Options:\n\n"; + Getopt::Long::Configure ("no_auto_abbrev") or exit (1); + Getopt::Long::GetOptions (\%options, @opts) or exit (1); - print " -c Cluster mode. This mode is intended to test\n"; - print " SCSI persistent reservation capabilties for\n"; - print " devices that are part of existing clustered\n"; - print " volumes. Only devices in LVM cluster volumes\n"; - print " will be tested.\n\n"; - print " -s SCSI mode. This mode is intended to test SCSI\n"; - print " persistent reservation capabilities for all SCSI\n"; - print " devices visible on a node.\n\n"; - print " -d Debug flag. This will print debugging information\n"; - print " such as the actual commands being run to register\n"; - print " and unregister a device.\n\n"; - print " -h Help. Prints out this usage information.\n\n"; + return; } -sub test_tools_path +sub get_devices () { - my $tool_name = "sg_persist"; - - for my $path ( split /:/, $ENV{PATH} ) { - if ( -f "$path/$tool_name" && -x _ ) { - return; - } + my @devices = split (/,/, $options{'d'}); + + map { s/^\s+// } @devices; + map { s/\s+$// } @devices; + + if (defined $options{'v'}) { + $self = (caller(0))[3]; + print " $self\n"; + } + + return (@devices); +} + +sub get_devices_clvm () +{ + my @devices = (); + + my $cmd = "vgs" . + " --noheadings" . + " --sort pv_uuid" . + " --options pv_name,vg_attr" . + " --config 'global { locking_type=0 }'"; + + my @out = qx { $cmd 2> /dev/null }; + my $err = ($?>>8); + + @devices = map { (split)[0] } grep { /c$/ } @out; + + if (defined $options{'v'}) { + $self = (caller(0))[3]; + print " $self\n"; + } + + return (@devices); +} + +sub get_devices_scsi () +{ + my @devices = (); + + open (\*DIR, "/sys/block/") or die "$!\n"; + @devices = grep { /^sd/ } readdir (DIR); + close (DIR); + + if (defined $options{'v'}) { + $self = (caller(0))[3]; + print " $self\n"; + } + + return (@devices); +} + +sub get_devices_name ($) +{ + my $dev = shift; + my $name = basename ($dev); + + if (-l "/dev/mpath/$name") { + $name = basename (readlink "/dev/mpath/$name"); + } + + $name = "/dev/$name"; + + if (defined $options{'v'}) { + $self = (caller(0))[3]; + print " $self (dev=$dev)\n"; + print " name=$name\n"; } - - die "No $tool_name command available, please install the sg3_utils package.\n" + + return ($name); } -### MAIN ####################################################################### +sub get_devices_path ($) +{ + my $dev = shift; + my $name = basename ($dev); + my @path = (); + + if (defined $options{'v'}) { + $self = (caller(0))[3]; + print " $self (dev=$dev)\n"; + } + + if (-d "/sys/block/$name/slaves/") { + opendir (\*DIR, "/sys/block/$name/slaves/"); + @path = grep { !/^\./ } readdir (DIR); + closedir (DIR); + } + + if (scalar (@path) == 0) { + push (@path, $name); + } -test_tools_path; + if ($path[0] =~ /^dm/) { + @path = get_devices_path ("/dev/$path[0]"); + } else { + @path = map { "/dev/$_" } @path; + if (defined $options{'v'}) { + print " path=[ @path ]\n"; + } + } -if (getopts("cdhst:v") == 0) + return (@path); +} + +sub get_devices_info ($) { - print_usage; - exit(1); + my $dev = shift; + my $name = basename ($dev); + my @info = (); + + if (-e "/sys/block/$name/device/vendor") { + open (\*FILE, "</sys/block/$name/device/vendor"); + chomp ($info[0] = <FILE>); + close (FILE); + } + + if (-e "/sys/block/$name/device/model") { + open (\*FILE, "</sys/block/$name/device/model"); + chomp ($info[1] = <FILE>); + close (FILE); + } + + map { s/^\s+// } @info; + map { s/\s+$// } @info; + + if (defined $options{'v'}) { + $self = (caller(0))[3]; + print " $self (dev=$dev)\n"; + print " info=[ @info ]\n"; + } + + return (@info); } -if ($opt_h) +sub get_keys_register ($) { - print_usage; - exit(0); + my $dev = shift; + my @keys = (); + + my $cmd = "sg_persist -n -i -k -d $dev"; + my @out = qx { $cmd 2> /dev/null }; + my $err = ($?>>8); + + foreach (@out) { + chomp; + push (@keys, $_) if ($_ =~ s/^\s+0x//i); + } + + if (defined $options{'v'}) { + $self = (caller(0))[3]; + print " $self (dev=$dev)\n"; + print " cmd=$cmd\n"; + print " err=$err\n"; + print " keys=[ @keys ]\n"; + } + + return (@keys); } -if ($opt_c && $opt_s) { - print_usage; - exit(1); +sub get_keys_reserve +{ + my $dev = shift; + my @keys = (); + + my $cmd = "sg_persist -n -i -r -d $dev"; + my @out = qx { $cmd 2> /dev/null }; + my $err = ($?>>8); + + foreach (@out) { + chomp; + push (@keys, $_) if ($_ =~ s/^\s+key=0x//i); + } + + if (defined $options{'v'}) { + $self = (caller(0))[3]; + print " $self (dev=$dev)\n"; + print " cmd=$cmd\n"; + print " err=$err\n"; + print " keys=[ @keys ]\n"; + } + + return (@keys); } -if ($opt_c) +sub test_config_fence () { - print "\nTesting devices in cluster volumes...\n"; - get_cluster_vol_devices; - test_devices; - print_results; + my $xml = XML::LibXML->new(); + my $file = "/etc/cluster/cluster.conf"; + my $tree = $xml->parse_file ($file); + my $root = "//cluster/fencedevices/fencedevice"; + my $err = 0; + + my $xpath_agent = "count(${root}[\@agent='fence_scsi'])"; + + if ($tree->findvalue ($xpath_agent) == 0) { + $err = 1; + } + + exit ($err); } -if ($opt_s) +sub test_config_nodes () { - print "\nTesting all SCSI block devices...\n"; - get_scsi_block_devices; - test_devices; - print_results; + my $xml = XML::LibXML->new(); + my $file = "/etc/cluster/cluster.conf"; + my $tree = $xml->parse_file ($file); + my $root = "//cluster/clusternodes/clusternode"; + my $err = 0; + + my $xpath_name = "count(${root}/\@name)"; + my $xpath_nodeid = "count(${root}/\@nodeid)"; + + if ($tree->findvalue ($xpath_name) != $tree->findvalue ($xpath_nodeid)) { + $err = 1; + } + + exit ($err); } -if ($opt_t) +sub print_devices () { - if ($opt_t eq "fence") - { - exit check_config_fence; + if (defined $options{'v'}) { + $self = (caller(0))[3]; + print " $self\n"; + foreach (@devices) { + print " $_\n"; + } } - if ($opt_t eq "nodes") - { - exit check_config_nodes; + + return; +} + +sub print_options () +{ + return; +} + +sub print_results ($) +{ + my $dev = shift; + my @results = @{$results{$dev}}; + + if (defined $options{'v'}) { + $self = (caller(0))[3]; + print " $self (dev=$dev)\n"; + print " results=[ action=$results[0] verify=$results[1] ]\n"; + } else { + printf (" %-*s", $width, $_); + if (grep { $_ } @results) { + $test = colored (" FAIL ", "red"); + } else { + $test = colored (" OK ", "green"); + } + printf ("[%s]\n", $test); } + + return; } -if (!$opt_c && !$opt_s && !$opt_t) +sub print_usage { - print "\nPlease specify either cluster or SCSI mode.\n"; - print_usage; - exit(1); + print "\n"; + print "Usage: fence_scsi_test -o <on|off> -k <key> [options]\n"; + print "\n"; + print "Actions:\n"; + print "\n"; + print " on Register <key> with the devices.\n"; + print " off Remove <key> form the devices.\n"; + print "\n"; + print "Options:\n"; + print "\n"; + print " -d, --devices=LIST Devices used for the current action.\n"; + print " -h, --help Display this help and exit.\n"; + print " -v, --verbose Verbose mode.\n"; + print "\n"; + + defined $options{'h'} ? exit (0) : exit (1); +} + +################################################################################ + +get_options (); + +if (scalar (keys %options) == 0) { + print_usage (); +} + +if (defined $options{'v'}) { + print_options (); +} + +if (defined $options{'h'}) { + print_usage (); +} + +if (defined $options{'t'}) { + for ($options{'t'}) { + ($_ =~ /^fence$/i) && do { + test_config_fence (); + last; + }; + ($_ =~ /^nodes$/i) && do { + test_config_nodes (); + last; + }; + exit (1); + } +} + +if ($options{'k'} =~ /^[[:xdigit:]]+$/) { + $node_key = lc ($options{'k'}); +} else { + print_usage (); +} + +for ($options{'o'}) { + ($_ =~ /^on$/i) && do { + do_key_write ($node_key); + last; + }; + ($_ =~ /^off$/i) && do { + do_key_read (\$host_key); + last; + }; + print_usage (); +} + +if (defined $options{'d'}) { + @devices = get_devices (); +} else { + @devices = get_devices_clvm (); +} + +if (scalar (@devices) == 0) { + die "error: no devices found\n"; +} else { + $count = scalar (@devices); + $width = (reverse sort { $a <=> $b } map { (length) + 8 } @devices)[0]; +} + +if (defined $options{'v'}) { + print_devices (); +} + +foreach (@devices) { + if (! -e $_) { + die "error: $_ does not exist\n"; + } + if (! -b $_) { + die "error: $_ is not a block device\n"; + } + + $devices_name{$_} = get_devices_name ($_); + $devices_path{$_} = [ get_devices_path ($devices_name{$_}) ]; +} + +for ($options{'o'}) { + ($_ =~ /^on$/i) && do { + print "\n" if (!defined $options{'v'}); + foreach (@devices) { + $results{$_}[0] = do_action_on (@{$devices_path{$_}}, $_, $node_key); + $results{$_}[1] = do_verify_on (@{$devices_path{$_}}, $_, $node_key); + print_results ($_); + } + print "\n" if (!defined $options{'v'}); + last; + }; + ($_ =~ /^off$/i) && do { + print "\n" if (!defined $options{'v'}); + foreach (@devices) { + $results{$_}[0] = do_action_off ($devices_name{$_}, $node_key, $host_key); + $results{$_}[1] = do_verify_off ($devices_name{$_}, $node_key, $host_key); + print_results ($_); + } + print "\n" if (!defined $options{'v'}); + last; + }; + print_usage (); } diff --git a/fence/man/fence_scsi_test.8 b/fence/man/fence_scsi_test.8 new file mode 100644 index 0000000..3252dca --- /dev/null +++ b/fence/man/fence_scsi_test.8 @@ -0,0 +1,68 @@ +.\" Copyright (C) 2010 Red Hat, Inc. All rights reserved. +.\" +.\" This copyrighted material is made available to anyone wishing to use, +.\" modify, copy, or redistribute it subject to the terms and conditions +.\" of the GNU General Public License v.2. + +.TH fence_scsi_test 8 + +.SH NAME +fence_scsi_test - Test for SCSI persistent reservation fencing + +.SH SYNOPSIS +.B +fence_scsi_test -o <on|off> -k <key> +[\fIOPTIONS\fR] + +.SH DESCRIPTION +fence_scsi_test is a script used to verify that specific hardware +configurations are capable of being used with the fence_scsi +agent. All storage devices that are to be used with the fence_scsi +agent must be capable of SCSI persistent reservations. This script +assists in determining that the requirements are met by performing +appropriate SCSI-PR actions and verifying the results. + +This script is capable of creating reservations and registrations, as +well as removing them. The "on" action is used to create reservations, +just as you would do when a node comes online. The "off" action is +used to remove registrations, which is equivalent to fencing. It is +important to test both the "on" and "off" actions when testing for +fence_scsi compatibility. + +Because this script is capable of creating, modifying, and removing +existing reservations and registrations, it should not be used on any +systems that are actively using SCSI persistent reservations. + +.SH OPTIONS +.TP +\fB-o, --action=[on|off]\fR +The action to perform. This value can be "on" or "off". For "on", the +script will attempt to register a key (see -k option) with the +device(s) (see -d option) and create a reservation if none exists. The +"off" action will attempt to remove a key from the device(s). This +option is required. + +.TP +\fB-k, --key=VALUE\fR +The key to use for the current action. The key must be a hexadecimal +value and should be unique to a node. For the "on" action, the key +specifies the key value use to register the local node with the +device(s). For the "off" action, the key specifies the key value to be +removed from the device(s). + +.TP +\fB-d, --devices=LIST\fR +List of devices to use for current action. This can be a single device +or a comma-separated list of devices. Each device should be given as +the full path (eg. /dev/sdc). + +.TP +\fB-h, --help\fR +Print out a help message describing available options, then exit. + +.TP +\fB-v, --verbose\fR +Verbose output. + +.SH SEE ALSO +fence_scsi(8), fence(8), fence_node(8), sg_persist(8), lvs(8), lvm.conf(5) -- 1.7.2.2