3 =head1 NAME $RCSfile: ClearadmWeb.pm,v $
5 Common routines for the web portion of Clearadm
13 Andrew DeFaria <Andrew@ClearSCM.com>
21 Sat Dec 18 08:43:27 EST 2010
25 $Date: 2011/12/26 19:00:58 $
31 This module holds common web routines for the web portion of Clearadm.
39 The following routines are exported:
73 use lib "$FindBin::Bin/../../lib";
82 my $clearadm = Clearadm->new;
84 our $APPNAME= 'Clearadm';
85 our $VERSION = '$Revision: 1.46 $';
86 ($VERSION) = ($VERSION =~ /\$Revision: (.*) /);
111 makeFilesystemDropdown
113 makeNotificationDropdown
114 makeStoragePoolDropdown
123 our @PREDEFINED_ALERTS = (
127 our @PREDEFINED_NOTIFICATIONS = (
137 our @PREDEFINED_TASKS = (
146 our @PREDEFINED_SCHEDULES = (
154 our @PREDEFINED_NOTMORETHAN = (
161 our @PREDEFINED_MULTIPLIERS = (
171 display font ({-class => 'error'}, '<br>DEBUG: '). $msg;
176 sub displayError($) {
179 display font ({-class => 'error'}, 'Error: ') . $msg;
185 my ($field, $label) = @_;
187 $label ||= 'Unknown';
189 my $undef = font {-class => 'unknown'}, $label;
191 return defined $field ? $field : $undef;
195 my ($label, %rec) = @_;
197 $rec{$_} = setField ($rec{$_}, $label)
207 dbug "$_: $vars{$_}";
218 # Make the image fit the message. It seems that characters are ~ 7px wide.
219 my $imageLength = length ($msg) * 7;
221 my $errorImage = GD::Image->new ($imageLength, 20);
223 # Allocate some colors
224 my $white = $errorImage->colorAllocate (255, 255, 255);
225 my $red = $errorImage->colorAllocate (255, 0, 0);
227 # Allow the text to shine through
228 $errorImage->transparent($white);
229 $errorImage->interlaced('true');
231 # Now put out the message
232 $errorImage->string (gdMediumBoldFont, 0, 0, $msg, $red);
235 print "Content-type: image/png\n\n";
236 print $errorImage->png;
238 # Since we've "returned" the error in the form of an image, there's nothing
239 # left for us to do so we can exit
247 my $meg = (1024 * $kbyte);
248 my $gig = (1024 * $meg);
250 my $size = $amount > $gig
251 ? sprintf ('%.2f Gig', $amount / $gig)
253 ? sprintf ('%.2f Meg', $amount / $meg)
254 : sprintf ('%.2f Kbyte', $amount / $kbyte);
259 sub _makeAlertlogSelection($$) {
260 my ($name, $default) = @_;
266 $values{All} = 'All';
268 $values{$$_{$name}} = $$_{$name}
269 for ($clearadm->FindAlertlog);
271 my $dropdown = popup_menu {
274 values => [sort keys %values],
279 } # _makeAlertlogSelection
281 sub _makeRunlogSelection($$) {
282 my ($name, $default) = @_;
286 my @values = sort $clearadm->GetUniqueList ('runlog', $name);
288 unshift @values, 'All';
296 $values{NULL} = '<NULL>';
300 my $dropdown = popup_menu {
309 } # _makeRunlogSelection
311 sub _makeRunlogSelectionNumeric($$) {
312 my ($name, $default) = @_;
316 my @values = sort {$a <=> $b} $clearadm->GetUniqueList ('runlog', $name);
318 unshift @values, 'All';
320 my $dropdown = popup_menu {
328 } # _makeRunlogSelection
330 sub makeAlertDropdown(;$$) {
331 my ($label, $default) = @_;
337 push @values, $$_{name} for ($clearadm->FindAlert);
339 my $dropdown = "$label ";
340 $dropdown .= popup_menu {
343 values => [sort @values],
348 } # makeAlertDropdown
350 sub makeMultiplierDropdown(;$$) {
351 my ($label, $default) = @_;
355 my $dropdown = "$label ";
356 $dropdown .= popup_menu {
357 name => 'multiplier',
359 values => [sort @PREDEFINED_MULTIPLIERS],
364 } # makeMultiplierDropdown
366 sub makeNoMoreThanDropdown(;$$) {
367 my ($label, $default) = @_;
371 my $dropdown = "$label ";
372 $dropdown .= popup_menu {
373 name => 'nomorethan',
375 values => [sort @PREDEFINED_NOTMORETHAN],
380 } # makeNoMorThanDropdown
382 sub makeTagsDropdown($$) {
383 my ($type, $tag) = @_;
385 my $dropdown = ucfirst $type . ' ';
387 if ($type eq 'vob') {
388 my $vobs = Clearcase::Vobs->new;
390 $dropdown .= popup_menu {
393 values => [sort $vobs->vobs],
397 my $views = Clearcase::Views->new;
399 $dropdown .= popup_menu {
402 values => [sort $views->views],
407 return span {id => $type}, $dropdown;
410 sub makeStoragePoolDropdown($$) {
411 my ($type, $tag) = @_;
415 my $dropdown = 'Storage pool ';
417 if ($type eq 'vob') {
418 push @values, qw(admin db cleartext derivedobj source total);
420 push @values, qw(admin db private total);
423 $dropdown .= popup_menu {
430 return span {id => $type}, $dropdown;
431 } # makeStoragePoolsDropdown
433 sub makeFilesystemDropdown($;$$$) {
434 my ($system, $label, $default, $onchange) = @_;
440 for ($clearadm->FindFilesystem ($system)) {
441 my %filesystem = %{$_};
443 my $value = "$filesystem{filesystem} ($filesystem{mount})";
445 $filesystems{$filesystem{filesystem}} = $value;
448 my $dropdown .= "$label ";
449 $dropdown .= popup_menu {
450 name => 'filesystem',
452 values => [sort keys %filesystems],
453 labels => \%filesystems,
454 onChange => ($onchange) ? $onchange : '',
458 return span {id => 'filesystems'}, $dropdown;
459 } # makeFilesystemDropdown
461 sub makeIntervalDropdown(;$$$) {
462 my ($label, $default, $onchange) = @_;
473 $default = ucfirst lc $default if $default;
475 my $dropdown = "$label ";
476 $dropdown .= popup_menu {
478 id => 'scalingFactor',
480 values => [@intervals],
482 onchange => $onchange,
485 return span {id => 'scaling'}, $dropdown;
486 } # makeIntervalDropdown;
488 sub makeNotificationDropdown(;$$) {
489 my ($label, $default) = @_;
495 push @values, $$_{name}
496 for ($clearadm->FindNotification);
498 my $dropdown = "$label ";
499 $dropdown .= popup_menu {
500 name => 'notification',
502 values => [sort @values],
507 } # makeNotificationDropdown
509 sub makeRestartableDropdown(;$$) {
510 my ($label, $default) = @_;
519 my $dropdown = "$label ";
520 $dropdown .= popup_menu {
521 name => 'restartable',
528 } # makeRestartableDropdown
530 sub makeSystemDropdown(;$$$%) {
531 my ($label, $default, $onchange, %systems) = @_;
535 for ($clearadm->FindSystem) {
538 my $value = $system{name};
539 $value .= $system{alias} ? " ($system{alias})" : '';
541 $systems{$system{name}} = $value;
544 my $systemDropdown .= "$label ";
545 $systemDropdown .= popup_menu {
548 values => [sort keys %systems],
550 onchange => ($onchange) ? $onchange : '',
554 return span {id => 'systems'}, $systemDropdown;
555 } # makeSystemDropdown
557 sub makeTaskDropdown(;$$) {
558 my ($label, $default) = @_;
564 push @values, $$_{name}
565 for ($clearadm->FindTask);
567 my $taskDropdown = "$label ";
568 $taskDropdown .= popup_menu {
571 values => [sort @values],
575 return $taskDropdown;
578 sub makeTimeDropdown($$$;$$$$$) {
596 push @times, 'Earliest';
598 if ($table =~ /loadavg/i) {
599 push @times, $$_{timestamp}
600 for ($clearadm->GetLoadavg ($system, undef, undef, undef, $interval));
601 } elsif ($table =~ /filesystem/i) {
602 push @times, $$_{timestamp}
603 for ($clearadm->GetFS ($system, $filesystem, undef, undef, undef, $interval));
606 push @times, 'Latest';
609 $default = $name eq 'start' ? 'Earliest' : 'Latest';
612 my $timeDropdown = "$label ";
613 $timeDropdown .= span {id => $elementID}, popup_menu {
620 return $timeDropdown;
624 my ($title, $type) = @_;
627 $title = "$APPNAME: $title";
635 -author => 'Andrew DeFaria <Andrew@ClearSCM.com>',
637 keywords => 'ClearSCM Clearadm',
638 copyright => 'Copyright (c) ClearSCM, Inc. 2010, All rights reserved',
641 -language => 'JavaScript',
642 -src => 'clearadm.js',
644 -style => ['clearadm.css', 'clearmenu.css'],
649 my $ieTableWrapStart = '<!--[if gt IE 6]><!--></a><!--<![endif]--><!--'
650 . '[if lt IE 7]><table border="0" cellpadding="0" '
651 . 'cellspacing="0"><tr><td><![endif]-->';
652 my $ieTableWrapEnd = '<!--[if lte IE 6]></td></tr></table></a><![endif]-->';
655 display div {id=>'mastheadlogo'}, h1 {class => 'title'}, $APPNAME;
656 display start_div {class => 'menu'};
659 display ul li a {href => '/clearadm'}, 'Home';
661 my @allSystems = $clearadm->FindSystem;
666 display a {href => 'systems.cgi'}, "Systems$ieTableWrapStart";
668 for (sort @allSystems) {
670 my $sysName = ucfirst $system{name};
671 $sysName .= " ($system{alias})"
675 href => "systemdetails.cgi?system=$system{name}"
676 }, ucfirst " $sysName";
679 display $ieTableWrapEnd;
687 display a {href => 'filesystems.cgi'}, "Filesystems$ieTableWrapStart";
691 my $sysName = ucfirst $system{name};
692 $sysName .= " ($system{alias})"
696 href => "filesystems.cgi?system=$system{name}"
697 }, ucfirst " $sysName";
700 display $ieTableWrapEnd;
707 display a {href => 'vobservers.cgi'}, " VOBs$ieTableWrapStart";
708 display $ieTableWrapEnd;
715 display a {href => 'viewager.cgi'}, "Views$ieTableWrapStart";
716 display $ieTableWrapEnd;
723 display a {href => '#'}, "Configure$ieTableWrapStart";
725 display li a {href => 'alerts.cgi'}, ' Alerts';
726 display li a {href => 'notifications.cgi'}, ' Notifications';
727 display li a {href => 'schedule.cgi'}, ' Schedule';
728 display li a {href => 'tasks.cgi'}, ' Tasks';
730 display $ieTableWrapEnd;
737 display a {href => '#'}, "Logs$ieTableWrapStart";
739 display li a {href => 'alertlog.cgi'}, ' Alert';
740 display li a {href => 'runlog.cgi'}, ' Run';
742 display $ieTableWrapEnd;
749 display a {href => '#'}, "Help$ieTableWrapStart";
750 display start_ul {class => 'rightmenu'};
751 display li a {href => 'readme.cgi'}, " About: $APPNAME $VERSION";
753 display $ieTableWrapEnd;
758 display start_div {class => 'page'};
763 sub displayAlert(;$) {
766 display start_table {cellspacing => 1};
769 display th {class => 'labelCentered'}, 'Actions';
770 display th {class => 'labelCentered'}, 'Name';
771 display th {class => 'labelCentered'}, 'Type';
772 display th {class => 'labelCentered'}, 'Who';
773 display th {class => 'labelCentered'}, 'Category';
776 for ($clearadm->FindAlert ($alert)) {
779 $alert{who} = setField $alert{who}, 'System Administrator';
782 my $areYouSure = "Are you sure you want to delete the $alert{name} alert?";
784 my $actions = start_form {
786 action => 'processalert.cgi',
792 value => $alert{name},
795 if (InArray $alert{name}, @PREDEFINED_ALERTS) {
803 title => 'Cannot delete predefined alert',
812 title => 'Cannot edit predefined alert',
822 onclick => "return AreYouSure ('$areYouSure');",
836 my $who = $alert{who};
838 if ($who =~ /^([a-zA-Z0-9._-]+)@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,4})$/) {
839 $who = a {href => "mailto:$1\@$2"}, $who;
842 display td {class => 'dataCentered'}, $actions;
843 display td {class => 'data'}, $alert{name};
844 display td {class => 'data'}, $alert{type};
845 display td {class => 'data'}, $who;
846 display td {class => 'data'},
847 (InArray $alert{name}, @PREDEFINED_ALERTS) ? 'Predefined' : 'User Defined';
853 display p {class => 'center'}, a {
854 href => 'processalert.cgi?action=Add',
855 }, 'New alert ', img {
863 sub displayAlertlog(%) {
868 unless (($opts{oldalert} and $opts{alert} and
869 $opts{oldalert} eq $opts{alert}) and
870 ($opts{oldsystem} and $opts{system} and
871 $opts{oldsystem} eq $opts{system}) and
872 ($opts{oldnotification} and $opts{notification} and
873 $opts{oldnotification} eq $opts{notification})) {
880 $condition = "alert like '%";
881 $condition .= $opts{alert} ? $opts{alert} : '';
883 $condition .= ' and ';
884 $condition .= "system like '%";
885 $condition .= $opts{system} ? $opts{system} : '';
887 $condition .= ' and ';
888 $condition .= "notification like '%";
889 $condition .= $opts{notification} ? $opts{notification} : '';
893 my $total = $clearadm->Count ('alertlog', $condition);
895 if ($opts{'nextArrow.x'}) {
896 $opts{start} = $opts{next};
897 } elsif ($opts{'prevArrow.x'}) {
898 $opts{start} = $opts{prev};
903 my $next = $opts{start} + $opts{page} < $total
904 ? $opts{start} + $opts{page}
906 my $prev = $opts{start} - $opts{page} >= 0
907 ? $opts{start} - $opts{page}
910 my $opts = $opts{start} + 1;
912 $opts .= $opts{start} + $opts{page} < $total
913 ? $opts{start} + $opts{page}
915 $opts .= " of $total";
919 action => 'alertlog.cgi'
922 # Hidden fields to pass along
923 display input {name => 'prev', type => 'hidden', value => $prev};
924 display input {name => 'next', type => 'hidden', value => $next};
929 value => $opts{alert},
934 value => $opts{system},
937 name => 'oldnotification',
939 value => $opts{notification},
942 my $caption = start_table {
948 $caption .= start_Tr;
951 $caption .= td {align => 'left'}, input {
959 $caption .= td {align => 'left'}, img {
961 disabled => 'disabled',
965 $caption .= td {align => 'center'}, $opts;
968 $caption .= td {align => 'right'}, input {
976 $caption .= td {align => 'right'}, img {
978 disabled => 'disabled',
984 $caption .= end_table;
986 display start_table {cellspacing => 1, width => '98%'};
988 display caption $caption;
991 display th {class => 'labelCentered'}, '#';
992 display th {class => 'labelCentered'}, 'Delete';
993 display th {class => 'labelCentered'}, 'Name';
994 display th {class => 'labelCentered'}, 'System';
995 display th {class => 'labelCentered'}, 'Notification';
996 display th {class => 'labelCentered'}, 'Date/Time';
997 display th {class => 'labelCentered'}, 'Runlog';
998 display th {class => 'labelCentered'}, 'Message';
1009 }, _makeAlertlogSelection ('alert', $opts{alert});
1012 }, _makeAlertlogSelection ('system', $opts{system});
1015 }, _makeAlertlogSelection ('notification', $opts{notification});
1027 # TODO: Would like to have Clear All Alerts be Clear Alerts and for it to
1028 # clear only the alerts that have been filtered.
1030 href => 'deletealertlog.cgi?alertlogid=all'
1033 value => 'Clear All Events',
1034 onclick => "return AreYouSure('Are you sure you want to delete all alerts?');",
1038 my $i = $opts{start};
1040 for ($clearadm->FindAlertlog (
1043 $opts{notification},
1047 my %alertlog = setFields 'N/A', %{$_};
1050 my %system = $clearadm->GetSystem ($alertlog{system});
1052 display td {class => 'dataCentered'}, ++$i;
1053 display td {class => 'dataCentered'}, a {
1054 href => "deletealertlog.cgi?alertlogid=$alertlog{id}"
1056 src => 'delete.png',
1060 onclick => "return AreYouSure ('Are you sure you wish to delete this alertlog entry?');",
1062 display td {class => 'data'}, a {
1063 href => "alerts.cgi?alert=$alertlog{alert}"
1064 }, $alertlog{alert};
1065 display td {class => 'data'}, a {
1066 href => "systemdetails.cgi?system=$alertlog{system}"
1067 }, $alertlog{system};
1068 display td {class => 'data'}, a {
1069 href => "notifications.cgi?notification=$alertlog{notification}"
1070 }, $alertlog{notification};
1071 display td {class => 'data'}, $alertlog{timestamp};
1072 display td {class => 'dataCentered'}, a {
1073 href => "runlog.cgi?id=$alertlog{runlog}"
1074 }, $alertlog{runlog};
1075 display td {class => 'data'}, $alertlog{message};
1086 sub displayFilesystem($) {
1087 my ($systemName) = @_;
1089 display start_table {cellspacing => 1, width => '98%'};
1092 display th {class => 'labelCentered'}, 'Action';
1093 display th {class => 'labelCentered'}, 'Name';
1094 display th {class => 'labelCentered'}, 'Alias';
1095 display th {class => 'labelCentered'}, 'Admin';
1096 display th {class => 'labelCentered'}, 'Filesystem';
1097 display th {class => 'labelCentered'}, 'Mount';
1098 display th {class => 'labelCentered'}, 'Type';
1099 display th {class => 'labelCentered'}, 'History';
1100 display th {class => 'labelCentered'}, 'Used';
1101 display th {class => 'labelCentered'}, 'Threshold';
1102 display th {class => 'labelCentered'}, 'Usage';
1105 for (sort { $a->{mount} cmp $b->{mount} } $clearadm->FindSystem ($systemName)) {
1108 %system = setFields ('N/A', %system);
1110 my $admin = ($system{email} !~ 'N/A')
1111 ? a {-href => "mailto:$system{email}"}, $system{admin}
1114 for ($clearadm->FindFilesystem ($system{name})) {
1115 my %filesystem = %{$_};
1117 my %fs = $clearadm->GetLatestFS ($system{name}, $filesystem{filesystem});
1119 my $size = autoScale $fs{size};
1120 my $used = autoScale $fs{used};
1121 my $free = autoScale $fs{free};
1123 # TODO: Note that this percentages does not agree with df output. I'm not
1125 my $usedPct = $fs{size} == 0 ? 0
1127 (($fs{reserve} + $fs{used}) / $fs{size} * 100));
1129 my $alias = ($system{alias} !~ 'N/A')
1131 href => "systemdetails.cgi?system=$system{name}"
1135 my $class = $usedPct < $filesystem{threshold}
1138 my $classRight = $usedPct < $filesystem{threshold}
1141 my $classCentered = $usedPct < $filesystem{threshold}
1143 : 'dataCenteredAlert';
1144 my $classRightTop = $usedPct < $filesystem{threshold}
1146 : 'dataRightAlertTop';
1149 display start_td {class => 'dataCentered'};
1151 my $areYouSure = 'Are you sure you want to delete '
1152 . "$system{name}:$filesystem{filesystem}?" . '\n'
1153 . 'Doing so will remove all records related to this\n'
1154 . 'filesystem and its history.';
1156 display start_form {
1158 action => "processfilesystem.cgi",
1164 value => $system{name},
1168 name => 'filesystem',
1169 value => $filesystem{filesystem},
1175 src => 'delete.png',
1179 onclick => "return AreYouSure ('$areYouSure');"
1190 if ($filesystem{notification}) {
1192 href => "alertlog.cgi?system=$filesystem{system}"}, img {
1196 title => 'This filesystem has alerts',
1203 display td {class => $class},
1204 a {-href => "systemdetails.cgi?system=$system{name}"}, $system{name};
1205 display td {class => $class}, $alias;
1206 display td {class => $class}, $admin;
1207 display td {class => $class}, $filesystem{filesystem};
1208 display td {class => $class}, $filesystem{mount};
1209 display td {class => $class}, $filesystem{fstype};
1210 display td {class => $classCentered}, $filesystem{filesystemHist};
1211 display td {class => $classRightTop}, "$used ($usedPct%)<br>",
1212 font {class => 'unknown'}, "$fs{timestamp}";
1213 display td {class => $classRightTop}, "$filesystem{threshold}%";
1215 my $image = $filesystem{fssmall}
1216 ? "data:image/png;base64,$filesystem{fssmall}"
1217 : "plotfs.cgi?system=$system{name}&filesystem=$filesystem{filesystem}&tiny=1";
1219 display td {class => $class},
1221 "plot.cgi?type=filesystem&system=$system{name}"
1222 . "&filesystem=$filesystem{filesystem}&scaling=Day&points=7"
1234 } # displayFilesystem
1236 sub displayNotification(;$) {
1237 my ($notification) = @_;
1239 display start_table {cellspacing => 1};
1242 display th {class => 'labelCentered'}, 'Actions';
1243 display th {class => 'labelCentered'}, 'Name';
1244 display th {class => 'labelCentered'}, 'Alert';
1245 display th {class => 'labelCentered'}, 'Condition';
1246 display th {class => 'labelCentered'}, 'Not More Than';
1247 display th {class => 'labelCentered'}, 'Category';
1250 for ($clearadm->FindNotification ($notification)) {
1251 my %notification= setFields 'N/A', %{$_};
1254 my $areYouSure = "Are you sure you want to delete the $notification{name} "
1257 my $actions = start_form {
1259 action => 'processnotification.cgi',
1265 value => $notification{name},
1268 if (InArray $notification{name}, @PREDEFINED_NOTIFICATIONS) {
1273 src => 'delete.png',
1276 title => 'Cannot delete predefined notification',
1285 title => 'Cannot edit predefined notification',
1291 src => 'delete.png',
1295 onclick => "return AreYouSure ('$areYouSure');",
1309 display td {class => 'dataCentered'}, $actions;
1310 display td {class => 'data'}, $notification{name};
1311 display td {class => 'data'}, a {
1312 href => "alerts.cgi?alert=$notification{alert}"
1313 }, $notification{alert};
1314 display td {class => 'data'}, $notification{cond};
1315 display td {class => 'data'}, $notification{nomorethan};
1316 display td {class => 'data'},
1317 (InArray $notification{name}, @PREDEFINED_NOTIFICATIONS)
1326 display p {class => 'center'}, a {
1327 href => 'processnotification.cgi?action=Add',
1328 }, 'New notification', img {
1334 } # displayNotification
1336 sub displayRunlog(%) {
1341 for (qw(task system not status)) {
1343 if (($opts{$old} and $opts{$_}) and ($opts{$old} ne $opts{$_})) {
1351 unless ($opts{id}) {
1352 if ($opts{task} !~ /all/i) {
1353 $condition = "task like '%";
1354 $condition .= $opts{task} ? $opts{task} : '';
1358 if ($opts{system} !~ /all/i) {
1359 if ($opts{system} eq '<NULL>') {
1360 $condition .= ' and system is null';
1362 } elsif ($opts{system} ne 'All') {
1363 $condition .= " and system like '%$opts{system}%'";;
1367 if ($opts{status} !~ /all/i) {
1368 $condition .= ' and ';
1369 unless ($opts{not}) {
1370 $condition .= "status=$opts{status}";
1372 $condition .= "status<>$opts{status}";
1377 my $total = $clearadm->Count ('runlog', $condition);
1379 $opts{start} = $opts{'nextArrow.x'} ? $opts{next} : $opts{prev};
1384 my $next = $opts{start} + $opts{page} < $total
1385 ? $opts{start} + $opts{page}
1387 my $prev = $opts{start} - $opts{page} >= 0
1388 ? $opts{start} - $opts{page}
1391 my $opts = $opts{'nextArrow.x'} ? $opts{next} + 1 : $opts{prev} + 1;
1393 $opts .= $opts{start} + $opts{page} < $total
1394 ? $opts{start} + $opts{page}
1396 $opts .= " of $total";
1398 display start_form {
1400 action => 'runlog.cgi'
1403 # Hidden fields to pass along
1404 display input {name => 'prev', type => 'hidden', value => $prev};
1405 display input {name => 'next', type => 'hidden', value => $next};
1406 display input {name => 'oldtask', type => 'hidden', value => $opts{task}};
1407 display input {name => 'oldsystem', type => 'hidden', value => $opts{system}};
1408 display input {name => 'oldnot', type => 'hidden', value => $opts{not}};
1409 display input {name => 'oldstatus', type => 'hidden', value => $opts{status}};
1411 my $caption = start_table {
1417 $caption .= start_Tr;
1419 unless ($opts{id}) {
1420 $caption .= td {align => 'left'}, input {
1421 name => 'prevArrow',
1428 $caption .= td {align => 'left'}, img {
1430 disabled => 'disabled',
1434 $caption .= td {align => 'center'}, $opts;
1436 unless ($opts{id}) {
1437 $caption .= td {align => 'right'}, input {
1438 name => 'nextArrow',
1445 $caption .= td {align => 'right'}, img {
1447 disabled => 'disabled',
1453 $caption .= end_table;
1455 display start_table {cellspacing => 1, width => '98%'};
1457 display caption $caption;
1460 display th {class => 'labelCentered'}, '#';
1461 display th {class => 'labelCentered'}, 'ID';
1462 display th {class => 'labelCentered'}, 'Task';
1463 display th {class => 'labelCentered'}, 'System';
1464 display th {class => 'labelCentered'}, 'Started';
1465 display th {class => 'labelCentered'}, 'Ended';
1466 display th {class => 'labelCentered'}, 'Status';
1467 display th {class => 'labelCentered'}, 'Message';
1471 $opts{not} ||= 'false';
1473 display start_form {
1475 action => 'runlog.cgi'
1484 }, _makeRunlogSelection ('task', $opts{task});
1487 }, _makeRunlogSelection ('system', $opts{system});
1488 display td {class => 'filter'}, ' ';
1492 }, "Not: ", checkbox {
1495 checked => $opts{not} eq 'true' ? 1 : 0,
1500 }, _makeRunlogSelectionNumeric ('status', $opts{status});
1511 my $i = $opts{start};
1515 if ($opts{status}) {
1516 if ($opts{status} !~ /all/i) {
1517 $status = $opts{not} ne 'true' ? $opts{status} : "!$opts{status}";
1521 for ($clearadm->FindRunlog (
1529 my %runlog = setFields 'N/A', %{$_};
1531 my $class = $runlog{status} == 0
1534 my $classCentered = $runlog{status} == 0
1536 : 'dataAlertCentered';
1537 my $classRight = $runlog{status} == 0
1542 display td {class => 'dataCentered'}, ++$i;
1543 display td {class => 'dataCentered'}, $runlog{id};
1544 display td {class => 'data'}, a {
1545 href => "tasks.cgi?task=$runlog{task}"
1547 display td {class => 'data'}, $runlog{system} eq 'localhost'
1550 href => "systemdetails.cgi?system=$runlog{system}"
1552 display td {class => 'dataCentered'}, $runlog{started};
1553 display td {class => 'dataCentered'}, $runlog{ended};
1554 display td {class => $classRight}, $runlog{status};
1556 my $message = $runlog{message};
1557 $message =~ s/\r\n/<br>/g;
1559 display td {class => $class, width => '50%'}, $message;
1568 sub displaySchedule() {
1569 display start_table {cellspacing => 1};
1572 display th {class => 'labelCentered'}, 'Actions';
1573 display th {class => 'labelCentered'}, 'Active';
1574 display th {class => 'labelCentered'}, 'Name';
1575 display th {class => 'labelCentered'}, 'Task';
1576 display th {class => 'labelCentered'}, 'Notification';
1577 display th {class => 'labelCentered'}, 'Frequency';
1578 display th {class => 'labelCentered'}, 'Category';
1581 for ($clearadm->FindSchedule) {
1582 my %schedule = setFields 'N/A', %{$_};
1585 my $areYouSure = "Are you sure you want to delete the $schedule{name} "
1588 my $actions = start_form {
1590 action => 'processschedule.cgi',
1596 value => $schedule{name},
1599 if (InArray $schedule{name}, @PREDEFINED_SCHEDULES) {
1604 src => 'delete.png',
1607 title => 'Cannot delete predefined schedule',
1616 title => 'Cannot edit predefined schedule',
1622 src => 'delete.png',
1626 onclick => "return AreYouSure ('$areYouSure');",
1640 display td {class => 'dataCentered'}, $actions;
1641 display td {class => 'dataCentered'}, checkbox {
1642 disabled => 'disabled',
1643 checked => $schedule{active} eq 'true' ? 1 : 0,
1645 display td {class => 'data'}, $schedule{name};
1646 display td {class => 'data'}, a {
1647 href => "tasks.cgi?task=$schedule{task}"
1649 display td {class => 'data'}, a {
1650 href => "notifications.cgi?notification=$schedule{notification}"
1651 }, $schedule{notification};
1652 display td {class => 'data'}, $schedule{frequency};
1653 display td {class => 'data'},
1654 (InArray $schedule{name}, @PREDEFINED_SCHEDULES)
1663 display p {class => 'center'}, a {
1664 href => 'processschedule.cgi?action=Add',
1665 }, 'New schedule', img {
1673 sub displaySystem($) {
1674 my ($systemName) = @_;
1676 my %system = $clearadm->GetSystem ($systemName);
1679 displayError "Nothing known about system $systemName";
1683 my $lastheardfromClass = 'dataCentered';
1684 my $lastheardfromData = $system{lastheardfrom};
1686 my %load = $clearadm->GetLatestLoadavg ($systemName);
1688 unless ($clearadm->SystemAlive (%system)) {
1689 $lastheardfromClass = 'dataCenteredAlert';
1690 $lastheardfromData = a {
1691 href => "alertlog.cgi?system=$system{name}",
1693 title => "Have not heard from $system{name} for a while"
1694 }, $system{lastheardfrom};
1695 $system{notification} = 'Heartbeat';
1698 my $admin = ($system{email})
1699 ? a {-href => "mailto:$system{email}"}, $system{admin}
1702 $system{alias} = setField $system{alias}, 'N/A';
1703 $system{region} = setField $system{region}, 'N/A';
1705 display start_table {cellspacing => 1};
1708 my $areYouSure = 'Are you sure you want to delete this system?\n'
1709 . "Doing so will remove all records related to $system{name}"
1710 . '\nincluding filesystem records and history as well as '
1711 . 'loadavg history.';
1713 my $actions = start_form {
1715 action => 'processsystem.cgi',
1721 value => $system{name},
1727 src => 'delete.png',
1731 onclick => "return AreYouSure ('$areYouSure');",
1741 $actions .= checkbox {
1742 disabled => 'disabled',
1743 checked => $system{active} eq 'true' ? 1 : 0,
1746 if ($system{notification}) {
1748 href => "alertlog.cgi?system=$system{name}"}, img {
1752 title => 'This system has alerts',
1756 display th {class => 'label'}, "$actions Name:";
1758 display td {class => 'dataCentered', colspan => 2}, $system{name};
1759 display th {class => 'label'}, 'Alias:';
1760 display td {class => 'dataCentered'}, $system{alias};
1761 display th {class => 'label'}, 'Admin:';
1762 display td {class => 'dataCentered', colspan => 2}, $admin;
1763 display th {class => 'label', colspan => 2}, 'Type:';
1764 display td {class => 'dataCentered'}, $system{type};
1768 display th {class => 'label'}, 'OS Version:';
1769 display td {class => 'data', colspan => 10}, $system{os};
1773 display th {class => 'label'}, 'Last Contacted:';
1775 class => $lastheardfromClass,
1777 }, "$lastheardfromData ",
1778 font {class => 'dim' }, "<br>Up: $load{uptime}";
1779 display th {class => 'label'}, 'Port:';
1780 display td {class => 'dataCentered'}, $system{port};
1781 display th {class => 'label'}, 'Threshold:';
1782 display td {class => 'dataCentered'}, $system{loadavgThreshold};
1783 display th {class => 'label'}, 'History:';
1784 display td {class => 'dataCentered'}, $system{loadavgHist};
1785 display th {class => 'label'}, 'Load Avg:';
1786 display td {class => 'data'},
1788 "plot.cgi?type=loadavg&system=$system{name}&scaling=Hour&points=24"
1790 src => "data:image/png;base64,$system{loadavgsmall}",
1794 my $description = $system{description};
1795 $description =~ s/\r\n/<br>/g;
1798 display th {class => 'label'}, 'Description:';
1799 display td {class => 'data', colSpan => 10}, $description;
1804 display p {class => 'center'}, a {
1805 href => 'processsystem.cgi?action=Add',
1806 }, 'New system', img {
1811 display h1 {class => 'center'},
1812 'Filesystem Details: ' . ucfirst $system{name};
1814 display start_table {cellspacing => 1};
1817 display th {class => 'labelCentered'}, 'Action';
1818 display th {class => 'labelCentered'}, 'Filesystem';
1819 display th {class => 'labelCentered'}, 'Type';
1820 display th {class => 'labelCentered'}, 'Mount';
1821 display th {class => 'labelCentered'}, 'Size';
1822 display th {class => 'labelCentered'}, 'Used';
1823 display th {class => 'labelCentered'}, 'Free';
1824 display th {class => 'labelCentered'}, 'Used %';
1825 display th {class => 'labelCentered'}, 'Threshold';
1826 display th {class => 'labelCentered'}, 'History';
1827 display th {class => 'labelCentered'}, 'Usage';
1830 for ($clearadm->FindFilesystem ($system{name})) {
1831 my %filesystem = %{$_};
1833 my %fs = $clearadm->GetLatestFS (
1834 $filesystem{system},
1835 $filesystem{filesystem}
1838 my $size = autoScale $fs{size};
1839 my $used = autoScale $fs{used};
1840 my $free = autoScale $fs{free};
1842 # TODO: Note that this percentages does not agree with df output. I'm not
1844 my $usedPct = $fs{size} == 0 ? 0
1846 (($fs{reserve} + $fs{used}) / $fs{size} * 100));
1848 my $class = $usedPct < $filesystem{threshold}
1851 my $classCentered = $class . 'Centered';
1852 my $classRight = $class . 'Right';
1855 display start_td {class => 'data'};
1857 my $areYouSure = 'Are you sure you want to delete '
1858 . "$system{name}:$filesystem{filesystem}?" . '\n'
1859 . 'Doing so will remove all records related to this\n'
1860 . 'filesystem and its history.';
1862 display start_form {
1864 action => 'processfilesystem.cgi',
1870 value => $system{name},
1874 name => 'filesystem',
1875 value => $filesystem{filesystem},
1881 src => 'delete.png',
1885 onclick => "return AreYouSure ('$areYouSure');"
1896 if ($filesystem{notification}) {
1898 href => "alertlog.cgi?system=$filesystem{system}"}, img {
1902 title => 'This filesystem has alerts',
1908 display td {class => $class}, $filesystem{filesystem};
1909 display td {class => $classCentered}, $filesystem{fstype};
1910 display td {class => $class}, $filesystem{mount};
1911 display td {class => $classRight}, $size;
1912 display td {class => $classRight}, $used;
1913 display td {class => $classRight}, $free;
1914 display td {class => $classRight}, "$usedPct%";
1915 display td {class => $classRight}, "$filesystem{threshold}%";
1916 display td {class => $classCentered}, $filesystem{filesystemHist};
1917 display td {class => $classCentered},
1919 "plot.cgi?type=filesystem&system=$system{name}"
1920 . "&filesystem=$filesystem{filesystem}"
1921 . "&scaling=Day&points=7"
1923 src => "data:image/png;base64,$filesystem{fssmall}",
1934 sub displayTask(;$) {
1937 display start_table {cellspacing => 1, width => '98%'};
1940 display th {class => 'labelCentered'}, 'Actions';
1941 display th {class => 'labelCentered'}, 'Name';
1942 display th {class => 'labelCentered'}, 'System';
1943 display th {class => 'labelCentered'}, 'Description';
1944 display th {class => 'labelCentered'}, 'Command';
1945 display th {class => 'labelCentered'}, 'Restartable';
1946 display th {class => 'labelCentered'}, 'Category';
1949 for ($clearadm->FindTask ($task)) {
1952 $task{system} = 'All Systems'
1953 unless $task{system};
1956 my $areYouSure = "Are you sure you want to delete the $task{name} task?";
1958 my $actions = start_form {
1960 action => 'processtask.cgi',
1966 value => $task{name},
1969 if (InArray $task{name}, @PREDEFINED_TASKS) {
1974 src => 'delete.png',
1977 title => 'Cannot delete predefined task',
1986 title => 'Cannot edit predefined task',
1992 src => 'delete.png',
1996 onclick => "return AreYouSure ('$areYouSure');",
2010 display td {class => 'dataCentered'}, $actions;
2011 display td {class => 'data'}, $task{name};
2012 display td {class => 'data'}, $task{system};
2013 display td {class => 'data'}, $task{description};
2014 display td {class => 'data'}, $task{command};
2015 display td {class => 'dataCentered'}, $task{restartable};
2016 display td {class => 'data'},
2017 (InArray $task{name}, @PREDEFINED_TASKS) ? 'Predefined' : 'User Defined';
2023 display p {class => 'center'}, a {
2024 href => 'processtask.cgi?action=Add',
2025 }, 'New task', img {
2036 display start_form (
2038 -action => 'processalert.cgi',
2039 -onsubmit => 'return validateAlert (this);',
2045 %alert = $clearadm->GetAlert ($alert);
2065 display start_table {cellspacing => 1};
2068 display th {class => 'labelCentered'}, 'Name';
2069 display th {class => 'labelCentered'}, 'Type';
2070 display th {class => 'labelCentered'}, 'Who';
2077 class => 'inputfield',
2082 value => $alert ? $alert{name} : '',
2085 class => 'dataCentered',
2088 class => 'dropdown',
2089 values => [ 'email', 'page', 'im' ],
2090 default => $alert ? $alert{type} : 'email',
2095 class => 'inputfield',
2100 value => $alert ? $alert{who} : '',
2106 display p submit ({value => $alert ? 'Update' : 'Add'}), reset;
2107 display '</center>';
2114 sub editFilesystem($$) {
2115 my ($system, $filesystem) = @_;
2117 display start_form (
2119 -action => 'processfilesystem.cgi',
2122 display start_table {width => '800px', cellspacing => 1};
2125 display th {class => 'labelCentered'}, 'Filesystem';
2126 display th {class => 'labelCentered'}, 'Type';
2127 display th {class => 'labelCentered'}, 'Mount';
2128 display th {class => 'labelCentered'}, 'Size';
2129 display th {class => 'labelCentered'}, 'Used';
2130 display th {class => 'labelCentered'}, 'Free';
2131 display th {class => 'labelCentered'}, 'Used %';
2132 display th {class => 'labelCentered'}, 'History';
2133 display th {class => 'labelCentered'}, 'Threshold';
2136 my %filesystem = $clearadm->GetFilesystem ($system, $filesystem);
2137 my %fs = $clearadm->GetLatestFS ($system, $filesystem);
2147 value => $filesystem{system},
2150 name => 'filesystem',
2152 value => $filesystem{filesystem},
2155 my $size = autoScale $fs{size};
2156 my $used = autoScale $fs{used};
2157 my $free = autoScale $fs{free};
2160 display td {class => 'data'}, $filesystem{filesystem};
2161 display td {class => 'dataCentered'}, $filesystem{fstype};
2162 display td {class => 'data'}, $filesystem{mount};
2163 display td {class => 'dataRight'}, $size;
2164 display td {class => 'dataRight'}, $used;
2165 display td {class => 'dataRight'}, $free;
2166 # TODO: Note that this percentages does not agree with df output. I'm not
2168 display td {class => 'dataCentered'},
2169 sprintf ('%.0f%%', (($fs{reserve} + $fs{used}) / $fs{size} * 100));
2171 my $historyDropdown = popup_menu {
2172 name => 'filesystemHist',
2173 class => 'dropdown',
2188 default => $system ? $filesystem{filesystemHist} : '6 months',
2192 class => 'dataRight',
2193 }, $historyDropdown;
2195 my $thresholdDropdown = popup_menu {
2196 name => 'threshold',
2197 class => 'dropdown',
2198 values => [1 .. 100],
2199 default => $filesystem{threshold},
2201 display td {class => 'dataCentered'}, $thresholdDropdown . '%';
2207 display p submit ({value => 'Update'}), reset;
2208 display '</center>';
2215 sub editNotification(;$) {
2216 my ($notification) = @_;
2218 display start_form (
2220 -action => 'processnotification.cgi',
2221 -onsubmit => 'return validateNotification (this);',
2226 if ($notification) {
2227 %notification = $clearadm->GetNotification ($notification);
2230 unless %notification;
2235 value => $notification,
2247 display start_table {cellspacing => 1};
2250 display th {class => 'labelCentered'}, 'Name';
2251 display th {class => 'labelCentered'}, 'Alert';
2252 display th {class => 'labelCentered'}, 'Condition';
2253 display th {class => 'labelCentered'}, 'Not More Than';
2260 class => 'inputfield',
2265 value => $notification ? $notification{name} : '',
2269 class => 'dataCentered',
2270 }, makeAlertDropdown undef, $notification{alert}
2271 ? $notification{alert}
2277 class => 'inputfield',
2282 value => $notification ? $notification{cond} : '',
2285 class => 'dataCentered',
2286 }, makeNoMoreThanDropdown undef, $notification{nomorethan};
2292 display p submit ({value => $notification ? 'Update' : 'Add'}), reset;
2293 display '</center>';
2298 } # editNotification
2300 sub editSchedule(;$) {
2301 my ($schedule) = @_;
2303 display start_form (
2305 -action => 'processschedule.cgi',
2306 -onsubmit => 'return validateSchedule (this);',
2312 %schedule = $clearadm->GetSchedule ($schedule);
2332 display start_table {cellspacing => 1};
2335 display th {class => 'labelCentered'}, 'Active';
2336 display th {class => 'labelCentered'}, 'Name';
2337 display th {class => 'labelCentered'}, 'Task';
2338 display th {class => 'labelCentered'}, 'Notification';
2339 display th {class => 'labelCentered'}, 'Frequency';
2344 class => 'dataCentered',
2348 checked => $schedule{active} eq 'false' ? 0 : 1,
2354 class => 'inputfield',
2359 value => $schedule ? $schedule{name} : '',
2362 class => 'dataCentered',
2363 }, makeTaskDropdown undef, $schedule{task};
2365 class => 'dataCentered',
2366 }, makeNotificationDropdown undef, $schedule{notification};
2369 my $multiplier = 'minutes';
2371 if ($schedule{frequency} =~ /(\d+)\s(\S+)/ ) {
2375 $multiplier .= 's' if $nbr == 1;
2381 class => 'inputfieldRight',
2389 makeMultiplierDropdown undef, $multiplier;
2395 display p submit ({value => $schedule ? 'Update' : 'Add'}), reset;
2396 display '</center>';
2403 sub editSystem(;$) {
2406 display start_form (
2408 -action => 'processsystem.cgi',
2409 -onsubmit => 'return validateSystem (this);',
2415 %system = $clearadm->GetSystem ($system);
2435 display start_table {cellspacing => 1};
2438 display th {class => 'label'}, checkbox ({
2441 checked => $system{active} eq 'false' ? 0 : 1,
2446 display td {class => 'data'}, $system{name};
2451 class => 'inputfield',
2459 display th {class => 'label'}, 'Alias:';
2463 class => 'inputfield',
2468 value => $system ? $system{alias} : '',
2471 display th {class => 'label'}, 'Port:';
2473 class => 'dataRight',
2475 class => 'inputfieldRight',
2482 : $Clearadm::CLEAROPTS{CLEARADM_PORT},
2485 my $systemTypeDropdown = popup_menu {
2487 class => 'dropdown',
2488 values => ['Unix', 'Linux', 'Windows'],
2489 default => $system ? $system{type} : 'Linux',
2492 display th {class => 'label'}, 'Type:';
2494 class => 'dataRight',
2495 }, $systemTypeDropdown;
2499 display th {class => 'label'}, 'Admin:';
2503 class => 'inputfield',
2508 value => $system ? $system{admin} : '',
2510 display th {class => 'label'}, 'Admin Email:';
2514 class => 'inputfield',
2519 value => $system ? $system{email} : '',
2522 display th {class => 'label'}, 'Threshold:';
2524 class => 'dataRight',
2526 class => 'inputfieldRight',
2528 name => 'loadavgThreshold',
2532 ? $system{loadavgThreshold}
2533 : $Clearadm::CLEAROPTS{CLEARADM_LOADAVG_THRESHOLD},
2536 my $historyDropdown = popup_menu {
2537 name => 'loadavgHist',
2538 class => 'dropdown',
2553 default => $system ? $system{loadavgHist} : '6 months',
2556 display th {class => 'label'}, 'History:';
2558 class => 'dataRight',
2559 }, $historyDropdown;
2561 my $description = $system ? $system{description} : '';
2562 $description =~ s/\r\n/<br>/g;
2565 display th {class => 'label'}, 'Description:';
2570 class => 'inputfield',
2572 name => 'description',
2574 value => $description,
2580 display p submit ({value => $system ? 'Update' : 'Add'}), reset;
2581 display '</center>';
2591 display start_form (
2593 -action => 'processtask.cgi',
2594 -onsubmit => 'return validateTask (this);',
2600 %task = $clearadm->GetTask ($task);
2620 display start_table {cellspacing => 1};
2623 display th {class => 'labelCentered'}, 'Name';
2624 display th {class => 'labelCentered'}, 'System';
2625 display th {class => 'labelCentered'}, 'Description';
2626 display th {class => 'labelCentered'}, 'Command';
2627 display th {class => 'labelCentered'}, 'Restartable';
2634 class => 'inputfield',
2639 value => $task ? $task{name} : '',
2641 my $systemDropdown = makeSystemDropdown (
2643 $task{system} ? $task{system} : 'localhost',
2645 'localhost' => 'localhost',
2649 display td {class => 'data'}, $systemDropdown;
2654 class => 'inputfield',
2656 name => 'description',
2659 value => $task ? $task{description} : '',
2665 class => 'inputfield',
2670 value => $task ? $task{command} : '',
2674 class => 'dataCentered',
2675 }, makeRestartableDropdown undef, $task{restartable};
2681 display p submit ({value => $task ? 'Update' : 'Add'}), reset;
2682 display '</center>';
2690 my $clearscm = a {-href => 'http://clearscm.com'}, 'ClearSCM, Inc.';
2692 # Figure out which script by using CLEARADM_BASE.
2693 my $script = basename (url {-absolute => 1});
2694 $script = 'index.cgi'
2695 if $script eq 'clearadm';
2697 my $scriptFullPath = "$Clearadm::CLEAROPTS{CLEARADM_BASE}/$script";
2699 my ($year, $mon, $mday, $hour, $min, $sec) =
2700 ymdhms ((stat ($scriptFullPath))[9]);
2702 my $dateModified = "$mon/$mday/$year @ $hour:$min";
2705 -href => "http://clearscm.com/php/scm_man.php?file=clearadm/$script"
2710 display start_div {-class => 'copyright'};
2711 display "$script: Last modified: $dateModified";
2712 display br "Copyright © $year, $clearscm - All rights reserved";
2724 =head1 CONFIGURATION AND ENVIRONMENT
2726 DEBUG: If set then $debug is set to this level.
2728 VERBOSE: If set then $verbose is set to this level.
2730 TRACE: If set then $trace is set to this level.
2740 L<File::Basename|File::Basename>
2746 =head2 ClearSCM Perl Modules
2760 <a href="http://clearscm.com/php/scm_man.php?file=lib/Clearadm.pm">Clearadm</a><br>
2761 <a href="http://clearscm.com/php/scm_man.php?file=lib/DateUtils.pm">DateUtils</a><br>
2762 <a href="http://clearscm.com/php/scm_man.php?file=lib/Display.pm">Display</a><br>
2763 <a href="http://clearscm.com/php/scm_man.php?file=lib/Utils.pm">Utils</a><br>
2768 =head1 BUGS AND LIMITATIONS
2770 There are no known bugs in this module.
2772 Please report problems to Andrew DeFaria <Andrew@ClearSCM.com>.
2774 =head1 LICENSE AND COPYRIGHT
2776 Copyright (c) 2010, ClearSCM, Inc. All rights reserved.