]> git.proxmox.com Git - pve-storage.git/blobdiff - PVE/Storage.pm
avoid call to lvs when deactivating devices
[pve-storage.git] / PVE / Storage.pm
index cb34139ac421ac4f901f7c559d198c41a3d46bcf..cd74a3ade79cd039fc93b0b9e17820f0d4a95976 100755 (executable)
@@ -13,14 +13,15 @@ use IPC::Open2;
 use Cwd 'abs_path';
 use Getopt::Long qw(GetOptionsFromArray);
 use Socket;
-use Digest::SHA1;
+use Digest::SHA;
 use Net::Ping;
 
-use PVE::Tools qw(run_command file_read_firstline trim);
+use PVE::Tools qw(run_command file_read_firstline trim dir_glob_regex dir_glob_foreach);
 use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_write_file cfs_lock_file);
 use PVE::Exception qw(raise_param_exc);
 use PVE::JSONSchema;
 use PVE::INotify;
+use PVE::RPCEnvironment;
 
 my $ISCSIADM = '/usr/bin/iscsiadm';
 my $UDEVADM = '/sbin/udevadm';
@@ -78,36 +79,6 @@ sub load_stable_scsi_paths {
     return $stable_paths;
 }
 
-sub dir_glob_regex {
-    my ($dir, $regex) = @_;
-
-    my $dh = IO::Dir->new ($dir);
-    return wantarray ? () : undef if !$dh;
-  
-    while (defined(my $tmp = $dh->read)) { 
-       if (my @res = $tmp =~ m/^($regex)$/) {
-           $dh->close;
-           return wantarray ? @res : $tmp;
-       }
-    }
-    $dh->close;
-
-    return wantarray ? () : undef;
-}
-
-sub dir_glob_foreach {
-    my ($dir, $regex, $func) = @_;
-
-    my $dh = IO::Dir->new ($dir);
-    if (defined $dh) {
-       while (defined(my $tmp = $dh->read)) {
-           if (my @res = $tmp =~ m/^($regex)$/) {
-               &$func (@res);
-           }
-       }
-    } 
-}
-
 sub read_proc_mounts {
     
     local $/; # enable slurp mode
@@ -137,6 +108,7 @@ my $confvars = {
     path => 'path',
     shared => 'bool',
     disable => 'bool',
+    saferemove => 'bool',
     format => 'format',
     content => 'content',
     server => 'server',
@@ -147,6 +119,7 @@ my $confvars = {
     target => 'target',
     nodes => 'nodes',
     options => 'options',
+    maxfiles => 'natural',
 };
 
 my $required_config = {
@@ -169,6 +142,7 @@ my $default_config = {
         nodes => 0,
        shared => 0,
        disable => 0,
+        maxfiles => 0, 
        content => [ { images => 1, rootdir => 1, vztmpl => 1, iso => 1, backup => 1, none => 1 },
                     { images => 1,  rootdir => 1 }],
        format => [ { raw => 1, qcow2 => 1, vmdk => 1 } , 'raw' ],
@@ -181,6 +155,7 @@ my $default_config = {
         server => 1,
         export => 1,
         options => 0,
+        maxfiles => 0, 
        content => [ { images => 1, rootdir => 1, vztmpl => 1, iso => 1, backup => 1},
                     { images => 1 }],
        format => [ { raw => 1, qcow2 => 1, vmdk => 1 } , 'raw' ],
@@ -191,6 +166,7 @@ my $default_config = {
         nodes => 0,
        shared => 0,
        disable => 0,
+        saferemove => 0,
        content => [ {images => 1}, { images => 1 }],
         base => 1,
     },
@@ -354,6 +330,10 @@ sub check_type {
        return parse_lvm_name ($value, $noerr);
     } elsif ($ct eq 'portal') {
        return verify_portal($value, $noerr);
+    } elsif ($ct eq 'natural') {
+       return int($value) if $value =~ m/^\d+$/; 
+       return undef if $noerr;
+       die "type check ('natural') failed - got '$value'\n";
     } elsif ($ct eq 'nodes') {
        my $res = {};
 
@@ -416,7 +396,7 @@ sub parse_config {
 
     my $ids = {};
 
-    my $digest = Digest::SHA1::sha1_hex(defined($raw) ? $raw : '');
+    my $digest = Digest::SHA::sha1_hex(defined($raw) ? $raw : '');
 
     my $pri = 0;
 
@@ -431,7 +411,7 @@ sub parse_config {
            my $type = $1;
            my $ignore = 0;
 
-           if (!parse_storage_id ($storeid, 1)) {
+           if (!PVE::JSONSchema::parse_storage_id($storeid, 1)) {
                $ignore = 1;
                warn "ignoring storage '$storeid' - (illegal characters)\n";
            } elsif (!$default_config->{$type}) {
@@ -478,7 +458,8 @@ sub parse_config {
        $ids->{local} = {
            type => 'dir',
            priority => $pri++,
-           path => '/var/lib/vz',    
+           path => '/var/lib/vz',
+           maxfiles => 0,
            content => { images => 1, rootdir => 1, vztmpl => 1, iso => 1},
        };
     }
@@ -607,7 +588,7 @@ sub storage_check_node {
     if ($scfg->{nodes}) {
        $node = PVE::INotify::nodename() if !$node || ($node eq 'localhost');
        if (!$scfg->{nodes}->{$node}) {
-           die "storage '$storeid' is not available on node '$node'" if !$noerr;
+           die "storage '$storeid' is not available on node '$node'\n" if !$noerr;
            return undef;
        }
     }
@@ -708,9 +689,9 @@ sub write_config {
 
        foreach my $k (keys %$def) {
            next if defined ($done_hash->{$k});
-           if (defined (my $v = $ids->{$storeid}->{$k})) {
-               $data .= sprint_config_line ($k, $v);
-           }
+           my $v = $ids->{$storeid}->{$k};
+           next if !defined($v);
+           $data .= sprint_config_line ($k, $v);
        }
 
        $out .= "$data\n";
@@ -916,7 +897,7 @@ sub iscsi_device_list {
 
            $res->{$target}->{$volid} = {
                'format' => 'raw', 
-               'size' => int($size 2), 
+               'size' => int($size * 512), 
                'vmid' => 0, # not assigned to any vm
                'channel' => int($channel),
                'id' => int($id),
@@ -933,23 +914,6 @@ sub iscsi_device_list {
 
 # library implementation
 
-
-PVE::JSONSchema::register_format('pve-storage-id', \&parse_storage_id);
-sub parse_storage_id {
-    my ($storeid, $noerr) = @_;
-
-    if ($storeid !~ m/^[a-z][a-z0-9\-\_\.]*[a-z0-9]$/i) {
-       return undef if $noerr;
-       die "storage ID '$storeid' contains illegal characters\n";
-    }
-    return $storeid;
-}
-
-PVE::JSONSchema::register_standard_option('pve-storage-id', {
-    description => "The storage identifier.",
-    type => 'string', format => 'pve-storage-id',
-}); 
-
 PVE::JSONSchema::register_format('pve-storage-vgname', \&parse_lvm_name);
 sub parse_lvm_name {
     my ($name, $noerr) = @_;
@@ -1004,8 +968,12 @@ sub parse_volname_dir {
        return ('vztmpl', $1);
     } elsif ($volname =~ m!^rootdir/(\d+)$!) {
        return ('rootdir', $1, $1);
-    } elsif ($volname =~ m!^backup/([^/]+(\.tar|\.tgz))$!) {
-       return ('backup', $1);
+    } elsif ($volname =~ m!^backup/([^/]+(\.(tar|tar\.gz|tar\.lzo|tgz)))$!) {
+       my $fn = $1;
+       if ($fn =~ m/^vzdump-(openvz|qemu)-(\d+)-.+/) {
+           return ('backup', $fn, $2);
+       }
+       return ('backup', $fn);
    }
     die "unable to parse directory volume name '$volname'\n";
 }
@@ -1050,7 +1018,9 @@ sub path_to_volume_id {
        return ('');
     }
 
-    $path = abs_path ($path);
+    # Note: abs_path() return undef if $path doesn not exist 
+    # for example when nfs storage is not mounted
+    $path = abs_path($path) || $path;
 
     foreach my $sid (keys %$ids) {
        my $type = $ids->{$sid}->{type};
@@ -1075,7 +1045,7 @@ sub path_to_volume_id {
        } elsif ($path =~ m!^$privatedir/(\d+)$!) {
            my $vmid = $1;
            return ('rootdir', "$sid:rootdir/$vmid");
-       } elsif ($path =~ m!^$backupdir/([^/]+\.(tar|tgz))$!) {
+       } elsif ($path =~ m!^$backupdir/([^/]+\.(tar|tar\.gz|tar\.lzo|tgz))$!) {
            my $name = $1;
            return ('iso', "$sid:backup/$name");        
        }
@@ -1094,9 +1064,11 @@ sub path {
 
     my $path;
     my $owner;
+    my $vtype = 'image';
 
     if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs') {
-       my ($vtype, $name, $vmid) = parse_volname_dir ($volname);
+       my ($name, $vmid);
+       ($vtype, $name, $vmid) = parse_volname_dir ($volname);
        $owner = $vmid;
 
        my $imagedir = get_image_dir($cfg, $storeid, $vmid);
@@ -1135,7 +1107,7 @@ sub path {
        die "unknown storage type '$scfg->{type}'";
     }
 
-    return wantarray ? ($path, $owner) : $path;
+    return wantarray ? ($path, $owner, $vtype) : $path;
 }
 
 sub storage_migrate {
@@ -1227,9 +1199,9 @@ sub vdisk_alloc {
 
     die "no storage id specified\n" if !$storeid;
 
-    parse_storage_id ($storeid);
+    PVE::JSONSchema::parse_storage_id($storeid);
 
-    my $scfg = storage_config ($cfg, $storeid);
+    my $scfg = storage_config($cfg, $storeid);
 
     die "no VMID specified\n" if !$vmid;
 
@@ -1271,7 +1243,7 @@ sub vdisk_alloc {
 
            my $path = "$imagedir/$name";
 
-           die "disk image '$path' already exists\n" if -f $path;
+           die "disk image '$path' already exists\n" if -e $path;
 
            run_command("/usr/bin/qemu-img create -f $fmt '$path' ${size}K", 
                        errmsg => "unable to create image");
@@ -1333,6 +1305,11 @@ sub vdisk_free {
 
     activate_storage ($cfg, $storeid);
 
+    # we need to zero out LVM data for security reasons
+    # and to allow thin provisioning
+
+    my $vg;
+
     # lock shared storage
     cluster_lock_storage($storeid, $scfg->{shared}, undef, sub {
 
@@ -1346,17 +1323,43 @@ sub vdisk_free {
            }
        } elsif ($scfg->{type} eq 'lvm') {
 
-           my $vg = $scfg->{vgname};
-
-           my $cmd = ['/sbin/lvremove', '-f', "$vg/$volname"];
+           if ($scfg->{saferemove}) {
+               # avoid long running task, so we only rename here
+               $vg = $scfg->{vgname};
+               my $cmd = ['/sbin/lvrename', $vg, $volname, "del-$volname"];
+               run_command($cmd, errmsg => "lvrename '$vg/$volname' error");
+           } else {
+               my $tmpvg = $scfg->{vgname};
+               my $cmd = ['/sbin/lvremove', '-f', "$tmpvg/$volname"];
+               run_command($cmd, errmsg => "lvremove '$tmpvg/$volname' error");
+           }
 
-           run_command($cmd, errmsg => "lvremove '$vg/$volname' error");
        } elsif ($scfg->{type} eq 'iscsi') {
            die "can't free space in iscsi storage\n";
        } else {
            die "unknown storage type '$scfg->{type}'";
        }
     });
+
+    return if !$vg;
+
+    my $zero_out_worker = sub {
+       print "zero-out data on image $volname\n";
+       my $cmd = ['dd', "if=/dev/zero", "of=/dev/$vg/del-$volname", "bs=1M"];
+       eval { run_command($cmd, errmsg => "zero out failed"); };
+       warn $@ if $@;
+
+       cluster_lock_storage($storeid, $scfg->{shared}, undef, sub {
+           my $cmd = ['/sbin/lvremove', '-f', "$vg/del-$volname"];
+           run_command($cmd, errmsg => "lvremove '$vg/del-$volname' error");
+       });
+       print "successfully removed volume $volname\n";
+    };
+
+    my $rpcenv = PVE::RPCEnvironment::get();
+    my $authuser = $rpcenv->get_user();
+
+    $rpcenv->fork_worker('imgdel', undef, $authuser, $zero_out_worker);
 }
 
 # lvm utility functions
@@ -1561,7 +1564,7 @@ sub template_list {
                    $info = { volid => "$sid:vztmpl/$1", format => 'tgz' };
 
                } elsif ($tt eq 'backup') {
-                   next if $fn !~ m!/([^/]+\.(tar|tgz))$!;
+                   next if $fn !~ m!/([^/]+\.(tar|tar\.gz|tar\.lzo|tgz))$!;
                    
                    $info = { volid => "$sid:backup/$1", format => $2 };
                }
@@ -1761,7 +1764,7 @@ sub nfs_is_mounted {
 
     $mountdata = read_proc_mounts() if !$mountdata;
 
-    if ($mountdata =~ m/^$source\s$mountpoint\snfs/m) {
+    if ($mountdata =~ m|^$source/?\s$mountpoint\snfs|m) {
        return $mountpoint;
     } 
 
@@ -1966,7 +1969,7 @@ sub activate_volumes {
 
        # check is volume exists
        if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs') {
-           die "volume '$volid' does not exist\n" if ! -f $path;
+           die "volume '$volid' does not exist\n" if ! -e $path;
        } else {
            die "volume '$volid' does not exist\n" if ! -b $path;
        }
@@ -1978,8 +1981,6 @@ sub deactivate_volumes {
 
     return if !($vollist && scalar(@$vollist));
 
-    my $lvs = lvm_lvs ();
-
     my @errlist = ();
     foreach my $volid (@$vollist) {
        my ($storeid, $volname) = parse_volume_id ($volid);
@@ -1987,16 +1988,14 @@ sub deactivate_volumes {
        my $scfg = storage_config ($cfg, $storeid);
 
        if ($scfg->{type} eq 'lvm') {
-           my ($name) = parse_volname_lvm ($volname);
-
-           if ($lvs->{$scfg->{vgname}}->{$name}) {
-               my $path = path ($cfg, $volid);
-               my $cmd = ['/sbin/lvchange', '-aln', $path];
-               eval { run_command($cmd, errmsg => "can't deactivate LV '$volid'"); };
-               if (my $err = $@) {
-                   warn $err;
-                   push @errlist, $volid;
-               }
+           my $path = path ($cfg, $volid);
+           next if ! -b $path;
+
+           my $cmd = ['/sbin/lvchange', '-aln', $path];
+           eval { run_command($cmd, errmsg => "can't deactivate LV '$volid'"); };
+           if (my $err = $@) {
+               warn $err;
+               push @errlist, $volid;
            }
        }
     }