use PVE::Storage::Plugin;
use PVE::Storage;
use PVE::QemuServer;
+use PVE::JSONSchema;
use IO::File;
use IPC::Open3;
PVE::VZDump::check_bin('qm');
- my $self = bless { vzdump => $vzdump };
+ my $self = bless { vzdump => $vzdump }, $class;
$self->{vmlist} = PVE::QemuServer::vzlist();
$self->{storecfg} = PVE::Storage::config();
$task->{disks} = [];
- my $conf = $self->{vmlist}->{$vmid} = PVE::QemuServer::load_config($vmid);
+ my $conf = $self->{vmlist}->{$vmid} = PVE::QemuConfig->load_config($vmid);
+
+ $self->loginfo("VM Name: $conf->{name}")
+ if defined($conf->{name});
$self->{vm_was_running} = 1;
if (!PVE::QemuServer::check_running($vmid)) {
return if PVE::QemuServer::drive_is_cdrom($drive);
- if (defined($drive->{backup}) && $drive->{backup} eq "no") {
- $self->loginfo("exclude disk '$ds' (backup=no)");
+ my $volid = $drive->{file};
+
+ if (defined($drive->{backup}) && !$drive->{backup}) {
+ $self->loginfo("exclude disk '$ds' '$volid' (backup=no)");
return;
+ } elsif ($drive->{iothread}) {
+ die "disk '$ds' '$volid' (iothread=on) can't use backup feature currently. Please set backup=no for this drive";
+ } else {
+ my $log = "include disk '$ds' '$volid'";
+ if (defined $drive->{size}) {
+ my $readable_size = PVE::JSONSchema::format_size($drive->{size});
+ $log .= " $readable_size";
+ }
+ $self->loginfo($log);
}
- my $volid = $drive->{file};
-
my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
push @$vollist, $volid if $storeid;
$drivehash->{$ds} = $drive;
sub assemble {
my ($self, $task, $vmid) = @_;
- my $conffile = PVE::QemuServer::config_file ($vmid);
+ my $conffile = PVE::QemuConfig->config_file($vmid);
my $outfile = "$task->{tmpdir}/qemu-server.conf";
-
- my $outfd;
- my $conffd;
-
- eval {
-
- $outfd = IO::File->new (">$outfile") ||
- die "unable to open '$outfile'";
- $conffd = IO::File->new ($conffile, 'r') ||
- die "unable open '$conffile'";
-
- my $found_snapshot;
- while (defined (my $line = <$conffd>)) {
- next if $line =~ m/^\#vzdump\#/; # just to be sure
- next if $line =~ m/^\#qmdump\#/; # just to be sure
- if ($line =~ m/^\[.*\]\s*$/) {
+ my $firewall_src = "/etc/pve/firewall/$vmid.fw";
+ my $firewall_dest = "$task->{tmpdir}/qemu-server.fw";
+
+ my $outfd = IO::File->new (">$outfile") ||
+ die "unable to open '$outfile'";
+ my $conffd = IO::File->new ($conffile, 'r') ||
+ die "unable open '$conffile'";
+
+ my $found_snapshot;
+ my $found_pending;
+ while (defined (my $line = <$conffd>)) {
+ next if $line =~ m/^\#vzdump\#/; # just to be sure
+ next if $line =~ m/^\#qmdump\#/; # just to be sure
+ if ($line =~ m/^\[(.*)\]\s*$/) {
+ if ($1 =~ m/PENDING/i) {
+ $found_pending = 1;
+ } else {
$found_snapshot = 1;
}
- next if $found_snapshot; # skip all snapshots data
- if ($line =~ m/^unused\d+:\s*(\S+)\s*/) {
- $self->loginfo("skip unused drive '$1' (not included into backup)");
- next;
- }
- next if $line =~ m/^lock:/ || $line =~ m/^parent:/;
-
- print $outfd $line;
}
- foreach my $di (@{$task->{disks}}) {
- if ($di->{type} eq 'block' || $di->{type} eq 'file') {
- my $storeid = $di->{storeid} || '';
- my $format = $di->{format} || '';
- print $outfd "#qmdump#map:$di->{virtdev}:$di->{qmdevice}:$storeid:$format:\n";
- } else {
- die "internal error";
- }
+ next if $found_snapshot; # skip all snapshots data
+ next if $found_pending; # skip all pending changes
+
+ if ($line =~ m/^unused\d+:\s*(\S+)\s*/) {
+ $self->loginfo("skip unused drive '$1' (not included into backup)");
+ next;
}
+ next if $line =~ m/^lock:/ || $line =~ m/^parent:/;
- if ($found_snapshot) {
- $self->loginfo("snapshots found (not included into backup)");
+ print $outfd $line;
+ }
+
+ foreach my $di (@{$task->{disks}}) {
+ if ($di->{type} eq 'block' || $di->{type} eq 'file') {
+ my $storeid = $di->{storeid} || '';
+ my $format = $di->{format} || '';
+ print $outfd "#qmdump#map:$di->{virtdev}:$di->{qmdevice}:$storeid:$format:\n";
+ } else {
+ die "internal error";
}
- };
- my $err = $@;
+ }
+
+ if ($found_snapshot) {
+ $self->loginfo("snapshots found (not included into backup)");
+ }
- close ($outfd) if $outfd;
- close ($conffd) if $conffd;
+ if ($found_pending) {
+ $self->loginfo("pending configuration changes found (not included into backup)");
+ }
- die $err if $err;
+ PVE::Tools::file_copy($firewall_src, $firewall_dest) if -f $firewall_src;
}
sub archive {
my ($self, $task, $vmid, $filename, $comp) = @_;
my $conffile = "$task->{tmpdir}/qemu-server.conf";
+ my $firewall = "$task->{tmpdir}/qemu-server.fw";
my $opts = $self->{vzdump}->{opts};
my $diskcount = scalar(@{$task->{disks}});
- if (PVE::QemuServer::is_template($self->{vmlist}->{$vmid}) || !$diskcount) {
+ if (PVE::QemuConfig->is_template($self->{vmlist}->{$vmid}) || !$diskcount) {
my @pathlist;
foreach my $di (@{$task->{disks}}) {
if ($di->{type} eq 'block' || $di->{type} eq 'file') {
$outcmd = "exec:cat";
}
- $outcmd .= ">$filename" if !$opts->{stdout};
+ $outcmd .= " > $filename" if !$opts->{stdout};
- my $cmd = ['/usr/bin/vma', 'create', '-v', '-c', $conffile, $outcmd, @pathlist];
+ my $cmd = ['/usr/bin/vma', 'create', '-v', '-c', $conffile];
+ push @$cmd, '-c', $firewall if -e $firewall;
+ push @$cmd, $outcmd, @pathlist;
$self->loginfo("starting template backup");
$self->loginfo(join(' ', @$cmd));
my $backup_cb = sub {
my ($vmid, $resp) = @_;
- $uuid = $resp->{return};
+ $uuid = $resp->{return}->{UUID};
};
my $outfh;
my $add_fd_cb = sub {
my ($vmid, $resp) = @_;
- $qmpclient->queue_cmd($vmid, $backup_cb, 'backup',
- 'backup-file' => "/dev/fdname/backup",
- speed => $speed,
- 'config-file' => $conffile,
- devlist => $devlist);
- };
+ my $params = {
+ 'backup-file' => "/dev/fdname/backup",
+ speed => $speed,
+ 'config-file' => $conffile,
+ devlist => $devlist
+ };
+ $params->{'firewall-file'} = $firewall if -e $firewall;
+ $qmpclient->queue_cmd($vmid, $backup_cb, 'backup', %$params);
+ };
$qmpclient->queue_cmd($vmid, $add_fd_cb, 'getfd',
fd => $outfileno, fdname => "backup");
- if ($self->{vmlist}->{$vmid}->{agent} && $vm_is_running){
+ my $agent_running = 0;
+
+ if ($self->{vmlist}->{$vmid}->{agent} && $vm_is_running) {
+ $agent_running = PVE::QemuServer::qga_check_running($vmid);
+ }
+
+ if ($agent_running){
eval { PVE::QemuServer::vm_mon_cmd($vmid, "guest-fsfreeze-freeze"); };
if (my $err = $@) {
$self->logerr($err);
}
}
- $qmpclient->queue_execute();
+ eval { $qmpclient->queue_execute() };
+ my $qmperr = $@;
- if ($self->{vmlist}->{$vmid}->{agent} && $vm_is_running ){
+ if ($agent_running){
eval { PVE::QemuServer::vm_mon_cmd($vmid, "guest-fsfreeze-thaw"); };
if (my $err = $@) {
$self->logerr($err);
}
}
+ die $qmperr if $qmperr;
die $qmpclient->{errors}->{$vmid} if $qmpclient->{errors}->{$vmid};
if ($cpid) {
$self->loginfo("started backup task '$uuid'");
if ($resume_on_backup) {
- $self->loginfo("resume VM");
+ if (my $stoptime = $task->{vmstoptime}) {
+ my $delay = time() - $task->{vmstoptime};
+ $task->{vmstoptime} = undef; # avoid printing 'online after ..' twice
+ $self->loginfo("resuming VM again after $delay seconds");
+ } else {
+ $self->loginfo("resuming VM again");
+ }
PVE::QemuServer::vm_mon_cmd($vmid, 'cont');
}
my $statusline = "status: $per% ($transferred/$total), " .
"sparse ${zero_per}% ($zero), duration $duration, " .
- "$mbps_read/$mbps_write MB/s";
+ "read/write $mbps_read/$mbps_write MB/s";
my $res = $status->{status} || 'unknown';
if ($res ne 'active') {
$self->loginfo($statusline);