[pve-devel] [PATCH] Add CT suspend/resume to PVE API (resubmit without whitespace changes)
Daniel Hunsaker
danhunsaker at gmail.com
Fri Oct 3 05:39:30 CEST 2014
From: Dan Hunsaker <danhunsaker at 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 <danhunsaker at 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
More information about the pve-devel
mailing list