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