]> git.proxmox.com Git - pve-storage.git/blobdiff - PVE/Storage/ISCSIPlugin.pm
plugin: remove volume_snapshot_list
[pve-storage.git] / PVE / Storage / ISCSIPlugin.pm
index 31137403a430a5d41c54ca3babb207e855813747..a79fcb08c56df17e1570f732eec8986bd7751c0e 100644 (file)
@@ -2,13 +2,14 @@ package PVE::Storage::ISCSIPlugin;
 
 use strict;
 use warnings;
+
 use File::stat;
 use IO::Dir;
 use IO::File;
-use PVE::Tools qw(run_command file_read_firstline trim dir_glob_regex dir_glob_foreach);
-use PVE::Storage::Plugin;
+
 use PVE::JSONSchema qw(get_standard_option);
-use Net::Ping;
+use PVE::Storage::Plugin;
+use PVE::Tools qw(run_command file_read_firstline trim dir_glob_regex dir_glob_foreach $IPV4RE $IPV6RE);
 
 use base qw(PVE::Storage::Plugin);
 
@@ -41,16 +42,20 @@ sub iscsi_session_list {
 
     my $res = {};
 
-    run_command($cmd, outfunc => sub {
-       my $line = shift;
+    eval {
+       run_command($cmd, errmsg => 'iscsi session scan failed', outfunc => sub {
+           my $line = shift;
 
-       if ($line =~ m/^tcp:\s+\[(\S+)\]\s+\S+\s+(\S+)\s*$/) {
-           my ($session, $target) = ($1, $2);
-           # there can be several sessions per target (multipath)
-           push @{$res->{$target}}, $session;
-
-       }
-    });
+           if ($line =~ m/^tcp:\s+\[(\S+)\]\s+\S+\s+(\S+)(\s+\S+)?\s*$/) {
+               my ($session, $target) = ($1, $2);
+               # there can be several sessions per target (multipath)
+               push @{$res->{$target}}, $session;
+           }
+       });
+    };
+    if (my $err = $@) {
+       die $err if $err !~ m/: No active sessions.$/i;
+    }
 
     return $res;
 }
