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