]> git.proxmox.com Git - pmg-api.git/blame - src/PMG/API2/Nodes.pm
api: nodes: drop deprecated 'upgrade' option of termproxy
[pmg-api.git] / src / PMG / API2 / Nodes.pm
CommitLineData
1360e6f0
DM
1package PMG::API2::NodeInfo;
2
3use strict;
4use warnings;
d934c136 5use Time::Local qw(timegm_nocheck);
c1f5acda 6use Filesys::Df;
ba11e2d3 7use Data::Dumper;
d934c136 8
1360e6f0
DM
9use PVE::INotify;
10use PVE::RESTHandler;
11use PVE::JSONSchema qw(get_standard_option);
9d82c6bc 12use PMG::RESTEnvironment;
1360e6f0 13use PVE::SafeSyslog;
c1f5acda 14use PVE::ProcFSTools;
1360e6f0 15
c1f5acda 16use PMG::pmgcfg;
1360e6f0 17use PMG::Ticket;
3d8cdfee 18use PMG::Report;
87952a8d 19use PMG::API2::Subscription;
cc115a48 20use PMG::API2::APT;
fa04fcb4 21use PMG::API2::Tasks;
163fb58f 22use PMG::API2::Services;
2551682a 23use PMG::API2::Network;
d17c5265 24use PMG::API2::ClamAV;
b00d2d73 25use PMG::API2::SpamAssassin;
70805f7d 26use PMG::API2::Postfix;
202c952a 27use PMG::API2::MailTracker;
464f7494 28use PMG::API2::Backup;
a0208dbd 29use PMG::API2::PBS::Job;
ac78733c 30use PMG::API2::Certificates;
ec7ce3a4 31use PMG::API2::NodeConfig;
1360e6f0
DM
32
33use base qw(PVE::RESTHandler);
34
70805f7d
DM
35__PACKAGE__->register_method ({
36 subclass => "PMG::API2::Postfix",
37 path => 'postfix',
38});
39
d17c5265
DM
40__PACKAGE__->register_method ({
41 subclass => "PMG::API2::ClamAV",
42 path => 'clamav',
43});
44
b00d2d73
DC
45__PACKAGE__->register_method ({
46 subclass => "PMG::API2::SpamAssassin",
47 path => 'spamassassin',
48});
49
2551682a
DM
50__PACKAGE__->register_method ({
51 subclass => "PMG::API2::Network",
52 path => 'network',
53});
54
fa04fcb4
DM
55__PACKAGE__->register_method ({
56 subclass => "PMG::API2::Tasks",
57 path => 'tasks',
58});
59
163fb58f
DM
60__PACKAGE__->register_method ({
61 subclass => "PMG::API2::Services",
62 path => 'services',
63});
64
87952a8d
DM
65__PACKAGE__->register_method ({
66 subclass => "PMG::API2::Subscription",
67 path => 'subscription',
68});
69
cc115a48
DM
70__PACKAGE__->register_method ({
71 subclass => "PMG::API2::APT",
72 path => 'apt',
73});
74
202c952a
DM
75__PACKAGE__->register_method ({
76 subclass => "PMG::API2::MailTracker",
77 path => 'tracker',
78});
79
464f7494
DM
80__PACKAGE__->register_method ({
81 subclass => "PMG::API2::Backup",
82 path => 'backup',
83});
84
a0208dbd
SI
85__PACKAGE__->register_method ({
86 subclass => "PMG::API2::PBS::Job",
87 path => 'pbs',
88});
89
ac78733c
WB
90__PACKAGE__->register_method ({
91 subclass => "PMG::API2::Certificates",
92 path => 'certificates',
93});
94
ec7ce3a4
WB
95__PACKAGE__->register_method ({
96 subclass => "PMG::API2::NodeConfig",
97 path => 'config',
98});
99
1360e6f0
DM
100__PACKAGE__->register_method ({
101 name => 'index',
102 path => '',
103 method => 'GET',
104 permissions => { user => 'all' },
105 description => "Node index.",
106 parameters => {
107 additionalProperties => 0,
108 properties => {
109 node => get_standard_option('pve-node'),
110 },
111 },
112 returns => {
113 type => 'array',
114 items => {
115 type => "object",
116 properties => {},
117 },
118 links => [ { rel => 'child', href => "{name}" } ],
119 },
120 code => sub {
121 my ($param) = @_;
122
123 my $result = [
cc115a48 124 { name => 'apt' },
464f7494 125 { name => 'backup' },
a0208dbd 126 { name => 'pbs' },
d17c5265 127 { name => 'clamav' },
b00d2d73 128 { name => 'spamassassin' },
70805f7d 129 { name => 'postfix' },
163fb58f 130 { name => 'services' },
7e4d6e51 131 { name => 'syslog' },
8b4a5410 132 { name => 'journal' },
fa04fcb4 133 { name => 'tasks' },
202c952a 134 { name => 'tracker' },
d934c136 135 { name => 'time' },
3d8cdfee 136 { name => 'report' },
c1f5acda 137 { name => 'status' },
87952a8d 138 { name => 'subscription' },
6466cf6d 139 { name => 'termproxy' },
229e2e68 140 { name => 'rrddata' },
ac78733c 141 { name => 'certificates' },
ec7ce3a4 142 { name => 'config' },
1360e6f0
DM
143 ];
144
145 return $result;
146 }});
147
3d8cdfee
DC
148__PACKAGE__->register_method({
149 name => 'report',
150 path => 'report',
151 method => 'GET',
152 protected => 1,
153 proxyto => 'node',
154 permissions => { check => [ 'admin', 'audit' ] },
155 description => "Gather various system information about a node",
156 parameters => {
157 additionalProperties => 0,
158 properties => {
159 node => get_standard_option('pve-node'),
160 },
161 },
162 returns => {
163 type => 'string',
164 },
165 code => sub {
166 return PMG::Report::generate();
167 }});
168
065b2986
DM
169__PACKAGE__->register_method({
170 name => 'rrddata',
171 path => 'rrddata',
172 method => 'GET',
173 protected => 1, # fixme: can we avoid that?
174 proxyto => 'node',
127154c3 175 permissions => { check => [ 'admin', 'audit' ] },
065b2986
DM
176 description => "Read node RRD statistics",
177 parameters => {
178 additionalProperties => 0,
179 properties => {
180 node => get_standard_option('pve-node'),
181 timeframe => {
182 description => "Specify the time frame you are interested in.",
183 type => 'string',
184 enum => [ 'hour', 'day', 'week', 'month', 'year' ],
185 },
186 cf => {
187 description => "The RRD consolidation function",
188 type => 'string',
189 enum => [ 'AVERAGE', 'MAX' ],
190 optional => 1,
191 },
192 },
193 },
194 returns => {
195 type => "array",
196 items => {
197 type => "object",
198 properties => {},
199 },
200 },
201 code => sub {
202 my ($param) = @_;
203
204 return PMG::Utils::create_rrd_data(
205 "pmg-node-v1.rrd", $param->{timeframe}, $param->{cf});
206 }});
207
208
7e4d6e51
DM
209__PACKAGE__->register_method({
210 name => 'syslog',
211 path => 'syslog',
212 method => 'GET',
213 description => "Read system log",
214 proxyto => 'node',
215 protected => 1,
da5777d7 216 permissions => { check => [ 'admin', 'audit' ] },
7e4d6e51
DM
217 parameters => {
218 additionalProperties => 0,
219 properties => {
220 node => get_standard_option('pve-node'),
221 start => {
222 type => 'integer',
223 minimum => 0,
224 optional => 1,
225 },
226 limit => {
227 type => 'integer',
228 minimum => 0,
229 optional => 1,
230 },
231 since => {
cf49e998 232 type => 'string',
7e4d6e51
DM
233 pattern => '^\d{4}-\d{2}-\d{2}( \d{2}:\d{2}(:\d{2})?)?$',
234 description => "Display all log since this date-time string.",
235 optional => 1,
236 },
237 'until' => {
cf49e998 238 type => 'string',
7e4d6e51
DM
239 pattern => '^\d{4}-\d{2}-\d{2}( \d{2}:\d{2}(:\d{2})?)?$',
240 description => "Display all log until this date-time string.",
241 optional => 1,
242 },
cf49e998
DM
243 service => {
244 description => "Service ID",
245 type => 'string',
246 maxLength => 128,
247 optional => 1,
248 },
7e4d6e51
DM
249 },
250 },
251 returns => {
252 type => 'array',
253 items => {
254 type => "object",
255 properties => {
256 n => {
257 description=> "Line number",
258 type=> 'integer',
259 },
260 t => {
261 description=> "Line text",
262 type => 'string',
263 }
264 }
265 }
266 },
267 code => sub {
268 my ($param) = @_;
269
f1c29260 270 my $restenv = PMG::RESTEnvironment->get();
7e4d6e51 271
cf49e998 272 my $service = $param->{service};
ef07e4d0
DM
273 $service = PMG::Utils::lookup_real_service_name($service)
274 if $service;
cf49e998
DM
275
276 my ($count, $lines) = PVE::Tools::dump_journal(
277 $param->{start}, $param->{limit},
278 $param->{since}, $param->{'until'}, $service);
7e4d6e51
DM
279
280 $restenv->set_result_attrib('total', $count);
281
282 return $lines;
283 }});
284
c2e3529a
DC
285__PACKAGE__->register_method({
286 name => 'journal',
287 path => 'journal',
288 method => 'GET',
289 description => "Read Journal",
290 proxyto => 'node',
291 permissions => { check => [ 'admin', 'audit' ] },
292 protected => 1,
293 parameters => {
294 additionalProperties => 0,
295 properties => {
296 node => get_standard_option('pve-node'),
297 since => {
13232555
TL
298 description => "Display all log since this UNIX epoch. Conflicts with 'startcursor'.",
299 type => 'integer',
300 minimum => 0,
c2e3529a
DC
301 optional => 1,
302 },
303 until => {
13232555
TL
304 description => "Display all log until this UNIX epoch. Conflicts with 'endcursor'.",
305 type => 'integer',
306 minimum => 0,
c2e3529a
DC
307 optional => 1,
308 },
309 lastentries => {
13232555 310 description => "Limit to the last X lines. Conflicts with a range.",
c2e3529a 311 type => 'integer',
13232555 312 minimum => 0,
c2e3529a
DC
313 optional => 1,
314 },
315 startcursor => {
13232555 316 description => "Start after the given Cursor. Conflicts with 'since'.",
c2e3529a
DC
317 type => 'string',
318 optional => 1,
319 },
320 endcursor => {
13232555 321 description => "End before the given Cursor. Conflicts with 'until'.",
c2e3529a
DC
322 type => 'string',
323 optional => 1,
324 },
325 },
326 },
327 returns => {
328 type => 'array',
329 items => {
330 type => "string",
331 }
332 },
333 code => sub {
334 my ($param) = @_;
335
c2e3529a
DC
336 my $cmd = ["/usr/bin/mini-journalreader"];
337 push @$cmd, '-n', $param->{lastentries} if $param->{lastentries};
338 push @$cmd, '-b', $param->{since} if $param->{since};
339 push @$cmd, '-e', $param->{until} if $param->{until};
340 push @$cmd, '-f', $param->{startcursor} if $param->{startcursor};
341 push @$cmd, '-t', $param->{endcursor} if $param->{endcursor};
342
80b9242d
TL
343 my $lines = [];
344 my $parser = sub { push @$lines, shift };
345
c2e3529a
DC
346 PVE::Tools::run_command($cmd, outfunc => $parser);
347
348 return $lines;
349 }});
350
d9e79ff4
TL
351my $shell_cmd_map = {
352 'login' => {
353 cmd => [ '/bin/login', '-f', 'root' ],
354 },
355 'upgrade' => {
356 cmd => [ 'pmgupgrade', '--shell' ],
357 },
358};
359
360sub get_shell_command {
361 my ($user, $shellcmd, $args) = @_;
362
363 my $cmd;
364 if ($user eq 'root@pam') {
365 if (defined($shellcmd) && exists($shell_cmd_map->{$shellcmd})) {
366 my $def = $shell_cmd_map->{$shellcmd};
367 $cmd = [ @{$def->{cmd}} ]; # clone
368 if (defined($args) && $def->{allow_args}) {
369 push @$cmd, split("\0", $args);
370 }
371 } else {
372 $cmd = [ '/bin/login', '-f', 'root' ];
373 }
374 } else {
375 # non-root must always login for now, we do not have a superuser role!
376 $cmd = [ '/bin/login' ];
377 }
378 return $cmd;
379}
6466cf6d 380
1360e6f0 381__PACKAGE__->register_method ({
6466cf6d
DC
382 name => 'termproxy',
383 path => 'termproxy',
1360e6f0 384 method => 'POST',
aafcc40e 385 permissions => { check => [ 'admin' ] },
fa04fcb4 386 protected => 1,
6466cf6d 387 description => "Creates a Terminal proxy.",
1360e6f0
DM
388 parameters => {
389 additionalProperties => 0,
390 properties => {
391 node => get_standard_option('pve-node'),
d9e79ff4
TL
392 cmd => {
393 type => 'string',
394 description => "Run specific command or default to login.",
395 enum => [keys %$shell_cmd_map],
396 optional => 1,
397 default => 'login',
398 },
399 'cmd-opts' => {
400 type => 'string',
401 description => "Add parameters to a command. Encoded as null terminated strings.",
402 requires => 'cmd',
403 optional => 1,
404 default => '',
405 },
1360e6f0
DM
406 },
407 },
408 returns => {
409 additionalProperties => 0,
410 properties => {
411 user => { type => 'string' },
412 ticket => { type => 'string' },
413 port => { type => 'integer' },
414 upid => { type => 'string' },
415 },
416 },
417 code => sub {
418 my ($param) = @_;
419
420 my $node = $param->{node};
421
5e6e07df 422 if ($node ne PVE::INotify::nodename()) {
6466cf6d 423 die "termproxy to remote node not implemented";
5e6e07df
DM
424 }
425
1360e6f0
DM
426 my $authpath = "/nodes/$node";
427
9d82c6bc 428 my $restenv = PMG::RESTEnvironment->get();
1360e6f0
DM
429 my $user = $restenv->get_user();
430
81e68f01 431 raise_perm_exc('user != root@pam') if $param->{cmd} eq 'upgrade' && $user ne 'root@pam';
5e6e07df 432
1360e6f0
DM
433 my $ticket = PMG::Ticket::assemble_vnc_ticket($user, $authpath);
434
435 my $family = PVE::Tools::get_host_address_family($node);
436 my $port = PVE::Tools::next_vnc_port($family);
437
d9e79ff4 438 my $shcmd = get_shell_command($user, $param->{cmd}, $param->{'cmd-opts'});
8e6893c6 439
d9e79ff4 440 my $cmd = ['/usr/bin/termproxy', $port, '--path', $authpath, '--', @$shcmd];
1360e6f0
DM
441
442 my $realcmd = sub {
443 my $upid = shift;
444
6466cf6d 445 syslog ('info', "starting termproxy $upid\n");
1360e6f0
DM
446
447 my $cmdstr = join (' ', @$cmd);
448 syslog ('info', "launch command: $cmdstr");
449
6466cf6d 450 PVE::Tools::run_command($cmd);
1360e6f0
DM
451
452 return;
453 };
454
6466cf6d 455 my $upid = $restenv->fork_worker('termproxy', "", $user, $realcmd);
1360e6f0
DM
456
457 PVE::Tools::wait_for_vnc_port($port);
458
459 return {
460 user => $user,
461 ticket => $ticket,
462 port => $port,
463 upid => $upid,
464 };
465 }});
466
467__PACKAGE__->register_method({
468 name => 'vncwebsocket',
469 path => 'vncwebsocket',
470 method => 'GET',
aafcc40e 471 permissions => { check => [ 'admin' ] },
1360e6f0
DM
472 description => "Opens a weksocket for VNC traffic.",
473 parameters => {
474 additionalProperties => 0,
475 properties => {
476 node => get_standard_option('pve-node'),
477 vncticket => {
478 description => "Ticket from previous call to vncproxy.",
479 type => 'string',
480 maxLength => 512,
481 },
482 port => {
483 description => "Port number returned by previous vncproxy call.",
484 type => 'integer',
485 minimum => 5900,
486 maximum => 5999,
487 },
488 },
489 },
490 returns => {
491 type => "object",
492 properties => {
493 port => { type => 'string' },
494 },
495 },
496 code => sub {
497 my ($param) = @_;
498
499 my $authpath = "/nodes/$param->{node}";
500
9d82c6bc 501 my $restenv = PMG::RESTEnvironment->get();
1360e6f0
DM
502 my $user = $restenv->get_user();
503
504 PMG::Ticket::verify_vnc_ticket($param->{vncticket}, $user, $authpath);
505
506 my $port = $param->{port};
507
508 return { port => $port };
509 }});
510
bce9f371
DM
511__PACKAGE__->register_method({
512 name => 'dns',
513 path => 'dns',
514 method => 'GET',
515 description => "Read DNS settings.",
516 proxyto => 'node',
aea3488a 517 permissions => { check => [ 'admin', 'audit' ] },
bce9f371
DM
518 parameters => {
519 additionalProperties => 0,
520 properties => {
521 node => get_standard_option('pve-node'),
522 },
523 },
524 returns => {
525 type => "object",
526 additionalProperties => 0,
527 properties => {
528 search => {
529 description => "Search domain for host-name lookup.",
530 type => 'string',
531 optional => 1,
532 },
533 dns1 => {
534 description => 'First name server IP address.',
535 type => 'string',
536 optional => 1,
537 },
538 dns2 => {
539 description => 'Second name server IP address.',
540 type => 'string',
541 optional => 1,
542 },
543 dns3 => {
544 description => 'Third name server IP address.',
545 type => 'string',
546 optional => 1,
547 },
548 },
549 },
550 code => sub {
551 my ($param) = @_;
552
553 my $res = PVE::INotify::read_file('resolvconf');
554
555 return $res;
556 }});
557
558__PACKAGE__->register_method({
559 name => 'update_dns',
560 path => 'dns',
561 method => 'PUT',
562 description => "Write DNS settings.",
563 proxyto => 'node',
564 protected => 1,
565 parameters => {
566 additionalProperties => 0,
567 properties => {
568 node => get_standard_option('pve-node'),
569 search => {
570 description => "Search domain for host-name lookup.",
571 type => 'string',
572 },
573 dns1 => {
574 description => 'First name server IP address.',
575 type => 'string', format => 'ip',
576 optional => 1,
577 },
578 dns2 => {
579 description => 'Second name server IP address.',
580 type => 'string', format => 'ip',
581 optional => 1,
582 },
583 dns3 => {
584 description => 'Third name server IP address.',
585 type => 'string', format => 'ip',
586 optional => 1,
587 },
588 },
589 },
590 returns => { type => "null" },
591 code => sub {
592 my ($param) = @_;
593
594 PVE::INotify::update_file('resolvconf', $param);
595
596 return undef;
597 }});
598
599
d934c136
DM
600__PACKAGE__->register_method({
601 name => 'time',
602 path => 'time',
603 method => 'GET',
604 description => "Read server time and time zone settings.",
605 proxyto => 'node',
aea3488a 606 permissions => { check => [ 'admin', 'audit' ] },
d934c136
DM
607 parameters => {
608 additionalProperties => 0,
609 properties => {
610 node => get_standard_option('pve-node'),
611 },
612 },
613 returns => {
614 type => "object",
615 additionalProperties => 0,
616 properties => {
617 timezone => {
618 description => "Time zone",
619 type => 'string',
620 },
621 time => {
622 description => "Seconds since 1970-01-01 00:00:00 UTC.",
623 type => 'integer',
624 minimum => 1297163644,
625 },
626 localtime => {
627 description => "Seconds since 1970-01-01 00:00:00 (local time)",
628 type => 'integer',
629 minimum => 1297163644,
630 },
631 },
632 },
633 code => sub {
634 my ($param) = @_;
635
636 my $ctime = time();
637 my $ltime = timegm_nocheck(localtime($ctime));
638 my $res = {
639 timezone => PVE::INotify::read_file('timezone'),
640 time => time(),
641 localtime => $ltime,
642 };
643
644 return $res;
645 }});
646
647__PACKAGE__->register_method({
648 name => 'set_timezone',
649 path => 'time',
650 method => 'PUT',
651 description => "Set time zone.",
652 proxyto => 'node',
653 protected => 1,
654 parameters => {
655 additionalProperties => 0,
656 properties => {
657 node => get_standard_option('pve-node'),
658 timezone => {
659 description => "Time zone. The file '/usr/share/zoneinfo/zone.tab' contains the list of valid names.",
660 type => 'string',
661 },
662 },
663 },
664 returns => { type => "null" },
665 code => sub {
666 my ($param) = @_;
667
668 PVE::INotify::write_file('timezone', $param->{timezone});
669
670 return undef;
671 }});
672
c1f5acda
DM
673__PACKAGE__->register_method({
674 name => 'status',
675 path => 'status',
676 method => 'GET',
677 description => "Read server status. This is used by the cluster manager to test the node health.",
678 proxyto => 'node',
e4842eec 679 permissions => { check => [ 'admin', 'qmanager', 'audit' ] },
c1f5acda
DM
680 protected => 1,
681 parameters => {
682 additionalProperties => 0,
683 properties => {
684 node => get_standard_option('pve-node'),
685 },
686 },
687 returns => {
688 type => "object",
689 additionalProperties => 1,
690 properties => {
691 time => {
692 description => "Seconds since 1970-01-01 00:00:00 UTC.",
693 type => 'integer',
694 minimum => 1297163644,
695 },
696 uptime => {
697 description => "The uptime of the system in seconds.",
698 type => 'integer',
699 minimum => 0,
700 },
701 insync => {
702 description => "Database is synced with other nodes.",
703 type => 'boolean',
704 },
705 },
706 },
707 code => sub {
708 my ($param) = @_;
709
710 my $restenv = PMG::RESTEnvironment->get();
711 my $cinfo = $restenv->{cinfo};
712
713 my $ctime = time();
714
715 my $res = { time => $ctime, insync => 1 };
716
717 my $si = PMG::DBTools::cluster_sync_status($cinfo);
718 foreach my $cid (keys %$si) {
719 my $lastsync = $si->{$cid};
720 my $sdiff = $ctime - $lastsync;
721 $sdiff = 0 if $sdiff < 0;
722 $res->{insync} = 0 if $sdiff > (60*3);
723 }
724
725 my ($uptime, $idle) = PVE::ProcFSTools::read_proc_uptime();
726 $res->{uptime} = $uptime;
727
728 my ($avg1, $avg5, $avg15) = PVE::ProcFSTools::read_loadavg();
729 $res->{loadavg} = [ $avg1, $avg5, $avg15];
730
731 my ($sysname, $nodename, $release, $version, $machine) = POSIX::uname();
732
733 $res->{kversion} = "$sysname $release $version";
734
735 $res->{cpuinfo} = PVE::ProcFSTools::read_cpuinfo();
736
737 my $stat = PVE::ProcFSTools::read_proc_stat();
738 $res->{cpu} = $stat->{cpu};
739 $res->{wait} = $stat->{wait};
740
741 my $meminfo = PVE::ProcFSTools::read_meminfo();
742 $res->{memory} = {
743 free => $meminfo->{memfree},
744 total => $meminfo->{memtotal},
745 used => $meminfo->{memused},
746 };
747
748 $res->{swap} = {
749 free => $meminfo->{swapfree},
750 total => $meminfo->{swaptotal},
751 used => $meminfo->{swapused},
752 };
753
754 $res->{pmgversion} = PMG::pmgcfg::package() . "/" .
755 PMG::pmgcfg::version_text();
756
757 my $dinfo = df('/', 1); # output is bytes
758
759 $res->{rootfs} = {
760 total => $dinfo->{blocks},
761 avail => $dinfo->{bavail},
762 used => $dinfo->{used},
d9f4add4 763 free => $dinfo->{blocks} - $dinfo->{used},
c1f5acda
DM
764 };
765
b4df3454
DM
766 if (my $subinfo = PVE::INotify::read_file('subscription')) {
767 if (my $level = $subinfo->{level}) {
768 $res->{level} = $level;
769 }
770 }
771
c1f5acda
DM
772 return $res;
773 }});
774
0a327ff4
DM
775__PACKAGE__->register_method({
776 name => 'node_cmd',
777 path => 'status',
778 method => 'POST',
aafcc40e 779 permissions => { check => [ 'admin' ] },
0a327ff4
DM
780 protected => 1,
781 description => "Reboot or shutdown a node.",
782 proxyto => 'node',
783 parameters => {
784 additionalProperties => 0,
785 properties => {
786 node => get_standard_option('pve-node'),
787 command => {
788 description => "Specify the command.",
789 type => 'string',
790 enum => [qw(reboot shutdown)],
791 },
792 },
793 },
794 returns => { type => "null" },
795 code => sub {
796 my ($param) = @_;
797
798 if ($param->{command} eq 'reboot') {
799 system ("(sleep 2;/sbin/reboot)&");
800 } elsif ($param->{command} eq 'shutdown') {
801 system ("(sleep 2;/sbin/poweroff)&");
802 }
803
804 return undef;
805 }});
1360e6f0
DM
806
807package PMG::API2::Nodes;
808
809use strict;
810use warnings;
811
812use PVE::RESTHandler;
813use PVE::JSONSchema qw(get_standard_option);
814
be512f56
DM
815use PMG::RESTEnvironment;
816
1360e6f0
DM
817use base qw(PVE::RESTHandler);
818
819__PACKAGE__->register_method ({
f6dcba61 820 subclass => "PMG::API2::NodeInfo",
1360e6f0
DM
821 path => '{node}',
822});
823
824__PACKAGE__->register_method ({
825 name => 'index',
826 path => '',
827 method => 'GET',
828 permissions => { user => 'all' },
829 description => "Cluster node index.",
830 parameters => {
831 additionalProperties => 0,
832 properties => {},
833 },
834 returns => {
835 type => 'array',
836 items => {
837 type => "object",
838 properties => {},
839 },
840 links => [ { rel => 'child', href => "{node}" } ],
841 },
842 code => sub {
843 my ($param) = @_;
844
845 my $nodename = PVE::INotify::nodename();
be512f56
DM
846
847 my $res = [ { node => $nodename } ];
848
849 my $done = {};
850
851 $done->{$nodename} = 1;
852
853 my $restenv = PMG::RESTEnvironment->get();
854 my $cinfo = $restenv->{cinfo};
855
856 foreach my $ni (values %{$cinfo->{ids}}) {
857 push @$res, { node => $ni->{name} } if !$done->{$ni->{name}};
858 $done->{$ni->{name}} = 1;
859 }
1360e6f0
DM
860
861 return $res;
862 }});
863
864
8651;