[pve-devel] [PATCH] Add CT suspend/resume to PVE API
Daniel Hunsaker
danhunsaker at gmail.com
Fri Oct 3 05:03:02 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 | 308 +++++++++++++++++++++++++++---------------
PVE/OpenVZ.pm | 92 ++++++++-----
bin/pvectl | 16 ++-
www/manager/Utils.js | 80 +++++------
www/manager/openvz/CmdMenu.js | 28 +++-
www/manager/qemu/CmdMenu.js | 26 +++-
www/mobile/OpenVzSummary.js | 30 ++--
www/mobile/QemuSummary.js | 34 +++--
8 files changed, 401 insertions(+), 213 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
@@ -71,7 +71,7 @@ my $get_container_storage = sub {
my $check_ct_modify_config_perm = sub {
my ($rpcenv, $authuser, $vmid, $pool, $key_list) = @_;
-
+
return 1 if $authuser ne 'root at pam';
foreach my $opt (@$key_list) {
@@ -82,7 +82,7 @@ my $check_ct_modify_config_perm = sub {
$rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Disk']);
} elsif ($opt eq 'memory' || $opt eq 'swap') {
$rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Memory']);
- } elsif ($opt eq 'netif' || $opt eq 'ip_address' || $opt eq 'nameserver' ||
+ } elsif ($opt eq 'netif' || $opt eq 'ip_address' || $opt eq 'nameserver' ||
$opt eq 'searchdomain' || $opt eq 'hostname') {
$rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Network']);
} else {
@@ -94,8 +94,8 @@ my $check_ct_modify_config_perm = sub {
};
__PACKAGE__->register_method({
- name => 'vmlist',
- path => '',
+ name => 'vmlist',
+ path => '',
method => 'GET',
description => "OpenVZ container index (per node).",
permissions => {
@@ -136,7 +136,7 @@ __PACKAGE__->register_method({
}
return $res;
-
+
}});
my $restore_openvz = sub {
@@ -153,10 +153,10 @@ my $restore_openvz = sub {
die "unable to create CT $vmid - container already exists\n"
if !$force && -f $conffile;
-
+
die "unable to create CT $vmid - directory '$private' already exists\n"
if !$force && -d $private;
-
+
die "unable to create CT $vmid - directory '$root' already exists\n"
if !$force && -d $root;
@@ -168,14 +168,14 @@ my $restore_openvz = sub {
my $oldprivate = PVE::OpenVZ::get_privatedir($conf, $vmid);
rmtree $oldprivate if -d $oldprivate;
-
+
my $oldroot = $conf->{ve_root} ? $conf->{ve_root}->{value} : $root;
rmtree $oldroot if -d $oldroot;
};
mkpath $private || die "unable to create private dir '$private'";
mkpath $root || die "unable to create root dir '$root'";
-
+
my $cmd = ['tar', 'xpf', $archive, '--totals', '--sparse', '-C', $private];
if ($archive eq '-') {
@@ -197,7 +197,7 @@ my $restore_openvz = sub {
$conf =~ s/host_ifname=veth[0-9]+\./host_ifname=veth${vmid}\./g;
PVE::Tools::file_set_contents($conffile, $conf);
-
+
foreach my $s (PVE::OpenVZ::SCRIPT_EXT) {
my $tfn = "$cfgdir/${vmid}.$s";
my $sfn = "$private/etc/vzdump/vps.$s";
@@ -228,8 +228,8 @@ my $restore_openvz = sub {
# create_vm is also used by vzrestore
__PACKAGE__->register_method({
- name => 'create_vm',
- path => '',
+ name => 'create_vm',
+ path => '',
method => 'POST',
description => "Create or restore a container.",
permissions => {
@@ -247,11 +247,11 @@ __PACKAGE__->register_method({
vmid => get_standard_option('pve-vmid'),
ostemplate => {
description => "The OS template or backup file.",
- type => 'string',
+ type => 'string',
maxLength => 255,
},
- password => {
- optional => 1,
+ password => {
+ optional => 1,
type => 'string',
description => "Sets root password inside container.",
},
@@ -261,23 +261,23 @@ __PACKAGE__->register_method({
optional => 1,
}),
force => {
- optional => 1,
+ optional => 1,
type => 'boolean',
description => "Allow to overwrite existing container.",
},
restore => {
- optional => 1,
+ optional => 1,
type => 'boolean',
description => "Mark this as restore task.",
},
- pool => {
+ pool => {
optional => 1,
type => 'string', format => 'pve-poolid',
description => "Add the VM to the specified pool.",
},
}),
},
- returns => {
+ returns => {
type => 'string',
},
code => sub {
@@ -296,7 +296,7 @@ __PACKAGE__->register_method({
my $storage = extract_param($param, 'storage') || 'local';
my $pool = extract_param($param, 'pool');
-
+
my $storage_cfg = cfs_read_file("storage.cfg");
my $scfg = PVE::Storage::storage_check_node($storage_cfg, $storage, $node);
@@ -311,7 +311,7 @@ __PACKAGE__->register_method({
if (defined($pool)) {
$rpcenv->check_pool_exist($pool);
$rpcenv->check_perm_modify($authuser, "/pool/$pool");
- }
+ }
$rpcenv->check($authuser, "/storage/$storage", ['Datastore.AllocateSpace']);
@@ -337,9 +337,9 @@ __PACKAGE__->register_method({
my $archive;
if ($ostemplate eq '-') {
- die "pipe requires cli environment\n"
- if $rpcenv->{type} ne 'cli';
- die "pipe can only be used with restore tasks\n"
+ die "pipe requires cli environment\n"
+ if $rpcenv->{type} ne 'cli';
+ die "pipe can only be used with restore tasks\n"
if !$param->{restore};
$archive = '-';
} else {
@@ -347,9 +347,9 @@ __PACKAGE__->register_method({
$archive = PVE::Storage::abs_filesystem_path($storage_cfg, $ostemplate);
}
- if (!defined($param->{searchdomain}) &&
+ if (!defined($param->{searchdomain}) &&
!defined($param->{nameserver})) {
-
+
my $resolv = PVE::INotify::read_file('resolvconf');
$param->{searchdomain} = $resolv->{search} if $resolv->{search};
@@ -371,7 +371,7 @@ __PACKAGE__->register_method({
my $check_vmid_usage = sub {
if ($param->{force}) {
- die "cant overwrite mounted container\n"
+ die "cant overwrite mounted container\n"
if PVE::OpenVZ::check_mounted($conf, $vmid);
} else {
die "CT $vmid already exists\n" if -f $basecfg_fn;
@@ -392,7 +392,7 @@ __PACKAGE__->register_method({
&$restore_openvz($private, $archive, $vmid, $param->{force});
# is this really needed?
- my $cmd = ['vzctl', '--skiplock', '--quiet', 'set', $vmid,
+ my $cmd = ['vzctl', '--skiplock', '--quiet', 'set', $vmid,
'--applyconfig_map', 'name', '--save'];
run_command($cmd);
@@ -411,10 +411,10 @@ __PACKAGE__->register_method({
my $cmd = ['vzctl', '--skiplock', 'create', $vmid,
'--ostemplate', $archive, '--private', $private];
run_command($cmd);
-
- # hack: vzctl '--userpasswd' starts the CT, but we want
+
+ # hack: vzctl '--userpasswd' starts the CT, but we want
# to avoid that for create
- PVE::OpenVZ::set_rootpasswd($private, $password)
+ PVE::OpenVZ::set_rootpasswd($private, $password)
if defined($password);
}
@@ -425,21 +425,21 @@ __PACKAGE__->register_method({
&$check_vmid_usage(); # first check before locking
- return $rpcenv->fork_worker($param->{restore} ? 'vzrestore' : 'vzcreate',
+ return $rpcenv->fork_worker($param->{restore} ? 'vzrestore' : 'vzcreate',
$vmid, $authuser, $realcmd);
}});
my $vm_config_perm_list = [
- 'VM.Config.Disk',
- 'VM.Config.CPU',
- 'VM.Config.Memory',
- 'VM.Config.Network',
+ 'VM.Config.Disk',
+ 'VM.Config.CPU',
+ 'VM.Config.Memory',
+ 'VM.Config.Network',
'VM.Config.Options',
];
__PACKAGE__->register_method({
- name => 'update_vm',
- path => '{vmid}/config',
+ name => 'update_vm',
+ path => '{vmid}/config',
method => 'PUT',
protected => 1,
proxyto => 'node',
@@ -457,7 +457,7 @@ __PACKAGE__->register_method({
type => 'string',
description => 'Prevent changes if current configuration file has different SHA1 digest. This can be used to prevent concurrent modifications.',
maxLength => 40,
- optional => 1,
+ optional => 1,
}
}),
},
@@ -482,7 +482,7 @@ __PACKAGE__->register_method({
my $code = sub {
my $conf = PVE::OpenVZ::load_config($vmid);
- die "checksum missmatch (file change by other user?)\n"
+ die "checksum missmatch (file change by other user?)\n"
if $digest && $digest ne $conf->{digest};
my $changes = PVE::OpenVZ::update_ovz_config($vmid, $conf, $param);
@@ -492,7 +492,7 @@ __PACKAGE__->register_method({
my $cmd = ['vzctl', '--skiplock', 'set', $vmid, @$changes, '--save'];
PVE::Cluster::log_msg('info', $authuser, "update CT $vmid: " . join(' ', @$changes));
-
+
run_command($cmd);
};
@@ -502,13 +502,13 @@ __PACKAGE__->register_method({
}});
__PACKAGE__->register_method ({
- subclass => "PVE::API2::Firewall::CT",
+ subclass => "PVE::API2::Firewall::CT",
path => '{vmid}/firewall',
});
__PACKAGE__->register_method({
name => 'vmdiridx',
- path => '{vmid}',
+ path => '{vmid}',
method => 'GET',
proxyto => 'node',
description => "Directory index",
@@ -549,13 +549,13 @@ __PACKAGE__->register_method({
{ subdir => 'rrddata' },
{ subdir => 'firewall' },
];
-
+
return $res;
}});
__PACKAGE__->register_method({
- name => 'rrd',
- path => '{vmid}/rrd',
+ name => 'rrd',
+ path => '{vmid}/rrd',
method => 'GET',
protected => 1, # fixme: can we avoid that?
permissions => {
@@ -594,14 +594,14 @@ __PACKAGE__->register_method({
my ($param) = @_;
return PVE::Cluster::create_rrd_graph(
- "pve2-vm/$param->{vmid}", $param->{timeframe},
+ "pve2-vm/$param->{vmid}", $param->{timeframe},
$param->{ds}, $param->{cf});
-
+
}});
__PACKAGE__->register_method({
- name => 'rrddata',
- path => '{vmid}/rrddata',
+ name => 'rrddata',
+ path => '{vmid}/rrddata',
method => 'GET',
protected => 1, # fixme: can we avoid that?
permissions => {
@@ -641,8 +641,8 @@ __PACKAGE__->register_method({
}});
__PACKAGE__->register_method({
- name => 'initlog',
- path => '{vmid}/initlog',
+ name => 'initlog',
+ path => '{vmid}/initlog',
method => 'GET',
protected => 1,
proxyto => 'node',
@@ -669,7 +669,7 @@ __PACKAGE__->register_method({
},
returns => {
type => 'array',
- items => {
+ items => {
type => "object",
properties => {
n => {
@@ -700,13 +700,13 @@ __PACKAGE__->register_method({
my ($count, $lines) = PVE::Tools::dump_logfile($logfn, $param->{start}, $param->{limit});
$rpcenv->set_result_attrib('total', $count);
-
- return $lines;
+
+ return $lines;
}});
__PACKAGE__->register_method({
- name => 'vm_config',
- path => '{vmid}/config',
+ name => 'vm_config',
+ path => '{vmid}/config',
method => 'GET',
proxyto => 'node',
description => "Get container configuration.",
@@ -720,7 +720,7 @@ __PACKAGE__->register_method({
vmid => get_standard_option('pve-vmid'),
},
},
- returns => {
+ returns => {
type => "object",
properties => {
digest => {
@@ -775,8 +775,8 @@ __PACKAGE__->register_method({
}});
__PACKAGE__->register_method({
- name => 'destroy_vm',
- path => '{vmid}',
+ name => 'destroy_vm',
+ path => '{vmid}',
method => 'DELETE',
protected => 1,
proxyto => 'node',
@@ -791,7 +791,7 @@ __PACKAGE__->register_method({
vmid => get_standard_option('pve-vmid'),
},
},
- returns => {
+ returns => {
type => 'string',
},
code => sub {
@@ -820,8 +820,8 @@ __PACKAGE__->register_method({
my $sslcert;
__PACKAGE__->register_method ({
- name => 'vncproxy',
- path => '{vmid}/vncproxy',
+ name => 'vncproxy',
+ path => '{vmid}/vncproxy',
method => 'POST',
protected => 1,
permissions => {
@@ -840,7 +840,7 @@ __PACKAGE__->register_method ({
},
},
},
- returns => {
+ returns => {
additionalProperties => 0,
properties => {
user => { type => 'string' },
@@ -870,19 +870,19 @@ __PACKAGE__->register_method ({
my $port = PVE::Tools::next_vnc_port();
my $remip;
-
+
if ($node ne PVE::INotify::nodename()) {
$remip = PVE::Cluster::remote_node_ip($node);
}
# NOTE: vncterm VNC traffic is already TLS encrypted,
# so we select the fastest chipher here (or 'none'?)
- my $remcmd = $remip ?
+ my $remcmd = $remip ?
['/usr/bin/ssh', '-t', $remip] : [];
- my $shcmd = [ '/usr/bin/dtach', '-A',
- "/var/run/dtach/vzctlconsole$vmid",
- '-r', 'winch', '-z',
+ my $shcmd = [ '/usr/bin/dtach', '-A',
+ "/var/run/dtach/vzctlconsole$vmid",
+ '-r', 'winch', '-z',
'/usr/sbin/vzctl', 'console', $vmid ];
my $realcmd = sub {
@@ -890,14 +890,14 @@ __PACKAGE__->register_method ({
syslog ('info', "starting openvz vnc proxy $upid\n");
- my $timeout = 10;
+ my $timeout = 10;
my $cmd = ['/usr/bin/vncterm', '-rfbport', $port,
- '-timeout', $timeout, '-authpath', $authpath,
+ '-timeout', $timeout, '-authpath', $authpath,
'-perm', 'VM.Console'];
if ($param->{websocket}) {
- $ENV{PVE_VNC_TICKET} = $ticket; # pass ticket to vncterm
+ $ENV{PVE_VNC_TICKET} = $ticket; # pass ticket to vncterm
push @$cmd, '-notls', '-listen', 'localhost';
}
@@ -915,9 +915,9 @@ __PACKAGE__->register_method ({
return {
user => $authuser,
ticket => $ticket,
- port => $port,
- upid => $upid,
- cert => $sslcert,
+ port => $port,
+ upid => $upid,
+ cert => $sslcert,
};
}});
@@ -925,7 +925,7 @@ __PACKAGE__->register_method({
name => 'vncwebsocket',
path => '{vmid}/vncwebsocket',
method => 'GET',
- permissions => {
+ permissions => {
description => "You also need to pass a valid ticket (vncticket).",
check => ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
},
@@ -966,13 +966,13 @@ __PACKAGE__->register_method({
PVE::AccessControl::verify_vnc_ticket($param->{vncticket}, $authuser, $authpath);
my $port = $param->{port};
-
+
return { port => $port };
}});
__PACKAGE__->register_method ({
- name => 'spiceproxy',
- path => '{vmid}/spiceproxy',
+ name => 'spiceproxy',
+ path => '{vmid}/spiceproxy',
method => 'POST',
protected => 1,
proxyto => 'node',
@@ -999,9 +999,9 @@ __PACKAGE__->register_method ({
my $authpath = "/vms/$vmid";
my $permissions = 'VM.Console';
- my $shcmd = ['/usr/bin/dtach', '-A',
- "/var/run/dtach/vzctlconsole$vmid",
- '-r', 'winch', '-z',
+ my $shcmd = ['/usr/bin/dtach', '-A',
+ "/var/run/dtach/vzctlconsole$vmid",
+ '-r', 'winch', '-z',
'/usr/sbin/vzctl', 'console', $vmid];
my $title = "CT $vmid";
@@ -1011,7 +1011,7 @@ __PACKAGE__->register_method ({
__PACKAGE__->register_method({
name => 'vmcmdidx',
- path => '{vmid}/status',
+ path => '{vmid}/status',
method => 'GET',
proxyto => 'node',
description => "Directory index",
@@ -1047,7 +1047,7 @@ __PACKAGE__->register_method({
{ subdir => 'start' },
{ subdir => 'stop' },
];
-
+
return $res;
}});
@@ -1057,12 +1057,12 @@ my $vm_is_ha_managed = sub {
my $cc = PVE::Cluster::cfs_read_file('cluster.conf');
if (PVE::Cluster::cluster_conf_lookup_pvevm($cc, 0, $vmid, 1)) {
return 1;
- }
+ }
return 0;
};
__PACKAGE__->register_method({
- name => 'vm_status',
+ name => 'vm_status',
path => '{vmid}/status/current',
method => 'GET',
proxyto => 'node',
@@ -1094,7 +1094,7 @@ __PACKAGE__->register_method({
}});
__PACKAGE__->register_method({
- name => 'vm_user_beancounters',
+ name => 'vm_user_beancounters',
path => '{vmid}/status/ubc',
method => 'GET',
proxyto => 'node',
@@ -1138,7 +1138,7 @@ __PACKAGE__->register_method({
}});
__PACKAGE__->register_method({
- name => 'vm_start',
+ name => 'vm_start',
path => '{vmid}/status/start',
method => 'POST',
protected => 1,
@@ -1154,7 +1154,7 @@ __PACKAGE__->register_method({
vmid => get_standard_option('pve-vmid'),
},
},
- returns => {
+ returns => {
type => 'string',
},
code => sub {
@@ -1202,13 +1202,13 @@ __PACKAGE__->register_method({
}
my $vzconf = PVE::OpenVZ::read_global_vz_config();
-
+
# make sure mount point is there (see bug #276)
my $root = PVE::OpenVZ::get_rootdir($veconf, $vmid);
mkpath $root || die "unable to create root dir '$root'";
my $cmd = ['vzctl', 'start', $vmid];
-
+
run_command($cmd);
return;
@@ -1219,7 +1219,7 @@ __PACKAGE__->register_method({
}});
__PACKAGE__->register_method({
- name => 'vm_stop',
+ name => 'vm_stop',
path => '{vmid}/status/stop',
method => 'POST',
protected => 1,
@@ -1235,7 +1235,7 @@ __PACKAGE__->register_method({
vmid => get_standard_option('pve-vmid'),
},
},
- returns => {
+ returns => {
type => 'string',
},
code => sub {
@@ -1278,7 +1278,7 @@ __PACKAGE__->register_method({
my $cmd = ['vzctl', 'stop', $vmid, '--fast'];
run_command($cmd);
-
+
return;
};
@@ -1287,7 +1287,7 @@ __PACKAGE__->register_method({
}});
__PACKAGE__->register_method({
- name => 'vm_mount',
+ name => 'vm_mount',
path => '{vmid}/status/mount',
method => 'POST',
protected => 1,
@@ -1303,7 +1303,7 @@ __PACKAGE__->register_method({
vmid => get_standard_option('pve-vmid'),
},
},
- returns => {
+ returns => {
type => 'string',
},
code => sub {
@@ -1325,7 +1325,7 @@ __PACKAGE__->register_method({
syslog('info', "mount CT $vmid: $upid\n");
my $cmd = ['vzctl', 'mount', $vmid];
-
+
run_command($cmd);
return;
@@ -1335,7 +1335,7 @@ __PACKAGE__->register_method({
}});
__PACKAGE__->register_method({
- name => 'vm_umount',
+ name => 'vm_umount',
path => '{vmid}/status/umount',
method => 'POST',
protected => 1,
@@ -1351,7 +1351,7 @@ __PACKAGE__->register_method({
vmid => get_standard_option('pve-vmid'),
},
},
- returns => {
+ returns => {
type => 'string',
},
code => sub {
@@ -1373,7 +1373,7 @@ __PACKAGE__->register_method({
syslog('info', "umount CT $vmid: $upid\n");
my $cmd = ['vzctl', 'umount', $vmid];
-
+
run_command($cmd);
return;
@@ -1383,7 +1383,7 @@ __PACKAGE__->register_method({
}});
__PACKAGE__->register_method({
- name => 'vm_shutdown',
+ name => 'vm_shutdown',
path => '{vmid}/status/shutdown',
method => 'POST',
protected => 1,
@@ -1412,7 +1412,7 @@ __PACKAGE__->register_method({
}
},
},
- returns => {
+ returns => {
type => 'string',
},
code => sub {
@@ -1449,7 +1449,7 @@ __PACKAGE__->register_method({
push @$cmd, '--fast';
run_command($cmd);
-
+
return;
};
@@ -1459,7 +1459,103 @@ __PACKAGE__->register_method({
}});
__PACKAGE__->register_method({
- name => 'migrate_vm',
+ 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',
protected => 1,
@@ -1481,7 +1577,7 @@ __PACKAGE__->register_method({
},
},
},
- returns => {
+ returns => {
type => 'string',
description => "the task ID.",
},
@@ -1510,7 +1606,7 @@ __PACKAGE__->register_method({
# try to detect errors early
if (PVE::OpenVZ::check_running($vmid)) {
- die "cant migrate running container without --online\n"
+ die "cant migrate running container without --online\n"
if !$param->{online};
}
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;
@@ -182,7 +182,7 @@ sub read_container_blkio_stat {
my $filename = "/proc/vz/beancounter/$vmid/blkio.io_service_bytes";
if (my $fh = IO::File->new ($filename, "r")) {
-
+
while (defined (my $line = <$fh>)) {
if ($line =~ m/^\S+\s+Read\s+(\d+)$/) {
$read += $1;
@@ -223,7 +223,7 @@ sub vmstatus {
$d->{mem} = 0;
$d->{swap} = 0;
-
+
($d->{maxmem}, $d->{maxswap}) = ovz_config_extract_mem_swap($conf);
$d->{nproc} = 0;
@@ -332,7 +332,7 @@ sub vmstatus {
my $d = $list->{$vmid};
next if !$d || !$d->{status} || $d->{status} ne 'running';
($d->{netin}, $d->{netout}) = read_container_network_usage($vmid);
- ($d->{diskread}, $d->{diskwrite}) = read_container_blkio_stat($vmid);
+ ($d->{diskread}, $d->{diskwrite}) = read_container_blkio_stat($vmid);
}
return $list;
@@ -449,7 +449,7 @@ sub read_global_vz_config {
lockdir => '/var/lib/vz/lock',
disk_quota => 1,
};
-
+
my $filename = "/etc/vz/vz.conf";
return $res if ! -f $filename;
@@ -485,7 +485,7 @@ sub read_global_vz_config {
$dir =~ s/^\"(.*)\"/$1/;
$res->{lockdir} = $dir;
}
- if ($data =~ m/^\s*DISK_QUOTA=(no|false|off|0)$/m) {
+ if ($data =~ m/^\s*DISK_QUOTA=(no|false|off|0)$/m) {
$res->{disk_quota} = 0;
}
@@ -523,7 +523,7 @@ sub parse_netif {
$d->{$1} = parse_boolean('mac_filter', $2);
} else {
$d->{$1} = $2;
- }
+ }
}
}
if ($d->{ifname}) {
@@ -631,7 +631,7 @@ sub parse_res_bytes_bytes {
my @a = split(/:/, $text);
$a[1] = $a[0] if !defined($a[1]);
-
+
my $bar = parse_res_bar_limit($a[0], 1);
my $lim = parse_res_bar_limit($a[1], 1);
@@ -647,7 +647,7 @@ sub parse_res_block_block {
my @a = split(/:/, $text);
$a[1] = $a[0] if !defined($a[1]);
-
+
my $bar = parse_res_bar_limit($a[0], 1024);
my $lim = parse_res_bar_limit($a[1], 1024);
@@ -663,7 +663,7 @@ sub parse_res_pages_pages {
my @a = split(/:/, $text);
$a[1] = $a[0] if !defined($a[1]);
-
+
my $bar = parse_res_bar_limit($a[0], 4096);
my $lim = parse_res_bar_limit($a[1], 4096);
@@ -678,9 +678,9 @@ sub parse_res_pages_unlimited {
my ($key, $text) = @_;
my @a = split(/:/, $text);
-
+
my $bar = parse_res_bar_limit($a[0], 4096);
-
+
if (defined($bar)) {
return { bar => $bar, lim => $res_unlimited };
}
@@ -692,9 +692,9 @@ sub parse_res_pages_ignore {
my ($key, $text) = @_;
my @a = split(/:/, $text);
-
+
my $bar = parse_res_bar_limit($a[0], 4096);
-
+
if (defined($bar)) {
return { bar => $bar };
}
@@ -707,9 +707,9 @@ sub parse_res_ignore_pages {
my @a = split(/:/, $text);
$a[1] = $a[0] if !defined($a[1]);
-
+
my $lim = parse_res_bar_limit($a[1] , 4096);
-
+
if (defined($lim)) {
return { bar => 0, lim => $lim };
}
@@ -740,7 +740,7 @@ my $ovz_ressources = {
numproc => \&parse_res_num_ignore,
numtcpsock => \&parse_res_num_ignore,
numothersock => \&parse_res_num_ignore,
- numfile => \&parse_res_num_ignore,
+ numfile => \&parse_res_num_ignore,
numflock => \&parse_res_num_num,
numpty => \&parse_res_num_ignore,
numsiginfo => \&parse_res_num_ignore,
@@ -873,9 +873,9 @@ sub format_res_bar_lim {
my ($key, $data) = @_;
if (defined($data->{lim}) && ($data->{lim} ne $data->{bar})) {
- return format_res_value($key, $data->{bar}) . ":" . format_res_value($key, $data->{lim});
+ return format_res_value($key, $data->{bar}) . ":" . format_res_value($key, $data->{lim});
} else {
- return format_res_value($key, $data->{bar});
+ return format_res_value($key, $data->{bar});
}
}
@@ -893,7 +893,7 @@ sub create_config_line {
}
} elsif (defined($data->{bar})) {
my $tmp = format_res_bar_lim($key, $data);
- $text .= uc($key) . "=\"$tmp\"\n";
+ $text .= uc($key) . "=\"$tmp\"\n";
}
}
@@ -907,8 +907,8 @@ sub ovz_config_extract_mem_swap {
my $maxpages = ($res_unlimited / 4096);
if ($veconf->{swappages}) {
- if ($veconf->{physpages} && $veconf->{physpages}->{lim} &&
- ($veconf->{physpages}->{lim} < $maxpages)) {
+ if ($veconf->{physpages} && $veconf->{physpages}->{lim} &&
+ ($veconf->{physpages}->{lim} < $maxpages)) {
$mem = int(($veconf->{physpages}->{lim} * 4096 + $unit - 1) / $unit);
}
if ($veconf->{swappages}->{lim} && ($veconf->{swappages}->{lim} < $maxpages)) {
@@ -928,15 +928,15 @@ sub update_ovz_config {
my ($vmid, $veconf, $param) = @_;
my $changes = [];
-
+
# test if barrier or limit changed
my $push_bl_changes = sub {
my ($name, $bar, $lim) = @_;
- my $old = format_res_bar_lim($name, $veconf->{$name})
+ my $old = format_res_bar_lim($name, $veconf->{$name})
if $veconf->{$name} && defined($veconf->{$name}->{bar});
my $new = format_res_bar_lim($name, { bar => $bar, lim => $lim });
if (!$old || ($old ne $new)) {
- $veconf->{$name}->{bar} = $bar;
+ $veconf->{$name}->{bar} = $bar;
$veconf->{$name}->{lim} = $lim;
push @$changes, "--$name", $new;
}
@@ -977,7 +977,7 @@ sub update_ovz_config {
$cpus = $param->{cpus};
}
- # memory related parameter
+ # memory related parameter
&$push_bl_changes('vmguarpages', 0, $res_unlimited);
&$push_bl_changes('oomguarpages', 0, $res_unlimited);
@@ -1061,9 +1061,9 @@ sub update_ovz_config {
};
&$cond_set_boolean('onboot');
-
+
&$cond_set_value('hostname');
-
+
&$cond_set_value('searchdomain');
if ($param->{'description'}) {
@@ -1118,7 +1118,7 @@ sub update_ovz_config {
$ifadd .= $newif->{$ifname}->{host_ifname} ? ",$newif->{$ifname}->{host_ifname}" : ',';
$ifadd .= $newif->{$ifname}->{host_mac} ? ",$newif->{$ifname}->{host_mac}" : ',';
$ifadd .= $newif->{$ifname}->{bridge} ? ",$newif->{$ifname}->{bridge}" : '';
-
+
# not possible with current vzctl
#$ifadd .= $newif->{$ifname}->{mac_filter} ? ",$newif->{$ifname}->{mac_filter}" : '';
@@ -1129,7 +1129,7 @@ sub update_ovz_config {
$veconf->{netif}->{value} = $newvalue;
}
- if (defined($param->{'nameserver'})) {
+ if (defined($param->{'nameserver'})) {
# remove duplicates
my $nshash = {};
my $newvalue = '';
@@ -1190,8 +1190,8 @@ sub create_lock_manager {
return LockFile::Simple->make(-format => '%f',
-autoclean => 1,
- -max => defined($max) ? $max : 60,
- -delay => 1,
+ -max => defined($max) ? $max : 60,
+ -delay => 1,
-stale => 1,
-nfs => 0);
}
@@ -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) = @_;
@@ -1238,7 +1262,7 @@ sub replacepw {
# copy owner and permissions
chmod $st->mode, \*DST;
chown $st->uid, $st->gid, \*DST;
-
+
while (defined (my $line = <SRC>)) {
$line =~ s/^root:[^:]*:/root:${epw}:/;
print DST $line;
@@ -1255,7 +1279,7 @@ sub replacepw {
} else {
rename $tmpfile, $file;
unlink $tmpfile; # in case rename fails
- }
+ }
}
sub set_rootpasswd {
diff --git a/bin/pvectl b/bin/pvectl
index f8ae3ad..9e9a797 100755
--- a/bin/pvectl
+++ b/bin/pvectl
@@ -28,7 +28,7 @@ my $rpcenv = PVE::RPCEnvironment->init('cli');
$rpcenv->init_request();
$rpcenv->set_language($ENV{LANG});
-$rpcenv->set_user('root at pam');
+$rpcenv->set_user('root at pam');
my $upid_exit = sub {
my $upid = shift;
@@ -44,15 +44,15 @@ my $cmddef = {
exit 0 if (!scalar(@$vmlist));
- printf "%10s %-20s %-10s %-10s %-12s\n",
+ printf "%10s %-20s %-10s %-10s %-12s\n",
qw(VMID NAME STATUS MEM(MB) DISK(GB));
foreach my $rec (sort { $a->{vmid} <=> $b->{vmid} } @$vmlist) {
- printf "%10s %-20s %-10s %-10s %-12.2f\n", $rec->{vmid}, $rec->{name} || '',
- $rec->{status},
- ($rec->{maxmem} || 0)/(1024*1024),
+ printf "%10s %-20s %-10s %-10s %-12.2f\n", $rec->{vmid}, $rec->{name} || '',
+ $rec->{status},
+ ($rec->{maxmem} || 0)/(1024*1024),
($rec->{maxdisk} || 0)/(1024*1024*1024);
- }
+ }
} ],
create => [ 'PVE::API2::OpenVZ', 'create_vm', ['vmid', 'ostemplate'], { node => $nodename }, $upid_exit ],
@@ -60,7 +60,7 @@ my $cmddef = {
set => [ "PVE::API2::OpenVZ", 'update_vm', ['vmid'], { node => $nodename } ],
- config => [ "PVE::API2::OpenVZ", 'vm_config', ['vmid'],
+ config => [ "PVE::API2::OpenVZ", 'vm_config', ['vmid'],
{ node => $nodename }, sub {
my $config = shift;
foreach my $k (sort (keys %$config)) {
@@ -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
@@ -1,13 +1,13 @@
Ext.ns('PVE');
// avoid errors when running without development tools
-if (!Ext.isDefined(Ext.global.console)) {
- var console = {
- dir: function() {},
- log: function() {}
+if (!Ext.isDefined(Ext.global.console)) {
+ var console = {
+ dir: function() {},
+ log: function() {}
};
}
-console.log("Starting PVE Manager");
+console.log("Starting PVE Manager");
Ext.Ajax.defaultHeaders = {
'Accept': 'application/json'
@@ -15,7 +15,7 @@ Ext.Ajax.defaultHeaders = {
Ext.Ajax.on('beforerequest', function(conn, options) {
if (PVE.CSRFPreventionToken) {
- if (!options.headers) {
+ if (!options.headers) {
options.headers = {};
}
options.headers.CSRFPreventionToken = PVE.CSRFPreventionToken;
@@ -48,7 +48,7 @@ Ext.define('PVE.Utils', { statics: {
// this class only contains static functions
- toolkit: undefined, // (extjs|touch), set inside Toolkit.js
+ toolkit: undefined, // (extjs|touch), set inside Toolkit.js
log_severity_hash: {
0: "panic",
@@ -104,7 +104,7 @@ Ext.define('PVE.Utils', { statics: {
},
render_network_iface_type: function(value) {
- return PVE.Utils.network_iface_types[value] ||
+ return PVE.Utils.network_iface_types[value] ||
PVE.Utils.unknownText;
},
@@ -131,29 +131,29 @@ Ext.define('PVE.Utils', { statics: {
kvm_keymaps: {
//ar: 'Arabic',
da: 'Danish',
- de: 'German',
- 'de-ch': 'German (Swiss)',
- 'en-gb': 'English (UK)',
+ de: 'German',
+ 'de-ch': 'German (Swiss)',
+ 'en-gb': 'English (UK)',
'en-us': 'English (USA',
es: 'Spanish',
//et: 'Estonia',
fi: 'Finnish',
- //fo: 'Faroe Islands',
- fr: 'French',
- 'fr-be': 'French (Belgium)',
+ //fo: 'Faroe Islands',
+ fr: 'French',
+ 'fr-be': 'French (Belgium)',
'fr-ca': 'French (Canada)',
'fr-ch': 'French (Swiss)',
//hr: 'Croatia',
hu: 'Hungarian',
is: 'Icelandic',
- it: 'Italian',
+ it: 'Italian',
ja: 'Japanese',
lt: 'Lithuanian',
//lv: 'Latvian',
- mk: 'Macedonian',
+ mk: 'Macedonian',
nl: 'Dutch',
//'nl-be': 'Dutch (Belgium)',
- no: 'Norwegian',
+ no: 'Norwegian',
pl: 'Polish',
pt: 'Portuguese',
'pt-br': 'Portuguese (Brazil)',
@@ -259,7 +259,7 @@ Ext.define('PVE.Utils', { statics: {
return PVE.Utils.defaultText;
}
var text = PVE.Utils.kvm_vga_drivers[value];
- if (text) {
+ if (text) {
return text + ' (' + value + ')';
}
return value;
@@ -304,7 +304,7 @@ Ext.define('PVE.Utils', { statics: {
// fixme: remove - not needed?
gridLineHeigh: function() {
return 21;
-
+
//if (Ext.isGecko)
//return 23;
//return 21;
@@ -324,10 +324,10 @@ Ext.define('PVE.Utils', { statics: {
if (verbose && Ext.isObject(result.errors)) {
msg += "<br>";
Ext.Object.each(result.errors, function(prop, desc) {
- msg += "<br><b>" + Ext.htmlEncode(prop) + "</b>: " +
+ msg += "<br><b>" + Ext.htmlEncode(prop) + "</b>: " +
Ext.htmlEncode(desc);
});
- }
+ }
}
return msg;
@@ -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') ],
@@ -530,7 +532,7 @@ Ext.define('PVE.Utils', { statics: {
stopall: [ '', gettext('Stop all VMs and Containers') ]
},
- format_task_description: function(type, id) {
+ format_task_description: function(type, id) {
var farray = PVE.Utils.task_desc_table[type];
if (!farray) {
return type;
@@ -538,7 +540,7 @@ Ext.define('PVE.Utils', { statics: {
var prefix = farray[0];
var text = farray[1];
if (prefix) {
- return prefix + ' ' + id + ' - ' + text;
+ return prefix + ' ' + id + ' - ' + text;
}
return text;
},
@@ -599,7 +601,7 @@ Ext.define('PVE.Utils', { statics: {
return "<div class='pve-bar-wrap'>" + text + "<div class='pve-bar-border'>" +
"<div class='pve-bar-inner' style='width:" + per + "%;'></div>" +
"</div></div>";
-
+
},
format_cpu_bar: function(per1, per2, text) {
@@ -607,7 +609,7 @@ Ext.define('PVE.Utils', { statics: {
return "<div class='pve-bar-border'>" +
"<div class='pve-bar-inner' style='width:" + per1 + "%;'></div>" +
"<div class='pve-bar-inner2' style='width:" + per2 + "%;'></div>" +
- "<div class='pve-bar-text'>" + text + "</div>" +
+ "<div class='pve-bar-text'>" + text + "</div>" +
"</div>";
},
@@ -619,7 +621,7 @@ Ext.define('PVE.Utils', { statics: {
return "<div class='pve-largebar-border'>" +
"<div class='pve-largebar-inner' style='width:" + per + "%;'></div>" +
- "<div class='pve-largebar-text'>" + text + "</div>" +
+ "<div class='pve-largebar-text'>" + text + "</div>" +
"</div>";
},
@@ -641,7 +643,7 @@ Ext.define('PVE.Utils', { statics: {
if (days) {
var ds = days > 1 ? PVE.Utils.daysText : PVE.Utils.dayText;
- return days.toString() + ' ' + ds + ' ' +
+ return days.toString() + ' ' + ds + ' ' +
hours_str + ':' + mins_str + ':' + ut_str;
} else {
return hours_str + ':' + mins_str + ':' + ut_str;
@@ -649,7 +651,7 @@ Ext.define('PVE.Utils', { statics: {
},
format_duration_short: function(ut) {
-
+
if (ut < 60) {
return ut.toString() + 's';
}
@@ -665,7 +667,7 @@ Ext.define('PVE.Utils', { statics: {
}
var days = ut / 86400;
- return days.toFixed(0) + 'd';
+ return days.toFixed(0) + 'd';
},
yesText: gettext('Yes'),
@@ -759,8 +761,8 @@ Ext.define('PVE.Utils', { statics: {
if (Ext.isNumber(data.channel) &&
Ext.isNumber(data.id) &&
Ext.isNumber(data.lun)) {
- return "CH " +
- Ext.String.leftPad(data.channel,2, '0') +
+ return "CH " +
+ Ext.String.leftPad(data.channel,2, '0') +
" ID " + data.id + " LUN " + data.lun;
}
return data.volid.replace(/^.*:(.*\/)?/,'');
@@ -781,7 +783,7 @@ Ext.define('PVE.Utils', { statics: {
if (!Ext.isNumeric(maxcpu) && (maxcpu >= 1)) {
return '';
}
-
+
var per = value * 100;
return per.toFixed(1) + '% of ' + maxcpu.toString() + (maxcpu > 1 ? 'CPUs' : 'CPU');
@@ -806,7 +808,7 @@ Ext.define('PVE.Utils', { statics: {
var mem = value;
var maxmem = record.data.maxmem;
-
+
if (!record.data.uptime) {
return '';
}
@@ -841,7 +843,7 @@ Ext.define('PVE.Utils', { statics: {
if (record.data.running) {
metaData.tdCls = cls + "-running";
} else if (record.data.template) {
- metaData.tdCls = cls + "-template";
+ metaData.tdCls = cls + "-template";
} else {
metaData.tdCls = cls;
}
@@ -856,7 +858,7 @@ Ext.define('PVE.Utils', { statics: {
if (uptime === undefined) {
return '';
}
-
+
if (uptime <= 0) {
return '-';
}
@@ -868,7 +870,7 @@ Ext.define('PVE.Utils', { statics: {
return PVE.Utils.support_level_hash[value] || '-';
},
- render_upid: function(value, metaData, record) {
+ render_upid: function(value, metaData, record) {
var type = record.data.type;
var id = record.data.id;
@@ -886,7 +888,7 @@ Ext.define('PVE.Utils', { statics: {
return gettext('Edit') + ': ' + subject;
}
},
-
+
openDefaultConsoleWindow: function(allowSpice, vmtype, vmid, nodename, vmname) {
var dv = PVE.Utils.defaultViewer(allowSpice);
PVE.Utils.openConsoleWindow(dv, vmtype, vmid, nodename, vmname);
@@ -989,13 +991,13 @@ Ext.define('PVE.Utils', { statics: {
});
var url = 'data:application/x-virt-viewer;charset=UTF-8,' +
encodeURIComponent(raw);
-
+
downloadWithName(url, "pve-spice.vv");
}
});
},
- // comp.setLoading() is buggy in ExtJS 4.0.7, so we
+ // comp.setLoading() is buggy in ExtJS 4.0.7, so we
// use el.mask() instead
setErrorMask: function(comp, msg) {
var el = comp.el;
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
@@ -37,7 +37,7 @@ Ext.define('PVE.openvz.CmdMenu', {
vm_command('start');
}
},
- {
+ {
text: gettext('Migrate'),
icon: '/pve2/images/forward.png',
handler: function() {
@@ -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;
@@ -61,13 +81,13 @@ Ext.define('PVE.openvz.CmdMenu', {
vm_command('shutdown');
});
- }
+ }
},
{
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
@@ -37,7 +37,7 @@ Ext.define('PVE.qemu.CmdMenu', {
vm_command('start');
}
},
- {
+ {
text: gettext('Migrate'),
icon: '/pve2/images/forward.png',
handler: function() {
@@ -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() {
@@ -61,7 +81,7 @@ Ext.define('PVE.qemu.CmdMenu', {
vm_command('shutdown');
});
- }
+ }
},
{
text: gettext('Stop'),
@@ -74,7 +94,7 @@ Ext.define('PVE.qemu.CmdMenu', {
}
vm_command("stop");
- });
+ });
}
},
{
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
@@ -31,11 +31,11 @@ Ext.define('PVE.OpenVzSummary', {
config: {
items: [
- {
+ {
xtype: 'titlebar',
docked: 'top',
items: [
- {
+ {
xtype: 'button',
align: 'right',
iconCls: 'refresh',
@@ -67,7 +67,7 @@ Ext.define('PVE.OpenVzSummary', {
if (!Ext.isDefined(values.mem)) {
return '-';
}
- return PVE.Utils.format_size(values.mem || 0) + " of " +
+ return PVE.Utils.format_size(values.mem || 0) + " of " +
PVE.Utils.format_size(values.maxmem);
},
cpuinfo: function(values) {
@@ -158,31 +158,43 @@ Ext.define('PVE.OpenVzSummary', {
me.vm_command("start", {});
}
},
- {
+ {
+ 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", {});
}
},
- {
+ {
text: gettext('Stop'),
handler: function() {
me.vm_command("stop", {});
}
},
- {
+ {
text: gettext('Migrate'),
handler: function() {
- PVE.Workspace.gotoPage('nodes/' + me.nodename + '/openvz/' + me.vmid + '/migrate');
+ PVE.Workspace.gotoPage('nodes/' + me.nodename + '/openvz/' + me.vmid + '/migrate');
}
},
- {
+ {
text: gettext('Console'),
handler: function() {
PVE.Utils.openConsoleWindow('html5', 'openvz', me.vmid, me.nodename);
}
},
- {
+ {
text: gettext('Spice'),
handler: function() {
PVE.Utils.openConsoleWindow('vv', 'openvz', me.vmid, me.nodename);
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
@@ -31,11 +31,11 @@ Ext.define('PVE.QemuSummary', {
config: {
items: [
- {
+ {
xtype: 'titlebar',
docked: 'top',
items: [
- {
+ {
xtype: 'button',
align: 'right',
iconCls: 'refresh',
@@ -67,7 +67,7 @@ Ext.define('PVE.QemuSummary', {
if (!Ext.isDefined(values.mem)) {
return '-';
}
- return PVE.Utils.format_size(values.mem || 0) + " of " +
+ return PVE.Utils.format_size(values.mem || 0) + " of " +
PVE.Utils.format_size(values.maxmem);
},
cpuinfo: function(values) {
@@ -131,8 +131,8 @@ Ext.define('PVE.QemuSummary', {
success: function(response) {
var d = response.result.data;
var names = ['name', 'memory', 'sockets', 'cores', 'ostype',
- 'bootdisk', /^net\d+/,
- /^ide\d+/, /^virtio\d+/, /^sata\d+/,
+ 'bootdisk', /^net\d+/,
+ /^ide\d+/, /^virtio\d+/, /^sata\d+/,
/^scsi\d+/, /^unused\d+/ ];
var kv = PVE.Workspace.obj_to_kv(d, names);
vmc.setData(kv);
@@ -161,31 +161,43 @@ Ext.define('PVE.QemuSummary', {
me.vm_command("start", {});
}
},
- {
+ {
+ 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", {});
}
},
- {
+ {
text: gettext('Stop'),
handler: function() {
me.vm_command("stop", {});
}
},
- {
+ {
text: gettext('Migrate'),
handler: function() {
- PVE.Workspace.gotoPage('nodes/' + me.nodename + '/qemu/' + me.vmid + '/migrate');
+ PVE.Workspace.gotoPage('nodes/' + me.nodename + '/qemu/' + me.vmid + '/migrate');
}
},
- {
+ {
text: gettext('Console'),
handler: function() {
PVE.Utils.openConsoleWindow('html5', 'kvm', me.vmid, me.nodename);
}
},
- {
+ {
text: gettext('Spice'),
handler: function() {
PVE.Utils.openConsoleWindow('vv', 'kvm', me.vmid, me.nodename);
--
1.9.1
More information about the pve-devel
mailing list