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 * 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.", + 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(); + 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. +# 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 + 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); }); } -- 2.20.1 _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel