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