}
};
-my $vmconfig_update_net = sub {
- my ($rpcenv, $authuser, $conf, $storecfg, $vmid, $opt, $value) = @_;
-
- if ($conf->{$opt} && PVE::QemuServer::check_running($vmid)) {
- my $oldnet = PVE::QemuServer::parse_net($conf->{$opt});
- my $newnet = PVE::QemuServer::parse_net($value);
-
- if($oldnet->{model} ne $newnet->{model}){
- #if model change, we try to hot-unplug
- die "error hot-unplug $opt for update" if !PVE::QemuServer::vm_deviceunplug($vmid, $conf, $opt);
- }else{
-
- if($newnet->{bridge} && $oldnet->{bridge}){
- my $iface = "tap".$vmid."i".$1 if $opt =~ m/net(\d+)/;
-
- if($newnet->{rate} ne $oldnet->{rate}){
- PVE::Network::tap_rate_limit($iface, $newnet->{rate});
- }
-
- if(($newnet->{bridge} ne $oldnet->{bridge}) || ($newnet->{tag} ne $oldnet->{tag}) || ($newnet->{firewall} ne $oldnet->{firewall})){
- PVE::Network::tap_unplug($iface);
- PVE::Network::tap_plug($iface, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall});
- }
-
- }else{
- #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);
- }
- }
-
- }
- $conf->{$opt} = $value;
- PVE::QemuServer::update_config_nolock($vmid, $conf, 1);
- $conf = PVE::QemuServer::load_config($vmid); # update/reload
-
- my $net = PVE::QemuServer::parse_net($conf->{$opt});
-
- die "error hotplug $opt" if !PVE::QemuServer::vm_deviceplug($storecfg, $conf, $vmid, $opt, $net);
-};
-
# POST/PUT {vmid}/config implementation
#
# The original API used PUT (idempotent) an we assumed that all operations
} elsif ($opt =~ m/^net(\d+)$/) { #nics
- &$vmconfig_update_net($rpcenv, $authuser, $conf, $storecfg, $vmid,
- $opt, $param->{$opt});
+ # &$vmconfig_update_net($rpcenv, $authuser, $conf, $storecfg, $vmid,
+ # $opt, $param->{$opt});
} else {
return $devices;
}
+# fixme: this should raise exceptions on error!
sub vm_deviceplug {
my ($storecfg, $conf, $vmid, $deviceid, $device) = @_;
return undef if !qemu_findorcreatescsihw($storecfg,$conf, $vmid, $device);
return undef if !qemu_driveadd($storecfg, $vmid, $device);
my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device);
- if(!qemu_deviceadd($vmid, $devicefull)) {
+ if(!qemu_deviceadd($vmid, $devicefull)) { # fixme: use qemu_deviceaddverify?
qemu_drivedel($vmid, $deviceid);
return undef;
}
return 1;
}
+# fixme: this should raise exceptions on error!
sub vm_deviceunplug {
my ($vmid, $conf, $deviceid) = @_;
} elsif ($opt eq 'cores') {
die "skip\n" if !$hotplug;
qemu_cpu_hotplug($vmid, $conf, 1);
+ } elsif ($opt =~ m/^net(\d+)$/) {
+ die "skip\n" if !$hotplug;
+ vm_deviceunplug($vmid, $conf, $opt);
} else {
die "skip\n";
}
# allow manual ballooning if shares is set to zero
my $balloon = $conf->{pending}->{balloon} || $conf->{memory} || $defaults->{memory};
vm_mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
+ } elsif ($opt =~ m/^net(\d+)$/) {
+ # some changes can be done without hotplug
+ vmconfig_update_net($storecfg, $conf, $vmid, $opt, $value);
} else {
die "skip\n"; # skip non-hot-pluggable options
}
}
}
+my $safe_num_ne = sub {
+ my ($a, $b) = @_;
+
+ return 0 if !defined($a) && !defined($b);
+ return 1 if !defined($a);
+ return 1 if !defined($b);
+
+ return $a != $b;
+};
+
+my $safe_string_ne = sub {
+ my ($a, $b) = @_;
+
+ return 0 if !defined($a) && !defined($b);
+ return 1 if !defined($a);
+ return 1 if !defined($b);
+
+ return $a ne $b;
+};
+
+sub vmconfig_update_net {
+ my ($storecfg, $conf, $vmid, $opt, $value) = @_;
+
+ my $newnet = parse_net($value);
+
+ if ($conf->{$opt}) {
+ my $oldnet = parse_net($conf->{$opt});
+
+ if (&$safe_string_ne($oldnet->{model}, $newnet->{model}) ||
+ &$safe_string_ne($oldnet->{macaddr}, $newnet->{macaddr}) ||
+ &$safe_num_ne($oldnet->{queues}, $newnet->{queues}) ||
+ !($newnet->{bridge} && $oldnet->{bridge})) { # bridge/nat mode change
+
+ # for non online change, we try to hot-unplug
+ die "skip\n" if !$conf->{hotplug};
+ vm_deviceunplug($vmid, $conf, $opt);
+ } else {
+
+ die "internal error" if $opt !~ m/net(\d+)/;
+ my $iface = "tap${vmid}i$1";
+
+ if (&$safe_num_ne($oldnet->{rate}, $newnet->{rate})) {
+ PVE::Network::tap_rate_limit($iface, $newnet->{rate});
+ }
+
+ if(&$safe_string_ne($oldnet->{bridge}, $newnet->{bridge}) ||
+ &$safe_num_ne($oldnet->{tag}, $newnet->{tag}) ||
+ &$safe_num_ne($oldnet->{firewall}, $newnet->{firewall})) {
+ PVE::Network::tap_unplug($iface);
+ PVE::Network::tap_plug($iface, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall});
+ }
+ }
+ }
+
+ vm_deviceplug($storecfg, $conf, $vmid, $opt, $newnet);
+}
+
sub vm_start {
my ($storecfg, $vmid, $statefile, $skiplock, $migratedfrom, $paused, $forcemachine, $spice_ticket) = @_;