]> git.proxmox.com Git - pve-manager.git/blame - PVE/API2/Nodes.pm
bump version to 4.2-19
[pve-manager.git] / PVE / API2 / Nodes.pm
CommitLineData
aff192e6
DM
1package PVE::API2::Nodes::Nodeinfo;
2
3use strict;
4use warnings;
b92400b6 5use POSIX qw(LONG_MAX);
aff192e6
DM
6use Filesys::Df;
7use Time::Local qw(timegm_nocheck);
40993593 8use HTTP::Status qw(:constants);
aff192e6
DM
9use PVE::pvecfg;
10use PVE::Tools;
b289829f 11use PVE::API2Tools;
aff192e6
DM
12use PVE::ProcFSTools;
13use PVE::SafeSyslog;
c9164975 14use PVE::Cluster qw(cfs_read_file);
aff192e6 15use PVE::INotify;
05b252dc 16use PVE::Exception qw(raise raise_perm_exc raise_param_exc);
aff192e6
DM
17use PVE::RESTHandler;
18use PVE::RPCEnvironment;
19use PVE::JSONSchema qw(get_standard_option);
20use PVE::AccessControl;
c9164975 21use PVE::Storage;
4a07fced 22use PVE::Firewall;
989b7743 23use PVE::LXC;
c9164975 24use PVE::APLInfo;
34ada77a 25use PVE::Report;
10a9a736 26use PVE::HA::Env::PVE2;
7e5cb2f0 27use PVE::HA::Config;
7141ae25 28use PVE::QemuConfig;
b92400b6 29use PVE::QemuServer;
21ace8d3 30use PVE::API2::Subscription;
aff192e6
DM
31use PVE::API2::Services;
32use PVE::API2::Network;
33use PVE::API2::Tasks;
34use PVE::API2::Storage::Scan;
35use PVE::API2::Storage::Status;
36use PVE::API2::Qemu;
989b7743 37use PVE::API2::LXC;
b38516eb 38use PVE::API2::LXC::Status;
bf58f8dd 39use PVE::API2::VZDump;
21299915 40use PVE::API2::APT;
38db610a 41use PVE::API2::Ceph;
4a07fced 42use PVE::API2::Firewall::Host;
3b81e495
FG
43use Digest::MD5;
44use Digest::SHA;
e781363f 45use PVE::API2::Disks;
aff192e6
DM
46use JSON;
47
48use base qw(PVE::RESTHandler);
49
50__PACKAGE__->register_method ({
51 subclass => "PVE::API2::Qemu",
52 path => 'qemu',
53});
54
989b7743
DM
55__PACKAGE__->register_method ({
56 subclass => "PVE::API2::LXC",
57 path => 'lxc',
58});
59
38db610a
DM
60__PACKAGE__->register_method ({
61 subclass => "PVE::API2::Ceph",
62 path => 'ceph',
63});
64
bf58f8dd
DM
65__PACKAGE__->register_method ({
66 subclass => "PVE::API2::VZDump",
67 path => 'vzdump',
68});
69
aff192e6
DM
70__PACKAGE__->register_method ({
71 subclass => "PVE::API2::Services",
72 path => 'services',
73});
74
21ace8d3
DM
75__PACKAGE__->register_method ({
76 subclass => "PVE::API2::Subscription",
77 path => 'subscription',
78});
79
aff192e6
DM
80__PACKAGE__->register_method ({
81 subclass => "PVE::API2::Network",
82 path => 'network',
83});
84
85__PACKAGE__->register_method ({
86 subclass => "PVE::API2::Tasks",
87 path => 'tasks',
88});
89
90__PACKAGE__->register_method ({
91 subclass => "PVE::API2::Storage::Scan",
92 path => 'scan',
93});
94
95__PACKAGE__->register_method ({
96 subclass => "PVE::API2::Storage::Status",
97 path => 'storage',
98});
99
e781363f
DC
100__PACKAGE__->register_method ({
101 subclass => "PVE::API2::Disks",
102 path => 'disks',
103});
104
21299915
DM
105__PACKAGE__->register_method ({
106 subclass => "PVE::API2::APT",
107 path => 'apt',
108});
109
4a07fced
DM
110__PACKAGE__->register_method ({
111 subclass => "PVE::API2::Firewall::Host",
112 path => 'firewall',
113});
114
aff192e6
DM
115__PACKAGE__->register_method ({
116 name => 'index',
117 path => '',
118 method => 'GET',
119 permissions => { user => 'all' },
120 description => "Node index.",
121 parameters => {
122 additionalProperties => 0,
123 properties => {
124 node => get_standard_option('pve-node'),
125 },
126 },
127 returns => {
128 type => 'array',
129 items => {
130 type => "object",
131 properties => {},
132 },
133 links => [ { rel => 'child', href => "{name}" } ],
134 },
135 code => sub {
136 my ($param) = @_;
137
138 my $result = [
38db610a 139 { name => 'ceph' },
e781363f 140 { name => 'disks' },
21299915 141 { name => 'apt' },
8747a9ec 142 { name => 'version' },
aff192e6
DM
143 { name => 'syslog' },
144 { name => 'status' },
21ace8d3 145 { name => 'subscription' },
34ada77a 146 { name => 'report' },
aff192e6
DM
147 { name => 'tasks' },
148 { name => 'rrd' }, # fixme: remove?
149 { name => 'rrddata' },# fixme: remove?
150 { name => 'vncshell' },
2d802f8c 151 { name => 'spiceshell' },
aff192e6
DM
152 { name => 'time' },
153 { name => 'dns' },
154 { name => 'services' },
155 { name => 'scan' },
156 { name => 'storage' },
aff192e6 157 { name => 'qemu' },
989b7743 158 { name => 'lxc' },
bf58f8dd 159 { name => 'vzdump' },
aff192e6 160 { name => 'network' },
c9164975 161 { name => 'aplinfo' },
b92400b6
DM
162 { name => 'startall' },
163 { name => 'stopall' },
0455911d 164 { name => 'netstat' },
4a07fced 165 { name => 'firewall' },
aff192e6
DM
166 ];
167
168 return $result;
169 }});
170
8747a9ec
DM
171__PACKAGE__->register_method ({
172 name => 'version',
173 path => 'version',
174 method => 'GET',
175 proxyto => 'node',
176 permissions => { user => 'all' },
177 description => "API version details",
178 parameters => {
179 additionalProperties => 0,
180 properties => {
181 node => get_standard_option('pve-node'),
182 },
183 },
184 returns => {
185 type => "object",
186 properties => {
187 version => { type => 'string' },
188 release => { type => 'string' },
189 repoid => { type => 'string' },
190 },
191 },
192 code => sub {
193 my ($resp, $param) = @_;
194
195 return PVE::pvecfg::version_info();
196 }});
197
aff192e6
DM
198__PACKAGE__->register_method({
199 name => 'status',
200 path => 'status',
201 method => 'GET',
202 permissions => {
7d020b42 203 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
aff192e6
DM
204 },
205 description => "Read node status",
206 proxyto => 'node',
207 parameters => {
208 additionalProperties => 0,
209 properties => {
210 node => get_standard_option('pve-node'),
211 },
212 },
213 returns => {
214 type => "object",
215 properties => {
216
217 },
218 },
219 code => sub {
220 my ($param) = @_;
221
222 my $res = {
223 uptime => 0,
224 idle => 0,
225 };
226
227 my ($uptime, $idle) = PVE::ProcFSTools::read_proc_uptime();
228 $res->{uptime} = $uptime;
229
230 my ($avg1, $avg5, $avg15) = PVE::ProcFSTools::read_loadavg();
231 $res->{loadavg} = [ $avg1, $avg5, $avg15];
232
233 my ($sysname, $nodename, $release, $version, $machine) = POSIX::uname();
234
235 $res->{kversion} = "$sysname $release $version";
236
237 $res->{cpuinfo} = PVE::ProcFSTools::read_cpuinfo();
238
239 my $stat = PVE::ProcFSTools::read_proc_stat();
240 $res->{cpu} = $stat->{cpu};
241 $res->{wait} = $stat->{wait};
242
243 my $meminfo = PVE::ProcFSTools::read_meminfo();
244 $res->{memory} = {
245 free => $meminfo->{memfree},
246 total => $meminfo->{memtotal},
247 used => $meminfo->{memused},
248 };
20539e0c
DM
249
250 $res->{ksm} = {
251 shared => $meminfo->{memshared},
252 };
253
aff192e6
DM
254 $res->{swap} = {
255 free => $meminfo->{swapfree},
256 total => $meminfo->{swaptotal},
257 used => $meminfo->{swapused},
258 };
259
260 $res->{pveversion} = PVE::pvecfg::package() . "/" .
8747a9ec 261 PVE::pvecfg::version_text();
aff192e6
DM
262
263 my $dinfo = df('/', 1); # output is bytes
264
265 $res->{rootfs} = {
266 total => $dinfo->{blocks},
267 avail => $dinfo->{bavail},
268 used => $dinfo->{used},
269 free => $dinfo->{bavail} - $dinfo->{used},
270 };
271
272 return $res;
273 }});
274
0455911d
SP
275__PACKAGE__->register_method({
276 name => 'netstat',
277 path => 'netstat',
278 method => 'GET',
279 permissions => {
280 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
281 },
282 description => "Read tap/vm network device interface counters",
283 proxyto => 'node',
284 parameters => {
285 additionalProperties => 0,
286 properties => {
287 node => get_standard_option('pve-node'),
288 },
289 },
290 returns => {
291 type => "array",
292 items => {
293 type => "object",
294 properties => {},
295 },
296 },
297 code => sub {
298 my ($param) = @_;
299
300 my $res = [ ];
301
302 my $netdev = PVE::ProcFSTools::read_proc_net_dev();
303 foreach my $dev (keys %$netdev) {
675946f2 304 next if $dev !~ m/^(?:tap|veth)([1-9]\d*)i(\d+)$/;
0455911d
SP
305 my $vmid = $1;
306 my $netid = $2;
307
308 push(
309 @$res,
310 {
311 vmid => $vmid,
312 dev => "net$netid",
313 in => $netdev->{$dev}->{transmit},
314 out => $netdev->{$dev}->{receive},
315 }
316 );
317 }
318
319 return $res;
320 }});
321
87c3e931
SP
322__PACKAGE__->register_method({
323 name => 'execute',
324 path => 'execute',
40993593 325 method => 'POST',
87c3e931
SP
326 permissions => {
327 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
328 },
40993593 329 description => "Execute multiple commands in order.",
87c3e931 330 proxyto => 'node',
40993593 331 protected => 1, # avoid problems with proxy code
87c3e931
SP
332 parameters => {
333 additionalProperties => 0,
334 properties => {
335 node => get_standard_option('pve-node'),
40993593
DM
336 commands => {
337 description => "JSON encoded array of commands.",
338 type => "string",
339 }
87c3e931
SP
340 },
341 },
342 returns => {
343 type => 'array',
344 properties => {
345
346 },
347 },
348 code => sub {
349 my ($param) = @_;
350 my $res = [];
351
40993593
DM
352 my $rpcenv = PVE::RPCEnvironment::get();
353 my $user = $rpcenv->get_user();
354
355 my $commands = eval { decode_json($param->{commands}); };
356
87c3e931
SP
357 die "commands param did not contain valid JSON: $@" if $@;
358 die "commands is not an array" if ref($commands) ne "ARRAY";
359
360 foreach my $cmd (@$commands) {
87c3e931 361 eval {
40993593
DM
362 die "$cmd is not a valid command" if (ref($cmd) ne "HASH" || !$cmd->{path} || !$cmd->{method});
363
364 $cmd->{args} //= {};
365
366 my $path = "nodes/$param->{node}/$cmd->{path}";
367
368 my $uri_param = {};
369 my ($handler, $info) = PVE::API2->find_handler($cmd->{method}, $path, $uri_param);
370 if (!$handler || !$info) {
371 die "no handler for '$path'\n";
372 }
373
374 foreach my $p (keys %{$cmd->{args}}) {
375 raise_param_exc({ $p => "duplicate parameter" }) if defined($uri_param->{$p});
376 $uri_param->{$p} = $cmd->{args}->{$p};
377 }
378
379 # check access permissions
380 $rpcenv->check_api2_permissions($info->{permissions}, $user, $uri_param);
381
382 push @$res, {
383 status => HTTP_OK,
a51ceeb7 384 data => $handler->handle($info, $uri_param),
40993593 385 };
87c3e931 386 };
40993593
DM
387 if (my $err = $@) {
388 my $resp = { status => HTTP_INTERNAL_SERVER_ERROR };
389 if (ref($err) eq "PVE::Exception") {
390 $resp->{status} = $err->{code} if $err->{code};
391 $resp->{errors} = $err->{errors} if $err->{errors};
392 $resp->{message} = $err->{msg};
393 } else {
394 $resp->{message} = $err;
395 }
396 push @$res, $resp;
397 }
87c3e931
SP
398 }
399
400 return $res;
401 }});
402
403
aff192e6
DM
404__PACKAGE__->register_method({
405 name => 'node_cmd',
406 path => 'status',
407 method => 'POST',
408 permissions => {
7d020b42 409 check => ['perm', '/nodes/{node}', [ 'Sys.PowerMgmt' ]],
aff192e6
DM
410 },
411 protected => 1,
412 description => "Reboot or shutdown a node.",
413 proxyto => 'node',
414 parameters => {
415 additionalProperties => 0,
416 properties => {
417 node => get_standard_option('pve-node'),
418 command => {
419 description => "Specify the command.",
420 type => 'string',
421 enum => [qw(reboot shutdown)],
422 },
423 },
424 },
425 returns => { type => "null" },
426 code => sub {
427 my ($param) = @_;
428
429 if ($param->{command} eq 'reboot') {
430 system ("(sleep 2;/sbin/reboot)&");
431 } elsif ($param->{command} eq 'shutdown') {
432 system ("(sleep 2;/sbin/poweroff)&");
433 }
434
435 return undef;
436 }});
437
438
439__PACKAGE__->register_method({
440 name => 'rrd',
441 path => 'rrd',
442 method => 'GET',
443 protected => 1, # fixme: can we avoid that?
444 permissions => {
7d020b42 445 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
aff192e6
DM
446 },
447 description => "Read node RRD statistics (returns PNG)",
448 parameters => {
449 additionalProperties => 0,
450 properties => {
451 node => get_standard_option('pve-node'),
452 timeframe => {
453 description => "Specify the time frame you are interested in.",
454 type => 'string',
455 enum => [ 'hour', 'day', 'week', 'month', 'year' ],
456 },
457 ds => {
458 description => "The list of datasources you want to display.",
459 type => 'string', format => 'pve-configid-list',
460 },
461 cf => {
462 description => "The RRD consolidation function",
463 type => 'string',
464 enum => [ 'AVERAGE', 'MAX' ],
465 optional => 1,
466 },
467 },
468 },
469 returns => {
470 type => "object",
471 properties => {
472 filename => { type => 'string' },
473 },
474 },
475 code => sub {
476 my ($param) = @_;
477
478 return PVE::Cluster::create_rrd_graph(
479 "pve2-node/$param->{node}", $param->{timeframe},
480 $param->{ds}, $param->{cf});
481
482 }});
483
484__PACKAGE__->register_method({
485 name => 'rrddata',
486 path => 'rrddata',
487 method => 'GET',
488 protected => 1, # fixme: can we avoid that?
489 permissions => {
7d020b42 490 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
aff192e6
DM
491 },
492 description => "Read node RRD statistics",
493 parameters => {
494 additionalProperties => 0,
495 properties => {
496 node => get_standard_option('pve-node'),
497 timeframe => {
498 description => "Specify the time frame you are interested in.",
499 type => 'string',
500 enum => [ 'hour', 'day', 'week', 'month', 'year' ],
501 },
502 cf => {
503 description => "The RRD consolidation function",
504 type => 'string',
505 enum => [ 'AVERAGE', 'MAX' ],
506 optional => 1,
507 },
508 },
509 },
510 returns => {
511 type => "array",
512 items => {
513 type => "object",
514 properties => {},
515 },
516 },
517 code => sub {
518 my ($param) = @_;
519
520 return PVE::Cluster::create_rrd_data(
521 "pve2-node/$param->{node}", $param->{timeframe}, $param->{cf});
522 }});
523
524__PACKAGE__->register_method({
525 name => 'syslog',
526 path => 'syslog',
527 method => 'GET',
528 description => "Read system log",
529 proxyto => 'node',
530 permissions => {
7d020b42 531 check => ['perm', '/nodes/{node}', [ 'Sys.Syslog' ]],
aff192e6
DM
532 },
533 protected => 1,
534 parameters => {
535 additionalProperties => 0,
536 properties => {
537 node => get_standard_option('pve-node'),
538 start => {
539 type => 'integer',
540 minimum => 0,
541 optional => 1,
542 },
543 limit => {
544 type => 'integer',
545 minimum => 0,
546 optional => 1,
547 },
01b753b6
TL
548 since => {
549 type=> 'string',
550 pattern => '^\d{4}-\d{2}-\d{2}( \d{2}:\d{2}(:\d{2})?)?$',
551 description => "Display all log since this date-time string.",
552 optional => 1,
553 },
554 until => {
555 type=> 'string',
556 pattern => '^\d{4}-\d{2}-\d{2}( \d{2}:\d{2}(:\d{2})?)?$',
557 description => "Display all log until this date-time string.",
558 optional => 1,
559 },
aff192e6
DM
560 },
561 },
562 returns => {
563 type => 'array',
564 items => {
565 type => "object",
566 properties => {
567 n => {
568 description=> "Line number",
569 type=> 'integer',
570 },
571 t => {
572 description=> "Line text",
573 type => 'string',
574 }
575 }
576 }
577 },
578 code => sub {
579 my ($param) = @_;
580
aff192e6
DM
581 my $rpcenv = PVE::RPCEnvironment::get();
582 my $user = $rpcenv->get_user();
583 my $node = $param->{node};
584
01b753b6
TL
585 my ($count, $lines) = PVE::Tools::dump_journal($param->{start}, $param->{limit},
586 $param->{since}, $param->{until});
ff9c330c
DM
587
588 $rpcenv->set_result_attrib('total', $count);
01b753b6
TL
589
590 return $lines;
ff9c330c
DM
591 }});
592
aff192e6
DM
593my $sslcert;
594
595__PACKAGE__->register_method ({
596 name => 'vncshell',
597 path => 'vncshell',
598 method => 'POST',
599 protected => 1,
600 permissions => {
d0289a19 601 description => "Restricted to users on realm 'pam'",
7d020b42 602 check => ['perm', '/nodes/{node}', [ 'Sys.Console' ]],
aff192e6
DM
603 },
604 description => "Creates a VNC Shell proxy.",
605 parameters => {
606 additionalProperties => 0,
607 properties => {
608 node => get_standard_option('pve-node'),
3a76893d
DM
609 upgrade => {
610 type => 'boolean',
611 description => "Run 'apt-get dist-upgrade' instead of normal shell.",
612 optional => 1,
613 default => 0,
614 },
8dfca17e
DM
615 websocket => {
616 optional => 1,
617 type => 'boolean',
618 description => "use websocket instead of standard vnc.",
619 },
aff192e6
DM
620 },
621 },
622 returns => {
623 additionalProperties => 0,
624 properties => {
625 user => { type => 'string' },
626 ticket => { type => 'string' },
627 cert => { type => 'string' },
628 port => { type => 'integer' },
629 upid => { type => 'string' },
630 },
631 },
632 code => sub {
633 my ($param) = @_;
634
635 my $rpcenv = PVE::RPCEnvironment::get();
636
d553e535 637 my ($user, undef, $realm) = PVE::AccessControl::verify_username($rpcenv->get_user());
d0289a19
DM
638
639 raise_perm_exc("realm != pam") if $realm ne 'pam';
aff192e6 640
3a76893d
DM
641 raise_perm_exc('user != root@pam') if $param->{upgrade} && $user ne 'root@pam';
642
aff192e6
DM
643 my $node = $param->{node};
644
57ebda08
DM
645 my $authpath = "/nodes/$node";
646
647 my $ticket = PVE::AccessControl::assemble_vnc_ticket($user, $authpath);
648
aff192e6
DM
649 $sslcert = PVE::Tools::file_get_contents("/etc/pve/pve-root-ca.pem", 8192)
650 if !$sslcert;
651
eb6d7497 652 my ($remip, $family);
b179a622 653
aff192e6 654 if ($node ne PVE::INotify::nodename()) {
eb6d7497
WB
655 ($remip, $family) = PVE::Cluster::remote_node_ip($node);
656 } else {
657 $family = PVE::Tools::get_host_address_family($node);
aff192e6
DM
658 }
659
eb6d7497
WB
660 my $port = PVE::Tools::next_vnc_port($family);
661
aff192e6
DM
662 # NOTE: vncterm VNC traffic is already TLS encrypted,
663 # so we select the fastest chipher here (or 'none'?)
664 my $remcmd = $remip ?
f2c68844 665 ['/usr/bin/ssh', '-t', $remip] : [];
aff192e6 666
3a76893d
DM
667 my $shcmd;
668
669 if ($user eq 'root@pam') {
670 if ($param->{upgrade}) {
ea5eb430
DM
671 my $upgradecmd = "pveupgrade --shell";
672 $upgradecmd = PVE::Tools::shellquote($upgradecmd) if $remip;
673 $shcmd = [ '/bin/bash', '-c', $upgradecmd ];
3a76893d
DM
674 } else {
675 $shcmd = [ '/bin/bash', '-l' ];
676 }
677 } else {
678 $shcmd = [ '/bin/login' ];
679 }
aff192e6
DM
680
681 my $timeout = 10;
682
6d394492 683 my $cmd = ['/usr/bin/vncterm', '-rfbport', $port,
57ebda08 684 '-timeout', $timeout, '-authpath', $authpath,
8dfca17e
DM
685 '-perm', 'Sys.Console'];
686
687 if ($param->{websocket}) {
688 $ENV{PVE_VNC_TICKET} = $ticket; # pass ticket to vncterm
689 push @$cmd, '-notls', '-listen', 'localhost';
690 }
691
692 push @$cmd, '-c', @$remcmd, @$shcmd;
aff192e6
DM
693
694 my $realcmd = sub {
695 my $upid = shift;
696
697 syslog ('info', "starting vnc proxy $upid\n");
698
6d394492 699 my $cmdstr = join (' ', @$cmd);
aff192e6
DM
700 syslog ('info', "launch command: $cmdstr");
701
6d394492
DM
702 eval {
703 foreach my $k (keys %ENV) {
8dfca17e 704 next if $k eq 'PVE_VNC_TICKET';
6d394492
DM
705 next if $k eq 'PATH' || $k eq 'TERM' || $k eq 'USER' || $k eq 'HOME';
706 delete $ENV{$k};
707 }
708 $ENV{PWD} = '/';
709
710 PVE::Tools::run_command($cmd, errmsg => "vncterm failed");
711 };
712 if (my $err = $@) {
713 syslog ('err', $err);
aff192e6
DM
714 }
715
716 return;
717 };
718
719 my $upid = $rpcenv->fork_worker('vncshell', "", $user, $realcmd);
6806a0f8
DM
720
721 PVE::Tools::wait_for_vnc_port($port);
aff192e6
DM
722
723 return {
724 user => $user,
725 ticket => $ticket,
726 port => $port,
727 upid => $upid,
728 cert => $sslcert,
729 };
730 }});
731
8dfca17e
DM
732__PACKAGE__->register_method({
733 name => 'vncwebsocket',
734 path => 'vncwebsocket',
735 method => 'GET',
736 permissions => {
737 description => "Restricted to users on realm 'pam'. You also need to pass a valid ticket (vncticket).",
738 check => ['perm', '/nodes/{node}', [ 'Sys.Console' ]],
739 },
740 description => "Opens a weksocket for VNC traffic.",
741 parameters => {
742 additionalProperties => 0,
743 properties => {
744 node => get_standard_option('pve-node'),
745 vncticket => {
746 description => "Ticket from previous call to vncproxy.",
747 type => 'string',
748 maxLength => 512,
749 },
750 port => {
751 description => "Port number returned by previous vncproxy call.",
752 type => 'integer',
753 minimum => 5900,
754 maximum => 5999,
755 },
756 },
757 },
758 returns => {
759 type => "object",
760 properties => {
761 port => { type => 'string' },
762 },
763 },
764 code => sub {
765 my ($param) = @_;
766
767 my $rpcenv = PVE::RPCEnvironment::get();
768
769 my ($user, undef, $realm) = PVE::AccessControl::verify_username($rpcenv->get_user());
770
771 raise_perm_exc("realm != pam") if $realm ne 'pam';
772
773 my $authpath = "/nodes/$param->{node}";
774
775 PVE::AccessControl::verify_vnc_ticket($param->{vncticket}, $user, $authpath);
776
777 my $port = $param->{port};
778
779 return { port => $port };
780 }});
781
2d802f8c
DM
782__PACKAGE__->register_method ({
783 name => 'spiceshell',
784 path => 'spiceshell',
785 method => 'POST',
786 protected => 1,
787 proxyto => 'node',
788 permissions => {
789 description => "Restricted to users on realm 'pam'",
790 check => ['perm', '/nodes/{node}', [ 'Sys.Console' ]],
791 },
7774df78 792 description => "Creates a SPICE shell.",
2d802f8c
DM
793 parameters => {
794 additionalProperties => 0,
795 properties => {
796 node => get_standard_option('pve-node'),
7774df78 797 proxy => get_standard_option('spice-proxy', { optional => 1 }),
2d802f8c
DM
798 upgrade => {
799 type => 'boolean',
800 description => "Run 'apt-get dist-upgrade' instead of normal shell.",
801 optional => 1,
802 default => 0,
803 },
804 },
805 },
7774df78 806 returns => get_standard_option('remote-viewer-config'),
2d802f8c
DM
807 code => sub {
808 my ($param) = @_;
809
810 my $rpcenv = PVE::RPCEnvironment::get();
811 my $authuser = $rpcenv->get_user();
812
813 my ($user, undef, $realm) = PVE::AccessControl::verify_username($authuser);
814
815 raise_perm_exc("realm != pam") if $realm ne 'pam';
816
817 raise_perm_exc('user != root@pam') if $param->{upgrade} && $user ne 'root@pam';
818
819 my $node = $param->{node};
820 my $proxy = $param->{proxy};
2d802f8c
DM
821
822 my $authpath = "/nodes/$node";
b289829f 823 my $permissions = 'Sys.Console';
2d802f8c
DM
824
825 my $shcmd;
826
827 if ($user eq 'root@pam') {
828 if ($param->{upgrade}) {
829 my $upgradecmd = "pveupgrade --shell";
830 $shcmd = [ '/bin/bash', '-c', $upgradecmd ];
831 } else {
832 $shcmd = [ '/bin/bash', '-l' ];
833 }
834 } else {
835 $shcmd = [ '/bin/login' ];
836 }
837
b289829f 838 my $title = "Shell on '$node'";
2d802f8c 839
b289829f 840 return PVE::API2Tools::run_spiceterm($authpath, $permissions, 0, $node, $proxy, $title, $shcmd);
2d802f8c
DM
841 }});
842
aff192e6
DM
843__PACKAGE__->register_method({
844 name => 'dns',
845 path => 'dns',
846 method => 'GET',
847 permissions => {
7d020b42 848 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
aff192e6
DM
849 },
850 description => "Read DNS settings.",
851 proxyto => 'node',
852 parameters => {
853 additionalProperties => 0,
854 properties => {
855 node => get_standard_option('pve-node'),
856 },
857 },
858 returns => {
859 type => "object",
860 additionalProperties => 0,
861 properties => {
862 search => {
863 description => "Search domain for host-name lookup.",
864 type => 'string',
865 optional => 1,
866 },
867 dns1 => {
868 description => 'First name server IP address.',
869 type => 'string',
870 optional => 1,
871 },
872 dns2 => {
873 description => 'Second name server IP address.',
874 type => 'string',
875 optional => 1,
876 },
877 dns3 => {
878 description => 'Third name server IP address.',
879 type => 'string',
880 optional => 1,
881 },
882 },
883 },
884 code => sub {
885 my ($param) = @_;
886
887 my $res = PVE::INotify::read_file('resolvconf');
888
889 return $res;
890 }});
891
892__PACKAGE__->register_method({
893 name => 'update_dns',
894 path => 'dns',
895 method => 'PUT',
896 description => "Write DNS settings.",
d0289a19
DM
897 permissions => {
898 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
899 },
aff192e6
DM
900 proxyto => 'node',
901 protected => 1,
902 parameters => {
903 additionalProperties => 0,
904 properties => {
905 node => get_standard_option('pve-node'),
906 search => {
907 description => "Search domain for host-name lookup.",
908 type => 'string',
909 },
910 dns1 => {
911 description => 'First name server IP address.',
7a9486a7 912 type => 'string', format => 'ip',
aff192e6
DM
913 optional => 1,
914 },
915 dns2 => {
916 description => 'Second name server IP address.',
7a9486a7 917 type => 'string', format => 'ip',
aff192e6
DM
918 optional => 1,
919 },
920 dns3 => {
921 description => 'Third name server IP address.',
7a9486a7 922 type => 'string', format => 'ip',
aff192e6
DM
923 optional => 1,
924 },
925 },
926 },
927 returns => { type => "null" },
928 code => sub {
929 my ($param) = @_;
930
931 PVE::INotify::update_file('resolvconf', $param);
932
933 return undef;
934 }});
935
936__PACKAGE__->register_method({
937 name => 'time',
938 path => 'time',
939 method => 'GET',
940 permissions => {
7d020b42
DM
941 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
942 },
aff192e6
DM
943 description => "Read server time and time zone settings.",
944 proxyto => 'node',
945 parameters => {
946 additionalProperties => 0,
947 properties => {
948 node => get_standard_option('pve-node'),
949 },
950 },
951 returns => {
952 type => "object",
953 additionalProperties => 0,
954 properties => {
955 timezone => {
956 description => "Time zone",
957 type => 'string',
958 },
959 time => {
960 description => "Seconds since 1970-01-01 00:00:00 UTC.",
961 type => 'integer',
962 minimum => 1297163644,
963 },
964 localtime => {
965 description => "Seconds since 1970-01-01 00:00:00 (local time)",
966 type => 'integer',
967 minimum => 1297163644,
968 },
969 },
970 },
971 code => sub {
972 my ($param) = @_;
973
974 my $ctime = time();
975 my $ltime = timegm_nocheck(localtime($ctime));
976 my $res = {
977 timezone => PVE::INotify::read_file('timezone'),
978 time => time(),
979 localtime => $ltime,
980 };
981
982 return $res;
983 }});
984
985__PACKAGE__->register_method({
986 name => 'set_timezone',
987 path => 'time',
988 method => 'PUT',
989 description => "Set time zone.",
d0289a19
DM
990 permissions => {
991 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
992 },
aff192e6
DM
993 proxyto => 'node',
994 protected => 1,
995 parameters => {
996 additionalProperties => 0,
997 properties => {
998 node => get_standard_option('pve-node'),
999 timezone => {
1000 description => "Time zone. The file '/usr/share/zoneinfo/zone.tab' contains the list of valid names.",
1001 type => 'string',
1002 },
1003 },
1004 },
1005 returns => { type => "null" },
1006 code => sub {
1007 my ($param) = @_;
1008
1009 PVE::INotify::write_file('timezone', $param->{timezone});
1010
1011 return undef;
1012 }});
1013
c9164975
DM
1014__PACKAGE__->register_method({
1015 name => 'aplinfo',
1016 path => 'aplinfo',
1017 method => 'GET',
1018 permissions => {
1019 user => 'all',
1020 },
1021 description => "Get list of appliances.",
1022 proxyto => 'node',
1023 parameters => {
1024 additionalProperties => 0,
1025 properties => {
1026 node => get_standard_option('pve-node'),
1027 },
1028 },
1029 returns => {
1030 type => 'array',
1031 items => {
1032 type => "object",
1033 properties => {},
1034 },
1035 },
1036 code => sub {
1037 my ($param) = @_;
1038
1039 my $res = [];
1040
1041 my $list = PVE::APLInfo::load_data();
1042
1043 foreach my $template (keys %{$list->{all}}) {
1044 my $pd = $list->{all}->{$template};
1045 next if $pd->{'package'} eq 'pve-web-news';
1046 push @$res, $pd;
1047 }
1048
1049 return $res;
1050 }});
1051
0532bd28
DM
1052__PACKAGE__->register_method({
1053 name => 'apl_download',
1054 path => 'aplinfo',
1055 method => 'POST',
1056 permissions => {
1057 check => ['perm', '/storage/{storage}', ['Datastore.AllocateTemplate']],
1058 },
1059 description => "Download appliance templates.",
1060 proxyto => 'node',
1061 protected => 1,
1062 parameters => {
1063 additionalProperties => 0,
1064 properties => {
1065 node => get_standard_option('pve-node'),
82282acf 1066 storage => get_standard_option('pve-storage-id', {
62180d0f 1067 description => "The storage where the template will be stored",
82282acf
WL
1068 completion => \&PVE::Storage::complete_storage_enabled,
1069 }),
1070 template => { type => 'string',
1071 description => "The template wich will downloaded",
1072 maxLength => 255,
1073 completion => \&complete_templet_repo,
1074 },
0532bd28
DM
1075 },
1076 },
1077 returns => { type => "string" },
1078 code => sub {
1079 my ($param) = @_;
1080
1081 my $rpcenv = PVE::RPCEnvironment::get();
1082
1083 my $user = $rpcenv->get_user();
1084
1085 my $node = $param->{node};
1086
1087 my $list = PVE::APLInfo::load_data();
1088
1089 my $template = $param->{template};
1090 my $pd = $list->{all}->{$template};
1091
1092 raise_param_exc({ template => "no such template"}) if !$pd;
1093
bbcfdc08 1094 my $cfg = PVE::Storage::config();
0532bd28
DM
1095 my $scfg = PVE::Storage::storage_check_enabled($cfg, $param->{storage}, $node);
1096
761a2b31
DM
1097 die "unknown template type '$pd->{type}'\n"
1098 if !($pd->{type} eq 'openvz' || $pd->{type} eq 'lxc');
0532bd28
DM
1099
1100 die "storage '$param->{storage}' does not support templates\n"
1101 if !$scfg->{content}->{vztmpl};
1102
1103 my $src = $pd->{location};
1104 my $tmpldir = PVE::Storage::get_vztmpl_dir($cfg, $param->{storage});
1105 my $dest = "$tmpldir/$template";
1106 my $tmpdest = "$tmpldir/${template}.tmp.$$";
1107
1108 my $worker = sub {
1109 my $upid = shift;
1110
1111 print "starting template download from: $src\n";
1112 print "target file: $dest\n";
1113
3b81e495
FG
1114 my $check_hash = sub {
1115 my ($template_info, $filename, $noerr) = @_;
1116
1117 my $digest;
1118 my $expected;
1119
1120 eval {
79be6db3
FG
1121 open(my $fh, '<', $filename) or die "Can't open '$filename': $!";
1122 binmode($fh);
95f99e8c
FG
1123 if (defined($template_info->{sha512sum})) {
1124 $expected = $template_info->{sha512sum};
79be6db3 1125 $digest = Digest::SHA->new(512)->addfile($fh)->hexdigest;
95f99e8c 1126 } elsif (defined($template_info->{md5sum})) {
3b81e495 1127 #fallback to MD5
95f99e8c 1128 $expected = $template_info->{md5sum};
3b81e495 1129 $digest = Digest::MD5->new->addfile($fh)->hexdigest;
3b81e495
FG
1130 } else {
1131 die "no expected checksum defined";
1132 }
79be6db3 1133 close($fh);
3b81e495
FG
1134 };
1135
1136 die "checking hash failed - $@\n" if $@ && !$noerr;
0532bd28 1137
3b81e495
FG
1138 return ($digest, $digest ? lc($digest) eq lc($expected) : 0);
1139 };
1140
1141 eval {
0532bd28 1142 if (-f $dest) {
3b81e495 1143 my ($hash, $correct) = &$check_hash($pd, $dest, 1);
0532bd28 1144
3b81e495
FG
1145 if ($hash && $correct) {
1146 print "file already exists $hash - no need to download\n";
0532bd28
DM
1147 return;
1148 }
1149 }
1150
1151 local %ENV;
1152 my $dccfg = PVE::Cluster::cfs_read_file('datacenter.cfg');
1153 if ($dccfg->{http_proxy}) {
1154 $ENV{http_proxy} = $dccfg->{http_proxy};
1155 }
1156
1157 my @cmd = ('/usr/bin/wget', '--progress=dot:mega', '-O', $tmpdest, $src);
1158 if (system (@cmd) != 0) {
1159 die "download failed - $!\n";
1160 }
1161
3b81e495
FG
1162 my ($hash, $correct) = &$check_hash($pd, $tmpdest);
1163
1164 die "could not calculate checksum\n" if !$hash;
0532bd28 1165
3b81e495
FG
1166 if (!$correct) {
1167 my $expected = $pd->{sha512sum} // $pd->{md5sum};
1168 die "wrong checksum: $hash != $expected\n";
0532bd28
DM
1169 }
1170
c351eda9 1171 if (!rename($tmpdest, $dest)) {
0532bd28
DM
1172 die "unable to save file - $!\n";
1173 }
1174 };
1175 my $err = $@;
1176
1177 unlink $tmpdest;
1178
1179 if ($err) {
1180 print "\n";
1181 die $err if $err;
1182 }
1183
1184 print "download finished\n";
1185 };
1186
1187 return $rpcenv->fork_worker('download', undef, $user, $worker);
1188 }});
1189
34ada77a
EK
1190__PACKAGE__->register_method({
1191 name => 'report',
1192 path => 'report',
1193 method => 'GET',
1194 permissions => {
1195 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
1196 },
6bbdf981 1197 protected => 1,
34ada77a
EK
1198 description => "Gather various systems information about a node",
1199 proxyto => 'node',
1200 parameters => {
1201 additionalProperties => 0,
1202 properties => {
1203 node => get_standard_option('pve-node'),
1204 },
1205 },
1206 returns => {
cdb5a209 1207 type => 'string',
34ada77a
EK
1208 },
1209 code => sub {
cdb5a209 1210 return PVE::Report::generate();
34ada77a 1211 }});
0532bd28 1212
b92400b6
DM
1213my $get_start_stop_list = sub {
1214 my ($nodename, $autostart) = @_;
1215
b92400b6
DM
1216 my $vmlist = PVE::Cluster::get_vmlist();
1217
1218 my $resList = {};
1219 foreach my $vmid (keys %{$vmlist->{ids}}) {
1220 my $d = $vmlist->{ids}->{$vmid};
1221 my $startup;
1222
1223 eval {
1224 return if $d->{node} ne $nodename;
1225
1226 my $bootorder = LONG_MAX;
1227
04b2004b 1228 my $conf;
2c27e4b7 1229 if ($d->{type} eq 'lxc') {
e93408d6 1230 $conf = PVE::LXC::Config->load_config($vmid);
2c27e4b7 1231 } elsif ($d->{type} eq 'qemu') {
7141ae25 1232 $conf = PVE::QemuConfig->load_config($vmid);
b92400b6
DM
1233 } else {
1234 die "unknown VM type '$d->{type}'\n";
1235 }
1236
04b2004b
TL
1237 return if $autostart && !$conf->{onboot};
1238
1239 if ($conf->{startup}) {
1240 $startup = PVE::JSONSchema::pve_parse_startup_order($conf->{startup});
1241 $startup->{order} = $bootorder if !defined($startup->{order});
1242 } else {
1243 $startup = { order => $bootorder };
1244 }
1245
b92400b6
DM
1246 $resList->{$startup->{order}}->{$vmid} = $startup;
1247 $resList->{$startup->{order}}->{$vmid}->{type} = $d->{type};
1248 };
1249 warn $@ if $@;
1250 }
1251
1252 return $resList;
1253};
1254
1255__PACKAGE__->register_method ({
1256 name => 'startall',
1257 path => 'startall',
1258 method => 'POST',
1259 protected => 1,
17e3b3b2
CS
1260 permissions => {
1261 check => ['perm', '/', [ 'VM.PowerMgmt' ]],
1262 },
9c8a09a7 1263 proxyto => 'node',
b92400b6
DM
1264 description => "Start all VMs and containers (when onboot=1).",
1265 parameters => {
1266 additionalProperties => 0,
1267 properties => {
1268 node => get_standard_option('pve-node'),
c09c7160
AD
1269 force => {
1270 optional => 1,
1271 type => 'boolean',
1272 description => "force if onboot=0.",
1273 },
b92400b6
DM
1274 },
1275 },
1276 returns => {
1277 type => 'string',
1278 },
1279 code => sub {
1280 my ($param) = @_;
1281
1282 my $rpcenv = PVE::RPCEnvironment::get();
1283 my $authuser = $rpcenv->get_user();
1284
1285 my $nodename = $param->{node};
1286 $nodename = PVE::INotify::nodename() if $nodename eq 'localhost';
1287
c09c7160
AD
1288 my $force = $param->{force};
1289
b92400b6
DM
1290 my $code = sub {
1291
1292 $rpcenv->{type} = 'priv'; # to start tasks in background
1293
efedd8c3
TL
1294 # wait up to 60 seconds for quorum
1295 for (my $i = 60; $i >= 0; $i--) {
b92400b6
DM
1296 last if PVE::Cluster::check_cfs_quorum($i != 0 ? 1 : 0);
1297 sleep(1);
1298 }
c09c7160
AD
1299 my $autostart = $force ? undef : 1;
1300 my $startList = &$get_start_stop_list($nodename, $autostart);
3f12bcfb
DM
1301
1302 # Note: use numeric sorting with <=>
5eec2b04 1303 foreach my $order (sort {$a <=> $b} keys %$startList) {
b92400b6
DM
1304 my $vmlist = $startList->{$order};
1305
3f12bcfb 1306 foreach my $vmid (sort {$a <=> $b} keys %$vmlist) {
b92400b6
DM
1307 my $d = $vmlist->{$vmid};
1308
6677f640
CS
1309 # skip templates
1310 my $conf;
1311 if ($d->{type} eq 'lxc') {
1312 $conf = PVE::LXC::Config->load_config($vmid);
1313 next if PVE::LXC::Config->is_template($conf);
1314 } elsif ($d->{type} eq 'qemu') {
1315 $conf = PVE::QemuConfig->load_config($vmid);
1316 next if PVE::QemuConfig->is_template($conf);
1317 } else {
1318 die "unknown VM type '$d->{type}'\n";
1319 }
1320
92c11ee1
CS
1321 # skip ha managed VMs (started by pve-ha-manager)
1322 next if PVE::HA::Config::vm_is_ha_managed($vmid);
1323
b92400b6
DM
1324 PVE::Cluster::check_cfs_quorum(); # abort when we loose quorum
1325
1326 eval {
1327 my $default_delay = 0;
1328 my $upid;
1329
2c27e4b7
DM
1330 if ($d->{type} eq 'lxc') {
1331 return if PVE::LXC::check_running($vmid);
1332 print STDERR "Starting CT $vmid\n";
1b5e56f2 1333 $upid = PVE::API2::LXC::Status->vm_start({node => $nodename, vmid => $vmid });
2c27e4b7 1334 } elsif ($d->{type} eq 'qemu') {
d6c49392 1335 $default_delay = 3; # to reduce load
b92400b6
DM
1336 return if PVE::QemuServer::check_running($vmid, 1);
1337 print STDERR "Starting VM $vmid\n";
1338 $upid = PVE::API2::Qemu->vm_start({node => $nodename, vmid => $vmid });
1339 } else {
1340 die "unknown VM type '$d->{type}'\n";
1341 }
1342
1343 my $res = PVE::Tools::upid_decode($upid);
1344 while (PVE::ProcFSTools::check_process_running($res->{pid})) {
1345 sleep(1);
1346 }
1347
1348 my $status = PVE::Tools::upid_read_status($upid);
1349 if ($status eq 'OK') {
1350 # use default delay to reduce load
1351 my $delay = defined($d->{up}) ? int($d->{up}) : $default_delay;
1352 if ($delay > 0) {
1353 print STDERR "Waiting for $delay seconds (startup delay)\n" if $d->{up};
1354 for (my $i = 0; $i < $delay; $i++) {
1355 sleep(1);
1356 }
1357 }
1358 } else {
2c27e4b7
DM
1359 if ($d->{type} eq 'lxc') {
1360 print STDERR "Starting CT $vmid failed: $status\n";
1361 } elsif ($d->{type} eq 'qemu') {
1362 print STDERR "Starting VM $vmid failed: status\n";
1363 }
b92400b6
DM
1364 }
1365 };
1366 warn $@ if $@;
1367 }
1368 }
1369 return;
1370 };
1371
1372 return $rpcenv->fork_worker('startall', undef, $authuser, $code);
1373 }});
1374
1375my $create_stop_worker = sub {
1376 my ($nodename, $type, $vmid, $down_timeout) = @_;
1377
1378 my $upid;
2c27e4b7
DM
1379 if ($type eq 'lxc') {
1380 return if !PVE::LXC::check_running($vmid);
1381 my $timeout = defined($down_timeout) ? int($down_timeout) : 60;
1382 print STDERR "Stopping CT $vmid (timeout = $timeout seconds)\n";
1b5e56f2 1383 $upid = PVE::API2::LXC::Status->vm_shutdown({node => $nodename, vmid => $vmid,
2c27e4b7
DM
1384 timeout => $timeout, forceStop => 1 });
1385 } elsif ($type eq 'qemu') {
b92400b6
DM
1386 return if !PVE::QemuServer::check_running($vmid, 1);
1387 my $timeout = defined($down_timeout) ? int($down_timeout) : 60*3;
88ba9a1d 1388 print STDERR "Stopping VM $vmid (timeout = $timeout seconds)\n";
b92400b6
DM
1389 $upid = PVE::API2::Qemu->vm_shutdown({node => $nodename, vmid => $vmid,
1390 timeout => $timeout, forceStop => 1 });
1391 } else {
1392 die "unknown VM type '$type'\n";
1393 }
1394
37bec895 1395 return $upid;
b92400b6
DM
1396};
1397
1398__PACKAGE__->register_method ({
1399 name => 'stopall',
1400 path => 'stopall',
1401 method => 'POST',
1402 protected => 1,
17e3b3b2
CS
1403 permissions => {
1404 check => ['perm', '/', [ 'VM.PowerMgmt' ]],
1405 },
9c8a09a7 1406 proxyto => 'node',
b92400b6
DM
1407 description => "Stop all VMs and Containers.",
1408 parameters => {
1409 additionalProperties => 0,
1410 properties => {
1411 node => get_standard_option('pve-node'),
1412 },
1413 },
1414 returns => {
1415 type => 'string',
1416 },
1417 code => sub {
1418 my ($param) = @_;
1419
1420 my $rpcenv = PVE::RPCEnvironment::get();
1421 my $authuser = $rpcenv->get_user();
1422
1423 my $nodename = $param->{node};
1424 $nodename = PVE::INotify::nodename() if $nodename eq 'localhost';
1425
1426 my $code = sub {
1427
1428 $rpcenv->{type} = 'priv'; # to start tasks in background
1429
1430 my $stopList = &$get_start_stop_list($nodename);
1431
1432 my $cpuinfo = PVE::ProcFSTools::read_cpuinfo();
89ceb802
TL
1433 my $datacenterconfig = cfs_read_file('datacenter.cfg');
1434 # if not set by user spawn max cpu count number of workers
1435 my $maxWorkers = $datacenterconfig->{max_workers} || $cpuinfo->{cpus};
b92400b6
DM
1436
1437 foreach my $order (sort {$b <=> $a} keys %$stopList) {
1438 my $vmlist = $stopList->{$order};
1439 my $workers = {};
37bec895
DM
1440
1441 my $finish_worker = sub {
1442 my $pid = shift;
1443 my $d = $workers->{$pid};
1444 return if !$d;
1445 delete $workers->{$pid};
1446
1447 syslog('info', "end task $d->{upid}");
1448 };
1449
b92400b6 1450 foreach my $vmid (sort {$b <=> $a} keys %$vmlist) {
92c11ee1
CS
1451 # skip ha managed VMs (stopped by pve-ha-manager)
1452 next if PVE::HA::Config::vm_is_ha_managed($vmid);
1453
b92400b6 1454 my $d = $vmlist->{$vmid};
37bec895
DM
1455 my $upid;
1456 eval { $upid = &$create_stop_worker($nodename, $d->{type}, $vmid, $d->{down}); };
b92400b6 1457 warn $@ if $@;
37bec895
DM
1458 next if !$upid;
1459
1460 my $res = PVE::Tools::upid_decode($upid, 1);
1461 next if !$res;
1462
1463 my $pid = $res->{pid};
1464
1465 $workers->{$pid} = { type => $d->{type}, upid => $upid, vmid => $vmid };
b92400b6
DM
1466 while (scalar(keys %$workers) >= $maxWorkers) {
1467 foreach my $p (keys %$workers) {
1468 if (!PVE::ProcFSTools::check_process_running($p)) {
37bec895 1469 &$finish_worker($p);
b92400b6
DM
1470 }
1471 }
1472 sleep(1);
1473 }
1474 }
1475 while (scalar(keys %$workers)) {
1476 foreach my $p (keys %$workers) {
1477 if (!PVE::ProcFSTools::check_process_running($p)) {
37bec895 1478 &$finish_worker($p);
b92400b6
DM
1479 }
1480 }
1481 sleep(1);
1482 }
1483 }
37bec895
DM
1484
1485 syslog('info', "all VMs and CTs stopped");
1486
b92400b6
DM
1487 return;
1488 };
1489
1490 return $rpcenv->fork_worker('stopall', undef, $authuser, $code);
b92400b6
DM
1491 }});
1492
9c8a09a7
AD
1493my $create_migrate_worker = sub {
1494 my ($nodename, $type, $vmid, $target) = @_;
1495
1496 my $upid;
2c27e4b7
DM
1497 if ($type eq 'lxc') {
1498 my $online = PVE::LXC::check_running($vmid) ? 1 : 0;
1499 print STDERR "Migrating CT $vmid\n";
1500 $upid = PVE::API2::LXC->migrate_vm({node => $nodename, vmid => $vmid, target => $target,
1501 online => $online });
1502 } elsif ($type eq 'qemu') {
9c8a09a7
AD
1503 my $online = PVE::QemuServer::check_running($vmid, 1) ? 1 : 0;
1504 print STDERR "Migrating VM $vmid\n";
1505 $upid = PVE::API2::Qemu->migrate_vm({node => $nodename, vmid => $vmid, target => $target,
2c27e4b7 1506 online => $online });
9c8a09a7
AD
1507 } else {
1508 die "unknown VM type '$type'\n";
1509 }
1510
1511 my $res = PVE::Tools::upid_decode($upid);
1512
1513 return $res->{pid};
1514};
1515
1516__PACKAGE__->register_method ({
1517 name => 'migrateall',
1518 path => 'migrateall',
1519 method => 'POST',
1520 proxyto => 'node',
1521 protected => 1,
17e3b3b2
CS
1522 permissions => {
1523 check => ['perm', '/', [ 'VM.Migrate' ]],
1524 },
9c8a09a7
AD
1525 description => "Migrate all VMs and Containers.",
1526 parameters => {
1527 additionalProperties => 0,
1528 properties => {
1529 node => get_standard_option('pve-node'),
1530 target => get_standard_option('pve-node', { description => "Target node." }),
1531 maxworkers => {
89ceb802
TL
1532 description => "Maximal number of parallel migration job." .
1533 " If not set use 'max_workers' from datacenter.cfg," .
1534 " one of both must be set!",
1535 optional => 1,
9c8a09a7
AD
1536 type => 'integer',
1537 minimum => 1
1538 },
1539 },
1540 },
1541 returns => {
1542 type => 'string',
1543 },
1544 code => sub {
1545 my ($param) = @_;
1546
1547 my $rpcenv = PVE::RPCEnvironment::get();
1548 my $authuser = $rpcenv->get_user();
1549
1550 my $nodename = $param->{node};
1551 $nodename = PVE::INotify::nodename() if $nodename eq 'localhost';
1552
1553 my $target = $param->{target};
89ceb802
TL
1554
1555 my $datacenterconfig = cfs_read_file('datacenter.cfg');
1556 # prefer parameter over datacenter cfg settings
1557 my $maxWorkers = $param->{maxworkers} || $datacenterconfig->{max_workers} ||
1558 die "either 'maxworkers' parameter or max_workers in datacenter.cfg must be set!\n";
9c8a09a7
AD
1559
1560 my $code = sub {
1561
1562 $rpcenv->{type} = 'priv'; # to start tasks in background
1563
1564 my $migrateList = &$get_start_stop_list($nodename);
1565
1566 foreach my $order (sort {$b <=> $a} keys %$migrateList) {
1567 my $vmlist = $migrateList->{$order};
1568 my $workers = {};
1569 foreach my $vmid (sort {$b <=> $a} keys %$vmlist) {
1570 my $d = $vmlist->{$vmid};
1571 my $pid;
1572 eval { $pid = &$create_migrate_worker($nodename, $d->{type}, $vmid, $target); };
1573 warn $@ if $@;
1574 next if !$pid;
1575
1576 $workers->{$pid} = 1;
1577 while (scalar(keys %$workers) >= $maxWorkers) {
1578 foreach my $p (keys %$workers) {
1579 if (!PVE::ProcFSTools::check_process_running($p)) {
1580 delete $workers->{$p};
1581 }
1582 }
1583 sleep(1);
1584 }
1585 }
1586 while (scalar(keys %$workers)) {
1587 foreach my $p (keys %$workers) {
1588 if (!PVE::ProcFSTools::check_process_running($p)) {
1589 delete $workers->{$p};
1590 }
1591 }
1592 sleep(1);
1593 }
1594 }
1595 return;
1596 };
1597
1598 return $rpcenv->fork_worker('migrateall', undef, $authuser, $code);
1599
1600 }});
1601
82282acf
WL
1602# bash completion helper
1603
1604sub complete_templet_repo {
1605 my ($cmdname, $pname, $cvalue) = @_;
1606
1607 my $repo = PVE::APLInfo::load_data();
1608 my $res = [];
1609 foreach my $templ (keys %{$repo->{all}}) {
1610 next if $templ !~ m/^$cvalue/;
1611 push @$res, $templ;
1612 }
1613
1614 return $res;
1615}
1616
aff192e6
DM
1617package PVE::API2::Nodes;
1618
1619use strict;
1620use warnings;
1621
1622use PVE::SafeSyslog;
1623use PVE::Cluster;
1624use PVE::RESTHandler;
1625use PVE::RPCEnvironment;
b193e4ac 1626use PVE::API2Tools;
aff192e6
DM
1627
1628use base qw(PVE::RESTHandler);
1629
1630__PACKAGE__->register_method ({
1631 subclass => "PVE::API2::Nodes::Nodeinfo",
1632 path => '{node}',
1633});
1634
1635__PACKAGE__->register_method ({
1636 name => 'index',
1637 path => '',
1638 method => 'GET',
1639 permissions => { user => 'all' },
1640 description => "Cluster node index.",
1641 parameters => {
1642 additionalProperties => 0,
1643 properties => {},
1644 },
1645 returns => {
1646 type => 'array',
1647 items => {
1648 type => "object",
1649 properties => {},
1650 },
b193e4ac 1651 links => [ { rel => 'child', href => "{node}" } ],
aff192e6
DM
1652 },
1653 code => sub {
1654 my ($param) = @_;
1655
1656 my $clinfo = PVE::Cluster::get_clinfo();
1657 my $res = [];
1658
b193e4ac
DM
1659 my $nodelist = PVE::Cluster::get_nodelist();
1660 my $members = PVE::Cluster::get_members();
aff192e6
DM
1661 my $rrd = PVE::Cluster::rrd_dump();
1662
b193e4ac
DM
1663 foreach my $node (@$nodelist) {
1664 my $entry = PVE::API2Tools::extract_node_stats($node, $members, $rrd);
aff192e6
DM
1665 push @$res, $entry;
1666 }
1667
1668 return $res;
1669 }});
1670
16711;