From: Dan Hunsaker <danhunsa...@gmail.com> As discussed in a previous thread, following is a patch to support container suspend (via vzctl chkpnt) and resume (via vzctl restore).
- Added /nodes/{node}/openvz/{vmid}/status/suspend to API - Added /nodes/{node}/openvz/{vmid}/status/resume to API - Adapted vm_suspend/vm_resume from PVE/QemuServer.pm into PVE/OpenVZ.pm - Removed locking since vzctl already does this for us, and the locks conflict with each other (container already locked) - Changed monitor commands to run_command(vzctl) calls - Refuse to suspend if CT is offline - Refuse to resume if CT is online - vzctl does these checks as well, but it doesn't really hurt to have them This was great, but there were artifacts in the web UI - specifically, the task descriptions were unformatted. So, I moved over to www/manager/Utils.js: - Added descriptions for vzsuspend and vzresume tasks in web UI And while I was working with the web UI anyway: - Added suspend/resume options to CmdMenu for both OpenVZ and QEMU guests - Confirm suspend before proceeding - No confirm on resume, since it's a startup action - Fixed OpenVZ CmdMenu shutdown and stop confirmation prompts to refer to CTs I considered adding these options to the toolbar, but there are enough options there already that it can get crowded quick in smaller browser windows (such as the ones I tend to use, for screen real estate purposes), so I opted against that. REVISION: Between the original version of this patch and the present, mobile support was added, so I went into www/mobile/(OpenVZ|QEMU)Summary.js and added the suspend and resume options there as well. No confirmation this time, since stop and shutdown don't bother with it either in the mobile interface. I also did a cursory search for other places where suspend/resume commands might be useful, and added them to bin/pvectl. If I've missed any other spots, I'll gladly add the commands to them, as well. Signed-off-by: Dan Hunsaker <danhunsa...@gmail.com> --- PVE/API2/OpenVZ.pm | 96 +++++++++++++++++++++++++++++++++++++++++++ PVE/OpenVZ.pm | 26 +++++++++++- bin/pvectl | 2 + www/manager/Utils.js | 2 + www/manager/openvz/CmdMenu.js | 24 ++++++++++- www/manager/qemu/CmdMenu.js | 20 +++++++++ www/mobile/OpenVzSummary.js | 12 ++++++ www/mobile/QemuSummary.js | 12 ++++++ 8 files changed, 191 insertions(+), 3 deletions(-) diff --git a/PVE/API2/OpenVZ.pm b/PVE/API2/OpenVZ.pm index 184ebdf..5d8c0c6 100644 --- a/PVE/API2/OpenVZ.pm +++ b/PVE/API2/OpenVZ.pm @@ -1459,6 +1459,102 @@ __PACKAGE__->register_method({ }}); __PACKAGE__->register_method({ + name => 'vm_suspend', + path => '{vmid}/status/suspend', + method => 'POST', + protected => 1, + proxyto => 'node', + description => "Suspend the container.", + permissions => { + check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]], + }, + parameters => { + additionalProperties => 0, + properties => { + node => get_standard_option('pve-node'), + vmid => get_standard_option('pve-vmid'), + }, + }, + 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'); + + die "CT $vmid not running\n" if !PVE::OpenVZ::check_running($vmid); + + my $realcmd = sub { + my $upid = shift; + + syslog('info', "suspend CT $vmid: $upid\n"); + + PVE::OpenVZ::vm_suspend($vmid); + + return; + }; + + my $upid = $rpcenv->fork_worker('vzsuspend', $vmid, $authuser, $realcmd); + + return $upid; + }}); + +__PACKAGE__->register_method({ + name => 'vm_resume', + path => '{vmid}/status/resume', + method => 'POST', + protected => 1, + proxyto => 'node', + description => "Resume the container.", + permissions => { + check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]], + }, + parameters => { + additionalProperties => 0, + properties => { + node => get_standard_option('pve-node'), + vmid => get_standard_option('pve-vmid'), + }, + }, + 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'); + + die "CT $vmid already running\n" if PVE::OpenVZ::check_running($vmid); + + my $realcmd = sub { + my $upid = shift; + + syslog('info', "resume CT $vmid: $upid\n"); + + PVE::OpenVZ::vm_resume($vmid); + + return; + }; + + my $upid = $rpcenv->fork_worker('vzresume', $vmid, $authuser, $realcmd); + + return $upid; + }}); + +__PACKAGE__->register_method({ name => 'migrate_vm', path => '{vmid}/migrate', method => 'POST', diff --git a/PVE/OpenVZ.pm b/PVE/OpenVZ.pm index aa6f502..fcfb0c2 100644 --- a/PVE/OpenVZ.pm +++ b/PVE/OpenVZ.pm @@ -6,7 +6,7 @@ use File::stat qw(); use POSIX qw (LONG_MAX); use IO::Dir; use IO::File; -use PVE::Tools qw(extract_param $IPV6RE $IPV4RE); +use PVE::Tools qw(run_command extract_param $IPV6RE $IPV4RE); use PVE::ProcFSTools; use PVE::Cluster qw(cfs_register_file cfs_read_file); use PVE::SafeSyslog; @@ -1220,6 +1220,30 @@ sub lock_container { return $res; } +sub vm_suspend { + my ($vmid) = @_; + + my $cmd = ['vzctl', 'chkpnt', $vmid]; + + eval { run_command($cmd); }; + if (my $err = $@) { + syslog("err", "CT $vmid suspend failed - $err"); + die $err; + } +} + +sub vm_resume { + my ($vmid) = @_; + + my $cmd = ['vzctl', 'restore', $vmid]; + + eval { run_command($cmd); }; + if (my $err = $@) { + syslog("err", "CT $vmid resume failed - $err"); + die $err; + } +} + sub replacepw { my ($file, $epw) = @_; diff --git a/bin/pvectl b/bin/pvectl index f8ae3ad..9e9a797 100755 --- a/bin/pvectl +++ b/bin/pvectl @@ -74,6 +74,8 @@ my $cmddef = { }], start => [ 'PVE::API2::OpenVZ', 'vm_start', ['vmid'], { node => $nodename }, $upid_exit], + suspend => [ 'PVE::API2::OpenVZ', 'vm_suspend', ['vmid'], { node => $nodename }, $upid_exit], + resume => [ 'PVE::API2::OpenVZ', 'vm_resume', ['vmid'], { node => $nodename }, $upid_exit], shutdown => [ 'PVE::API2::OpenVZ', 'vm_shutdown', ['vmid'], { node => $nodename }, $upid_exit], stop => [ 'PVE::API2::OpenVZ', 'vm_stop', ['vmid'], { node => $nodename }, $upid_exit], mount => [ 'PVE::API2::OpenVZ', 'vm_mount', ['vmid'], { node => $nodename }, $upid_exit], diff --git a/www/manager/Utils.js b/www/manager/Utils.js index f95c180..151df32 100644 --- a/www/manager/Utils.js +++ b/www/manager/Utils.js @@ -510,6 +510,8 @@ Ext.define('PVE.Utils', { statics: { vzmount: ['CT', gettext('Mount') ], vzumount: ['CT', gettext('Unmount') ], vzshutdown: ['CT', gettext('Shutdown') ], + vzsuspend: [ 'CT', gettext('Suspend') ], + vzresume: [ 'CT', gettext('Resume') ], hamigrate: [ 'HA', gettext('Migrate') ], hastart: [ 'HA', gettext('Start') ], hastop: [ 'HA', gettext('Stop') ], diff --git a/www/manager/openvz/CmdMenu.js b/www/manager/openvz/CmdMenu.js index 85589ed..6bb5326 100644 --- a/www/manager/openvz/CmdMenu.js +++ b/www/manager/openvz/CmdMenu.js @@ -50,10 +50,30 @@ Ext.define('PVE.openvz.CmdMenu', { } }, { + text: gettext('Suspend'), + icon: '/pve2/images/forward.png', + handler: function() { + var msg = Ext.String.format(gettext("Do you really want to suspend CT {0}?"), vmid); + Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { + if (btn !== 'yes') { + return; + } + vm_command('suspend'); + }); + } + }, + { + text: gettext('Resume'), + icon: '/pve2/images/forward.png', + handler: function() { + vm_command('resume'); + } + }, + { text: gettext('Shutdown'), icon: '/pve2/images/stop.png', handler: function() { - var msg = Ext.String.format(gettext("Do you really want to shutdown VM {0}?"), vmid); + var msg = Ext.String.format(gettext("Do you really want to shutdown CT {0}?"), vmid); Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { if (btn !== 'yes') { return; @@ -67,7 +87,7 @@ Ext.define('PVE.openvz.CmdMenu', { text: gettext('Stop'), icon: '/pve2/images/gtk-stop.png', handler: function() { - var msg = Ext.String.format(gettext("Do you really want to stop VM {0}?"), vmid); + var msg = Ext.String.format(gettext("Do you really want to stop CT {0}?"), vmid); Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { if (btn !== 'yes') { return; diff --git a/www/manager/qemu/CmdMenu.js b/www/manager/qemu/CmdMenu.js index 853f57b..25591e9 100644 --- a/www/manager/qemu/CmdMenu.js +++ b/www/manager/qemu/CmdMenu.js @@ -50,6 +50,26 @@ Ext.define('PVE.qemu.CmdMenu', { } }, { + text: gettext('Suspend'), + icon: '/pve2/images/forward.png', + handler: function() { + var msg = Ext.String.format(gettext("Do you really want to suspend VM {0}?"), vmid); + Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { + if (btn !== 'yes') { + return; + } + vm_command('suspend'); + }); + } + }, + { + text: gettext('Resume'), + icon: '/pve2/images/forward.png', + handler: function() { + vm_command('resume'); + } + }, + { text: gettext('Shutdown'), icon: '/pve2/images/stop.png', handler: function() { diff --git a/www/mobile/OpenVzSummary.js b/www/mobile/OpenVzSummary.js index f71fbec..4c27e93 100644 --- a/www/mobile/OpenVzSummary.js +++ b/www/mobile/OpenVzSummary.js @@ -159,6 +159,18 @@ Ext.define('PVE.OpenVzSummary', { } }, { + text: gettext('Suspend'), + handler: function() { + me.vm_command("suspend", {}); + } + }, + { + text: gettext('Resume'), + handler: function() { + me.vm_command("resume", {}); + } + }, + { text: gettext('Shutdown'), handler: function() { me.vm_command("shutdown", {}); diff --git a/www/mobile/QemuSummary.js b/www/mobile/QemuSummary.js index eb33222..b392e1e 100644 --- a/www/mobile/QemuSummary.js +++ b/www/mobile/QemuSummary.js @@ -162,6 +162,18 @@ Ext.define('PVE.QemuSummary', { } }, { + text: gettext('Suspend'), + handler: function() { + me.vm_command("suspend", {}); + } + }, + { + text: gettext('Resume'), + handler: function() { + me.vm_command("resume", {}); + } + }, + { text: gettext('Shutdown'), handler: function() { me.vm_command("shutdown", {}); -- 1.9.1 _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel