[pve-devel] [PATCH v2 qemu-server] restore: implement rate limiting
Wolfgang Bumiller
w.bumiller at proxmox.com
Thu Feb 15 13:41:23 CET 2018
Signed-off-by: Wolfgang Bumiller <w.bumiller at proxmox.com>
---
This version avoids the refactoring and double-reading of the VMA file,
thereby supporting reading from a pipe again.
Instead this now uses an extension to vma extract (see the other patch),
and instead of picking the lowest limit altogether, applies limits to
disks separately per-storage by creating a throttling group per storage
id.
PVE/API2/Qemu.pm | 11 +++++++-
PVE/CLI/qmrestore.pm | 6 ++++
PVE/QemuServer.pm | 78 ++++++++++++++++++++++++++++++++++++++++------------
3 files changed, 77 insertions(+), 18 deletions(-)
diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index b277a26..a63f2c9 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -405,6 +405,12 @@ __PACKAGE__->register_method({
type => 'string', format => 'pve-poolid',
description => "Add the VM to the specified pool.",
},
+ bwlimit => {
+ description => "Override io bandwidth.",
+ optional => 1,
+ type => 'number',
+ minimum => '0',
+ }
}),
},
returns => {
@@ -431,6 +437,8 @@ __PACKAGE__->register_method({
my $pool = extract_param($param, 'pool');
+ my $bwlimit = extract_param($param, 'bwlimit');
+
my $filename = PVE::QemuConfig->config_file($vmid);
my $storecfg = PVE::Storage::config();
@@ -513,7 +521,8 @@ __PACKAGE__->register_method({
PVE::QemuServer::restore_archive($archive, $vmid, $authuser, {
storage => $storage,
pool => $pool,
- unique => $unique });
+ unique => $unique,
+ bwlimit => $bwlimit, });
PVE::AccessControl::add_vm_to_pool($vmid, $pool) if $pool;
};
diff --git a/PVE/CLI/qmrestore.pm b/PVE/CLI/qmrestore.pm
index 17018d2..7e12b10 100755
--- a/PVE/CLI/qmrestore.pm
+++ b/PVE/CLI/qmrestore.pm
@@ -53,6 +53,12 @@ __PACKAGE__->register_method({
type => 'string', format => 'pve-poolid',
description => "Add the VM to the specified pool.",
},
+ bwlimit => {
+ description => "Override io bandwidth.",
+ optional => 1,
+ type => 'number',
+ minimum => '0',
+ }
},
},
returns => {
diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index 20d6682..1607932 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -5539,21 +5539,52 @@ sub rescan {
sub restore_vma_archive {
my ($archive, $vmid, $user, $opts, $comp) = @_;
- my $input = $archive eq '-' ? "<&STDIN" : undef;
my $readfrom = $archive;
- my $uncomp = '';
- if ($comp) {
+ my $cfg = PVE::Storage::config();
+ my $commands = [];
+ my $dbg_cmdstring = '';
+ my $bwlimit = $opts->{bwlimit};
+
+ my $add_pipe = sub {
+ my ($cmd, $final) = @_;
+ push @$commands, $cmd;
+ $dbg_cmdstring .= PVE::Tools::cmd2string($cmd);
+ $dbg_cmdstring .= ' | ' if !$final;
$readfrom = '-';
- my $qarchive = PVE::Tools::shellquote($archive);
+ };
+
+ my $input = undef;
+ if ($archive eq '-') {
+ $input = '<&STDIN';
+ } else {
+ # If we use a backup from a PVE defined storage we also consider that
+ # storage's rate limit:
+ my (undef, $volid) = PVE::Storage::path_to_volume_id($cfg, $archive);
+ if (defined($volid)) {
+ my ($sid, undef) = PVE::Storage::parse_volume_id($volid);
+ my $readlimit = PVE::Storage::get_bandwidth_limit('restore', [$sid], $bwlimit);
+ if ($readlimit) {
+ print STDERR "applying read rate limit: $readlimit\n";
+ my $cstream = ['cstream', '-t', $readlimit*1024];
+ if ($readfrom ne '-') {
+ push @$cstream, '--', $readfrom;
+ }
+ $add_pipe->($cstream);
+ }
+ }
+ }
+
+ if ($comp) {
+ my $cmd;
if ($comp eq 'gzip') {
- $uncomp = "zcat $qarchive|";
+ $cmd = ['zcat', $readfrom];
} elsif ($comp eq 'lzop') {
- $uncomp = "lzop -d -c $qarchive|";
+ $cmd = ['lzop', '-d', '-c', $readfrom];
} else {
die "unknown compression method '$comp'\n";
}
-
+ $add_pipe->($cmd);
}
my $tmpdir = "/var/tmp/vzdumptmp$$";
@@ -5573,7 +5604,7 @@ sub restore_vma_archive {
open($fifofh, '>', $mapfifo) || die $!;
};
- my $cmd = "${uncomp}vma extract -v -r $mapfifo $readfrom $tmpdir";
+ $add_pipe->(['vma', 'extract', '-v', '-r', $mapfifo, $readfrom, $tmpdir]);
my $oldtimeout;
my $timeout = 5;
@@ -5589,6 +5620,8 @@ sub restore_vma_archive {
my $cfs_path = PVE::QemuConfig->cfs_config_path($vmid);
my $oldconf = PVE::Cluster::cfs_read_file($cfs_path);
+ my %storage_limits;
+
my $print_devmap = sub {
my $virtdev_hash = {};
@@ -5627,17 +5660,23 @@ sub restore_vma_archive {
$rpcenv->check($user, "/storage/$storeid", ['Datastore.AllocateSpace']);
}
+ $storage_limits{$storeid} = $bwlimit;
+
$virtdev_hash->{$virtdev} = $devinfo->{$devname};
}
}
+ foreach my $key (keys %storage_limits) {
+ my $limit = PVE::Storage::get_bandwidth_limit('restore', [$key], $bwlimit);
+ print STDERR "rate limit for storage $key: $limit KiB/s\n";
+ $storage_limits{$key} = $limit * 1024;
+ }
+
foreach my $devname (keys %$devinfo) {
die "found no device mapping information for device '$devname'\n"
if !$devinfo->{$devname}->{virtdev};
}
- my $cfg = PVE::Storage::config();
-
# create empty/temp config
if ($oldconf) {
PVE::Tools::file_set_contents($conffile, "memory: 128\n");
@@ -5680,14 +5719,20 @@ sub restore_vma_archive {
foreach my $virtdev (sort keys %$virtdev_hash) {
my $d = $virtdev_hash->{$virtdev};
my $alloc_size = int(($d->{size} + 1024 - 1)/1024);
- my $scfg = PVE::Storage::storage_config($cfg, $d->{storeid});
+ my $storeid = $d->{storeid};
+ my $scfg = PVE::Storage::storage_config($cfg, $storeid);
+
+ my $map_opts = '';
+ if (my $limit = $storage_limits{$storeid}) {
+ $map_opts .= "throttling.bps=$limit:throttling.group=$storeid:";
+ }
# test if requested format is supported
- my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($cfg, $d->{storeid});
+ my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($cfg, $storeid);
my $supported = grep { $_ eq $d->{format} } @$validFormats;
$d->{format} = $defFormat if !$supported;
- my $volid = PVE::Storage::vdisk_alloc($cfg, $d->{storeid}, $vmid,
+ my $volid = PVE::Storage::vdisk_alloc($cfg, $storeid, $vmid,
$d->{format}, undef, $alloc_size);
print STDERR "new volume ID is '$volid'\n";
$d->{volid} = $volid;
@@ -5700,7 +5745,7 @@ sub restore_vma_archive {
$write_zeros = 0;
}
- print $fifofh "format=$d->{format}:${write_zeros}:$d->{devname}=$path\n";
+ print $fifofh "${map_opts}format=$d->{format}:${write_zeros}:$d->{devname}=$path\n";
print "map '$d->{devname}' to '$path' (write zeros = ${write_zeros})\n";
$map->{$virtdev} = $volid;
@@ -5753,8 +5798,8 @@ sub restore_vma_archive {
}
};
- print "restore vma archive: $cmd\n";
- run_command($cmd, input => $input, outfunc => $parser, afterfork => $openfifo);
+ print "restore vma archive: $dbg_cmdstring\n";
+ run_command($commands, input => $input, outfunc => $parser, afterfork => $openfifo);
};
my $err = $@;
@@ -5766,7 +5811,6 @@ sub restore_vma_archive {
push @$vollist, $volid if $volid;
}
- my $cfg = PVE::Storage::config();
PVE::Storage::deactivate_volumes($cfg, $vollist);
unlink $mapfifo;
--
2.11.0
More information about the pve-devel
mailing list