+sub volume_resize {
+ my ($class, $scfg, $storeid, $volname, $size, $running) = @_;
+
+ $size = ($size/1024/1024) . "M";
+
+ my $path = $class->path($scfg, $volname);
+ my $cmd = ['/sbin/lvextend', '-L', $size, $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) = @_;
+
+ die "lvm snapshot is not implemented";
+}
+
+sub volume_snapshot_rollback {
+ my ($class, $scfg, $storeid, $volname, $snap) = @_;
+
+ die "lvm snapshot rollback is not implemented";
+}
+
+sub volume_snapshot_delete {
+ my ($class, $scfg, $storeid, $volname, $snap) = @_;
+
+ die "lvm snapshot delete is not implemented";
+}
+
+sub volume_has_feature {
+ my ($class, $scfg, $feature, $storeid, $volname, $snapname, $running) = @_;
+
+ my $features = {
+ copy => { base => 1, current => 1},
+ rename => {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;
+}
+
+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, $snapshot, $base_snapshot, $with_snapshots);
+}
+
+sub volume_export {
+ my ($class, $scfg, $storeid, $fh, $volname, $format, $snapshot, $base_snapshot, $with_snapshots) = @_;
+ die "volume export format $format not available for $class\n"
+ if $format ne 'raw+size';
+ die "cannot export volumes together with their snapshots in $class\n"
+ if $with_snapshots;
+ die "cannot export a snapshot in $class\n" if defined($snapshot);
+ die "cannot export an incremental stream in $class\n" if defined($base_snapshot);
+ my $file = $class->path($scfg, $volname, $storeid);
+ my $size;
+ # should be faster than querying LVM, also checks for the device file's availability
+ run_command(['/sbin/blockdev', '--getsize64', $file], outfunc => sub {
+ my ($line) = @_;
+ die "unexpected output from /sbin/blockdev: $line\n" if $line !~ /^(\d+)$/;
+ $size = int($1);
+ });
+ PVE::Storage::Plugin::write_common_header($fh, $size);
+ run_command(['dd', "if=$file", "bs=64k"], output => '>&'.fileno($fh));
+}
+
+sub volume_import_formats {
+ 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, $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"
+ if $with_snapshots;
+ die "cannot import an incremental stream in $class\n" if defined($base_snapshot);
+
+ my ($vtype, $name, $vmid, $basename, $basevmid, $isBase, $file_format) =
+ $class->parse_volname($volname);
+ die "cannot import format $format into a file of format $file_format\n"
+ if $file_format ne 'raw';
+
+ my $vg = $scfg->{vgname};
+ my $lvs = lvm_list_volumes($vg);
+ 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);
+ 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";
+
+ $class->volume_import_write($fh, $file);
+ };
+ if (my $err = $@) {
+ 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));
+}
+
+sub rename_volume {
+ my ($class, $scfg, $storeid, $source_volname, $target_vmid, $target_volname) = @_;
+
+ my (
+ undef,
+ $source_image,
+ $source_vmid,
+ $base_name,
+ $base_vmid,
+ undef,
+ $format
+ ) = $class->parse_volname($source_volname);
+ $target_volname = $class->find_free_diskname($storeid, $scfg, $target_vmid, $format)
+ if !$target_volname;
+
+ my $vg = $scfg->{vgname};
+ my $lvs = lvm_list_volumes($vg);
+ die "target volume '${target_volname}' already exists\n"
+ if ($lvs->{$vg}->{$target_volname});
+
+ lvrename($vg, $source_volname, $target_volname);
+ return "${storeid}:${target_volname}";
+}
+