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