diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index 5cbaa820..53855acc 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -293,7 +293,12 @@ __PACKAGE__->register_method({ }, returns => { type => "object", - properties => {}, + properties => { + digest => { + type => 'string', + description => 'SHA1 digest of configuration file. This can be used to prevent concurrent modifications.', + } + }, }, code => sub { my ($param) = @_; @@ -332,6 +337,12 @@ __PACKAGE__->register_method({ optional => 1, requires => 'delete', }, + digest => { + type => 'string', + description => 'Prevent changes if current configuration file has different SHA1 digest. This can be used to prevent concurrent modifications.', + maxLength => 40, + optional => 1, + } }), }, returns => { type => 'null'}, @@ -356,6 +367,8 @@ __PACKAGE__->register_method({ die "no options specified\n" if !$delete && !scalar(keys %$param); + my $digest = extract_param($param, 'digest'); + my $storecfg = PVE::Storage::config(); &$resolve_cdrom_alias($param); @@ -396,6 +409,9 @@ __PACKAGE__->register_method({ my $conf = PVE::QemuServer::load_config($vmid); + die "checksum missmatch (file change by other user?)\n" + if $digest && $digest ne $conf->{digest}; + PVE::QemuServer::check_lock($conf) if !$skiplock; foreach my $opt (keys %$eject) { diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 8613ba3b..8e0352d6 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -1323,7 +1323,9 @@ sub parse_vm_config { return undef if !defined($raw); - my $res = {}; + my $res = { + digest => Digest::SHA1::sha1_hex($raw), + }; $filename =~ m|/qemu-server/(\d+)\.conf$| || die "got strange filename '$filename'"; @@ -1408,6 +1410,7 @@ sub change_config_nolock { my $new_volids = {}; foreach my $key (keys %$settings) { + next if $key eq 'digest'; my $value = $settings->{$key}; if ($key eq 'description') { $value = PVE::Tools::encode_text($value); diff --git a/qm b/qm index 546e08fa..7f572887 100755 --- a/qm +++ b/qm @@ -392,6 +392,7 @@ my $cmddef = { { node => $nodename }, sub { my $config = shift; foreach my $k (sort (keys %$config)) { + next if $k eq 'digest'; my $v = $config->{$k}; if ($k eq 'description') { $v = PVE::Tools::encode_text($v);