#!/usr/bin/perl
-$Version = 'SCST Configurator v1.0.9';
+$Version = 'SCST Configurator v1.0.10';
# Configures SCST
#
Target Driver Operations
-enable <wwn|host> : Enable target mode for driver at specified WWN or host.
-disable <wwn|host> : Disable target mode for driver at specified WWN or host.
+ -issuelip : Issue LIP on all target-enabled fabrics.
Device Operations
-adddev <device> : Adds a device to a handler.
Rename a security group:
scstadmin -RenameGroup HOST01 -to SERVER01
+ Tell all initiators to rescan LUNs:
+ scstadmin -issuelip
+
EndUsage
}
my $blocksize;
my $enable;
my $disable;
+ my $issuelip;
my $p = new Getopt::Long::Parser;
'blocksize=s' => \$blocksize,
'enable=s' => \$enable,
'disable=s' => \$disable,
+ 'issuelip' => \$issuelip,
'debug' => \$_DEBUG_)) {
&usage();
}
usage();
}
+ if (defined($issuelip) && ($enable || $disable)) {
+ print "Argument -issuelip cannot be used with -enable or -disable.\n\n";
+ usage();
+ }
+
if ($handler && !$_HANDLER_MAP_{$handler}) {
print "Invalid handler '$handler' specified. Available handlers are:\n\n";
foreach my $_handler (keys %_HANDLER_MAP_) {
$forceConfig = $TRUE if (defined($forceConfig));
$showSessions = $TRUE if (defined($showSessions));
+ $issuelip = $TRUE if (defined($issuelip));
$enable =~ tr/A-Z/a-z/; $disable =~ tr/A-Z/a-z/;
$options =~ tr/a-z/A-Z/ if ($options);
$applyConfig = $_DEF_CONFIG_ if (defined($applyConfig) && !$applyConfig);
$checkConfig = $_DEF_CONFIG_ if (defined($checkConfig) && !$checkConfig);
- return ($enable, $disable, $addDev, $devPath, $devLun, $resyncDev, $removeDev, $addUser,
+ return ($enable, $disable, $issuelip, $addDev, $devPath, $devLun, $resyncDev, $removeDev, $addUser,
$moveUser, $removeUser, $clearUsers, $addGroup, $renameGroup, $toGroup, $removeGroup,
$assignDev, $replaceDev, $releaseDev, $clearDevs, $handler, $group, $options, $blocksize,
$applyConfig, $forceConfig, $clearConfig, $writeConfig, $checkConfig, $showSessions);
# We need to run as root
if ( $> ) {die("This program must run as root.\n");}
- my ($enable, $disable, $addDev, $devPath, $devLun, $resyncDev, $removeDev, $addUser,
+ my ($enable, $disable, $issuelip, $addDev, $devPath, $devLun, $resyncDev, $removeDev, $addUser,
$moveUser, $removeUser, $clearUsers, $addGroup, $renameGroup, $toGroup, $removeGroup,
$assignDev, $replaceDev, $releaseDev, $clearDevs, $handler, $group, $options, $blocksize,
$applyConfig, $forceConfig, $clearConfig, $writeConfig, $checkConfig, $showSessions) = getArgs();
$rc = enableTarget($disable, $FALSE);
last SWITCH;
};
+ $issuelip && do {
+ $rc = issueLIP();
+ last SWITCH;
+ };
print "No valid operations specified.\n";
usage();
exit $TRUE;
}
- print "All done.\n";
+ print "\nAll done.\n";
exit $rc;
}
sub readWorkingConfig {
my %empty;
- print "Collecting current configuration.. ";
+ print "Collecting current configuration: ";
$TARGETS = undef;
$DEVICES = undef;
sub writeConfiguration {
my $file = shift;
+ my $keep_config;
+
+ my $config = readConfig($file, $TRUE);
if (-f $file) {
if (!unlink $file) {
print $io "# NOTE: Options are pipe (|) seperated.\n\n";
+ print $io "[OPTIONS]\n";
+ print $io "#OPTION <1|0|YES|NO|TRUE|FALSE|VALUE>\n";
+ print $io "# Copy configuration options during a -writeconfig\n";
+ print $io "KEEP_CONFIG ";
+
+ if (defined($config)) {
+ $keep_config = $$config{'OPTIONS'}->{'default'}->{'KEEP_CONFIG'}[0];
+ } else {
+ $keep_config = $FALSE;
+ }
+
+ if (!defined($config)) {
+ print $io "TRUE\n";
+ } elsif (!$keep_config ) {
+ print $io "FALSE\n";
+ } else {
+ print $io "TRUE\n";
+ }
+
+ print $io "# For FC targets, issue a LIP after every assignment change\n";
+ print $io "ISSUE_LIP ";
+
+ if (!$keep_config) {
+ print $io "TRUE\n";
+ } else {
+ print $io ($$config{'OPTIONS'}->{'default'}->{'ISSUE_LIP'}[0] ? "TRUE\n" : "FALSE\n");
+ }
+
+ print $io "\n";
+
# Device information
foreach my $handler (sort keys %HANDLERS) {
if ($SCST->handlerType($handler) == $SCST::SCST::IOTYPE_VIRTUAL) {
my $errs;
my $changes = 0;
+ my $assign_changes = 0;
+ my $targets_changed = $FALSE;
+
my %used_devs;
my %used_users;
my %used_assignments;
my %rename_group;
+ my $issue_lip = $$config{'OPTIONS'}->{'default'}->{'ISSUE_LIP'}[0];
+
# Cache device/handler configuration
foreach my $entry (keys %{$$config{'HANDLER'}}) {
foreach my $device (@{$$config{'HANDLER'}->{$entry}->{'DEVICE'}}) {
# Cache device association configuration
foreach my $group (keys %{$$config{'ASSIGNMENT'}}) {
+ my %seen_luns;
+
foreach my $device (@{$$config{'ASSIGNMENT'}->{$group}->{'DEVICE'}}) {
my($vname, $arg) = split(/\,/, $device, 2);
$vname = cleanupString($vname);
+ my($lun, $options) = split(/\,/, $arg);
+ if ($seen_luns{$lun}) {
+ print "\t-> FATAL: Configuration invalid. Group '$group' has multiple ".
+ "devices assigned to LUN $lun.\n";
+ exit 1;
+ }
$used_assignments{$group}->{$vname} = $arg;
+ $seen_luns{$lun}++;
}
}
$errs++;
} else {
$changes++;
+ $assign_changes++;
}
} else {
print ".\n";
if (!$check) {
my $_lun = $$_assignments{$device};
-
my $replace_dev = findAssignedLun($used_assignments{$group}, $_lun);
if (defined($replace_dev) && ($replace_dev ne $device)) {
$errs++;
} else {
$changes++;
+ $assign_changes++;
}
} else {
print ", releasing.\n";
$errs++;
} else {
$changes++;
+ $assign_changes++;
}
}
} else {
}
}
- print "Applying configuration additions..\n" if (!$check);
+ print "\nApplying configuration additions..\n" if (!$check);
print "\n";
readWorkingConfig() if ($force);
$errs++;
} else {
$changes++;
+ $assign_changes++;
}
}
}
$enable = $FALSE;
} else {
print "\t-> WARNING: Ignoring invalid TARGETS specifier '$type'. ".
- "Should be one of enable,disable.\n";
+ "Should be one of enable, disable.\n";
next;
}
$errs++;
} else {
$changes++;
+ $targets_changed = $TRUE;
}
} else {
print ".\n";
$errs++;
} else {
$changes++;
+ $targets_changed = $TRUE;
}
} else {
print ".\n";
}
}
- print "\nEncountered $errs error(s) while processing.\n" if ($errs);
+ if ($issue_lip && $assign_changes && !$targets_changed) {
+ print "\nMaking initiators aware of assignment changes..\n\n";
+ issueLIP();
+ }
+
+ print "\n\nEncountered $errs error(s) while processing.\n" if ($errs);
if ($check) {
- print "Configuration checked, $changes difference(s) found with working configuration.\n";
+ print "\nConfiguration checked, $changes difference(s) found with working configuration.\n";
} else {
$changes = 0 if ($_DEBUG_);
- print "Configuration applied, $changes changes made.\n";
+ print "\nConfiguration applied, $changes changes made.\n";
}
return $TRUE if ($errs);
$targets{$fcards{$entry}}->{'enabled'} = $enabled;
$targets{$fcards{$entry}}->{'path'} =
"$_SCSI_CLASS_/$entry/target_mode_enabled";
+
+ if (-f "$_FC_CLASS_/$entry/issue_lip") {
+ $targets{$fcards{$entry}}->{'ilip'} = "$_FC_CLASS_/$entry/issue_lip";
+ }
+
$targets{$entry}->{'duplicate'} = $TRUE;
} else {
$targets{$entry}->{'duplicate'} = $FALSE;
return $FALSE;
}
+sub issueLIP {
+ foreach my $target (keys %{$TARGETS}) {
+ next if ($$TARGETS{$target}->{'duplicate'});
+
+ if (defined($$TARGETS{$target}->{'ilip'})) {
+ my $io = new IO::File $$TARGETS{$target}->{'ilip'}, O_WRONLY;
+ return $TRUE if (!$io);
+
+ print "\t-> Issuing LIP for target '$target': ";
+
+ if ($_DEBUG_) {
+ print "DBG($$): ".$$TARGETS{$target}->{'ilip'}." -> $TRUE\n\n";
+ } else {
+ print $io $TRUE;
+ }
+
+ print "done.\n";
+ }
+ }
+}
+
sub readConfig {
my $confile = shift;
+ my $ignoreError = shift;
my %config;
my $section;
my $last_section;
my $io = new IO::File $confile, O_RDONLY;
- die("FATAL: Unable to open specified configuration file $confile: $!\n") if (!$io);
+ if (!$io) {
+ return undef if ($ignoreError);
+
+ die("FATAL: Unable to open specified configuration file $confile: $!\n");
+ }
while (my $line = <$io>) {
($line, undef) = split(/\#/, $line, 2);
if ($line =~ /^\[(.*)\]$/) {
($section, $arg) = split(/\s+/, $1, 2);
+ $arg = 'default' if ($section eq 'OPTIONS');
+
if ($last_arg && ($last_arg ne $arg) &&
!defined($config{$last_section}->{$last_arg})) {
$config{$last_section}->{$last_arg} = \%empty;
} elsif ($section && $arg && $line) {
my($parameter, $value) = split(/\s+/, $line, 2);
+ if ($section eq 'OPTIONS') {
+ $value = $TRUE if (($value == 1) ||
+ ($value =~ /^TRUE$/i) ||
+ ($value =~ /^YES$/i));
+ $value = $FALSE if (($value == 0) ||
+ ($value =~ /^FALSE$/i) ||
+ ($value =~ /^NO$/i));
+ }
+
push @{$config{$section}->{$arg}->{$parameter}}, $value;
}
}