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