]> git.proxmox.com Git - pve-manager.git/blob - lib/PVE.old/HTMLServices.pm
imported from svn 'pve-manager/pve2'
[pve-manager.git] / lib / PVE.old / HTMLServices.pm
1 package PVE::HTMLServices;
2
3 use strict;
4 use CGI '3.12';
5 use mod_perl2;
6 use Apache2::Const qw(:common);
7 use HTML::Entities;
8 use JSON;
9 use IO::File;
10 use POSIX qw(tzset strftime);
11 use Text::Wrap qw(wrap);
12 use PVE::Config;
13 use PVE::I18N;
14 use PVE::ConfigServer;
15 use PVE::HTMLUtils;
16 use PVE::HTMLTable;
17 use PVE::SafeSyslog;
18
19 $PVE::HTMLServices::Obj = bless {
20 helo => 'Helo',
21
22 methods => {
23 index => { proto => "index ()" },
24 command => { proto => "command (upid, did)" },
25 command_abort => { "command_abort (upid)" },
26 status => { proto => "status (cid)" },
27 status_update => { proto => "status_update (cid)" },
28 vzlist => { proto => "vzlist (cid)" },
29 vmops => { proto => "vmops (inactive)" },
30 vmlogview => { proto => "vmlogview (cid, veid, service)" },
31 viewlog => { proto => "viewlog (service, filter)" },
32 vmstatus => { proto => "vmstatus (cid, veid, type)" },
33 hello => { proto => "hello ()" },
34 servertime => { proto => "servertime ()" },
35 },
36
37 };
38
39 sub ws_json_hello {
40 my($conn, $args) = @_;
41
42 return {
43 name => "A JSON Example",
44 value => [ 'a', 'b'],
45 };
46 }
47
48 sub ws_servertime {
49
50 # trak TZ changes
51
52 POSIX::tzset();
53
54 my ($sec, $min, $hour) = localtime (time());
55
56 return sprintf ("%02d:%02d:%02d", $hour, $min, $sec);
57 }
58
59 sub ws_json_vzlist {
60 my($conn, $args) = @_;
61
62 my $cid = $args->{"cid"};
63
64 my $html;
65 my $status;
66
67 eval {
68 my $cvzl = PVE::Cluster::vzlist_update ($cid, $conn->{ticket});
69 die "no data" if !$cvzl;
70 $html = PVE::HTMLUtils::create_vzlist_table ($cid, $cvzl->{"CID_$cid"});
71
72 };
73
74 my $err = $@;
75
76 if ($err) {
77 syslog ('err', $err);
78 $html = "Unable to get data for Cluster Node $cid<br>";
79 $status = "<blink><font color=red>Unable to load local cluster table</blink>";
80 }
81
82 return {
83 html => $html,
84 status => $status || "Online",
85 };
86 }
87
88 sub ws_json_status {
89 my($conn, $args) = @_;
90
91 my $cid = $args->{"cid"};
92 my $verbose = $args->{"verbose"};
93
94 my $html;
95
96 my $cinfo = PVE::Cluster::clusterinfo ();
97
98 eval {
99 my $rcon = PVE::ConfigClient::connect ($conn->{ticket}, $cinfo, $cid);
100 my $status = $rcon->ping()->result;
101 $html = PVE::HTMLUtils::create_host_status ($cinfo, $status, $verbose);
102 };
103
104 my $err = $@;
105
106 if ($err) {
107 syslog ('err', $err);
108
109 return {
110 html => encode_entities ("ERROR: $err"),
111 status => "Unable to get data for Cluster Node $cid<br>",
112 };
113 }
114
115 return {
116 html => $html,
117 status => "Online",
118 };
119 }
120
121 sub ws_status_update {
122 my($conn, $args) = @_;
123
124 my $cid = $args->{"cid"};
125
126 my $html;
127
128 my $cinfo = PVE::Cluster::clusterinfo ();
129
130 my $ni = $cinfo->{"CID_$cid"};
131
132 my $role = '-';
133 my $name = "Node $cid";
134 my $nodeip = '-';
135
136 eval {
137
138 die "unknown CID '$cid'\n" if !$ni;
139
140 $role = $ni->{role};
141 $role = 'Master' if $role eq 'M';
142 $role = 'Node' if $role eq 'N';
143
144 $name = $ni->{name};
145 $nodeip = $ni->{ip};
146
147 my $rcon = PVE::ConfigClient::connect ($conn->{ticket}, $cinfo, $cid);
148
149 my $status = $rcon->ping()->result;
150
151 my $state = $status->{insync} ? 'active' : '<blink><font color=red>nosync</font></blink>';
152
153 my $mem = int (0.5 + ($status->{meminfo}->{mbmemused}*100/$status->{meminfo}->{mbmemtotal}));
154 my $disk = int (0.5 + ($status->{hdinfo}->{root}->{used}*100/$status->{hdinfo}->{root}->{avail}));
155
156 my $cpu = int ($status->{cpu}*100);
157 my $wait = int ($status->{wait}*100);
158
159 $html = "<td>$name</td><td>$nodeip</td><td>$role</td><td>$state</td>" .
160 "<td>$status->{uptime}->{uptimestrshort}</td>" .
161 "<td>$status->{uptime}->{avg1}</td>" .
162 "<td>$cpu%</td><td>$wait%</td><td>$mem%</td><td>$disk%</td>";
163
164 };
165
166 my $err = $@;
167
168 if ($err) {
169 syslog ('err', $err);
170 my $state = "<blink><font color=red>ERROR: " . encode_entities ($err) . "</blink>";
171 return "<td>$name</td><td>$nodeip</td><td>$role</td><td colspan=7>$state</td>";
172 }
173
174 return $html;
175 }
176
177 sub ws_json_vmstatus {
178 my($conn, $args) = @_;
179
180 my $cid = $args->{"cid"};
181 my $veid = $args->{"veid"};
182 my $type = $args->{"type"};
183
184 my $html;
185 my $status = '';
186
187 my $cinfo = PVE::Cluster::clusterinfo ();
188
189 eval {
190 my $vzinfo = PVE::Cluster::load_vmconfig ($cinfo, $cid, $veid, $type, $conn->{ticket});
191 $html = PVE::HTMLUtils::create_vmstatus ($cid, $veid, $type, $vzinfo);
192 };
193
194 my $err = $@;
195
196 if ($err) {
197 syslog ('err', $err);
198 $html = "Unable to get data for VM $veid<br>";
199 $status = "<blink><font color=red>Unable to load virtual machine config</blink>";
200 }
201
202 return {
203 html => $html,
204 status => $status,
205 };
206 }
207
208 sub ws_json_vmlogview {
209 my($conn, $args) = @_;
210
211 my $cid = $args->{"cid"};
212 my $veid = $args->{"veid"};
213 my $service = $args->{"service"};
214
215 my $html = '';
216 my $status;
217
218 my $cinfo = PVE::Cluster::clusterinfo ();
219
220 eval {
221 my $ni = $cinfo->{"CID_$cid"} || die "unknown CID '$cid'";
222
223 my $rcon = PVE::ConfigClient::connect ($conn->{ticket}, $cinfo, $cid);
224 my $lines = $rcon->vmlogview($cid, $veid, $service)->result;
225
226 foreach my $line (@$lines) {
227 my $el = encode_entities ($line) . "<br>\n";
228 if ($service eq 'init') {
229 $el =~ s/&\#27;\[0;31m(.*)&\#27;\[0;39m/<font color=red>$1<\/font>/g;
230 $el =~ s/&\#27;\[31m(.*)&\#27;\[39;49m/<font color=red>$1<\/font>/g;
231 $el =~ s/&\#27;\[33m(.*)&\#27;\[39;49m/<font color=yellow>$1<\/font>/g;
232 $el =~ s/&\#27;\[0;32m(.*)&\#27;\[0;39m/<font color=green>$1<\/font>/g;
233 $el =~ s/&\#27;\[\d+G//g;
234 }
235 $html .= $el;
236 }
237 };
238
239 my $err = $@;
240
241 if ($err) {
242 syslog ('err', $err);
243 $html = "Unable to get data for Cluster Node $cid<br>";
244 $status = "<blink><font color=red>Unable to load local cluster table</blink>";
245 }
246
247 return {
248 html => $html,
249 status => $status || "Online",
250 };
251 }
252
253 # parse syslog line
254 sub syslog_parse_line {
255 my ($line) = @_;
256
257 my $rec;
258
259 if ($line =~
260 m/^(\S+\s+\S+\s+\S+)\s+(\S+)\s+([^\s\[:]+)(\[(\S+)\])?([^:]*):\s+(.*)$/) {
261 $rec->{date} = $1;
262 $rec->{host} = $2;
263 $rec->{prog} = $3;
264 $rec->{prog} .= " $6" if $6;
265 $rec->{pid} = $5;
266 $rec->{text} = $7;
267 } else {
268 if ($line =~
269 m/^(\S+\s+\S+\s+\S+)\s+(\S+)\s+(last message repeated \d+ times)$/) {
270 $rec->{date} = $1;
271 $rec->{host} = $2;
272 $rec->{prog} = 'syslog';
273 $rec->{pid} = 0;
274 $rec->{text} = $3;
275 } else {
276 # unknown log format
277 $rec->{date} = "0";
278 $rec->{host} = "unknown";
279 $rec->{prog} = "unknown";
280 $rec->{pid} = "0";
281 $rec->{text} = $line;
282 }
283 }
284
285 if (lc ($rec->{prog}) eq '/usr/sbin/cron') {
286 $rec->{prog} = 'cron';
287 }
288
289 return $rec;
290 }
291
292 sub ws_json_viewlog {
293 my($conn, $args) = @_;
294
295 my $out = '';
296
297 my $filter = $args->{"filter"};
298 my $service = $args->{"service"} || '';
299 my $trackid = $args->{"trackid"} || '';
300
301 $filter =~ s|\\|\\\\|g;
302 $filter =~ s/\?/\\\?/g;
303 $filter =~ s/\(/\\\(/g;
304 $filter =~ s/\)/\\\)/g;
305 $filter =~ s/\{/\\\{/g;
306 $filter =~ s/\}/\\\}/g;
307 $filter =~ s/\[/\\\[/g;
308 $filter =~ s/\]/\\\]/g;
309 $filter =~ s/\./\\\./g;
310 $filter =~ s/\*/\\\*/g;
311 $filter =~ s/\+/\\\+/g;
312
313 my $filename= "/var/log/syslog";
314
315 my $limit = 100;
316
317 if ($service eq 'apache') {
318 $filename = "/var/log/apache2/access.log";
319 }
320
321 my $running = 0;
322
323 if ($trackid) {
324 my $rcon = PVE::ConfigClient::connect ($conn->{ticket});
325 $running = $rcon->check_worker ($trackid)->result;
326 }
327
328 $out .= "<table border=0 cellspacing=3 cellpadding=0 style='font-family:monospace;'>";
329
330 if ($filename eq '/var/log/syslog') {
331 my $loga;
332 my $needhost;
333
334 open (TMP, "tail -$limit $filename|");
335 while (my $line = <TMP>) {
336 if (my $rec = syslog_parse_line ($line)) {
337 next if $filter && $line !~ m/$filter/i;
338 next if ($service && ($rec->{prog} !~ m"(^$service\/|\/$service$|^$service$)"));
339
340 push @$loga, $rec;
341 }
342 }
343 close (TMP);
344
345
346 foreach my $rec (@$loga) {
347 $out .= "<tr><td nowrap>" . encode_entities ($rec->{date}) . "&nbsp</td>";
348 if ($needhost) {
349 $out .= "<td nowrap>" . encode_entities ($rec->{host}) . "</td>";
350 }
351
352 $rec->{prog} =~ s|^postfix/||;
353
354 $out .= "<td nowrap>" . encode_entities ($rec->{prog}) . "</td>";
355 $out .= "<td align=right nowrap>" . $rec->{pid} . "&nbsp</td>";
356 $out .= "<td nowrap>" . encode_entities ($rec->{text}) . "</td>";
357 $out .= "</tr>";
358 }
359 } else {
360 open (TMP, "tail -$limit $filename|");
361 while (my $line = <TMP>) {
362 chomp $line;
363 next if $filter && $line !~ m/$filter/i;
364 $line = encode_entities ($line);
365 $out .= "<tr><td nowrap>" . $line . "</td></tr>";
366 }
367 close (TMP);
368 }
369 $out .= "</table>\n";
370
371 if ($trackid) {
372 return {
373 html => $out,
374 status => $running ? "Update in progress" : "Update finished",
375 running => $running,
376 };
377 } else {
378 return {
379 html => $out,
380 status => "Online",
381 running => 1,
382 };
383 }
384 }
385
386 sub ws_json_vmops {
387 my($conn, $args) = @_;
388
389 my $inactive = $args->{"inactive"};
390
391 my $vmops = PVE::Config::read_file ("vmops");
392 my $out = PVE::HTMLUtils::create_vmops_table ($vmops, $inactive);
393
394 return { html => $out, status => 'OK' };
395 }
396
397 sub ws_command_abort {
398 my($conn, $args) = @_;
399
400 my $upid = $args->{"upid"};
401
402 my $rcon = PVE::ConfigClient::connect ($conn->{ticket});
403 $rcon->check_worker ($upid, 1);
404 }
405
406 sub ws_json_command {
407 my($conn, $args) = @_;
408
409 my $out = '';
410
411 my $upid = $args->{"upid"};
412 my $jsvar = $args->{"jsvar"};
413
414 my $upid_hash = PVE::Utils::upid_decode ($upid);
415
416 my $cmdtype = $upid_hash->{type}; # 'vmops', 'apldownload'
417
418 if (!$upid_hash || !$upid_hash->{filename}) {
419 return {
420 html => '',
421 running => 0,
422 status => "got strange parameters $upid"
423 };
424 }
425
426 my $filename = $upid_hash->{filename};
427
428 my $fh = new IO::File $filename, "r";
429 if (!defined ($fh)) {
430 return {
431 html => '',
432 running => 1,
433 status => "unable to open output file '$filename'" };
434 }
435
436 my $savedid = <$fh>;
437 chomp $savedid;
438
439 if ($savedid ne $upid) {
440 return { html => '', running => 0, status => "no data"};
441 }
442
443 my $out = '';
444 my $line;
445
446 while (defined ($line = <$fh>)) {
447 chomp $line;
448
449 # skip ssh warning
450 next if $line =~ m/^tcgetattr: Inappropriate ioctl for device$/;
451
452 my $stat = '';
453 while (defined ($line) && $line =~ m/^rsync\s+status:/) {
454 $stat = $line;
455 if (defined ($line = <$fh>)) {
456 chomp $line;
457 }
458 }
459
460 $out .= encode_entities ($stat) . "<br>" if $stat;
461
462 $out .= encode_entities ($line) . "<br>" if defined ($line);;
463 }
464
465 $fh->close;
466
467 my $rcon = PVE::ConfigClient::connect ($conn->{ticket});
468 my $running = $rcon->check_worker ($upid)->result;
469
470 my $status;
471
472 if ($cmdtype eq 'apldownload') {
473 $status = $running ? "downloading '$upid_hash->{apl}'" : "download finished";
474 } else {
475 $status = $running ? "executing command" : "command finished";
476 }
477 return {
478 html => $out,
479 status => $status,
480 running => $running,
481 };
482 }
483
484 sub ws_index {
485 my($conn, $args) = @_;
486
487 my $obj = $conn->{server};
488
489 my $out = "Proxmox Web Service Description<br><br>";
490
491 foreach my $m (keys %{$obj->{methods}}) {
492 my $proto = $obj->{methods}->{$m}->{proto};
493 $out .= "METHOD: $proto<br>";
494 }
495
496 return $out;
497 }
498
499 sub handler ($$) {
500 my($obj, $r) = @_;
501
502 my $auth_type = $r->ap_auth_type;
503 my $uri = $r->uri;
504
505 my $cookie_name = $auth_type->cookie_name ($r);
506 my $cookie = $auth_type->key ($r);
507
508 my ($username, $group) = split /::/, $cookie;
509
510 my $conn = {
511 server => $obj,
512 request => $r,
513 uri => $uri,
514 user => $username,
515 group => $group,
516 ticket => $cookie,
517 };
518
519 my $pvecfg = PVE::Config::read_file ('pvecfg');
520 my $language = $pvecfg->{language} || 'C';
521 PVE::I18N::set_lang ($language);
522
523 my $path = $r->path_info;
524 if ($path =~ m'^/?(\w+)(\.htm|\.pl)?$' && $obj->{methods}->{$1}) {
525 my $name = $1;
526
527 my $cgi = CGI->new ($r);
528
529 my $arglist = $cgi->Vars();
530
531 $r->no_cache (1);
532
533 if (my $serv = $obj->can ("ws_script_$name")) {
534 my $data = &$serv ($conn, $arglist);
535 my $x = length ($data);
536 $r->content_type ('application/javascript');
537 $r->headers_out->set ("Content-length", "$x");
538 $r->headers_out->set ("Pragma", "no-cache");
539 $r->print ($data);
540 return OK;
541 } elsif (my $serv = $obj->can ("ws_json_$name")) {
542 my $data = &$serv ($conn, $arglist);
543 my $js = to_json($data, {utf8 => 1});
544 my $x = length ($js);
545 $r->content_type ('application/json');
546 $r->headers_out->set ("Content-length", "$x");
547 $r->headers_out->set ("Pragma", "no-cache");
548 $r->print ($js);
549 return OK;
550 } elsif (my $serv = $obj->can ("ws_$name")) {
551 my $data = &$serv ($conn, $arglist);
552 my $x = length ($data);
553 $r->content_type ('text/html');
554 $r->headers_out->set ("Content-length", "$x");
555 $r->headers_out->set ("Pragma", "no-cache");
556 $r->print ($data);
557 return OK;
558 } else {
559 return NOT_FOUND;
560 }
561 } else {
562 return NOT_FOUND;
563 }
564
565 return OK;
566 }
567
568 1;