TGT-admin

From WPKG | Open Source Software Deployment and Distribution
Jump to: navigation, search

Right now, TGT does not have any config file nor a configuration tool.

Below is an attempt to create one.

Please note that the current development takes place in the tgt git tree:
https://github.com/fujita/tgt

Contents

[edit] Configuration instructions

First, create a /etc/tgt/targets.conf file similar to the one below:

include /etc/tgt/xen/*.conf
include /etc/tgt/vmware/*.conf
include /etc/tgt/temp/*.conf


<target iqn.2007-04.com.example:san.monitoring>
   backing-store /dev/san/monitoring

   # if no "incominguser" is specified, it is not used
   incominguser backup secretpass12

   # defaults to ALL if no "initiator-address" is specified
   initiator-address 192.168.1.2
</target>

<target iqn.2007-02.com.example:san.xen1>
   backing-store /dev/san/xen1-disk1 # LUN1
   backing-store /dev/san/xen1-disk2 # LUN2
   backing-store /dev/san/xen1-disk2 # LUN3

   initiator-address 192.168.1.2     # Allowed IP
   initiator-address 192.168.5.6     # Allowed IP

   incominguser user1 secretpass12
   incominguser user2 secretpass23

   outgoinguser userA secretpassA
</target>

<target iqn.2007-02.com.example:san.xen2>
   backing-store /dev/san/xen2
</target>

<target iqn.2007-06.com.example:san.vmware1>
   backing-store /dev/san/vmware1
</target>

You can include other config files with i.e. include /etc/tgt/xen/*.conf.


You will need Config::General. In Debian, it is in libconfig-general-perl package. In RPM-based distributions, the package will be likely called perl-Config-General. Alternatively, you can get it from CPAN.

Here are tgt-admin options:

# tgt-admin -h
Usage:
tgt-admin [OPTION]...
This tool configures tgt targets.

  -e, --execute   read /etc/tgt/targets.conf and execute tgtadm commands
  -f, --force     don't exit on tgtadm errors
  -p, --pretend   only print tgtadm options
  -v, --verbose   increase verbosity (no effect in "pretend" mode)
  -h, --help      show this help


[edit] adding targets

For each target which is:

it will print "Adding target" with this target's name, and three tgtadm commands which setup the target. For example:

# Adding target: iqn.2007-11.com.example:san.rescue-live
tgtadm --lld iscsi --op new --mode target --tid 46 -T iqn.2007-11.com.example:san.rescue-live
tgtadm --lld iscsi --op new --mode logicalunit --tid 46 --lun 1 -b /dev/san/rescue-live
tgtadm --lld iscsi --op bind --mode target --tid 46 -I ALL

[edit] removing targets

It will also try to remove the targets which don't exist in the config file (which are in tgtadm --lld iscsi --op show --mode target output, but not in the config file), for example:

# Removing target: iqn.2007-04.com.example:san.monitoring
tgtadm --op update --mode target --tid=9 -n state -v offline
tgtadm --mode target --op delete --tid=9

Note that it will not forcibly remove the target if any initiators are still connected to it, as tgtadm does not support this feature yet.


[edit] and what happens if a target already exists?

If the target is already configured and the tool is started, it will output:

# Target iqn.2006-12.com.example:san.backup1 already exists!

# Target iqn.2006-12.com.example:san.kolab1 already exists!

# Target iqn.2006-12.com.example:san.mysql2 already exists!

This means you can safely add or remove any targets in your config file, it will be automatically recognized and a proper action will be taken.

[edit] Source code

Below, a configuration tool written in Perl. Alternatively, you can download it from http://wpkg.org/tgt-admin

Please note that the current development takes place in the tgt git tree: http://git.kernel.org/?p=linux/kernel/git/tomo/tgt.git;a=summary


#!/usr/bin/perl
 
# This tools parses /etc/tgt/targets.conf file and configures tgt - see http://stgt.berlios.de
#
# Author:  Tomasz Chmielewski
# License: GPLv2
#
 
use strict;
use Config::General;
use Data::Dumper;
use Getopt::Long;
 
# Our config file
my $configfile = "/etc/tgt/targets.conf";
 
# Parse the config file with Config::General
my %conf = Config::General::ParseConfig(-ConfigFile => "$configfile", -UseApacheInclude => 1, -IncludeGlob => 1,);
 
sub usage {
        print <<EOF;
Usage:
tgt-admin [OPTION]...
This tool configures tgt targets.
 
  -e, --execute   read $configfile and execute tgtadm commands
  -f, --force     do not exit on tgtadm errors
  -p, --pretend   only print tgtadm options
  -v, --verbose   increase verbosity (no effect in "pretend" mode)
  -h, --help      show this help
 
EOF
        exit;
}
 
 
my $param = $ARGV[0];
 
my $execute = 0;
my $force = 0;
my $pretend = 0;
my $verbose = 0;
my $help = 0;
my $result = GetOptions (
        "e|execute" => \$execute,
        "f|force"   => \$force,
        "p|pretend" => \$pretend,
        "v|verbose" => \$verbose,
        "h|help"    => \$help,
);
 
if (($help == 1) || ($param eq undef)) {
        &usage
}
 
 
# Some variables/arrays/hashes we will use globally
my %tgtadm_output;
my %tgtadm_output_tid;
my @largest_tid;
my $next_tid;
 
# Add targets, if they are not configured already
my $target;
my $option;
my $value;
 
sub add_targets {
        foreach my $k (sort keys %conf) {
                if ( $k eq "target" ) {
                        foreach my $k2 (sort keys %{$conf{$k}}) {
                                $target = $k2;
                                my $allowall = 1;
                                if ( $tgtadm_output{$k2} eq undef ) {
                                        # We have to find available tid
                                        $next_tid = $next_tid + 1;
 
                                        execute("# Adding target: $target");
                                        execute("tgtadm --lld iscsi --op new --mode target --tid $next_tid -T $target");
 
                                        foreach my $k3 (sort keys %{$conf{$k}{$k2}}) {
                                                $option = $k3;
                                                $value = $conf{$k}{$k2}{$k3};
                                                &process_options;
                                                # 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 iscsi --op bind --mode target --tid $next_tid -I ALL");
                                        }
                                } else {
                                        execute("# Target $target already exists!");
                                }
                                execute();
                        }
                }
        }
}
 
 
# Process options from the config file
sub process_options {
        if ( $option eq "backing-store" ) {
                if (ref($value) eq "ARRAY") {
                        my @value_arr = @$value;
                        my $i = 1;
                        foreach my $backing_store (@value_arr) {
                                execute("tgtadm --lld iscsi --op new --mode logicalunit --tid $next_tid --lun $i -b $backing_store");
                                $i += 1;
                        }
                } else {
                        execute("tgtadm --lld iscsi --op new --mode logicalunit --tid $next_tid --lun 1 -b $value");
                }
        }
 
        if ( $option eq "incominguser" ) {
                if (ref($value) eq "ARRAY") {
                        my @value_arr = @$value;
                        foreach my $incominguser (@value_arr) {
                                my @userpass = split(/ /, $incominguser);
                                execute("tgtadm --lld iscsi --mode account --op delete --user=$userpass[0]");
                                execute("tgtadm --lld iscsi --mode account --op new --user=$userpass[0] --password=$userpass[1]");
                                execute("tgtadm --lld iscsi --mode account --op bind --tid=$next_tid --user=$userpass[0]");
                        }
                } else {
                        my @userpass = split(/ /, $value);
                        execute("tgtadm --lld iscsi --mode account --op delete --user=$userpass[0]");
                        execute("tgtadm --lld iscsi --mode account --op new --user=$userpass[0] --password=$userpass[1]");
                        execute("tgtadm --lld iscsi --mode account --op bind --tid=$next_tid --user=$userpass[0]");
                }
        }
 
        if ( $option eq "outgoinguser" ) {
                if (ref($value) eq "ARRAY") {
                        execute("# Warning: only one outgoinguser is allowed. Will only use the first one.");
                        my @userpass = split(/ /, @$value[0]);
                        execute("tgtadm --lld iscsi --mode account --op delete --user=$userpass[0]");
                        execute("tgtadm --lld iscsi --mode account --op new --user=$userpass[0] --password=$userpass[1]");
                        execute("tgtadm --lld iscsi --mode account --op bind --tid=$next_tid --user=$userpass[0] --outgoing");
                } else {
                        my @userpass = split(/ /, $value);
                        execute("tgtadm --lld iscsi --mode account --op delete --user=$userpass[0]");
                        execute("tgtadm --lld iscsi --mode account --op new --user=$userpass[0] --password=$userpass[1]");
                        execute("tgtadm --lld iscsi --mode account --op bind --tid=$next_tid --user=$userpass[0] --outgoing");
                }
        }
 
        if ( $option eq "initiator-address" ) {
                if (ref($value) eq "ARRAY") {
                        my @value_arr = @$value;
                        foreach my $initiator_address (@value_arr) {
                                execute("tgtadm --lld iscsi --op bind --mode target --tid $next_tid -I $initiator_address");
                        }
                } else {
                        execute("tgtadm --lld iscsi --op bind --mode target --tid $next_tid -I $value");
                }
        }
}
 
 
# Look up which targets are configured
sub process_configs {
        # We need to run as root
        if ( $> ) {
                die("You must be root to run this program.\n");
        }
 
        my @show_target = `tgtadm --lld iscsi --op show --mode target`;
        my $tid;
        my $targetname;
 
        # Here, we create hashes of target names (all target data) and target tids
        foreach my $show_target_line (@show_target) {
                if ( $show_target_line =~ m/^Target (\d*): (.+)/ ) {
                        $tid = $1;
                        $targetname = $2;
                        $tgtadm_output{$targetname} = $show_target_line;
                        $tgtadm_output_tid{$targetname} = $tid;
                } else {
                        $tgtadm_output{$targetname} .= $show_target_line;
                }
        }
 
        # What is the largest tid?
        my @tids = values %tgtadm_output_tid;
        @largest_tid = sort { $a <=> $b } @tids;
        $next_tid = $largest_tid[$#largest_tid];
}
 
 
# If the target is configured, but not present in the config file,
# offline it and try to remove it
sub remove_targets {
 
        &process_configs;
        my @all_targets = keys %tgtadm_output_tid;
 
        foreach my $existing_target (@all_targets) {
                my $dontremove = 0;
                my $k2;
                foreach my $k (sort keys %conf) {
                        foreach $k2 (sort keys %{$conf{$k}}) {
                                if ( $k2 eq $existing_target ) {
                                        $dontremove = 1;
                                }
                        }
 
                        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}");
                        }
                }
        }
 
}
 
# Execute or just print (or both) everything we start or would start
sub execute {
        if ($pretend == 0) {
 
                my $args = "@_";
                if ($verbose == 1) {
                        print "$args\n";
                }
                # Don't try to execute if it's a comment
                my @execargs = split(/#/, $args);
                if ( $execargs[0] ne undef  ) {
                        system($args);
 
                        # If non-zero exit code was return, exit
                        my $exit_value  = $? >> 8;
                        if (($exit_value != 0) && ($force == 0)) {
                                print "Command:\n\t$args\nexited with code: $exit_value.\n";
                                exit $exit_value;
                        }
                }
 
        } elsif ( $pretend == 1 ) {
                print "@_\n";
        }
}
 
if (($execute == 1) || ($pretend == 1)) {
        &process_configs;
 
        &add_targets;
 
        &remove_targets;
} else {
        print "No action specified.\n";
}

[edit] TO DO

If there is any discussion about this tool, more features could be implemented:

[edit] Contact

Please ask questions on stgt-devel mailing list: http://stgt.berlios.de/

Retrieved from "http://wpkg.org/TGT-admin"
Personal tools
Namespaces
Variants
Actions
Navigation
ideas?
Toolbox