]> git.proxmox.com Git - pve-manager.git/blame - PVE/API2/Nodes.pm
fix #2185: add option to change nfs version on gui
[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 => {
718 type=> 'number',
f9b08743 719 description => "Display all log since this UNIX epoch. Conflicts with 'startcursor'.",
1d397a83
DC
720 optional => 1,
721 },
722 until => {
723 type=> 'number',
f9b08743 724 description => "Display all log until this UNIX epoch. Conflicts with 'endcursor'.",
1d397a83
DC
725 optional => 1,
726 },
727 lastentries => {
f9b08743 728 description => "Limit to the last X lines. Conflicts with any cursor or time range parameters.",
1d397a83
DC
729 type => 'integer',
730 optional => 1,
731 },
732 startcursor => {
f9b08743 733 description => "Start after the given Cursor. Conflicts with 'since'",
1d397a83
DC
734 type => 'string',
735 optional => 1,
736 },
737 endcursor => {
f9b08743 738 description => "End before the given Cursor. Conflicts with 'until'",
1d397a83
DC
739 type => 'string',
740 optional => 1,
741 },
742 },
743 },
744 returns => {
745 type => 'array',
746 items => {
747 type => "string",
748 }
749 },
750 code => sub {
751 my ($param) = @_;
752
753 my $rpcenv = PVE::RPCEnvironment::get();
754 my $user = $rpcenv->get_user();
755
f9b08743
TL
756 my $cmd = ["/usr/bin/mini-journalreader"];
757
758 my $add_to_cmd = sub {
759 my ($opt, $p, @conflicts) = @_;
760 return if !defined($param->{$p});
761
762 for my $c (@conflicts) {
763 die "parameters '$p' and '$c' conflict with each other!\n"
764 if defined($param->{$c});
765 }
1d397a83 766
f9b08743 767 push @$cmd, $opt, $param->{$p};
1d397a83
DC
768 };
769
f9b08743
TL
770 $add_to_cmd->('-b', 'since', 'startcursor', 'lastentries');
771 $add_to_cmd->('-e', 'until', 'endcursor', 'lastentries');
772 $add_to_cmd->('-f', 'startcursor', 'lastentries');
773 $add_to_cmd->('-t', 'endcursor', 'lastentries');
774 $add_to_cmd->('-n', 'lastentries');
775
776 my $lines = [];
777 my $parser = sub { push @$lines, shift };
1d397a83
DC
778
779 PVE::Tools::run_command($cmd, outfunc => $parser);
780
781 return $lines;
782 }});
783
aff192e6
DM
784my $sslcert;
785
dab7a849 786my $shell_cmd_map = {
d03d7e1e
TM
787 'login' => [ '/bin/login', '-f', 'root' ],
788 'upgrade' => [ '/usr/bin/pveupgrade', '--shell' ],
c3b04731 789 'ceph_install' => [ '/usr/bin/pveceph', 'install' ],
d03d7e1e
TM
790};
791
792sub get_shell_command {
793 my ($user, $shellcmd) = @_;
794
795 if ($user eq 'root@pam') {
fc1da3b0 796 if (defined($shellcmd) && exists($shell_cmd_map->{$shellcmd})) {
dab7a849 797 return $shell_cmd_map->{$shellcmd};
d03d7e1e
TM
798 } else {
799 return [ '/bin/login', '-f', 'root' ];
800 }
801 } else {
802 return [ '/bin/login' ];
803 }
804}
805
aff192e6 806__PACKAGE__->register_method ({
f9d26e09
TL
807 name => 'vncshell',
808 path => 'vncshell',
aff192e6
DM
809 method => 'POST',
810 protected => 1,
811 permissions => {
d0289a19 812 description => "Restricted to users on realm 'pam'",
7d020b42 813 check => ['perm', '/nodes/{node}', [ 'Sys.Console' ]],
aff192e6
DM
814 },
815 description => "Creates a VNC Shell proxy.",
816 parameters => {
817 additionalProperties => 0,
818 properties => {
819 node => get_standard_option('pve-node'),
3a76893d
DM
820 upgrade => {
821 type => 'boolean',
2d39fd70 822 description => "Deprecated, use the 'cmd' property instead! Run 'apt-get dist-upgrade' instead of normal shell.",
3a76893d
DM
823 optional => 1,
824 default => 0,
825 },
d03d7e1e
TM
826 cmd => {
827 type => 'string',
828 description => "Run specific command or default to login.",
dab7a849 829 enum => [keys %$shell_cmd_map],
d03d7e1e
TM
830 optional => 1,
831 default => 'login',
832 },
8dfca17e
DM
833 websocket => {
834 optional => 1,
835 type => 'boolean',
836 description => "use websocket instead of standard vnc.",
837 },
b8ac8b0c
DC
838 width => {
839 optional => 1,
840 description => "sets the width of the console in pixels.",
841 type => 'integer',
842 minimum => 16,
843 maximum => 4096,
844 },
845 height => {
846 optional => 1,
847 description => "sets the height of the console in pixels.",
848 type => 'integer',
849 minimum => 16,
850 maximum => 2160,
851 },
aff192e6
DM
852 },
853 },
f9d26e09 854 returns => {
aff192e6
DM
855 additionalProperties => 0,
856 properties => {
857 user => { type => 'string' },
858 ticket => { type => 'string' },
859 cert => { type => 'string' },
860 port => { type => 'integer' },
861 upid => { type => 'string' },
862 },
863 },
864 code => sub {
865 my ($param) = @_;
866
867 my $rpcenv = PVE::RPCEnvironment::get();
868
d553e535 869 my ($user, undef, $realm) = PVE::AccessControl::verify_username($rpcenv->get_user());
d0289a19 870
f9d26e09 871 raise_perm_exc("realm != pam") if $realm ne 'pam';
aff192e6 872
3a76893d
DM
873 raise_perm_exc('user != root@pam') if $param->{upgrade} && $user ne 'root@pam';
874
aff192e6
DM
875 my $node = $param->{node};
876
57ebda08
DM
877 my $authpath = "/nodes/$node";
878
879 my $ticket = PVE::AccessControl::assemble_vnc_ticket($user, $authpath);
880
aff192e6
DM
881 $sslcert = PVE::Tools::file_get_contents("/etc/pve/pve-root-ca.pem", 8192)
882 if !$sslcert;
883
eb6d7497 884 my ($remip, $family);
b179a622 885
aff192e6 886 if ($node ne PVE::INotify::nodename()) {
eb6d7497
WB
887 ($remip, $family) = PVE::Cluster::remote_node_ip($node);
888 } else {
889 $family = PVE::Tools::get_host_address_family($node);
aff192e6
DM
890 }
891
eb6d7497
WB
892 my $port = PVE::Tools::next_vnc_port($family);
893
aff192e6
DM
894 # NOTE: vncterm VNC traffic is already TLS encrypted,
895 # so we select the fastest chipher here (or 'none'?)
f9d26e09 896 my $remcmd = $remip ?
cb1b1712 897 ['/usr/bin/ssh', '-e', 'none', '-t', $remip] : [];
d4a25f0b 898
2d39fd70 899 # FIXME: remove with 6.0
d03d7e1e
TM
900 if ($param->{upgrade}) {
901 $param->{cmd} = 'upgrade';
3a76893d 902 }
d03d7e1e 903 my $shcmd = get_shell_command($user, $param->{cmd});
aff192e6 904
f9d26e09 905 my $timeout = 10;
aff192e6 906
6d394492 907 my $cmd = ['/usr/bin/vncterm', '-rfbport', $port,
f9d26e09 908 '-timeout', $timeout, '-authpath', $authpath,
8dfca17e
DM
909 '-perm', 'Sys.Console'];
910
b8ac8b0c
DC
911 if ($param->{width}) {
912 push @$cmd, '-width', $param->{width};
913 }
914
915 if ($param->{height}) {
916 push @$cmd, '-height', $param->{height};
917 }
918
8dfca17e 919 if ($param->{websocket}) {
f9d26e09 920 $ENV{PVE_VNC_TICKET} = $ticket; # pass ticket to vncterm
8dfca17e
DM
921 push @$cmd, '-notls', '-listen', 'localhost';
922 }
923
924 push @$cmd, '-c', @$remcmd, @$shcmd;
aff192e6
DM
925
926 my $realcmd = sub {
927 my $upid = shift;
928
929 syslog ('info', "starting vnc proxy $upid\n");
930
6d394492 931 my $cmdstr = join (' ', @$cmd);
aff192e6
DM
932 syslog ('info', "launch command: $cmdstr");
933
f9d26e09 934 eval {
6d394492 935 foreach my $k (keys %ENV) {
8dfca17e 936 next if $k eq 'PVE_VNC_TICKET';
b0d4b407 937 next if $k eq 'PATH' || $k eq 'TERM' || $k eq 'USER' || $k eq 'HOME' || $k eq 'LANG' || $k eq 'LANGUAGE';
6d394492
DM
938 delete $ENV{$k};
939 }
940 $ENV{PWD} = '/';
941
b0d4b407 942 PVE::Tools::run_command($cmd, errmsg => "vncterm failed", keeplocale => 1);
6d394492
DM
943 };
944 if (my $err = $@) {
945 syslog ('err', $err);
aff192e6
DM
946 }
947
948 return;
949 };
950
951 my $upid = $rpcenv->fork_worker('vncshell', "", $user, $realcmd);
f9d26e09 952
6806a0f8 953 PVE::Tools::wait_for_vnc_port($port);
aff192e6
DM
954
955 return {
956 user => $user,
957 ticket => $ticket,
f9d26e09
TL
958 port => $port,
959 upid => $upid,
960 cert => $sslcert,
aff192e6
DM
961 };
962 }});
963
4b168c27
DC
964__PACKAGE__->register_method ({
965 name => 'termproxy',
966 path => 'termproxy',
967 method => 'POST',
968 protected => 1,
969 permissions => {
970 description => "Restricted to users on realm 'pam'",
971 check => ['perm', '/nodes/{node}', [ 'Sys.Console' ]],
972 },
973 description => "Creates a VNC Shell proxy.",
974 parameters => {
975 additionalProperties => 0,
976 properties => {
977 node => get_standard_option('pve-node'),
978 upgrade => {
979 type => 'boolean',
2d39fd70 980 description => "Deprecated, use the 'cmd' property instead! Run 'apt-get dist-upgrade' instead of normal shell.",
4b168c27
DC
981 optional => 1,
982 default => 0,
983 },
d03d7e1e
TM
984 cmd => {
985 type => 'string',
986 description => "Run specific command or default to login.",
dab7a849 987 enum => [keys %$shell_cmd_map],
d03d7e1e
TM
988 optional => 1,
989 default => 'login',
990 },
4b168c27
DC
991 },
992 },
993 returns => {
994 additionalProperties => 0,
995 properties => {
996 user => { type => 'string' },
997 ticket => { type => 'string' },
998 port => { type => 'integer' },
999 upid => { type => 'string' },
1000 },
1001 },
1002 code => sub {
1003 my ($param) = @_;
1004
1005 my $rpcenv = PVE::RPCEnvironment::get();
1006
1007 my ($user, undef, $realm) = PVE::AccessControl::verify_username($rpcenv->get_user());
1008
1009 raise_perm_exc("realm != pam") if $realm ne 'pam';
1010
1011 my $node = $param->{node};
1012
1013 my $authpath = "/nodes/$node";
1014
1015 my $ticket = PVE::AccessControl::assemble_vnc_ticket($user, $authpath);
1016
1017 my ($remip, $family);
1018
1019 if ($node ne 'localhost' && $node ne PVE::INotify::nodename()) {
1020 ($remip, $family) = PVE::Cluster::remote_node_ip($node);
1021 } else {
1022 $family = PVE::Tools::get_host_address_family($node);
1023 }
1024
1025 my $port = PVE::Tools::next_vnc_port($family);
1026
1027 my $remcmd = $remip ?
1028 ['/usr/bin/ssh', '-e', 'none', '-t', $remip , '--'] : [];
2d39fd70 1029 # FIXME: remove with 6.0
d03d7e1e
TM
1030 if ($param->{upgrade}) {
1031 $param->{cmd} = 'upgrade';
4b168c27 1032 }
d03d7e1e 1033 my $shcmd = get_shell_command($user, $param->{cmd});
4b168c27
DC
1034
1035 my $realcmd = sub {
1036 my $upid = shift;
1037
1038 syslog ('info', "starting termproxy $upid\n");
1039
1040 my $cmd = ['/usr/bin/termproxy', $port, '--path', $authpath,
1041 '--perm', 'Sys.Console', '--'];
d03d7e1e 1042 push @$cmd, @$remcmd, @$shcmd;
4b168c27
DC
1043
1044 PVE::Tools::run_command($cmd);
1045 };
1046
1047 my $upid = $rpcenv->fork_worker('vncshell', "", $user, $realcmd);
1048
1049 PVE::Tools::wait_for_vnc_port($port);
1050
1051 return {
1052 user => $user,
1053 ticket => $ticket,
1054 port => $port,
1055 upid => $upid,
1056 };
1057 }});
1058
8dfca17e
DM
1059__PACKAGE__->register_method({
1060 name => 'vncwebsocket',
1061 path => 'vncwebsocket',
1062 method => 'GET',
f9d26e09 1063 permissions => {
8dfca17e
DM
1064 description => "Restricted to users on realm 'pam'. You also need to pass a valid ticket (vncticket).",
1065 check => ['perm', '/nodes/{node}', [ 'Sys.Console' ]],
1066 },
1067 description => "Opens a weksocket for VNC traffic.",
1068 parameters => {
1069 additionalProperties => 0,
1070 properties => {
1071 node => get_standard_option('pve-node'),
1072 vncticket => {
1073 description => "Ticket from previous call to vncproxy.",
1074 type => 'string',
1075 maxLength => 512,
1076 },
1077 port => {
1078 description => "Port number returned by previous vncproxy call.",
1079 type => 'integer',
1080 minimum => 5900,
1081 maximum => 5999,
1082 },
1083 },
1084 },
1085 returns => {
1086 type => "object",
1087 properties => {
1088 port => { type => 'string' },
1089 },
1090 },
1091 code => sub {
1092 my ($param) = @_;
1093
1094 my $rpcenv = PVE::RPCEnvironment::get();
1095
1096 my ($user, undef, $realm) = PVE::AccessControl::verify_username($rpcenv->get_user());
1097
f9d26e09 1098 raise_perm_exc("realm != pam") if $realm ne 'pam';
8dfca17e
DM
1099
1100 my $authpath = "/nodes/$param->{node}";
1101
1102 PVE::AccessControl::verify_vnc_ticket($param->{vncticket}, $user, $authpath);
1103
1104 my $port = $param->{port};
f9d26e09 1105
8dfca17e
DM
1106 return { port => $port };
1107 }});
1108
2d802f8c 1109__PACKAGE__->register_method ({
f9d26e09
TL
1110 name => 'spiceshell',
1111 path => 'spiceshell',
2d802f8c
DM
1112 method => 'POST',
1113 protected => 1,
1114 proxyto => 'node',
1115 permissions => {
1116 description => "Restricted to users on realm 'pam'",
1117 check => ['perm', '/nodes/{node}', [ 'Sys.Console' ]],
1118 },
7774df78 1119 description => "Creates a SPICE shell.",
2d802f8c
DM
1120 parameters => {
1121 additionalProperties => 0,
1122 properties => {
1123 node => get_standard_option('pve-node'),
7774df78 1124 proxy => get_standard_option('spice-proxy', { optional => 1 }),
2d802f8c
DM
1125 upgrade => {
1126 type => 'boolean',
2d39fd70 1127 description => "Deprecated, use the 'cmd' property instead! Run 'apt-get dist-upgrade' instead of normal shell.",
2d802f8c
DM
1128 optional => 1,
1129 default => 0,
1130 },
d03d7e1e
TM
1131 cmd => {
1132 type => 'string',
1133 description => "Run specific command or default to login.",
dab7a849 1134 enum => [keys %$shell_cmd_map],
d03d7e1e
TM
1135 optional => 1,
1136 default => 'login',
1137 },
2d802f8c
DM
1138 },
1139 },
7774df78 1140 returns => get_standard_option('remote-viewer-config'),
2d802f8c
DM
1141 code => sub {
1142 my ($param) = @_;
1143
1144 my $rpcenv = PVE::RPCEnvironment::get();
1145 my $authuser = $rpcenv->get_user();
1146
1147 my ($user, undef, $realm) = PVE::AccessControl::verify_username($authuser);
1148
f9d26e09 1149 raise_perm_exc("realm != pam") if $realm ne 'pam';
2d802f8c
DM
1150
1151 raise_perm_exc('user != root@pam') if $param->{upgrade} && $user ne 'root@pam';
1152
1153 my $node = $param->{node};
1154 my $proxy = $param->{proxy};
2d802f8c
DM
1155
1156 my $authpath = "/nodes/$node";
b289829f 1157 my $permissions = 'Sys.Console';
2d39fd70 1158 # FIXME: remove with 6.0
d03d7e1e
TM
1159 if ($param->{upgrade}) {
1160 $param->{cmd} = 'upgrade';
2d802f8c 1161 }
d03d7e1e 1162 my $shcmd = get_shell_command($user, $param->{cmd});
2d802f8c 1163
b289829f 1164 my $title = "Shell on '$node'";
2d802f8c 1165
b289829f 1166 return PVE::API2Tools::run_spiceterm($authpath, $permissions, 0, $node, $proxy, $title, $shcmd);
2d802f8c
DM
1167 }});
1168
aff192e6 1169__PACKAGE__->register_method({
f9d26e09
TL
1170 name => 'dns',
1171 path => 'dns',
aff192e6
DM
1172 method => 'GET',
1173 permissions => {
7d020b42 1174 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
aff192e6
DM
1175 },
1176 description => "Read DNS settings.",
1177 proxyto => 'node',
1178 parameters => {
1179 additionalProperties => 0,
1180 properties => {
1181 node => get_standard_option('pve-node'),
1182 },
1183 },
1184 returns => {
1185 type => "object",
1186 additionalProperties => 0,
1187 properties => {
1188 search => {
1189 description => "Search domain for host-name lookup.",
1190 type => 'string',
1191 optional => 1,
1192 },
1193 dns1 => {
1194 description => 'First name server IP address.',
1195 type => 'string',
1196 optional => 1,
f9d26e09 1197 },
aff192e6
DM
1198 dns2 => {
1199 description => 'Second name server IP address.',
1200 type => 'string',
1201 optional => 1,
f9d26e09 1202 },
aff192e6
DM
1203 dns3 => {
1204 description => 'Third name server IP address.',
1205 type => 'string',
1206 optional => 1,
f9d26e09 1207 },
aff192e6
DM
1208 },
1209 },
1210 code => sub {
1211 my ($param) = @_;
1212
1213 my $res = PVE::INotify::read_file('resolvconf');
1214
1215 return $res;
1216 }});
1217
1218__PACKAGE__->register_method({
f9d26e09
TL
1219 name => 'update_dns',
1220 path => 'dns',
aff192e6
DM
1221 method => 'PUT',
1222 description => "Write DNS settings.",
d0289a19
DM
1223 permissions => {
1224 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
1225 },
aff192e6
DM
1226 proxyto => 'node',
1227 protected => 1,
1228 parameters => {
1229 additionalProperties => 0,
1230 properties => {
1231 node => get_standard_option('pve-node'),
1232 search => {
1233 description => "Search domain for host-name lookup.",
1234 type => 'string',
1235 },
1236 dns1 => {
1237 description => 'First name server IP address.',
7a9486a7 1238 type => 'string', format => 'ip',
aff192e6 1239 optional => 1,
f9d26e09 1240 },
aff192e6
DM
1241 dns2 => {
1242 description => 'Second name server IP address.',
7a9486a7 1243 type => 'string', format => 'ip',
aff192e6 1244 optional => 1,
f9d26e09 1245 },
aff192e6
DM
1246 dns3 => {
1247 description => 'Third name server IP address.',
7a9486a7 1248 type => 'string', format => 'ip',
aff192e6 1249 optional => 1,
f9d26e09 1250 },
aff192e6
DM
1251 },
1252 },
1253 returns => { type => "null" },
1254 code => sub {
1255 my ($param) = @_;
1256
1257 PVE::INotify::update_file('resolvconf', $param);
1258
1259 return undef;
1260 }});
1261
1262__PACKAGE__->register_method({
f9d26e09
TL
1263 name => 'time',
1264 path => 'time',
aff192e6
DM
1265 method => 'GET',
1266 permissions => {
7d020b42
DM
1267 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
1268 },
aff192e6
DM
1269 description => "Read server time and time zone settings.",
1270 proxyto => 'node',
1271 parameters => {
1272 additionalProperties => 0,
1273 properties => {
1274 node => get_standard_option('pve-node'),
1275 },
1276 },
1277 returns => {
1278 type => "object",
1279 additionalProperties => 0,
1280 properties => {
1281 timezone => {
1282 description => "Time zone",
1283 type => 'string',
1284 },
1285 time => {
1286 description => "Seconds since 1970-01-01 00:00:00 UTC.",
1287 type => 'integer',
1288 minimum => 1297163644,
bed5fdfc 1289 renderer => 'timestamp',
aff192e6
DM
1290 },
1291 localtime => {
1292 description => "Seconds since 1970-01-01 00:00:00 (local time)",
1293 type => 'integer',
1294 minimum => 1297163644,
bed5fdfc 1295 renderer => 'timestamp_gmt',
aff192e6
DM
1296 },
1297 },
1298 },
1299 code => sub {
1300 my ($param) = @_;
1301
1302 my $ctime = time();
1303 my $ltime = timegm_nocheck(localtime($ctime));
1304 my $res = {
1305 timezone => PVE::INotify::read_file('timezone'),
bed5fdfc 1306 time => $ctime,
aff192e6
DM
1307 localtime => $ltime,
1308 };
1309
1310 return $res;
1311 }});
1312
1313__PACKAGE__->register_method({
f9d26e09
TL
1314 name => 'set_timezone',
1315 path => 'time',
aff192e6
DM
1316 method => 'PUT',
1317 description => "Set time zone.",
d0289a19
DM
1318 permissions => {
1319 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
1320 },
aff192e6
DM
1321 proxyto => 'node',
1322 protected => 1,
1323 parameters => {
1324 additionalProperties => 0,
1325 properties => {
1326 node => get_standard_option('pve-node'),
1327 timezone => {
1328 description => "Time zone. The file '/usr/share/zoneinfo/zone.tab' contains the list of valid names.",
1329 type => 'string',
1330 },
1331 },
1332 },
1333 returns => { type => "null" },
1334 code => sub {
1335 my ($param) = @_;
1336
1337 PVE::INotify::write_file('timezone', $param->{timezone});
1338
1339 return undef;
1340 }});
1341
c9164975 1342__PACKAGE__->register_method({
f9d26e09
TL
1343 name => 'aplinfo',
1344 path => 'aplinfo',
c9164975
DM
1345 method => 'GET',
1346 permissions => {
1347 user => 'all',
1348 },
1349 description => "Get list of appliances.",
1350 proxyto => 'node',
1351 parameters => {
1352 additionalProperties => 0,
1353 properties => {
1354 node => get_standard_option('pve-node'),
1355 },
1356 },
1357 returns => {
1358 type => 'array',
1359 items => {
1360 type => "object",
1361 properties => {},
1362 },
1363 },
1364 code => sub {
1365 my ($param) = @_;
1366
1367 my $res = [];
1368
1369 my $list = PVE::APLInfo::load_data();
1370
1371 foreach my $template (keys %{$list->{all}}) {
1372 my $pd = $list->{all}->{$template};
1373 next if $pd->{'package'} eq 'pve-web-news';
1374 push @$res, $pd;
1375 }
1376
1377 return $res;
1378 }});
1379
0532bd28 1380__PACKAGE__->register_method({
f9d26e09
TL
1381 name => 'apl_download',
1382 path => 'aplinfo',
0532bd28
DM
1383 method => 'POST',
1384 permissions => {
1385 check => ['perm', '/storage/{storage}', ['Datastore.AllocateTemplate']],
1386 },
1387 description => "Download appliance templates.",
1388 proxyto => 'node',
1389 protected => 1,
1390 parameters => {
1391 additionalProperties => 0,
1392 properties => {
1393 node => get_standard_option('pve-node'),
82282acf 1394 storage => get_standard_option('pve-storage-id', {
62180d0f 1395 description => "The storage where the template will be stored",
82282acf
WL
1396 completion => \&PVE::Storage::complete_storage_enabled,
1397 }),
1398 template => { type => 'string',
1399 description => "The template wich will downloaded",
1400 maxLength => 255,
1401 completion => \&complete_templet_repo,
1402 },
0532bd28
DM
1403 },
1404 },
1405 returns => { type => "string" },
1406 code => sub {
1407 my ($param) = @_;
1408
1409 my $rpcenv = PVE::RPCEnvironment::get();
1410
1411 my $user = $rpcenv->get_user();
1412
1413 my $node = $param->{node};
1414
1415 my $list = PVE::APLInfo::load_data();
1416
1417 my $template = $param->{template};
1418 my $pd = $list->{all}->{$template};
1419
1420 raise_param_exc({ template => "no such template"}) if !$pd;
1421
bbcfdc08 1422 my $cfg = PVE::Storage::config();
0532bd28
DM
1423 my $scfg = PVE::Storage::storage_check_enabled($cfg, $param->{storage}, $node);
1424
761a2b31
DM
1425 die "unknown template type '$pd->{type}'\n"
1426 if !($pd->{type} eq 'openvz' || $pd->{type} eq 'lxc');
0532bd28 1427
f9d26e09 1428 die "storage '$param->{storage}' does not support templates\n"
0532bd28
DM
1429 if !$scfg->{content}->{vztmpl};
1430
1431 my $src = $pd->{location};
1432 my $tmpldir = PVE::Storage::get_vztmpl_dir($cfg, $param->{storage});
1433 my $dest = "$tmpldir/$template";
1434 my $tmpdest = "$tmpldir/${template}.tmp.$$";
1435
1436 my $worker = sub {
1437 my $upid = shift;
f9d26e09 1438
0532bd28
DM
1439 print "starting template download from: $src\n";
1440 print "target file: $dest\n";
1441
3b81e495
FG
1442 my $check_hash = sub {
1443 my ($template_info, $filename, $noerr) = @_;
1444
1445 my $digest;
1446 my $expected;
1447
1448 eval {
79be6db3
FG
1449 open(my $fh, '<', $filename) or die "Can't open '$filename': $!";
1450 binmode($fh);
95f99e8c
FG
1451 if (defined($template_info->{sha512sum})) {
1452 $expected = $template_info->{sha512sum};
79be6db3 1453 $digest = Digest::SHA->new(512)->addfile($fh)->hexdigest;
95f99e8c 1454 } elsif (defined($template_info->{md5sum})) {
3b81e495 1455 #fallback to MD5
95f99e8c 1456 $expected = $template_info->{md5sum};
3b81e495 1457 $digest = Digest::MD5->new->addfile($fh)->hexdigest;
3b81e495
FG
1458 } else {
1459 die "no expected checksum defined";
1460 }
79be6db3 1461 close($fh);
3b81e495
FG
1462 };
1463
1464 die "checking hash failed - $@\n" if $@ && !$noerr;
0532bd28 1465
3b81e495
FG
1466 return ($digest, $digest ? lc($digest) eq lc($expected) : 0);
1467 };
1468
1469 eval {
0532bd28 1470 if (-f $dest) {
3b81e495 1471 my ($hash, $correct) = &$check_hash($pd, $dest, 1);
0532bd28 1472
3b81e495
FG
1473 if ($hash && $correct) {
1474 print "file already exists $hash - no need to download\n";
0532bd28
DM
1475 return;
1476 }
1477 }
1478
1479 local %ENV;
1480 my $dccfg = PVE::Cluster::cfs_read_file('datacenter.cfg');
1481 if ($dccfg->{http_proxy}) {
1482 $ENV{http_proxy} = $dccfg->{http_proxy};
1483 }
1484
1485 my @cmd = ('/usr/bin/wget', '--progress=dot:mega', '-O', $tmpdest, $src);
1486 if (system (@cmd) != 0) {
1487 die "download failed - $!\n";
1488 }
1489
3b81e495
FG
1490 my ($hash, $correct) = &$check_hash($pd, $tmpdest);
1491
1492 die "could not calculate checksum\n" if !$hash;
f9d26e09 1493
3b81e495
FG
1494 if (!$correct) {
1495 my $expected = $pd->{sha512sum} // $pd->{md5sum};
1496 die "wrong checksum: $hash != $expected\n";
0532bd28
DM
1497 }
1498
c351eda9 1499 if (!rename($tmpdest, $dest)) {
0532bd28
DM
1500 die "unable to save file - $!\n";
1501 }
1502 };
1503 my $err = $@;
1504
1505 unlink $tmpdest;
1506
1507 if ($err) {
1508 print "\n";
1509 die $err if $err;
1510 }
1511
1512 print "download finished\n";
1513 };
1514
1515 return $rpcenv->fork_worker('download', undef, $user, $worker);
1516 }});
1517
34ada77a
EK
1518__PACKAGE__->register_method({
1519 name => 'report',
1520 path => 'report',
1521 method => 'GET',
1522 permissions => {
1523 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
1524 },
6bbdf981 1525 protected => 1,
34ada77a
EK
1526 description => "Gather various systems information about a node",
1527 proxyto => 'node',
1528 parameters => {
1529 additionalProperties => 0,
1530 properties => {
1531 node => get_standard_option('pve-node'),
1532 },
1533 },
1534 returns => {
cdb5a209 1535 type => 'string',
34ada77a
EK
1536 },
1537 code => sub {
cdb5a209 1538 return PVE::Report::generate();
34ada77a 1539 }});
0532bd28 1540
b72cdbb7
TL
1541# returns a list of VMIDs, those can be filtered by
1542# * current parent node
1543# * vmid whitelist
1544# * guest is a template (default: skip)
1545# * guest is HA manged (default: skip)
1546my $get_filtered_vmlist = sub {
1547 my ($nodename, $vmfilter, $templates, $ha_managed) = @_;
b92400b6 1548
b92400b6
DM
1549 my $vmlist = PVE::Cluster::get_vmlist();
1550
b72cdbb7 1551 my $vms_allowed = {};
2a498506 1552 if (defined($vmfilter)) {
2a498506 1553 foreach my $vmid (PVE::Tools::split_list($vmfilter)) {
b72cdbb7 1554 $vms_allowed->{$vmid} = 1;
2a498506
DC
1555 }
1556 }
1557
b72cdbb7 1558 my $res = {};
b92400b6 1559 foreach my $vmid (keys %{$vmlist->{ids}}) {
b72cdbb7 1560 next if %$vms_allowed && !$vms_allowed->{$vmid};
b92400b6 1561
b72cdbb7
TL
1562 my $d = $vmlist->{ids}->{$vmid};
1563 next if $nodename && $d->{node} ne $nodename;
b92400b6 1564
b72cdbb7
TL
1565 eval {
1566 my $class;
2c27e4b7 1567 if ($d->{type} eq 'lxc') {
b72cdbb7 1568 $class = 'PVE::LXC::Config';
2c27e4b7 1569 } elsif ($d->{type} eq 'qemu') {
b72cdbb7 1570 $class = 'PVE::QemuConfig';
b92400b6
DM
1571 } else {
1572 die "unknown VM type '$d->{type}'\n";
1573 }
1574
b72cdbb7
TL
1575 my $conf = $class->load_config($vmid);
1576 return if !$templates && $class->is_template($conf);
1577 return if !$ha_managed && PVE::HA::Config::vm_is_ha_managed($vmid);
04b2004b 1578
b2bb6d77 1579 $res->{$vmid}->{conf} = $conf;
b72cdbb7 1580 $res->{$vmid}->{type} = $d->{type};
1c8dc310 1581 $res->{$vmid}->{class} = $class;
b92400b6
DM
1582 };
1583 warn $@ if $@;
1584 }
1585
b72cdbb7
TL
1586 return $res;
1587};
1588
1589# return all VMs which should get started/stopped on power up/down
1590my $get_start_stop_list = sub {
1591 my ($nodename, $autostart, $vmfilter) = @_;
1592
ae9d10ca
TL
1593 # do not skip HA vms on force or if a specific VMID set is wanted
1594 my $include_ha_managed = defined($vmfilter) ? 1 : 0;
1595
1596 my $vmlist = &$get_filtered_vmlist($nodename, $vmfilter, undef, $include_ha_managed);
b72cdbb7
TL
1597
1598 my $resList = {};
1599 foreach my $vmid (keys %$vmlist) {
b2bb6d77 1600 my $conf = $vmlist->{$vmid}->{conf};
b72cdbb7
TL
1601
1602 next if $autostart && !$conf->{onboot};
1603
1604 my $startup = {};
1605 if ($conf->{startup}) {
1606 $startup = PVE::JSONSchema::pve_parse_startup_order($conf->{startup});
1607 }
1608
1609 $startup->{order} = LONG_MAX if !defined($startup->{order});
1610
1611 $resList->{$startup->{order}}->{$vmid} = $startup;
23b54109 1612 $resList->{$startup->{order}}->{$vmid}->{type} = $vmlist->{$vmid}->{type};
b72cdbb7
TL
1613 }
1614
b92400b6
DM
1615 return $resList;
1616};
1617
1c8dc310
TL
1618my $remove_locks_on_startup = sub {
1619 my ($nodename) = @_;
1620
1621 my $vmlist = &$get_filtered_vmlist($nodename, undef, undef, 1);
1622
1623 foreach my $vmid (keys %$vmlist) {
1624 my $conf = $vmlist->{$vmid}->{conf};
23b54109 1625 my $class = $vmlist->{$vmid}->{class};
1c8dc310
TL
1626
1627 eval {
1628 if ($class->has_lock($conf, 'backup')) {
1629 $class->remove_lock($vmid, 'backup');
1630 my $msg = "removed left over backup lock from '$vmid'!";
1631 warn "$msg\n"; # prints to task log
1632 syslog('warning', $msg);
1633 }
1634 }; warn $@ if $@;
1635 }
1636};
1637
b92400b6 1638__PACKAGE__->register_method ({
f9d26e09
TL
1639 name => 'startall',
1640 path => 'startall',
b92400b6
DM
1641 method => 'POST',
1642 protected => 1,
17e3b3b2
CS
1643 permissions => {
1644 check => ['perm', '/', [ 'VM.PowerMgmt' ]],
1645 },
9c8a09a7 1646 proxyto => 'node',
b92400b6
DM
1647 description => "Start all VMs and containers (when onboot=1).",
1648 parameters => {
1649 additionalProperties => 0,
1650 properties => {
1651 node => get_standard_option('pve-node'),
c09c7160
AD
1652 force => {
1653 optional => 1,
1654 type => 'boolean',
1655 description => "force if onboot=0.",
1656 },
2a498506
DC
1657 vms => {
1658 description => "Only consider Guests with these IDs.",
1659 type => 'string', format => 'pve-vmid-list',
1660 optional => 1,
1661 },
b92400b6
DM
1662 },
1663 },
1664 returns => {
1665 type => 'string',
1666 },
1667 code => sub {
1668 my ($param) = @_;
1669
1670 my $rpcenv = PVE::RPCEnvironment::get();
1671 my $authuser = $rpcenv->get_user();
1672
1673 my $nodename = $param->{node};
1674 $nodename = PVE::INotify::nodename() if $nodename eq 'localhost';
1675
c09c7160
AD
1676 my $force = $param->{force};
1677
b92400b6
DM
1678 my $code = sub {
1679
1680 $rpcenv->{type} = 'priv'; # to start tasks in background
1681
3947d0a0
DM
1682 if (!PVE::Cluster::check_cfs_quorum(1)) {
1683 print "waiting for quorum ...\n";
1684 do {
1685 sleep(1);
1686 } while (!PVE::Cluster::check_cfs_quorum(1));
1687 print "got quorum\n";
b92400b6 1688 }
1c8dc310
TL
1689
1690 eval { # remove backup locks, but avoid running into a scheduled backup job
1691 PVE::Tools::lock_file('/var/run/vzdump.lock', 10, $remove_locks_on_startup, $nodename);
1692 }; warn $@ if $@;
1693
c09c7160 1694 my $autostart = $force ? undef : 1;
2a498506 1695 my $startList = &$get_start_stop_list($nodename, $autostart, $param->{vms});
3f12bcfb
DM
1696
1697 # Note: use numeric sorting with <=>
5eec2b04 1698 foreach my $order (sort {$a <=> $b} keys %$startList) {
b92400b6
DM
1699 my $vmlist = $startList->{$order};
1700
3f12bcfb 1701 foreach my $vmid (sort {$a <=> $b} keys %$vmlist) {
b92400b6
DM
1702 my $d = $vmlist->{$vmid};
1703
1704 PVE::Cluster::check_cfs_quorum(); # abort when we loose quorum
b72cdbb7 1705
b92400b6
DM
1706 eval {
1707 my $default_delay = 0;
1708 my $upid;
c6bb5891 1709 my $typeText = '';
b92400b6 1710
2c27e4b7 1711 if ($d->{type} eq 'lxc') {
c6bb5891 1712 $typeText = 'CT';
2c27e4b7
DM
1713 return if PVE::LXC::check_running($vmid);
1714 print STDERR "Starting CT $vmid\n";
1b5e56f2 1715 $upid = PVE::API2::LXC::Status->vm_start({node => $nodename, vmid => $vmid });
2c27e4b7 1716 } elsif ($d->{type} eq 'qemu') {
c6bb5891 1717 $typeText = 'VM';
d6c49392 1718 $default_delay = 3; # to reduce load
b92400b6
DM
1719 return if PVE::QemuServer::check_running($vmid, 1);
1720 print STDERR "Starting VM $vmid\n";
1721 $upid = PVE::API2::Qemu->vm_start({node => $nodename, vmid => $vmid });
1722 } else {
1723 die "unknown VM type '$d->{type}'\n";
1724 }
1725
1726 my $res = PVE::Tools::upid_decode($upid);
1727 while (PVE::ProcFSTools::check_process_running($res->{pid})) {
1728 sleep(1);
1729 }
1730
1731 my $status = PVE::Tools::upid_read_status($upid);
1732 if ($status eq 'OK') {
1733 # use default delay to reduce load
1734 my $delay = defined($d->{up}) ? int($d->{up}) : $default_delay;
1735 if ($delay > 0) {
1736 print STDERR "Waiting for $delay seconds (startup delay)\n" if $d->{up};
1737 for (my $i = 0; $i < $delay; $i++) {
1738 sleep(1);
1739 }
1740 }
1741 } else {
c6bb5891 1742 print STDERR "Starting $typeText $vmid failed: $status\n";
b92400b6
DM
1743 }
1744 };
1745 warn $@ if $@;
1746 }
1747 }
1748 return;
1749 };
1750
1751 return $rpcenv->fork_worker('startall', undef, $authuser, $code);
1752 }});
1753
1754my $create_stop_worker = sub {
1755 my ($nodename, $type, $vmid, $down_timeout) = @_;
1756
1757 my $upid;
2c27e4b7
DM
1758 if ($type eq 'lxc') {
1759 return if !PVE::LXC::check_running($vmid);
1760 my $timeout = defined($down_timeout) ? int($down_timeout) : 60;
1761 print STDERR "Stopping CT $vmid (timeout = $timeout seconds)\n";
1b5e56f2 1762 $upid = PVE::API2::LXC::Status->vm_shutdown({node => $nodename, vmid => $vmid,
2c27e4b7
DM
1763 timeout => $timeout, forceStop => 1 });
1764 } elsif ($type eq 'qemu') {
b92400b6
DM
1765 return if !PVE::QemuServer::check_running($vmid, 1);
1766 my $timeout = defined($down_timeout) ? int($down_timeout) : 60*3;
88ba9a1d 1767 print STDERR "Stopping VM $vmid (timeout = $timeout seconds)\n";
f9d26e09 1768 $upid = PVE::API2::Qemu->vm_shutdown({node => $nodename, vmid => $vmid,
b92400b6
DM
1769 timeout => $timeout, forceStop => 1 });
1770 } else {
1771 die "unknown VM type '$type'\n";
1772 }
1773
37bec895 1774 return $upid;
b92400b6
DM
1775};
1776
1777__PACKAGE__->register_method ({
f9d26e09
TL
1778 name => 'stopall',
1779 path => 'stopall',
b92400b6
DM
1780 method => 'POST',
1781 protected => 1,
17e3b3b2
CS
1782 permissions => {
1783 check => ['perm', '/', [ 'VM.PowerMgmt' ]],
1784 },
9c8a09a7 1785 proxyto => 'node',
b92400b6
DM
1786 description => "Stop all VMs and Containers.",
1787 parameters => {
1788 additionalProperties => 0,
1789 properties => {
1790 node => get_standard_option('pve-node'),
2a498506
DC
1791 vms => {
1792 description => "Only consider Guests with these IDs.",
1793 type => 'string', format => 'pve-vmid-list',
1794 optional => 1,
1795 },
b92400b6
DM
1796 },
1797 },
1798 returns => {
1799 type => 'string',
1800 },
1801 code => sub {
1802 my ($param) = @_;
1803
1804 my $rpcenv = PVE::RPCEnvironment::get();
1805 my $authuser = $rpcenv->get_user();
1806
1807 my $nodename = $param->{node};
1808 $nodename = PVE::INotify::nodename() if $nodename eq 'localhost';
1809
1810 my $code = sub {
1811
1812 $rpcenv->{type} = 'priv'; # to start tasks in background
1813
2a498506 1814 my $stopList = &$get_start_stop_list($nodename, undef, $param->{vms});
b92400b6
DM
1815
1816 my $cpuinfo = PVE::ProcFSTools::read_cpuinfo();
89ceb802
TL
1817 my $datacenterconfig = cfs_read_file('datacenter.cfg');
1818 # if not set by user spawn max cpu count number of workers
1819 my $maxWorkers = $datacenterconfig->{max_workers} || $cpuinfo->{cpus};
b92400b6
DM
1820
1821 foreach my $order (sort {$b <=> $a} keys %$stopList) {
1822 my $vmlist = $stopList->{$order};
1823 my $workers = {};
37bec895
DM
1824
1825 my $finish_worker = sub {
1826 my $pid = shift;
1827 my $d = $workers->{$pid};
1828 return if !$d;
1829 delete $workers->{$pid};
1830
1831 syslog('info', "end task $d->{upid}");
1832 };
1833
b92400b6
DM
1834 foreach my $vmid (sort {$b <=> $a} keys %$vmlist) {
1835 my $d = $vmlist->{$vmid};
37bec895
DM
1836 my $upid;
1837 eval { $upid = &$create_stop_worker($nodename, $d->{type}, $vmid, $d->{down}); };
b92400b6 1838 warn $@ if $@;
37bec895
DM
1839 next if !$upid;
1840
1841 my $res = PVE::Tools::upid_decode($upid, 1);
1842 next if !$res;
1843
1844 my $pid = $res->{pid};
1845
1846 $workers->{$pid} = { type => $d->{type}, upid => $upid, vmid => $vmid };
b92400b6
DM
1847 while (scalar(keys %$workers) >= $maxWorkers) {
1848 foreach my $p (keys %$workers) {
1849 if (!PVE::ProcFSTools::check_process_running($p)) {
37bec895 1850 &$finish_worker($p);
b92400b6
DM
1851 }
1852 }
1853 sleep(1);
1854 }
1855 }
1856 while (scalar(keys %$workers)) {
1857 foreach my $p (keys %$workers) {
1858 if (!PVE::ProcFSTools::check_process_running($p)) {
37bec895 1859 &$finish_worker($p);
b92400b6
DM
1860 }
1861 }
1862 sleep(1);
1863 }
1864 }
37bec895
DM
1865
1866 syslog('info', "all VMs and CTs stopped");
1867
b92400b6
DM
1868 return;
1869 };
1870
1871 return $rpcenv->fork_worker('stopall', undef, $authuser, $code);
b92400b6
DM
1872 }});
1873
9c8a09a7
AD
1874my $create_migrate_worker = sub {
1875 my ($nodename, $type, $vmid, $target) = @_;
1876
1877 my $upid;
2c27e4b7
DM
1878 if ($type eq 'lxc') {
1879 my $online = PVE::LXC::check_running($vmid) ? 1 : 0;
1880 print STDERR "Migrating CT $vmid\n";
1881 $upid = PVE::API2::LXC->migrate_vm({node => $nodename, vmid => $vmid, target => $target,
e5045607 1882 restart => $online });
2c27e4b7 1883 } elsif ($type eq 'qemu') {
9c8a09a7
AD
1884 my $online = PVE::QemuServer::check_running($vmid, 1) ? 1 : 0;
1885 print STDERR "Migrating VM $vmid\n";
1886 $upid = PVE::API2::Qemu->migrate_vm({node => $nodename, vmid => $vmid, target => $target,
2c27e4b7 1887 online => $online });
9c8a09a7
AD
1888 } else {
1889 die "unknown VM type '$type'\n";
1890 }
1891
1892 my $res = PVE::Tools::upid_decode($upid);
1893
1894 return $res->{pid};
1895};
1896
1897__PACKAGE__->register_method ({
1898 name => 'migrateall',
1899 path => 'migrateall',
1900 method => 'POST',
1901 proxyto => 'node',
1902 protected => 1,
17e3b3b2
CS
1903 permissions => {
1904 check => ['perm', '/', [ 'VM.Migrate' ]],
1905 },
9c8a09a7
AD
1906 description => "Migrate all VMs and Containers.",
1907 parameters => {
1908 additionalProperties => 0,
1909 properties => {
1910 node => get_standard_option('pve-node'),
1911 target => get_standard_option('pve-node', { description => "Target node." }),
1912 maxworkers => {
89ceb802
TL
1913 description => "Maximal number of parallel migration job." .
1914 " If not set use 'max_workers' from datacenter.cfg," .
1915 " one of both must be set!",
1916 optional => 1,
9c8a09a7
AD
1917 type => 'integer',
1918 minimum => 1
1919 },
2a498506
DC
1920 vms => {
1921 description => "Only consider Guests with these IDs.",
1922 type => 'string', format => 'pve-vmid-list',
1923 optional => 1,
1924 },
9c8a09a7
AD
1925 },
1926 },
1927 returns => {
1928 type => 'string',
1929 },
1930 code => sub {
1931 my ($param) = @_;
1932
1933 my $rpcenv = PVE::RPCEnvironment::get();
1934 my $authuser = $rpcenv->get_user();
1935
1936 my $nodename = $param->{node};
1937 $nodename = PVE::INotify::nodename() if $nodename eq 'localhost';
1938
73981e39
DC
1939 my $target = $param->{target};
1940 raise_param_exc({ target => "target is local node."}) if $target eq $nodename;
1941
1942 PVE::Cluster::check_cfs_quorum();
1943
1944 PVE::Cluster::check_node_exists($target);
89ceb802
TL
1945
1946 my $datacenterconfig = cfs_read_file('datacenter.cfg');
1947 # prefer parameter over datacenter cfg settings
1948 my $maxWorkers = $param->{maxworkers} || $datacenterconfig->{max_workers} ||
1949 die "either 'maxworkers' parameter or max_workers in datacenter.cfg must be set!\n";
9c8a09a7
AD
1950
1951 my $code = sub {
9c8a09a7
AD
1952 $rpcenv->{type} = 'priv'; # to start tasks in background
1953
f5c1dde5 1954 my $vmlist = &$get_filtered_vmlist($nodename, $param->{vms}, 1, 1);
9c8a09a7 1955
f5c1dde5
TL
1956 my $workers = {};
1957 foreach my $vmid (sort keys %$vmlist) {
1958 my $d = $vmlist->{$vmid};
1959 my $pid;
1960 eval { $pid = &$create_migrate_worker($nodename, $d->{type}, $vmid, $target); };
1961 warn $@ if $@;
1962 next if !$pid;
9c8a09a7 1963
f5c1dde5
TL
1964 $workers->{$pid} = 1;
1965 while (scalar(keys %$workers) >= $maxWorkers) {
9c8a09a7
AD
1966 foreach my $p (keys %$workers) {
1967 if (!PVE::ProcFSTools::check_process_running($p)) {
1968 delete $workers->{$p};
1969 }
1970 }
1971 sleep(1);
1972 }
1973 }
f5c1dde5
TL
1974 while (scalar(keys %$workers)) {
1975 foreach my $p (keys %$workers) {
1976 if (!PVE::ProcFSTools::check_process_running($p)) {
1977 delete $workers->{$p};
1978 }
1979 }
1980 sleep(1);
1981 }
9c8a09a7
AD
1982 return;
1983 };
1984
1985 return $rpcenv->fork_worker('migrateall', undef, $authuser, $code);
1986
1987 }});
1988
820d0458
DC
1989__PACKAGE__->register_method ({
1990 name => 'get_etc_hosts',
1991 path => 'hosts',
1992 method => 'GET',
1993 proxyto => 'node',
1994 protected => 1,
1995 permissions => {
1996 check => ['perm', '/', [ 'Sys.Audit' ]],
1997 },
1998 description => "Get the content of /etc/hosts.",
1999 parameters => {
2000 additionalProperties => 0,
2001 properties => {
2002 node => get_standard_option('pve-node'),
2003 },
2004 },
2005 returns => {
2006 type => 'object',
2007 properties => {
2008 digest => get_standard_option('pve-config-digest'),
2009 data => {
2010 type => 'string',
2011 description => 'The content of /etc/hosts.'
2012 },
2013 },
2014 },
2015 code => sub {
2016 my ($param) = @_;
2017
2018 return PVE::INotify::read_file('etchosts');
2019
2020 }});
2021
2022__PACKAGE__->register_method ({
2023 name => 'write_etc_hosts',
2024 path => 'hosts',
2025 method => 'POST',
2026 proxyto => 'node',
2027 protected => 1,
2028 permissions => {
2029 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
2030 },
2031 description => "Write /etc/hosts.",
2032 parameters => {
2033 additionalProperties => 0,
2034 properties => {
2035 node => get_standard_option('pve-node'),
2036 digest => get_standard_option('pve-config-digest'),
2037 data => {
2038 type => 'string',
2039 description => 'The target content of /etc/hosts.'
2040 },
2041 },
2042 },
2043 returns => {
2044 type => 'null',
2045 },
2046 code => sub {
2047 my ($param) = @_;
2048
2049 PVE::Tools::lock_file('/var/lock/pve-etchosts.lck', undef, sub{
2050 if ($param->{digest}) {
2051 my $hosts = PVE::INotify::read_file('etchosts');
2052 PVE::Tools::assert_if_modified($hosts->{digest}, $param->{digest});
2053 }
2054 PVE::INotify::write_file('etchosts', $param->{data});
2055 });
2056 die $@ if $@;
2057
2058 return undef;
2059 }});
2060
82282acf
WL
2061# bash completion helper
2062
2063sub complete_templet_repo {
2064 my ($cmdname, $pname, $cvalue) = @_;
2065
2066 my $repo = PVE::APLInfo::load_data();
2067 my $res = [];
2068 foreach my $templ (keys %{$repo->{all}}) {
2069 next if $templ !~ m/^$cvalue/;
2070 push @$res, $templ;
2071 }
2072
2073 return $res;
2074}
2075
aff192e6
DM
2076package PVE::API2::Nodes;
2077
2078use strict;
2079use warnings;
2080
2081use PVE::SafeSyslog;
2082use PVE::Cluster;
2083use PVE::RESTHandler;
2084use PVE::RPCEnvironment;
b193e4ac 2085use PVE::API2Tools;
f57cbe5d 2086use PVE::JSONSchema qw(get_standard_option);
aff192e6
DM
2087
2088use base qw(PVE::RESTHandler);
2089
2090__PACKAGE__->register_method ({
f9d26e09 2091 subclass => "PVE::API2::Nodes::Nodeinfo",
aff192e6
DM
2092 path => '{node}',
2093});
2094
2095__PACKAGE__->register_method ({
f9d26e09
TL
2096 name => 'index',
2097 path => '',
aff192e6
DM
2098 method => 'GET',
2099 permissions => { user => 'all' },
2100 description => "Cluster node index.",
2101 parameters => {
2102 additionalProperties => 0,
2103 properties => {},
2104 },
2105 returns => {
2106 type => 'array',
2107 items => {
2108 type => "object",
f57cbe5d
DM
2109 properties => {
2110 node => get_standard_option('pve-node'),
2111 status => {
2112 description => "Node status.",
2113 type => 'string',
2114 enum => ['unknown', 'online', 'offline'],
2115 },
2116 cpu => {
2117 description => "CPU utilization.",
2118 type => 'number',
2119 optional => 1,
2120 renderer => 'fraction_as_percentage',
2121 },
2122 maxcpu => {
2123 description => "Number of available CPUs.",
2124 type => 'integer',
2125 optional => 1,
2126 },
2127 mem => {
2128 description => "Used memory in bytes.",
2129 type => 'string',
2130 optional => 1,
2131 renderer => 'bytes',
2132 },
2133 maxmem => {
2134 description => "Number of available memory in bytes.",
2135 type => 'integer',
2136 optional => 1,
2137 renderer => 'bytes',
2138 },
2139 level => {
2140 description => "Support level.",
2141 type => 'string',
2142 optional => 1,
2143 },
2144 uptime => {
2145 description => "Node uptime in seconds.",
2146 type => 'integer',
2147 optional => 1,
2148 renderer => 'duration',
2149 },
2150 ssl_fingerprint => {
2151 description => "The SSL fingerprint for the node certificate.",
2152 type => 'string',
2153 optional => 1,
2154 },
2155 },
aff192e6 2156 },
b193e4ac 2157 links => [ { rel => 'child', href => "{node}" } ],
aff192e6
DM
2158 },
2159 code => sub {
2160 my ($param) = @_;
f9d26e09 2161
57d56896
TL
2162 my $rpcenv = PVE::RPCEnvironment::get();
2163 my $authuser = $rpcenv->get_user();
2164
aff192e6
DM
2165 my $clinfo = PVE::Cluster::get_clinfo();
2166 my $res = [];
2167
b193e4ac
DM
2168 my $nodelist = PVE::Cluster::get_nodelist();
2169 my $members = PVE::Cluster::get_members();
aff192e6
DM
2170 my $rrd = PVE::Cluster::rrd_dump();
2171
b193e4ac 2172 foreach my $node (@$nodelist) {
57d56896
TL
2173 my $can_audit = $rpcenv->check($authuser, "/nodes/$node", [ 'Sys.Audit' ], 1);
2174 my $entry = PVE::API2Tools::extract_node_stats($node, $members, $rrd, !$can_audit);
16efab9e 2175 $entry->{ssl_fingerprint} = PVE::Cluster::get_node_fingerprint($node);
aff192e6
DM
2176 push @$res, $entry;
2177 }
2178
2179 return $res;
2180 }});
2181
21821;