[pve-devel] [PATCH qemu-server v2] fix #1908: add vmgenid config/device

Dominik Csapak d.csapak at proxmox.com
Tue Sep 18 15:24:59 CEST 2018


this adds a VM Generation ID device uses by Windows (Server) to determine
some specific actions that may have happened with the vm
such as rollback, restore, etc.

see:

https://docs.microsoft.com/en-us/windows/desktop/hyperv_v2/virtual-machine-generation-identifier

for details on how it works and when it should change

Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
---
changes from v1:
* use a property string with 'enabled' and 'uuid'
* add it by default on creation
* generate the uuid explicitely on change instead of setting it to a magic
  value every time
* also generate one on importovf

tested with windows 2016 ad ds, works as expected

 PVE/API2/Qemu.pm  | 15 ++++++++----
 PVE/CLI/qm.pm     |  1 +
 PVE/QemuConfig.pm |  5 ++++
 PVE/QemuServer.pm | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 4 files changed, 87 insertions(+), 6 deletions(-)

diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index c1cc01b..eb5b91c 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -599,6 +599,10 @@ __PACKAGE__->register_method({
 			$conf->{smbios1} = PVE::QemuServer::generate_smbios1_uuid();
 		    }
 
+		    if (!$conf->{vmgenid}) {
+			$conf->{vmgenid} = PVE::QemuServer::generate_vmgenid();
+		    }
+
 		    PVE::QemuConfig->write_config($vmid, $conf);
 
 		};
