[pve-devel] [PATCH] check for valid config changes

Stefan Priebe s.priebe at profihost.ag
Fri Sep 5 09:42:47 CEST 2014


currently it is allowed to change all configuration settings for a VM. But may be some
are only allowed if VM is offline or if hotplug is enabled

Signed-off-by: Stefan Priebe <s.priebe at profihost.ag>
---
 PVE/API2/Qemu.pm |  127 +++++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 102 insertions(+), 25 deletions(-)

diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index 985a9f8..bf560cf 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -759,6 +759,8 @@ my $vmconfig_update_disk = sub {
 	$rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']);
     }
 
+    my $running = PVE::QemuServer::check_running($vmid);
+
     if ($conf->{$opt}) {
 
 	if (my $old_drive = PVE::QemuServer::parse_drive($opt, $conf->{$opt}))  {
@@ -770,10 +772,20 @@ my $vmconfig_update_disk = sub {
 	    if (!PVE::QemuServer::drive_is_cdrom($old_drive) &&
 		($drive->{file} ne $old_drive->{file})) {  # delete old disks
 
+		die "Deleting drive '$drive->{file}' not possible while VM is running and hotplug disabled.\n"
+		    if $running && !$conf->{hotplug};
+
 		&$vmconfig_delete_option($rpcenv, $authuser, $conf, $storecfg, $vmid, $opt, $force);
 		$conf = PVE::QemuServer::load_config($vmid); # update/reload
 	    }
 
+	    if ((defined $old_drive->{cache} && defined $drive->{cache} && $old_drive->{cache} ne $drive->{cache}) ||
+		    (!defined $drive->{cache} && defined $old_drive->{cache}) ||
+		    (!defined $old_drive->{cache} && defined $drive->{cache})) {
+		die "Changing cache mode for drive '$opt' not possible while VM is running and hotplug disabled.\n"
+		    if ($running && !PVE::QemuServer::drive_is_cdrom($drive) && !$conf->{hotplug});
+	    }
+
             if(&$safe_num_ne($drive->{mbps}, $old_drive->{mbps}) ||
                &$safe_num_ne($drive->{mbps_rd}, $old_drive->{mbps_rd}) ||
                &$safe_num_ne($drive->{mbps_wr}, $old_drive->{mbps_wr}) ||
@@ -802,6 +814,9 @@ my $vmconfig_update_disk = sub {
 		   if !PVE::QemuServer::drive_is_cdrom($drive);
             }
 	}
+    } else {
+	die "Adding drive '$opt' not possible while VM is running and hotplug disabled.\n"
+	    if ($running && !PVE::QemuServer::drive_is_cdrom($drive) && !$conf->{hotplug});
     }
 
     &$create_disks($rpcenv, $authuser, $conf, $storecfg, $vmid, undef, {$opt => $value});
@@ -812,7 +827,7 @@ my $vmconfig_update_disk = sub {
 
     if (PVE::QemuServer::drive_is_cdrom($drive)) { # cdrom
 
-	if (PVE::QemuServer::check_running($vmid)) {
+	if ($running) {
 	    if ($drive->{file} eq 'none') {
 		PVE::QemuServer::vm_mon_cmd($vmid, "eject",force => JSON::true,device => "drive-$opt");
 	    } else {
@@ -836,8 +851,10 @@ my $vmconfig_update_net = sub {
 	my $newnet = PVE::QemuServer::parse_net($value);
 
 	if($oldnet->{model} ne $newnet->{model}){
+	    die "Changing model is not possible while VM is running and hotplug disabled.\n"
+		if PVE::QemuServer::check_running($vmid) && !$conf->{hotplug};
 	    #if model change, we try to hot-unplug
-            die "error hot-unplug $opt for update" if !PVE::QemuServer::vm_deviceunplug($vmid, $conf, $opt);
+            die "error hot-unplug $opt for update\n" if !PVE::QemuServer::vm_deviceunplug($vmid, $conf, $opt);
 	}else{
 
 	    if($newnet->{bridge} && $oldnet->{bridge}){
@@ -853,11 +870,17 @@ my $vmconfig_update_net = sub {
 		}
 
 	    }else{
+		die "Changing bridged/nat mode is not possible while VM is running and hotplug disabled.\n"
+		    if PVE::QemuServer::check_running($vmid) && !$conf->{hotplug};
 		#if bridge/nat mode change, we try to hot-unplug
-		die "error hot-unplug $opt for update" if !PVE::QemuServer::vm_deviceunplug($vmid, $conf, $opt);
+		die "error hot-unplug $opt for update\n" if !PVE::QemuServer::vm_deviceunplug($vmid, $conf, $opt);
 	    }
 	}
 
+    } else {
+	# new device
+	die "Adding new net device '$opt' not possible while VM is running and hotplug disabled.\n"
+	    if PVE::QemuServer::check_running($vmid) && !$conf->{hotplug};
     }
     $conf->{$opt} = $value;
     PVE::QemuServer::update_config_nolock($vmid, $conf, 1);
@@ -974,50 +997,104 @@ my $update_vm_api  = sub {
 
 	    print "update VM $vmid: " . join (' ', @paramarr) . "\n";
 
-	    foreach my $opt (@delete) { # delete
-		$conf = PVE::QemuServer::load_config($vmid); # update/reload
-		&$vmconfig_delete_option($rpcenv, $authuser, $conf, $storecfg, $vmid, $opt, $force);
-	    }
-
 	    my $running = PVE::QemuServer::check_running($vmid);
 
-	    foreach my $opt (keys %$param) { # add/change
+	    my $conf_param = $param;
+	    foreach my $del (@delete) {
+		die "You cannot update/add and delete at the same time '$del'\n" if exists $conf_param->{$del};
+		$conf_param->{$del} = undef;
+	    }
+
+	    foreach my $opt (keys %$conf_param) { # add/change/delete
 
 		$conf = PVE::QemuServer::load_config($vmid); # update/reload
 
-		next if $conf->{$opt} && ($param->{$opt} eq $conf->{$opt}); # skip if nothing changed
+		next if ((defined $conf->{$opt} && defined $conf_param->{$opt} && $conf_param->{$opt} eq $conf->{$opt}) ||
+			    (!defined $conf->{$opt} && !defined $conf_param->{$opt})); # skip if nothing changed
 
-		if (PVE::QemuServer::valid_drivename($opt)) {
+		# ignore sockets setting if maxcpu is set
+		next if ($opt eq "sockets" && $conf->{maxcpus});
+
+		my $need_hotplug = 0;
+		my $need_offlinevm = 0;
+
+		if ($opt eq 'tablet') {
+		    # special handling for tablet option
+
+		    if ($conf_param->{$opt}) {
+			PVE::QemuServer::vm_deviceplug(undef, $conf, $vmid, $opt);
+		    } else {
+			PVE::QemuServer::vm_deviceunplug($vmid, $conf, $opt);
+		    }
+		    next;
 
-		    &$vmconfig_update_disk($rpcenv, $authuser, $conf, $storecfg, $vmid,
-					   $opt, $param->{$opt}, $force);
+		} elsif (PVE::QemuServer::valid_drivename($opt)) {
+
+		    if (defined $conf_param->{$opt}) {
+			&$vmconfig_update_disk($rpcenv, $authuser, $conf, $storecfg, $vmid,
+					$opt, $conf_param->{$opt}, $force);
+			next;
+		    }
+		    $need_hotplug = 1 if ($opt !~ /^unused/);
 
 		} elsif ($opt =~ m/^net(\d+)$/) { #nics
 
-		    &$vmconfig_update_net($rpcenv, $authuser, $conf, $storecfg, $vmid,
-					  $opt, $param->{$opt});
+		    if (defined $conf_param->{$opt}) {
+			&$vmconfig_update_net($rpcenv, $authuser, $conf, $storecfg, $vmid,
+					$opt, $conf_param->{$opt});
+			next;
+		    }
+		    $need_hotplug = 1; # for delete / remove
 
 		} else {
+		    # all options which do not update config on their own
 
-		    if($opt eq 'tablet' && $param->{$opt} == 1){
-			PVE::QemuServer::vm_deviceplug(undef, $conf, $vmid, $opt);
-		    } elsif($opt eq 'tablet' && $param->{$opt} == 0){
-			PVE::QemuServer::vm_deviceunplug($vmid, $conf, $opt);
+		    if ($opt =~ /^(cores)$/) {
+			# options only possible to change if hotplug is enabled
+			$need_hotplug = 1;
+
+		    } elsif ($opt =~ /^(balloon)$/ && length $conf->{$opt}) {
+
+			# options always beeing able to change IF they already exist config
+
+		    } elsif ($opt =~ /^(onboot|name|boot|shares|lock|startup|description)$/) {
+
+			# options always beeing able to change
+
+		    } else {
+
+			# options only possible to change if the vm isn't running
+			$need_offlinevm = 1;
 		    }
-		
-		    if($opt eq 'cores' && $conf->{maxcpus}){
-			PVE::QemuServer::qemu_cpu_hotplug($vmid, $conf, $param->{$opt});
+		}
+
+		my $type = (!defined $conf_param->{$opt}) ? "Removing" : "Changing";
+		if ($running && $need_hotplug && !$conf->{hotplug}) {
+		    die "$type '$opt' not possible while VM is running and hotplug disabled.\n";
+		}
+		if ($running && $need_offlinevm) {
+		    die "$type '$opt' not possible while VM is running.\n";
+		}
+
+		if (!defined $conf_param->{$opt}) {
+
+		    # undefined values are "delete" values
+		    &$vmconfig_delete_option($rpcenv, $authuser, $conf, $storecfg, $vmid, $opt, $force);
+		} else {
+
+		    if ($opt eq 'cores' && $conf->{maxcpus}){
+			PVE::QemuServer::qemu_cpu_hotplug($vmid, $conf, $conf_param->{$opt});
 		    }
 
-		    $conf->{$opt} = $param->{$opt};
+		    $conf->{$opt} = $conf_param->{$opt};
 		    PVE::QemuServer::update_config_nolock($vmid, $conf, 1);
 		}
 	    }
 
 	    # allow manual ballooning if shares is set to zero
-	    if ($running && defined($param->{balloon}) &&
+	    if ($running && defined($conf_param->{balloon}) &&
 		defined($conf->{shares}) && ($conf->{shares} == 0)) {
-		my $balloon = $param->{'balloon'} || $conf->{memory} || $defaults->{memory};
+		my $balloon = $conf_param->{'balloon'} || $conf->{memory} || $defaults->{memory};
 		PVE::QemuServer::vm_mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
 	    }
 	};
-- 
1.7.10.4




More information about the pve-devel mailing list