ostype => {
optional => 1,
type => 'string',
- enum => [qw(other wxp w2k w2k3 w2k8 wvista win7 l24 l26)],
+ enum => [qw(other wxp w2k w2k3 w2k8 wvista win7 win8 l24 l26)],
description => <<EODESC,
Used to enable special optimization/features for specific
operating systems:
w2k8 => Microsoft Windows 2008
wvista => Microsoft Windows Vista
win7 => Microsoft Windows 7
+win8 => Microsoft Windows 8/2012
l24 => Linux 2.4 Kernel
l26 => Linux 2.6/3.X Kernel
-other|l24|l26 ... no special behaviour
-wxp|w2k|w2k3|w2k8|wvista|win7 ... use --localtime switch
+other|l24|l26 ... no special behaviour
+wxp|w2k|w2k3|w2k8|wvista|win7|win8 ... use --localtime switch
EODESC
},
boot => {
tdf => {
optional => 1,
type => 'boolean',
- description => "Enable/disable time drift fix. This is ignored for kvm versions newer that 1.0 (not needed anymore).",
- default => 1,
+ description => "Enable/disable time drift fix.",
+ default => 0,
},
localtime => {
optional => 1,
vga => {
optional => 1,
type => 'string',
- description => "Select VGA type. If you want to use high resolution modes (>= 1280x1024x16) then you should use option 'std' or 'vmware'. Default is 'std' for win7/w2k8, and 'cirrur' for other OS types",
+ description => "Select VGA type. If you want to use high resolution modes (>= 1280x1024x16) then you should use option 'std' or 'vmware'. Default is 'std' for win8/win7/w2k8, and 'cirrur' for other OS types",
enum => [qw(std cirrus vmware)],
},
watchdog => {
optional => 1,
description => "Emulated CPU type.",
type => 'string',
- enum => [ qw(486 athlon pentium pentium2 pentium3 coreduo core2duo kvm32 kvm64 qemu32 qemu64 phenom cpu64-rhel6 cpu64-rhel5 Conroe Penryn Nehalem Westmere Opteron_G1 Opteron_G2 Opteron_G3 host) ],
+ enum => [ qw(486 athlon pentium pentium2 pentium3 coreduo core2duo kvm32 kvm64 qemu32 qemu64 phenom Conroe Penryn Nehalem Westmere SandyBridge Haswell Opteron_G1 Opteron_G2 Opteron_G3 Opteron_G4 Opteron_G5 host) ],
default => 'qemu64',
},
parent => get_standard_option('pve-snapshot-name', {
my $tmp = `kvm -help 2>/dev/null`;
- if ($tmp =~ m/^QEMU( PC)? emulator version (\d+\.\d+(\.\d+)?) /) {
+ if ($tmp =~ m/^QEMU( PC)? emulator version (\d+\.\d+(\.\d+)?)[,\s]/) {
$kvm_user_version = $2;
}
w2k8 => 'Windows 2008',
wvista => 'Windows Vista',
win7 => 'Windows 7',
+ win8 => 'Windows 8/2012',
l24 => 'Linux 2.4',
l26 => 'Linux 2.6',
};
}
}
+sub foreach_volid {
+ my ($conf, $func) = @_;
+
+ my $volhash = {};
+
+ my $test_volid = sub {
+ my ($volid, $is_cdrom) = @_;
+
+ return if !$volid;
+
+ $volhash->{$volid} = $is_cdrom || 0;
+ };
+
+ PVE::QemuServer::foreach_drive($conf, sub {
+ my ($ds, $drive) = @_;
+ &$test_volid($drive->{file}, drive_is_cdrom($drive));
+ });
+
+ foreach my $snapname (keys %{$conf->{snapshots}}) {
+ my $snap = $conf->{snapshots}->{$snapname};
+ &$test_volid($snap->{vmstate}, 0);
+ PVE::QemuServer::foreach_drive($snap, sub {
+ my ($ds, $drive) = @_;
+ &$test_volid($drive->{file}, drive_is_cdrom($drive));
+ });
+ }
+
+ foreach my $volid (keys %$volhash) {
+ &$func($volid, $volhash->{$volid});
+ }
+}
+
sub config_to_command {
my ($storecfg, $vmid, $conf, $defaults) = @_;
my $cmd = [];
+ my $globalFlags = [];
+ my $machineFlags = [];
+ my $rtcFlags = [];
my $devices = [];
my $pciaddr = '';
my $bridges = {};
my $vga = $conf->{vga};
if (!$vga) {
- if ($conf->{ostype} && ($conf->{ostype} eq 'win7' || $conf->{ostype} eq 'w2k8')) {
+ if ($conf->{ostype} && ($conf->{ostype} eq 'win8' || $conf->{ostype} eq 'win7' || $conf->{ostype} eq 'w2k8')) {
$vga = 'std';
} else {
$vga = 'cirrus';
# time drift fix
my $tdf = defined($conf->{tdf}) ? $conf->{tdf} : $defaults->{tdf};
- # ignore - no longer supported by newer kvm
- # push @$cmd, '-tdf' if $tdf;
my $nokvm = defined($conf->{kvm}) && $conf->{kvm} == 0 ? 1 : 0;
+ my $useLocaltime = $conf->{localtime};
if (my $ost = $conf->{ostype}) {
- # other, wxp, w2k, w2k3, w2k8, wvista, win7, l24, l26
+ # other, wxp, w2k, w2k3, w2k8, wvista, win7, win8, l24, l26
if ($ost =~ m/^w/) { # windows
- push @$cmd, '-localtime' if !defined($conf->{localtime});
+ $useLocaltime = 1 if !defined($conf->{localtime});
- # use rtc-td-hack when acpi is enabled
+ # use time drift fix when acpi is enabled
if (!(defined($conf->{acpi}) && $conf->{acpi} == 0)) {
- push @$cmd, '-rtc-td-hack';
+ $tdf = 1 if !defined($conf->{tdf});
}
}
- if ($ost eq 'win7' || $ost eq 'w2k8' || $ost eq 'wvista') {
- push @$cmd, '-no-kvm-pit-reinjection';
+ if ($ost eq 'win7' || $ost eq 'win8' || $ost eq 'w2k8' ||
+ $ost eq 'wvista') {
+ push @$globalFlags, 'kvm-pit.lost_tick_policy=discard';
push @$cmd, '-no-hpet';
}
-
- # -tdf ?
- # -no-acpi
- # -no-kvm
- # -win2k-hack ?
}
+ push @$rtcFlags, 'driftfix=slew' if $tdf;
+
if ($nokvm) {
- push @$cmd, '-no-kvm';
+ push @$machineFlags, 'accel=tcg';
} else {
die "No accelerator found!\n" if !$cpuinfo->{hvm};
}
- push @$cmd, '-localtime' if $conf->{localtime};
-
- push @$cmd, '-startdate', $conf->{startdate} if $conf->{startdate};
+ if ($conf->{startdate}) {
+ push @$rtcFlags, "base=$conf->{startdate}";
+ } elsif ($useLocaltime) {
+ push @$rtcFlags, 'base=localtime';
+ }
push @$cmd, '-S' if $conf->{freeze};
}
push @$cmd, @$devices;
+ push @$cmd, '-rtc', join(',', @$rtcFlags)
+ if scalar(@$rtcFlags);
+ push @$cmd, '-machine', join(',', @$machineFlags)
+ if scalar(@$machineFlags);
+ push @$cmd, '-global', join(',', @$globalFlags)
+ if scalar(@$globalFlags);
+
return wantarray ? ($cmd, $vollist) : $cmd;
}
PVE::Storage::activate_volumes($storecfg, $vollist);
- eval { run_command($cmd, timeout => $migrate_port ? undef : 30); };
+ eval { run_command($cmd, timeout => $statefile ? undef : 30,
+ umask => 0077); };
my $err = $@;
die "start failed: $err" if $err;
print "migration listens on port $migrate_port\n" if $migrate_port;
- # always set migrate speed (overwrite kvm default of 32m)
- # we set a very hight default of 8192m which is basically unlimited
- my $migrate_speed = $defaults->{migrate_speed} || 8192;
- $migrate_speed = $conf->{migrate_speed} || $migrate_speed;
- $migrate_speed = $migrate_speed * 1048576;
- eval {
- vm_mon_cmd($vmid, "migrate_set_speed", value => $migrate_speed);
- };
-
- my $migrate_downtime = $defaults->{migrate_downtime};
- $migrate_downtime = $conf->{migrate_downtime} if defined($conf->{migrate_downtime});
- if (defined($migrate_downtime)) {
- eval { vm_mon_cmd($vmid, "migrate_set_downtime", value => $migrate_downtime); };
+ if ($statefile && $statefile ne 'tcp') {
+ eval { vm_mon_cmd_nocheck($vmid, "cont"); };
+ warn $@ if $@;
}
if($migratedfrom) {
eval { PVE::QemuServer::vm_mon_cmd_nocheck($vmid, "migrate-set-capabilities", capabilities => [$capabilities]); };
}
- vm_balloonset($vmid, $conf->{balloon}) if $conf->{balloon};
-
+ vm_mon_cmd_nocheck($vmid, "balloon", value => $conf->{balloon}*1024*1024)
+ if $conf->{balloon};
});
}
my ($conf) = @_;
my $vollist = [];
- foreach_drive($conf, sub {
- my ($ds, $drive) = @_;
+ foreach_volid($conf, sub {
+ my ($volid, $is_cdrom) = @_;
- my ($sid, $volname) = PVE::Storage::parse_volume_id($drive->{file}, 1);
- return if !$sid;
+ return if $volid =~ m|^/|;
- my $volid = $drive->{file};
- return if !$volid || $volid =~ m|^/|;
+ my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
+ return if !$sid;
push @$vollist, $volid;
});
sub vm_balloonset {
my ($vmid, $value) = @_;
- vm_mon_cmd($vmid, "balloon", value => $value);
+ vm_mon_cmd($vmid, "balloon", value => $value*1024*1024);
}
# vzdump restore implementaion
$net->{macaddr} = PVE::Tools::random_ether_addr() if $net->{macaddr};
$netstr = print_net($net);
print $outfd "$id: $netstr\n";
- } elsif ($line =~ m/^((ide|scsi|virtio)\d+):\s*(\S+)\s*$/) {
+ } elsif ($line =~ m/^((ide|scsi|virtio|sata)\d+):\s*(\S+)\s*$/) {
my $virtdev = $1;
my $value = $2;
if ($line =~ m/backup=no/) {
$target = 'local' if !$target;
- my $driver_state_size = 32; # assume 32MB is enough to safe all driver state;
- my $size = $conf->{memory} + $driver_state_size;
+ my $driver_state_size = 500; # assume 32MB is enough to safe all driver state;
+ # we abort live save after $conf->{memory}, so we need at max twice that space
+ my $size = $conf->{memory}*2 + $driver_state_size;
my $name = "vm-$vmid-state-$snapname";
my $scfg = PVE::Storage::storage_config($storecfg, $target);
my $conf = load_config($vmid);
+ $snap = $conf->{snapshots}->{$snapname};
+
+ die "snapshot '$snapname' does not exist\n" if !defined($snap);
+
+ die "unable to rollback to incomplete snapshot (snapstate = $snap->{snapstate})\n"
+ if $snap->{snapstate};
+
if ($prepare) {
check_lock($conf);
vm_stop($storecfg, $vmid, undef, undef, 5, undef, undef);
delete $conf->{lock};
}
- $snap = $conf->{snapshots}->{$snapname};
-
- die "snapshot '$snapname' does not exist\n" if !defined($snap);
-
- die "unable to rollback to incomplete snapshot (snapstate = $snap->{snapstate})\n"
- if $snap->{snapstate};
-
if (!$prepare) {
# copy snapshot config to current config
$conf = &$snapshot_apply_config($conf, $snap);
lock_config($vmid, $updatefn);
}
+my $savevm_wait = sub {
+ my ($vmid) = @_;
+
+ for(;;) {
+ my $stat = PVE::QemuServer::vm_mon_cmd_nocheck($vmid, "query-savevm");
+ if (!$stat->{status}) {
+ die "savevm not active\n";
+ } elsif ($stat->{status} eq 'active') {
+ sleep(1);
+ next;
+ } elsif ($stat->{status} eq 'completed') {
+ last;
+ } else {
+ die "query-savevm returned status '$stat->{status}'\n";
+ }
+ }
+};
+
sub snapshot_create {
my ($vmid, $snapname, $save_vmstate, $freezefs, $comment) = @_;
if ($running) {
if ($snap->{vmstate}) {
my $path = PVE::Storage::path($storecfg, $snap->{vmstate});
- vm_mon_cmd($vmid, "snapshot-start", statefile => $path);
+ vm_mon_cmd($vmid, "savevm-start", statefile => $path);
+ &$savevm_wait($vmid);
} else {
- vm_mon_cmd($vmid, "snapshot-start");
+ vm_mon_cmd($vmid, "savevm-start");
}
};
eval { gqa_unfreezefs($vmid) if $running && $freezefs; };
warn $@ if $@;
- eval { vm_mon_cmd($vmid, "snapshot-end") if $running; };
+ eval { vm_mon_cmd($vmid, "savevm-end") if $running; };
warn $@ if $@;
if ($err) {