@@ -58,10 +63,9 @@ sub iscsi_session_list {
 sub iscsi_test_portal {
     my ($portal) = @_;
 
-    my ($server, $port) = split(':', $portal);
-    my $p = Net::Ping->new("tcp", 2);
-    $p->port_number($port || 3260);
-    return $p->ping($server);
+    my ($server, $port) = PVE::Tools::parse_host_and_port($portal);
+    return 0 if !$server;
+    return PVE::Network::tcp_ping($server, $port || 3260, 2);
 }
 
 sub iscsi_discovery {
@@ -69,17 +73,14 @@ sub iscsi_discovery {
 
     check_iscsi_support ();
 
-    my $cmd = [$ISCSIADM, '--mode', 'discovery', '--type', 'sendtargets', 
-              '--portal', $portal];
-
     my $res = {};
-
     return $res if !iscsi_test_portal($portal); # fixme: raise exception here?
 
+    my $cmd = [$ISCSIADM, '--mode', 'discovery', '--type', 'sendtargets', '--portal', $portal];
     run_command($cmd, outfunc => sub {
        my $line = shift;
 
-       if ($line =~ m/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+)\,\S+\s+(\S+)\s*$/) {
+       if ($line =~ m/^((?:$IPV4RE|\[$IPV6RE\]):\d+)\,\S+\s+(\S+)\s*$/) {
            my $portal = $1;
            my $target = $2;
            # one target can have more than one portal (multipath).
@@ -93,22 +94,20 @@ sub iscsi_discovery {
 sub iscsi_login {
     my ($target, $portal_in) = @_;
 
-    check_iscsi_support ();
+    check_iscsi_support();
 
-    eval { iscsi_discovery ($portal_in); };
+    eval { iscsi_discovery($portal_in); };
     warn $@ if $@;
 
-    my $cmd = [$ISCSIADM, '--mode', 'node', '--targetname',  $target, '--login'];
-    run_command($cmd);
+    run_command([$ISCSIADM, '--mode', 'node', '--targetname',  $target, '--login']);
 }
 
 sub iscsi_logout {
     my ($target, $portal) = @_;
 
-    check_iscsi_support ();
+    check_iscsi_support();
 
-    my $cmd = [$ISCSIADM, '--mode', 'node', '--targetname', $target, '--logout'];
-    run_command($cmd);
+    run_command([$ISCSIADM, '--mode', 'node', '--targetname', $target, '--logout']);
 }
 
 my $rescan_filename = "/var/run/pve-iscsi-rescan.lock";
@@ -134,7 +133,7 @@ sub iscsi_session_rescan {
     }
 
     foreach my $session (@$session_list) {
-       my $cmd = [$ISCSIADM, '--mode', 'session', '-r', $session, '-R'];
+       my $cmd = [$ISCSIADM, '--mode', 'session', '--sid', $session, '--rescan'];
        eval { run_command($cmd, outfunc => sub {}); };
        warn $@ if $@;
     }
@@ -147,11 +146,11 @@ sub load_stable_scsi_paths {
     my $stabledir = "/dev/disk/by-id";
 
     if (my $dh = IO::Dir->new($stabledir)) {
-       while (defined(my $tmp = $dh->read)) {
+       foreach my $tmp (sort $dh->read) {
            # exclude filenames with part in name (same disk but partitions)
-           # use only filenames with scsi(with multipath i have the same device 
+           # use only filenames with scsi(with multipath i have the same device
           # with dm-uuid-mpath , dm-name and scsi in name)
-           if($tmp !~ m/-part\d+$/ && $tmp =~ m/^scsi-/) {
+           if($tmp !~ m/-part\d+$/ && ($tmp =~ m/^scsi-/ || $tmp =~ m/^dm-uuid-mpath-/)) {
                  my $path = "$stabledir/$tmp";
                  my $bdevdest = readlink($path);
                 if ($bdevdest && $bdevdest =~ m|^../../([^/]+)|) {
@@ -195,8 +194,8 @@ sub iscsi_device_list {
            }
            return if !$bdev;
 
-           #check multipath           
-           if (-d "/sys/block/$bdev/holders") { 
+           #check multipath
+           if (-d "/sys/block/$bdev/holders") {
                my $multipathdev = dir_glob_regex("/sys/block/$bdev/holders", '[A-Za-z]\S*');
                $bdev = $multipathdev if $multipathdev;
            }
@@ -210,15 +209,15 @@ sub iscsi_device_list {
            my $volid = "$channel.$id.$lun.$blockdev";
 
            $res->{$target}->{$volid} = {
-               'format' => 'raw', 
-               'size' => int($size * 512), 
+               'format' => 'raw',
+               'size' => int($size * 512),
                'vmid' => 0, # not assigned to any vm
                'channel' => int($channel),
                'id' => int($id),
                'lun' => int($lun),
            };
 
-           #print "TEST: $target $session $host,$bus,$tg,$lun $blockdev\n"; 
+           #print "TEST: $target $session $host,$bus,$tg,$lun $blockdev\n";
        });
 
     });
@@ -235,6 +234,7 @@ sub type {
 sub plugindata {
     return {
        content => [ {images => 1, none => 1}, { images => 1 }],
+       select_existing => 1,
     };
 }
 
@@ -258,6 +258,7 @@ sub options {
         nodes => { optional => 1},
        disable => { optional => 1},
        content => { optional => 1},
+       bwlimit => { optional => 1 },
     };
 }
 
@@ -267,22 +268,36 @@ sub parse_volname {
     my ($class, $volname) = @_;
 
     if ($volname =~ m!^\d+\.\d+\.\d+\.(\S+)$!) {
-       return ('images', $1, undef);
+       return ('images', $1, undef, undef, undef, undef, 'raw');
     }
 
     die "unable to parse iscsi volume name '$volname'\n";
 }
 
-sub path {
-    my ($class, $scfg, $volname) = @_;
+sub filesystem_path {
+    my ($class, $scfg, $volname, $snapname) = @_;
+
+    die "snapshot is not possible on iscsi storage\n" if defined($snapname);
 
     my ($vtype, $name, $vmid) = $class->parse_volname($volname);
-    
+
     my $path = "/dev/disk/by-id/$name";
 
     return wantarray ? ($path, $vmid, $vtype) : $path;
 }
 
+sub create_base {
+    my ($class, $storeid, $scfg, $volname) = @_;
+
+    die "can't create base images in iscsi storage\n";
+}
+
+sub clone_image {
+    my ($class, $scfg, $storeid, $volname, $vmid, $snap) = @_;
+
+    die "can't clone images in iscsi storage\n";
+}
+
 sub alloc_image {
     my ($class, $storeid, $scfg, $vmid, $fmt, $name, $size) = @_;
 
@@ -290,11 +305,25 @@ sub alloc_image {
 }
 
 sub free_image {
-    my ($class, $storeid, $scfg, $volname) = @_;
+    my ($class, $storeid, $scfg, $volname, $isBase) = @_;
 
     die "can't free space in iscsi storage\n";
 }
 
+# list all luns regardless of set content_types, since we need it for
+# listing in the gui and we can only have images anyway
+sub list_volumes {
+    my ($class, $storeid, $scfg, $vmid, $content_types) = @_;
+
+    my $res = $class->list_images($storeid, $scfg, $vmid);
+
+    for my $item (@$res) {
+       $item->{content} = 'images'; # we only have images
+    }
+
+    return $res;
+}
+
 sub list_images {
     my ($class, $storeid, $scfg, $vmid, $vollist, $cache) = @_;
 
@@ -330,12 +359,17 @@ sub list_images {
     return $res;
 }
 
+sub iscsi_session {
+    my ($cache, $target) = @_;
+    $cache->{iscsi_sessions} = iscsi_session_list() if !$cache->{iscsi_sessions};
+    return $cache->{iscsi_sessions}->{$target};
+}
+
 sub status {
     my ($class, $storeid, $scfg, $cache) = @_;
 
-    $cache->{iscsi_sessions} = iscsi_session_list() if !$cache->{iscsi_sessions};
-
-    my $active = defined($cache->{iscsi_sessions}->{$scfg->{target}});
+    my $session = iscsi_session($cache, $scfg->{target});
+    my $active = defined($session) ? 1 : 0;
 
     return (0, 0, 0, $active);
 }
@@ -345,15 +379,14 @@ sub activate_storage {
 
     return if !check_iscsi_support(1);
 
-    $cache->{iscsi_sessions} = iscsi_session_list() if !$cache->{iscsi_sessions};
+    my $session = iscsi_session($cache, $scfg->{target});
 
-    my $iscsi_sess = $cache->{iscsi_sessions}->{$scfg->{target}};
-    if (!defined ($iscsi_sess)) {
+    if (!defined ($session)) {
        eval { iscsi_login($scfg->{target}, $scfg->{portal}); };
        warn $@ if $@;
     } else {
        # make sure we get all devices
-       iscsi_session_rescan($iscsi_sess);
+       iscsi_session_rescan($session);
     }
 }
 
@@ -362,13 +395,43 @@ sub deactivate_storage {
 
     return if !check_iscsi_support(1);
 
-    $cache->{iscsi_sessions} = iscsi_session_list() if !$cache->{iscsi_sessions};
+    if (defined(iscsi_session($cache, $scfg->{target}))) {
+       iscsi_logout($scfg->{target}, $scfg->{portal});
+    }
+}
 
-    my $iscsi_sess = $cache->{iscsi_sessions}->{$scfg->{target}};
+sub check_connection {
+    my ($class, $storeid, $scfg) = @_;
 
-    if (defined ($iscsi_sess)) {
-       iscsi_logout($scfg->{target}, $scfg->{portal});
+    my $portal = $scfg->{portal};
+    return iscsi_test_portal($portal);
+}
+
+sub volume_resize {
+    my ($class, $scfg, $storeid, $volname, $size, $running) = @_;
+    die "volume resize is not possible on iscsi device";
+}
+
+sub volume_has_feature {
+    my ($class, $scfg, $feature, $storeid, $volname, $snapname, $running) = @_;
+
+    my $features = {
+       copy => { current => 1},
+    };
+
+    my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) =
+       $class->parse_volname($volname);
+
+    my $key = undef;
+    if($snapname){
+       $key = 'snap';
+    }else{
+       $key =  $isBase ? 'base' : 'current';
     }
+    return 1 if $features->{$feature}->{$key};
+
+    return undef;
 }
 
+
 1;