@@ -2725,13 +2729,16 @@ __PACKAGE__->register_method({
 	    }
 
             # auto generate a new uuid
-            my ($uuid, $uuid_str);
-            UUID::generate($uuid);
-            UUID::unparse($uuid, $uuid_str);
 	    my $smbios1 = PVE::QemuServer::parse_smbios1($newconf->{smbios1} || '');
-	    $smbios1->{uuid} = $uuid_str;
+	    $smbios1->{uuid} = PVE::QemuServer::generate_uuid();
 	    $newconf->{smbios1} = PVE::QemuServer::print_smbios1($smbios1);
 
+	    # auto generate a new vmgenid if the option was set
+	    if (my $vmgenid = PVE::QemuServer::parse_vmgenid($newconf->{vmgenid})) {
+		$vmgenid->{uuid} = PVE::QemuServer::generate_uuid();
+		$newconf->{vmgenid} = PVE::QemuServer::print_vmgenid($vmgenid);
+	    }
+
 	    delete $newconf->{template};
 
 	    if ($param->{name}) {
diff --git a/PVE/CLI/qm.pm b/PVE/CLI/qm.pm
index 84b8531..5ea390b 100755
--- a/PVE/CLI/qm.pm
+++ b/PVE/CLI/qm.pm
@@ -621,6 +621,7 @@ __PACKAGE__->register_method ({
 
 	    eval {
 		# order matters, as do_import() will load_config() internally
+		$conf->{vmgenid} = PVE::QemuServer::generate_vmgenid();
 		$conf->{smbios1} = PVE::QemuServer::generate_smbios1_uuid();
 		PVE::QemuConfig->write_config($vmid, $conf);
 
diff --git a/PVE/QemuConfig.pm b/PVE/QemuConfig.pm
index cd116bd..e39aaad 100644
--- a/PVE/QemuConfig.pm
+++ b/PVE/QemuConfig.pm
@@ -300,6 +300,11 @@ sub __snapshot_rollback_hook {
 	    # in the original config.
 	    delete $conf->{machine} if $snap->{vmstate} && !defined($data->{oldmachine});
 	}
+
+	if (my $vmgenid = PVE::QemuServer::parse_vmgenid($conf->{vmgenid})) {
+	    $vmgenid->{uuid} = PVE::QemuServer::generate_uuid();
+	    $conf->{vmgenid} = PVE::QemuServer::print_vmgenid($vmgenid);
+	}
     }
 
     return;
diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index af0631d..b150648 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -559,6 +559,11 @@ EODESCR
 	description => "Select BIOS implementation.",
 	default => 'seabios',
     },
+    vmgenid => {
+	description => "Specify VM Generation ID fields.",
+	type => 'string', format => 'pve-qm-vmgenid',
+	optional => 1,
+    },
 };
 
 my $confdesc_cloudinit = {
@@ -2210,6 +2215,39 @@ sub print_smbios1 {
 
 PVE::JSONSchema::register_format('pve-qm-smbios1', $smbios1_fmt);
 
+# vmgenid: [enabled=bool][,uuid=uuid]
+my $vmgenid_fmt = {
+    uuid => {
+	type => 'string',
+	pattern => '[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}',
+	format_description => 'UUID',
+        description => "Set VM Generation ID UUID.",
+	optional => 1,
+    },
+    enabled => {
+	default_key => 1,
+	type => 'boolean',
+	description => "Enables or disables the vmgenid device.",
+	default => 1,
+    },
+};
+
+sub parse_vmgenid {
+    my ($data) = @_;
+
+    return undef if !$data;
+    my $res = eval { PVE::JSONSchema::parse_property_string($vmgenid_fmt, $data) };
+    warn $@ if $@;
+    return $res;
+}
+
+sub print_vmgenid {
+    my ($vmgenid) = @_;
+    return PVE::JSONSchema::print_property_string($vmgenid, $vmgenid_fmt);
+}
+
+PVE::JSONSchema::register_format('pve-qm-vmgenid', $vmgenid_fmt);
+
 PVE::JSONSchema::register_format('pve-qm-bootdisk', \&verify_bootdisk);
 sub verify_bootdisk {
     my ($value, $noerr) = @_;
@@ -3191,6 +3229,10 @@ sub config_to_command {
 	push @$cmd, '-smbios', "type=1,$conf->{smbios1}";
     }
 
+    if (my $vmgenid = parse_vmgenid($conf->{vmgenid})) {
+	push @$devices, '-device', 'vmgenid,guid='.$vmgenid->{uuid} if $vmgenid->{enabled};
+    }
+
     if ($conf->{bios} && $conf->{bios} eq 'ovmf') {
 	die "uefi base image not found\n" if ! -f $OVMF_CODE;
 
@@ -4593,6 +4635,17 @@ sub vmconfig_apply_pending {
     }
 }
 
+sub vmconfig_apply_vmgenid_generation {
+    my ($vmid, $conf) = @_;
+    if (my $vmgenid = parse_vmgenid($conf->{vmgenid})) {
+	if ($vmgenid->{enabled} && !$vmgenid->{uuid}) {
+	    $vmgenid->{uuid} = generate_uuid();
+	    $conf->{vmgenid} = print_vmgenid($vmgenid);
+	    PVE::QemuConfig->write_config($vmid, $conf);
+	}
+    }
+}
+
 my $safe_num_ne = sub {
     my ($a, $b) = @_;
 
@@ -4779,6 +4832,8 @@ sub vm_start {
 
 	die "VM $vmid already running\n" if check_running($vmid, undef, $migratedfrom);
 
+	vmconfig_apply_vmgenid_generation($vmid, $conf);
+
 	if (!$statefile && scalar(keys %{$conf->{pending}})) {
 	    vmconfig_apply_pending($vmid, $conf, $storecfg);
 	    $conf = PVE::QemuConfig->load_config($vmid); # update/reload
@@ -5554,6 +5609,11 @@ sub restore_update_config_line {
 	} else {
 	    print $outfd $line;
 	}
+    } elsif (($line =~ m/^(vmgenid: )(.*)/)) {
+	# always generate a new vmgenid
+	my $vmgenid = parse_vmgenid($2);
+	$vmgenid->{uuid} = generate_uuid();
+	print $outfd $1.print_vmgenid($vmgenid)."\n";
     } elsif (($line =~ m/^(smbios1: )(.*)/) && $unique) {
 	my ($uuid, $uuid_str);
 	UUID::generate($uuid);
@@ -6756,11 +6816,19 @@ sub resolve_first_disk {
     return $firstdisk;
 }
 
-sub generate_smbios1_uuid {
+sub generate_uuid {
     my ($uuid, $uuid_str);
     UUID::generate($uuid);
     UUID::unparse($uuid, $uuid_str);
-    return "uuid=$uuid_str";
+    return $uuid_str;
+}
+
+sub generate_smbios1_uuid {
+    return "uuid=".generate_uuid();
+}
+
+sub generate_vmgenid {
+    return "enabled=1,uuid=".generate_uuid();
 }
 
 # bash completion helper
-- 
2.11.0




More information about the pve-devel mailing list