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