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