]> git.proxmox.com Git - pve-storage.git/blobdiff - PVE/Storage.pm
iSCSI ipv6 support
[pve-storage.git] / PVE / Storage.pm
index 7858f4172c8cce98804b70abee994a9cedcb1438..927219ab878fd99287973c5cccece722eb80eb40 100755 (executable)
@@ -2,6 +2,7 @@ package PVE::Storage;
 
 use strict;
 use warnings;
+use Data::Dumper;
 
 use POSIX;
 use IO::Select;
@@ -11,7 +12,7 @@ use File::Path;
 use Cwd 'abs_path';
 use Socket;
 
-use PVE::Tools qw(run_command file_read_firstline);
+use PVE::Tools qw(run_command file_read_firstline $IPV6RE);
 use PVE::Cluster qw(cfs_read_file cfs_lock_file);
 use PVE::Exception qw(raise_param_exc);
 use PVE::JSONSchema;
@@ -26,8 +27,10 @@ use PVE::Storage::ISCSIPlugin;
 use PVE::Storage::RBDPlugin;
 use PVE::Storage::SheepdogPlugin;
 use PVE::Storage::ISCSIDirectPlugin;
-use PVE::Storage::NexentaPlugin;
 use PVE::Storage::GlusterfsPlugin;
+use PVE::Storage::ZFSPoolPlugin;
+use PVE::Storage::ZFSPlugin;
+use PVE::Storage::DRBDPlugin;
 
 # load and initialize all plugins
 PVE::Storage::DirPlugin->register();
@@ -37,8 +40,10 @@ PVE::Storage::ISCSIPlugin->register();
 PVE::Storage::RBDPlugin->register();
 PVE::Storage::SheepdogPlugin->register();
 PVE::Storage::ISCSIDirectPlugin->register();
-PVE::Storage::NexentaPlugin->register();
 PVE::Storage::GlusterfsPlugin->register();
+PVE::Storage::ZFSPoolPlugin->register();
+PVE::Storage::ZFSPlugin->register();
+PVE::Storage::DRBDPlugin->register();
 PVE::Storage::Plugin->init();
 
 my $UDEVADM = '/sbin/udevadm';
@@ -136,24 +141,39 @@ sub volume_resize {
         my $plugin = PVE::Storage::Plugin->lookup($scfg->{type});
         return $plugin->volume_resize($scfg, $storeid, $volname, $size, $running);
     } elsif ($volid =~ m|^(/.+)$| && -e $volid) {
-        die "resize device is not possible";
+        die "resize file/device '$volid' is not possible\n";
     } else {
-        die "can't resize";
+       die "unable to parse volume ID '$volid'\n";
+    }
+}
+
+sub volume_rollback_is_possible {
+    my ($cfg, $volid, $snap) = @_;
+
+    my ($storeid, $volname) = parse_volume_id($volid, 1);
+    if ($storeid) {
+        my $scfg = storage_config($cfg, $storeid);
+        my $plugin = PVE::Storage::Plugin->lookup($scfg->{type});
+        return $plugin->volume_rollback_is_possible($scfg, $storeid, $volname, $snap);
+    } elsif ($volid =~ m|^(/.+)$| && -e $volid) {
+        die "snapshot rollback file/device '$volid' is not possible\n";
+    } else {
+       die "unable to parse volume ID '$volid'\n";
     }
 }
 
 sub volume_snapshot {
-    my ($cfg, $volid, $snap, $running) = @_;
+    my ($cfg, $volid, $snap) = @_;
 
     my ($storeid, $volname) = parse_volume_id($volid, 1);
     if ($storeid) {
         my $scfg = storage_config($cfg, $storeid);
         my $plugin = PVE::Storage::Plugin->lookup($scfg->{type});
-        return $plugin->volume_snapshot($scfg, $storeid, $volname, $snap, $running);
+        return $plugin->volume_snapshot($scfg, $storeid, $volname, $snap);
     } elsif ($volid =~ m|^(/.+)$| && -e $volid) {
-        die "snapshot device is not possible";
+        die "snapshot file/device '$volid' is not possible\n";
     } else {
-        die "can't snapshot";
+       die "unable to parse volume ID '$volid'\n";
     }
 }
 
