On 06.09.19 14:24, Dominik Csapak wrote: > this creates a reboot trigger file (inspired by pve-container) > and relies on the 'qm cleanup' call by the qmeventd to detect > and restart the vm afterwards > > includes some refactoring of the 'vm_stop' call, so that > we can have a clean 'vm_reboot' sub without the many parameters > of 'vm_stop' > > Signed-off-by: Dominik Csapak <d.csa...@proxmox.com> > --- > better view with '-w' as it is mostly code shift > > changes from v2: > * refactored vm_stop so that the code can be reused in vm_reboot
looks better, but I'd done the refactoring in a separate no-semantic-change patch first. > * better function signature > * add vm running check in api, such that it cannot be called on > stopped vms (which would not start the vm) > > PVE/API2/Qemu.pm | 60 +++++++++++++++++ > PVE/CLI/qm.pm | 2 + > PVE/QemuServer.pm | 162 ++++++++++++++++++++++++++-------------------- > 3 files changed, 153 insertions(+), 71 deletions(-) > > diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm > index 9db8967..fb63cab 100644 > --- a/PVE/API2/Qemu.pm > +++ b/PVE/API2/Qemu.pm > @@ -1910,6 +1910,7 @@ __PACKAGE__->register_method({ > { subdir => 'reset' }, > { subdir => 'shutdown' }, > { subdir => 'suspend' }, > + { subdir => 'reboot' }, > ]; > > return $res; > @@ -2333,6 +2334,65 @@ __PACKAGE__->register_method({ > } > }}); > > +__PACKAGE__->register_method({ > + name => 'vm_reboot', > + path => '{vmid}/status/reboot', > + method => 'POST', > + protected => 1, > + proxyto => 'node', > + description => "Reboot the VM by shutting it down, and starting it > again.", we could add a hint that thus pending changes get applied? Or do you think that this is clear and expected? > + permissions => { > + check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]], > + }, > + parameters => { > + additionalProperties => 0, > + properties => { > + node => get_standard_option('pve-node'), > + vmid => get_standard_option('pve-vmid', > + { completion => > \&PVE::QemuServer::complete_vmid_running }), > + timeout => { > + description => "Wait maximal timeout seconds for the shutdown.", > + type => 'integer', > + minimum => 0, > + optional => 1, > + }, > + }, > + }, > + returns => { > + type => 'string', > + }, > + code => sub { > + my ($param) = @_; > + > + my $rpcenv = PVE::RPCEnvironment::get(); > + my $authuser = $rpcenv->get_user(); > + > + my $node = extract_param($param, 'node'); > + my $vmid = extract_param($param, 'vmid'); > + > + my $qmpstatus = eval { > + PVE::QemuServer::vm_qmp_command($vmid, { execute => "query-status" > }, 0); > + }; > + my $err = $@ if $@; > + > + if (!$err && $qmpstatus->{status} eq "paused") { > + die "VM is paused - cannot shutdown\n"; > + } > + > + die "VM $vmid not running\n" if !PVE::QemuServer::check_running($vmid); > + > + my $realcmd = sub { > + my $upid = shift; > + > + syslog('info', "requesting reboot of VM $vmid: $upid\n"); > + my $storecfg = PVE::Storage::config(); hmm, I'd maybe move the storecfg read into vm_reboot, removing it from the method parameter signature? As nothing else is done with it here anyway? > + PVE::QemuServer::vm_reboot($storecfg, $vmid, $param->{timeout}); > + return; > + }; > + > + return $rpcenv->fork_worker('qmreboot', $vmid, $authuser, $realcmd); > + }}); > + > __PACKAGE__->register_method({ > name => 'vm_suspend', > path => '{vmid}/status/suspend', > diff --git a/PVE/CLI/qm.pm b/PVE/CLI/qm.pm > index dea3deb..3ec8a8c 100755 > --- a/PVE/CLI/qm.pm > +++ b/PVE/CLI/qm.pm > @@ -1000,6 +1000,8 @@ our $cmddef = { > > shutdown => [ "PVE::API2::Qemu", 'vm_shutdown', ['vmid'], { node => > $nodename }, $upid_exit ], > > + reboot => [ "PVE::API2::Qemu", 'vm_reboot', ['vmid'], { node => > $nodename }, $upid_exit ], > + > suspend => [ "PVE::API2::Qemu", 'vm_suspend', ['vmid'], { node => > $nodename }, $upid_exit ], > > resume => [ "PVE::API2::Qemu", 'vm_resume', ['vmid'], { node => > $nodename }, $upid_exit ], > diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm > index ba83b10..a1d4969 100644 > --- a/PVE/QemuServer.pm > +++ b/PVE/QemuServer.pm > @@ -5773,85 +5773,42 @@ sub vm_stop_cleanup { > warn $@ if $@; # avoid errors - just warn > } > > -# Note: use $nockeck to skip tests if VM configuration file exists. > -# We need that when migration VMs to other nodes (files already moved) > -# Note: we set $keepActive in vzdump stop mode - volumes need to stay active > -sub vm_stop { > - my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, > $keepActive, $migratedfrom) = @_; > +# call only in locked context > +sub _do_vm_stop { > + my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, > $keepActive) = @_; > > - $force = 1 if !defined($force) && !$shutdown; > + my $pid = check_running($vmid, $nocheck); > + return if !$pid; > > - if ($migratedfrom){ > - my $pid = check_running($vmid, $nocheck, $migratedfrom); > - kill 15, $pid if $pid; > - my $conf = PVE::QemuConfig->load_config($vmid, $migratedfrom); > - vm_stop_cleanup($storecfg, $vmid, $conf, $keepActive, 0); > - return; > - } > - > - PVE::QemuConfig->lock_config($vmid, sub { > - > - my $pid = check_running($vmid, $nocheck); > - return if !$pid; > - > - my $conf; > - if (!$nocheck) { > - $conf = PVE::QemuConfig->load_config($vmid); > - PVE::QemuConfig->check_lock($conf) if !$skiplock; > - if (!defined($timeout) && $shutdown && $conf->{startup}) { > - my $opts = > PVE::JSONSchema::pve_parse_startup_order($conf->{startup}); > - $timeout = $opts->{down} if $opts->{down}; > - } > - PVE::GuestHelpers::exec_hookscript($conf, $vmid, 'pre-stop'); > + my $conf; > + if (!$nocheck) { > + $conf = PVE::QemuConfig->load_config($vmid); > + PVE::QemuConfig->check_lock($conf) if !$skiplock; > + if (!defined($timeout) && $shutdown && $conf->{startup}) { > + my $opts = > PVE::JSONSchema::pve_parse_startup_order($conf->{startup}); > + $timeout = $opts->{down} if $opts->{down}; > } > + PVE::GuestHelpers::exec_hookscript($conf, $vmid, 'pre-stop'); > + } > > - eval { > - if ($shutdown) { > - if (defined($conf) && parse_guest_agent($conf)->{enabled}) { > - vm_qmp_command($vmid, { > + eval { > + if ($shutdown) { > + if (defined($conf) && parse_guest_agent($conf)->{enabled}) { > + vm_qmp_command($vmid, { > execute => "guest-shutdown", > arguments => { timeout => $timeout } > }, $nocheck); > - } else { > - vm_qmp_command($vmid, { execute => "system_powerdown" }, > $nocheck); > - } > - } else { > - vm_qmp_command($vmid, { execute => "quit" }, $nocheck); > - } > - }; > - my $err = $@; > - > - if (!$err) { > - $timeout = 60 if !defined($timeout); > - > - my $count = 0; > - while (($count < $timeout) && check_running($vmid, $nocheck)) { > - $count++; > - sleep 1; > - } > - > - if ($count >= $timeout) { > - if ($force) { > - warn "VM still running - terminating now with SIGTERM\n"; > - kill 15, $pid; > - } else { > - die "VM quit/powerdown failed - got timeout\n"; > - } > } else { > - vm_stop_cleanup($storecfg, $vmid, $conf, $keepActive, 1) if > $conf; > - return; > + vm_qmp_command($vmid, { execute => "system_powerdown" }, > $nocheck); > } > } else { > - if ($force) { > - warn "VM quit/powerdown failed - terminating now with > SIGTERM\n"; > - kill 15, $pid; > - } else { > - die "VM quit/powerdown failed\n"; > - } > + vm_qmp_command($vmid, { execute => "quit" }, $nocheck); > } > + }; > + my $err = $@; > > - # wait again > - $timeout = 10; > + if (!$err) { > + $timeout = 60 if !defined($timeout); > > my $count = 0; > while (($count < $timeout) && check_running($vmid, $nocheck)) { > @@ -5860,12 +5817,75 @@ sub vm_stop { > } > > if ($count >= $timeout) { > - warn "VM still running - terminating now with SIGKILL\n"; > - kill 9, $pid; > - sleep 1; > + if ($force) { > + warn "VM still running - terminating now with SIGTERM\n"; > + kill 15, $pid; > + } else { > + die "VM quit/powerdown failed - got timeout\n"; > + } > + } else { > + vm_stop_cleanup($storecfg, $vmid, $conf, $keepActive, 1) if $conf; > + return; > } > + } else { > + if ($force) { > + warn "VM quit/powerdown failed - terminating now with SIGTERM\n"; > + kill 15, $pid; > + } else { > + die "VM quit/powerdown failed\n"; > + } > + } > + > + # wait again > + $timeout = 10; > + > + my $count = 0; > + while (($count < $timeout) && check_running($vmid, $nocheck)) { > + $count++; > + sleep 1; > + } > + > + if ($count >= $timeout) { > + warn "VM still running - terminating now with SIGKILL\n"; > + kill 9, $pid; > + sleep 1; > + } > + > + vm_stop_cleanup($storecfg, $vmid, $conf, $keepActive, 1) if $conf; > +} > + > +# Note: use $nockeck to skip tests if VM configuration file exists. s/nockeck/nocheck > +# We need that when migration VMs to other nodes (files already moved) > +# Note: we set $keepActive in vzdump stop mode - volumes need to stay active > +sub vm_stop { > + my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, > $keepActive, $migratedfrom) = @_; > + > + $force = 1 if !defined($force) && !$shutdown; > + > + if ($migratedfrom){ > + my $pid = check_running($vmid, $nocheck, $migratedfrom); > + kill 15, $pid if $pid; > + my $conf = PVE::QemuConfig->load_config($vmid, $migratedfrom); > + vm_stop_cleanup($storecfg, $vmid, $conf, $keepActive, 0); > + return; > + } > + > + PVE::QemuConfig->lock_config($vmid, sub { > + _do_vm_stop($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, > $force, $keepActive); > + }); > +} > + > +sub vm_reboot { > + my ($storecfg, $vmid, $timeout) = @_; > + > + PVE::QemuConfig->lock_config($vmid, sub { > + # do not request reboot if it is not running, since > + # it can only start again if it qmeventd detects that it stopped double negation, maybe: # only reboot if running, as a qmeventd stop event starts it again > + return if !check_running($vmid); > + > + create_reboot_request($vmid); > > - vm_stop_cleanup($storecfg, $vmid, $conf, $keepActive, 1) if $conf; > + _do_vm_stop($storecfg, $vmid, undef, undef, $timeout, 1); > }); > } > > _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel