]>
Commit | Line | Data |
---|---|---|
aff192e6 DM |
1 | package PVE::HTMLUtils; |
2 | ||
3 | use strict; | |
4 | require Exporter; | |
5 | use vars qw(@ISA @EXPORT); | |
6 | use Socket; | |
7 | use PVE::I18N; | |
8 | use HTML::Entities; | |
9 | use PVE::HTMLDropDown; | |
10 | use PVE::HTMLTable; | |
11 | use PVE::HTMLGrid; | |
12 | use PVE::HTMLControls; | |
13 | use PVE::APLInfo; | |
14 | use PVE::Storage; | |
15 | use Data::Dumper; | |
16 | ||
17 | @ISA = qw(Exporter); | |
18 | @EXPORT = qw(check_field check_range check_write_mode); | |
19 | ||
20 | ||
21 | # useful for debugging | |
22 | sub var_to_html { | |
23 | my $v = shift; | |
24 | ||
25 | return "<pre>" . Dumper ($v) . "</pre>"; | |
26 | } | |
27 | ||
28 | sub 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) | |
48 | sub 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 | ||
58 | sub 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 | ||
68 | sub 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 | ||
79 | sub 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 | ||
137 | sub 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 | ||
148 | sub check_write_mode { | |
149 | my ($perm) = @_; | |
150 | ||
151 | if ($perm ne 'w') { | |
152 | die msg('nowr') . "\n"; | |
153 | } | |
154 | } | |
155 | ||
156 | sub 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 | ||
177 | sub 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 | # |----------------------------| | |
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>"; | |
195 | return $html; | |
196 | } | |
197 | ||
198 | # create a nice box | |
199 | # | |
200 | # |----------------------------| | |
201 | # |$left $right| | |
202 | # |----------------------------| | |
203 | # |$content | | |
204 | # |----------------------------| | |
205 | ||
206 | sub create_statusframe { | |
207 | my ($id, $left, $right, $content, $height) = @_; | |
208 | ||
209 | $left = ' ' if !$left; | |
210 | $right = ' ' 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 | ||
229 | sub 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 | ||
257 | sub 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 | ||
284 | sub 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 | ||
299 | sub 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 | ||
317 | sub 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 | ||
354 | sub create_vzlist_table { | |
355 | my ($cid, $vzlist) = @_; | |
356 | ||
357 | my $table = PVE::HTMLTable->new ([]); | |
358 | ||
359 | my $out = ''; | |
360 | ||
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'), | |
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, '', "&cid=$cid&veid=$veid&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/\./\.­/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&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 | ||
464 | sub 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', ' ', | |
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', '', "&cid=$cid&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}&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 | ||
547 | sub 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) : ' '; | |
553 | ||
554 | my $maxtext = defined ($max) ? $max : "-"; | |
555 | ||
556 | $table->add_row ('', $name, $cur, $maxtext, $bar); | |
557 | } | |
558 | ||
559 | sub 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 | ||
567 | sub 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 | ||
576 | sub 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 | ||
590 | sub 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", ' ', | |
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 | ||
725 | sub 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 | ||
819 | sub 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 | ||
857 | sub 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'><$2></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&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 | ||
891 | sub 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 | ||
919 | sub 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 | ||
952 | sub storage_format_storage_list { | |
953 | my ($stinfo, $sel) = @_; | |
954 | ||
955 | my $res = { | |
956 | titles => [ 'Storage', 'Type', 'Used (GB)', 'Capacity (GB)', ' ' ], | |
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 | ||
989 | sub 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 | ||
1005 | sub 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 | ||
1018 | sub 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 | ||
1044 | sub 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 | ||
1067 | sub 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 | ||
1089 | sub 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 | ||
1109 | sub 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 | ||
1118 | 1; | |
1119 |