1 package PVE
::HTMLUtils
;
5 use vars
qw(@ISA @EXPORT);
12 use PVE
::HTMLControls
;
18 @EXPORT = qw(check_field check_range check_write_mode);
21 # useful for debugging
25 return "<pre>" . Dumper
($v) . "</pre>";
31 my $kb = $size / 1024;
34 return int ($kb) . "KB";
37 my $mb = $size / (1024*1024);
40 return int ($mb) . "MB";
43 return sprintf ("%.2fGB", $gb);
47 # HTML encode/decode text to store in config files (single line encoding)
48 sub encode_description
{
51 $desc = encode_entities
($desc);
53 $desc =~ s
|\r?
\n|<br
>|gm
;
58 sub decode_description
{
63 return decode_entities
($desc);
69 my ($name, $value, $min, $max) = @_;
71 if ($min && ($value < $min)) {
72 die sprintf(__
("Field '%s' is below minimum ($value < $min)") . "\n", $name);
74 if ($max && ($value > $max)) {
75 die sprintf(__
("Field '%s' is above maximum ($value > $max)") . "\n", $name);
80 my ($name, $value, @checks) = @_;
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);
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);
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);
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);
110 $value = inet_ntoa
($packed_ip);
111 } elsif ($c eq 'PORTAL') { # resolves iscsi portal name
113 if ($value =~ m/^([^:]+)(:(\d+))?$/) {
117 my $packed_ip = gethostbyname($server);
118 if (defined $packed_ip) {
119 $server = inet_ntoa
($packed_ip);
120 $value = $port ?
"$server:$port" : $server;
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/;
130 die "unimplemente check '$c' - internal error";
140 return __
('You do not have write access.') if $id eq 'nowr';
142 return __
('This information is only available on the master node.') if $id eq 'infoatmaster';
144 return __
("Are you sure you want to remove VM %s? This will permanently erase all VM data.") if $id eq 'confirm_remove';
148 sub check_write_mode
{
152 die msg
('nowr') . "\n";
157 my ($uri, %args, %mod) = @_;
161 $args{action
} = undef if !defined ($args{action
});
162 $args{aa
} = undef if !defined ($args{aa
});
164 foreach my $p (keys (%args)) {
165 next if defined ($mod{$p}) || !defined ($args{$p});
166 $qstring .= $qstring ?
"&" : "?";
167 $qstring .= "$p=$args{$p}";
169 foreach my $p (keys (%mod)) {
170 $qstring .= $qstring ?
"&" : "?";
171 $qstring .= "$p=$mod{$p}";
174 return $uri . $qstring;
179 return () unless defined $string and $string;
183 s/%([0-9a-fA-F]{2})/pack("C",hex($1))/ge;
185 } split /[=&;]/, $string, -1;
188 # |----------------------------|
189 # |<b>$title</b>: $msg |
190 # |----------------------------|
191 sub 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>";
200 # |----------------------------|
202 # |----------------------------|
204 # |----------------------------|
206 sub create_statusframe
{
207 my ($id, $left, $right, $content, $height) = @_;
209 $left = ' ' if !$left;
210 $right = ' ' if !$right;
212 my $idtxt = $id ?
"id='$id'" : '';
213 my $idtxtleft = $id ?
"id='${id}left'" : '';
214 my $idtxtright = $id ?
"id='${id}right'" : '';
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>";
219 $out .= "<td $idtxtright align=right>$right</td></tr></table>";
221 my $hs = $height ?
"height:${height}px;" : '';
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>";
229 sub create_vmops_frame
{
230 my ($vmid, $upid) = @_;
234 my $filename = "/tmp/vmops-$vmid.out";
235 if (my $fh = IO
::File-
>new ($filename, "r")) {
245 my $href = "javascript:command_abort(\"$upid\");";
246 my $abort = "<a
class='frmsubmit' id
='abortbutton' href
='$href'></a
>";
248 $out .= create_statusframe ('logview', undef, $abort, undef, 450, 1);
249 $out .= PVE::HTMLControls::create_command_viewer ('logview', 'logviewleft', 'abortbutton', $upid);
251 $out .= __("Nothing to view
");
257 sub create_apldownload_frame {
258 my ($userid, $upid) = @_;
261 my $filename = "/tmp/apldownload
-$userid.out
";
262 if (my $fh = IO::File->new ($filename, "r
")) {
272 my $href = "javascript
:command_abort
(\"$upid\");";
273 my $abort = "<a class='frmsubmit' id='abortbutton' href='$href'></a>";
275 $out .= create_statusframe
('logview', undef, $abort, undef, 100);
276 $out .= PVE
::HTMLControls
::create_command_viewer
('logview', 'logviewleft', 'abortbutton', $upid);
278 $out .= __
("Nothing to view");
285 my ($width, $abs, $rel) = @_;
287 my $dvrel = $rel > 100 ?
100 : $rel;
288 my $dvabs = $abs > 100 ?
100 : $abs;
290 my $hwidth1 = sprintf ("%dpx", $width);
291 my $hwidth2 = sprintf ("%dpx", int (($width * $dvrel)/100));
292 my $hwidth3 = sprintf ("%dpx", int (($width * $dvabs)/100));
294 my $per = sprintf ("%0.2f%", $rel);
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>";
300 my ($width, $max, $value, $text) = @_;
302 if (!$max || ($max <= 0)) {
307 my $dv = $value > $max ?
$max : $value;
309 my $hwidth1 = sprintf ("%dpx", $width);
311 my $hwidth2 = sprintf ("%dpx", int (($width * $dv)/$max));
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>";
318 my ($ut, $long) = @_;
325 my $days = int ($ut / 86400);
327 my $hours = int ($ut / 3600);
329 my $mins = int ($ut / 60);
333 my $ds = $days > 1 ? __
('days') : __
('day');
334 return sprintf "%d $ds %02d:%02d:%02d", $days, $hours, $mins, $ut;
336 return sprintf "%02d:%02d:%02d", $hours, $mins, $ut;
342 } elsif ($ut < 3600) {
343 my $mins = int ($ut / 60);
345 } elsif ($ut < 86400) {
346 my $hours = int ($ut / 3600);
349 my $days = int ($ut / 86400);
354 sub create_vzlist_table
{
355 my ($cid, $vzlist) = @_;
357 my $table = PVE
::HTMLTable-
>new ([]);
361 my @header = ('1', '20px', ' ',
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'),
371 $table->add_headline (\
@header);
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'));
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'));
384 $ddown->add_item ("menu${cid}_2", "?action=start", __
('Start'));
385 $ddown->add_item ("menu${cid}_2", "?action=umount", __
('Unmount'));
389 foreach my $vkey (sort keys %$vzlist) {
390 next if $vkey !~ m/^VEID_(\d+)$/;
392 my $d = $vzlist->{$vkey};
396 my $type = $d->{type
};
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') {
402 my $mlabel = "menu${cid}_1";
404 if ($d->{status
} eq 'stopped') {
405 $mlabel = "menu${cid}_0";
406 } elsif ($d->{status
} eq 'mounted') {
407 $mlabel = "menu${cid}_2";
410 my $menu = $ddown->out_symbol ($mlabel, '', "&cid=$cid&veid=$veid&type=$type");
412 $table->set_row_link ("/$type/$cid-$veid/index.htm");
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));
422 if ($d->{status
} ne 'stopped') {
423 $diskbar = create_bar
(100, $d->{maxdisk
}, $d->{disk
},
424 format_size
($d->{disk
}*1024*1024));
426 my $ds = format_size
($d->{maxdisk
}*1024*1024);
427 $diskbar = "<div width=100 align=right>$ds</div>";
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>";
436 # add soft hyphenation (in case someone has a very long hostname)
437 $d->{name
} =~ s/\./\.­/g;
439 if ($d->{status
} eq 'stopped' || $d->{status
} eq 'mounted') {
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&veid=$veid");
447 $table->add_row ('', '', $veid, $d->{status
}, '', '', '', '', '');
449 $table->add_row ('', '', $veid, $d->{status
}, '', '', '', '', '');
453 return __
("Node has no VMs") if !$found;
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");
459 $out .= $table->out_table ();
464 sub create_vmops_table
{
465 my ($vmops, $inactive) = @_;
467 my $table = PVE
::HTMLTable-
>new ([]);
474 @header = ('1', '120px', __
('Command'),
475 '1', '200px', __
('Start time'),
476 '1', '100px', __
('User'),
477 '1', '100px', __
('CID'),
478 '1', '100px', __
('VMID'),
481 @header = ('1', '20px', ' ',
482 '1', '100px', __
('Command'),
483 '1', '200px', __
('Start time'),
484 '1', '100px', __
('User'),
485 '1', '100px', __
('CID'),
486 '1', '100px', __
('VMID'),
490 $table->add_headline (\
@header);
492 my $ddown = PVE
::HTMLDropDown-
>new ();
493 $ddown->add_item ('menu0', "?action=stop", __
('Stop'));
498 PVE
::Utils
::foreach_vmrec
($vmops, sub {
499 my ($cid, $vmid, $d) = @_;
501 # command still running
502 my $running = PVE
::Utils
::check_process
($d->{pid
}, $d->{pstart
});
504 if (!$inactive && $running) {
505 my $menu = $ddown->out_symbol ('menu0', '', "&cid=$cid&veid=$vmid");
509 starttime
=> $d->{starttime
},
511 command
=> $d->{command
},
514 } elsif ($inactive && !$running) {
518 starttime
=> $d->{starttime
},
519 command
=> $d->{command
},
526 return __
('Nothing to view');
528 foreach my $ref (sort {$b->{starttime
} <=> $a->{starttime
}} @$tlist) {
529 $table->set_row_link ("/logs/index.htm?cid=$ref->{cid}&veid=$ref->{veid}");
530 my $ct = localtime ($ref->{starttime
});
532 $table->add_row ('', $ref->{command
}, $ct, $ref->{user
},
533 $ref->{cid
}, $ref->{veid
});
535 $table->add_row ('', $ref->{menu
}, $ref->{command
}, $ct,
536 $ref->{user
}, $ref->{cid
}, $ref->{veid
});
541 $out .= $ddown->out_dropdown_menu("menu0");
542 $out .= $table->out_table ();
547 sub html_table_ressource
{
548 my ($table, $barwidth, $name, $max, $cur, $text) = @_;
550 my $rmax = defined ($max) ?
$max : 1;
552 my $bar = defined ($max) ? create_bar
($barwidth, $rmax, $cur, $text) : ' ';
554 my $maxtext = defined ($max) ?
$max : "-";
556 $table->add_row ('', $name, $cur, $maxtext, $bar);
560 my ($text, $action, $disabled) = @_;
562 my $dtext = $disabled ?
'disabled' : '';
563 my $loc = "?action=$action";
564 return "<button $dtext type=button onclick='location=\"$loc\"'>$text</button>";
568 my ($text, $href, $disabled) = @_;
570 $href = '' if !defined ($href);
572 my $dtext = $disabled ? 'disabled
' : '';
573 return "<button $dtext type=button onclick='location
=\"$href\"'>$text</button>";
576 sub create_confirmframe
{
577 my ($msg, $action, $href1, $href2) = @_;
579 my $html .= "<br><div align=center>$msg</div><br>";
581 my $b1 = PVE
::HTMLUtils
::href_button
($action, $href1);
582 my $b2 = PVE
::HTMLUtils
::href_button
(__
("Cancel"), $href2 || '');
584 $html .= "<div align=center>$b1$b2</div><br>";
586 return create_statusframe
(undef, __
("Confirm"), undef, $html);
590 sub create_vmstatus
{
591 my ($cid, $veid, $type, $vzinfo) = @_;
593 my $status = $vzinfo->{vzlist
}->{"VEID_$veid"}->{status
};
594 my $ip = $vzinfo->{vzlist
}->{"VEID_$veid"}->{ip
};
595 my $name = $vzinfo->{vzlist
}->{"VEID_$veid"}->{name
};
597 my $uptime = uptime_to_str
($vzinfo->{vzlist
}->{"VEID_$veid"}->{uptime
}, 1);
599 my $veconf = $vzinfo->{config
};
600 my $ni = $vzinfo->{ni
};
603 my $pkglist = PVE
::APLInfo
::load_data
();
604 my $tmpl = $veconf->{ostemplate
}->{value
};
605 my $pkginfo = $pkglist->{'all'}->{"$tmpl\.tar\.gz"};
608 if ($ip && (my $url = $pkginfo->{manageurl
})) {
610 $manageurl =~ s/__IPADDRESS__/$ip/i;
613 my $vmops = PVE
::Config
::read_file
("vmops");
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
623 my $grid = PVE
::HTMLGrid-
>new ('fw1', 'fw2', "fw3to4:right");
627 if ($status eq 'running') {
628 $cmds .= action_button
($type eq 'openvz' ? __
("Restart") : __
("Reset"),
629 'restart', defined ($op));
631 $cmds .= action_button
(__
("Start"), 'start', defined ($op));
633 $cmds .= action_button
(__
("Shutdown"), 'shutdown', defined ($op) || ($status ne 'running'));
634 if ($status eq 'mounted') {
635 $cmds .= action_button
(__
("Unmount"), 'umount', defined ($op));
637 $cmds .= action_button
(__
("Stop"), 'stop', defined ($op) || ($status eq 'stopped'));
639 $cmds .= href_button
(__
("Remove"), '?confirmdestroy=1', defined ($op) || ($status ne 'stopped'));
642 $grid->add_row (__
('Status') . ':',
643 "<b>" . (defined ($op) ?
"executing task '$op'" : $status),
646 if ($type eq 'openvz') {
649 if ($ip && $ip ne '-') {
650 if ($manageurl && ($status eq 'running')) {
651 $iptext = "<a class=cmd target=top href='$manageurl'>$ip</a>";
656 $iptext = __
('unknown');
660 $grid->add_row (__
('Hostname') . ':', $name,
661 $uptime eq '-' ?
'' : __
('Uptime') . ": $uptime");
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>";
670 $grid->add_row (__
('IP Address') . ':', $iptext, $clink);
672 if ($uptime ne '-') {
673 $grid->add_row (undef, undef, __
('Uptime') . ": $uptime");
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>");
682 $html .= $grid->html();
686 my $table = PVE
::HTMLTable-
>new ([]);
689 my $fw2 = int ((PVE
::HTMLGrid
::get_width
('fw') - $barwidth -
690 PVE
::HTMLGrid
::get_width
('fw1'))/2);
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", ' ',
697 $table->add_headline (\
@header);
699 my $relcpu = $vzinfo->{vzlist
}->{"VEID_$veid"}->{relcpu
};
700 html_table_ressource
($table, $barwidth, __
('CPU Utilization') . ':', 100, $relcpu);
702 if ($type eq 'openvz') {
703 my $curmem = int ($vzinfo->{vzlist
}->{"VEID_$veid"}->{mem
});
704 my $maxmem = int ($vzinfo->{vzlist
}->{"VEID_$veid"}->{maxmem
});
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);
712 my $curmem = int ($vzinfo->{vzlist
}->{"VEID_$veid"}->{mem
});
713 my $maxmem = int ($vzinfo->{vzlist
}->{"VEID_$veid"}->{maxmem
});
715 html_table_ressource
($table, $barwidth, __
("Memory") . ' (MB):', $maxmem, $curmem,
716 format_size
($curmem*1024*1024));
720 $html .= $table->out_table ();
725 sub create_host_status
{
726 my ($cinfo, $status, $verbose) = @_;
728 my @cellwidth = ('290px', '450px');
730 my $table = PVE
::HTMLTable-
>new (\
@cellwidth);
732 $table->add_row ('', __
("Uptime"), $status->{uptime
}->{uptimestr
});
734 $table->add_row ('', "CPU(s)", "$status->{cpuinfo}->{cpus} x $status->{cpuinfo}->{model}");
736 my $stat = create_bar
(300, 1, $status->{cpu
});
737 $table->add_row ('', __
('CPU Utilization'), $stat);
739 my $iowait = create_bar
(300, 1, $status->{wait});
740 $table->add_row ('', __
('IO Delays'), $iowait);
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);
750 if ($status->{meminfo
}->{mbswaptotal
}) {
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);
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);
768 $table->add_row ('', __
("Version") . " (package/version/build)", $status->{cpuinfo
}->{proxversion
});
770 $table->add_row ('', __
("Kernel Version"), $status->{cpuinfo
}->{kversion
});
772 my $out = $table->out_table();
774 return $out if !$verbose || (scalar (@{$cinfo->{nodes
}}) <= 1);
778 $table = PVE
::HTMLTable-
>new ([]);
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)'),
786 $table->add_headline (\
@header_sync);
788 foreach my $ni (@{$cinfo->{nodes
}}) {
789 my $lastsync = $status->{"lastsync_$ni->{cid}"};
791 $table->set_row_link ("/cluster/index.htm?cid=$ni->{cid}");
794 if (defined ($lastsync)) {
795 $diff = time() - $lastsync;
796 $diff = 0 if $diff < 0;
798 $table->add_row ('', $ni->{name
}, $ni->{ip
}, '-', '-', '-');
804 if ($diff > (60*3)) {
805 $sstatus = '<blink><font color=red>nosync</font></blink>';
806 $dstatus = int (($diff + 59)/60);
809 my $synctime = localtime ($lastsync);
811 $table->add_row ('', $ni->{name
}, $ni->{ip
}, $sstatus, $synctime, $dstatus);
814 $out .= $table->out_table();
819 sub create_cluster_status
{
824 my $table = PVE
::HTMLTable-
>new ([]);
826 my @header = ('1', '100px', __
('Hostname'),
827 '1', '100px', __
('IP Address'),
828 '1', '50px', __
('Role'),
829 '1', '50px', __
('State'),
830 '1', '100px', __
('Uptime'),
833 '1', '60px', 'IODelay',
834 '1', '60px', 'Memory',
838 $table->add_headline (\
@header);
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, '');
846 $out .= $table->out_table ();
848 foreach my $ni (@{$cinfo->{nodes
}}) {
849 $out .= PVE
::HTMLControls
::create_periodic_updater
("rowcid$ni->{cid}",
851 { cid
=> $ni->{cid
} }, 5);
857 sub create_pkginfo_frame
{
858 my ($d, $download) = @_;
860 my $html = '<table>';
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>";
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
872 if ($d->{maintainer
} =~ m/^\s*(.*\S)\s*\<(\S+\@\S+)\>\s*$/) {
873 $html .= "<tr><td>Maintainer:</td><td>$1 <a href='mailto:$2'><$2></a></td>";
876 $html .= "<tr><td>Filename:</td><td>$d->{template}</td>";
877 $html .= "<tr><td>MD5SUM:</td><td>$d->{md5sum}</td>";
880 $html .= "<tr><td colspan=2><tr><td colspan=2>";
883 $html .= "<tr><td><td><a class=cmd href='?action=download&aa=$d->{template}'>start download</a>";
888 return PVE
::HTMLUtils
::create_statusframe
('', "Template Information for appliance '$d->{package}'", $d->{type
}, $html);
891 sub storage_format_volume_list
{
892 my ($cfg, $vdisks) = @_;
896 return $res if !$vdisks;
898 PVE
::Storage
::foreach_volid
($vdisks, sub {
899 my ($volid, $sid, $volname, $info) = @_;
901 my $scfg = PVE
::Storage
::storage_config
($cfg, $sid);
904 return if PVE
::Storage
::volume_is_used
($cfg, $volid);
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
} ];
912 push @$res, [ $volid, $volname];
919 sub storage_format_volume_list_iscsi
{
920 my ($cfg, $vdisks) = @_;
923 titles
=> [ 'CH', 'ID', 'LUN', 'Size (GB)', 'VolumeID' ],
927 return $res if !$vdisks;
929 PVE
::Storage
::foreach_volid
($vdisks, sub {
930 my ($volid, $sid, $volname, $info) = @_;
931 my $scfg = PVE
::Storage
::storage_config
($cfg, $sid);
934 return if PVE
::Storage
::volume_is_used
($cfg, $volid);
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
},
945 die "wrong storage type";
952 sub storage_format_storage_list
{
953 my ($stinfo, $sel) = @_;
956 titles
=> [ 'Storage', 'Type', 'Used (GB)', 'Capacity (GB)', ' ' ],
960 return $res if !$stinfo;
962 my $cfg = $stinfo->{cfg
};
964 $sel = 'images' if !$sel;
966 foreach my $sid (sort keys %{$stinfo->{$sel}}) {
967 my $scfg = PVE
::Storage
::storage_config
($cfg, $sid);
973 if ($scfg->{type
} eq 'iscsi') {
974 $used = $avail = "n/a";
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
});
983 push @{$res->{values}}, [ $sid, "$sid ($scfg->{type})", $sid, $scfg->{type
}, $used, $avail, $diskbar ];
989 sub storage_format_iso_list
{
990 my ($cfg, $tlist) = @_;
994 return $res if !$tlist;
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];
1005 sub check_vztmpl_name
{
1006 my ($name, $noerr) = @_;
1008 if ($name =~ m/^([^-]+-[^-]+)-([^_]+)_([^_]+)\_(i386|amd64)\.tar\.gz$/) {
1009 return [$1, $2, $3, $4];
1012 return undef if $noerr;
1014 die sprintf __
("name '%s' does not conform to template naming scheme") .
1015 " (<OS>-<OSVERSION>-<NAME>_<VERSION>_(i386|amd64).tar.gz)\n", $name;
1018 sub storage_format_vztmpl_list
{
1019 my ($cfg, $tlist) = @_;
1024 titles
=> [__
('OS'), __
('Name'), __
('Version'), __
('Arch.')],
1028 return $res if !$tlist;
1030 PVE
::Storage
::foreach_volid
($tlist, sub {
1031 my ($volid, $sid, $volname, $info) = @_;
1032 my (undef, $name) = PVE
::Storage
::parse_volname_dir
($volname);
1034 $default = $volid if !$default && $volid =~ m
|^local:vztmpl
/debian-5
.0
-standard
|;
1036 if (my $td = check_vztmpl_name
($name, 1)) {
1037 push @{$res->{values}}, [$volid, $name, @$td];
1041 return wantarray ?
($res, $default) : $res;
1044 sub storage_format_vgs_list
{
1045 my ($cfg, $tlist) = @_;
1049 return $res if !$tlist;
1051 foreach my $vgname (sort keys %$tlist) {
1054 next if PVE
::Storage
::vgroup_is_used
($cfg, $vgname);
1056 my $size = int ($tlist->{$vgname}->{size
}/(1024*1024));
1057 push @$res, [$vgname, "$vgname (${size} GB)"];
1060 if (!scalar(@$res)) {
1061 push @$res, [ '', "Found no volume groups"];
1067 sub cluster_format_cid_list
{
1068 my ($cinfo, $exclude) = @_;
1071 titles
=> [__
('Name'), __
('IP Address'), __
('Role'), 'CID'],
1075 return $res if !$cinfo;
1077 foreach my $ni (@{$cinfo->{nodes
}}) {
1079 next if defined ($exclude) && ($exclude eq $ni->{cid
});
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
}];
1089 sub cluster_format_vmid_list
{
1093 titles
=> ['VMID', __
('Name'), __
('Status') ],
1098 return $res if !$vzl;
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
}];
1109 sub cluster_format_role
{
1112 $role = __
('Master') if $role eq 'M';
1113 $role = __
('Node') if $role eq 'N';