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