zvol2iscsi

Since moving to COMSTAR on OpenSolaris I've been fighting with the amount of work required to publish a zvol, especially when trying to document the process for administrators with not a lot of time to fiddle with this kind of stuff.

So I wrote the following script to simplify my life. It's based on the idea that your setup may be fairly simple so there's no need to get fancy with some of the more advanced view mapping options. The basic syntax is:

zvol2iscsi.pl data/vol02

If the volume has an lu created, it will use it, otherwise it will create one. The LUN ID is auto-incremented to the highest value currently assigned + 1. If you have not defined target or host groups with stmfadm it will skip those values, or if you have created a single target or host group, it will automatically pick that up and use them. If you have multiple target and/or host groups, or you wish to be explicit, you add them to the command line as follows:

zvol2iscsi.pl data/vol02 hg=esx tg=esx

There's one additional option: lastsnap=true. By adding this option is will take the most recent snapshot of the zvol, clone it, and add the COMSTAR view just like a regular volume.

root@osol0906a$ ./zvol2iscsi.pl zvol=data/vol02 lastsnap=true
Checking for zvol: data/vol02
Attaching zvol to target group prod-esx
Cloning data/vol02@test01 to data/vol02-20100204-153503
data/vol02-20100204-153503 does not have an lu assigned - creating one
Created lu: 600144f012de4a0000004b6adb190022 for data/vol02-20100204-153503
stmfadm: 600144f012de4a0000004b6adb190022: no views found
stmfadm: 600144f012de4a0000004b6ad0d40019: no views found
stmfadm: 600144f012de4a0000004b6ad0e4001a: no views found
stmfadm: 600144f012de4a0000004b6ad6a4001b: no views found
stmfadm: 600144f012de4a0000004b6ad8e8001d: no views found
stmfadm: 600144f012de4a0000004b6adb190022: no views found
Creating view on hg:prod-esx with LUN 4


So here's the script:

Download file "zvol2iscsi.pl"

And here's the source code:


#!/usr/bin/perl
# zvol2iscsi.pl v 0.9

# alphageek@infrageeks.com
# http://infrageeks.com/

#############################################################################
# Script for presenting a zvol over iSCSI as automatically as possible
# Environment : OpenSolaris 2009.06 using COMSTAR 
#
# The process of switching to COMSTAR from the older iscsit has been a little
# annoying for me since it adds a significant level of complexity to the 
# presentation of a zvol over iSCSI (as opposed to the simple shareiscsi=on)
#
# In order to make life a little easier, this script will let you present a
# zvol over iSCSI with a minimum of work.
#
# The automation based on the following choices - if you have not defined a
# host group or target group then it will create the view without them.  If
# you defined a single host or target group, it will be used automatically.
#
# If there are multiple host or target groups, you will need to specify them
# on the command line.
#
# It will automatically increment the LUN ID to a new unused LUN.
#
# If you are using this for any kind of disaster recovery where you are 
# mounting replicated volumes, or you want to clone a volume automatically, 
# use the lastsnap=true and it will clone the most recent snapshot to a new
# volume and present it as per the standard options, leaving the original
# volume untouched.
# 
# In order to create the logical unit, the volume must be unlocked so the
# script will automaticall set readonly=off to the volume being presented.
# If you use the lastsnap option, the original volume is not touched, only
# the cloned volume is unlocked.
#
# OPTIONS
# zvol=pool/volume (required)
# hg=hostgroup (optional if there are zero or 1 host groups)
# tg=target group (optional if there are zero or 1 target groups)
# lastsnap=true (optional)
#
# CHANGE LOG
#  4 feb 2010 : AG : First version
#



##############################################################################
# Basic variables

my $dateStr = getDate();
my $lunid = 0;
my %options; # stores command line arguments, and acquired settings

##############################################################################
# Convert arguments to options hash
foreach my $argnum (@ARGV) {
	my ($attribute,$value) = split (/=/,$argnum);
	$options{$attribute}=$value;
}

##############################################################################
# Verify that the zvol exists
if (exists $options{'zvol'}) {
	if ($options{'zvol'} =~ /\//) {
 print "Checking for zvol: $options{'zvol'}\n";
 if (-e "/dev/zvol/rdsk/$options{'zvol'}") {
 # print "     zvol found\n";
 }
 else {
 print "$options{'zvol'} not found - quitting\n";
 exit 1;
 }
	}
	else {
 print "Missing volume - don't use the zpool\n";
 exit 1;
	}
}
else {
	print "The zvol=pool/volume is required - quitting\n";
	exit 1;
}

