]> git.proxmox.com Git - pve-manager.git/blame - PVE/API2/Nodes.pm
api: nodes: simplify appliance download code
[pve-manager.git] / PVE / API2 / Nodes.pm
CommitLineData
aff192e6
DM
1package PVE::API2::Nodes::Nodeinfo;
2
3use strict;
4use warnings;
5a378425
TL
5
6use Digest::MD5;
7use Digest::SHA;
aff192e6 8use Filesys::Df;
40993593 9use HTTP::Status qw(:constants);
5a378425
TL
10use JSON;
11use POSIX qw(LONG_MAX);
12use Time::Local qw(timegm_nocheck);
13use Socket;
14
b289829f 15use PVE::API2Tools;
5a378425
TL
16use PVE::APLInfo;
17use PVE::AccessControl;
c9164975 18use PVE::Cluster qw(cfs_read_file);
5a378425 19use PVE::DataCenterConfig;
05b252dc 20use PVE::Exception qw(raise raise_perm_exc raise_param_exc);
4a07fced 21use PVE::Firewall;
7e5cb2f0 22use PVE::HA::Config;
5a378425
TL
23use PVE::HA::Env::PVE2;
24use PVE::INotify;
25use PVE::JSONSchema qw(get_standard_option);
26use PVE::LXC;
27use PVE::ProcFSTools;
7141ae25 28use PVE::QemuConfig;
b92400b6 29use PVE::QemuServer;
5a378425
TL
30use PVE::RESTHandler;
31use PVE::RPCEnvironment;
32use PVE::RRD;
33use PVE::Report;
34use PVE::SafeSyslog;
35use PVE::Storage;
36use PVE::Tools;
37use PVE::pvecfg;
38
21299915 39use PVE::API2::APT;
c6c4b278 40use PVE::API2::Capabilities;
38db610a 41use PVE::API2::Ceph;
036475f8 42use PVE::API2::Certificates;
e781363f 43use PVE::API2::Disks;
5a378425
TL
44use PVE::API2::Firewall::Host;
45use PVE::API2::Hardware;
46use PVE::API2::LXC::Status;
47use PVE::API2::LXC;
48use PVE::API2::Network;
49use PVE::API2::NodeConfig;
50use PVE::API2::Qemu::CPU;
5a378425
TL
51use PVE::API2::Qemu;
52use PVE::API2::Replication;
53use PVE::API2::Services;
54use PVE::API2::Storage::Scan;
55use PVE::API2::Storage::Status;
56use PVE::API2::Subscription;
57use PVE::API2::Tasks;
58use PVE::API2::VZDump;
aff192e6 59
4be427a0
AD
60my $have_sdn;
61eval {
bb654699 62 require PVE::API2::Network::SDN::Zones::Status;
4be427a0
AD
63 $have_sdn = 1;
64};
65
aff192e6
DM
66use base qw(PVE::RESTHandler);
67
68__PACKAGE__->register_method ({
f9d26e09 69 subclass => "PVE::API2::Qemu",
aff192e6
DM
70 path => 'qemu',
71});
72
989b7743 73__PACKAGE__->register_method ({
f9d26e09 74 subclass => "PVE::API2::LXC",
989b7743
DM
75 path => 'lxc',
76});
77
38db610a 78__PACKAGE__->register_method ({
f9d26e09 79 subclass => "PVE::API2::Ceph",
38db610a
DM
80 path => 'ceph',
81});
82
bf58f8dd 83__PACKAGE__->register_method ({
f9d26e09 84 subclass => "PVE::API2::VZDump",
bf58f8dd
DM
85 path => 'vzdump',
86});
87
aff192e6 88__PACKAGE__->register_method ({
f9d26e09 89 subclass => "PVE::API2::Services",
aff192e6
DM
90 path => 'services',
91});
92
21ace8d3 93__PACKAGE__->register_method ({
f9d26e09 94 subclass => "PVE::API2::Subscription",
21ace8d3
DM
95 path => 'subscription',
96});
97
aff192e6 98__PACKAGE__->register_method ({
f9d26e09 99 subclass => "PVE::API2::Network",
aff192e6
DM
100 path => 'network',
101});
102
103__PACKAGE__->register_method ({
f9d26e09 104 subclass => "PVE::API2::Tasks",
aff192e6
DM
105 path => 'tasks',
106});
107
108__PACKAGE__->register_method ({
f5b6ccb1 109 subclass => "PVE::API2::Storage::Scan",
aff192e6
DM
110 path => 'scan',
111});
112
523d5f48
TL
113__PACKAGE__->register_method ({
114 subclass => "PVE::API2::Hardware",
115 path => 'hardware',
116});
117
c6c4b278
TL
118__PACKAGE__->register_method ({
119 subclass => "PVE::API2::Capabilities",
120 path => 'capabilities',
121});
523d5f48 122
aff192e6 123__PACKAGE__->register_method ({
f9d26e09 124 subclass => "PVE::API2::Storage::Status",
aff192e6
DM
125 path => 'storage',
126});
127
e781363f
DC
128__PACKAGE__->register_method ({
129 subclass => "PVE::API2::Disks",
130 path => 'disks',
131});
132
21299915 133__PACKAGE__->register_method ({
f9d26e09 134 subclass => "PVE::API2::APT",
21299915
DM
135 path => 'apt',
136});
137
4a07fced 138__PACKAGE__->register_method ({
f9d26e09 139 subclass => "PVE::API2::Firewall::Host",
4a07fced
DM
140 path => 'firewall',
141});
142
892821fd
DM
143__PACKAGE__->register_method ({
144 subclass => "PVE::API2::Replication",
145 path => 'replication',
146});
147
036475f8
FG
148__PACKAGE__->register_method ({
149 subclass => "PVE::API2::Certificates",
150 path => 'certificates',
151});
152
153
c4f78bb7
FG
154__PACKAGE__->register_method ({
155 subclass => "PVE::API2::NodeConfig",
156 path => 'config',
157});
158
4be427a0
AD
159if ($have_sdn) {
160 __PACKAGE__->register_method ({
bb654699
AD
161 subclass => "PVE::API2::Network::SDN::Zones::Status",
162 path => 'sdn/zones',
4be427a0 163 });
48181607
TL
164
165__PACKAGE__->register_method ({
166 name => 'sdnindex',
167 path => 'sdn',
168 method => 'GET',
169 permissions => { user => 'all' },
170 description => "SDN index.",
171 parameters => {
172 additionalProperties => 0,
173 properties => {
174 node => get_standard_option('pve-node'),
175 },
176 },
177 returns => {
178 type => 'array',
179 items => {
180 type => "object",
181 properties => {},
182 },
183 links => [ { rel => 'child', href => "{name}" } ],
184 },
185 code => sub {
186 my ($param) = @_;
187
188 my $result = [
189 { name => 'zones' },
190 ];
191 return $result;
192 }});
4be427a0
AD
193}
194
aff192e6 195__PACKAGE__->register_method ({
f9d26e09
TL
196 name => 'index',
197 path => '',
aff192e6
DM
198 method => 'GET',
199 permissions => { user => 'all' },
200 description => "Node index.",
201 parameters => {
6110ed03 202 additionalProperties => 0,
aff192e6
DM
203 properties => {
204 node => get_standard_option('pve-node'),
205 },
206 },
207 returns => {
208 type => 'array',
209 items => {
210 type => "object",
211 properties => {},
212 },
213 links => [ { rel => 'child', href => "{name}" } ],
214 },
215 code => sub {
216 my ($param) = @_;
f9d26e09 217
aff192e6 218 my $result = [
784d6ee9
TL
219 { name => 'aplinfo' },
220 { name => 'apt' },
c6c4b278 221 { name => 'capabilities' },
38db610a 222 { name => 'ceph' },
784d6ee9
TL
223 { name => 'certificates' },
224 { name => 'config' },
e781363f 225 { name => 'disks' },
784d6ee9
TL
226 { name => 'dns' },
227 { name => 'firewall' },
228 { name => 'hosts' },
952280b4 229 { name => 'journal' },
784d6ee9
TL
230 { name => 'lxc' },
231 { name => 'netstat' },
232 { name => 'network' },
233 { name => 'qemu' },
234 { name => 'replication' },
34ada77a 235 { name => 'report' },
aff192e6
DM
236 { name => 'rrd' }, # fixme: remove?
237 { name => 'rrddata' },# fixme: remove?
aff192e6 238 { name => 'scan' },
784d6ee9
TL
239 { name => 'services' },
240 { name => 'spiceshell' },
b92400b6 241 { name => 'startall' },
784d6ee9 242 { name => 'status' },
b92400b6 243 { name => 'stopall' },
784d6ee9
TL
244 { name => 'storage' },
245 { name => 'subscription' },
246 { name => 'syslog' },
247 { name => 'tasks' },
248 { name => 'termproxy' },
249 { name => 'time' },
250 { name => 'version' },
251 { name => 'vncshell' },
252 { name => 'vzdump' },
253 { name => 'wakeonlan' },
952280b4 254 ];
aff192e6 255
bb654699
AD
256 push @$result, { name => 'sdn' } if $have_sdn;
257
aff192e6
DM
258 return $result;
259 }});
260
8747a9ec 261__PACKAGE__->register_method ({
f9d26e09 262 name => 'version',
8747a9ec
DM
263 path => 'version',
264 method => 'GET',
265 proxyto => 'node',
266 permissions => { user => 'all' },
267 description => "API version details",
268 parameters => {
6110ed03 269 additionalProperties => 0,
8747a9ec
DM
270 properties => {
271 node => get_standard_option('pve-node'),
272 },
273 },
274 returns => {
275 type => "object",
276 properties => {
180a86d3
TL
277 version => {
278 type => 'string',
279 description => 'The current installed pve-manager package version',
280 },
281 release => {
282 type => 'string',
283 description => 'The current installed Proxmox VE Release',
284 },
285 repoid => {
286 type => 'string',
287 description => 'The short git commit hash ID from which this version was build',
288 },
8747a9ec
DM
289 },
290 },
291 code => sub {
292 my ($resp, $param) = @_;
f9d26e09 293
8747a9ec
DM
294 return PVE::pvecfg::version_info();
295 }});
296
aff192e6 297__PACKAGE__->register_method({
f9d26e09
TL
298 name => 'status',
299 path => 'status',
aff192e6
DM
300 method => 'GET',
301 permissions => {
7d020b42 302 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
aff192e6
DM
303 },
304 description => "Read node status",
305 proxyto => 'node',
306 parameters => {
6110ed03 307 additionalProperties => 0,
aff192e6
DM
308 properties => {
309 node => get_standard_option('pve-node'),
310 },
311 },
312 returns => {
313 type => "object",
314 properties => {
315
316 },
317 },
318 code => sub {
319 my ($param) = @_;
320
321 my $res = {
322 uptime => 0,
323 idle => 0,
324 };
325
326 my ($uptime, $idle) = PVE::ProcFSTools::read_proc_uptime();
327 $res->{uptime} = $uptime;
f9d26e09 328
aff192e6
DM
329 my ($avg1, $avg5, $avg15) = PVE::ProcFSTools::read_loadavg();
330 $res->{loadavg} = [ $avg1, $avg5, $avg15];
f9d26e09 331
aff192e6
DM
332 my ($sysname, $nodename, $release, $version, $machine) = POSIX::uname();
333
334 $res->{kversion} = "$sysname $release $version";
335
336 $res->{cpuinfo} = PVE::ProcFSTools::read_cpuinfo();
337
338 my $stat = PVE::ProcFSTools::read_proc_stat();
339 $res->{cpu} = $stat->{cpu};
340 $res->{wait} = $stat->{wait};
341
342 my $meminfo = PVE::ProcFSTools::read_meminfo();
343 $res->{memory} = {
344 free => $meminfo->{memfree},
345 total => $meminfo->{memtotal},
346 used => $meminfo->{memused},
347 };
f9d26e09 348
20539e0c
DM
349 $res->{ksm} = {
350 shared => $meminfo->{memshared},
351 };
352
aff192e6
DM
353 $res->{swap} = {
354 free => $meminfo->{swapfree},
355 total => $meminfo->{swaptotal},
356 used => $meminfo->{swapused},
357 };
358
359 $res->{pveversion} = PVE::pvecfg::package() . "/" .
8747a9ec 360 PVE::pvecfg::version_text();
aff192e6
DM
361
362 my $dinfo = df('/', 1); # output is bytes
363
364 $res->{rootfs} = {
365 total => $dinfo->{blocks},
366 avail => $dinfo->{bavail},
367 used => $dinfo->{used},
3d0fcc46 368 free => $dinfo->{blocks} - $dinfo->{used},
aff192e6
DM
369 };
370
371 return $res;
372 }});
373
0455911d
SP
374__PACKAGE__->register_method({
375 name => 'netstat',
376 path => 'netstat',
377 method => 'GET',
378 permissions => {
379 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
380 },
381 description => "Read tap/vm network device interface counters",
382 proxyto => 'node',
383 parameters => {
384 additionalProperties => 0,
385 properties => {
386 node => get_standard_option('pve-node'),
387 },
388 },
389 returns => {
d6e7fa04
TL
390 type => "array",
391 items => {
392 type => "object",
393 properties => {},
394 },
0455911d
SP
395 },
396 code => sub {
397 my ($param) = @_;
398
399 my $res = [ ];
400
401 my $netdev = PVE::ProcFSTools::read_proc_net_dev();
d6e7fa04
TL
402 foreach my $dev (sort keys %$netdev) {
403 next if $dev !~ m/^(?:tap|veth)([1-9]\d*)i(\d+)$/;
404 my ($vmid, $netid) = ($1, $2);
405
406 push @$res, {
407 vmid => $vmid,
408 dev => "net$netid",
409 in => $netdev->{$dev}->{transmit},
410 out => $netdev->{$dev}->{receive},
411 };
0455911d
SP
412 }
413
414 return $res;
415 }});
416
87c3e931
SP
417__PACKAGE__->register_method({
418 name => 'execute',
419 path => 'execute',
40993593 420 method => 'POST',
87c3e931
SP
421 permissions => {
422 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
423 },
40993593 424 description => "Execute multiple commands in order.",
87c3e931 425 proxyto => 'node',
40993593 426 protected => 1, # avoid problems with proxy code
87c3e931
SP
427 parameters => {
428 additionalProperties => 0,
429 properties => {
430 node => get_standard_option('pve-node'),
40993593
DM
431 commands => {
432 description => "JSON encoded array of commands.",
433 type => "string",
434 }
87c3e931
SP
435 },
436 },
437 returns => {
438 type => 'array',
439 properties => {
440
441 },
442 },
443 code => sub {
444 my ($param) = @_;
445 my $res = [];
446
40993593
DM
447 my $rpcenv = PVE::RPCEnvironment::get();
448 my $user = $rpcenv->get_user();
449
450 my $commands = eval { decode_json($param->{commands}); };
451
87c3e931
SP
452 die "commands param did not contain valid JSON: $@" if $@;
453 die "commands is not an array" if ref($commands) ne "ARRAY";
454
455 foreach my $cmd (@$commands) {
87c3e931 456 eval {
40993593 457 die "$cmd is not a valid command" if (ref($cmd) ne "HASH" || !$cmd->{path} || !$cmd->{method});
f9d26e09 458
40993593
DM
459 $cmd->{args} //= {};
460
461 my $path = "nodes/$param->{node}/$cmd->{path}";
f9d26e09 462
40993593
DM
463 my $uri_param = {};
464 my ($handler, $info) = PVE::API2->find_handler($cmd->{method}, $path, $uri_param);
465 if (!$handler || !$info) {
466 die "no handler for '$path'\n";
467 }
468
469 foreach my $p (keys %{$cmd->{args}}) {
470 raise_param_exc({ $p => "duplicate parameter" }) if defined($uri_param->{$p});
471 $uri_param->{$p} = $cmd->{args}->{$p};
472 }
473
474 # check access permissions
475 $rpcenv->check_api2_permissions($info->{permissions}, $user, $uri_param);
476
477 push @$res, {
478 status => HTTP_OK,
a51ceeb7 479 data => $handler->handle($info, $uri_param),
40993593 480 };
87c3e931 481 };
40993593
DM
482 if (my $err = $@) {
483 my $resp = { status => HTTP_INTERNAL_SERVER_ERROR };
484 if (ref($err) eq "PVE::Exception") {
485 $resp->{status} = $err->{code} if $err->{code};
486 $resp->{errors} = $err->{errors} if $err->{errors};
487 $resp->{message} = $err->{msg};
488 } else {
489 $resp->{message} = $err;
490 }
491 push @$res, $resp;
492 }
87c3e931
SP
493 }
494
495 return $res;
496 }});
497
498
aff192e6 499__PACKAGE__->register_method({
f9d26e09
TL
500 name => 'node_cmd',
501 path => 'status',
aff192e6
DM
502 method => 'POST',
503 permissions => {
7d020b42 504 check => ['perm', '/nodes/{node}', [ 'Sys.PowerMgmt' ]],
aff192e6
DM
505 },
506 protected => 1,
507 description => "Reboot or shutdown a node.",
508 proxyto => 'node',
509 parameters => {
6110ed03 510 additionalProperties => 0,
aff192e6
DM
511 properties => {
512 node => get_standard_option('pve-node'),
513 command => {
514 description => "Specify the command.",
515 type => 'string',
516 enum => [qw(reboot shutdown)],
517 },
518 },
519 },
520 returns => { type => "null" },
521 code => sub {
522 my ($param) = @_;
523
524 if ($param->{command} eq 'reboot') {
525 system ("(sleep 2;/sbin/reboot)&");
526 } elsif ($param->{command} eq 'shutdown') {
527 system ("(sleep 2;/sbin/poweroff)&");
528 }
529
530 return undef;
531 }});
532
b3d84542
CE
533__PACKAGE__->register_method({
534 name => 'wakeonlan',
535 path => 'wakeonlan',
536 method => 'POST',
537 permissions => {
538 check => ['perm', '/nodes/{node}', [ 'Sys.PowerMgmt' ]],
539 },
540 protected => 1,
541 description => "Try to wake a node via 'wake on LAN' network packet.",
542 parameters => {
543 additionalProperties => 0,
544 properties => {
545 node => get_standard_option('pve-node', {
546 description => 'target node for wake on LAN packet',
5aa7b909
CE
547 completion => sub {
548 my $members = PVE::Cluster::get_members();
549 return [ grep { !$members->{$_}->{online} } keys %$members ];
550 }
b3d84542
CE
551 }),
552 },
553 },
0f615ea9
CE
554 returns => {
555 type => 'string',
556 format => 'mac-addr',
ec178804 557 description => 'MAC address used to assemble the WoL magic packet.',
0f615ea9 558 },
b3d84542
CE
559 code => sub {
560 my ($param) = @_;
561
82436996
TL
562 my $node = $param->{node};
563
564 die "'$node' is local node, cannot wake my self!\n"
565 if $node eq 'localhost' || $node eq PVE::INotify::nodename();
566
567 PVE::Cluster::check_node_exists($node);
568
569 my $config = PVE::NodeConfig::load_config($node);
b3d84542
CE
570 my $mac_addr = $config->{wakeonlan};
571 if (!defined($mac_addr)) {
82436996 572 die "No wake on LAN MAC address defined for '$node'!\n";
b3d84542
CE
573 }
574
575 $mac_addr =~ s/://g;
576 my $packet = chr(0xff) x 6 . pack('H*', $mac_addr) x 16;
577
578 my $addr = gethostbyname('255.255.255.255');
579 my $port = getservbyname('discard', 'udp');
580 my $to = Socket::pack_sockaddr_in($port, $addr);
82436996 581
b3d84542
CE
582 socket(my $sock, Socket::AF_INET, Socket::SOCK_DGRAM, Socket::IPPROTO_UDP)
583 || die "Unable to open socket: $!\n";
584 setsockopt($sock, Socket::SOL_SOCKET, Socket::SO_BROADCAST, 1)
585 || die "Unable to set socket option: $!\n";
586
587 send($sock, $packet, 0, $to)
588 || die "Unable to send packet: $!\n";
589
590 close($sock);
591
0f615ea9 592 return $config->{wakeonlan};
b3d84542 593 }});
aff192e6
DM
594
595__PACKAGE__->register_method({
f9d26e09
TL
596 name => 'rrd',
597 path => 'rrd',
aff192e6
DM
598 method => 'GET',
599 protected => 1, # fixme: can we avoid that?
600 permissions => {
7d020b42 601 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
aff192e6
DM
602 },
603 description => "Read node RRD statistics (returns PNG)",
604 parameters => {
6110ed03 605 additionalProperties => 0,
aff192e6
DM
606 properties => {
607 node => get_standard_option('pve-node'),
608 timeframe => {
609 description => "Specify the time frame you are interested in.",
610 type => 'string',
611 enum => [ 'hour', 'day', 'week', 'month', 'year' ],
612 },
613 ds => {
614 description => "The list of datasources you want to display.",
615 type => 'string', format => 'pve-configid-list',
616 },
617 cf => {
618 description => "The RRD consolidation function",
619 type => 'string',
620 enum => [ 'AVERAGE', 'MAX' ],
621 optional => 1,
622 },
623 },
624 },
625 returns => {
626 type => "object",
627 properties => {
628 filename => { type => 'string' },
629 },
630 },
631 code => sub {
632 my ($param) = @_;
633
516a7948 634 return PVE::RRD::create_rrd_graph(
f9d26e09 635 "pve2-node/$param->{node}", $param->{timeframe},
aff192e6
DM
636 $param->{ds}, $param->{cf});
637
638 }});
639
640__PACKAGE__->register_method({
f9d26e09
TL
641 name => 'rrddata',
642 path => 'rrddata',
aff192e6
DM
643 method => 'GET',
644 protected => 1, # fixme: can we avoid that?
645 permissions => {
7d020b42 646 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
aff192e6
DM
647 },
648 description => "Read node RRD statistics",
649 parameters => {
6110ed03 650 additionalProperties => 0,
aff192e6
DM
651 properties => {
652 node => get_standard_option('pve-node'),
653 timeframe => {
654 description => "Specify the time frame you are interested in.",
655 type => 'string',
656 enum => [ 'hour', 'day', 'week', 'month', 'year' ],
657 },
658 cf => {
659 description => "The RRD consolidation function",
660 type => 'string',
661 enum => [ 'AVERAGE', 'MAX' ],
662 optional => 1,
663 },
664 },
665 },
666 returns => {
667 type => "array",
668 items => {
669 type => "object",
670 properties => {},
671 },
672 },
673 code => sub {
674 my ($param) = @_;
675
516a7948 676 return PVE::RRD::create_rrd_data(
aff192e6
DM
677 "pve2-node/$param->{node}", $param->{timeframe}, $param->{cf});
678 }});
679
680__PACKAGE__->register_method({
f9d26e09
TL
681 name => 'syslog',
682 path => 'syslog',
aff192e6
DM
683 method => 'GET',
684 description => "Read system log",
685 proxyto => 'node',
686 permissions => {
7d020b42 687 check => ['perm', '/nodes/{node}', [ 'Sys.Syslog' ]],
aff192e6
DM
688 },
689 protected => 1,
690 parameters => {
6110ed03 691 additionalProperties => 0,
aff192e6
DM
692 properties => {
693 node => get_standard_option('pve-node'),
694 start => {
695 type => 'integer',
696 minimum => 0,
697 optional => 1,
698 },
699 limit => {
700 type => 'integer',
701 minimum => 0,
702 optional => 1,
703 },
01b753b6
TL
704 since => {
705 type=> 'string',
706 pattern => '^\d{4}-\d{2}-\d{2}( \d{2}:\d{2}(:\d{2})?)?$',
707 description => "Display all log since this date-time string.",
708 optional => 1,
709 },
710 until => {
711 type=> 'string',
712 pattern => '^\d{4}-\d{2}-\d{2}( \d{2}:\d{2}(:\d{2})?)?$',
713 description => "Display all log until this date-time string.",
714 optional => 1,
715 },
34142272
DC
716 service => {
717 description => "Service ID",
718 type => 'string',
719 maxLength => 128,
720 optional => 1,
721 },
aff192e6
DM
722 },
723 },
724 returns => {
725 type => 'array',
f9d26e09 726 items => {
aff192e6
DM
727 type => "object",
728 properties => {
729 n => {
730 description=> "Line number",
731 type=> 'integer',
732 },
733 t => {
734 description=> "Line text",
735 type => 'string',
736 }
737 }
738 }
739 },
740 code => sub {
741 my ($param) = @_;
742
aff192e6
DM
743 my $rpcenv = PVE::RPCEnvironment::get();
744 my $user = $rpcenv->get_user();
745 my $node = $param->{node};
34142272
DC
746 my $service;
747
748 if ($param->{service}) {
749 my $service_aliases = {
750 'postfix' => 'postfix@-',
751 };
752
753 $service = $service_aliases->{$param->{service}} // $param->{service};
754 }
aff192e6 755
01b753b6 756 my ($count, $lines) = PVE::Tools::dump_journal($param->{start}, $param->{limit},
34142272 757 $param->{since}, $param->{until}, $service);
ff9c330c
DM
758
759 $rpcenv->set_result_attrib('total', $count);
01b753b6
TL
760
761 return $lines;
ff9c330c
DM
762 }});
763
1d397a83
DC
764__PACKAGE__->register_method({
765 name => 'journal',
766 path => 'journal',
767 method => 'GET',
768 description => "Read Journal",
769 proxyto => 'node',
770 permissions => {
771 check => ['perm', '/nodes/{node}', [ 'Sys.Syslog' ]],
772 },
773 protected => 1,
774 parameters => {
775 additionalProperties => 0,
776 properties => {
777 node => get_standard_option('pve-node'),
778 since => {
961399a3
TL
779 type=> 'integer',
780 minimum => 0,
f9b08743 781 description => "Display all log since this UNIX epoch. Conflicts with 'startcursor'.",
1d397a83
DC
782 optional => 1,
783 },
784 until => {
961399a3
TL
785 type=> 'integer',
786 minimum => 0,
f9b08743 787 description => "Display all log until this UNIX epoch. Conflicts with 'endcursor'.",
1d397a83
DC
788 optional => 1,
789 },
790 lastentries => {
a03cb8b8 791 description => "Limit to the last X lines. Conflicts with a range.",
1d397a83 792 type => 'integer',
961399a3 793 minimum => 0,
1d397a83
DC
794 optional => 1,
795 },
796 startcursor => {
f9b08743 797 description => "Start after the given Cursor. Conflicts with 'since'",
1d397a83
DC
798 type => 'string',
799 optional => 1,
800 },
801 endcursor => {
f9b08743 802 description => "End before the given Cursor. Conflicts with 'until'",
1d397a83
DC
803 type => 'string',
804 optional => 1,
805 },
806 },
807 },
808 returns => {
809 type => 'array',
810 items => {
811 type => "string",
812 }
813 },
814 code => sub {
815 my ($param) = @_;
816
817 my $rpcenv = PVE::RPCEnvironment::get();
818 my $user = $rpcenv->get_user();
819
f9b08743 820 my $cmd = ["/usr/bin/mini-journalreader"];
a03cb8b8
TL
821 push @$cmd, '-n', $param->{lastentries} if $param->{lastentries};
822 push @$cmd, '-b', $param->{since} if $param->{since};
823 push @$cmd, '-e', $param->{until} if $param->{until};
824 push @$cmd, '-f', $param->{startcursor} if $param->{startcursor};
825 push @$cmd, '-t', $param->{endcursor} if $param->{endcursor};
f9b08743
TL
826
827 my $lines = [];
828 my $parser = sub { push @$lines, shift };
1d397a83
DC
829
830 PVE::Tools::run_command($cmd, outfunc => $parser);
831
832 return $lines;
833 }});
834
aff192e6
DM
835my $sslcert;
836
dab7a849 837my $shell_cmd_map = {
f24be7e7
TL
838 'login' => {
839 cmd => [ '/bin/login', '-f', 'root' ],
840 },
841 'upgrade' => {
842 cmd => [ '/usr/bin/pveupgrade', '--shell' ],
843 },
844 'ceph_install' => {
845 cmd => [ '/usr/bin/pveceph', 'install' ],
846 allow_args => 1,
847 },
d03d7e1e
TM
848};
849
850sub get_shell_command {
f24be7e7 851 my ($user, $shellcmd, $args) = @_;
d03d7e1e 852
f24be7e7 853 my $cmd;
d03d7e1e 854 if ($user eq 'root@pam') {
fc1da3b0 855 if (defined($shellcmd) && exists($shell_cmd_map->{$shellcmd})) {
f24be7e7
TL
856 my $def = $shell_cmd_map->{$shellcmd};
857 $cmd = [ @{$def->{cmd}} ]; # clone
858 if (defined($args) && $def->{allow_args}) {
859 push @$cmd, split("\0", $args);
860 }
d03d7e1e 861 } else {
f24be7e7 862 $cmd = [ '/bin/login', '-f', 'root' ];
d03d7e1e
TM
863 }
864 } else {
f24be7e7
TL
865 # non-root must always login for now, we do not have a superuser role!
866 $cmd = [ '/bin/login' ];
d03d7e1e 867 }
f24be7e7 868 return $cmd;
d03d7e1e
TM
869}
870
677bee7c
TL
871my $get_vnc_connection_info = sub {
872 my $node = shift;
873
874 my $remote_cmd = [];
875
876 my ($remip, $family);
877 if ($node ne 'localhost' && $node ne PVE::INotify::nodename()) {
878 ($remip, $family) = PVE::Cluster::remote_node_ip($node);
879 $remote_cmd = ['/usr/bin/ssh', '-e', 'none', '-t', $remip , '--'];
880 } else {
881 $family = PVE::Tools::get_host_address_family($node);
882 }
883 my $port = PVE::Tools::next_vnc_port($family);
884
885 return ($port, $remote_cmd);
886};
887
aff192e6 888__PACKAGE__->register_method ({
f9d26e09
TL
889 name => 'vncshell',
890 path => 'vncshell',
aff192e6
DM
891 method => 'POST',
892 protected => 1,
893 permissions => {
d0289a19 894 description => "Restricted to users on realm 'pam'",
7d020b42 895 check => ['perm', '/nodes/{node}', [ 'Sys.Console' ]],
aff192e6
DM
896 },
897 description => "Creates a VNC Shell proxy.",
898 parameters => {
6110ed03 899 additionalProperties => 0,
aff192e6
DM
900 properties => {
901 node => get_standard_option('pve-node'),
d03d7e1e
TM
902 cmd => {
903 type => 'string',
904 description => "Run specific command or default to login.",
dab7a849 905 enum => [keys %$shell_cmd_map],
d03d7e1e
TM
906 optional => 1,
907 default => 'login',
908 },
f24be7e7
TL
909 'cmd-opts' => {
910 type => 'string',
911 description => "Add parameters to a command. Encoded as null terminated strings.",
912 requires => 'cmd',
913 optional => 1,
914 default => '',
915 },
8dfca17e
DM
916 websocket => {
917 optional => 1,
918 type => 'boolean',
919 description => "use websocket instead of standard vnc.",
920 },
b8ac8b0c
DC
921 width => {
922 optional => 1,
923 description => "sets the width of the console in pixels.",
924 type => 'integer',
925 minimum => 16,
926 maximum => 4096,
927 },
928 height => {
929 optional => 1,
930 description => "sets the height of the console in pixels.",
931 type => 'integer',
932 minimum => 16,
933 maximum => 2160,
934 },
aff192e6
DM
935 },
936 },
f9d26e09 937 returns => {
6110ed03 938 additionalProperties => 0,
aff192e6
DM
939 properties => {
940 user => { type => 'string' },
941 ticket => { type => 'string' },
942 cert => { type => 'string' },
943 port => { type => 'integer' },
944 upid => { type => 'string' },
945 },
946 },
947 code => sub {
948 my ($param) = @_;
949
950 my $rpcenv = PVE::RPCEnvironment::get();
d553e535 951 my ($user, undef, $realm) = PVE::AccessControl::verify_username($rpcenv->get_user());
d0289a19 952
f9d26e09 953 raise_perm_exc("realm != pam") if $realm ne 'pam';
0c8e509e
FE
954
955 if (defined($param->{cmd}) && $param->{cmd} eq 'upgrade' && $user ne 'root@pam') {
956 raise_perm_exc('user != root@pam');
957 }
3a76893d 958
aff192e6
DM
959 my $node = $param->{node};
960
57ebda08 961 my $authpath = "/nodes/$node";
57ebda08
DM
962 my $ticket = PVE::AccessControl::assemble_vnc_ticket($user, $authpath);
963
aff192e6
DM
964 $sslcert = PVE::Tools::file_get_contents("/etc/pve/pve-root-ca.pem", 8192)
965 if !$sslcert;
966
677bee7c 967 my ($port, $remcmd) = $get_vnc_connection_info->($node);
d4a25f0b 968
f24be7e7 969 my $shcmd = get_shell_command($user, $param->{cmd}, $param->{'cmd-opts'});
aff192e6 970
f9d26e09 971 my $timeout = 10;
aff192e6 972
677bee7c
TL
973 my $cmd = ['/usr/bin/vncterm',
974 '-rfbport', $port,
975 '-timeout', $timeout,
976 '-authpath', $authpath,
977 '-perm', 'Sys.Console',
978 ];
b8ac8b0c 979
677bee7c
TL
980 push @$cmd, '-width', $param->{width} if $param->{width};
981 push @$cmd, '-height', $param->{height} if $param->{height};
b8ac8b0c 982
8dfca17e 983 if ($param->{websocket}) {
f9d26e09 984 $ENV{PVE_VNC_TICKET} = $ticket; # pass ticket to vncterm
8dfca17e
DM
985 push @$cmd, '-notls', '-listen', 'localhost';
986 }
987
988 push @$cmd, '-c', @$remcmd, @$shcmd;
aff192e6
DM
989
990 my $realcmd = sub {
991 my $upid = shift;
992
993 syslog ('info', "starting vnc proxy $upid\n");
994
6d394492 995 my $cmdstr = join (' ', @$cmd);
aff192e6
DM
996 syslog ('info', "launch command: $cmdstr");
997
f9d26e09 998 eval {
6d394492 999 foreach my $k (keys %ENV) {
8dfca17e 1000 next if $k eq 'PVE_VNC_TICKET';
b0d4b407 1001 next if $k eq 'PATH' || $k eq 'TERM' || $k eq 'USER' || $k eq 'HOME' || $k eq 'LANG' || $k eq 'LANGUAGE';
6d394492
DM
1002 delete $ENV{$k};
1003 }
1004 $ENV{PWD} = '/';
1005
b0d4b407 1006 PVE::Tools::run_command($cmd, errmsg => "vncterm failed", keeplocale => 1);
6d394492
DM
1007 };
1008 if (my $err = $@) {
1009 syslog ('err', $err);
aff192e6
DM
1010 }
1011
1012 return;
1013 };
1014
1015 my $upid = $rpcenv->fork_worker('vncshell', "", $user, $realcmd);
f9d26e09 1016
6806a0f8 1017 PVE::Tools::wait_for_vnc_port($port);
aff192e6
DM
1018
1019 return {
1020 user => $user,
1021 ticket => $ticket,
f9d26e09
TL
1022 port => $port,
1023 upid => $upid,
1024 cert => $sslcert,
aff192e6
DM
1025 };
1026 }});
1027
4b168c27
DC
1028__PACKAGE__->register_method ({
1029 name => 'termproxy',
1030 path => 'termproxy',
1031 method => 'POST',
1032 protected => 1,
1033 permissions => {
1034 description => "Restricted to users on realm 'pam'",
1035 check => ['perm', '/nodes/{node}', [ 'Sys.Console' ]],
1036 },
1037 description => "Creates a VNC Shell proxy.",
1038 parameters => {
1039 additionalProperties => 0,
1040 properties => {
1041 node => get_standard_option('pve-node'),
d03d7e1e
TM
1042 cmd => {
1043 type => 'string',
1044 description => "Run specific command or default to login.",
dab7a849 1045 enum => [keys %$shell_cmd_map],
d03d7e1e
TM
1046 optional => 1,
1047 default => 'login',
1048 },
f24be7e7
TL
1049 'cmd-opts' => {
1050 type => 'string',
1051 description => "Add parameters to a command. Encoded as null terminated strings.",
1052 requires => 'cmd',
1053 optional => 1,
1054 default => '',
1055 },
4b168c27
DC
1056 },
1057 },
1058 returns => {
1059 additionalProperties => 0,
1060 properties => {
1061 user => { type => 'string' },
1062 ticket => { type => 'string' },
1063 port => { type => 'integer' },
1064 upid => { type => 'string' },
1065 },
1066 },
1067 code => sub {
1068 my ($param) = @_;
1069
1070 my $rpcenv = PVE::RPCEnvironment::get();
4b168c27 1071 my ($user, undef, $realm) = PVE::AccessControl::verify_username($rpcenv->get_user());
f24be7e7 1072 raise_perm_exc("realm $realm != pam") if $realm ne 'pam';
4b168c27
DC
1073
1074 my $node = $param->{node};
4b168c27 1075 my $authpath = "/nodes/$node";
4b168c27
DC
1076 my $ticket = PVE::AccessControl::assemble_vnc_ticket($user, $authpath);
1077
677bee7c 1078 my ($port, $remcmd) = $get_vnc_connection_info->($node);
4b168c27 1079
f24be7e7 1080 my $shcmd = get_shell_command($user, $param->{cmd}, $param->{'cmd-opts'});
4b168c27
DC
1081
1082 my $realcmd = sub {
1083 my $upid = shift;
1084
1085 syslog ('info', "starting termproxy $upid\n");
1086
f24be7e7
TL
1087 my $cmd = [
1088 '/usr/bin/termproxy',
1089 $port,
677bee7c
TL
1090 '--path', $authpath,
1091 '--perm', 'Sys.Console',
1092 '--'
1093 ];
d03d7e1e 1094 push @$cmd, @$remcmd, @$shcmd;
4b168c27
DC
1095
1096 PVE::Tools::run_command($cmd);
1097 };
4b168c27
DC
1098 my $upid = $rpcenv->fork_worker('vncshell', "", $user, $realcmd);
1099
1100 PVE::Tools::wait_for_vnc_port($port);
1101
1102 return {
1103 user => $user,
1104 ticket => $ticket,
1105 port => $port,
1106 upid => $upid,
1107 };
1108 }});
1109
8dfca17e
DM
1110__PACKAGE__->register_method({
1111 name => 'vncwebsocket',
1112 path => 'vncwebsocket',
1113 method => 'GET',
f9d26e09 1114 permissions => {
8dfca17e
DM
1115 description => "Restricted to users on realm 'pam'. You also need to pass a valid ticket (vncticket).",
1116 check => ['perm', '/nodes/{node}', [ 'Sys.Console' ]],
1117 },
0e68b116 1118 description => "Opens a websocket for VNC traffic.",
8dfca17e 1119 parameters => {
6110ed03 1120 additionalProperties => 0,
8dfca17e
DM
1121 properties => {
1122 node => get_standard_option('pve-node'),
1123 vncticket => {
1124 description => "Ticket from previous call to vncproxy.",
1125 type => 'string',
1126 maxLength => 512,
1127 },
1128 port => {
1129 description => "Port number returned by previous vncproxy call.",
1130 type => 'integer',
1131 minimum => 5900,
1132 maximum => 5999,
1133 },
1134 },
1135 },
1136 returns => {
1137 type => "object",
1138 properties => {
1139 port => { type => 'string' },
1140 },
1141 },
1142 code => sub {
1143 my ($param) = @_;
1144
1145 my $rpcenv = PVE::RPCEnvironment::get();
1146
1147 my ($user, undef, $realm) = PVE::AccessControl::verify_username($rpcenv->get_user());
1148
f9d26e09 1149 raise_perm_exc("realm != pam") if $realm ne 'pam';
8dfca17e
DM
1150
1151 my $authpath = "/nodes/$param->{node}";
1152
1153 PVE::AccessControl::verify_vnc_ticket($param->{vncticket}, $user, $authpath);
1154
1155 my $port = $param->{port};
f9d26e09 1156
8dfca17e
DM
1157 return { port => $port };
1158 }});
1159
2d802f8c 1160__PACKAGE__->register_method ({
f9d26e09
TL
1161 name => 'spiceshell',
1162 path => 'spiceshell',
2d802f8c
DM
1163 method => 'POST',
1164 protected => 1,
1165 proxyto => 'node',
1166 permissions => {
1167 description => "Restricted to users on realm 'pam'",
1168 check => ['perm', '/nodes/{node}', [ 'Sys.Console' ]],
1169 },
7774df78 1170 description => "Creates a SPICE shell.",
2d802f8c 1171 parameters => {
6110ed03 1172 additionalProperties => 0,
2d802f8c
DM
1173 properties => {
1174 node => get_standard_option('pve-node'),
7774df78 1175 proxy => get_standard_option('spice-proxy', { optional => 1 }),
d03d7e1e
TM
1176 cmd => {
1177 type => 'string',
1178 description => "Run specific command or default to login.",
dab7a849 1179 enum => [keys %$shell_cmd_map],
d03d7e1e
TM
1180 optional => 1,
1181 default => 'login',
1182 },
f24be7e7
TL
1183 'cmd-opts' => {
1184 type => 'string',
1185 description => "Add parameters to a command. Encoded as null terminated strings.",
1186 requires => 'cmd',
1187 optional => 1,
1188 default => '',
1189 },
2d802f8c
DM
1190 },
1191 },
7774df78 1192 returns => get_standard_option('remote-viewer-config'),
2d802f8c
DM
1193 code => sub {
1194 my ($param) = @_;
1195
1196 my $rpcenv = PVE::RPCEnvironment::get();
1197 my $authuser = $rpcenv->get_user();
1198
1199 my ($user, undef, $realm) = PVE::AccessControl::verify_username($authuser);
1200
f9d26e09 1201 raise_perm_exc("realm != pam") if $realm ne 'pam';
b270a6b4
FE
1202
1203 if (defined($param->{cmd}) && $param->{cmd} eq 'upgrade' && $user ne 'root@pam') {
1204 raise_perm_exc('user != root@pam');
1205 }
2d802f8c
DM
1206
1207 my $node = $param->{node};
1208 my $proxy = $param->{proxy};
2d802f8c
DM
1209
1210 my $authpath = "/nodes/$node";
b289829f 1211 my $permissions = 'Sys.Console';
b270a6b4 1212
f24be7e7 1213 my $shcmd = get_shell_command($user, $param->{cmd}, $param->{'cmd-opts'});
2d802f8c 1214
b289829f 1215 my $title = "Shell on '$node'";
2d802f8c 1216
b289829f 1217 return PVE::API2Tools::run_spiceterm($authpath, $permissions, 0, $node, $proxy, $title, $shcmd);
2d802f8c
DM
1218 }});
1219
aff192e6 1220__PACKAGE__->register_method({
f9d26e09
TL
1221 name => 'dns',
1222 path => 'dns',
aff192e6
DM
1223 method => 'GET',
1224 permissions => {
7d020b42 1225 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
aff192e6
DM
1226 },
1227 description => "Read DNS settings.",
1228 proxyto => 'node',
1229 parameters => {
6110ed03 1230 additionalProperties => 0,
aff192e6
DM
1231 properties => {
1232 node => get_standard_option('pve-node'),
1233 },
1234 },
1235 returns => {
1236 type => "object",
6110ed03 1237 additionalProperties => 0,
aff192e6
DM
1238 properties => {
1239 search => {
1240 description => "Search domain for host-name lookup.",
1241 type => 'string',
1242 optional => 1,
1243 },
1244 dns1 => {
1245 description => 'First name server IP address.',
1246 type => 'string',
1247 optional => 1,
f9d26e09 1248 },
aff192e6
DM
1249 dns2 => {
1250 description => 'Second name server IP address.',
1251 type => 'string',
1252 optional => 1,
f9d26e09 1253 },
aff192e6
DM
1254 dns3 => {
1255 description => 'Third name server IP address.',
1256 type => 'string',
1257 optional => 1,
f9d26e09 1258 },
aff192e6
DM
1259 },
1260 },
1261 code => sub {
1262 my ($param) = @_;
1263
1264 my $res = PVE::INotify::read_file('resolvconf');
1265
1266 return $res;
1267 }});
1268
1269__PACKAGE__->register_method({
f9d26e09
TL
1270 name => 'update_dns',
1271 path => 'dns',
aff192e6
DM
1272 method => 'PUT',
1273 description => "Write DNS settings.",
d0289a19
DM
1274 permissions => {
1275 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
1276 },
aff192e6
DM
1277 proxyto => 'node',
1278 protected => 1,
1279 parameters => {
6110ed03 1280 additionalProperties => 0,
aff192e6
DM
1281 properties => {
1282 node => get_standard_option('pve-node'),
1283 search => {
1284 description => "Search domain for host-name lookup.",
1285 type => 'string',
1286 },
1287 dns1 => {
1288 description => 'First name server IP address.',
7a9486a7 1289 type => 'string', format => 'ip',
aff192e6 1290 optional => 1,
f9d26e09 1291 },
aff192e6
DM
1292 dns2 => {
1293 description => 'Second name server IP address.',
7a9486a7 1294 type => 'string', format => 'ip',
aff192e6 1295 optional => 1,
f9d26e09 1296 },
aff192e6
DM
1297 dns3 => {
1298 description => 'Third name server IP address.',
7a9486a7 1299 type => 'string', format => 'ip',
aff192e6 1300 optional => 1,
f9d26e09 1301 },
aff192e6
DM
1302 },
1303 },
1304 returns => { type => "null" },
1305 code => sub {
1306 my ($param) = @_;
1307
1308 PVE::INotify::update_file('resolvconf', $param);
1309
1310 return undef;
1311 }});
1312
1313__PACKAGE__->register_method({
f9d26e09
TL
1314 name => 'time',
1315 path => 'time',
aff192e6
DM
1316 method => 'GET',
1317 permissions => {
7d020b42
DM
1318 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
1319 },
aff192e6
DM
1320 description => "Read server time and time zone settings.",
1321 proxyto => 'node',
1322 parameters => {
1323 additionalProperties => 0,
1324 properties => {
1325 node => get_standard_option('pve-node'),
1326 },
1327 },
1328 returns => {
1329 type => "object",
6110ed03 1330 additionalProperties => 0,
aff192e6
DM
1331 properties => {
1332 timezone => {
1333 description => "Time zone",
1334 type => 'string',
1335 },
1336 time => {
1337 description => "Seconds since 1970-01-01 00:00:00 UTC.",
1338 type => 'integer',
1339 minimum => 1297163644,
bed5fdfc 1340 renderer => 'timestamp',
aff192e6
DM
1341 },
1342 localtime => {
1343 description => "Seconds since 1970-01-01 00:00:00 (local time)",
1344 type => 'integer',
1345 minimum => 1297163644,
bed5fdfc 1346 renderer => 'timestamp_gmt',
aff192e6
DM
1347 },
1348 },
1349 },
1350 code => sub {
1351 my ($param) = @_;
1352
1353 my $ctime = time();
1354 my $ltime = timegm_nocheck(localtime($ctime));
1355 my $res = {
1356 timezone => PVE::INotify::read_file('timezone'),
bed5fdfc 1357 time => $ctime,
aff192e6
DM
1358 localtime => $ltime,
1359 };
1360
1361 return $res;
1362 }});
1363
1364__PACKAGE__->register_method({
f9d26e09
TL
1365 name => 'set_timezone',
1366 path => 'time',
aff192e6
DM
1367 method => 'PUT',
1368 description => "Set time zone.",
d0289a19
DM
1369 permissions => {
1370 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
1371 },
aff192e6
DM
1372 proxyto => 'node',
1373 protected => 1,
1374 parameters => {
6110ed03 1375 additionalProperties => 0,
aff192e6
DM
1376 properties => {
1377 node => get_standard_option('pve-node'),
1378 timezone => {
1379 description => "Time zone. The file '/usr/share/zoneinfo/zone.tab' contains the list of valid names.",
1380 type => 'string',
1381 },
1382 },
1383 },
1384 returns => { type => "null" },
1385 code => sub {
1386 my ($param) = @_;
1387
1388 PVE::INotify::write_file('timezone', $param->{timezone});
1389
1390 return undef;
1391 }});
1392
c9164975 1393__PACKAGE__->register_method({
f9d26e09
TL
1394 name => 'aplinfo',
1395 path => 'aplinfo',
c9164975
DM
1396 method => 'GET',
1397 permissions => {
1398 user => 'all',
1399 },
1400 description => "Get list of appliances.",
1401 proxyto => 'node',
1402 parameters => {
6110ed03 1403 additionalProperties => 0,
c9164975
DM
1404 properties => {
1405 node => get_standard_option('pve-node'),
1406 },
1407 },
1408 returns => {
1409 type => 'array',
1410 items => {
1411 type => "object",
1412 properties => {},
1413 },
1414 },
1415 code => sub {
1416 my ($param) = @_;
1417
1418 my $res = [];
1419
1420 my $list = PVE::APLInfo::load_data();
1421
1422 foreach my $template (keys %{$list->{all}}) {
1423 my $pd = $list->{all}->{$template};
1424 next if $pd->{'package'} eq 'pve-web-news';
1425 push @$res, $pd;
1426 }
1427
1428 return $res;
1429 }});
1430
0532bd28 1431__PACKAGE__->register_method({
f9d26e09
TL
1432 name => 'apl_download',
1433 path => 'aplinfo',
0532bd28
DM
1434 method => 'POST',
1435 permissions => {
1436 check => ['perm', '/storage/{storage}', ['Datastore.AllocateTemplate']],
1437 },
1438 description => "Download appliance templates.",
1439 proxyto => 'node',
1440 protected => 1,
1441 parameters => {
6110ed03 1442 additionalProperties => 0,
0532bd28
DM
1443 properties => {
1444 node => get_standard_option('pve-node'),
82282acf 1445 storage => get_standard_option('pve-storage-id', {
62180d0f 1446 description => "The storage where the template will be stored",
82282acf
WL
1447 completion => \&PVE::Storage::complete_storage_enabled,
1448 }),
6110ed03
TL
1449 template => {
1450 type => 'string',
1451 description => "The template which will downloaded",
1452 maxLength => 255,
1453 completion => \&complete_templet_repo,
82282acf 1454 },
0532bd28
DM
1455 },
1456 },
1457 returns => { type => "string" },
1458 code => sub {
1459 my ($param) = @_;
1460
1461 my $rpcenv = PVE::RPCEnvironment::get();
0532bd28 1462 my $user = $rpcenv->get_user();
0532bd28 1463
aee25c2e 1464 my $node = $param->{node};
0532bd28 1465 my $template = $param->{template};
0532bd28 1466
aee25c2e
TL
1467 my $list = PVE::APLInfo::load_data();
1468 my $appliance = $list->{all}->{$template};
1469 raise_param_exc({ template => "no such template"}) if !$appliance;
0532bd28 1470
bbcfdc08 1471 my $cfg = PVE::Storage::config();
0532bd28
DM
1472 my $scfg = PVE::Storage::storage_check_enabled($cfg, $param->{storage}, $node);
1473
aee25c2e
TL
1474 die "unknown template type '$appliance->{type}'\n"
1475 if !($appliance->{type} eq 'openvz' || $appliance->{type} eq 'lxc');
0532bd28 1476
f9d26e09 1477 die "storage '$param->{storage}' does not support templates\n"
0532bd28
DM
1478 if !$scfg->{content}->{vztmpl};
1479
0532bd28 1480 my $tmpldir = PVE::Storage::get_vztmpl_dir($cfg, $param->{storage});
0532bd28 1481
fac5d57e 1482 my $worker = sub {
aee25c2e
TL
1483 my $dccfg = PVE::Cluster::cfs_read_file('datacenter.cfg');
1484
1485 PVE::Tools::download_file_from_url("$tmpldir/$template", $appliance->{location}, {
1486 hash_required => 1,
1487 sha512sum => $appliance->{sha512sum},
1488 md5sum => $appliance->{md5sum},
1489 http_proxy => $dccfg->{http_proxy},
1490 });
fac5d57e 1491 };
0532bd28 1492
fac5d57e 1493 my $upid = $rpcenv->fork_worker('download', $template, $user, $worker);
0532bd28 1494
fac5d57e 1495 return $upid;
0532bd28
DM
1496 }});
1497
34ada77a
EK
1498__PACKAGE__->register_method({
1499 name => 'report',
1500 path => 'report',
1501 method => 'GET',
1502 permissions => {
1503 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
1504 },
6bbdf981 1505 protected => 1,
34ada77a
EK
1506 description => "Gather various systems information about a node",
1507 proxyto => 'node',
1508 parameters => {
6110ed03 1509 additionalProperties => 0,
34ada77a
EK
1510 properties => {
1511 node => get_standard_option('pve-node'),
1512 },
1513 },
1514 returns => {
cdb5a209 1515 type => 'string',
34ada77a
EK
1516 },
1517 code => sub {
cdb5a209 1518 return PVE::Report::generate();
34ada77a 1519 }});
0532bd28 1520
b72cdbb7
TL
1521# returns a list of VMIDs, those can be filtered by
1522# * current parent node
1523# * vmid whitelist
1524# * guest is a template (default: skip)
1525# * guest is HA manged (default: skip)
1526my $get_filtered_vmlist = sub {
1527 my ($nodename, $vmfilter, $templates, $ha_managed) = @_;
b92400b6 1528
b92400b6
DM
1529 my $vmlist = PVE::Cluster::get_vmlist();
1530
b72cdbb7 1531 my $vms_allowed = {};
2a498506 1532 if (defined($vmfilter)) {
2a498506 1533 foreach my $vmid (PVE::Tools::split_list($vmfilter)) {
b72cdbb7 1534 $vms_allowed->{$vmid} = 1;
2a498506
DC
1535 }
1536 }
1537
b72cdbb7 1538 my $res = {};
b92400b6 1539 foreach my $vmid (keys %{$vmlist->{ids}}) {
b72cdbb7 1540 next if %$vms_allowed && !$vms_allowed->{$vmid};
b92400b6 1541
b72cdbb7
TL
1542 my $d = $vmlist->{ids}->{$vmid};
1543 next if $nodename && $d->{node} ne $nodename;
b92400b6 1544
b72cdbb7
TL
1545 eval {
1546 my $class;
2c27e4b7 1547 if ($d->{type} eq 'lxc') {
b72cdbb7 1548 $class = 'PVE::LXC::Config';
2c27e4b7 1549 } elsif ($d->{type} eq 'qemu') {
b72cdbb7 1550 $class = 'PVE::QemuConfig';
b92400b6
DM
1551 } else {
1552 die "unknown VM type '$d->{type}'\n";
1553 }
1554
b72cdbb7
TL
1555 my $conf = $class->load_config($vmid);
1556 return if !$templates && $class->is_template($conf);
1557 return if !$ha_managed && PVE::HA::Config::vm_is_ha_managed($vmid);
04b2004b 1558
b2bb6d77 1559 $res->{$vmid}->{conf} = $conf;
b72cdbb7 1560 $res->{$vmid}->{type} = $d->{type};
1c8dc310 1561 $res->{$vmid}->{class} = $class;
b92400b6
DM
1562 };
1563 warn $@ if $@;
1564 }
1565
b72cdbb7
TL
1566 return $res;
1567};
1568
1569# return all VMs which should get started/stopped on power up/down
1570my $get_start_stop_list = sub {
1571 my ($nodename, $autostart, $vmfilter) = @_;
1572
ae9d10ca
TL
1573 # do not skip HA vms on force or if a specific VMID set is wanted
1574 my $include_ha_managed = defined($vmfilter) ? 1 : 0;
1575
1576 my $vmlist = &$get_filtered_vmlist($nodename, $vmfilter, undef, $include_ha_managed);
b72cdbb7
TL
1577
1578 my $resList = {};
1579 foreach my $vmid (keys %$vmlist) {
b2bb6d77 1580 my $conf = $vmlist->{$vmid}->{conf};
b72cdbb7
TL
1581
1582 next if $autostart && !$conf->{onboot};
1583
1584 my $startup = {};
1585 if ($conf->{startup}) {
1586 $startup = PVE::JSONSchema::pve_parse_startup_order($conf->{startup});
1587 }
1588
1589 $startup->{order} = LONG_MAX if !defined($startup->{order});
1590
1591 $resList->{$startup->{order}}->{$vmid} = $startup;
23b54109 1592 $resList->{$startup->{order}}->{$vmid}->{type} = $vmlist->{$vmid}->{type};
b72cdbb7
TL
1593 }
1594
b92400b6
DM
1595 return $resList;
1596};
1597
1c8dc310
TL
1598my $remove_locks_on_startup = sub {
1599 my ($nodename) = @_;
1600
1601 my $vmlist = &$get_filtered_vmlist($nodename, undef, undef, 1);
1602
1603 foreach my $vmid (keys %$vmlist) {
1604 my $conf = $vmlist->{$vmid}->{conf};
23b54109 1605 my $class = $vmlist->{$vmid}->{class};
1c8dc310
TL
1606
1607 eval {
1608 if ($class->has_lock($conf, 'backup')) {
1609 $class->remove_lock($vmid, 'backup');
1610 my $msg = "removed left over backup lock from '$vmid'!";
1611 warn "$msg\n"; # prints to task log
1612 syslog('warning', $msg);
1613 }
1614 }; warn $@ if $@;
1615 }
1616};
1617
b92400b6 1618__PACKAGE__->register_method ({
f9d26e09
TL
1619 name => 'startall',
1620 path => 'startall',
b92400b6
DM
1621 method => 'POST',
1622 protected => 1,
17e3b3b2
CS
1623 permissions => {
1624 check => ['perm', '/', [ 'VM.PowerMgmt' ]],
1625 },
9c8a09a7 1626 proxyto => 'node',
e1b57809 1627 description => "Start all VMs and containers located on this node (by default only those with onboot=1).",
b92400b6 1628 parameters => {
6110ed03 1629 additionalProperties => 0,
b92400b6
DM
1630 properties => {
1631 node => get_standard_option('pve-node'),
c09c7160
AD
1632 force => {
1633 optional => 1,
1634 type => 'boolean',
e1b57809
TL
1635 default => 'off',
1636 description => "Issue start command even if virtual guest have 'onboot' not set or set to off.",
c09c7160 1637 },
2a498506 1638 vms => {
a740deff 1639 description => "Only consider guests from this comma separated list of VMIDs.",
2a498506
DC
1640 type => 'string', format => 'pve-vmid-list',
1641 optional => 1,
1642 },
b92400b6
DM
1643 },
1644 },
1645 returns => {
1646 type => 'string',
1647 },
1648 code => sub {
1649 my ($param) = @_;
1650
1651 my $rpcenv = PVE::RPCEnvironment::get();
1652 my $authuser = $rpcenv->get_user();
1653
1654 my $nodename = $param->{node};
1655 $nodename = PVE::INotify::nodename() if $nodename eq 'localhost';
1656
c09c7160
AD
1657 my $force = $param->{force};
1658
b92400b6
DM
1659 my $code = sub {
1660
1661 $rpcenv->{type} = 'priv'; # to start tasks in background
1662
3947d0a0
DM
1663 if (!PVE::Cluster::check_cfs_quorum(1)) {
1664 print "waiting for quorum ...\n";
1665 do {
1666 sleep(1);
1667 } while (!PVE::Cluster::check_cfs_quorum(1));
1668 print "got quorum\n";
b92400b6 1669 }
1c8dc310
TL
1670
1671 eval { # remove backup locks, but avoid running into a scheduled backup job
1672 PVE::Tools::lock_file('/var/run/vzdump.lock', 10, $remove_locks_on_startup, $nodename);
1673 }; warn $@ if $@;
1674
c09c7160 1675 my $autostart = $force ? undef : 1;
2a498506 1676 my $startList = &$get_start_stop_list($nodename, $autostart, $param->{vms});
3f12bcfb
DM
1677
1678 # Note: use numeric sorting with <=>
5eec2b04 1679 foreach my $order (sort {$a <=> $b} keys %$startList) {
b92400b6
DM
1680 my $vmlist = $startList->{$order};
1681
3f12bcfb 1682 foreach my $vmid (sort {$a <=> $b} keys %$vmlist) {
b92400b6
DM
1683 my $d = $vmlist->{$vmid};
1684
1685 PVE::Cluster::check_cfs_quorum(); # abort when we loose quorum
b72cdbb7 1686
b92400b6
DM
1687 eval {
1688 my $default_delay = 0;
1689 my $upid;
c6bb5891 1690 my $typeText = '';
b92400b6 1691
2c27e4b7 1692 if ($d->{type} eq 'lxc') {
c6bb5891 1693 $typeText = 'CT';
2c27e4b7
DM
1694 return if PVE::LXC::check_running($vmid);
1695 print STDERR "Starting CT $vmid\n";
1b5e56f2 1696 $upid = PVE::API2::LXC::Status->vm_start({node => $nodename, vmid => $vmid });
2c27e4b7 1697 } elsif ($d->{type} eq 'qemu') {
c6bb5891 1698 $typeText = 'VM';
d6c49392 1699 $default_delay = 3; # to reduce load
b92400b6
DM
1700 return if PVE::QemuServer::check_running($vmid, 1);
1701 print STDERR "Starting VM $vmid\n";
1702 $upid = PVE::API2::Qemu->vm_start({node => $nodename, vmid => $vmid });
1703 } else {
1704 die "unknown VM type '$d->{type}'\n";
1705 }
1706
1707 my $res = PVE::Tools::upid_decode($upid);
1708 while (PVE::ProcFSTools::check_process_running($res->{pid})) {
1709 sleep(1);
1710 }
1711
1712 my $status = PVE::Tools::upid_read_status($upid);
1713 if ($status eq 'OK') {
1714 # use default delay to reduce load
1715 my $delay = defined($d->{up}) ? int($d->{up}) : $default_delay;
1716 if ($delay > 0) {
1717 print STDERR "Waiting for $delay seconds (startup delay)\n" if $d->{up};
1718 for (my $i = 0; $i < $delay; $i++) {
1719 sleep(1);
1720 }
1721 }
1722 } else {
c6bb5891 1723 print STDERR "Starting $typeText $vmid failed: $status\n";
b92400b6
DM
1724 }
1725 };
1726 warn $@ if $@;
1727 }
1728 }
1729 return;
1730 };
1731
1732 return $rpcenv->fork_worker('startall', undef, $authuser, $code);
1733 }});
1734
1735my $create_stop_worker = sub {
1736 my ($nodename, $type, $vmid, $down_timeout) = @_;
1737
1738 my $upid;
2c27e4b7
DM
1739 if ($type eq 'lxc') {
1740 return if !PVE::LXC::check_running($vmid);
1741 my $timeout = defined($down_timeout) ? int($down_timeout) : 60;
1742 print STDERR "Stopping CT $vmid (timeout = $timeout seconds)\n";
1b5e56f2 1743 $upid = PVE::API2::LXC::Status->vm_shutdown({node => $nodename, vmid => $vmid,
2c27e4b7
DM
1744 timeout => $timeout, forceStop => 1 });
1745 } elsif ($type eq 'qemu') {
b92400b6
DM
1746 return if !PVE::QemuServer::check_running($vmid, 1);
1747 my $timeout = defined($down_timeout) ? int($down_timeout) : 60*3;
88ba9a1d 1748 print STDERR "Stopping VM $vmid (timeout = $timeout seconds)\n";
f9d26e09 1749 $upid = PVE::API2::Qemu->vm_shutdown({node => $nodename, vmid => $vmid,
b92400b6
DM
1750 timeout => $timeout, forceStop => 1 });
1751 } else {
1752 die "unknown VM type '$type'\n";
1753 }
1754
37bec895 1755 return $upid;
b92400b6
DM
1756};
1757
1758__PACKAGE__->register_method ({
f9d26e09
TL
1759 name => 'stopall',
1760 path => 'stopall',
b92400b6
DM
1761 method => 'POST',
1762 protected => 1,
17e3b3b2
CS
1763 permissions => {
1764 check => ['perm', '/', [ 'VM.PowerMgmt' ]],
1765 },
9c8a09a7 1766 proxyto => 'node',
b92400b6
DM
1767 description => "Stop all VMs and Containers.",
1768 parameters => {
6110ed03 1769 additionalProperties => 0,
b92400b6
DM
1770 properties => {
1771 node => get_standard_option('pve-node'),
2a498506
DC
1772 vms => {
1773 description => "Only consider Guests with these IDs.",
1774 type => 'string', format => 'pve-vmid-list',
1775 optional => 1,
1776 },
b92400b6
DM
1777 },
1778 },
1779 returns => {
1780 type => 'string',
1781 },
1782 code => sub {
1783 my ($param) = @_;
1784
1785 my $rpcenv = PVE::RPCEnvironment::get();
1786 my $authuser = $rpcenv->get_user();
1787
1788 my $nodename = $param->{node};
1789 $nodename = PVE::INotify::nodename() if $nodename eq 'localhost';
1790
1791 my $code = sub {
1792
1793 $rpcenv->{type} = 'priv'; # to start tasks in background
1794
2a498506 1795 my $stopList = &$get_start_stop_list($nodename, undef, $param->{vms});
b92400b6
DM
1796
1797 my $cpuinfo = PVE::ProcFSTools::read_cpuinfo();
89ceb802
TL
1798 my $datacenterconfig = cfs_read_file('datacenter.cfg');
1799 # if not set by user spawn max cpu count number of workers
1800 my $maxWorkers = $datacenterconfig->{max_workers} || $cpuinfo->{cpus};
b92400b6
DM
1801
1802 foreach my $order (sort {$b <=> $a} keys %$stopList) {
1803 my $vmlist = $stopList->{$order};
1804 my $workers = {};
37bec895
DM
1805
1806 my $finish_worker = sub {
1807 my $pid = shift;
1808 my $d = $workers->{$pid};
1809 return if !$d;
1810 delete $workers->{$pid};
1811
1812 syslog('info', "end task $d->{upid}");
1813 };
1814
b92400b6
DM
1815 foreach my $vmid (sort {$b <=> $a} keys %$vmlist) {
1816 my $d = $vmlist->{$vmid};
37bec895
DM
1817 my $upid;
1818 eval { $upid = &$create_stop_worker($nodename, $d->{type}, $vmid, $d->{down}); };
b92400b6 1819 warn $@ if $@;
37bec895
DM
1820 next if !$upid;
1821
1822 my $res = PVE::Tools::upid_decode($upid, 1);
1823 next if !$res;
1824
1825 my $pid = $res->{pid};
1826
1827 $workers->{$pid} = { type => $d->{type}, upid => $upid, vmid => $vmid };
b92400b6
DM
1828 while (scalar(keys %$workers) >= $maxWorkers) {
1829 foreach my $p (keys %$workers) {
1830 if (!PVE::ProcFSTools::check_process_running($p)) {
37bec895 1831 &$finish_worker($p);
b92400b6
DM
1832 }
1833 }
1834 sleep(1);
1835 }
1836 }
1837 while (scalar(keys %$workers)) {
1838 foreach my $p (keys %$workers) {
1839 if (!PVE::ProcFSTools::check_process_running($p)) {
37bec895 1840 &$finish_worker($p);
b92400b6
DM
1841 }
1842 }
1843 sleep(1);
1844 }
1845 }
37bec895
DM
1846
1847 syslog('info', "all VMs and CTs stopped");
1848
b92400b6
DM
1849 return;
1850 };
1851
1852 return $rpcenv->fork_worker('stopall', undef, $authuser, $code);
b92400b6
DM
1853 }});
1854
9c8a09a7 1855my $create_migrate_worker = sub {
fc6b77a1 1856 my ($nodename, $type, $vmid, $target, $with_local_disks) = @_;
9c8a09a7
AD
1857
1858 my $upid;
2c27e4b7
DM
1859 if ($type eq 'lxc') {
1860 my $online = PVE::LXC::check_running($vmid) ? 1 : 0;
1861 print STDERR "Migrating CT $vmid\n";
1862 $upid = PVE::API2::LXC->migrate_vm({node => $nodename, vmid => $vmid, target => $target,
e5045607 1863 restart => $online });
2c27e4b7 1864 } elsif ($type eq 'qemu') {
0b54f653
TL
1865 print STDERR "Check VM $vmid: ";
1866 *STDERR->flush();
9c8a09a7 1867 my $online = PVE::QemuServer::check_running($vmid, 1) ? 1 : 0;
d8d17271 1868 my $preconditions = PVE::API2::Qemu->migrate_vm_precondition({node => $nodename, vmid => $vmid, target => $target});
0b54f653
TL
1869 my $invalidConditions = '';
1870 if ($online && !$with_local_disks && scalar @{$preconditions->{local_disks}}) {
1871 $invalidConditions .= "\n Has local disks: " .
1872 join(', ', map { $_->{volid} } @{$preconditions->{local_disks}});
d8d17271
TM
1873 }
1874
1875 if (@{$preconditions->{local_resources}}) {
0b54f653 1876 $invalidConditions .= "\n Has local resources: " . join(', ', @{$preconditions->{local_resources}});
d8d17271
TM
1877 }
1878
0b54f653
TL
1879 if ($invalidConditions && $invalidConditions ne '') {
1880 print STDERR "skip VM $vmid - precondition check failed:";
1881 die "$invalidConditions\n";
1882 }
1883 print STDERR "precondition check passed\n";
9c8a09a7 1884 print STDERR "Migrating VM $vmid\n";
71fd3de9
TL
1885
1886 my $params = {
6f3d18dd
TL
1887 node => $nodename,
1888 vmid => $vmid,
1889 target => $target,
1890 online => $online,
71fd3de9 1891 };
13411f99 1892 $params->{'with-local-disks'} = $with_local_disks if defined($with_local_disks);
71fd3de9
TL
1893
1894 $upid = PVE::API2::Qemu->migrate_vm($params);
9c8a09a7
AD
1895 } else {
1896 die "unknown VM type '$type'\n";
1897 }
1898
1899 my $res = PVE::Tools::upid_decode($upid);
1900
1901 return $res->{pid};
1902};
1903
1904__PACKAGE__->register_method ({
1905 name => 'migrateall',
1906 path => 'migrateall',
1907 method => 'POST',
1908 proxyto => 'node',
1909 protected => 1,
17e3b3b2
CS
1910 permissions => {
1911 check => ['perm', '/', [ 'VM.Migrate' ]],
1912 },
9c8a09a7
AD
1913 description => "Migrate all VMs and Containers.",
1914 parameters => {
1915 additionalProperties => 0,
1916 properties => {
1917 node => get_standard_option('pve-node'),
1918 target => get_standard_option('pve-node', { description => "Target node." }),
1919 maxworkers => {
89ceb802
TL
1920 description => "Maximal number of parallel migration job." .
1921 " If not set use 'max_workers' from datacenter.cfg," .
1922 " one of both must be set!",
1923 optional => 1,
9c8a09a7
AD
1924 type => 'integer',
1925 minimum => 1
1926 },
2a498506
DC
1927 vms => {
1928 description => "Only consider Guests with these IDs.",
1929 type => 'string', format => 'pve-vmid-list',
1930 optional => 1,
1931 },
fc6b77a1
TM
1932 "with-local-disks" => {
1933 type => 'boolean',
1934 description => "Enable live storage migration for local disk",
1935 optional => 1,
1936 },
9c8a09a7
AD
1937 },
1938 },
1939 returns => {
1940 type => 'string',
1941 },
1942 code => sub {
1943 my ($param) = @_;
1944
1945 my $rpcenv = PVE::RPCEnvironment::get();
1946 my $authuser = $rpcenv->get_user();
1947
1948 my $nodename = $param->{node};
1949 $nodename = PVE::INotify::nodename() if $nodename eq 'localhost';
1950
73981e39 1951 my $target = $param->{target};
fc6b77a1 1952 my $with_local_disks = $param->{'with-local-disks'};
73981e39
DC
1953 raise_param_exc({ target => "target is local node."}) if $target eq $nodename;
1954
1955 PVE::Cluster::check_cfs_quorum();
1956
1957 PVE::Cluster::check_node_exists($target);
89ceb802
TL
1958
1959 my $datacenterconfig = cfs_read_file('datacenter.cfg');
1960 # prefer parameter over datacenter cfg settings
1961 my $maxWorkers = $param->{maxworkers} || $datacenterconfig->{max_workers} ||
1962 die "either 'maxworkers' parameter or max_workers in datacenter.cfg must be set!\n";
9c8a09a7
AD
1963
1964 my $code = sub {
9c8a09a7
AD
1965 $rpcenv->{type} = 'priv'; # to start tasks in background
1966
f5c1dde5 1967 my $vmlist = &$get_filtered_vmlist($nodename, $param->{vms}, 1, 1);
49652c46
TL
1968 if (!scalar(keys %$vmlist)) {
1969 warn "no virtual guests matched, nothing to do..\n";
1970 return;
1971 }
9c8a09a7 1972
f5c1dde5 1973 my $workers = {};
49652c46 1974 my $workers_started = 0;
f5c1dde5
TL
1975 foreach my $vmid (sort keys %$vmlist) {
1976 my $d = $vmlist->{$vmid};
1977 my $pid;
fc6b77a1 1978 eval { $pid = &$create_migrate_worker($nodename, $d->{type}, $vmid, $target, $with_local_disks); };
f5c1dde5
TL
1979 warn $@ if $@;
1980 next if !$pid;
9c8a09a7 1981
49652c46 1982 $workers_started++;
f5c1dde5
TL
1983 $workers->{$pid} = 1;
1984 while (scalar(keys %$workers) >= $maxWorkers) {
9c8a09a7
AD
1985 foreach my $p (keys %$workers) {
1986 if (!PVE::ProcFSTools::check_process_running($p)) {
1987 delete $workers->{$p};
1988 }
1989 }
1990 sleep(1);
1991 }
1992 }
f5c1dde5
TL
1993 while (scalar(keys %$workers)) {
1994 foreach my $p (keys %$workers) {
8e4bee65 1995 # FIXME: what about PID re-use ?!?!
f5c1dde5
TL
1996 if (!PVE::ProcFSTools::check_process_running($p)) {
1997 delete $workers->{$p};
1998 }
1999 }
2000 sleep(1);
2001 }
49652c46
TL
2002 if ($workers_started <= 0) {
2003 die "no migrations worker started...\n";
2004 }
2005 print STDERR "All jobs finished, used $workers_started workers in total.\n";
9c8a09a7
AD
2006 return;
2007 };
2008
2009 return $rpcenv->fork_worker('migrateall', undef, $authuser, $code);
2010
2011 }});
2012
820d0458
DC
2013__PACKAGE__->register_method ({
2014 name => 'get_etc_hosts',
2015 path => 'hosts',
2016 method => 'GET',
2017 proxyto => 'node',
2018 protected => 1,
2019 permissions => {
2020 check => ['perm', '/', [ 'Sys.Audit' ]],
2021 },
2022 description => "Get the content of /etc/hosts.",
2023 parameters => {
2024 additionalProperties => 0,
2025 properties => {
2026 node => get_standard_option('pve-node'),
2027 },
2028 },
2029 returns => {
2030 type => 'object',
2031 properties => {
2032 digest => get_standard_option('pve-config-digest'),
2033 data => {
2034 type => 'string',
2035 description => 'The content of /etc/hosts.'
2036 },
2037 },
2038 },
2039 code => sub {
2040 my ($param) = @_;
2041
2042 return PVE::INotify::read_file('etchosts');
2043
2044 }});
2045
2046__PACKAGE__->register_method ({
2047 name => 'write_etc_hosts',
2048 path => 'hosts',
2049 method => 'POST',
2050 proxyto => 'node',
2051 protected => 1,
2052 permissions => {
2053 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
2054 },
2055 description => "Write /etc/hosts.",
2056 parameters => {
2057 additionalProperties => 0,
2058 properties => {
2059 node => get_standard_option('pve-node'),
2060 digest => get_standard_option('pve-config-digest'),
2061 data => {
2062 type => 'string',
2063 description => 'The target content of /etc/hosts.'
2064 },
2065 },
2066 },
2067 returns => {
2068 type => 'null',
2069 },
2070 code => sub {
2071 my ($param) = @_;
2072
2073 PVE::Tools::lock_file('/var/lock/pve-etchosts.lck', undef, sub{
2074 if ($param->{digest}) {
2075 my $hosts = PVE::INotify::read_file('etchosts');
2076 PVE::Tools::assert_if_modified($hosts->{digest}, $param->{digest});
2077 }
2078 PVE::INotify::write_file('etchosts', $param->{data});
2079 });
2080 die $@ if $@;
2081
2082 return undef;
2083 }});
2084
82282acf
WL
2085# bash completion helper
2086
2087sub complete_templet_repo {
2088 my ($cmdname, $pname, $cvalue) = @_;
2089
2090 my $repo = PVE::APLInfo::load_data();
2091 my $res = [];
2092 foreach my $templ (keys %{$repo->{all}}) {
2093 next if $templ !~ m/^$cvalue/;
2094 push @$res, $templ;
2095 }
2096
2097 return $res;
2098}
2099
aff192e6
DM
2100package PVE::API2::Nodes;
2101
2102use strict;
2103use warnings;
2104
2105use PVE::SafeSyslog;
2106use PVE::Cluster;
2107use PVE::RESTHandler;
2108use PVE::RPCEnvironment;
b193e4ac 2109use PVE::API2Tools;
f57cbe5d 2110use PVE::JSONSchema qw(get_standard_option);
aff192e6
DM
2111
2112use base qw(PVE::RESTHandler);
2113
2114__PACKAGE__->register_method ({
f9d26e09 2115 subclass => "PVE::API2::Nodes::Nodeinfo",
aff192e6
DM
2116 path => '{node}',
2117});
2118
2119__PACKAGE__->register_method ({
f9d26e09
TL
2120 name => 'index',
2121 path => '',
aff192e6
DM
2122 method => 'GET',
2123 permissions => { user => 'all' },
2124 description => "Cluster node index.",
2125 parameters => {
6110ed03 2126 additionalProperties => 0,
aff192e6
DM
2127 properties => {},
2128 },
2129 returns => {
2130 type => 'array',
2131 items => {
2132 type => "object",
f57cbe5d
DM
2133 properties => {
2134 node => get_standard_option('pve-node'),
2135 status => {
2136 description => "Node status.",
2137 type => 'string',
2138 enum => ['unknown', 'online', 'offline'],
2139 },
2140 cpu => {
2141 description => "CPU utilization.",
2142 type => 'number',
2143 optional => 1,
2144 renderer => 'fraction_as_percentage',
2145 },
2146 maxcpu => {
2147 description => "Number of available CPUs.",
2148 type => 'integer',
2149 optional => 1,
2150 },
2151 mem => {
2152 description => "Used memory in bytes.",
4a512d7a 2153 type => 'integer',
f57cbe5d
DM
2154 optional => 1,
2155 renderer => 'bytes',
2156 },
2157 maxmem => {
2158 description => "Number of available memory in bytes.",
2159 type => 'integer',
2160 optional => 1,
2161 renderer => 'bytes',
2162 },
2163 level => {
2164 description => "Support level.",
2165 type => 'string',
2166 optional => 1,
2167 },
2168 uptime => {
2169 description => "Node uptime in seconds.",
2170 type => 'integer',
2171 optional => 1,
2172 renderer => 'duration',
2173 },
2174 ssl_fingerprint => {
2175 description => "The SSL fingerprint for the node certificate.",
2176 type => 'string',
2177 optional => 1,
2178 },
2179 },
aff192e6 2180 },
b193e4ac 2181 links => [ { rel => 'child', href => "{node}" } ],
aff192e6
DM
2182 },
2183 code => sub {
2184 my ($param) = @_;
f9d26e09 2185
57d56896
TL
2186 my $rpcenv = PVE::RPCEnvironment::get();
2187 my $authuser = $rpcenv->get_user();
2188
aff192e6
DM
2189 my $clinfo = PVE::Cluster::get_clinfo();
2190 my $res = [];
2191
b193e4ac
DM
2192 my $nodelist = PVE::Cluster::get_nodelist();
2193 my $members = PVE::Cluster::get_members();
aff192e6
DM
2194 my $rrd = PVE::Cluster::rrd_dump();
2195
b193e4ac 2196 foreach my $node (@$nodelist) {
57d56896
TL
2197 my $can_audit = $rpcenv->check($authuser, "/nodes/$node", [ 'Sys.Audit' ], 1);
2198 my $entry = PVE::API2Tools::extract_node_stats($node, $members, $rrd, !$can_audit);
1a664b0f
FG
2199
2200 $entry->{ssl_fingerprint} = eval { PVE::Cluster::get_node_fingerprint($node) };
2201 warn "$@" if $@;
2202
aff192e6
DM
2203 push @$res, $entry;
2204 }
2205
2206 return $res;
2207 }});
2208
22091;