[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