[Stgt-devel] [PATCH] updating targets in tgt-admin
Tomasz Chmielewski
mangoo
Fri Aug 1 10:50:45 CEST 2008
This patch adds proper updating of targets to tgt-admin.
You have to add --force/-f option to update targets which are in use.
Updating consists of two things:
- target is deleted
- target is configured, basing on its config options
Updating a target which is no longer in a config file will result in the target's deletion.
The patch also changes the behaviour of --execute slightly - it now tries to delete the targets which are not in the config file; if the target is in use, it won't be touched (unless --force is used); if the target is not in use, it will be deleted.
On my TO DO list are still:
- don't assume cid is 0 when removing targets (check for a proper cid)
- some code cleanups
- shutting down tgtd (only if there are no targets)
- adding some more options (disabling write-cache etc.)
Signed-off-by: Tomasz Chmielewski <mangoo at wpkg.org>
diff --git a/scripts/tgt-admin b/scripts/tgt-admin
index fe95723..354dc9c 100755
--- a/scripts/tgt-admin
+++ b/scripts/tgt-admin
@@ -30,6 +30,8 @@ This tool configures tgt targets.
(see "--offline help" for more info)
--ready <value> put all or selected targets in ready state
(see "--ready help" for more info)
+ --update <value> update configuration for all or selected targets
+ (see "--update help" for more info)
-s, --show show all the targets
-c, --conf <conf file> specify an alternative configuration file
--ignore-errors continue even if tgtadm exits with non-zero code
@@ -49,6 +51,7 @@ my $execute = 0;
my $delete = 0;
my $offline = 0;
my $ready = 0;
+my $update = 0;
my $show = 0;
my $alternate_conf="0";
my $ignore_errors = 0;
@@ -62,6 +65,7 @@ my $result = GetOptions (
"delete=s" => \$delete,
"offline=s" => \$offline,
"ready=s" => \$ready,
+ "update=s" => \$update,
"s|show" => \$show,
"c|conf=s" => \$alternate_conf,
"ignore-errors" => \$ignore_errors,
@@ -147,7 +151,10 @@ my $option;
my $value;
sub add_targets {
-
+ my $single_target = $_[0];
+ my $configured = $_[1];
+ my $connected = $_[2];
+ my $in_configfile = $_[3];
foreach my $k (sort keys %conf) {
if ( $k eq "default-driver" ) {
@@ -166,61 +173,84 @@ sub add_targets {
execute("# default-driver not defined, defaulting to iscsi.\n");
$default_driver = "iscsi";
}
-
+
foreach my $k (sort keys %conf) {
if ( $k eq "target" ) {
foreach my $k2 (sort keys %{$conf{$k}}) {
- $target = $k2;
- my $allowall = 1;
- if ( not defined $tgtadm_output{$k2} ) {
- # We have to find available tid
- $next_tid = $next_tid + 1;
+ # Do we run update or execute?
+ if (length $single_target) {
+ if ($single_target ne $k2) {
+ next;
+ } else {
+ $target = $single_target;
+ }
+ } else {
+ $target = $k2;
}
- else {
- execute("# Target $target already exist!");
- execute("# Updating Target $target");
- execute("tgtadm --op update --mode target --tid=$next_tid -n state -v offline");
- execute("tgtadm --mode target --op delete --tid=$next_tid");
+ if (length $single_target) {
+ &main_delete($target);
}
+ my $allowall = 1;
+ if ((not defined $tgtadm_output{$k2}) || (length $single_target && $force == 1)) {
+ # We have to find available tid
+ if (($in_configfile == 1) && ($configured == 0)) {
+ my $maxtid = &find_max_tid;
+ $next_tid = $maxtid + 1;
+ } elsif (length $single_target) {
+ $next_tid = $tgtadm_output_tid{$target};
+ } else {
+ $next_tid = $next_tid + 1;
+ }
+ # Before we add a target, we need to know its type
+ my $driver;
+ foreach my $k3 (sort keys %{$conf{$k}{$k2}}) {
+ $option = $k3;
+ $value = $conf{$k}{$k2}{$k3};
+ &check_value($value);
+ if ($option eq "driver") {
+ if (ref($value) eq "ARRAY") {
+ print "Multiple driver definitions not allowed!\n";
+ print "Check your config file for errors (target: $target).\n";
+ exit 1;
+ }
+ $driver = $value;
+ }
+ }
+
+ if (not defined $driver) {
+ $driver = $default_driver;
+ }
- # Before we add a target, we need to know its type
- my $driver;
- foreach my $k3 (sort keys %{$conf{$k}{$k2}}) {
- $option = $k3;
- $value = $conf{$k}{$k2}{$k3};
- &check($value);
- if ( $option eq "driver" ) {
- if (ref($value) eq "ARRAY") {
- print "Multiple driver definitions not allowed!\n";
- print "Check your config file for errors (target: $target).\n";
- exit 1;
+ execute("# Adding target: $target");
+ execute("tgtadm --lld $driver --op new --mode target --tid $next_tid -T $target");
+ foreach my $k3 (sort keys %{$conf{$k}{$k2}}) {
+ $option = $k3;
+ $value = $conf{$k}{$k2}{$k3};
+ &check_value($value);
+ &process_options($driver);
+ # If there was no option called "initiator-address", it means
+ # we want to allow ALL initiators for this target
+ if ($option eq "initiator-address") {
+ $allowall = 0;
}
- $driver = $value;
}
- }
- if ( not defined $driver ) {
- $driver = $default_driver;
- }
- execute("# Adding target: $target");
- execute("tgtadm --lld $driver --op new --mode target --tid $next_tid -T $target");
- foreach my $k3 (sort keys %{$conf{$k}{$k2}}) {
- $option = $k3;
- $value = $conf{$k}{$k2}{$k3};
- &check($value);
- &process_options($driver);
- # If there was no option called "initiator-address", it means
- # we want to allow ALL initiators for this target
- if ( $option eq "initiator-address" ) {
- $allowall = 0;
+ if ($allowall == 1) {
+ execute("tgtadm --lld $driver --op bind --mode target --tid $next_tid -I ALL");
}
- }
- if ( $allowall == 1 ) {
- execute("tgtadm --lld $driver --op bind --mode target --tid $next_tid -I ALL");
+ } else {
+ if (not length $update) {
+ execute("# Target $target already exists!");
+ }
}
- execute();
}
+ if (length $single_target && $in_configfile == 0 && $configured == 0) {
+ print "Target $single_target is $connected currently not configured\n";
+ print "and does not exist in the config file - can't continue!\n";
+ exit 1;
+ }
+ execute();
}
}
}
@@ -289,7 +319,7 @@ sub process_options {
my @value_arr = @$value;
foreach my $incominguser (@value_arr) {
my @userpass = split(/ /, $incominguser);
- &check($userpass[1]);
+ &check_value($userpass[1]);
execute("tgtadm --lld $driver --mode account --op delete --user=$userpass[0]");
execute("tgtadm --lld $driver --mode account --op new --user=$userpass[0] --password=$userpass[1]");
execute("tgtadm --lld $driver --mode account --op bind --tid=$next_tid --user=$userpass[0]");
@@ -303,7 +333,7 @@ sub process_options {
}
execute("# Warning: only one outgoinguser is allowed. Will only use the first one.");
my @userpass = split(/ /, @$value[0]);
- &check($userpass[1]);
+ &check_value($userpass[1]);
execute("tgtadm --lld $driver --mode account --op delete --user=$userpass[0]");
execute("tgtadm --lld $driver --mode account --op new --user=$userpass[0] --password=$userpass[1]");
execute("tgtadm --lld $driver --mode account --op bind --tid=$next_tid --user=$userpass[0] --outgoing");
@@ -340,13 +370,8 @@ sub remove_targets {
}
if ( $dontremove == 0 ) {
- # Right now, it is not possible to remove a target if any initiators
- # are connected to it. We'll do our best - offline the target first
- # (so it won't accept any new connections), and remove.
- # Note that remove will only work if no initiator is connected.
- execute("# Removing target: $existing_target");
- execute("tgtadm --op update --mode target --tid=$tgtadm_output_tid{$existing_target} -n state -v offline");
- execute("tgtadm --mode target --op delete --tid=$tgtadm_output_tid{$existing_target}");
+ # Remove the target
+ &main_delete($existing_target);
}
}
}
@@ -498,48 +523,60 @@ sub show_target_info {
}
}
-# Delete the targets which are not in use
-sub delete_targets {
-
- # Check if the target is used by an initiator
- sub check_in_use {
- my $existing_target = $_[0];
- my $cur_option = $_[1];
- my $cur_tid = $_[2];
- if ($tgtadm_output{$existing_target} =~ m/\s+Connection:/) {
- if ($force == 1) {
- # Remove ACLs first
- my @acl_info = &show_target_info($existing_target, "acl_information");
- foreach my $acl (@acl_info) {
- execute("tgtadm --op unbind --mode target --tid $tgtadm_output_tid{$existing_target} -I $acl");
- }
- # Now, remove all sessions / connections from that tid
- my @sessions = &show_target_info($existing_target, "sessions");
- foreach my $session (@sessions) {
- execute("tgtadm --op delete --mode conn --tid $tgtadm_output_tid{$existing_target} --sid $session --cid 0");
- }
- execute("tgtadm --mode target --op delete --tid=$tgtadm_output_tid{$existing_target}");
- } else {
- execute("# Target with tid $tgtadm_output_tid{$existing_target} ($existing_target) is in use, it won't be deleted.");
+# Main subroutine for deleting targets
+sub main_delete {
+ my $current_target = $_[0];
+ my $current_tid = $_[1];
+ my $configured = &check_configured($current_target);
+ my $del_upd_text;
+ # Check if the target has initiators connected
+ if ($tgtadm_output{$current_target} =~ m/\s+Connection:/) {
+ if ($force == 1) {
+ execute("# Removing target: $current_target");
+ # Remove ACLs first
+ my @acl_info = &show_target_info($current_target, "acl_information");
+ foreach my $acl (@acl_info) {
+ execute("tgtadm --op unbind --mode target --tid $tgtadm_output_tid{$current_target} -I $acl");
+ }
+ # Now, remove all sessions / connections from that tid
+ my @sessions = &show_target_info($current_target, "sessions");
+ foreach my $session (@sessions) {
+ execute("tgtadm --op delete --mode conn --tid $tgtadm_output_tid{$current_target} --sid $session --cid 0");
}
- } elsif (length $tgtadm_output_tid{$existing_target}) {
- execute("tgtadm --mode target --op delete --tid=$tgtadm_output_tid{$existing_target}");
+ execute("tgtadm --mode target --op delete --tid=$tgtadm_output_tid{$current_target}");
} else {
- if ($cur_option eq "tid") {
- execute("# Target with tid $cur_tid does not exist!");
+ if ($update ne 0) {
+ $del_upd_text = "updated";
} else {
- execute("# Target $existing_target does not exist!");
+ $del_upd_text = "deleted";
}
+ execute("# Target with tid $tgtadm_output_tid{$current_target} ($current_target) is in use, it won't be $del_upd_text.");
+ }
+ } elsif (length $tgtadm_output_tid{$current_target}) {
+ execute("# Removing target: $current_target");
+ execute("tgtadm --mode target --op delete --tid=$tgtadm_output_tid{$current_target}");
+ } else {
+ if (length $current_tid) {
+ execute("# Target with $current_target tid $current_tid does not exist!");
+ } else {
+ execute("# Target with name $current_target does not exist!");
}
}
+ if ($configured ne 0) {
+ execute();
+ }
+}
+# Delete the targets
+sub delete_targets {
+
if ($delete eq "help") {
print <<EOF;
--delete <value> delete all or selected targets
- The target will be deleted only if it's not used
+ The target will be deleted only if it's not used
(no initiator is connected to it).
If you want to delete targets which are in use,
- you have to add "--force" flag
+ you have to add "--force" flag.
Example usage:
--delete help - display this help
@@ -551,26 +588,130 @@ EOF
exit;
} elsif ($delete eq "ALL") {
&process_targets;
- # Run over all targets and delete them if they are not in use
+ # Run over all targets and delete them
my @all_targets = keys %tgtadm_output_tid;
- foreach my $existing_target (@all_targets) {
- &check_in_use($existing_target);
+ foreach my $current_target (@all_targets) {
+ &main_delete($current_target);
}
- } elsif ($delete =~ m/tid=(.+)/) {
+ } elsif ($delete =~ m/^tid=(.+)/) {
# Delete by tid
&process_targets;
- my $existing_target = $1;
- &check_in_use($tgtadm_output_name{$existing_target}, "tid", $existing_target);
+ my $current_target = $tgtadm_output_name{$1};
+ &main_delete($current_target, $1);
} else {
# Delete by name
&process_targets;
- my $existing_target = $delete;
- &check_in_use($existing_target);
+ my $current_target = $delete;
+ &main_delete($current_target);
+ }
+}
+
+# Update targets
+sub update_targets {
+ if ($update eq "help") {
+ print <<EOF;
+ --update <value> update all or selected targets
+ The target will be update only if it's not used
+ (no initiator is connected to it).
+ If you want to update targets which are in use,
+ you have to add "--force" flag.
+
+Example usage:
+ --update help - display this help
+ --update ALL - update all targets
+ --update tid=4 - update target 4 (target with tid 4)
+ --update iqn.2008-08.com.example:some.target - update this target
+
+EOF
+ exit;
+ } elsif ($update eq "ALL") {
+ # Run over all targets and delete them if they are not in use
+ &parse_configs;
+ &process_targets;
+# my @all_targets = keys %tgtadm_output_tid;
+ my @targets_combined = &combine_targets;
+ foreach my $current_target (@targets_combined) {
+ my $configured = &check_configured($current_target);
+ my $connected = &check_connected($current_target);
+ my $in_configfile = &check_in_configfile($current_target);
+ &combine_targets;
+ if (($in_configfile == 0) && ($configured == 1)) {
+ # Delete the target if it's not in the config file
+ &main_delete($current_target);
+ } else {
+ &add_targets($current_target, $configured, $connected, $in_configfile);
+ }
+
+ }
+ } elsif ($update =~ m/^tid=(.+)/) {
+ # Update by tid
+ &parse_configs;
+ &process_targets;
+ my $current_target = $tgtadm_output_name{$1};
+ my $configured = &check_configured($current_target);
+ my $connected = &check_connected($current_target);
+ my $in_configfile = &check_in_configfile($current_target);
+ if (($in_configfile == 0) && ($configured == 1)) {
+ # Delete the target if it's not in the config file
+ &main_delete($current_target);
+ } elsif ($configured == 1) {
+ &add_targets($current_target, $configured, $connected);
+ } else {
+ print "There is no target with tid $1, can't continue!\n";
+ exit 1;
+ }
+ } else {
+ # Update by name
+ &parse_configs;
+ &process_targets;
+ my $current_target = $update;
+ my $configured = &check_configured($current_target);
+ my $connected = &check_connected($current_target);
+ my $in_configfile = &check_in_configfile($current_target);
+ if (($in_configfile == 0) && ($configured == 1)) {
+ # Delete the target if it's not in the config file
+ &main_delete($current_target);
+ } else {
+ &add_targets($current_target, $configured, $connected);
+ }
+ }
+}
+
+# Find the biggest tid
+sub find_max_tid {
+ my @all_targets = keys %tgtadm_output_tid;
+ my $maxtid = 0;
+ foreach my $var (@all_targets) {
+ if ($tgtadm_output_tid{$var} > $maxtid) {
+ $maxtid = $tgtadm_output_tid{$var};
+ }
+ return $maxtid;
+ }
+}
+
+# Combine targets from the config file and currently configured targets
+sub combine_targets {
+ my @targets_in_configfile;
+ my @all_targets = keys %tgtadm_output_tid;
+ my @targets_combined;
+ # Make an array of targets in the config file
+ foreach my $k (sort keys %conf) {
+ if ( $k eq "target" ) {
+ foreach my $k2 (sort keys %{$conf{$k}}) {
+ push(@targets_in_configfile, $k2)
+ }
+ }
+ }
+ # Use only unique elements from bot arrays
+ foreach my $target (@all_targets) {
+ push (@targets_combined, $target) unless grep { $_ eq $target } @targets_in_configfile;
}
+ @targets_combined = (@targets_combined, @targets_in_configfile);
+ return @targets_combined;
}
-# Some checks
-sub check {
+# Check if a value is correct
+sub check_value {
if ( not defined $_[0] or not length $_[0] ) {
print "\nOption $option has a missing value!\n";
print "Check your config file for errors (target: $target)\n";
@@ -578,6 +719,42 @@ sub check {
}
}
+# Check if the target is in the config file
+sub check_in_configfile {
+ my $current_target = $_[0];
+ my $result;
+ foreach my $k (sort keys %conf) {
+ if ( $k eq "target" ) {
+ foreach my $k2 (sort keys %{$conf{$k}}) {
+ if ($k2 eq $current_target) {
+ return 1;
+ }
+ }
+ # If we're here, we didn't find a match
+ return 0;
+ }
+ }
+}
+
+# Check if the target is configured in tgtd
+sub check_configured {
+ my $current_target = $_[0];
+ if (length $tgtadm_output_tid{$current_target}) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+# Check if any initiators are connected to the target
+sub check_connected {
+ my $current_target = $_[0];
+ if ($tgtadm_output{$current_target} =~ m/\s+Connection:/) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
# Execute or just print (or both) everything we start or would start
sub execute {
@@ -612,6 +789,8 @@ if ($execute == 1) {
&remove_targets;
} elsif ($delete ne 0) {
&delete_targets;
+} elsif ($update ne 0) {
+ &update_targets;
} elsif ($dump == 1) {
&dump_config;
} elsif ($offline ne 0) {
--
Tomasz Chmielewski
http://wpkg.org
More information about the stgt
mailing list