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