##############################################################################
# Verify that the host group exists, if not defined and there is only one host
# group defined on the server, use that value.
if (exists $options{'hg'}) {
	$hgcheck = `pfexec stmfadm list-hg $options{'hg'} | wc -l`;
	if ($hgcheck == 0) {
 print "$options{'hg'} does not exist - quitting\n";
 exit 1;
	}
}
else {
	my $hgcount=`pfexec stmfadm list-hg | wc -l`;

	if ($hgcount == 0) {
 # No target groups defined - using "All" should work
	}
	elsif ($hgcount == 1) {
 $hgline = `pfexec stmfadm list-hg`;
 chomp $hgline;
 my ($discard,$hg) = split(/: /, $hgline);
 $options{'hg'}=$hg;
 print "Attaching zvol to target group $options{'hg'}\n";
	}
	else {
 print "Multiple target groups defined: use hg= to select the target group - quitting\n";
 exit 1
	}
}


##############################################################################
# Verify that the target group exists, if not defined and there is only one
# target group defined on the server, use that value.
if (exists $options{'tg'}) {
	$tgcheck = `pfexec stmfadm list-tg $options{'tg'} | wc -l`;
	if ($tgcheck == 0) {
 print "$options{'tg'} does not exist - quitting\n";
 exit 1;
	}
}
else {
	my $tgcount=`pfexec stmfadm list-tg | wc -l`;

	if ($tgcount == 0) {
 # No target groups defined - using "All" should work
	}
	elsif ($tgcount == 1) {
 $tgline = `pfexec stmfadm list-tg`;
 chomp $tgline;
 my ($discard,$tg) = split(/: /, $tgline);
 $options{'tg'}=$tg;
 print "Attaching zvol to target group $options{'tg'}\n";
	}
	else {
 print "Multiple target groups defined: use tg= to select the target group - quitting\n";
 exit 1
	}
}

##############################################################################
# Check for lastsnap option
if (exists $options{'lastsnap'}) {
	if ($options{'lastsnap'} eq 'true') {
 $options{'lastsnap'} = 1;
 $snapstrcount = `zfs list -o name -t snapshot | grep $options{'zvol'} | tail -1 | wc -l`;
 if ($snapstrcount == 0) {
 print "No snapshots available for this volume - quitting\n";
 exit 1;
 }
 elsif ($snapstrcount == 1) {
 $snapstr = `pfexec zfs list -o name -t snapshot | grep $options{'zvol'} | tail -1`;
 chomp $snapstr;
 $clonevol = "$options{'zvol'}-$dateStr";
 $cmd = `pfexec zfs clone $snapstr $clonevol`;
 print "Cloning $snapstr to $clonevol\n";
 $options{'zvol'}=$clonevol;
 }
	}
	else {
 print "Invalid value for lastsnap - quitting\n";
 exit 1;
	}
}
else {
	$options{'lastsnap'}=0;
}


##############################################################################
# Check for an existing lu and create as necessary

$existinglu = `pfexec sbdadm list-lu`;
if ($existinglu =~ $options{'zvol'}) {
	print "$options{'zvol'} already has an lu assigned\n";
	@listlu = split(/\n/, $existinglu);
	foreach $line (@listlu) {
 my ($lu, $size, $source) = split(/\s+/, $line);
 if ($source =~ /$options{'zvol'}$/) {
 $options{'lu'}=$lu;
 print "Using existing lu: $lu for $options{'zvol'}\n";
 }
	}
	
}
else {
	print "$options{'zvol'} does not have an lu assigned - creating one\n";
	$unlock = `pfexec zfs set readonly=off $options{'zvol'}`;
	$newlu = `pfexec sbdadm create-lu /dev/zvol/rdsk/$options{'zvol'}`;
	@newlu = split(/\n/, $newlu);
	my ($lu, $size, $source) = split(/\s+/, $newlu[5]);
	$options{'lu'}=$lu;
	print "Created lu: $lu for $options{'zvol'}\n";
}

