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