Use ssh key stored in pmxcfg. Use ssh -i /etc/pve/priv/zfs/portal_id_rsa for remote commands

Signed-off-by: Michael Rasmussen <mir@datanom.net>
This commit is contained in:
Michael Rasmussen
2013-10-23 02:53:08 +02:00
committed by Dietmar Maurer
parent 86f00da603
commit 3b219e8037
4 changed files with 634 additions and 630 deletions

View File

@ -8,94 +8,95 @@ use Data::Dumper;
my @ssh_opts = ('-o', 'BatchMode=yes'); my @ssh_opts = ('-o', 'BatchMode=yes');
my @ssh_cmd = ('/usr/bin/ssh', @ssh_opts); my @ssh_cmd = ('/usr/bin/ssh', @ssh_opts);
my $id_rsa_path = '/etc/pve/priv/zfs';
my $get_lun_cmd_map = sub { my $get_lun_cmd_map = sub {
my ($method) = @_; my ($method) = @_;
my $stmfadmcmd = "/usr/sbin/stmfadm";
my $sbdadmcmd = "/usr/sbin/sbdadm";
my $cmdmap = { my $stmfadmcmd = "/usr/sbin/stmfadm";
create_lu => { cmd => $stmfadmcmd, method => 'create-lu' }, my $sbdadmcmd = "/usr/sbin/sbdadm";
delete_lu => { cmd => $stmfadmcmd, method => 'delete-lu' },
import_lu => { cmd => $stmfadmcmd, method => 'import-lu' }, my $cmdmap = {
modify_lu => { cmd => $stmfadmcmd, method => 'modify-lu' }, create_lu => { cmd => $stmfadmcmd, method => 'create-lu' },
add_view => { cmd => $stmfadmcmd, method => 'add-view' }, delete_lu => { cmd => $stmfadmcmd, method => 'delete-lu' },
list_view => { cmd => $stmfadmcmd, method => 'list-view' }, import_lu => { cmd => $stmfadmcmd, method => 'import-lu' },
list_lu => { cmd => $sbdadmcmd, method => 'list-lu' }, modify_lu => { cmd => $stmfadmcmd, method => 'modify-lu' },
}; add_view => { cmd => $stmfadmcmd, method => 'add-view' },
list_view => { cmd => $stmfadmcmd, method => 'list-view' },
die "unknown command '$method'" unless exists $cmdmap->{$method}; list_lu => { cmd => $sbdadmcmd, method => 'list-lu' },
};
return $cmdmap->{$method};
die "unknown command '$method'" unless exists $cmdmap->{$method};
return $cmdmap->{$method};
}; };
sub get_base { sub get_base {
return '/dev/zvol/rdsk'; return '/dev/zvol/rdsk';
} }
sub run_lun_command { sub run_lun_command {
my ($scfg, $timeout, $method, @params) = @_; my ($scfg, $timeout, $method, @params) = @_;
my $msg = ''; my $msg = '';
my $luncmd; my $luncmd;
my $target; my $target;
my $guid; my $guid;
$timeout = 10 if !$timeout; $timeout = 10 if !$timeout;
my $output = sub { my $output = sub {
my $line = shift; my $line = shift;
$msg .= "$line\n"; $msg .= "$line\n";
}; };
if ($method eq 'create_lu') { if ($method eq 'create_lu') {
my $prefix = '600144f'; my $prefix = '600144f';
my $digest = md5_hex($params[0]); my $digest = md5_hex($params[0]);
$digest =~ /(\w{7}(.*))/; $digest =~ /(\w{7}(.*))/;
$guid = "$prefix$2"; $guid = "$prefix$2";
@params = ('-p', 'wcd=false', '-p', "guid=$guid", @params); @params = ('-p', 'wcd=false', '-p', "guid=$guid", @params);
} elsif ($method eq 'modify_lu') { } elsif ($method eq 'modify_lu') {
@params = ('-s', @params); @params = ('-s', @params);
} elsif ($method eq 'list_view') { } elsif ($method eq 'list_view') {
@params = ('-l', @params); @params = ('-l', @params);
} elsif ($method eq 'list_lu') {
$guid = $params[0];
@params = undef;
}
my $cmdmap = $get_lun_cmd_map->($method);
$luncmd = $cmdmap->{cmd};
my $lunmethod = $cmdmap->{method};
$target = 'root@' . $scfg->{portal};
my $cmd = [@ssh_cmd, $target, $luncmd, $lunmethod, @params];
run_command($cmd, outfunc => $output, timeout => $timeout);
if ($method eq 'list_view') {
my @lines = split /\n/, $msg;
$msg = undef;
foreach my $line (@lines) {
if ($line =~ /^\s*LUN\s*:\s*(\d+)$/) {
$msg = $1;
last;
}
}
} elsif ($method eq 'list_lu') { } elsif ($method eq 'list_lu') {
my $object = $guid; $guid = $params[0];
my @lines = split /\n/, $msg; @params = undef;
$msg = undef; }
foreach my $line (@lines) {
if ($line =~ /(\w+)\s+\d+\s+$object$/) { my $cmdmap = $get_lun_cmd_map->($method);
$msg = $1; $luncmd = $cmdmap->{cmd};
last; my $lunmethod = $cmdmap->{method};
}
} $target = 'root@' . $scfg->{portal};
} elsif ($method eq 'create_lu') {
$msg = $guid; my $cmd = [@ssh_cmd, '-i', "$id_rsa_path/$scfg->{portal}_id_rsa", $target, $luncmd, $lunmethod, @params];
}
run_command($cmd, outfunc => $output, timeout => $timeout);
return $msg;
if ($method eq 'list_view') {
my @lines = split /\n/, $msg;
$msg = undef;
foreach my $line (@lines) {
if ($line =~ /^\s*LUN\s*:\s*(\d+)$/) {
$msg = $1;
last;
}
}
} elsif ($method eq 'list_lu') {
my $object = $guid;
my @lines = split /\n/, $msg;
$msg = undef;
foreach my $line (@lines) {
if ($line =~ /(\w+)\s+\d+\s+$object$/) {
$msg = $1;
last;
}
}
} elsif ($method eq 'create_lu') {
$msg = $guid;
}
return $msg;
} }

View File