@@ -164,11 +184,12 @@ sub volume_snapshot_rollback {
     if ($storeid) {
         my $scfg = storage_config($cfg, $storeid);
         my $plugin = PVE::Storage::Plugin->lookup($scfg->{type});
+       $plugin->volume_rollback_is_possible($scfg, $storeid, $volname, $snap);
         return $plugin->volume_snapshot_rollback($scfg, $storeid, $volname, $snap);
     } elsif ($volid =~ m|^(/.+)$| && -e $volid) {
-        die "snapshot rollback device is not possible";
+        die "snapshot rollback file/device '$volid' is not possible\n";
     } else {
-        die "can't snapshot";
+       die "unable to parse volume ID '$volid'\n";
     }
 }
 
@@ -181,9 +202,9 @@ sub volume_snapshot_delete {
         my $plugin = PVE::Storage::Plugin->lookup($scfg->{type});
         return $plugin->volume_snapshot_delete($scfg, $storeid, $volname, $snap, $running);
     } elsif ($volid =~ m|^(/.+)$| && -e $volid) {
-        die "snapshot delete device is not possible";
+        die "snapshot delete file/device '$volid' is not possible\n";
     } else {
-        die "can't delete snapshot";
+       die "unable to parse volume ID '$volid'\n";
     }
 }
 
@@ -269,6 +290,9 @@ sub parse_volname {
     my $scfg = storage_config($cfg, $storeid);
 
     my $plugin = PVE::Storage::Plugin->lookup($scfg->{type});
+
+    # returns ($vtype, $name, $vmid, $basename, $basevmid, $isBase, $format)
+
     return $plugin->parse_volname($volname);
 }
 
@@ -472,13 +496,44 @@ sub storage_migrate {
        } else {
            die "$errstr - target type '$tcfg->{type}' not implemented\n";
        }