##############################################################################
# Verify that the lu is not already associated with a view for this host group
$listview = `pfexec stmfadm list-view -l $options{'lu'}`;
if ($listview =~ /: $options{'hg'}/) {
	print "This zvol is already associated with a view for $options{'hg'} - quitting\n";
	exit 1;
}


##############################################################################
# Check for the current LUN ID's used by the view associated with the host-group

$listlu = `pfexec stmfadm list-lu`;
@listlu = split(/\n/, $listlu);
foreach $line (@listlu) {
	my ($discard, $lu) = split(/: /, $line);
	$viewStr = `pfexec stmfadm list-view -l $lu`;

	if ($viewStr =~ /View Entry/) {

 @viewArr = split(/\n/, $viewStr);
 ($discard,$viewentry) = split(/: /, $viewArr[0]);
 ($discard,$viewhg) = split(/: /, $viewArr[1]);
 ($discard,$viewtg) = split(/: /, $viewArr[2]);	
 ($discard,$viewlun) = split(/: /, $viewArr[3]);

 if ($viewlun >= $lunid ) { $lunid = ($viewlun + 1); }
	}
}

if (exists $options{'tg'} && exists $options{'hg'}) {
	print "Creating view on hg: $options{'hg'}, tg: $options{'tg'} with LUN $lunid\n";
	$cmd = `pfexec stmfadm add-view -h $options{'hg'} -n $lunid -t $options{'tg'} $options{'lu'}`;
}
elsif (exists $options{'hg'}) {
	print "Creating view on hg:$options{'hg'} with LUN $lunid\n";
	$cmd = `pfexec stmfadm add-view -h $options{'hg'} -n $lunid $options{'lu'}`;
}
elsif (exists $options{'tg'}) {
	print "Creating view on tg: $options{'tg'} with LUN $lunid\n";
	$cmd = `pfexec stmfadm add-view -t $options{'tg'} -n $lunid $options{'lu'}`;
}
else {
	print "Creating view with LUN $lunid\n";
	$cmd = `pfexec stmfadm add-view -n $lunid $options{'lu'}`;
}


#############################################################################
# returns the date/time in DDMMYYY-HHMMSS
sub getDate() {
	my @date;
	if (@_) {
 (my $seconds) = @_;
 @date = localtime($seconds);
	}
	else {
 @date = localtime(time());
	}
	$date[5] += 1900;
	$date[4]++;
	if(length($date[4]) == 1) { $date[4] = "0" . $date[4]; }
	if(length($date[3]) == 1) { $date[3] = "0" . $date[3]; }
	if(length($date[2]) == 1) { $date[2] = "0" . $date[2]; }
	if(length($date[1]) == 1) { $date[1] = "0" . $date[1]; }
	if(length($date[0]) == 1) { $date[0] = "0" . $date[0]; }
#	my $date = $date[5] . $date[4] . $date[3] . "-" . $date[2] . $date[1] . $date[0];
	my $date = $date[5] . $date[4] . $date[3] . "-" . $date[2] . $date[1] . $date[0];
	return $date;
}

Comments

/groups/infrageeks/search/index.rss?sort=modifiedDate&sortDirection=reverse&tag=virtualisationlist/groups/infrageeks/search/?sort=modifiedDate&sortDirection=reverse&tag=virtualisationVirtualisationCustomTagSidebarCustomTagSidebar?sort=modifiedDate&sortDirection=reverse&tag=virtualisation0/groups/infrageeks/sidebar/CustomTagSidebarmodifiedDate5CustomTagSidebarreversevirtualisationVirtualisationcustom/groups/infrageeks/search/index.rss?tag=hotlist/groups/infrageeks/search/?tag=hotWhat’s HotHotListHot!?tag=hot0/groups/infrageeks/sidebar/HotListNo items tagged with hot.hot/groups/infrageeks/search/index.rss?sort=modifiedDate&kind=all&sortDirection=reverse&excludePages=wiki/welcomelist/groups/infrageeks/search/?sort=modifiedDate&kind=all&sortDirection=reverse&excludePages=wiki/welcomeRecent ChangesRecentChangesListUpdates?sort=modifiedDate&kind=all&sortDirection=reverse&excludePages=wiki/welcome0/groups/infrageeks/sidebar/RecentChangesListmodifiedDateallRecent ChangesRecentChangesListUpdateswiki/welcomeNo recent changes.reverse5search