X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=PVE%2FStorage%2FLVMPlugin.pm;h=139d391eacd3befc6b714f2ed5792f085a68ade3;hb=d5fc368503fc04fdbc411d0a6dde13fce75ece18;hp=19bac55e503d5e8862cc58a666bd7a33ae8f4d7b;hpb=ca552c7639808018b50d70944006560a5a2f0ee5;p=pve-storage.git diff --git a/PVE/Storage/LVMPlugin.pm b/PVE/Storage/LVMPlugin.pm index 19bac55..139d391 100644 --- a/PVE/Storage/LVMPlugin.pm +++ b/PVE/Storage/LVMPlugin.pm @@ -13,6 +13,15 @@ use base qw(PVE::Storage::Plugin); # lvm helper functions +my $ignore_no_medium_warnings = sub { + my $line = shift; + # ignore those, most of the time they're from (virtual) IPMI/iKVM devices + # and just spam the log.. + if ($line !~ /open failed: No medium found/) { + print STDERR "$line\n"; + } +}; + sub lvm_pv_info { my ($device) = @_; @@ -85,7 +94,7 @@ sub lvm_create_volume_group { $cmd = ['/sbin/vgcreate', $vgname, $device]; # push @$cmd, '-c', 'y' if $shared; # we do not use this yet - run_command($cmd, errmsg => "vgcreate $vgname $device error"); + run_command($cmd, errmsg => "vgcreate $vgname $device error", errfunc => $ignore_no_medium_warnings, outfunc => $ignore_no_medium_warnings); } sub lvm_vgs { @@ -106,13 +115,15 @@ sub lvm_vgs { eval { run_command($cmd, outfunc => sub { my $line = shift; - $line = trim($line); my ($name, $size, $free, $lvcount, $pvname, $pvsize, $pvfree) = split (':', $line); - $vgs->{$name} = { size => int ($size), free => int ($free), lvcount => int($lvcount) } - if !$vgs->{$name}; + $vgs->{$name} //= { + size => int ($size), + free => int ($free), + lvcount => int($lvcount) + }; if (defined($pvname) && defined($pvsize) && defined($pvfree)) { push @{$vgs->{$name}->{pvs}}, { @@ -121,7 +132,9 @@ sub lvm_vgs { free => int($pvfree), }; } - }); + }, + errfunc => $ignore_no_medium_warnings, + ); }; my $err = $@; @@ -135,9 +148,14 @@ sub lvm_vgs { sub lvm_list_volumes { my ($vgname) = @_; - my $cmd = ['/sbin/lvs', '--separator', ':', '--noheadings', '--units', 'b', - '--unbuffered', '--nosuffix', '--options', - 'vg_name,lv_name,lv_size,lv_attr,pool_lv,data_percent,metadata_percent,snap_percent,uuid,tags,metadata_size']; + my $option_list = 'vg_name,lv_name,lv_size,lv_attr,pool_lv,data_percent,metadata_percent,snap_percent,uuid,tags,metadata_size,time'; + + my $cmd = [ + '/sbin/lvs', '--separator', ':', '--noheadings', '--units', 'b', + '--unbuffered', '--nosuffix', + '--config', 'report/time_format="%s"', + '--options', $option_list, + ]; push @$cmd, $vgname if $vgname; @@ -147,7 +165,7 @@ sub lvm_list_volumes { $line = trim($line); - my ($vg_name, $lv_name, $lv_size, $lv_attr, $pool_lv, $data_percent, $meta_percent, $snap_percent, $uuid, $tags, $meta_size) = split(':', $line); + my ($vg_name, $lv_name, $lv_size, $lv_attr, $pool_lv, $data_percent, $meta_percent, $snap_percent, $uuid, $tags, $meta_size, $ctime) = split(':', $line); return if !$vg_name; return if !$lv_name; @@ -159,6 +177,7 @@ sub lvm_list_volumes { }; $d->{pool_lv} = $pool_lv if $pool_lv; $d->{tags} = $tags if $tags; + $d->{ctime} = $ctime; if ($lv_type eq 't') { $data_percent ||= 0; @@ -169,7 +188,9 @@ sub lvm_list_volumes { $d->{used} = int(($data_percent * $lv_size)/100); } $lvs->{$vg_name}->{$lv_name} = $d; - }); + }, + errfunc => $ignore_no_medium_warnings, + ); return $lvs; } @@ -248,6 +269,8 @@ sub on_add_hook { lvm_create_volume_group($path, $scfg->{vgname}, $scfg->{shared}); } + + return; } sub parse_volname { @@ -288,26 +311,32 @@ sub clone_image { die "can't clone images in lvm storage\n"; } -sub lvm_find_free_diskname { - my ($lvs, $vg, $storeid, $vmid) = @_; +sub find_free_diskname { + my ($class, $storeid, $scfg, $vmid, $fmt, $add_fmt_suffix) = @_; - my $name; + my $vg = $scfg->{vgname}; - my $disk_ids = {}; - my @vols = keys(%{$lvs->{$vg}}); + my $lvs = lvm_list_volumes($vg); - foreach my $vol (@vols) { - if ($vol =~ m/(vm|base)-\Q$vmid\E-disk-(\d+)/){ - $disk_ids->{$2} = 1; - } - } + my $disk_list = [ keys %{$lvs->{$vg}} ]; - for (my $i = 1; $i < 100; $i++) { - return "vm-$vmid-disk-$i" if !$disk_ids->{$i}; + return PVE::Storage::Plugin::get_next_vm_diskname($disk_list, $storeid, $vmid, undef, $scfg); +} + +sub lvcreate { + my ($vg, $name, $size, $tags) = @_; + + if ($size =~ m/\d$/) { # no unit is given + $size .= "k"; # default to kilobytes } - die "unable to allocate an image name for ID $vmid in storage '$storeid'\n"; + my $cmd = ['/sbin/lvcreate', '-aly', '-Wy', '--yes', '--size', $size, '--name', $name]; + for my $tag (@$tags) { + push @$cmd, '--addtag', $tag; + } + push @$cmd, $vg; + run_command($cmd, errmsg => "lvcreate '$vg/$name' error"); } sub alloc_image { @@ -315,7 +344,7 @@ sub alloc_image { die "unsupported format '$fmt'" if $fmt ne 'raw'; - die "illegal name '$name' - sould be 'vm-$vmid-*'\n" + die "illegal name '$name' - should be 'vm-$vmid-*'\n" if $name && $name !~ m/^vm-$vmid-/; my $vgs = lvm_vgs(); @@ -328,12 +357,10 @@ sub alloc_image { die "not enough free space ($free < $size)\n" if $free < $size; - $name = lvm_find_free_diskname(lvm_list_volumes($vg), $vg, $storeid, $vmid) + $name = $class->find_free_diskname($storeid, $scfg, $vmid) if !$name; - my $cmd = ['/sbin/lvcreate', '-aly', '--addtag', "pve-vm-$vmid", '--size', "${size}k", '--name', $name, $vg]; - - run_command($cmd, errmsg => "lvcreate '$vg/pve-vm-$vmid' error"); + lvcreate($vg, $name, $size, ["pve-vm-$vmid"]); return $name; } @@ -376,6 +403,8 @@ sub free_image { my $cmd = ['/sbin/lvchange', '-aly', "$vg/$volname"]; run_command($cmd, errmsg => "can't activate LV '$vg/$volname' to zero-out its data"); + $cmd = ['/sbin/lvchange', '--refresh', "$vg/$volname"]; + run_command($cmd, errmsg => "can't refresh LV '$vg/$volname' to zero-out its data"); if ($scfg->{saferemove}) { # avoid long running task, so we only rename here @@ -417,7 +446,8 @@ sub list_images { next if $scfg->{tagged_only} && !&$check_tags($info->{tags}); - next if $info->{lv_type} ne '-'; + # Allow mirrored and RAID LVs + next if $info->{lv_type} !~ m/^[-mMrR]$/; my $volid = "$storeid:$volname"; @@ -430,6 +460,7 @@ sub list_images { push @$res, { volid => $volid, format => 'raw', size => $info->{lv_size}, vmid => $owner, + ctime => $info->{ctime}, }; } } @@ -486,6 +517,8 @@ sub activate_volume { my $cmd = ['/sbin/lvchange', "-a$lvm_activate_mode", $path]; run_command($cmd, errmsg => "can't activate LV '$path'"); + $cmd = ['/sbin/lvchange', '--refresh', $path]; + run_command($cmd, errmsg => "can't refresh LV '$path' for activation"); } sub deactivate_volume { @@ -505,11 +538,29 @@ sub volume_resize { my $path = $class->path($scfg, $volname); my $cmd = ['/sbin/lvextend', '-L', $size, $path]; - run_command($cmd, errmsg => "error resizing volume '$path'"); + + $class->cluster_lock_storage($storeid, $scfg->{shared}, undef, sub { + run_command($cmd, errmsg => "error resizing volume '$path'"); + }); return 1; } +sub volume_size_info { + my ($class, $scfg, $storeid, $volname, $timeout) = @_; + my $path = $class->filesystem_path($scfg, $volname); + + my $cmd = ['/sbin/lvs', '--separator', ':', '--noheadings', '--units', 'b', + '--unbuffered', '--nosuffix', '--options', 'lv_size', $path]; + + my $size; + run_command($cmd, timeout => $timeout, errmsg => "can't get size of '$path'", + outfunc => sub { + $size = int(shift); + }); + return wantarray ? ($size, 'raw', 0, undef) : $size; +} + sub volume_snapshot { my ($class, $scfg, $storeid, $volname, $snap) = @_; @@ -552,7 +603,7 @@ sub volume_has_feature { sub volume_export_formats { my ($class, $scfg, $storeid, $volname, $snapshot, $base_snapshot, $with_snapshots) = @_; return () if defined($snapshot); # lvm-thin only - return volume_import_formats($class, $scfg, $storeid, $volname, $base_snapshot, $with_snapshots); + return volume_import_formats($class, $scfg, $storeid, $volname, $snapshot, $base_snapshot, $with_snapshots); } sub volume_export { @@ -576,14 +627,14 @@ sub volume_export { } sub volume_import_formats { - my ($class, $scfg, $storeid, $volname, $base_snapshot, $with_snapshots) = @_; + my ($class, $scfg, $storeid, $volname, $snapshot, $base_snapshot, $with_snapshots) = @_; return () if $with_snapshots; # not supported return () if defined($base_snapshot); # not supported return ('raw+size'); } sub volume_import { - my ($class, $scfg, $storeid, $fh, $volname, $format, $base_snapshot, $with_snapshots) = @_; + my ($class, $scfg, $storeid, $fh, $volname, $format, $snapshot, $base_snapshot, $with_snapshots, $allow_rename) = @_; die "volume import format $format not available for $class\n" if $format ne 'raw+size'; die "cannot import volumes together with their snapshots in $class\n" @@ -597,29 +648,48 @@ sub volume_import { my $vg = $scfg->{vgname}; my $lvs = lvm_list_volumes($vg); - die "volume $vg/$volname already exists\n" - if $lvs->{$vg}->{$volname}; + if ($lvs->{$vg}->{$volname}) { + die "volume $vg/$volname already exists\n" if !$allow_rename; + warn "volume $vg/$volname already exists - importing with a different name\n"; + $name = undef; + } my ($size) = PVE::Storage::Plugin::read_common_header($fh); $size = int($size/1024); eval { my $allocname = $class->alloc_image($storeid, $scfg, $vmid, 'raw', $name, $size); - if ($allocname ne $volname) { - my $oldname = $volname; - $volname = $allocname; # Let the cleanup code know what to free + my $oldname = $volname; + $volname = $allocname; + if (defined($name) && $allocname ne $oldname) { die "internal error: unexpected allocated name: '$allocname' != '$oldname'\n"; } my $file = $class->path($scfg, $volname, $storeid) or die "internal error: failed to get path to newly allocated volume $volname\n"; - run_command(['dd', "of=$file", 'conv=sparse', 'bs=64k'], - input => '<&'.fileno($fh)); + + $class->volume_import_write($fh, $file); }; if (my $err = $@) { - eval { $class->free_image($storeid, $scfg, $volname, 0) }; + my $cleanup_worker = eval { $class->free_image($storeid, $scfg, $volname, 0) }; warn $@ if $@; + + if ($cleanup_worker) { + my $rpcenv = PVE::RPCEnvironment::get(); + my $authuser = $rpcenv->get_user(); + + $rpcenv->fork_worker('imgdel', undef, $authuser, $cleanup_worker); + } + die $err; } + + return "$storeid:$volname"; +} + +sub volume_import_write { + my ($class, $input_fh, $output_file) = @_; + run_command(['dd', "of=$output_file", 'bs=64k'], + input => '<&'.fileno($input_fh)); } 1;