+
+    } elsif ($scfg->{type} eq 'zfspool') {
+
+       if ($tcfg->{type} eq 'zfspool') {
+
+           die "$errstr - pool on target has not same name as source!"
+               if $tcfg->{pool} ne $scfg->{pool};
+
+           my (undef, $volname) = parse_volname($cfg, $volid);
+
+           my $zfspath = "$scfg->{pool}\/$volname";
+
+           my $snap = "zfs snapshot $zfspath\@__migration__";
+
+           my $send = "zfs send -v $zfspath\@__migration__ \| ssh root\@$target_host zfs recv $zfspath";
+
+           my $destroy_target = "ssh root\@$target_host zfs destroy $zfspath\@__migration__";
+           run_command($snap);
+           eval{
+               run_command($send);
+           };
+           my $err;
+           if ($err = $@){
+               run_command("zfs destroy $zfspath\@__migration__");
+               die $err;
+           }
+           run_command($destroy_target);
+
+       } else {
+           die "$errstr - target type $tcfg->{type} is not valid\n";
+       }
     } else {
        die "$errstr - source type '$scfg->{type}' not implemented\n";
     }
 }
 
 sub vdisk_clone {
-    my ($cfg, $volid, $vmid) = @_;
+    my ($cfg, $volid, $vmid, $snap) = @_;
 
     my ($storeid, $volname) = parse_volume_id($volid);
 
@@ -490,7 +545,7 @@ sub vdisk_clone {
 
     # lock shared storage
     return $plugin->cluster_lock_storage($storeid, $scfg->{shared}, undef, sub {
-       my $volname = $plugin->clone_image($scfg, $storeid, $volname, $vmid);
+       my $volname = $plugin->clone_image($scfg, $storeid, $volname, $vmid, $snap);
        return "$storeid:$volname";
     });
 }
@@ -536,7 +591,11 @@ sub vdisk_alloc {
 
     # lock shared storage
     return $plugin->cluster_lock_storage($storeid, $scfg->{shared}, undef, sub {
-       my $volname = $plugin->alloc_image($storeid, $scfg, $vmid, $fmt, $name, $size);
+       my $old_umask = umask(umask|0037);
+       my $volname = eval { $plugin->alloc_image($storeid, $scfg, $vmid, $fmt, $name, $size) };
+       my $err = $@;
+       umask $old_umask;
+       die $err if $err;
        return "$storeid:$volname";
     });
 }
@@ -557,7 +616,7 @@ sub vdisk_free {
     # lock shared storage
     $plugin->cluster_lock_storage($storeid, $scfg->{shared}, undef, sub {
 
-       my ($vtype, $name, $vmid, undef, undef, $isBase) =
+       my ($vtype, $name, $vmid, undef, undef, $isBase, $format) =
            $plugin->parse_volname($volname);
        if ($isBase) {
            my $vollist = $plugin->list_images($storeid, $scfg);
@@ -577,7 +636,7 @@ sub vdisk_free {
                }
            }
        }
-       my $cleanup_worker = $plugin->free_image($storeid, $scfg, $volname, $isBase);
+       $cleanup_worker = $plugin->free_image($storeid, $scfg, $volname, $isBase, $format);
     });
 
     return if !$cleanup_worker;
@@ -632,9 +691,9 @@ sub template_list {
                    $info = { volid => "$sid:iso/$1", format => 'iso' };
 
                } elsif ($tt eq 'vztmpl') {
-                   next if $fn !~ m!/([^/]+\.tar\.gz)$!;
+                   next if $fn !~ m!/([^/]+\.tar\.([gx]z))$!;
 
-                   $info = { volid => "$sid:vztmpl/$1", format => 'tgz' };
+                   $info = { volid => "$sid:vztmpl/$1", format => "t$2" };
 
                } elsif ($tt eq 'backup') {
                    next if $fn !~ m!/([^/]+\.(tar|tar\.gz|tar\.lzo|tgz|vma|vma\.gz|vma\.lzo))$!;
@@ -827,14 +886,25 @@ sub storage_info {
     my $ids = $cfg->{ids};
 
     my $info = {};
-
+    
+    my @ctypes = PVE::Tools::split_list($content);
+    
     my $slist = [];
     foreach my $storeid (keys %$ids) {
 
-       next if $content && !$ids->{$storeid}->{content}->{$content};
-
        next if !storage_check_enabled($cfg, $storeid, undef, 1);
 
+       if (defined($content)) {
+           my $want_ctype = 0;
+           foreach my $ctype (@ctypes) {
+               if ($ids->{$storeid}->{content}->{$ctype}) {
+                   $want_ctype = 1;
+                   last;
+               }
+           }
+           next if !$want_ctype;
+       }
+       
        my $type = $ids->{$storeid}->{type};
 
        $info->{$storeid} = {
@@ -879,9 +949,14 @@ sub storage_info {
 sub resolv_server {
     my ($server) = @_;
 
-    my $packed_ip = gethostbyname($server);
+    my ($packed_ip, $family);
+    eval {
+       my @res = PVE::Tools::getaddrinfo_all($server);
+       $family = $res[0]->{family};
+       $packed_ip = (PVE::Tools::unpack_sockaddr_in46($res[0]->{addr}))[2];
+    };
     if (defined $packed_ip) {
-       return inet_ntoa($packed_ip);
+       return Socket::inet_ntop($family, $packed_ip);
     }
     return undef;
 }
@@ -909,15 +984,35 @@ sub scan_nfs {
     return $res;
 }
 
+sub scan_zfs {
+
+    my $cmd = ['zfs',  'list', '-t', 'filesystem', '-H', '-o', 'name,avail,used'];
+
+    my $res = [];
+    run_command($cmd, outfunc => sub {
+       my $line = shift;
+
+       if ($line =~m/^(\S+)\s+(\S+)\s+(\S+)$/) {
+           my ($pool, $size_str, $used_str) = ($1, $2, $3);
+           my $size = PVE::Storage::ZFSPoolPlugin::zfs_parse_size($size_str);
+           my $used = PVE::Storage::ZFSPoolPlugin::zfs_parse_size($used_str);
+           # ignore subvolumes generated by our ZFSPoolPlugin
+           return if $pool =~ m!/subvol-\d+-[^/]+$!; 
+           push @$res, { pool => $pool, size => $size, free => $size-$used };
+       }
+    });
+
+    return $res;
+}
+
 sub resolv_portal {
     my ($portal, $noerr) = @_;
 
-    if ($portal =~ m/^([^:]+)(:(\d+))?$/) {
-       my $server = $1;
-       my $port = $3;
-
+    my ($server, $port) = PVE::Tools::parse_host_and_port($portal);
+    if ($server) {
        if (my $ip = resolv_server($server)) {
            $server = $ip;
+           $server = "[$server]" if $server =~ /^$IPV6RE$/;
            return $port ? "$server:$port" : $server;
        }
     }