]> git.proxmox.com Git - pmg-api.git/blob - PMG/API2/Nodes.pm
PMG/Postfix.pm: implement mailq (but we use postqueue directly)
[pmg-api.git] / PMG / API2 / Nodes.pm
1 package PMG::API2::NodeInfo;
2
3 use strict;
4 use warnings;
5
6 use Time::Local qw(timegm_nocheck);
7
8 use PVE::INotify;
9 use PVE::RESTHandler;
10 use PVE::JSONSchema qw(get_standard_option);
11 use PVE::RESTEnvironment;
12 use PVE::SafeSyslog;
13
14 use PMG::Ticket;
15 use PMG::API2::Tasks;
16 use PMG::API2::Services;
17 use PMG::API2::Network;
18 use PMG::API2::ClamAV;
19 use PMG::API2::Postfix;
20
21 use base qw(PVE::RESTHandler);
22
23 __PACKAGE__->register_method ({
24 subclass => "PMG::API2::Postfix",
25 path => 'postfix',
26 });
27
28 __PACKAGE__->register_method ({
29 subclass => "PMG::API2::ClamAV",
30 path => 'clamav',
31 });
32
33 __PACKAGE__->register_method ({
34 subclass => "PMG::API2::Network",
35 path => 'network',
36 });
37
38 __PACKAGE__->register_method ({
39 subclass => "PMG::API2::Tasks",
40 path => 'tasks',
41 });
42
43 __PACKAGE__->register_method ({
44 subclass => "PMG::API2::Services",
45 path => 'services',
46 });
47
48 __PACKAGE__->register_method ({
49 name => 'index',
50 path => '',
51 method => 'GET',
52 permissions => { user => 'all' },
53 description => "Node index.",
54 parameters => {
55 additionalProperties => 0,
56 properties => {
57 node => get_standard_option('pve-node'),
58 },
59 },
60 returns => {
61 type => 'array',
62 items => {
63 type => "object",
64 properties => {},
65 },
66 links => [ { rel => 'child', href => "{name}" } ],
67 },
68 code => sub {
69 my ($param) = @_;
70
71 my $result = [
72 { name => 'clamav' },
73 { name => 'postfix' },
74 { name => 'services' },
75 { name => 'syslog' },
76 { name => 'tasks' },
77 { name => 'time' },
78 { name => 'vncshell' },
79 { name => 'rrddata' },
80 ];
81
82 return $result;
83 }});
84
85 __PACKAGE__->register_method({
86 name => 'rrddata',
87 path => 'rrddata',
88 method => 'GET',
89 protected => 1, # fixme: can we avoid that?
90 proxyto => 'node',
91 description => "Read node RRD statistics",
92 parameters => {
93 additionalProperties => 0,
94 properties => {
95 node => get_standard_option('pve-node'),
96 timeframe => {
97 description => "Specify the time frame you are interested in.",
98 type => 'string',
99 enum => [ 'hour', 'day', 'week', 'month', 'year' ],
100 },
101 cf => {
102 description => "The RRD consolidation function",
103 type => 'string',
104 enum => [ 'AVERAGE', 'MAX' ],
105 optional => 1,
106 },
107 },
108 },
109 returns => {
110 type => "array",
111 items => {
112 type => "object",
113 properties => {},
114 },
115 },
116 code => sub {
117 my ($param) = @_;
118
119 return PMG::Utils::create_rrd_data(
120 "pmg-node-v1.rrd", $param->{timeframe}, $param->{cf});
121 }});
122
123
124 __PACKAGE__->register_method({
125 name => 'syslog',
126 path => 'syslog',
127 method => 'GET',
128 description => "Read system log",
129 proxyto => 'node',
130 protected => 1,
131 parameters => {
132 additionalProperties => 0,
133 properties => {
134 node => get_standard_option('pve-node'),
135 start => {
136 type => 'integer',
137 minimum => 0,
138 optional => 1,
139 },
140 limit => {
141 type => 'integer',
142 minimum => 0,
143 optional => 1,
144 },
145 since => {
146 type=> 'string',
147 pattern => '^\d{4}-\d{2}-\d{2}( \d{2}:\d{2}(:\d{2})?)?$',
148 description => "Display all log since this date-time string.",
149 optional => 1,
150 },
151 'until' => {
152 type=> 'string',
153 pattern => '^\d{4}-\d{2}-\d{2}( \d{2}:\d{2}(:\d{2})?)?$',
154 description => "Display all log until this date-time string.",
155 optional => 1,
156 },
157 },
158 },
159 returns => {
160 type => 'array',
161 items => {
162 type => "object",
163 properties => {
164 n => {
165 description=> "Line number",
166 type=> 'integer',
167 },
168 t => {
169 description=> "Line text",
170 type => 'string',
171 }
172 }
173 }
174 },
175 code => sub {
176 my ($param) = @_;
177
178 my $restenv = PVE::RESTEnvironment::get();
179
180 my ($count, $lines) = PVE::Tools::dump_journal($param->{start}, $param->{limit},
181 $param->{since}, $param->{'until'});
182
183 $restenv->set_result_attrib('total', $count);
184
185 return $lines;
186 }});
187
188 __PACKAGE__->register_method ({
189 name => 'vncshell',
190 path => 'vncshell',
191 method => 'POST',
192 protected => 1,
193 description => "Creates a VNC Shell proxy.",
194 parameters => {
195 additionalProperties => 0,
196 properties => {
197 node => get_standard_option('pve-node'),
198 websocket => {
199 optional => 1,
200 type => 'boolean',
201 description => "use websocket instead of standard vnc.",
202 default => 1,
203 },
204 },
205 },
206 returns => {
207 additionalProperties => 0,
208 properties => {
209 user => { type => 'string' },
210 ticket => { type => 'string' },
211 port => { type => 'integer' },
212 upid => { type => 'string' },
213 },
214 },
215 code => sub {
216 my ($param) = @_;
217
218 my $node = $param->{node};
219
220 # we only implement the websocket based VNC here
221 my $websocket = $param->{websocket} // 1;
222 die "standard VNC not implemented" if !$websocket;
223
224 my $authpath = "/nodes/$node";
225
226 my $restenv = PVE::RESTEnvironment->get();
227 my $user = $restenv->get_user();
228
229 my $ticket = PMG::Ticket::assemble_vnc_ticket($user, $authpath);
230
231 my $family = PVE::Tools::get_host_address_family($node);
232 my $port = PVE::Tools::next_vnc_port($family);
233
234 my $shcmd;
235
236 if ($user eq 'root@pam') {
237 $shcmd = [ '/bin/login', '-f', 'root' ];
238 } else {
239 $shcmd = [ '/bin/login' ];
240 }
241
242 my $cmd = ['/usr/bin/vncterm', '-rfbport', $port,
243 '-timeout', 10, '-notls', '-listen', 'localhost',
244 '-c', @$shcmd];
245
246 my $realcmd = sub {
247 my $upid = shift;
248
249 syslog ('info', "starting vnc proxy $upid\n");
250
251 my $cmdstr = join (' ', @$cmd);
252 syslog ('info', "launch command: $cmdstr");
253
254 eval {
255 foreach my $k (keys %ENV) {
256 next if $k eq 'PATH' || $k eq 'TERM' || $k eq 'USER' || $k eq 'HOME';
257 delete $ENV{$k};
258 }
259 $ENV{PWD} = '/';
260
261 $ENV{PVE_VNC_TICKET} = $ticket; # pass ticket to vncterm
262
263 PVE::Tools::run_command($cmd, errmsg => "vncterm failed");
264 };
265 if (my $err = $@) {
266 syslog('err', $err);
267 }
268
269 return;
270 };
271
272 my $upid = $restenv->fork_worker('vncshell', "", $user, $realcmd);
273
274 PVE::Tools::wait_for_vnc_port($port);
275
276 return {
277 user => $user,
278 ticket => $ticket,
279 port => $port,
280 upid => $upid,
281 };
282 }});
283
284 __PACKAGE__->register_method({
285 name => 'vncwebsocket',
286 path => 'vncwebsocket',
287 method => 'GET',
288 description => "Opens a weksocket for VNC traffic.",
289 parameters => {
290 additionalProperties => 0,
291 properties => {
292 node => get_standard_option('pve-node'),
293 vncticket => {
294 description => "Ticket from previous call to vncproxy.",
295 type => 'string',
296 maxLength => 512,
297 },
298 port => {
299 description => "Port number returned by previous vncproxy call.",
300 type => 'integer',
301 minimum => 5900,
302 maximum => 5999,
303 },
304 },
305 },
306 returns => {
307 type => "object",
308 properties => {
309 port => { type => 'string' },
310 },
311 },
312 code => sub {
313 my ($param) = @_;
314
315 my $authpath = "/nodes/$param->{node}";
316
317 my $restenv = PVE::RESTEnvironment->get();
318 my $user = $restenv->get_user();
319
320 PMG::Ticket::verify_vnc_ticket($param->{vncticket}, $user, $authpath);
321
322 my $port = $param->{port};
323
324 return { port => $port };
325 }});
326
327 __PACKAGE__->register_method({
328 name => 'dns',
329 path => 'dns',
330 method => 'GET',
331 description => "Read DNS settings.",
332 proxyto => 'node',
333 parameters => {
334 additionalProperties => 0,
335 properties => {
336 node => get_standard_option('pve-node'),
337 },
338 },
339 returns => {
340 type => "object",
341 additionalProperties => 0,
342 properties => {
343 search => {
344 description => "Search domain for host-name lookup.",
345 type => 'string',
346 optional => 1,
347 },
348 dns1 => {
349 description => 'First name server IP address.',
350 type => 'string',
351 optional => 1,
352 },
353 dns2 => {
354 description => 'Second name server IP address.',
355 type => 'string',
356 optional => 1,
357 },
358 dns3 => {
359 description => 'Third name server IP address.',
360 type => 'string',
361 optional => 1,
362 },
363 },
364 },
365 code => sub {
366 my ($param) = @_;
367
368 my $res = PVE::INotify::read_file('resolvconf');
369
370 return $res;
371 }});
372
373 __PACKAGE__->register_method({
374 name => 'update_dns',
375 path => 'dns',
376 method => 'PUT',
377 description => "Write DNS settings.",
378 proxyto => 'node',
379 protected => 1,
380 parameters => {
381 additionalProperties => 0,
382 properties => {
383 node => get_standard_option('pve-node'),
384 search => {
385 description => "Search domain for host-name lookup.",
386 type => 'string',
387 },
388 dns1 => {
389 description => 'First name server IP address.',
390 type => 'string', format => 'ip',
391 optional => 1,
392 },
393 dns2 => {
394 description => 'Second name server IP address.',
395 type => 'string', format => 'ip',
396 optional => 1,
397 },
398 dns3 => {
399 description => 'Third name server IP address.',
400 type => 'string', format => 'ip',
401 optional => 1,
402 },
403 },
404 },
405 returns => { type => "null" },
406 code => sub {
407 my ($param) = @_;
408
409 PVE::INotify::update_file('resolvconf', $param);
410
411 return undef;
412 }});
413
414
415 __PACKAGE__->register_method({
416 name => 'time',
417 path => 'time',
418 method => 'GET',
419 description => "Read server time and time zone settings.",
420 proxyto => 'node',
421 parameters => {
422 additionalProperties => 0,
423 properties => {
424 node => get_standard_option('pve-node'),
425 },
426 },
427 returns => {
428 type => "object",
429 additionalProperties => 0,
430 properties => {
431 timezone => {
432 description => "Time zone",
433 type => 'string',
434 },
435 time => {
436 description => "Seconds since 1970-01-01 00:00:00 UTC.",
437 type => 'integer',
438 minimum => 1297163644,
439 },
440 localtime => {
441 description => "Seconds since 1970-01-01 00:00:00 (local time)",
442 type => 'integer',
443 minimum => 1297163644,
444 },
445 },
446 },
447 code => sub {
448 my ($param) = @_;
449
450 my $ctime = time();
451 my $ltime = timegm_nocheck(localtime($ctime));
452 my $res = {
453 timezone => PVE::INotify::read_file('timezone'),
454 time => time(),
455 localtime => $ltime,
456 };
457
458 return $res;
459 }});
460
461 __PACKAGE__->register_method({
462 name => 'set_timezone',
463 path => 'time',
464 method => 'PUT',
465 description => "Set time zone.",
466 proxyto => 'node',
467 protected => 1,
468 parameters => {
469 additionalProperties => 0,
470 properties => {
471 node => get_standard_option('pve-node'),
472 timezone => {
473 description => "Time zone. The file '/usr/share/zoneinfo/zone.tab' contains the list of valid names.",
474 type => 'string',
475 },
476 },
477 },
478 returns => { type => "null" },
479 code => sub {
480 my ($param) = @_;
481
482 PVE::INotify::write_file('timezone', $param->{timezone});
483
484 return undef;
485 }});
486
487
488 package PMG::API2::Nodes;
489
490 use strict;
491 use warnings;
492
493 use PVE::RESTHandler;
494 use PVE::JSONSchema qw(get_standard_option);
495
496 use base qw(PVE::RESTHandler);
497
498 __PACKAGE__->register_method ({
499 subclass => "PMG::API2::NodeInfo",
500 path => '{node}',
501 });
502
503 __PACKAGE__->register_method ({
504 name => 'index',
505 path => '',
506 method => 'GET',
507 permissions => { user => 'all' },
508 description => "Cluster node index.",
509 parameters => {
510 additionalProperties => 0,
511 properties => {},
512 },
513 returns => {
514 type => 'array',
515 items => {
516 type => "object",
517 properties => {},
518 },
519 links => [ { rel => 'child', href => "{node}" } ],
520 },
521 code => sub {
522 my ($param) = @_;
523
524 my $nodename = PVE::INotify::nodename();
525 my $res = [
526 { node => $nodename },
527 ];
528
529 return $res;
530 }});
531
532
533 1;