]> git.proxmox.com Git - pve-manager.git/blame - lib/PVE.old/HTMLUtils.pm
imported from svn 'pve-manager/pve2'
[pve-manager.git] / lib / PVE.old / HTMLUtils.pm
CommitLineData
aff192e6
DM
1package PVE::HTMLUtils;
2
3use strict;
4require Exporter;
5use vars qw(@ISA @EXPORT);
6use Socket;
7use PVE::I18N;
8use HTML::Entities;
9use PVE::HTMLDropDown;
10use PVE::HTMLTable;
11use PVE::HTMLGrid;
12use PVE::HTMLControls;
13use PVE::APLInfo;
14use PVE::Storage;
15use Data::Dumper;
16
17@ISA = qw(Exporter);
18@EXPORT = qw(check_field check_range check_write_mode);
19
20
21# useful for debugging
22sub var_to_html {
23 my $v = shift;
24
25 return "<pre>" . Dumper ($v) . "</pre>";
26}
27
28sub format_size {
29 my $size = shift;
30
31 my $kb = $size / 1024;
32
33 if ($kb < 1024) {
34 return int ($kb) . "KB";
35 }
36
37 my $mb = $size / (1024*1024);
38
39 if ($mb < 1024) {
40 return int ($mb) . "MB";
41 } else {
42 my $gb = $mb / 1024;
43 return sprintf ("%.2fGB", $gb);
44 }
45}
46
47# HTML encode/decode text to store in config files (single line encoding)
48sub encode_description {
49 my $desc = shift;
50
51 $desc = encode_entities ($desc);
52
53 $desc =~ s|\r?\n|<br>|gm;
54
55 return $desc;
56}
57
58sub decode_description {
59 my $desc = shift;
60
61 $desc =~ s|<br>|\n|g;
62
63 return decode_entities ($desc);
64}
65
66# html field checks
67
68sub check_range {
69 my ($name, $value, $min, $max) = @_;
70
71 if ($min && ($value < $min)) {
72 die sprintf(__("Field '%s' is below minimum ($value < $min)") . "\n", $name);
73 }
74 if ($max && ($value > $max)) {
75 die sprintf(__("Field '%s' is above maximum ($value > $max)") . "\n", $name);
76 }
77}
78
79sub check_field {
80 my ($name, $value, @checks) = @_;
81
82 foreach my $c (@checks) {
83 if ($c eq 'NOTEMPTY') {
84 die sprintf(__("Field '%s' must not be empty") . "\n", $name) if !defined ($value) || ($value eq '');
85 } elsif ($c eq 'NATURAL') {
86 die sprintf(__("Field '%s' contains invalid characters") . "\n", $name) if $value !~ m/^\d+$/;
87 } elsif ($c eq 'FLOAT') {
88 die sprintf(__("Field '%s' contains invalid characters") . "\n", $name) if $value !~ m/^\d+(\.\d+)?$/;
89 } elsif ($c eq 'NOWHITESPACES') {
90 die sprintf(__("Field '%s' must not contain white spaces") . "\n", $name) if $value =~ m/\s/;
91 } elsif ($c eq 'HTMLCOLOR') {
92 die sprintf(__("Field '%s' is no valid html color (required format: #XXXXXX)") . "\n", $name) if $value !~ m/^\s*\#[a-f0-9A-F]{6}\s*$/;
93 } elsif ($c eq 'EMAIL') {
94 if ($value !~ m/^\S+\@\S+\.\S+$/) {
95 die sprintf(__("Field '%s' does not look like a valid email address") . "\n", $name);
96 }
97 } elsif ($c eq 'IPADDRESS') {
98 if ($value !~ m/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) {
99 die sprintf (__("Field '%s' does not look like a valid IP address") . "\n", $name);
100 }
101 } elsif ($c eq 'MAC') {
102 if ($value !~ m/^([a-f0-9A-F]{2}:){5}[a-f0-9A-F]{2}$/) {
103 die sprintf(__("Field '%s' does not look like no valid MAC address (required format: XX:XX:XX:XX:XX:XX)") . "\n", $name);
104 }
105 } elsif ($c eq 'SERVER') { # resolves server name
106 my $packed_ip = gethostbyname($value);
107 if (!defined $packed_ip) {
108 die sprintf(__("Field '%s' does not look like a valid server address") . "\n", $name);
109 }
110 $value = inet_ntoa($packed_ip);
111 } elsif ($c eq 'PORTAL') { # resolves iscsi portal name
112
113 if ($value =~ m/^([^:]+)(:(\d+))?$/) {
114 my $server = $1;
115 my $port = $3;
116
117 my $packed_ip = gethostbyname($server);
118 if (defined $packed_ip) {
119 $server = inet_ntoa($packed_ip);
120 $value = $port ? "$server:$port" : $server;
121 next;
122 }
123 }
124 die sprintf(__("Field '%s' does not look like a valid ISCSI portal") . "\n", $name);
125 } elsif ($c =~ m/^CHAREXCL:(.*)$/) {
126 die sprintf(__("Field '%s' must not contain special characters") . "\n", $name) if $value =~ m/$1/;
127 } elsif ($c =~ m/^REGMATCH:(.*)$/) {
128 die sprintf(__("Field '%s' must not contain special characters") . "\n", $name) if $value !~ m/$1/;
129 } else {
130 die "unimplemente check '$c' - internal error";
131 }
132 }
133
134 return $value;
135}
136
137sub msg {
138 my $id = shift;
139
140 return __('You do not have write access.') if $id eq 'nowr';
141
142 return __('This information is only available on the master node.') if $id eq 'infoatmaster';
143
144 return __("Are you sure you want to remove VM %s? This will permanently erase all VM data.") if $id eq 'confirm_remove';
145
146}
147
148sub check_write_mode {
149 my ($perm) = @_;
150
151 if ($perm ne 'w') {
152 die msg('nowr') . "\n";
153 }
154}
155
156sub modify_url {
157 my ($uri, %args, %mod) = @_;
158
159 my $qstring = "";
160
161 $args{action} = undef if !defined ($args{action});
162 $args{aa} = undef if !defined ($args{aa});
163
164 foreach my $p (keys (%args)) {
165 next if defined ($mod{$p}) || !defined ($args{$p});
166 $qstring .= $qstring ? "&" : "?";
167 $qstring .= "$p=$args{$p}";
168 }
169 foreach my $p (keys (%mod)) {
170 $qstring .= $qstring ? "&" : "?";
171 $qstring .= "$p=$mod{$p}";
172 }
173
174 return $uri . $qstring;
175}
176
177sub parse_args {
178 my ($string) = @_;
179 return () unless defined $string and $string;
180
181 return map {
182 tr/+/ /;
183 s/%([0-9a-fA-F]{2})/pack("C",hex($1))/ge;
184 $_;
185 } split /[=&;]/, $string, -1;
186}
187
188# |----------------------------|
189# |<b>$title</b>: $msg |
190# |----------------------------|
191sub create_noteframe {
192 my ($title, $msg) = @_;
193 my $html = "<div class='menubg lightcolbd' style='width:741px;border:1px solid;padding:2px;'>";
194 $html .= "<b>$title:</b> $msg</div>";
195 return $html;
196}
197
198# create a nice box
199#
200# |----------------------------|
201# |$left $right|
202# |----------------------------|
203# |$content |
204# |----------------------------|
205
206sub create_statusframe {
207 my ($id, $left, $right, $content, $height) = @_;
208
209 $left = '&nbsp;' if !$left;
210 $right = '&nbsp;' if !$right;
211
212 my $idtxt = $id ? "id='$id'" : '';
213 my $idtxtleft = $id ? "id='${id}left'" : '';
214 my $idtxtright = $id ? "id='${id}right'" : '';
215
216 my $out .= "<table border=0 cellspacing=0 cellpadding=0 class='menubg lightcolbd' style='width:747px; border: 1px solid;border-bottom:0px;padding:2px;padding-left:5px;padding-right:5px;'>";
217 $out .= "<tr><td $idtxtleft>$left</td>";
218
219 $out .= "<td $idtxtright align=right>$right</td></tr></table>";
220
221 my $hs = $height ? "height:${height}px;" : '';
222
223 my $ovfl = $height ? 'auto' : 'visible';
224 $out .= "<div $idtxt class=lightcolbd style='border: 1px solid; width:735px; $hs overflow:$ovfl; white-space: nowrap;padding:5px;'>$content</div>";
225
226 return $out;
227}
228
229sub create_vmops_frame {
230 my ($vmid, $upid) = @_;
231
232
233 if (!$upid) {
234 my $filename = "/tmp/vmops-$vmid.out";
235 if (my $fh = IO::File->new ($filename, "r")) {
236 $upid = <$fh>;
237 close ($fh);
238 chomp $upid;
239 }
240 }
241
242 my $out = '';
243
244 if ($upid) {
245 my $href = "javascript:command_abort(\"$upid\");";
246 my $abort = "<a class='frmsubmit' id='abortbutton' href='$href'></a>";
247
248 $out .= create_statusframe ('logview', undef, $abort, undef, 450, 1);
249 $out .= PVE::HTMLControls::create_command_viewer ('logview', 'logviewleft', 'abortbutton', $upid);
250 }else {
251 $out .= __("Nothing to view");
252 }
253
254 return $out;
255}
256
257sub create_apldownload_frame {
258 my ($userid, $upid) = @_;
259
260 if (!$upid) {
261 my $filename = "/tmp/apldownload-$userid.out";
262 if (my $fh = IO::File->new ($filename, "r")) {
263 $upid = <$fh>;
264 close ($fh);
265 chomp $upid;
266 }
267 }
268
269 my $out = '';
270
271 if ($upid) {
272 my $href = "javascript:command_abort(\"$upid\");";
273 my $abort = "<a class='frmsubmit' id='abortbutton' href='$href'></a>";
274
275 $out .= create_statusframe ('logview', undef, $abort, undef, 100);
276 $out .= PVE::HTMLControls::create_command_viewer ('logview', 'logviewleft', 'abortbutton', $upid);
277 }else {
278 $out .= __("Nothing to view");
279 }
280
281 return $out;
282}
283
284sub create_cpubar {
285 my ($width, $abs, $rel) = @_;
286
287 my $dvrel = $rel > 100 ? 100 : $rel;
288 my $dvabs = $abs > 100 ? 100 : $abs;
289
290 my $hwidth1 = sprintf ("%dpx", $width);
291 my $hwidth2 = sprintf ("%dpx", int (($width * $dvrel)/100));
292 my $hwidth3 = sprintf ("%dpx", int (($width * $dvabs)/100));
293
294 my $per = sprintf ("%0.2f%", $rel);
295
296 return "<div style='padding:0px;background-color:#C0C0C0;border:1px solid #000000;width:$hwidth1;height:14px;position:relative;'><div style='position:absolute;top:0px;left:0px;background-color:#00C000;border:0px;width:$hwidth2;height:14px;'></div><div style='position:absolute;top:11px;left:0px;background-color:#00F000;border:0px;width:$hwidth3;height:3px;'></div><div align=center style='width:100%;position:absolute;top:1px;left:0px;font-size:10px;'>$per</div></div>";
297}
298
299sub create_bar {
300 my ($width, $max, $value, $text) = @_;
301
302 if (!$max || ($max <= 0)) {
303 $max = 1;
304 $value = 0;
305 }
306
307 my $dv = $value > $max ? $max : $value;
308
309 my $hwidth1 = sprintf ("%dpx", $width);
310
311 my $hwidth2 = sprintf ("%dpx", int (($width * $dv)/$max));
312
313 my $per = $text ? $text : sprintf ("%0.2f%", ($value*100)/$max);
314 return "<div style='padding:0px;background-color:#C0C0C0;border:1px solid #000000;width:$hwidth1;height:14px;position:relative;'><div style='position:absolute;top:0px;left:0px;background-color:#00C000;border:0px;width:$hwidth2;height:14px;'></div><div align=center style='width:100%;position:absolute;top:1px;left:0px;font-size:10px;'>$per</div></div>";
315}
316
317sub uptime_to_str {
318 my ($ut, $long) = @_;
319
320 if (!$ut) {
321 return '-';
322 }
323
324 if ($long) {
325 my $days = int ($ut / 86400);
326 $ut -= $days*86400;
327 my $hours = int ($ut / 3600);
328 $ut -= $hours*3600;
329 my $mins = int ($ut / 60);
330 $ut -= $mins*60;
331
332 if ($days) {
333 my $ds = $days > 1 ? __('days') : __('day');
334 return sprintf "%d $ds %02d:%02d:%02d", $days, $hours, $mins, $ut;
335 } else {
336 return sprintf "%02d:%02d:%02d", $hours, $mins, $ut;
337 }
338 }
339
340 if ($ut < 60) {
341 return "${ut}s";
342 } elsif ($ut < 3600) {
343 my $mins = int ($ut / 60);
344 return "${mins}m";
345 } elsif ($ut < 86400) {
346 my $hours = int ($ut / 3600);
347 return "${hours}h";
348 } else {
349 my $days = int ($ut / 86400);
350 return "${days}d";
351 }
352}
353
354sub create_vzlist_table {
355 my ($cid, $vzlist) = @_;
356
357 my $table = PVE::HTMLTable->new ([]);
358
359 my $out = '';
360
361 my @header = ('1', '20px', '&nbsp;',
362 '1', '50px', __('VMID'),
363 '1', '70px', __('Status'),
364 '1', '235px', __('Name'),
365 '1', '50px', __('Uptime'),
366 '1', '100px', __('Disk'),
367 '1', '100px', __('Memory'),
368 '1', '100px', __('CPU'),
369 );
370
371 $table->add_headline (\@header);
372
373 my $ddown = PVE::HTMLDropDown->new ();
374 $ddown->add_item ("menu${cid}_0", "?action=start", __('Start'));
375 $ddown->add_item ("menu${cid}_0", "?confirmdestroy=1", __('Remove'));
376 $ddown->add_item ("menu${cid}_0", "/vmlist/migrate.htm?online=0", __('Migrate'));
377
378 $ddown->add_item ("menu${cid}_1", "?action=restart", __('Restart'));
379 $ddown->add_item ("menu${cid}_1", "?action=shutdown", __('Shutdown'));
380 $ddown->add_item ("menu${cid}_1", "?action=stop", __('Stop'));
381 $ddown->add_item ("menu${cid}_1", "javascript:pve_console()", __('Console'));
382 $ddown->add_item ("menu${cid}_1", "/vmlist/migrate.htm?online=0", __('Migrate'));
383
384 $ddown->add_item ("menu${cid}_2", "?action=start", __('Start'));
385 $ddown->add_item ("menu${cid}_2", "?action=umount", __('Unmount'));
386
387 my $found = 0;
388
389 foreach my $vkey (sort keys %$vzlist) {
390 next if $vkey !~ m/^VEID_(\d+)$/;
391 my $veid = $1;
392 my $d = $vzlist->{$vkey};
393
394 $found = 1;
395
396 my $type = $d->{type};
397
398 if ($d->{status} eq 'running' || $d->{status} eq 'stopped' ||
399 $d->{status} eq 'shutdown' || $d->{status} eq 'stop' ||
400 $d->{status} eq 'start' || $d->{status} eq 'mounted') {
401
402 my $mlabel = "menu${cid}_1";
403
404 if ($d->{status} eq 'stopped') {
405 $mlabel = "menu${cid}_0";
406 } elsif ($d->{status} eq 'mounted') {
407 $mlabel = "menu${cid}_2";
408 }
409
410 my $menu = $ddown->out_symbol ($mlabel, '', "&amp;cid=$cid&amp;veid=$veid&amp;type=$type");
411
412 $table->set_row_link ("/$type/$cid-$veid/index.htm");
413
414 my $membar;
415 my $diskbar;
416
417 my $cpubar = create_cpubar (100, $d->{pctcpu}, $d->{relcpu});
418 if ($d->{type} eq 'openvz') {
419 $membar = create_bar (100, $d->{maxmem}, $d->{mem},
420 format_size ($d->{mem}*1024*1024));
421
422 if ($d->{status} ne 'stopped') {
423 $diskbar = create_bar (100, $d->{maxdisk}, $d->{disk},
424 format_size ($d->{disk}*1024*1024));
425 } else {
426 my $ds = format_size ($d->{maxdisk}*1024*1024);
427 $diskbar = "<div width=100 align=right>$ds</div>";
428 }
429 } elsif ($d->{type} eq 'qemu') {
430 $membar = create_bar (100, $d->{maxmem}, $d->{mem},
431 format_size ($d->{mem}*1024*1024));
432 my $ds = format_size ($d->{maxdisk}*1024*1024);
433 $diskbar = "<div width=100 align=right>$ds</div>";
434 }
435
436 # add soft hyphenation (in case someone has a very long hostname)
437 $d->{name} =~ s/\./\.&shy;/g;
438
439 if ($d->{status} eq 'stopped' || $d->{status} eq 'mounted') {
440 $membar = '';
441 $cpubar = '';
442 }
443 $table->add_row ('', $menu, $veid, $d->{status}, $d->{name},
444 uptime_to_str ($d->{uptime}), $diskbar, $membar, $cpubar);
445 } elsif ($d->{status} eq 'create') {
446 $table->set_row_link ("/logs/index.htm?cid=$cid&amp;veid=$veid");
447 $table->add_row ('', '', $veid, $d->{status}, '', '', '', '', '');
448 } else {
449 $table->add_row ('', '', $veid, $d->{status}, '', '', '', '', '');
450 }
451 }
452
453 return __("Node has no VMs") if !$found;
454
455 $out .= $ddown->out_dropdown_menu("menu${cid}_0");
456 $out .= $ddown->out_dropdown_menu("menu${cid}_1");
457 $out .= $ddown->out_dropdown_menu("menu${cid}_2");
458
459 $out .= $table->out_table ();
460
461 return $out;
462}
463
464sub create_vmops_table {
465 my ($vmops, $inactive) = @_;
466
467 my $table = PVE::HTMLTable->new ([]);
468
469 my $out = '';
470
471 my @header;
472
473 if ($inactive) {
474 @header = ('1', '120px', __('Command'),
475 '1', '200px', __('Start time'),
476 '1', '100px', __('User'),
477 '1', '100px', __('CID'),
478 '1', '100px', __('VMID'),
479 );
480 } else {
481 @header = ('1', '20px', '&nbsp;',
482 '1', '100px', __('Command'),
483 '1', '200px', __('Start time'),
484 '1', '100px', __('User'),
485 '1', '100px', __('CID'),
486 '1', '100px', __('VMID'),
487 );
488 }
489
490 $table->add_headline (\@header);
491
492 my $ddown = PVE::HTMLDropDown->new ();
493 $ddown->add_item ('menu0', "?action=stop", __('Stop'));
494
495 my $tlist;
496
497
498 PVE::Utils::foreach_vmrec ($vmops, sub {
499 my ($cid, $vmid, $d) = @_;
500
501 # command still running
502 my $running = PVE::Utils::check_process ($d->{pid}, $d->{pstart});
503
504 if (!$inactive && $running) {
505 my $menu = $ddown->out_symbol ('menu0', '', "&amp;cid=$cid&amp;veid=$vmid");
506 push @$tlist, {
507 cid => $cid,
508 veid => $vmid,
509 starttime => $d->{starttime},
510 menu => $menu,
511 command => $d->{command},
512 user => $d->{user},
513 };
514 } elsif ($inactive && !$running) {
515 push @$tlist, {
516 cid => $cid,
517 veid => $vmid,
518 starttime => $d->{starttime},
519 command => $d->{command},
520 user => $d->{user}
521 };
522 }
523 });
524
525 if (!$tlist) {
526 return __('Nothing to view');
527 } else {
528 foreach my $ref (sort {$b->{starttime} <=> $a->{starttime}} @$tlist) {
529 $table->set_row_link ("/logs/index.htm?cid=$ref->{cid}&amp;veid=$ref->{veid}");
530 my $ct = localtime ($ref->{starttime});
531 if ($inactive) {
532 $table->add_row ('', $ref->{command}, $ct, $ref->{user},
533 $ref->{cid}, $ref->{veid});
534 } else {
535 $table->add_row ('', $ref->{menu}, $ref->{command}, $ct,
536 $ref->{user}, $ref->{cid}, $ref->{veid});
537 }
538 }
539 }
540
541 $out .= $ddown->out_dropdown_menu("menu0");
542 $out .= $table->out_table ();
543
544 return $out;
545}
546
547sub html_table_ressource {
548 my ($table, $barwidth, $name, $max, $cur, $text) = @_;
549
550 my $rmax = defined ($max) ? $max : 1;
551
552 my $bar = defined ($max) ? create_bar ($barwidth, $rmax, $cur, $text) : '&nbsp';
553
554 my $maxtext = defined ($max) ? $max : "-";
555
556 $table->add_row ('', $name, $cur, $maxtext, $bar);
557}
558
559sub action_button {
560 my ($text, $action, $disabled) = @_;
561
562 my $dtext = $disabled ? 'disabled' : '';
563 my $loc = "?action=$action";
564 return "<button $dtext type=button onclick='location=\"$loc\"'>$text</button>";
565}
566
567sub href_button {
568 my ($text, $href, $disabled) = @_;
569
570 $href = '' if !defined ($href);
571
572 my $dtext = $disabled ? 'disabled' : '';
573 return "<button $dtext type=button onclick='location=\"$href\"'>$text</button>";
574}
575
576sub create_confirmframe {
577 my ($msg, $action, $href1, $href2) = @_;
578
579 my $html .= "<br><div align=center>$msg</div><br>";
580
581 my $b1 = PVE::HTMLUtils::href_button($action, $href1);
582 my $b2 = PVE::HTMLUtils::href_button(__("Cancel"), $href2 || '');
583
584 $html .= "<div align=center>$b1$b2</div><br>";
585
586 return create_statusframe (undef, __("Confirm"), undef, $html);
587}
588
589
590sub create_vmstatus {
591 my ($cid, $veid, $type, $vzinfo) = @_;
592
593 my $status = $vzinfo->{vzlist}->{"VEID_$veid"}->{status};
594 my $ip = $vzinfo->{vzlist}->{"VEID_$veid"}->{ip};
595 my $name = $vzinfo->{vzlist}->{"VEID_$veid"}->{name};
596
597 my $uptime = uptime_to_str ($vzinfo->{vzlist}->{"VEID_$veid"}->{uptime}, 1);
598
599 my $veconf = $vzinfo->{config};
600 my $ni = $vzinfo->{ni};
601 my $html = '';
602
603 my $pkglist = PVE::APLInfo::load_data();
604 my $tmpl = $veconf->{ostemplate}->{value};
605 my $pkginfo = $pkglist->{'all'}->{"$tmpl\.tar\.gz"};
606
607 my $manageurl;
608 if ($ip && (my $url = $pkginfo->{manageurl})) {
609 $manageurl = $url;
610 $manageurl =~ s/__IPADDRESS__/$ip/i;
611 }
612
613 my $vmops = PVE::Config::read_file ("vmops");
614
615 my $op;
616 if (defined($vmops->{"CID_$cid"}) && defined ($vmops->{"CID_$cid"}->{"VEID_$veid"})) {
617 my $d = $vmops->{"CID_$cid"}->{"VEID_$veid"};
618 if (PVE::Utils::check_process ($d->{pid}, $d->{pstart})) { # still running
619 $op = $d->{command};
620 }
621 }
622
623 my $grid = PVE::HTMLGrid->new ('fw1', 'fw2', "fw3to4:right");
624
625 my $cmds = "<div>";
626
627 if ($status eq 'running') {
628 $cmds .= action_button ($type eq 'openvz' ? __("Restart") : __("Reset"),
629 'restart', defined ($op));
630 } else {
631 $cmds .= action_button (__("Start"), 'start', defined ($op));
632 }
633 $cmds .= action_button (__("Shutdown"), 'shutdown', defined ($op) || ($status ne 'running'));
634 if ($status eq 'mounted') {
635 $cmds .= action_button (__("Unmount"), 'umount', defined ($op));
636 } else {
637 $cmds .= action_button (__("Stop"), 'stop', defined ($op) || ($status eq 'stopped'));
638 }
639 $cmds .= href_button (__("Remove"), '?confirmdestroy=1', defined ($op) || ($status ne 'stopped'));
640 $cmds .= "</div>";
641
642 $grid->add_row (__('Status') . ':',
643 "<b>" . (defined ($op) ? "executing task '$op'" : $status),
644 $cmds);
645
646 if ($type eq 'openvz') {
647 my $iptext;
648
649 if ($ip && $ip ne '-') {
650 if ($manageurl && ($status eq 'running')) {
651 $iptext = "<a class=cmd target=top href='$manageurl'>$ip</a>";
652 } else {
653 $iptext = $ip;
654 }
655 } else {
656 $iptext = __('unknown');
657 }
658
659
660 $grid->add_row (__('Hostname') . ':', $name,
661 $uptime eq '-' ? '' : __('Uptime') . ": $uptime");
662
663
664 my $clink;
665 if ($status ne 'stopped') {
666 my $href = "javascript:pve_openvz_console($cid, $veid)";
667 $clink .= "<a class=cmd href='$href'>" . __("Open VNC console") . "</a>";
668 }
669
670 $grid->add_row (__('IP Address') . ':', $iptext, $clink);
671 } else {
672 if ($uptime ne '-') {
673 $grid->add_row (undef, undef, __('Uptime') . ": $uptime");
674 }
675
676 if ($status ne 'stopped') {
677 my $href = "javascript:pve_qemu_console($cid, $veid)";
678 $grid->add_row (undef, undef, "<a class=cmd href='$href'>Open VNC console</a>");
679 }
680 }
681
682 $html .= $grid->html();
683
684 $html .= "<br><br>";
685
686 my $table = PVE::HTMLTable->new ([]);
687
688 my $barwidth = 300;
689 my $fw2 = int ((PVE::HTMLGrid::get_width ('fw') - $barwidth -
690 PVE::HTMLGrid::get_width ('fw1'))/2);
691
692 my @header = ('1', PVE::HTMLGrid::get_width ('fw1') . 'px', __('Resource'),
693 '1', "${fw2}px", __('Current'),
694 '1', "${fw2}px", __('Maximum'),
695 '1', "${barwidth}px", '&nbsp',
696 );
697 $table->add_headline (\@header);
698
699 my $relcpu = $vzinfo->{vzlist}->{"VEID_$veid"}->{relcpu};
700 html_table_ressource ($table, $barwidth, __('CPU Utilization') . ':', 100, $relcpu);
701
702 if ($type eq 'openvz') {
703 my $curmem = int ($vzinfo->{vzlist}->{"VEID_$veid"}->{mem});
704 my $maxmem = int ($vzinfo->{vzlist}->{"VEID_$veid"}->{maxmem});
705
706 html_table_ressource ($table, $barwidth, __("Memory/Swap") . ' (MB):', $maxmem, $curmem,
707 format_size ($curmem*1024*1024));
708 my $curdisk = sprintf ("%0.2f", $vzinfo->{vzlist}->{"VEID_$veid"}->{disk} / 1024);
709 my $maxdisk = sprintf ("%0.2f", $vzinfo->{vzlist}->{"VEID_$veid"}->{maxdisk} / 1024);
710 html_table_ressource ($table, $barwidth, __("Disk space") . ' (GB):', $maxdisk, $curdisk);
711 } else {
712 my $curmem = int ($vzinfo->{vzlist}->{"VEID_$veid"}->{mem});
713 my $maxmem = int ($vzinfo->{vzlist}->{"VEID_$veid"}->{maxmem});
714
715 html_table_ressource ($table, $barwidth, __("Memory") . ' (MB):', $maxmem, $curmem,
716 format_size ($curmem*1024*1024));
717 }
718
719
720 $html .= $table->out_table ();
721
722 return $html;
723}
724
725sub create_host_status {
726 my ($cinfo, $status, $verbose) = @_;
727
728 my @cellwidth = ('290px', '450px');
729
730 my $table = PVE::HTMLTable->new (\@cellwidth);
731
732 $table->add_row ('', __("Uptime"), $status->{uptime}->{uptimestr});
733
734 $table->add_row ('', "CPU(s)", "$status->{cpuinfo}->{cpus} x $status->{cpuinfo}->{model}");
735
736 my $stat = create_bar (300, 1, $status->{cpu});
737 $table->add_row ('', __('CPU Utilization'), $stat);
738
739 my $iowait = create_bar (300, 1, $status->{wait});
740 $table->add_row ('', __('IO Delays'), $iowait);
741
742 my $f1 = format_size ($status->{meminfo}->{mbmemtotal}*1024*1024);
743 my $f2 = format_size ($status->{meminfo}->{mbmemused}*1024*1024);
744 my $txt = __("Physical Memory") . " ($f1/$f2)";
745 $stat = create_bar (300, $status->{meminfo}->{mbmemtotal},
746 $status->{meminfo}->{mbmemused},
747 format_size ($status->{meminfo}->{mbmemused}*1024*1024));
748 $table->add_row ('', $txt, $stat);
749
750 if ($status->{meminfo}->{mbswaptotal}) {
751
752 $f1 = format_size ($status->{meminfo}->{mbswaptotal}*1024*1024);
753 $f2 = format_size ($status->{meminfo}->{mbswapused}*1024*1024);
754 $txt = __("Swap Space") . " ($f1/$f2)";
755 $stat = create_bar (300, $status->{meminfo}->{mbswaptotal},
756 $status->{meminfo}->{mbswapused},
757 format_size ($status->{meminfo}->{mbswapused}*1024*1024));
758 $table->add_row ('', $txt, $stat);
759 }
760
761 $f1 = format_size ($status->{hdinfo}->{root}->{total}*1024*1024);
762 $f2 = format_size ($status->{hdinfo}->{root}->{used}*1024*1024);
763 $txt = __("HD Space root") . " ($f1/$f2)";
764 $stat = create_bar (300, $status->{hdinfo}->{root}->{avail},
765 $status->{hdinfo}->{root}->{used});
766 $table->add_row ('', $txt, $stat);
767
768 $table->add_row ('', __("Version") . " (package/version/build)", $status->{cpuinfo}->{proxversion});
769
770 $table->add_row ('', __("Kernel Version"), $status->{cpuinfo}->{kversion});
771
772 my $out = $table->out_table();
773
774 return $out if !$verbose || (scalar (@{$cinfo->{nodes}}) <= 1);
775
776 $out .= "<br>";
777
778 $table = PVE::HTMLTable->new ([]);
779
780 my @header_sync = ('1', '150px', __('Synchronized Nodes'),
781 '1', '150px', __('IP Address'),
782 '1', '100px', __('Sync Status'),
783 '1', '200px', __('Last succesfull sync'),
784 '1', '100px', __('Delay (minutes)'),
785 );
786 $table->add_headline (\@header_sync);
787
788 foreach my $ni (@{$cinfo->{nodes}}) {
789 my $lastsync = $status->{"lastsync_$ni->{cid}"};
790
791 $table->set_row_link ("/cluster/index.htm?cid=$ni->{cid}");
792
793 my $diff;
794 if (defined ($lastsync)) {
795 $diff = time() - $lastsync;
796 $diff = 0 if $diff < 0;
797 } else {
798 $table->add_row ('', $ni->{name}, $ni->{ip}, '-', '-', '-');
799 next;
800 }
801 my $sstatus = 'OK';
802 my $dstatus = '-';
803
804 if ($diff > (60*3)) {
805 $sstatus = '<blink><font color=red>nosync</font></blink>';
806 $dstatus = int (($diff + 59)/60);
807 }
808
809 my $synctime = localtime ($lastsync);
810
811 $table->add_row ('', $ni->{name}, $ni->{ip}, $sstatus, $synctime, $dstatus);
812 }
813
814 $out .= $table->out_table();
815
816 return $out;
817}
818
819sub create_cluster_status {
820 my ($cinfo) = @_;
821
822 my $out = '';
823
824 my $table = PVE::HTMLTable->new ([]);
825
826 my @header = ('1', '100px', __('Hostname'),
827 '1', '100px', __('IP Address'),
828 '1', '50px', __('Role'),
829 '1', '50px', __('State'),
830 '1', '100px', __('Uptime'),
831 '1', '60px', 'Load',
832 '1', '60px', 'CPU',
833 '1', '60px', 'IODelay',
834 '1', '60px', 'Memory',
835 '1', '60px', 'Disk',
836 );
837
838 $table->add_headline (\@header);
839
840 foreach my $ni (@{$cinfo->{nodes}}) {
841 my $role = cluster_format_role ($ni->{role});
842 $table->set_row_link ("/cluster/index.htm?cid=$ni->{cid}");
843 $table->set_col_span ([1,1,1,7]);
844 $table->add_row ("rowcid$ni->{cid}", $ni->{name}, $ni->{ip}, $role, '');
845 }
846 $out .= $table->out_table ();
847
848 foreach my $ni (@{$cinfo->{nodes}}) {
849 $out .= PVE::HTMLControls::create_periodic_updater ("rowcid$ni->{cid}",
850 '/ws/status_update',
851 { cid => $ni->{cid} }, 5);
852 }
853
854 return $out;
855}
856
857sub create_pkginfo_frame {
858 my ($d, $download) = @_;
859
860 my $html = '<table>';
861
862 $html .= "<tr><td width=100>Description:</td><td width=645><b>$d->{headline}</td>";
863 $html .= "<tr><td><td style='white-space:normal;'>$d->{description}</td>" if $d->{description};
864 $html .= "<tr><td colspan=2><hr></tr>";
865 $html .= "<tr><td>Information:</td><td><a target=top href='$d->{infopage}'>$d->{infopage}</a></td>";
866
867 #$html .= "<tr><td>Appliance:</td><td>$d->{package}</td>";
868 $html .= "<tr><td>Version:</td><td>$d->{version}</td>";
869 $html .= "<tr><td>Section:</td><td>$d->{section}</td>";
870 #$html .= "<tr><td>OS:</td><td>$d->{os}</td>"; # already displayed with filename
871
872 if ($d->{maintainer} =~ m/^\s*(.*\S)\s*\<(\S+\@\S+)\>\s*$/) {
873 $html .= "<tr><td>Maintainer:</td><td>$1 <a href='mailto:$2'>&lt;$2&gt;</a></td>";
874 }
875
876 $html .= "<tr><td>Filename:</td><td>$d->{template}</td>";
877 $html .= "<tr><td>MD5SUM:</td><td>$d->{md5sum}</td>";
878
879
880 $html .= "<tr><td colspan=2><tr><td colspan=2>";
881
882 if ($download) {
883 $html .= "<tr><td><td><a class=cmd href='?action=download&amp;aa=$d->{template}'>start download</a>";
884 }
885
886 $html .= "</table>";
887
888 return PVE::HTMLUtils::create_statusframe ('', "Template Information for appliance '$d->{package}'", $d->{type}, $html);
889}
890
891sub storage_format_volume_list {
892 my ($cfg, $vdisks) = @_;
893
894 my $res = [];
895
896 return $res if !$vdisks;
897
898 PVE::Storage::foreach_volid ($vdisks, sub {
899 my ($volid, $sid, $volname, $info) = @_;
900
901 my $scfg = PVE::Storage::storage_config ($cfg, $sid);
902
903 # skip used volumes
904 return if PVE::Storage::volume_is_used ($cfg, $volid);
905
906 my $stype = $scfg->{type};
907 if ($stype eq 'iscsi') {
908 my $size = int ($info->{size} / (1024 *1024));
909 push @$res, [ $volid, sprintf "CH %02d ID %d LUN %d ($size GB)",
910 $info->{channel}, $info->{id}, $info->{lun} ];
911 } else {
912 push @$res, [ $volid, $volname];
913 }
914 });
915
916 return $res;
917}
918
919sub storage_format_volume_list_iscsi {
920 my ($cfg, $vdisks) = @_;
921
922 my $res = {
923 titles => [ 'CH', 'ID', 'LUN', 'Size (GB)', 'VolumeID' ],
924 values => [],
925 };
926
927 return $res if !$vdisks;
928
929 PVE::Storage::foreach_volid ($vdisks, sub {
930 my ($volid, $sid, $volname, $info) = @_;
931 my $scfg = PVE::Storage::storage_config ($cfg, $sid);
932
933 # skip used volumes
934 return if PVE::Storage::volume_is_used ($cfg, $volid);
935
936 my $stype = $scfg->{type};
937 if ($stype eq 'iscsi') {
938 my $size = int ($info->{size} / (1024 *1024));
939 my $short = sprintf "CH %02d ID %d LUN %d ($size GB)", $info->{channel},
940 $info->{id}, $info->{lun}, $size;
941 push @{$res->{values}}, [ $volid, $short,
942 $info->{channel}, $info->{id}, $info->{lun},
943 $size, $volid ];
944 } else {
945 die "wrong storage type";
946 }
947 });
948
949 return $res;
950}
951
952sub storage_format_storage_list {
953 my ($stinfo, $sel) = @_;
954
955 my $res = {
956 titles => [ 'Storage', 'Type', 'Used (GB)', 'Capacity (GB)', '&nbsp;' ],
957 values => [],
958 };
959
960 return $res if !$stinfo;
961
962 my $cfg = $stinfo->{cfg};
963
964 $sel = 'images' if !$sel;
965
966 foreach my $sid (sort keys %{$stinfo->{$sel}}) {
967 my $scfg = PVE::Storage::storage_config ($cfg, $sid);
968
969 my $used;
970 my $avail;
971 my $diskbar;
972
973 if ($scfg->{type} eq 'iscsi') {
974 $used = $avail = "n/a";
975 $diskbar = '';
976 } else {
977 my $d = $stinfo->{info}->{$sid};
978 $used = int ($d->{used} / (1024*1024));
979 $avail = int ($d->{avail} / (1024*1024));
980 $diskbar = create_bar (200, $d->{avail} , $d->{used});
981 }
982
983 push @{$res->{values}}, [ $sid, "$sid ($scfg->{type})", $sid, $scfg->{type}, $used, $avail, $diskbar ];
984 }
985
986 return $res;
987}
988
989sub storage_format_iso_list {
990 my ($cfg, $tlist) = @_;
991
992 my $res = [];
993
994 return $res if !$tlist;
995
996 PVE::Storage::foreach_volid ($tlist, sub {
997 my ($volid, $sid, $volname, $info) = @_;
998 my (undef, $name) = PVE::Storage::parse_volname_dir ($volname);
999 push @$res, [$volid, $name];
1000 });
1001
1002 return $res;
1003}
1004
1005sub check_vztmpl_name {
1006 my ($name, $noerr) = @_;
1007
1008 if ($name =~ m/^([^-]+-[^-]+)-([^_]+)_([^_]+)\_(i386|amd64)\.tar\.gz$/) {
1009 return [$1, $2, $3, $4];
1010 }
1011
1012 return undef if $noerr;
1013
1014 die sprintf __("name '%s' does not conform to template naming scheme") .
1015 " (<OS>-<OSVERSION>-<NAME>_<VERSION>_(i386|amd64).tar.gz)\n", $name;
1016}
1017
1018sub storage_format_vztmpl_list {
1019 my ($cfg, $tlist) = @_;
1020
1021 my $default;
1022
1023 my $res = {
1024 titles => [__('OS'), __('Name'), __('Version'), __('Arch.')],
1025 values => [],
1026 };
1027
1028 return $res if !$tlist;
1029
1030 PVE::Storage::foreach_volid ($tlist, sub {
1031 my ($volid, $sid, $volname, $info) = @_;
1032 my (undef, $name) = PVE::Storage::parse_volname_dir ($volname);
1033
1034 $default = $volid if !$default && $volid =~ m|^local:vztmpl/debian-5.0-standard|;
1035
1036 if (my $td = check_vztmpl_name ($name, 1)) {
1037 push @{$res->{values}}, [$volid, $name, @$td];
1038 }
1039 });
1040
1041 return wantarray ? ($res, $default) : $res;
1042}
1043
1044sub storage_format_vgs_list {
1045 my ($cfg, $tlist) = @_;
1046
1047 my $res = [];
1048
1049 return $res if !$tlist;
1050
1051 foreach my $vgname (sort keys %$tlist) {
1052
1053 # skip used groups
1054 next if PVE::Storage::vgroup_is_used ($cfg, $vgname);
1055
1056 my $size = int ($tlist->{$vgname}->{size}/(1024*1024));
1057 push @$res, [$vgname, "$vgname (${size} GB)"];
1058 }
1059
1060 if (!scalar(@$res)) {
1061 push @$res, [ '', "Found no volume groups"];
1062 }
1063
1064 return $res;
1065}
1066
1067sub cluster_format_cid_list {
1068 my ($cinfo, $exclude) = @_;
1069
1070 my $res = {
1071 titles => [__('Name'), __('IP Address'), __('Role'), 'CID'],
1072 values => [],
1073 };
1074
1075 return $res if !$cinfo;
1076
1077 foreach my $ni (@{$cinfo->{nodes}}) {
1078
1079 next if defined ($exclude) && ($exclude eq $ni->{cid});
1080
1081 my $role = cluster_format_role ($ni->{role});
1082 push @{$res->{values}}, [$ni->{cid}, "$ni->{name} ($ni->{ip})",
1083 $ni->{name}, $ni->{ip}, $role, $ni->{cid}];
1084 }
1085
1086 return $res;
1087}
1088
1089sub cluster_format_vmid_list {
1090 my ($vzl) = @_;
1091
1092 my $res = {
1093 titles => ['VMID', __('Name'), __('Status') ],
1094 values => [],
1095 default => '-',
1096 };
1097
1098 return $res if !$vzl;
1099
1100 PVE::Utils::foreach_veid_sorted ($vzl, sub {
1101 my ($veid, $d) = @_;
1102 push @{$res->{values}}, [$veid, "VM $veid ($d->{name})", $veid,
1103 $d->{name}, $d->{status}];
1104 });
1105
1106 return $res;
1107}
1108
1109sub cluster_format_role {
1110 my $role = shift;
1111
1112 $role = __('Master') if $role eq 'M';
1113 $role = __('Node') if $role eq 'N';
1114
1115 return $role;
1116}
1117
11181;
1119