@ -33,6 +33,7 @@ my $OLD_CONFIG = undef;
my @ssh_opts = ('-o', 'BatchMode=yes'); my @ssh_opts = ('-o', 'BatchMode=yes');
my @ssh_cmd = ('/usr/bin/ssh', @ssh_opts); my @ssh_cmd = ('/usr/bin/ssh', @ssh_opts);
my @scp_cmd = ('/usr/bin/scp', @ssh_opts); my @scp_cmd = ('/usr/bin/scp', @ssh_opts);
my $id_rsa_path = '/etc/pve/priv/zfs';
my $ietadm = '/usr/sbin/ietadm'; my $ietadm = '/usr/sbin/ietadm';
my $execute_command = sub { my $execute_command = sub {
@ -59,9 +60,9 @@ my $execute_command = sub {
$target = 'root@' . $scfg->{portal}; $target = 'root@' . $scfg->{portal};
if ($exec eq 'scp') { if ($exec eq 'scp') {
$cmd = [@scp_cmd, $method, "$target:$params[0]"]; $cmd = [@scp_cmd, '-i', "$id_rsa_path/$scfg->{portal}_id_rsa", $method, "$target:$params[0]"];
} else { } else {
$cmd = [@ssh_cmd, $target, $method, @params]; $cmd = [@ssh_cmd, '-i', "$id_rsa_path/$scfg->{portal}_id_rsa", $target, $method, @params];
} }
eval { eval {
@ -103,7 +104,7 @@ my $read_config = sub {
$target = 'root@' . $scfg->{portal}; $target = 'root@' . $scfg->{portal};
my $cmd = [@ssh_cmd, $target, $luncmd, $CONFIG_FILE]; my $cmd = [@ssh_cmd, '-i', "$id_rsa_path/$scfg->{portal}_id_rsa", $target, $luncmd, $CONFIG_FILE];
eval { eval {
run_command($cmd, outfunc => $output, errfunc => $errfunc, timeout => $timeout); run_command($cmd, outfunc => $output, errfunc => $errfunc, timeout => $timeout);
}; };

View File

@ -10,12 +10,12 @@ use PVE::Tools qw(run_command file_read_firstline trim dir_glob_regex dir_glob_f
use Data::Dumper; use Data::Dumper;
my @CONFIG_FILES = ( my @CONFIG_FILES = (
'/usr/local/etc/istgt/istgt.conf', # FreeBSD, FreeNAS '/usr/local/etc/istgt/istgt.conf', # FreeBSD, FreeNAS
'/var/etc/iscsi/istgt.conf' # NAS4Free '/var/etc/iscsi/istgt.conf' # NAS4Free
); );
my @DAEMONS = ( my @DAEMONS = (
'/usr/local/etc/rc.d/istgt', # FreeBSD, FreeNAS '/usr/local/etc/rc.d/istgt', # FreeBSD, FreeNAS
'/var/etc/rc.d/istgt' # NAS4Free '/var/etc/rc.d/istgt' # NAS4Free
); );
# A logical unit can max have 63 LUNs # A logical unit can max have 63 LUNs
@ -31,6 +31,7 @@ my $OLD_CONFIG = undef;
my @ssh_opts = ('-o', 'BatchMode=yes'); my @ssh_opts = ('-o', 'BatchMode=yes');
my @ssh_cmd = ('/usr/bin/ssh', @ssh_opts); my @ssh_cmd = ('/usr/bin/ssh', @ssh_opts);
my @scp_cmd = ('/usr/bin/scp', @ssh_opts); my @scp_cmd = ('/usr/bin/scp', @ssh_opts);
my $id_rsa_path = '/etc/pve/priv/zfs';
#Current SIGHUP reload limitations (http://www.peach.ne.jp/archives/istgt/): #Current SIGHUP reload limitations (http://www.peach.ne.jp/archives/istgt/):
# #
@ -38,7 +39,7 @@ my @scp_cmd = ('/usr/bin/scp', @ssh_opts);
# LU connected by the initiator can't be reloaded by SIGHUP. # LU connected by the initiator can't be reloaded by SIGHUP.
# PG and IG mapped to LU can't be deleted by SIGHUP. # PG and IG mapped to LU can't be deleted by SIGHUP.
# If you delete an active LU, all connections of the LU are closed by SIGHUP. # If you delete an active LU, all connections of the LU are closed by SIGHUP.
# Updating IG is not affected until the next login. # Updating IG is not affected until the next login.
# #
# FreeBSD # FreeBSD
# 1. Alt-F2 to change to native shell (zfsguru) # 1. Alt-F2 to change to native shell (zfsguru)
@ -59,57 +60,57 @@ sub get_base;
sub run_lun_command; sub run_lun_command;
my $read_config = sub { my $read_config = sub {
my ($scfg, $timeout, $method) = @_; my ($scfg, $timeout, $method) = @_;
my $msg = ''; my $msg = '';
my $err = undef; my $err = undef;
my $luncmd = 'cat'; my $luncmd = 'cat';
my $target; my $target;
$timeout = 10 if !$timeout; $timeout = 10 if !$timeout;
my $output = sub { my $output = sub {
my $line = shift; my $line = shift;
$msg .= "$line\n"; $msg .= "$line\n";
}; };
my $errfunc = sub { my $errfunc = sub {
my $line = shift; my $line = shift;
$err .= "$line"; $err .= "$line";
}; };
$target = 'root@' . $scfg->{portal}; $target = 'root@' . $scfg->{portal};
my $daemon = 0; my $daemon = 0;
foreach my $config (@CONFIG_FILES) { foreach my $config (@CONFIG_FILES) {
$err = undef; $err = undef;
my $cmd = [@ssh_cmd, $target, $luncmd, $config]; my $cmd = [@ssh_cmd, '-i', "$id_rsa_path/$scfg->{portal}_id_rsa", $target, $luncmd, $config];
eval { eval {
run_command($cmd, outfunc => $output, errfunc => $errfunc, timeout => $timeout); run_command($cmd, outfunc => $output, errfunc => $errfunc, timeout => $timeout);
}; };
do { do {
$err = undef; $err = undef;
$DAEMON = $DAEMONS[$daemon]; $DAEMON = $DAEMONS[$daemon];
$CONFIG_FILE = $config; $CONFIG_FILE = $config;
last; last;
} unless $@; } unless $@;
$daemon++; $daemon++;
} }
die $err if ($err && $err !~ /No such file or directory/); die $err if ($err && $err !~ /No such file or directory/);
die "No configuration found. Install istgt on $scfg->{portal}" if $msg eq ''; die "No configuration found. Install istgt on $scfg->{portal}" if $msg eq '';
return $msg; return $msg;
}; };
my $get_config = sub { my $get_config = sub {
my ($scfg) = @_; my ($scfg) = @_;
my @conf = undef; my @conf = undef;
my $config = $read_config->($scfg, undef, 'get_config');
die "Missing config file" unless $config;
$OLD_CONFIG = $config; my $config = $read_config->($scfg, undef, 'get_config');
die "Missing config file" unless $config;
return $config; $OLD_CONFIG = $config;
return $config;
}; };
my $parse_size = sub { my $parse_size = sub {
@ -118,25 +119,25 @@ my $parse_size = sub {
return 0 if !$text; return 0 if !$text;
if ($text =~ m/^(\d+(\.\d+)?)([TGMK]B)?$/) { if ($text =~ m/^(\d+(\.\d+)?)([TGMK]B)?$/) {
my ($size, $reminder, $unit) = ($1, $2, $3); my ($size, $reminder, $unit) = ($1, $2, $3);
return $size if !$unit; return $size if !$unit;
if ($unit eq 'KB') { if ($unit eq 'KB') {
$size *= 1024; $size *= 1024;
} elsif ($unit eq 'MB') { } elsif ($unit eq 'MB') {
$size *= 1024*1024; $size *= 1024*1024;
} elsif ($unit eq 'GB') { } elsif ($unit eq 'GB') {
$size *= 1024*1024*1024; $size *= 1024*1024*1024;
} elsif ($unit eq 'TB') { } elsif ($unit eq 'TB') {
$size *= 1024*1024*1024*1024; $size *= 1024*1024*1024*1024;
} }
if ($reminder) { if ($reminder) {
$size = ceil($size); $size = ceil($size);
} }
return $size; return $size;
} elsif ($text =~ /^auto$/i) { } elsif ($text =~ /^auto$/i) {
return 'AUTO'; return 'AUTO';
} else { } else {
return 0; return 0;
} }
}; };
@ -145,435 +146,435 @@ my $size_with_unit = sub {
return '0KB' if !$size; return '0KB' if !$size;
return $size if $size eq 'AUTO'; return $size if $size eq 'AUTO';
if ($size =~ m/^\d+$/) { if ($size =~ m/^\d+$/) {
++$n and $size /= 1024 until $size < 1024; ++$n and $size /= 1024 until $size < 1024;
if ($size =~ /\./) { if ($size =~ /\./) {
return sprintf "%.2f%s", $size, ( qw[bytes KB MB GB TB] )[ $n ]; return sprintf "%.2f%s", $size, ( qw[bytes KB MB GB TB] )[ $n ];
} else { } else {
return sprintf "%d%s", $size, ( qw[bytes KB MB GB TB] )[ $n ]; return sprintf "%d%s", $size, ( qw[bytes KB MB GB TB] )[ $n ];
} }
} }
die "$size: Not a number"; die "$size: Not a number";
}; };
my $lun_dumper = sub { my $lun_dumper = sub {
my ($lun) = @_; my ($lun) = @_;
my $config = ''; my $config = '';
$config .= "\n[$lun]\n"; $config .= "\n[$lun]\n";
$config .= 'TargetName ' . $SETTINGS->{$lun}->{TargetName} . "\n"; $config .= 'TargetName ' . $SETTINGS->{$lun}->{TargetName} . "\n";
$config .= 'Mapping ' . $SETTINGS->{$lun}->{Mapping} . "\n"; $config .= 'Mapping ' . $SETTINGS->{$lun}->{Mapping} . "\n";
$config .= 'AuthGroup ' . $SETTINGS->{$lun}->{AuthGroup} . "\n"; $config .= 'AuthGroup ' . $SETTINGS->{$lun}->{AuthGroup} . "\n";
$config .= 'UnitType ' . $SETTINGS->{$lun}->{UnitType} . "\n"; $config .= 'UnitType ' . $SETTINGS->{$lun}->{UnitType} . "\n";
$config .= 'QueueDepth ' . $SETTINGS->{$lun}->{QueueDepth} . "\n"; $config .= 'QueueDepth ' . $SETTINGS->{$lun}->{QueueDepth} . "\n";
foreach my $conf (@{$SETTINGS->{$lun}->{luns}}) { foreach my $conf (@{$SETTINGS->{$lun}->{luns}}) {
$config .= "$conf->{lun} Storage " . $conf->{Storage}; $config .= "$conf->{lun} Storage " . $conf->{Storage};
$config .= ' ' . $size_with_unit->($conf->{Size}) . "\n"; $config .= ' ' . $size_with_unit->($conf->{Size}) . "\n";
} }
$config .= "\n"; $config .= "\n";
return $config; return $config;
}; };
my $get_lu_name = sub { my $get_lu_name = sub {
my ($target) = @_; my ($target) = @_;
my $used = (); my $used = ();
my $i; my $i;
if (! exists $SETTINGS->{$target}->{used}) {
for ($i = 0; $i < $MAX_LUNS; $i++) {
$used->{$i} = 0;
}
foreach my $lun (@{$SETTINGS->{$target}->{luns}}) {
$lun->{lun} =~ /^LUN(\d+)$/;
$used->{$1} = 1;
}
$SETTINGS->{$target}->{used} = $used;
}
$used = $SETTINGS->{$target}->{used};
for ($i = 0; $i < $MAX_LUNS; $i++) {
last unless $used->{$i};
}
$SETTINGS->{$target}->{used}->{$i} = 1;
return "LUN$i"; if (! exists $SETTINGS->{$target}->{used}) {
for ($i = 0; $i < $MAX_LUNS; $i++) {
$used->{$i} = 0;
}
foreach my $lun (@{$SETTINGS->{$target}->{luns}}) {
$lun->{lun} =~ /^LUN(\d+)$/;
$used->{$1} = 1;
}
$SETTINGS->{$target}->{used} = $used;
}
$used = $SETTINGS->{$target}->{used};
for ($i = 0; $i < $MAX_LUNS; $i++) {
last unless $used->{$i};
}
$SETTINGS->{$target}->{used}->{$i} = 1;
return "LUN$i";
}; };
my $init_lu_name = sub { my $init_lu_name = sub {
my ($target) = @_; my ($target) = @_;
my $used = (); my $used = ();
if (! exists($SETTINGS->{$target}->{used})) { if (! exists($SETTINGS->{$target}->{used})) {
for (my $i = 0; $i < $MAX_LUNS; $i++) { for (my $i = 0; $i < $MAX_LUNS; $i++) {
$used->{$i} = 0; $used->{$i} = 0;
} }
$SETTINGS->{$target}->{used} = $used; $SETTINGS->{$target}->{used} = $used;
} }
foreach my $lun (@{$SETTINGS->{$target}->{luns}}) { foreach my $lun (@{$SETTINGS->{$target}->{luns}}) {
$lun->{lun} =~ /^LUN(\d+)$/; $lun->{lun} =~ /^LUN(\d+)$/;
$SETTINGS->{$target}->{used}->{$1} = 1; $SETTINGS->{$target}->{used}->{$1} = 1;
} }
}; };
my $free_lu_name = sub { my $free_lu_name = sub {
my ($target, $lu_name) = @_; my ($target, $lu_name) = @_;
$lu_name =~ /^LUN(\d+)$/; $lu_name =~ /^LUN(\d+)$/;
$SETTINGS->{$target}->{used}->{$1} = 0; $SETTINGS->{$target}->{used}->{$1} = 0;
}; };
my $make_lun = sub { my $make_lun = sub {
my ($path) = @_; my ($path) = @_;
my $target = $SETTINGS->{current};
die 'Maximum number of LUNs per target is 63' if scalar @{$SETTINGS->{$target}->{luns}} >= $MAX_LUNS;
my $lun = $get_lu_name->($target);
my $conf = {
lun => $lun,
Storage => $path,
Size => 'AUTO',
};
push @{$SETTINGS->{$target}->{luns}}, $conf;
return $conf->{lun}; my $target = $SETTINGS->{current};
die 'Maximum number of LUNs per target is 63' if scalar @{$SETTINGS->{$target}->{luns}} >= $MAX_LUNS;
my $lun = $get_lu_name->($target);
my $conf = {
lun => $lun,
Storage => $path,
Size => 'AUTO',
};
push @{$SETTINGS->{$target}->{luns}}, $conf;
return $conf->{lun};
}; };
my $parser = sub { my $parser = sub {
my ($scfg) = @_; my ($scfg) = @_;
my $lun = undef;
my $line = 0;
my $config = $get_config->($scfg);
my @cfgfile = split "\n", $config;
foreach (@cfgfile) { my $lun = undef;
$line++; my $line = 0;
if ($_ =~ /^\s*\[(PortalGroup\d+)\]\s*/) {
$lun = undef;
$SETTINGS->{$1} = ();
} elsif ($_ =~ /^\s*\[(InitiatorGroup\d+)\]\s*/) {
$lun = undef;
$SETTINGS->{$1} = ();
} elsif ($_ =~ /^\s*PidFile\s+"?([\w\/\.]+)"?\s*/) {
$lun = undef;
$SETTINGS->{pidfile} = $1;
} elsif ($_ =~ /^\s*NodeBase\s+"?([\w\-\.]+)"?\s*/) {
$lun = undef;
$SETTINGS->{nodebase} = $1;
} elsif ($_ =~ /^\s*\[(LogicalUnit\d+)\]\s*/) {
$lun = $1;
$SETTINGS->{$lun} = ();
$SETTINGS->{targets}++;
} elsif ($lun) {
next if (($_ =~ /^\s*#/) || ($_ =~ /^\s*$/));
if ($_ =~ /^\s*(\w+)\s+(.+)\s*/) {
#next if $2 =~ /^Option.*/;
$SETTINGS->{$lun}->{$1} = $2;
$SETTINGS->{$lun}->{$1} =~ s/^\s+|\s+$|"\s*//g;
} else {
die "$line: parse error [$_]";
}
}
$CONFIG .= "$_\n" unless $lun;
}
$CONFIG =~ s/\n$//; my $config = $get_config->($scfg);
die "$scfg->{target}: Target not found" unless $SETTINGS->{targets}; my @cfgfile = split "\n", $config;
my $max = $SETTINGS->{targets};
my $base = get_base;
for (my $i = 1; $i <= $max; $i++) { foreach (@cfgfile) {
my $target = $SETTINGS->{nodebase}.':'.$SETTINGS->{"LogicalUnit$i"}->{TargetName}; $line++;
if ($target eq $scfg->{target}) { if ($_ =~ /^\s*\[(PortalGroup\d+)\]\s*/) {
my $lu = (); $lun = undef;
while ((my $key, my $val) = each(%{$SETTINGS->{"LogicalUnit$i"}})) { $SETTINGS->{$1} = ();
if ($key =~ /^LUN\d+/) { } elsif ($_ =~ /^\s*\[(InitiatorGroup\d+)\]\s*/) {
if ($val =~ /^Storage\s+([\w\/\-]+)\s+(\w+)/) { $lun = undef;
my $storage = $1; $SETTINGS->{$1} = ();
my $size = $parse_size->($2); } elsif ($_ =~ /^\s*PidFile\s+"?([\w\/\.]+)"?\s*/) {
my $conf = undef; $lun = undef;
if ($storage =~ /^$base\/$scfg->{pool}\/([\w\-]+)$/) { $SETTINGS->{pidfile} = $1;
$conf = { } elsif ($_ =~ /^\s*NodeBase\s+"?([\w\-\.]+)"?\s*/) {
lun => $key, $lun = undef;
Storage => $storage, $SETTINGS->{nodebase} = $1;
Size => $size, } elsif ($_ =~ /^\s*\[(LogicalUnit\d+)\]\s*/) {
}; $lun = $1;
} $SETTINGS->{$lun} = ();
push @$lu, $conf if $conf; $SETTINGS->{targets}++;
} } elsif ($lun) {
delete $SETTINGS->{"LogicalUnit$i"}->{$key}; next if (($_ =~ /^\s*#/) || ($_ =~ /^\s*$/));
} if ($_ =~ /^\s*(\w+)\s+(.+)\s*/) {
} #next if $2 =~ /^Option.*/;
$SETTINGS->{"LogicalUnit$i"}->{luns} = $lu; $SETTINGS->{$lun}->{$1} = $2;
$SETTINGS->{current} = "LogicalUnit$i"; $SETTINGS->{$lun}->{$1} =~ s/^\s+|\s+$|"\s*//g;
$init_lu_name->("LogicalUnit$i"); } else {
} else { die "$line: parse error [$_]";
$CONFIG .= $lun_dumper->("LogicalUnit$i"); }
delete $SETTINGS->{"LogicalUnit$i"}; }
$SETTINGS->{targets}--; $CONFIG .= "$_\n" unless $lun;
} }
}
die "$scfg->{target}: Target not found" unless $SETTINGS->{targets} > 0; $CONFIG =~ s/\n$//;
die "$scfg->{target}: Target not found" unless $SETTINGS->{targets};
my $max = $SETTINGS->{targets};
my $base = get_base;
for (my $i = 1; $i <= $max; $i++) {
my $target = $SETTINGS->{nodebase}.':'.$SETTINGS->{"LogicalUnit$i"}->{TargetName};
if ($target eq $scfg->{target}) {
my $lu = ();
while ((my $key, my $val) = each(%{$SETTINGS->{"LogicalUnit$i"}})) {
if ($key =~ /^LUN\d+/) {
if ($val =~ /^Storage\s+([\w\/\-]+)\s+(\w+)/) {
my $storage = $1;
my $size = $parse_size->($2);
my $conf = undef;
if ($storage =~ /^$base\/$scfg->{pool}\/([\w\-]+)$/) {
$conf = {
lun => $key,
Storage => $storage,
Size => $size,
};
}
push @$lu, $conf if $conf;
}
delete $SETTINGS->{"LogicalUnit$i"}->{$key};
}
}
$SETTINGS->{"LogicalUnit$i"}->{luns} = $lu;
$SETTINGS->{current} = "LogicalUnit$i";
$init_lu_name->("LogicalUnit$i");
} else {
$CONFIG .= $lun_dumper->("LogicalUnit$i");
delete $SETTINGS->{"LogicalUnit$i"};
$SETTINGS->{targets}--;
}
}
die "$scfg->{target}: Target not found" unless $SETTINGS->{targets} > 0;
}; };
my $list_lun = sub { my $list_lun = sub {
my ($scfg, $timeout, $method, @params) = @_; my ($scfg, $timeout, $method, @params) = @_;
my $name = undef; my $name = undef;
my $object = $params[0]; my $object = $params[0];
for my $key (keys %$SETTINGS) { for my $key (keys %$SETTINGS) {
next unless $key =~ /^LogicalUnit\d+$/; next unless $key =~ /^LogicalUnit\d+$/;
foreach my $lun (@{$SETTINGS->{$key}->{luns}}) { foreach my $lun (@{$SETTINGS->{$key}->{luns}}) {
if ($lun->{Storage} =~ /^$object$/) { if ($lun->{Storage} =~ /^$object$/) {
return $lun->{Storage}; return $lun->{Storage};
} }
} }
} }
return $name; return $name;
}; };
my $create_lun = sub { my $create_lun = sub {
my ($scfg, $timeout, $method, @params) = @_; my ($scfg, $timeout, $method, @params) = @_;
my $res = (); my $res = ();
my $file = "/tmp/config$$"; my $file = "/tmp/config$$";
if ($list_lun->($scfg, $timeout, $method, @params)) { if ($list_lun->($scfg, $timeout, $method, @params)) {
die "$params[0]: LUN exists"; die "$params[0]: LUN exists";
} }
my $lun = $params[0]; my $lun = $params[0];
$lun = $make_lun->($lun); $lun = $make_lun->($lun);
my $config = $lun_dumper->($SETTINGS->{current}); my $config = $lun_dumper->($SETTINGS->{current});
open(my $fh, '>', $file) or die "Could not open file '$file' $!"; open(my $fh, '>', $file) or die "Could not open file '$file' $!";
print $fh $CONFIG; print $fh $CONFIG;
print $fh $config; print $fh $config;
close $fh; close $fh;
@params = ($CONFIG_FILE); @params = ($CONFIG_FILE);
$res = { $res = {
cmd => 'scp', cmd => 'scp',
method => $file, method => $file,
params => \@params, params => \@params,
msg => $lun, msg => $lun,
post_exe => sub { post_exe => sub {
unlink $file; unlink $file;
}, },
}; };
return $res; return $res;
}; };
my $delete_lun = sub { my $delete_lun = sub {
my ($scfg, $timeout, $method, @params) = @_; my ($scfg, $timeout, $method, @params) = @_;
my $res = (); my $res = ();
my $file = "/tmp/config$$"; my $file = "/tmp/config$$";
my $target = $SETTINGS->{current};
my $luns = ();
foreach my $conf (@{$SETTINGS->{$target}->{luns}}) { my $target = $SETTINGS->{current};
if ($conf->{Storage} =~ /^$params[0]$/) { my $luns = ();
$free_lu_name->($target, $conf->{lun});
} else {
push @$luns, $conf;
}
}
$SETTINGS->{$target}->{luns} = $luns;
my $config = $lun_dumper->($SETTINGS->{current});
open(my $fh, '>', $file) or die "Could not open file '$file' $!";
print $fh $CONFIG; foreach my $conf (@{$SETTINGS->{$target}->{luns}}) {
print $fh $config; if ($conf->{Storage} =~ /^$params[0]$/) {
close $fh; $free_lu_name->($target, $conf->{lun});
@params = ($CONFIG_FILE); } else {
$res = { push @$luns, $conf;
cmd => 'scp', }
method => $file, }
params => \@params, $SETTINGS->{$target}->{luns} = $luns;
post_exe => sub {
unlink $file;
run_lun_command($scfg, undef, 'add_view', 'restart');
},
};
return $res; my $config = $lun_dumper->($SETTINGS->{current});
open(my $fh, '>', $file) or die "Could not open file '$file' $!";
print $fh $CONFIG;
print $fh $config;
close $fh;
@params = ($CONFIG_FILE);
$res = {
cmd => 'scp',
method => $file,
params => \@params,
post_exe => sub {
unlink $file;
run_lun_command($scfg, undef, 'add_view', 'restart');
},
};
return $res;
}; };
my $import_lun = sub { my $import_lun = sub {
my ($scfg, $timeout, $method, @params) = @_; my ($scfg, $timeout, $method, @params) = @_;
my $res = $create_lun->($scfg, $timeout, $method, @params); my $res = $create_lun->($scfg, $timeout, $method, @params);
return $res; return $res;
}; };
my $add_view = sub { my $add_view = sub {
my ($scfg, $timeout, $method, @params) = @_; my ($scfg, $timeout, $method, @params) = @_;
my $cmdmap; my $cmdmap;
if (@params && $params[0] eq 'restart') {
@params = ('restart', '1>&2', '>', '/dev/null');
$cmdmap = {
cmd => 'ssh',
method => $DAEMON,
params => \@params,
};
} else {
@params = ('-HUP', '$(cat '. "$SETTINGS->{pidfile})");
$cmdmap = {
cmd => 'ssh',
method => 'kill',
params => \@params,
};
}
return $cmdmap; if (@params && $params[0] eq 'restart') {
@params = ('restart', '1>&2', '>', '/dev/null');
$cmdmap = {
cmd => 'ssh',
method => $DAEMON,
params => \@params,
};
} else {
@params = ('-HUP', '$(cat '. "$SETTINGS->{pidfile})");
$cmdmap = {
cmd => 'ssh',
method => 'kill',
params => \@params,
};
}
return $cmdmap;
}; };
my $modify_lun = sub { my $modify_lun = sub {
my ($scfg, $timeout, $method, @params) = @_; my ($scfg, $timeout, $method, @params) = @_;
# Current SIGHUP reload limitations # Current SIGHUP reload limitations
# LU connected by the initiator can't be reloaded by SIGHUP. # LU connected by the initiator can't be reloaded by SIGHUP.
# Until above limitation persists modifying a LUN will require # Until above limitation persists modifying a LUN will require
# a restart of the daemon breaking all current connections # a restart of the daemon breaking all current connections
#die 'Modify a connected LUN is not currently supported by istgt'; #die 'Modify a connected LUN is not currently supported by istgt';
@params = ('restart', @params); @params = ('restart', @params);
return $add_view->($scfg, $timeout, $method, @params); return $add_view->($scfg, $timeout, $method, @params);
}; };
my $list_view = sub { my $list_view = sub {
my ($scfg, $timeout, $method, @params) = @_; my ($scfg, $timeout, $method, @params) = @_;
my $lun = undef; my $lun = undef;
my $object = $params[0]; my $object = $params[0];
for my $key (keys %$SETTINGS) { for my $key (keys %$SETTINGS) {
next unless $key =~ /^LogicalUnit\d+$/; next unless $key =~ /^LogicalUnit\d+$/;
foreach my $lun (@{$SETTINGS->{$key}->{luns}}) { foreach my $lun (@{$SETTINGS->{$key}->{luns}}) {
if ($lun->{Storage} =~ /^$object$/) { if ($lun->{Storage} =~ /^$object$/) {
if ($lun->{lun} =~ /^LUN(\d+)/) { if ($lun->{lun} =~ /^LUN(\d+)/) {
return $1; return $1;
} }
die "$lun->{Storage}: Missing LUN"; die "$lun->{Storage}: Missing LUN";
} }
} }
} }
return $lun; return $lun;
}; };
my $get_lun_cmd_map = sub { my $get_lun_cmd_map = sub {
my ($method) = @_; my ($method) = @_;
my $cmdmap = { my $cmdmap = {
create_lu => { cmd => $create_lun }, create_lu => { cmd => $create_lun },
delete_lu => { cmd => $delete_lun }, delete_lu => { cmd => $delete_lun },
import_lu => { cmd => $import_lun }, import_lu => { cmd => $import_lun },
modify_lu => { cmd => $modify_lun }, modify_lu => { cmd => $modify_lun },
add_view => { cmd => $add_view }, add_view => { cmd => $add_view },
list_view => { cmd => $list_view }, list_view => { cmd => $list_view },
list_lu => { cmd => $list_lun }, list_lu => { cmd => $list_lun },
}; };
die "unknown command '$method'" unless exists $cmdmap->{$method}; die "unknown command '$method'" unless exists $cmdmap->{$method};
return $cmdmap->{$method}; return $cmdmap->{$method};
}; };
sub run_lun_command { sub run_lun_command {
my ($scfg, $timeout, $method, @params) = @_; my ($scfg, $timeout, $method, @params) = @_;
my $msg = ''; my $msg = '';
my $luncmd; my $luncmd;
my $target; my $target;
my $cmd; my $cmd;
my $res; my $res;
$timeout = 10 if !$timeout; $timeout = 10 if !$timeout;
my $is_add_view = 0; my $is_add_view = 0;
my $output = sub { my $output = sub {
my $line = shift; my $line = shift;
$msg .= "$line\n"; $msg .= "$line\n";
}; };
$target = 'root@' . $scfg->{portal}; $target = 'root@' . $scfg->{portal};
$parser->($scfg) unless $SETTINGS; $parser->($scfg) unless $SETTINGS;
my $cmdmap = $get_lun_cmd_map->($method); my $cmdmap = $get_lun_cmd_map->($method);
if ($method eq 'add_view') { if ($method eq 'add_view') {
$is_add_view = 1 ; $is_add_view = 1 ;
$timeout = 15; $timeout = 15;
} }
if (ref $cmdmap->{cmd} eq 'CODE') { if (ref $cmdmap->{cmd} eq 'CODE') {
$res = $cmdmap->{cmd}->($scfg, $timeout, $method, @params); $res = $cmdmap->{cmd}->($scfg, $timeout, $method, @params);
if (ref $res) { if (ref $res) {
$method = $res->{method}; $method = $res->{method};
@params = @{$res->{params}}; @params = @{$res->{params}};
if ($res->{cmd} eq 'scp') { if ($res->{cmd} eq 'scp') {
$cmd = [@scp_cmd, $method, "$target:$params[0]"]; $cmd = [@scp_cmd, '-i', "$id_rsa_path/$scfg->{portal}_id_rsa", $method, "$target:$params[0]"];
} else { } else {
$cmd = [@ssh_cmd, $target, $method, @params]; $cmd = [@ssh_cmd, '-i', "$id_rsa_path/$scfg->{portal}_id_rsa", $target, $method, @params];
} }
} else { } else {
return $res; return $res;
} }
} else { } else {
$luncmd = $cmdmap->{cmd}; $luncmd = $cmdmap->{cmd};
$method = $cmdmap->{method}; $method = $cmdmap->{method};
$cmd = [@ssh_cmd, $target, $luncmd, $method, @params]; $cmd = [@ssh_cmd, '-i', "$id_rsa_path/$scfg->{portal}_id_rsa", $target, $luncmd, $method, @params];
} }
eval { eval {
run_command($cmd, outfunc => $output, timeout => $timeout); run_command($cmd, outfunc => $output, timeout => $timeout);
}; };
if ($@ && $is_add_view) { if ($@ && $is_add_view) {
my $err = $@; my $err = $@;
if ($OLD_CONFIG) { if ($OLD_CONFIG) {
my $err1 = undef; my $err1 = undef;
my $file = "/tmp/config$$"; my $file = "/tmp/config$$";
open(my $fh, '>', $file) or die "Could not open file '$file' $!"; open(my $fh, '>', $file) or die "Could not open file '$file' $!";
print $fh $OLD_CONFIG; print $fh $OLD_CONFIG;
close $fh; close $fh;
$cmd = [@scp_cmd, $file, $CONFIG_FILE]; $cmd = [@scp_cmd, '-i', "$id_rsa_path/$scfg->{portal}_id_rsa", $file, $CONFIG_FILE];
eval { eval {
run_command($cmd, outfunc => $output, timeout => $timeout); run_command($cmd, outfunc => $output, timeout => $timeout);
}; };
$err1 = $@ if $@; $err1 = $@ if $@;
unlink $file; unlink $file;
die "$err\n$err1" if $err1; die "$err\n$err1" if $err1;
eval { eval {
run_lun_command($scfg, undef, 'add_view', 'restart'); run_lun_command($scfg, undef, 'add_view', 'restart');
}; };
die "$err\n$@" if ($@); die "$err\n$@" if ($@);
} }
die $err; die $err;
} elsif ($@) { } elsif ($@) {
die $@; die $@;
} elsif ($is_add_view) { } elsif ($is_add_view) {
$OLD_CONFIG = undef; $OLD_CONFIG = undef;
} }
if ($res->{post_exe} && ref $res->{post_exe} eq 'CODE') {
$res->{post_exe}->();
}
if ($res->{msg}) { if ($res->{post_exe} && ref $res->{post_exe} eq 'CODE') {
$msg = $res->{msg}; $res->{post_exe}->();
} }
return $msg; if ($res->{msg}) {
$msg = $res->{msg};
}
return $msg;
} }
sub get_base { sub get_base {
return '/dev/zvol'; return '/dev/zvol';
} }
1; 1;

View File

@ -14,35 +14,36 @@ use PVE::Storage::LunCmd::Iet;
my @ssh_opts = ('-o', 'BatchMode=yes'); my @ssh_opts = ('-o', 'BatchMode=yes');
my @ssh_cmd = ('/usr/bin/ssh', @ssh_opts); my @ssh_cmd = ('/usr/bin/ssh', @ssh_opts);
my $id_rsa_path = '/etc/pve/priv/zfs';
my $lun_cmds = { my $lun_cmds = {
create_lu => 1, create_lu => 1,
delete_lu => 1, delete_lu => 1,
import_lu => 1, import_lu => 1,
modify_lu => 1, modify_lu => 1,
add_view => 1, add_view => 1,
list_view => 1, list_view => 1,
list_lu => 1, list_lu => 1,
}; };
my $zfs_unknown_scsi_provider = sub { my $zfs_unknown_scsi_provider = sub {
my ($provider) = @_; my ($provider) = @_;
die "$provider: unknown iscsi provider. Available [comstar, istgt, iet]"; die "$provider: unknown iscsi provider. Available [comstar, istgt, iet]";
}; };
my $zfs_get_base = sub { my $zfs_get_base = sub {
my ($scfg) = @_; my ($scfg) = @_;
if ($scfg->{iscsiprovider} eq 'comstar') { if ($scfg->{iscsiprovider} eq 'comstar') {
return PVE::Storage::LunCmd::Comstar::get_base; return PVE::Storage::LunCmd::Comstar::get_base;
} elsif ($scfg->{iscsiprovider} eq 'istgt') { } elsif ($scfg->{iscsiprovider} eq 'istgt') {
return PVE::Storage::LunCmd::Istgt::get_base; return PVE::Storage::LunCmd::Istgt::get_base;
} elsif ($scfg->{iscsiprovider} eq 'iet') { } elsif ($scfg->{iscsiprovider} eq 'iet') {
return PVE::Storage::LunCmd::Iet::get_base; return PVE::Storage::LunCmd::Iet::get_base;
} else { } else {
$zfs_unknown_scsi_provider->($scfg->{iscsiprovider}); $zfs_unknown_scsi_provider->($scfg->{iscsiprovider});
} }
}; };
sub zfs_request { sub zfs_request {
@ -51,41 +52,41 @@ sub zfs_request {
my $cmdmap; my $cmdmap;
my $zfscmd; my $zfscmd;
my $target; my $target;
my $msg; my $msg;
$timeout = 5 if !$timeout; $timeout = 5 if !$timeout;
if ($lun_cmds->{$method}) { if ($lun_cmds->{$method}) {
if ($scfg->{iscsiprovider} eq 'comstar') { if ($scfg->{iscsiprovider} eq 'comstar') {
$msg = PVE::Storage::LunCmd::Comstar::run_lun_command($scfg, $timeout, $method, @params); $msg = PVE::Storage::LunCmd::Comstar::run_lun_command($scfg, $timeout, $method, @params);
} elsif ($scfg->{iscsiprovider} eq 'istgt') { } elsif ($scfg->{iscsiprovider} eq 'istgt') {
$msg = PVE::Storage::LunCmd::Istgt::run_lun_command($scfg, $timeout, $method, @params); $msg = PVE::Storage::LunCmd::Istgt::run_lun_command($scfg, $timeout, $method, @params);
} elsif ($scfg->{iscsiprovider} eq 'iet') { } elsif ($scfg->{iscsiprovider} eq 'iet') {
$msg = PVE::Storage::LunCmd::Iet::run_lun_command($scfg, $timeout, $method, @params); $msg = PVE::Storage::LunCmd::Iet::run_lun_command($scfg, $timeout, $method, @params);
} else { } else {
$zfs_unknown_scsi_provider->($scfg->{iscsiprovider}); $zfs_unknown_scsi_provider->($scfg->{iscsiprovider});
} }
} else { } else {
if ($method eq 'zpool_list') { if ($method eq 'zpool_list') {
$zfscmd = 'zpool'; $zfscmd = 'zpool';
$method = 'list', $method = 'list',
} else { } else {
$zfscmd = 'zfs'; $zfscmd = 'zfs';
} }
$target = 'root@' . $scfg->{portal}; $target = 'root@' . $scfg->{portal};
my $cmd = [@ssh_cmd, $target, $zfscmd, $method, @params]; my $cmd = [@ssh_cmd, '-i', "$id_rsa_path/$scfg->{portal}_id_rsa", $target, $zfscmd, $method, @params];
$msg = ''; $msg = '';
my $output = sub { my $output = sub {
my $line = shift; my $line = shift;
$msg .= "$line\n"; $msg .= "$line\n";
}; };
run_command($cmd, outfunc => $output, timeout => $timeout); run_command($cmd, outfunc => $output, timeout => $timeout);
} }
return $msg; return $msg;
} }
@ -96,24 +97,24 @@ sub zfs_parse_size {
return 0 if !$text; return 0 if !$text;
if ($text =~ m/^(\d+(\.\d+)?)([TGMK])?$/) { if ($text =~ m/^(\d+(\.\d+)?)([TGMK])?$/) {
my ($size, $reminder, $unit) = ($1, $2, $3); my ($size, $reminder, $unit) = ($1, $2, $3);
return $size if !$unit; return $size if !$unit;
if ($unit eq 'K') { if ($unit eq 'K') {
$size *= 1024; $size *= 1024;
} elsif ($unit eq 'M') { } elsif ($unit eq 'M') {
$size *= 1024*1024; $size *= 1024*1024;
} elsif ($unit eq 'G') { } elsif ($unit eq 'G') {
$size *= 1024*1024*1024; $size *= 1024*1024*1024;
} elsif ($unit eq 'T') { } elsif ($unit eq 'T') {
$size *= 1024*1024*1024*1024; $size *= 1024*1024*1024*1024;
} }
if ($reminder) { if ($reminder) {
$size = ceil($size); $size = ceil($size);
} }
return $size; return $size;
} else { } else {
return 0; return 0;
} }
} }
@ -124,16 +125,16 @@ sub zfs_get_pool_stats {
my $used = 0; my $used = 0;
my $text = zfs_request($scfg, undef, 'get', '-o', 'value', '-Hp', my $text = zfs_request($scfg, undef, 'get', '-o', 'value', '-Hp',
'available,used', $scfg->{pool}); 'available,used', $scfg->{pool});
my @lines = split /\n/, $text; my @lines = split /\n/, $text;
if($lines[0] =~ /^(\d+)$/) { if($lines[0] =~ /^(\d+)$/) {
$available = $1; $available = $1;
} }
if($lines[1] =~ /^(\d+)$/) { if($lines[1] =~ /^(\d+)$/) {
$used = $1; $used = $1;
} }
return ($available, $used); return ($available, $used);
@ -148,28 +149,28 @@ sub zfs_parse_zvol_list {
my @lines = split /\n/, $text; my @lines = split /\n/, $text;
foreach my $line (@lines) { foreach my $line (@lines) {
if ($line =~ /^(.+)\s+([a-zA-Z0-9\.]+|\-)\s+(.+)$/) { if ($line =~ /^(.+)\s+([a-zA-Z0-9\.]+|\-)\s+(.+)$/) {
my $zvol = {}; my $zvol = {};
my $name; my $name;
my $disk; my $disk;
my @zvols = split /\//, $1; my @zvols = split /\//, $1;
my $pool = $zvols[0]; my $pool = $zvols[0];
if (scalar(@zvols) == 2 && $zvols[0] !~ /^rpool$/) { if (scalar(@zvols) == 2 && $zvols[0] !~ /^rpool$/) {
$disk = $zvols[1]; $disk = $zvols[1];
next unless $disk =~ m!^(\w+)-(\d+)-(\w+)-(\d+)$!; next unless $disk =~ m!^(\w+)-(\d+)-(\w+)-(\d+)$!;
$name = $pool . '/' . $disk; $name = $pool . '/' . $disk;
} else { } else {
next; next;
} }
$zvol->{name} = $name; $zvol->{name} = $name;
$zvol->{size} = zfs_parse_size($2); $zvol->{size} = zfs_parse_size($2);
if ($3 !~ /^-$/) { if ($3 !~ /^-$/) {
$zvol->{origin} = $3; $zvol->{origin} = $3;
} }
push @$list, $zvol; push @$list, $zvol;
} }
} }
return $list; return $list;
@ -179,7 +180,7 @@ sub zfs_get_lu_name {
my ($scfg, $zvol) = @_; my ($scfg, $zvol) = @_;
my $object; my $object;
my $base = $zfs_get_base->($scfg); my $base = $zfs_get_base->($scfg);
if ($zvol =~ /^.+\/.+/) { if ($zvol =~ /^.+\/.+/) {
$object = "$base/$zvol"; $object = "$base/$zvol";
} else { } else {
@ -188,8 +189,8 @@ sub zfs_get_lu_name {
my $lu_name = zfs_request($scfg, undef, 'list_lu', $object); my $lu_name = zfs_request($scfg, undef, 'list_lu', $object);
return $lu_name if $lu_name; return $lu_name if $lu_name;
die "Could not find lu_name for zvol $zvol"; die "Could not find lu_name for zvol $zvol";
} }
@ -199,7 +200,7 @@ sub zfs_get_zvol_size {
my $text = zfs_request($scfg, undef, 'get', '-Hp', 'volsize', "$scfg->{pool}/$zvol"); my $text = zfs_request($scfg, undef, 'get', '-Hp', 'volsize', "$scfg->{pool}/$zvol");
if($text =~ /volsize\s(\d+)/){ if($text =~ /volsize\s(\d+)/){
return $1; return $1;
} }
die "Could not get zvol size"; die "Could not get zvol size";
@ -209,9 +210,9 @@ sub zfs_add_lun_mapping_entry {
my ($scfg, $zvol, $guid) = @_; my ($scfg, $zvol, $guid) = @_;
if (! defined($guid)) { if (! defined($guid)) {
$guid = zfs_get_lu_name($scfg, $zvol); $guid = zfs_get_lu_name($scfg, $zvol);
} }
zfs_request($scfg, undef, 'add_view', $guid); zfs_request($scfg, undef, 'add_view', $guid);
} }
@ -226,7 +227,7 @@ sub zfs_delete_lu {
sub zfs_create_lu { sub zfs_create_lu {
my ($scfg, $zvol) = @_; my ($scfg, $zvol) = @_;
my $base = $zfs_get_base->($scfg); my $base = $zfs_get_base->($scfg);
my $guid = zfs_request($scfg, undef, 'create_lu', "$base/$scfg->{pool}/$zvol"); my $guid = zfs_request($scfg, undef, 'create_lu', "$base/$scfg->{pool}/$zvol");
return $guid; return $guid;
@ -235,7 +236,7 @@ sub zfs_create_lu {
sub zfs_import_lu { sub zfs_import_lu {
my ($scfg, $zvol) = @_; my ($scfg, $zvol) = @_;
my $base = $zfs_get_base->($scfg); my $base = $zfs_get_base->($scfg);
zfs_request($scfg, undef, 'import_lu', "$base/$scfg->{pool}/$zvol"); zfs_request($scfg, undef, 'import_lu', "$base/$scfg->{pool}/$zvol");
} }
@ -276,26 +277,26 @@ sub zfs_list_zvol {
my $list = (); my $list = ();
foreach my $zvol (@$zvols) { foreach my $zvol (@$zvols) {
my @values = split('/', $zvol->{name}); my @values = split('/', $zvol->{name});
my $pool = $values[0]; my $pool = $values[0];
my $image = $values[1]; my $image = $values[1];
next if $image !~ m/^((vm|base)-(\d+)-\S+)$/; next if $image !~ m/^((vm|base)-(\d+)-\S+)$/;
my $owner = $3; my $owner = $3;
my $parent = $zvol->{origin}; my $parent = $zvol->{origin};
if($zvol->{origin} && $zvol->{origin} =~ m/^$scfg->{pool}\/(\S+)$/){ if($zvol->{origin} && $zvol->{origin} =~ m/^$scfg->{pool}\/(\S+)$/){
$parent = $1; $parent = $1;
} }
$list->{$pool}->{$image} = { $list->{$pool}->{$image} = {
name => $image, name => $image,
size => $zvol->{size}, size => $zvol->{size},
parent => $parent, parent => $parent,
format => 'raw', format => 'raw',
vmid => $owner vmid => $owner
}; };
} }
return $list; return $list;
@ -309,16 +310,16 @@ sub type {
sub plugindata { sub plugindata {
return { return {
content => [ {images => 1}, { images => 1 }], content => [ {images => 1}, { images => 1 }],
}; };
} }
sub properties { sub properties {
return { return {
iscsiprovider => { iscsiprovider => {
description => "iscsi provider", description => "iscsi provider",
type => 'string', type => 'string',
}, },
blocksize => { blocksize => {
description => "block size", description => "block size",
type => 'string', type => 'string',
@ -331,11 +332,11 @@ sub options {
nodes => { optional => 1 }, nodes => { optional => 1 },
disable => { optional => 1 }, disable => { optional => 1 },
portal => { fixed => 1 }, portal => { fixed => 1 },
target => { fixed => 1 }, target => { fixed => 1 },
pool => { fixed => 1 }, pool => { fixed => 1 },
blocksize => { fixed => 1 }, blocksize => { fixed => 1 },
iscsiprovider => { fixed => 1 }, iscsiprovider => { fixed => 1 },
content => { optional => 1 }, content => { optional => 1 },
}; };
} }
@ -345,7 +346,7 @@ sub parse_volname {
my ($class, $volname) = @_; my ($class, $volname) = @_;
if ($volname =~ m/^(((base|vm)-(\d+)-\S+)\/)?((base)?(vm)?-(\d+)-\S+)$/) { if ($volname =~ m/^(((base|vm)-(\d+)-\S+)\/)?((base)?(vm)?-(\d+)-\S+)$/) {
return ('images', $5, $8, $2, $4, $6); return ('images', $5, $8, $2, $4, $6);
} }
die "unable to parse zfs volume name '$volname'\n"; die "unable to parse zfs volume name '$volname'\n";
@ -361,9 +362,9 @@ sub path {
my $guid = zfs_get_lu_name($scfg, $name); my $guid = zfs_get_lu_name($scfg, $name);
my $lun = zfs_get_lun_number($scfg, $guid); my $lun = zfs_get_lun_number($scfg, $guid);
my $path = "iscsi://$portal/$target/$lun"; my $path = "iscsi://$portal/$target/$lun";
return ($path, $vmid, $vtype); return ($path, $vmid, $vtype);
} }
@ -448,7 +449,7 @@ sub alloc_image {
die "unsupported format '$fmt'" if $fmt ne 'raw'; die "unsupported format '$fmt'" if $fmt ne 'raw';
die "illegal name '$name' - sould be 'vm-$vmid-*'\n" die "illegal name '$name' - sould be 'vm-$vmid-*'\n"
if $name && $name !~ m/^vm-$vmid-/; if $name && $name !~ m/^vm-$vmid-/;
$name = &$find_free_diskname($storeid, $scfg, $vmid); $name = &$find_free_diskname($storeid, $scfg, $vmid);
@ -487,31 +488,31 @@ sub list_images {
if (my $dat = $cache->{zfs}->{$zfspool}) { if (my $dat = $cache->{zfs}->{$zfspool}) {
foreach my $image (keys %$dat) { foreach my $image (keys %$dat) {
my $volname = $dat->{$image}->{name}; my $volname = $dat->{$image}->{name};
my $parent = $dat->{$image}->{parent}; my $parent = $dat->{$image}->{parent};
my $volid = undef; my $volid = undef;
if ($parent && $parent =~ m/^(\S+)@(\S+)$/) { if ($parent && $parent =~ m/^(\S+)@(\S+)$/) {
my ($basename) = ($1); my ($basename) = ($1);
$volid = "$storeid:$basename/$volname"; $volid = "$storeid:$basename/$volname";
} else { } else {
$volid = "$storeid:$volname"; $volid = "$storeid:$volname";
} }
my $owner = $dat->{$volname}->{vmid}; my $owner = $dat->{$volname}->{vmid};
if ($vollist) { if ($vollist) {
my $found = grep { $_ eq $volid } @$vollist; my $found = grep { $_ eq $volid } @$vollist;
next if !$found; next if !$found;
} else { } else {
next if defined ($vmid) && ($owner ne $vmid); next if defined ($vmid) && ($owner ne $vmid);
} }
my $info = $dat->{$volname}; my $info = $dat->{$volname};
$info->{volid} = $volid; $info->{volid} = $volid;
push @$res, $info; push @$res, $info;
} }
} }
return $res; return $res;
@ -526,9 +527,9 @@ sub status {
my $active = 0; my $active = 0;
eval { eval {
($free, $used) = zfs_get_pool_stats($scfg); ($free, $used) = zfs_get_pool_stats($scfg);
$active = 1; $active = 1;
$total = $free + $used; $total = $free + $used;
}; };
warn $@ if $@; warn $@ if $@;
@ -598,21 +599,21 @@ sub volume_has_feature {
my ($class, $scfg, $feature, $storeid, $volname, $snapname, $running) = @_; my ($class, $scfg, $feature, $storeid, $volname, $snapname, $running) = @_;
my $features = { my $features = {
snapshot => { current => 1, snap => 1}, snapshot => { current => 1, snap => 1},
clone => { base => 1}, clone => { base => 1},
template => { current => 1}, template => { current => 1},
copy => { base => 1, current => 1}, copy => { base => 1, current => 1},
}; };
my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) = my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) =
$class->parse_volname($volname); $class->parse_volname($volname);
my $key = undef; my $key = undef;
if ($snapname) { if ($snapname) {
$key = 'snap'; $key = 'snap';
} else { } else {
$key = $isBase ? 'base' : 'current'; $key = $isBase ? 'base' : 'current';
} }
return 1 if $features->{$feature}->{$key}; return 1 if $features->{$feature}->{$key};