]> git.proxmox.com Git - pve-storage.git/blobdiff - PVE/Storage.pm
delete volume requires Datastore.Allocate
[pve-storage.git] / PVE / Storage.pm
index 5fe6c95f1753a15bdc5739a5e21fa90a5ac5f7bf..9d1c895c440e516444653cd2aa3083b549826ea2 100755 (executable)
@@ -181,7 +181,7 @@ my $default_config = {
         server => 1,
         export => 1,
         options => 0,
-       content => [ { images => 1, iso => 1, backup => 1},
+       content => [ { images => 1, rootdir => 1, vztmpl => 1, iso => 1, backup => 1},
                     { images => 1 }],
        format => [ { raw => 1, qcow2 => 1, vmdk => 1 } , 'raw' ],
     },
@@ -397,18 +397,9 @@ sub check_type {
            $res->{$c} = 1;
        } 
 
-       # only local storage may have several content types
-       if ($res->{none} || !($storeid && $storeid eq 'local')) {
-           if (scalar (keys %$res) > 1) {
+       if ($res->{none} && scalar (keys %$res) > 1) {
                return undef if $noerr;
-               die "storage does not support multiple content types\n";
-           }
-       }
-
-       # no backup to local storage
-       if ($storeid && $storeid eq 'local' && $res->{backup}) {
-               return undef if $noerr;
-               die "storage 'local' does not support backups\n";
+               die "unable to combine 'none' with other content types\n";
        }
 
        return $res;    
@@ -425,15 +416,13 @@ sub parse_config {
 
     my $ids = {};
 
-    my $sha1 = Digest::SHA1->new;
+    my $digest = Digest::SHA1::sha1_hex(defined($raw) ? $raw : '');
 
     my $pri = 0;
 
     while ($raw && $raw =~ s/^(.*?)(\n|$)//) {
        my $line = $1;
 
-       $sha1->add ($line); # compute digest
-
        next if $line =~ m/^\#/;
        next if $line =~ m/^\s*$/;
 
@@ -442,7 +431,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}) {
@@ -526,8 +515,6 @@ sub parse_config {
        }
     }
 
-    my $digest = $sha1->hexdigest;
-
     my $cfg = { ids => $ids, digest => $digest};
 
     return $cfg;
@@ -739,11 +726,18 @@ sub get_image_dir {
     return $vmid ? "$path/images/$vmid" : "$path/images";
 }
 
+sub get_private_dir {
+    my ($cfg, $storeid, $vmid) = @_;
+
+    my $path = $cfg->{ids}->{$storeid}->{path};
+    return $vmid ? "$path/private/$vmid" : "$path/private";
+}
+
 sub get_iso_dir {
     my ($cfg, $storeid) = @_;
 
     my $isodir =  $cfg->{ids}->{$storeid}->{path};
-    $isodir .= '/template/iso' if $storeid eq 'local';
+    $isodir .= '/template/iso';
 
     return $isodir;
 }
@@ -752,11 +746,20 @@ sub get_vztmpl_dir {
     my ($cfg, $storeid) = @_;
 
     my $tmpldir =  $cfg->{ids}->{$storeid}->{path};
-    $tmpldir .= '/template/cache' if $storeid eq 'local';
+    $tmpldir .= '/template/cache';
 
     return $tmpldir;
 }
 
+sub get_backup_dir {
+    my ($cfg, $storeid) = @_;
+
+    my $dir =  $cfg->{ids}->{$storeid}->{path};
+    $dir .= '/dump';
+
+    return $dir;
+}
+
 # iscsi utility functions
 
 sub iscsi_session_list {
@@ -767,7 +770,7 @@ sub iscsi_session_list {
 
     my $res = {};
 
-    run_command ($cmd, outfunc => sub {
+    run_command($cmd, outfunc => sub {
        my $line = shift;
 
        if ($line =~ m/^tcp:\s+\[(\S+)\]\s+\S+\s+(\S+)\s*$/) {
@@ -802,7 +805,7 @@ sub iscsi_discovery {
 
     return $res if !iscsi_test_portal($portal); # fixme: raise exception here?
 
-    run_command ($cmd, outfunc => sub {
+    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*$/) {
@@ -825,7 +828,7 @@ sub iscsi_login {
     warn $@ if $@;
 
     my $cmd = [$ISCSIADM, '--mode', 'node', '--targetname',  $target, '--login'];
-    run_command ($cmd);
+    run_command($cmd);
 }
 
 sub iscsi_logout {
@@ -834,7 +837,7 @@ sub iscsi_logout {
     check_iscsi_support ();
 
     my $cmd = [$ISCSIADM, '--mode', 'node', '--targetname', $target, '--logout'];
-    run_command ($cmd);
+    run_command($cmd);
 }
 
 my $rescan_filename = "/var/run/pve-iscsi-rescan.lock";
@@ -861,7 +864,7 @@ sub iscsi_session_rescan {
 
     foreach my $session (@$session_list) {
        my $cmd = [$ISCSIADM, '--mode', 'session', '-r', $session, '-R'];
-       eval { run_command ($cmd, outfunc => sub {}); };
+       eval { run_command($cmd, outfunc => sub {}); };
        warn $@ if $@;
     }
 }
@@ -913,7 +916,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),
@@ -930,23 +933,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) = @_;
@@ -999,7 +985,15 @@ sub parse_volname_dir {
        return ('iso', $1);
     } elsif ($volname =~ m!^vztmpl/([^/]+\.tar\.gz)$!) {
        return ('vztmpl', $1);
-    }
+    } elsif ($volname =~ m!^rootdir/(\d+)$!) {
+       return ('rootdir', $1, $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";
 }
 
@@ -1049,9 +1043,11 @@ sub path_to_volume_id {
        my $type = $ids->{$sid}->{type};
        next if !($type eq 'dir' || $type eq 'nfs');
        
-       my $imagedir = $ids->{$sid}->{path} . "/images";
-       my $isodir = get_iso_dir ($cfg, $sid);
-       my $tmpldir = get_vztmpl_dir ($cfg, $sid);
+       my $imagedir = get_image_dir($cfg, $sid);
+       my $isodir = get_iso_dir($cfg, $sid);
+       my $tmpldir = get_vztmpl_dir($cfg, $sid);
+       my $backupdir = get_backup_dir($cfg, $sid);
+       my $privatedir = get_private_dir($cfg, $sid);
 
        if ($path =~ m!^$imagedir/(\d+)/([^/\s]+)$!) {
            my $vmid = $1;
@@ -1063,6 +1059,12 @@ sub path_to_volume_id {
        } elsif ($path =~ m!^$tmpldir/([^/]+\.tar\.gz)$!) {
            my $name = $1;
            return ('vztmpl', "$sid:vztmpl/$name");
+       } elsif ($path =~ m!^$privatedir/(\d+)$!) {
+           my $vmid = $1;
+           return ('rootdir', "$sid:rootdir/$vmid");
+       } elsif ($path =~ m!^$backupdir/([^/]+\.(tar|tar\.gz|tar\.lzo|tgz))$!) {
+           my $name = $1;
+           return ('iso', "$sid:backup/$name");        
        }
     }
 
@@ -1079,14 +1081,18 @@ 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);
-       my $isodir = get_iso_dir ($cfg, $storeid);
-       my $tmpldir = get_vztmpl_dir ($cfg, $storeid);
+       my $imagedir = get_image_dir($cfg, $storeid, $vmid);
+       my $isodir = get_iso_dir($cfg, $storeid);
+       my $tmpldir = get_vztmpl_dir($cfg, $storeid);
+       my $backupdir = get_backup_dir($cfg, $storeid);
+       my $privatedir = get_private_dir($cfg, $storeid);
 
        if ($vtype eq 'image') {
            $path = "$imagedir/$name";
@@ -1094,6 +1100,10 @@ sub path {
            $path = "$isodir/$name";
        } elsif ($vtype eq 'vztmpl') {
            $path = "$tmpldir/$name";
+       } elsif ($vtype eq 'rootdir') {
+           $path = "$privatedir/$name";
+       } elsif ($vtype eq 'backup') {
+           $path = "$backupdir/$name";
        } else {
            die "should not be reached";
        }
@@ -1114,7 +1124,7 @@ sub path {
        die "unknown storage type '$scfg->{type}'";
     }
 
-    return wantarray ? ($path, $owner) : $path;
+    return wantarray ? ($path, $owner, $vtype) : $path;
 }
 
 sub storage_migrate {
@@ -1150,23 +1160,23 @@ sub storage_migrate {
 
            if ($tcfg->{shared}) { # we can do a local copy
                
-               run_command (['/bin/mkdir', '-p', $dirname]);
+               run_command(['/bin/mkdir', '-p', $dirname]);
 
-               run_command (['/bin/cp', $src, $dst]);
+               run_command(['/bin/cp', $src, $dst]);
 
            } else {
 
-               run_command (['/usr/bin/ssh', "root\@${target_host}", 
-                             '/bin/mkdir', '-p', $dirname]);
+               run_command(['/usr/bin/ssh', "root\@${target_host}", 
+                            '/bin/mkdir', '-p', $dirname]);
 
                # we use rsync with --sparse, so we can't use --inplace,
                # so we remove file on the target if it already exists to
                # save space
                my ($size, $format) = file_size_info($src);
                if ($format && ($format eq 'raw') && $size) {
-                   run_command (['/usr/bin/ssh', "root\@${target_host}", 
-                                 'rm', '-f', $dst],
-                                outfunc => sub {});
+                   run_command(['/usr/bin/ssh', "root\@${target_host}", 
+                                'rm', '-f', $dst],
+                               outfunc => sub {});
                }
 
                my $cmd = ['/usr/bin/rsync', '--progress', '--sparse', '--whole-file', 
@@ -1174,7 +1184,7 @@ sub storage_migrate {
 
                my $percent = -1;
 
-               run_command ($cmd, outfunc => sub {
+               run_command($cmd, outfunc => sub {
                    my $line = shift;
 
                    if ($line =~ m/^\s*(\d+\s+(\d+)%\s.*)$/) {
@@ -1206,9 +1216,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;
 
@@ -1250,7 +1260,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");
@@ -1289,9 +1299,9 @@ sub vdisk_alloc {
            die "unable to allocate an image name for VM $vmid in storage '$storeid'\n"
                if !$name;
 
-           my $cmd = ['/sbin/lvcreate', '--addtag', "pve-vm-$vmid", '--size', "${size}k", '--name', $name, $vg];
+           my $cmd = ['/sbin/lvcreate', '-aly', '--addtag', "pve-vm-$vmid", '--size', "${size}k", '--name', $name, $vg];
 
-           run_command ($cmd);
+           run_command($cmd, errmsg => "lvcreate '$vg/pve-vm-$vmid' error");
 
            return "$storeid:$name";
 
@@ -1329,7 +1339,7 @@ sub vdisk_free {
 
            my $cmd = ['/sbin/lvremove', '-f', "$vg/$volname"];
 
-           run_command ($cmd);
+           run_command($cmd, errmsg => "lvremove '$vg/$volname' error");
        } elsif ($scfg->{type} eq 'iscsi') {
            die "can't free space in iscsi storage\n";
        } else {
@@ -1348,7 +1358,7 @@ sub lvm_pv_info {
     my $has_label = 0;
 
     my $cmd = ['/usr/bin/file', '-L', '-s', $device];
-    run_command ($cmd, outfunc => sub {
+    run_command($cmd, outfunc => sub {
        my $line = shift;
        $has_label = 1 if $line =~ m/LVM2/;
     });
@@ -1360,7 +1370,7 @@ sub lvm_pv_info {
            'pv_name,pv_size,vg_name,pv_uuid', $device];
 
     my $pvinfo;
-    run_command ($cmd, outfunc => sub {
+    run_command($cmd, outfunc => sub {
        my $line = shift;
 
        $line = trim($line);
@@ -1407,12 +1417,12 @@ sub lvm_create_volume_group {
     # so pe_start is aligned on a 128k boundary (advantage for SSDs)
     my $cmd = ['/sbin/pvcreate', '--metadatasize', '250k', $device];
 
-    run_command ($cmd);
+    run_command($cmd, errmsg => "pvcreate '$device' error");
 
     $cmd = ['/sbin/vgcreate', $vgname, $device];
     # push @$cmd, '-c', 'y' if $shared; # we do not use this yet
 
-    run_command ($cmd);
+    run_command($cmd, errmsg => "vgcreate $vgname $device error");
 }
 
 sub lvm_vgs {
@@ -1422,15 +1432,22 @@ sub lvm_vgs {
               'vg_name,vg_size,vg_free'];
 
     my $vgs = {};
-    run_command ($cmd, outfunc => sub {
-       my $line = shift;
+    eval {
+       run_command($cmd, outfunc => sub {
+           my $line = shift;
 
-       $line = trim($line);
+           $line = trim($line);
 
-       my ($name, $size, $free) = split (':', $line);
+           my ($name, $size, $free) = split (':', $line);
 
-       $vgs->{$name} = { size => int ($size), free => int ($free) };
-    });
+           $vgs->{$name} = { size => int ($size), free => int ($free) };
+        });
+    };
+    my $err = $@;
+
+    # just warn (vgs return error code 5 if clvmd does not run)
+    # but output is still OK (list without clustered VGs)
+    warn $err if $err;
 
     return $vgs;
 }
@@ -1445,7 +1462,7 @@ sub lvm_lvs {
     push @$cmd, $vgname if $vgname;
 
     my $lvs = {};
-    run_command ($cmd, outfunc => sub {
+    run_command($cmd, outfunc => sub {
        my $line = shift;
 
        $line = trim($line);
@@ -1477,50 +1494,6 @@ sub lvm_lvs {
     return $lvs;
 }
 
-#install iso or openvz template ($tt = <iso|vztmpl>)
-# we simply overwrite when file already exists
-sub install_template {
-    my ($cfg, $storeid, $tt, $srcfile, $destfile) = @_;
-
-    my $scfg = storage_config ($cfg, $storeid);
-
-    my $type = $scfg->{type};
-
-    die "invalid storage type '$type'" if !($type eq 'dir' || $type eq 'nfs');
-
-    my $path;
-
-    if ($tt eq 'iso') {
-       die "file '$destfile' has no '.iso' extension\n" 
-           if $destfile !~ m![^/]+\.[Ii][Ss][Oo]$!;
-       die "storage '$storeid' does not support 'iso' content\n" 
-           if !$scfg->{content}->{iso};
-       $path = get_iso_dir ($cfg, $storeid);
-    } elsif ($tt eq 'vztmpl') {
-       die "file '$destfile' has no '.tar.gz' extension\n"
-           if $destfile !~ m![^/]+\.tar\.gz$!;
-       die "storage '$storeid' does not support 'vztmpl' content\n" 
-           if !$scfg->{content}->{vztmpl};
-       $path = get_vztmpl_dir ($cfg, $storeid);
-    } else {
-       die "unknown template type '$tt'";
-    }
-
-    activate_storage ($cfg, $storeid);
-
-    my $dest = "$path/$destfile";
-
-    my $cmd = ['cp', $srcfile, $dest];
-
-    eval { run_command ($cmd); };
-    my $err = $@;
-
-    if ($err) {
-       unlink $dest;
-       die $err;
-    }
-}
-
 #list iso or openvz template ($tt = <iso|vztmpl|backup>)
 sub template_list {
     my ($cfg, $storeid, $tt) = @_;
@@ -1553,11 +1526,11 @@ sub template_list {
 
            my $path;
            if ($tt eq 'iso') {
-               $path = get_iso_dir ($cfg, $sid);
+               $path = get_iso_dir($cfg, $sid);
            } elsif ($tt eq 'vztmpl') {
-               $path = get_vztmpl_dir ($cfg, $sid);
+               $path = get_vztmpl_dir($cfg, $sid);
            } elsif ($tt eq 'backup') {
-               $path = $scfg->{path};
+               $path = get_backup_dir($cfg, $sid);
            } else {
                die "unknown template type '$tt'\n";
            }
@@ -1577,7 +1550,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 };
                }
@@ -1605,7 +1578,7 @@ sub file_size_info {
     my $used = 0;
 
     eval {
-       run_command ($cmd, timeout => $timeout, outfunc => sub {
+       run_command($cmd, timeout => $timeout, outfunc => sub {
            my $line = shift;
 
            if ($line =~ m/^file format:\s+(\S+)\s*$/) {
@@ -1777,7 +1750,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;
     } 
 
@@ -1794,7 +1767,7 @@ sub nfs_mount {
        push @$cmd, '-o', $options;
     } 
 
-    run_command ($cmd);
+    run_command($cmd, errmsg => "mount error");
 }
 
 sub uevent_seqnum {
@@ -1860,9 +1833,11 @@ sub __activate_storage_full {
                "directory '$path' does not exist\n" if ! -d $path;
        }
 
-       my $imagedir = get_image_dir ($cfg, $storeid);
-       my $isodir = get_iso_dir ($cfg, $storeid);
-       my $tmpldir = get_vztmpl_dir ($cfg, $storeid);
+       my $imagedir = get_image_dir($cfg, $storeid);
+       my $isodir = get_iso_dir($cfg, $storeid);
+       my $tmpldir = get_vztmpl_dir($cfg, $storeid);
+       my $backupdir = get_backup_dir($cfg, $storeid);
+       my $privatedir = get_private_dir($cfg, $storeid);
 
        if (defined($scfg->{content})) {
            mkpath $imagedir if $scfg->{content}->{images} &&
@@ -1871,6 +1846,10 @@ sub __activate_storage_full {
                $isodir ne $path;
            mkpath $tmpldir if $scfg->{content}->{vztmpl} &&
                $tmpldir ne $path;
+           mkpath $privatedir if $scfg->{content}->{rootdir} &&
+               $privatedir ne $path;
+           mkpath $backupdir if $scfg->{content}->{backup} &&
+               $backupdir ne $path;
        }
 
     } elsif ($type eq 'lvm') {
@@ -1890,12 +1869,12 @@ sub __activate_storage_full {
            !$session->{vgs}->{$scfg->{vgname}}) {
            $session->{vgscaned} = 1;
            my $cmd = ['/sbin/vgscan', '--ignorelockingfailure', '--mknodes'];
-           eval { run_command ($cmd, outfunc => sub {}); };
+           eval { run_command($cmd, outfunc => sub {}); };
            warn $@ if $@;
        }
 
-       my $cmd = ['/sbin/vgchange', '-aly', $scfg->{vgname}];
-       run_command ($cmd, outfunc => sub {});
+       # we do not acticate any volumes here ('vgchange -aly')
+       # instead, volumes are activate individually later
 
     } elsif ($type eq 'iscsi') {
 
@@ -1948,7 +1927,11 @@ sub activate_storage {
 }
 
 sub activate_volumes {
-    my ($cfg, $vollist) = @_;
+    my ($cfg, $vollist, $exclusive) = @_;
+
+    return if !($vollist && scalar(@$vollist));
+
+    my $lvm_activate_mode = $exclusive ? 'ey' : 'ly';
 
     my $storagehash = {};
     foreach my $volid (@$vollist) {
@@ -1966,14 +1949,13 @@ sub activate_volumes {
        my $path = path ($cfg, $volid);
 
        if ($scfg->{type} eq 'lvm') {
-           my $cmd = ['/sbin/lvchange', '-aly', $path];
-           eval { run_command ($cmd); };
-           warn $@ if $@;
+           my $cmd = ['/sbin/lvchange', "-a$lvm_activate_mode", $path];
+           run_command($cmd, errmsg => "can't activate LV '$volid'");
        }
 
        # 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;
        }
@@ -1983,8 +1965,11 @@ sub activate_volumes {
 sub deactivate_volumes {
     my ($cfg, $vollist) = @_;
 
+    return if !($vollist && scalar(@$vollist));
+
     my $lvs = lvm_lvs ();
 
+    my @errlist = ();
     foreach my $volid (@$vollist) {
        my ($storeid, $volname) = parse_volume_id ($volid);
 
@@ -1996,11 +1981,17 @@ sub deactivate_volumes {
            if ($lvs->{$scfg->{vgname}}->{$name}) {
                my $path = path ($cfg, $volid);
                my $cmd = ['/sbin/lvchange', '-aln', $path];
-               eval { run_command ($cmd); };
-               warn $@ if $@;
+               eval { run_command($cmd, errmsg => "can't deactivate LV '$volid'"); };
+               if (my $err = $@) {
+                   warn $err;
+                   push @errlist, $volid;
+               }
            }
        }
     }
+
+    die "volume deativation failed: " . join(' ', @errlist)
+       if scalar(@errlist);
 }
 
 sub deactivate_storage {
@@ -2022,10 +2013,12 @@ sub deactivate_storage {
 
        my $cmd = ['/bin/umount', $path];
 
-       run_command ($cmd) if nfs_is_mounted ($server, $export, $path, $mountdata); 
+       run_command($cmd, errmsg => 'umount error') 
+           if nfs_is_mounted ($server, $export, $path, $mountdata); 
+
     } elsif ($type eq 'lvm') {
        my $cmd = ['/sbin/vgchange', '-aln', $scfg->{vgname}];
-       run_command ($cmd);
+       run_command($cmd, errmsg => "can't deactivate VG '$scfg->{vgname}'");
     } elsif ($type eq 'iscsi') {
        my $portal = $scfg->{portal};
        my $target = $scfg->{target};
@@ -2061,6 +2054,7 @@ sub storage_info {
            total => 0, 
            avail => 0, 
            used => 0, 
+           shared => $ids->{$storeid}->{shared} ? 1 : 0,
            content => content_hash_to_string($ids->{$storeid}->{content}),
            active => 0,
        };
@@ -2172,7 +2166,7 @@ sub scan_nfs {
     my $cmd = ['/sbin/showmount',  '--no-headers', '--exports', $server];
 
     my $res = {};
-    run_command ($cmd, outfunc => sub {
+    run_command($cmd, outfunc => sub {
        my $line = shift;
 
        # note: howto handle white spaces in export path??