use strict;
use warnings;
-use POSIX qw(LONG_MAX);
+
+use Digest::MD5;
+use Digest::SHA;
use Filesys::Df;
-use Time::Local qw(timegm_nocheck);
use HTTP::Status qw(:constants);
-use PVE::pvecfg;
-use PVE::Tools;
+use JSON;
+use POSIX qw(LONG_MAX);
+use Time::Local qw(timegm_nocheck);
+use Socket;
+
use PVE::API2Tools;
-use PVE::ProcFSTools;
-use PVE::SafeSyslog;
+use PVE::APLInfo;
+use PVE::AccessControl;
use PVE::Cluster qw(cfs_read_file);
-use PVE::INotify;
+use PVE::DataCenterConfig;
use PVE::Exception qw(raise raise_perm_exc raise_param_exc);
-use PVE::RESTHandler;
-use PVE::RPCEnvironment;
-use PVE::JSONSchema qw(get_standard_option);
-use PVE::AccessControl;
-use PVE::Storage;
use PVE::Firewall;
-use PVE::LXC;
-use PVE::APLInfo;
-use PVE::Report;
-use PVE::HA::Env::PVE2;
use PVE::HA::Config;
+use PVE::HA::Env::PVE2;
+use PVE::INotify;
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::LXC;
+use PVE::ProcFSTools;
use PVE::QemuConfig;
use PVE::QemuServer;
-use PVE::API2::Subscription;
-use PVE::API2::Services;
-use PVE::API2::Network;
-use PVE::API2::Tasks;
-use PVE::API2::Scan;
-use PVE::API2::Storage::Status;
-use PVE::API2::Qemu;
-use PVE::API2::LXC;
-use PVE::API2::LXC::Status;
-use PVE::API2::VZDump;
+use PVE::RESTHandler;
+use PVE::RPCEnvironment;
+use PVE::RRD;
+use PVE::Report;
+use PVE::SafeSyslog;
+use PVE::Storage;
+use PVE::Tools;
+use PVE::pvecfg;
+
use PVE::API2::APT;
+use PVE::API2::Capabilities;
use PVE::API2::Ceph;
-use PVE::API2::Firewall::Host;
-use PVE::API2::Replication;
use PVE::API2::Certificates;
-use PVE::API2::NodeConfig;
-use PVE::API2::Hardware;
-use Digest::MD5;
-use Digest::SHA;
use PVE::API2::Disks;
-use PVE::DataCenterConfig;
-use PVE::RRD;
-use JSON;
-use Socket;
+use PVE::API2::Firewall::Host;
+use PVE::API2::Hardware;
+use PVE::API2::LXC::Status;
+use PVE::API2::LXC;
+use PVE::API2::Network;
+use PVE::API2::NodeConfig;
+use PVE::API2::Qemu::CPU;
+use PVE::API2::Qemu;
+use PVE::API2::Replication;
+use PVE::API2::Services;
+use PVE::API2::Storage::Scan;
+use PVE::API2::Storage::Status;
+use PVE::API2::Subscription;
+use PVE::API2::Tasks;
+use PVE::API2::VZDump;
my $have_sdn;
eval {
});
__PACKAGE__->register_method ({
- subclass => "PVE::API2::Scan",
+ subclass => "PVE::API2::Storage::Scan",
path => 'scan',
});
path => 'hardware',
});
+__PACKAGE__->register_method ({
+ subclass => "PVE::API2::Capabilities",
+ path => 'capabilities',
+});
__PACKAGE__->register_method ({
subclass => "PVE::API2::Storage::Status",
permissions => { user => 'all' },
description => "Node index.",
parameters => {
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
},
my ($param) = @_;
my $result = [
+ { name => 'aplinfo' },
+ { name => 'apt' },
+ { name => 'capabilities' },
{ name => 'ceph' },
+ { name => 'certificates' },
+ { name => 'config' },
{ name => 'disks' },
- { name => 'apt' },
- { name => 'version' },
- { name => 'syslog' },
+ { name => 'dns' },
+ { name => 'firewall' },
+ { name => 'hosts' },
{ name => 'journal' },
- { name => 'status' },
- { name => 'wakeonlan' },
- { name => 'subscription' },
+ { name => 'lxc' },
+ { name => 'netstat' },
+ { name => 'network' },
+ { name => 'qemu' },
+ { name => 'replication' },
{ name => 'report' },
- { name => 'tasks' },
{ name => 'rrd' }, # fixme: remove?
{ name => 'rrddata' },# fixme: remove?
- { name => 'replication' },
- { name => 'vncshell' },
- { name => 'termproxy' },
- { name => 'spiceshell' },
- { name => 'time' },
- { name => 'dns' },
- { name => 'services' },
{ name => 'scan' },
- { name => 'storage' },
- { name => 'qemu' },
- { name => 'lxc' },
- { name => 'vzdump' },
- { name => 'network' },
- { name => 'aplinfo' },
+ { name => 'services' },
+ { name => 'spiceshell' },
{ name => 'startall' },
+ { name => 'status' },
{ name => 'stopall' },
- { name => 'netstat' },
- { name => 'firewall' },
- { name => 'certificates' },
- { name => 'config' },
- { name => 'hosts' },
+ { name => 'storage' },
+ { name => 'subscription' },
+ { name => 'syslog' },
+ { name => 'tasks' },
+ { name => 'termproxy' },
+ { name => 'time' },
+ { name => 'version' },
+ { name => 'vncshell' },
+ { name => 'vzdump' },
+ { name => 'wakeonlan' },
];
push @$result, { name => 'sdn' } if $have_sdn;
permissions => { user => 'all' },
description => "API version details",
parameters => {
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
},
description => "Read node status",
proxyto => 'node',
parameters => {
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
},
},
},
returns => {
- type => "array",
- items => {
- type => "object",
- properties => {},
- },
+ type => "array",
+ items => {
+ type => "object",
+ properties => {},
+ },
},
code => sub {
my ($param) = @_;
my $res = [ ];
my $netdev = PVE::ProcFSTools::read_proc_net_dev();
- foreach my $dev (keys %$netdev) {
- next if $dev !~ m/^(?:tap|veth)([1-9]\d*)i(\d+)$/;
- my $vmid = $1;
- my $netid = $2;
-
- push(
- @$res,
- {
- vmid => $vmid,
- dev => "net$netid",
- in => $netdev->{$dev}->{transmit},
- out => $netdev->{$dev}->{receive},
- }
- );
+ foreach my $dev (sort keys %$netdev) {
+ next if $dev !~ m/^(?:tap|veth)([1-9]\d*)i(\d+)$/;
+ my ($vmid, $netid) = ($1, $2);
+
+ push @$res, {
+ vmid => $vmid,
+ dev => "net$netid",
+ in => $netdev->{$dev}->{transmit},
+ out => $netdev->{$dev}->{receive},
+ };
}
return $res;
description => "Reboot or shutdown a node.",
proxyto => 'node',
parameters => {
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
command => {
},
description => "Read node RRD statistics (returns PNG)",
parameters => {
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
timeframe => {
},
description => "Read node RRD statistics",
parameters => {
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
timeframe => {
},
protected => 1,
parameters => {
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
start => {
my $sslcert;
my $shell_cmd_map = {
- 'login' => [ '/bin/login', '-f', 'root' ],
- 'upgrade' => [ '/usr/bin/pveupgrade', '--shell' ],
- 'ceph_install' => [ '/usr/bin/pveceph', 'install' ],
+ 'login' => {
+ cmd => [ '/bin/login', '-f', 'root' ],
+ },
+ 'upgrade' => {
+ cmd => [ '/usr/bin/pveupgrade', '--shell' ],
+ },
+ 'ceph_install' => {
+ cmd => [ '/usr/bin/pveceph', 'install' ],
+ allow_args => 1,
+ },
};
sub get_shell_command {
- my ($user, $shellcmd) = @_;
+ my ($user, $shellcmd, $args) = @_;
+ my $cmd;
if ($user eq 'root@pam') {
if (defined($shellcmd) && exists($shell_cmd_map->{$shellcmd})) {
- return $shell_cmd_map->{$shellcmd};
+ my $def = $shell_cmd_map->{$shellcmd};
+ $cmd = [ @{$def->{cmd}} ]; # clone
+ if (defined($args) && $def->{allow_args}) {
+ push @$cmd, split("\0", $args);
+ }
} else {
- return [ '/bin/login', '-f', 'root' ];
+ $cmd = [ '/bin/login', '-f', 'root' ];
}
} else {
- return [ '/bin/login' ];
+ # non-root must always login for now, we do not have a superuser role!
+ $cmd = [ '/bin/login' ];
}
+ return $cmd;
}
+my $get_vnc_connection_info = sub {
+ my $node = shift;
+
+ my $remote_cmd = [];
+
+ my ($remip, $family);
+ if ($node ne 'localhost' && $node ne PVE::INotify::nodename()) {
+ ($remip, $family) = PVE::Cluster::remote_node_ip($node);
+ $remote_cmd = ['/usr/bin/ssh', '-e', 'none', '-t', $remip , '--'];
+ } else {
+ $family = PVE::Tools::get_host_address_family($node);
+ }
+ my $port = PVE::Tools::next_vnc_port($family);
+
+ return ($port, $remote_cmd);
+};
+
__PACKAGE__->register_method ({
name => 'vncshell',
path => 'vncshell',
},
description => "Creates a VNC Shell proxy.",
parameters => {
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
- upgrade => {
- type => 'boolean',
- description => "Deprecated, use the 'cmd' property instead! Run 'apt-get dist-upgrade' instead of normal shell.",
- optional => 1,
- default => 0,
- },
cmd => {
type => 'string',
description => "Run specific command or default to login.",
optional => 1,
default => 'login',
},
+ 'cmd-opts' => {
+ type => 'string',
+ description => "Add parameters to a command. Encoded as null terminated strings.",
+ requires => 'cmd',
+ optional => 1,
+ default => '',
+ },
websocket => {
optional => 1,
type => 'boolean',
},
},
returns => {
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {
user => { type => 'string' },
ticket => { type => 'string' },
my ($param) = @_;
my $rpcenv = PVE::RPCEnvironment::get();
-
my ($user, undef, $realm) = PVE::AccessControl::verify_username($rpcenv->get_user());
raise_perm_exc("realm != pam") if $realm ne 'pam';
- raise_perm_exc('user != root@pam') if $param->{upgrade} && $user ne 'root@pam';
+ if (defined($param->{cmd}) && $param->{cmd} eq 'upgrade' && $user ne 'root@pam') {
+ raise_perm_exc('user != root@pam');
+ }
my $node = $param->{node};
my $authpath = "/nodes/$node";
-
my $ticket = PVE::AccessControl::assemble_vnc_ticket($user, $authpath);
$sslcert = PVE::Tools::file_get_contents("/etc/pve/pve-root-ca.pem", 8192)
if !$sslcert;
- my ($remip, $family);
+ my ($port, $remcmd) = $get_vnc_connection_info->($node);
- if ($node ne PVE::INotify::nodename()) {
- ($remip, $family) = PVE::Cluster::remote_node_ip($node);
- } else {
- $family = PVE::Tools::get_host_address_family($node);
- }
-
- my $port = PVE::Tools::next_vnc_port($family);
-
- # NOTE: vncterm VNC traffic is already TLS encrypted,
- # so we select the fastest chipher here (or 'none'?)
- my $remcmd = $remip ?
- ['/usr/bin/ssh', '-e', 'none', '-t', $remip] : [];
-
- # FIXME: remove with 6.0
- if ($param->{upgrade}) {
- $param->{cmd} = 'upgrade';
- }
- my $shcmd = get_shell_command($user, $param->{cmd});
+ my $shcmd = get_shell_command($user, $param->{cmd}, $param->{'cmd-opts'});
my $timeout = 10;
- my $cmd = ['/usr/bin/vncterm', '-rfbport', $port,
- '-timeout', $timeout, '-authpath', $authpath,
- '-perm', 'Sys.Console'];
-
- if ($param->{width}) {
- push @$cmd, '-width', $param->{width};
- }
+ my $cmd = ['/usr/bin/vncterm',
+ '-rfbport', $port,
+ '-timeout', $timeout,
+ '-authpath', $authpath,
+ '-perm', 'Sys.Console',
+ ];
- if ($param->{height}) {
- push @$cmd, '-height', $param->{height};
- }
+ push @$cmd, '-width', $param->{width} if $param->{width};
+ push @$cmd, '-height', $param->{height} if $param->{height};
if ($param->{websocket}) {
$ENV{PVE_VNC_TICKET} = $ticket; # pass ticket to vncterm
additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
- upgrade => {
- type => 'boolean',
- description => "Deprecated, use the 'cmd' property instead! Run 'apt-get dist-upgrade' instead of normal shell.",
- optional => 1,
- default => 0,
- },
cmd => {
type => 'string',
description => "Run specific command or default to login.",
optional => 1,
default => 'login',
},
+ 'cmd-opts' => {
+ type => 'string',
+ description => "Add parameters to a command. Encoded as null terminated strings.",
+ requires => 'cmd',
+ optional => 1,
+ default => '',
+ },
},
},
returns => {
my ($param) = @_;
my $rpcenv = PVE::RPCEnvironment::get();
-
my ($user, undef, $realm) = PVE::AccessControl::verify_username($rpcenv->get_user());
-
- raise_perm_exc("realm != pam") if $realm ne 'pam';
+ raise_perm_exc("realm $realm != pam") if $realm ne 'pam';
my $node = $param->{node};
-
my $authpath = "/nodes/$node";
-
my $ticket = PVE::AccessControl::assemble_vnc_ticket($user, $authpath);
- my ($remip, $family);
-
- if ($node ne 'localhost' && $node ne PVE::INotify::nodename()) {
- ($remip, $family) = PVE::Cluster::remote_node_ip($node);
- } else {
- $family = PVE::Tools::get_host_address_family($node);
- }
+ my ($port, $remcmd) = $get_vnc_connection_info->($node);
- my $port = PVE::Tools::next_vnc_port($family);
-
- my $remcmd = $remip ?
- ['/usr/bin/ssh', '-e', 'none', '-t', $remip , '--'] : [];
- # FIXME: remove with 6.0
- if ($param->{upgrade}) {
- $param->{cmd} = 'upgrade';
- }
- my $shcmd = get_shell_command($user, $param->{cmd});
+ my $shcmd = get_shell_command($user, $param->{cmd}, $param->{'cmd-opts'});
my $realcmd = sub {
my $upid = shift;
syslog ('info', "starting termproxy $upid\n");
- my $cmd = ['/usr/bin/termproxy', $port, '--path', $authpath,
- '--perm', 'Sys.Console', '--'];
+ my $cmd = [
+ '/usr/bin/termproxy',
+ $port,
+ '--path', $authpath,
+ '--perm', 'Sys.Console',
+ '--'
+ ];
push @$cmd, @$remcmd, @$shcmd;
PVE::Tools::run_command($cmd);
};
-
my $upid = $rpcenv->fork_worker('vncshell', "", $user, $realcmd);
PVE::Tools::wait_for_vnc_port($port);
description => "Restricted to users on realm 'pam'. You also need to pass a valid ticket (vncticket).",
check => ['perm', '/nodes/{node}', [ 'Sys.Console' ]],
},
- description => "Opens a weksocket for VNC traffic.",
+ description => "Opens a websocket for VNC traffic.",
parameters => {
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
vncticket => {
},
description => "Creates a SPICE shell.",
parameters => {
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
proxy => get_standard_option('spice-proxy', { optional => 1 }),
- upgrade => {
- type => 'boolean',
- description => "Deprecated, use the 'cmd' property instead! Run 'apt-get dist-upgrade' instead of normal shell.",
- optional => 1,
- default => 0,
- },
cmd => {
type => 'string',
description => "Run specific command or default to login.",
optional => 1,
default => 'login',
},
+ 'cmd-opts' => {
+ type => 'string',
+ description => "Add parameters to a command. Encoded as null terminated strings.",
+ requires => 'cmd',
+ optional => 1,
+ default => '',
+ },
},
},
returns => get_standard_option('remote-viewer-config'),
raise_perm_exc("realm != pam") if $realm ne 'pam';
- raise_perm_exc('user != root@pam') if $param->{upgrade} && $user ne 'root@pam';
+ if (defined($param->{cmd}) && $param->{cmd} eq 'upgrade' && $user ne 'root@pam') {
+ raise_perm_exc('user != root@pam');
+ }
my $node = $param->{node};
my $proxy = $param->{proxy};
my $authpath = "/nodes/$node";
my $permissions = 'Sys.Console';
- # FIXME: remove with 6.0
- if ($param->{upgrade}) {
- $param->{cmd} = 'upgrade';
- }
- my $shcmd = get_shell_command($user, $param->{cmd});
+
+ my $shcmd = get_shell_command($user, $param->{cmd}, $param->{'cmd-opts'});
my $title = "Shell on '$node'";
description => "Read DNS settings.",
proxyto => 'node',
parameters => {
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
},
},
returns => {
type => "object",
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {
search => {
description => "Search domain for host-name lookup.",
proxyto => 'node',
protected => 1,
parameters => {
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
search => {
},
returns => {
type => "object",
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {
timezone => {
description => "Time zone",
proxyto => 'node',
protected => 1,
parameters => {
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
timezone => {
description => "Get list of appliances.",
proxyto => 'node',
parameters => {
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
},
proxyto => 'node',
protected => 1,
parameters => {
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
storage => get_standard_option('pve-storage-id', {
description => "The storage where the template will be stored",
completion => \&PVE::Storage::complete_storage_enabled,
}),
- template => { type => 'string',
- description => "The template which will downloaded",
- maxLength => 255,
- completion => \&complete_templet_repo,
+ template => {
+ type => 'string',
+ description => "The template which will downloaded",
+ maxLength => 255,
+ completion => \&complete_templet_repo,
},
},
},
my ($param) = @_;
my $rpcenv = PVE::RPCEnvironment::get();
-
my $user = $rpcenv->get_user();
my $node = $param->{node};
-
- my $list = PVE::APLInfo::load_data();
-
my $template = $param->{template};
- my $pd = $list->{all}->{$template};
- raise_param_exc({ template => "no such template"}) if !$pd;
+ my $list = PVE::APLInfo::load_data();
+ my $appliance = $list->{all}->{$template};
+ raise_param_exc({ template => "no such template"}) if !$appliance;
my $cfg = PVE::Storage::config();
my $scfg = PVE::Storage::storage_check_enabled($cfg, $param->{storage}, $node);
- die "unknown template type '$pd->{type}'\n"
- if !($pd->{type} eq 'openvz' || $pd->{type} eq 'lxc');
+ die "unknown template type '$appliance->{type}'\n"
+ if !($appliance->{type} eq 'openvz' || $appliance->{type} eq 'lxc');
die "storage '$param->{storage}' does not support templates\n"
if !$scfg->{content}->{vztmpl};
- my $src = $pd->{location};
my $tmpldir = PVE::Storage::get_vztmpl_dir($cfg, $param->{storage});
- my $dest = "$tmpldir/$template";
- my $tmpdest = "$tmpldir/${template}.tmp.$$";
-
- my $worker = sub {
- my $upid = shift;
-
- print "starting template download from: $src\n";
- print "target file: $dest\n";
-
- my $check_hash = sub {
- my ($template_info, $filename, $noerr) = @_;
-
- my $digest;
- my $expected;
-
- eval {
- open(my $fh, '<', $filename) or die "Can't open '$filename': $!";
- binmode($fh);
- if (defined($template_info->{sha512sum})) {
- $expected = $template_info->{sha512sum};
- $digest = Digest::SHA->new(512)->addfile($fh)->hexdigest;
- } elsif (defined($template_info->{md5sum})) {
- #fallback to MD5
- $expected = $template_info->{md5sum};
- $digest = Digest::MD5->new->addfile($fh)->hexdigest;
- } else {
- die "no expected checksum defined";
- }
- close($fh);
- };
-
- die "checking hash failed - $@\n" if $@ && !$noerr;
-
- return ($digest, $digest ? lc($digest) eq lc($expected) : 0);
- };
-
- eval {
- if (-f $dest) {
- my ($hash, $correct) = &$check_hash($pd, $dest, 1);
-
- if ($hash && $correct) {
- print "file already exists $hash - no need to download\n";
- return;
- }
- }
-
- local %ENV;
- my $dccfg = PVE::Cluster::cfs_read_file('datacenter.cfg');
- if ($dccfg->{http_proxy}) {
- $ENV{http_proxy} = $dccfg->{http_proxy};
- }
-
- my @cmd = ('/usr/bin/wget', '--progress=dot:mega', '-O', $tmpdest, $src);
- if (system (@cmd) != 0) {
- die "download failed - $!\n";
- }
- my ($hash, $correct) = &$check_hash($pd, $tmpdest);
+ my $worker = sub {
+ my $dccfg = PVE::Cluster::cfs_read_file('datacenter.cfg');
- die "could not calculate checksum\n" if !$hash;
-
- if (!$correct) {
- my $expected = $pd->{sha512sum} // $pd->{md5sum};
- die "wrong checksum: $hash != $expected\n";
- }
-
- if (!rename($tmpdest, $dest)) {
- die "unable to save file - $!\n";
- }
- };
- my $err = $@;
-
- unlink $tmpdest;
-
- if ($err) {
- print "\n";
- die $err if $err;
- }
-
- print "download finished\n";
+ PVE::Tools::download_file_from_url("$tmpldir/$template", $appliance->{location}, {
+ hash_required => 1,
+ sha512sum => $appliance->{sha512sum},
+ md5sum => $appliance->{md5sum},
+ http_proxy => $dccfg->{http_proxy},
+ });
};
- return $rpcenv->fork_worker('download', undef, $user, $worker);
+ my $upid = $rpcenv->fork_worker('download', $template, $user, $worker);
+
+ return $upid;
}});
__PACKAGE__->register_method({
description => "Gather various systems information about a node",
proxyto => 'node',
parameters => {
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
},
proxyto => 'node',
description => "Start all VMs and containers located on this node (by default only those with onboot=1).",
parameters => {
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
force => {
proxyto => 'node',
description => "Stop all VMs and Containers.",
parameters => {
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
vms => {
$upid = PVE::API2::LXC->migrate_vm({node => $nodename, vmid => $vmid, target => $target,
restart => $online });
} elsif ($type eq 'qemu') {
+ print STDERR "Check VM $vmid: ";
+ *STDERR->flush();
my $online = PVE::QemuServer::check_running($vmid, 1) ? 1 : 0;
+ my $preconditions = PVE::API2::Qemu->migrate_vm_precondition({node => $nodename, vmid => $vmid, target => $target});
+ my $invalidConditions = '';
+ if ($online && !$with_local_disks && scalar @{$preconditions->{local_disks}}) {
+ $invalidConditions .= "\n Has local disks: " .
+ join(', ', map { $_->{volid} } @{$preconditions->{local_disks}});
+ }
+
+ if (@{$preconditions->{local_resources}}) {
+ $invalidConditions .= "\n Has local resources: " . join(', ', @{$preconditions->{local_resources}});
+ }
+
+ if ($invalidConditions && $invalidConditions ne '') {
+ print STDERR "skip VM $vmid - precondition check failed:";
+ die "$invalidConditions\n";
+ }
+ print STDERR "precondition check passed\n";
print STDERR "Migrating VM $vmid\n";
- $upid = PVE::API2::Qemu->migrate_vm({node => $nodename, vmid => $vmid, target => $target,
- online => $online, 'with-local-disks' => $with_local_disks});
+
+ my $params = {
+ node => $nodename,
+ vmid => $vmid,
+ target => $target,
+ online => $online,
+ };
+ $params->{'with-local-disks'} = $with_local_disks if defined($with_local_disks);
+
+ $upid = PVE::API2::Qemu->migrate_vm($params);
} else {
die "unknown VM type '$type'\n";
}
$rpcenv->{type} = 'priv'; # to start tasks in background
my $vmlist = &$get_filtered_vmlist($nodename, $param->{vms}, 1, 1);
+ if (!scalar(keys %$vmlist)) {
+ warn "no virtual guests matched, nothing to do..\n";
+ return;
+ }
my $workers = {};
+ my $workers_started = 0;
foreach my $vmid (sort keys %$vmlist) {
my $d = $vmlist->{$vmid};
my $pid;
warn $@ if $@;
next if !$pid;
+ $workers_started++;
$workers->{$pid} = 1;
while (scalar(keys %$workers) >= $maxWorkers) {
foreach my $p (keys %$workers) {
}
while (scalar(keys %$workers)) {
foreach my $p (keys %$workers) {
+ # FIXME: what about PID re-use ?!?!
if (!PVE::ProcFSTools::check_process_running($p)) {
delete $workers->{$p};
}
}
sleep(1);
}
+ if ($workers_started <= 0) {
+ die "no migrations worker started...\n";
+ }
+ print STDERR "All jobs finished, used $workers_started workers in total.\n";
return;
};
permissions => { user => 'all' },
description => "Cluster node index.",
parameters => {
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {},
},
returns => {
foreach my $node (@$nodelist) {
my $can_audit = $rpcenv->check($authuser, "/nodes/$node", [ 'Sys.Audit' ], 1);
my $entry = PVE::API2Tools::extract_node_stats($node, $members, $rrd, !$can_audit);
- $entry->{ssl_fingerprint} = PVE::Cluster::get_node_fingerprint($node);
+
+ $entry->{ssl_fingerprint} = eval { PVE::Cluster::get_node_fingerprint($node) };
+ warn "$@" if $@;
+
push @$res, $entry;
}