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