]> git.proxmox.com Git - pmg-api.git/blob - PMG/API2/Nodes.pm
correctly use PMG::RESTEnvironment->get()
[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 PMG::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 service => {
158 description => "Service ID",
159 type => 'string',
160 maxLength => 128,
161 optional => 1,
162 },
163 },
164 },
165 returns => {
166 type => 'array',
167 items => {
168 type => "object",
169 properties => {
170 n => {
171 description=> "Line number",
172 type=> 'integer',
173 },
174 t => {
175 description=> "Line text",
176 type => 'string',
177 }
178 }
179 }
180 },
181 code => sub {
182 my ($param) = @_;
183
184 my $restenv = PMG::RESTEnvironment->get();
185
186 my $service = $param->{service};
187 if ($service && $service eq 'postfix') {
188 $service = 'postfix@-';
189 }
190
191 my ($count, $lines) = PVE::Tools::dump_journal(
192 $param->{start}, $param->{limit},
193 $param->{since}, $param->{'until'}, $service);
194
195 $restenv->set_result_attrib('total', $count);
196
197 return $lines;
198 }});
199
200 __PACKAGE__->register_method ({
201 name => 'vncshell',
202 path => 'vncshell',
203 method => 'POST',
204 protected => 1,
205 description => "Creates a VNC Shell proxy.",
206 parameters => {
207 additionalProperties => 0,
208 properties => {
209 node => get_standard_option('pve-node'),
210 websocket => {
211 optional => 1,
212 type => 'boolean',
213 description => "use websocket instead of standard vnc.",
214 default => 1,
215 },
216 },
217 },
218 returns => {
219 additionalProperties => 0,
220 properties => {
221 user => { type => 'string' },
222 ticket => { type => 'string' },
223 port => { type => 'integer' },
224 upid => { type => 'string' },
225 },
226 },
227 code => sub {
228 my ($param) = @_;
229
230 my $node = $param->{node};
231
232 # we only implement the websocket based VNC here
233 my $websocket = $param->{websocket} // 1;
234 die "standard VNC not implemented" if !$websocket;
235
236 my $authpath = "/nodes/$node";
237
238 my $restenv = PMG::RESTEnvironment->get();
239 my $user = $restenv->get_user();
240
241 my $ticket = PMG::Ticket::assemble_vnc_ticket($user, $authpath);
242
243 my $family = PVE::Tools::get_host_address_family($node);
244 my $port = PVE::Tools::next_vnc_port($family);
245
246 my $shcmd;
247
248 if ($user eq 'root@pam') {
249 $shcmd = [ '/bin/login', '-f', 'root' ];
250 } else {
251 $shcmd = [ '/bin/login' ];
252 }
253
254 my $cmd = ['/usr/bin/vncterm', '-rfbport', $port,
255 '-timeout', 10, '-notls', '-listen', 'localhost',
256 '-c', @$shcmd];
257
258 my $realcmd = sub {
259 my $upid = shift;
260
261 syslog ('info', "starting vnc proxy $upid\n");
262
263 my $cmdstr = join (' ', @$cmd);
264 syslog ('info', "launch command: $cmdstr");
265
266 eval {
267 foreach my $k (keys %ENV) {
268 next if $k eq 'PATH' || $k eq 'TERM' || $k eq 'USER' || $k eq 'HOME';
269 delete $ENV{$k};
270 }
271 $ENV{PWD} = '/';
272
273 $ENV{PVE_VNC_TICKET} = $ticket; # pass ticket to vncterm
274
275 PVE::Tools::run_command($cmd, errmsg => "vncterm failed");
276 };
277 if (my $err = $@) {
278 syslog('err', $err);
279 }
280
281 return;
282 };
283
284 my $upid = $restenv->fork_worker('vncshell', "", $user, $realcmd);
285
286 PVE::Tools::wait_for_vnc_port($port);
287
288 return {
289 user => $user,
290 ticket => $ticket,
291 port => $port,
292 upid => $upid,
293 };
294 }});
295
296 __PACKAGE__->register_method({
297 name => 'vncwebsocket',
298 path => 'vncwebsocket',
299 method => 'GET',
300 description => "Opens a weksocket for VNC traffic.",
301 parameters => {
302 additionalProperties => 0,
303 properties => {
304 node => get_standard_option('pve-node'),
305 vncticket => {
306 description => "Ticket from previous call to vncproxy.",
307 type => 'string',
308 maxLength => 512,
309 },
310 port => {
311 description => "Port number returned by previous vncproxy call.",
312 type => 'integer',
313 minimum => 5900,
314 maximum => 5999,
315 },
316 },
317 },
318 returns => {
319 type => "object",
320 properties => {
321 port => { type => 'string' },
322 },
323 },
324 code => sub {
325 my ($param) = @_;
326
327 my $authpath = "/nodes/$param->{node}";
328
329 my $restenv = PMG::RESTEnvironment->get();
330 my $user = $restenv->get_user();
331
332 PMG::Ticket::verify_vnc_ticket($param->{vncticket}, $user, $authpath);
333
334 my $port = $param->{port};
335
336 return { port => $port };
337 }});
338
339 __PACKAGE__->register_method({
340 name => 'dns',
341 path => 'dns',
342 method => 'GET',
343 description => "Read DNS settings.",
344 proxyto => 'node',
345 parameters => {
346 additionalProperties => 0,
347 properties => {
348 node => get_standard_option('pve-node'),
349 },
350 },
351 returns => {
352 type => "object",
353 additionalProperties => 0,
354 properties => {
355 search => {
356 description => "Search domain for host-name lookup.",
357 type => 'string',
358 optional => 1,
359 },
360 dns1 => {
361 description => 'First name server IP address.',
362 type => 'string',
363 optional => 1,
364 },
365 dns2 => {
366 description => 'Second name server IP address.',
367 type => 'string',
368 optional => 1,
369 },
370 dns3 => {
371 description => 'Third name server IP address.',
372 type => 'string',
373 optional => 1,
374 },
375 },
376 },
377 code => sub {
378 my ($param) = @_;
379
380 my $res = PVE::INotify::read_file('resolvconf');
381
382 return $res;
383 }});
384
385 __PACKAGE__->register_method({
386 name => 'update_dns',
387 path => 'dns',
388 method => 'PUT',
389 description => "Write DNS settings.",
390 proxyto => 'node',
391 protected => 1,
392 parameters => {
393 additionalProperties => 0,
394 properties => {
395 node => get_standard_option('pve-node'),
396 search => {
397 description => "Search domain for host-name lookup.",
398 type => 'string',
399 },
400 dns1 => {
401 description => 'First name server IP address.',
402 type => 'string', format => 'ip',
403 optional => 1,
404 },
405 dns2 => {
406 description => 'Second name server IP address.',
407 type => 'string', format => 'ip',
408 optional => 1,
409 },
410 dns3 => {
411 description => 'Third name server IP address.',
412 type => 'string', format => 'ip',
413 optional => 1,
414 },
415 },
416 },
417 returns => { type => "null" },
418 code => sub {
419 my ($param) = @_;
420
421 PVE::INotify::update_file('resolvconf', $param);
422
423 return undef;
424 }});
425
426
427 __PACKAGE__->register_method({
428 name => 'time',
429 path => 'time',
430 method => 'GET',
431 description => "Read server time and time zone settings.",
432 proxyto => 'node',
433 parameters => {
434 additionalProperties => 0,
435 properties => {
436 node => get_standard_option('pve-node'),
437 },
438 },
439 returns => {
440 type => "object",
441 additionalProperties => 0,
442 properties => {
443 timezone => {
444 description => "Time zone",
445 type => 'string',
446 },
447 time => {
448 description => "Seconds since 1970-01-01 00:00:00 UTC.",
449 type => 'integer',
450 minimum => 1297163644,
451 },
452 localtime => {
453 description => "Seconds since 1970-01-01 00:00:00 (local time)",
454 type => 'integer',
455 minimum => 1297163644,
456 },
457 },
458 },
459 code => sub {
460 my ($param) = @_;
461
462 my $ctime = time();
463 my $ltime = timegm_nocheck(localtime($ctime));
464 my $res = {
465 timezone => PVE::INotify::read_file('timezone'),
466 time => time(),
467 localtime => $ltime,
468 };
469
470 return $res;
471 }});
472
473 __PACKAGE__->register_method({
474 name => 'set_timezone',
475 path => 'time',
476 method => 'PUT',
477 description => "Set time zone.",
478 proxyto => 'node',
479 protected => 1,
480 parameters => {
481 additionalProperties => 0,
482 properties => {
483 node => get_standard_option('pve-node'),
484 timezone => {
485 description => "Time zone. The file '/usr/share/zoneinfo/zone.tab' contains the list of valid names.",
486 type => 'string',
487 },
488 },
489 },
490 returns => { type => "null" },
491 code => sub {
492 my ($param) = @_;
493
494 PVE::INotify::write_file('timezone', $param->{timezone});
495
496 return undef;
497 }});
498
499
500 package PMG::API2::Nodes;
501
502 use strict;
503 use warnings;
504
505 use PVE::RESTHandler;
506 use PVE::JSONSchema qw(get_standard_option);
507
508 use base qw(PVE::RESTHandler);
509
510 __PACKAGE__->register_method ({
511 subclass => "PMG::API2::NodeInfo",
512 path => '{node}',
513 });
514
515 __PACKAGE__->register_method ({
516 name => 'index',
517 path => '',
518 method => 'GET',
519 permissions => { user => 'all' },
520 description => "Cluster node index.",
521 parameters => {
522 additionalProperties => 0,
523 properties => {},
524 },
525 returns => {
526 type => 'array',
527 items => {
528 type => "object",
529 properties => {},
530 },
531 links => [ { rel => 'child', href => "{node}" } ],
532 },
533 code => sub {
534 my ($param) = @_;
535
536 my $nodename = PVE::INotify::nodename();
537 my $res = [
538 { node => $nodename },
539 ];
540
541 return $res;
542 }});
543
544
545 1;