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}
338 for ($clearadm->FindAlert);
340 my $dropdown = "$label ";
341 $dropdown .= popup_menu {
344 values => [sort @values],
349 } # makeAlertDropdown
351 sub makeMultiplierDropdown (;$$) {
352 my ($label, $default) = @_;
356 my $dropdown = "$label ";
357 $dropdown .= popup_menu {
358 name => 'multiplier',
360 values => [sort @PREDEFINED_MULTIPLIERS],
365 } # makeMultiplierDropdown
367 sub makeNoMoreThanDropdown (;$$) {
368 my ($label, $default) = @_;
372 my $dropdown = "$label ";
373 $dropdown .= popup_menu {
374 name => 'nomorethan',
376 values => [sort @PREDEFINED_NOTMORETHAN],
381 } # makeNoMorThanDropdown
383 sub makeTagsDropdown($$) {
384 my ($type, $tag) = @_;
386 my $dropdown = ucfirst $type . ' ';
388 if ($type eq 'vob') {
389 my $vobs = Clearcase::Vobs->new;
391 $dropdown .= popup_menu {
394 values => [sort $vobs->vobs],
398 my $views = Clearcase::Views->new;
400 $dropdown .= popup_menu {
403 values => [sort $views->views],
408 return span {id => $type}, $dropdown;
411 sub makeStoragePoolDropdown($$) {
412 my ($type, $tag) = @_;
416 my $dropdown = 'Storage pool ';
418 if ($type eq 'vob') {
419 push @values, qw(admin db cleartext derivedobj source total);
421 push @values, qw(admin db private total);
424 $dropdown .= popup_menu {
431 return span {id => $type}, $dropdown;
432 } # makeStoragePoolsDropdown
434 sub makeFilesystemDropdown ($;$$$) {
435 my ($system, $label, $default, $onchange) = @_;
441 for ($clearadm->FindFilesystem ($system)) {
442 my %filesystem = %{$_};
444 my $value = "$filesystem{filesystem} ($filesystem{mount})";
446 $filesystems{$filesystem{filesystem}} = $value;
449 my $dropdown .= "$label ";
450 $dropdown .= popup_menu {
451 name => 'filesystem',
453 values => [sort keys %filesystems],
454 labels => \%filesystems,
455 onChange => ($onchange) ? $onchange : '',
459 return span {id => 'filesystems'}, $dropdown;
460 } # makeFilesystemDropdown
462 sub makeIntervalDropdown (;$$$) {
463 my ($label, $default, $onchange) = @_;
474 $default = ucfirst lc $default
477 my $dropdown = "$label ";
478 $dropdown .= popup_menu {
480 id => 'scalingFactor',
482 values => [@intervals],
484 onchange => $onchange,
487 return span {id => 'scaling'}, $dropdown;
488 } # makeIntervalDropdown;
490 sub makeNotificationDropdown (;$$) {
491 my ($label, $default) = @_;
497 push @values, $$_{name}
498 for ($clearadm->FindNotification);
500 my $dropdown = "$label ";
501 $dropdown .= popup_menu {
502 name => 'notification',
504 values => [sort @values],
509 } # makeNotificationDropdown
511 sub makeRestartableDropdown (;$$) {
512 my ($label, $default) = @_;
521 my $dropdown = "$label ";
522 $dropdown .= popup_menu {
523 name => 'restartable',
530 } # makeRestartableDropdown
532 sub makeSystemDropdown (;$$$%) {
533 my ($label, $default, $onchange, %systems) = @_;
537 for ($clearadm->FindSystem) {
540 my $value = $system{name};
541 $value .= $system{alias} ? " ($system{alias})" : '';
543 $systems{$system{name}} = $value;
546 my $systemDropdown .= "$label ";
547 $systemDropdown .= popup_menu {
550 values => [sort keys %systems],
552 onchange => ($onchange) ? $onchange : '',
556 return span {id => 'systems'}, $systemDropdown;
557 } # makeSystemDropdown
559 sub makeTaskDropdown (;$$) {
560 my ($label, $default) = @_;
566 push @values, $$_{name}
567 for ($clearadm->FindTask);
569 my $taskDropdown = "$label ";
570 $taskDropdown .= popup_menu {
573 values => [sort @values],
577 return $taskDropdown;
580 sub makeTimeDropdown ($$$;$$$$$) {
598 push @times, 'Earliest';
600 if ($table =~ /loadavg/i) {
601 push @times, $$_{timestamp}
602 for ($clearadm->GetLoadavg ($system, undef, undef, undef, $interval));
603 } elsif ($table =~ /filesystem/i) {
604 push @times, $$_{timestamp}
605 for ($clearadm->GetFS ($system, $filesystem, undef, undef, undef, $interval));
608 push @times, 'Latest';
611 $default = $name eq 'start' ? 'Earliest' : 'Latest';
614 my $timeDropdown = "$label ";
615 $timeDropdown .= span {id => $elementID}, popup_menu {
622 return $timeDropdown;
626 my ($title, $type) = @_;
629 $title = "$APPNAME: $title";
637 -author => 'Andrew DeFaria <Andrew@ClearSCM.com>',
639 keywords => 'ClearSCM Clearadm',
640 copyright => 'Copyright (c) ClearSCM, Inc. 2010, All rights reserved',
643 -language => 'JavaScript',
644 -src => 'clearadm.js',
646 -style => ['clearadm.css', 'clearmenu.css'],
651 my $ieTableWrapStart = '<!--[if gt IE 6]><!--></a><!--<![endif]--><!--'
652 . '[if lt IE 7]><table border="0" cellpadding="0" '
653 . 'cellspacing="0"><tr><td><![endif]-->';
654 my $ieTableWrapEnd = '<!--[if lte IE 6]></td></tr></table></a><![endif]-->';
657 display div {id=>'mastheadlogo'}, h1 {class => 'title'}, $APPNAME;
658 display start_div {class => 'menu'};
661 display ul li a {href => '/clearadm'}, 'Home';
663 my @allSystems = $clearadm->FindSystem;
668 display a {href => 'systems.cgi'}, "Systems$ieTableWrapStart";
670 for (sort @allSystems) {
672 my $sysName = ucfirst $system{name};
673 $sysName .= " ($system{alias})"
677 href => "systemdetails.cgi?system=$system{name}"
678 }, ucfirst " $sysName";
681 display $ieTableWrapEnd;
689 display a {href => 'filesystems.cgi'}, "Filesystems$ieTableWrapStart";
693 my $sysName = ucfirst $system{name};
694 $sysName .= " ($system{alias})"
698 href => "filesystems.cgi?system=$system{name}"
699 }, ucfirst " $sysName";
702 display $ieTableWrapEnd;
709 display a {href => 'vobservers.cgi'}, " VOBs$ieTableWrapStart";
710 display $ieTableWrapEnd;
717 display a {href => 'viewager.cgi'}, "Views$ieTableWrapStart";
718 display $ieTableWrapEnd;
725 display a {href => '#'}, "Configure$ieTableWrapStart";
727 display li a {href => 'alerts.cgi'}, ' Alerts';
728 display li a {href => 'notifications.cgi'}, ' Notifications';
729 display li a {href => 'schedule.cgi'}, ' Schedule';
730 display li a {href => 'tasks.cgi'}, ' Tasks';
732 display $ieTableWrapEnd;
739 display a {href => '#'}, "Logs$ieTableWrapStart";
741 display li a {href => 'alertlog.cgi'}, ' Alert';
742 display li a {href => 'runlog.cgi'}, ' Run';
744 display $ieTableWrapEnd;
751 display a {href => '#'}, "Help$ieTableWrapStart";
752 display start_ul {class => 'rightmenu'};
753 display li a {href => 'readme.cgi'}, " About: $APPNAME $VERSION";
755 display $ieTableWrapEnd;
760 display start_div {class => 'page'};
765 sub displayAlert (;$) {
768 display start_table {cellspacing => 1};
771 display th {class => 'labelCentered'}, 'Actions';
772 display th {class => 'labelCentered'}, 'Name';
773 display th {class => 'labelCentered'}, 'Type';
774 display th {class => 'labelCentered'}, 'Who';
775 display th {class => 'labelCentered'}, 'Category';
778 for ($clearadm->FindAlert ($alert)) {
781 $alert{who} = setField $alert{who}, 'System Administrator';
784 my $areYouSure = "Are you sure you want to delete the $alert{name} alert?";
786 my $actions = start_form {
788 action => 'processalert.cgi',
794 value => $alert{name},
797 if (InArray $alert{name}, @PREDEFINED_ALERTS) {
805 title => 'Cannot delete predefined alert',
814 title => 'Cannot edit predefined alert',
824 onclick => "return AreYouSure ('$areYouSure');",
838 my $who = $alert{who};
840 if ($who =~ /^([a-zA-Z0-9._-]+)@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,4})$/) {
841 $who = a {href => "mailto:$1\@$2"}, $who;
844 display td {class => 'dataCentered'}, $actions;
845 display td {class => 'data'}, $alert{name};
846 display td {class => 'data'}, $alert{type};
847 display td {class => 'data'}, $who;
848 display td {class => 'data'},
849 (InArray $alert{name}, @PREDEFINED_ALERTS) ? 'Predefined' : 'User Defined';
855 display p {class => 'center'}, a {
856 href => 'processalert.cgi?action=Add',
857 }, 'New alert ', img {
865 sub displayAlertlog (%) {
870 unless (($opts{oldalert} and $opts{alert} and
871 $opts{oldalert} eq $opts{alert}) and
872 ($opts{oldsystem} and $opts{system} and
873 $opts{oldsystem} eq $opts{system}) and
874 ($opts{oldnotification} and $opts{notification} and
875 $opts{oldnotification} eq $opts{notification})) {
882 $condition = "alert like '%";
883 $condition .= $opts{alert} ? $opts{alert} : '';
885 $condition .= ' and ';
886 $condition .= "system like '%";
887 $condition .= $opts{system} ? $opts{system} : '';
889 $condition .= ' and ';
890 $condition .= "notification like '%";
891 $condition .= $opts{notification} ? $opts{notification} : '';
895 my $total = $clearadm->Count ('alertlog', $condition);
897 if ($opts{'nextArrow.x'}) {
898 $opts{start} = $opts{next};
899 } elsif ($opts{'prevArrow.x'}) {
900 $opts{start} = $opts{prev};
905 my $next = $opts{start} + $opts{page} < $total
906 ? $opts{start} + $opts{page}
908 my $prev = $opts{start} - $opts{page} >= 0
909 ? $opts{start} - $opts{page}
912 my $opts = $opts{start} + 1;
914 $opts .= $opts{start} + $opts{page} < $total
915 ? $opts{start} + $opts{page}
917 $opts .= " of $total";
921 action => 'alertlog.cgi'
924 # Hidden fields to pass along
925 display input {name => 'prev', type => 'hidden', value => $prev};
926 display input {name => 'next', type => 'hidden', value => $next};
931 value => $opts{alert},
936 value => $opts{system},
939 name => 'oldnotification',
941 value => $opts{notification},
944 my $caption = start_table {
950 $caption .= start_Tr;
953 $caption .= td {align => 'left'}, input {
961 $caption .= td {align => 'left'}, img {
963 disabled => 'disabled',
967 $caption .= td {align => 'center'}, $opts;
970 $caption .= td {align => 'right'}, input {
978 $caption .= td {align => 'right'}, img {
980 disabled => 'disabled',
986 $caption .= end_table;
988 display start_table {cellspacing => 1, width => '98%'};
990 display caption $caption;
993 display th {class => 'labelCentered'}, '#';
994 display th {class => 'labelCentered'}, 'Delete';
995 display th {class => 'labelCentered'}, 'Name';
996 display th {class => 'labelCentered'}, 'System';
997 display th {class => 'labelCentered'}, 'Notification';
998 display th {class => 'labelCentered'}, 'Date/Time';
999 display th {class => 'labelCentered'}, 'Runlog';
1000 display th {class => 'labelCentered'}, 'Message';
1011 }, _makeAlertlogSelection ('alert', $opts{alert});
1014 }, _makeAlertlogSelection ('system', $opts{system});
1017 }, _makeAlertlogSelection ('notification', $opts{notification});
1029 # TODO: Would like to have Clear All Alerts be Clear Alerts and for it to
1030 # clear only the alerts that have been filtered.
1032 href => 'deletealertlog.cgi?alertlogid=all'
1035 value => 'Clear All Events',
1036 onclick => "return AreYouSure('Are you sure you want to delete all alerts?');",
1040 my $i = $opts{start};
1042 for ($clearadm->FindAlertlog (
1045 $opts{notification},
1049 my %alertlog = setFields 'N/A', %{$_};
1052 my %system = $clearadm->GetSystem ($alertlog{system});
1054 display td {class => 'dataCentered'}, ++$i;
1055 display td {class => 'dataCentered'}, a {
1056 href => "deletealertlog.cgi?alertlogid=$alertlog{id}"
1058 src => 'delete.png',
1062 onclick => "return AreYouSure ('Are you sure you wish to delete this alertlog entry?');",
1064 display td {class => 'data'}, a {
1065 href => "alerts.cgi?alert=$alertlog{alert}"
1066 }, $alertlog{alert};
1067 display td {class => 'data'}, a {
1068 href => "systemdetails.cgi?system=$alertlog{system}"
1069 }, $alertlog{system};
1070 display td {class => 'data'}, a {
1071 href => "notifications.cgi?notification=$alertlog{notification}"
1072 }, $alertlog{notification};
1073 display td {class => 'data'}, $alertlog{timestamp};
1074 display td {class => 'dataCentered'}, a {
1075 href => "runlog.cgi?id=$alertlog{runlog}"
1076 }, $alertlog{runlog};
1077 display td {class => 'data'}, $alertlog{message};
1088 sub displayFilesystem ($) {
1089 my ($systemName) = @_;
1091 display start_table {cellspacing => 1, width => '98%'};
1094 display th {class => 'labelCentered'}, 'Action';
1095 display th {class => 'labelCentered'}, 'Name';
1096 display th {class => 'labelCentered'}, 'Alias';
1097 display th {class => 'labelCentered'}, 'Admin';
1098 display th {class => 'labelCentered'}, 'Filesystem';
1099 display th {class => 'labelCentered'}, 'Mount';
1100 display th {class => 'labelCentered'}, 'Type';
1101 display th {class => 'labelCentered'}, 'History';
1102 display th {class => 'labelCentered'}, 'Used';
1103 display th {class => 'labelCentered'}, 'Threshold';
1104 display th {class => 'labelCentered'}, 'Usage';
1107 for (sort { $a->{mount} cmp $b->{mount} } $clearadm->FindSystem ($systemName)) {
1110 %system = setFields ('N/A', %system);
1112 my $admin = ($system{email} !~ 'N/A')
1113 ? a {-href => "mailto:$system{email}"}, $system{admin}
1116 for ($clearadm->FindFilesystem ($system{name})) {
1117 my %filesystem = %{$_};
1119 my %fs = $clearadm->GetLatestFS ($system{name}, $filesystem{filesystem});
1121 my $size = autoScale $fs{size};
1122 my $used = autoScale $fs{used};
1123 my $free = autoScale $fs{free};
1125 # TODO: Note that this percentages does not agree with df output. I'm not
1127 my $usedPct = $fs{size} == 0 ? 0
1129 (($fs{reserve} + $fs{used}) / $fs{size} * 100));
1131 my $alias = ($system{alias} !~ 'N/A')
1133 href => "systemdetails.cgi?system=$system{name}"
1137 my $class = $usedPct < $filesystem{threshold}
1140 my $classRight = $usedPct < $filesystem{threshold}
1143 my $classCentered = $usedPct < $filesystem{threshold}
1145 : 'dataCenteredAlert';
1146 my $classRightTop = $usedPct < $filesystem{threshold}
1148 : 'dataRightAlertTop';
1151 display start_td {class => 'dataCentered'};
1153 my $areYouSure = 'Are you sure you want to delete '
1154 . "$system{name}:$filesystem{filesystem}?" . '\n'
1155 . 'Doing so will remove all records related to this\n'
1156 . 'filesystem and its history.';
1158 display start_form {
1160 action => "processfilesystem.cgi",
1166 value => $system{name},
1170 name => 'filesystem',
1171 value => $filesystem{filesystem},
1177 src => 'delete.png',
1181 onclick => "return AreYouSure ('$areYouSure');"
1192 if ($filesystem{notification}) {
1194 href => "alertlog.cgi?system=$filesystem{system}"}, img {
1198 title => 'This filesystem has alerts',
1205 display td {class => $class},
1206 a {-href => "systemdetails.cgi?system=$system{name}"}, $system{name};
1207 display td {class => $class}, $alias;
1208 display td {class => $class}, $admin;
1209 display td {class => $class}, $filesystem{filesystem};
1210 display td {class => $class}, $filesystem{mount};
1211 display td {class => $class}, $filesystem{fstype};
1212 display td {class => $classCentered}, $filesystem{filesystemHist};
1213 display td {class => $classRightTop}, "$used ($usedPct%)<br>",
1214 font {class => 'unknown'}, "$fs{timestamp}";
1215 display td {class => $classRightTop}, "$filesystem{threshold}%";
1216 display td {class => $class},
1218 "plot.cgi?type=filesystem&system=$system{name}"
1219 . "&filesystem=$filesystem{filesystem}&scaling=Day&points=7"
1221 src => "plotfs.cgi?system=$system{name}"
1222 . "&filesystem=$filesystem{filesystem}&tiny=1",
1232 } # displayFilesystem
1234 sub displayNotification (;$) {
1235 my ($notification) = @_;
1237 display start_table {cellspacing => 1};
1240 display th {class => 'labelCentered'}, 'Actions';
1241 display th {class => 'labelCentered'}, 'Name';
1242 display th {class => 'labelCentered'}, 'Alert';
1243 display th {class => 'labelCentered'}, 'Condition';
1244 display th {class => 'labelCentered'}, 'Not More Than';
1245 display th {class => 'labelCentered'}, 'Category';
1248 for ($clearadm->FindNotification ($notification)) {
1249 my %notification= setFields 'N/A', %{$_};
1252 my $areYouSure = "Are you sure you want to delete the $notification{name} "
1255 my $actions = start_form {
1257 action => 'processnotification.cgi',
1263 value => $notification{name},
1266 if (InArray $notification{name}, @PREDEFINED_NOTIFICATIONS) {
1271 src => 'delete.png',
1274 title => 'Cannot delete predefined notification',
1283 title => 'Cannot edit predefined notification',
1289 src => 'delete.png',
1293 onclick => "return AreYouSure ('$areYouSure');",
1307 display td {class => 'dataCentered'}, $actions;
1308 display td {class => 'data'}, $notification{name};
1309 display td {class => 'data'}, a {
1310 href => "alerts.cgi?alert=$notification{alert}"
1311 }, $notification{alert};
1312 display td {class => 'data'}, $notification{cond};
1313 display td {class => 'data'}, $notification{nomorethan};
1314 display td {class => 'data'},
1315 (InArray $notification{name}, @PREDEFINED_NOTIFICATIONS)
1324 display p {class => 'center'}, a {
1325 href => 'processnotification.cgi?action=Add',
1326 }, 'New notification', img {
1332 } # displayNotification
1334 sub displayRunlog (%) {
1339 for (qw(task system not status)) {
1341 if (($opts{$old} and $opts{$_}) and ($opts{$old} ne $opts{$_})) {
1349 unless ($opts{id}) {
1350 if ($opts{task} !~ /all/i) {
1351 $condition = "task like '%";
1352 $condition .= $opts{task} ? $opts{task} : '';
1356 if ($opts{system} !~ /all/i) {
1357 if ($opts{system} eq '<NULL>') {
1358 $condition .= ' and system is null';
1360 } elsif ($opts{system} ne 'All') {
1361 $condition .= " and system like '%$opts{system}%'";;
1365 if ($opts{status} !~ /all/i) {
1366 $condition .= ' and ';
1367 unless ($opts{not}) {
1368 $condition .= "status=$opts{status}";
1370 $condition .= "status<>$opts{status}";
1375 my $total = $clearadm->Count ('runlog', $condition);
1377 $opts{start} = $opts{'nextArrow.x'} ? $opts{next} : $opts{prev};
1382 my $next = $opts{start} + $opts{page} < $total
1383 ? $opts{start} + $opts{page}
1385 my $prev = $opts{start} - $opts{page} >= 0
1386 ? $opts{start} - $opts{page}
1389 my $opts = $opts{'nextArrow.x'} ? $opts{next} + 1 : $opts{prev} + 1;
1391 $opts .= $opts{start} + $opts{page} < $total
1392 ? $opts{start} + $opts{page}
1394 $opts .= " of $total";
1396 display start_form {
1398 action => 'runlog.cgi'
1401 # Hidden fields to pass along
1402 display input {name => 'prev', type => 'hidden', value => $prev};
1403 display input {name => 'next', type => 'hidden', value => $next};
1404 display input {name => 'oldtask', type => 'hidden', value => $opts{task}};
1405 display input {name => 'oldsystem', type => 'hidden', value => $opts{system}};
1406 display input {name => 'oldnot', type => 'hidden', value => $opts{not}};
1407 display input {name => 'oldstatus', type => 'hidden', value => $opts{status}};
1409 my $caption = start_table {
1415 $caption .= start_Tr;
1417 unless ($opts{id}) {
1418 $caption .= td {align => 'left'}, input {
1419 name => 'prevArrow',
1426 $caption .= td {align => 'left'}, img {
1428 disabled => 'disabled',
1432 $caption .= td {align => 'center'}, $opts;
1434 unless ($opts{id}) {
1435 $caption .= td {align => 'right'}, input {
1436 name => 'nextArrow',
1443 $caption .= td {align => 'right'}, img {
1445 disabled => 'disabled',
1451 $caption .= end_table;
1453 display start_table {cellspacing => 1, width => '98%'};
1455 display caption $caption;
1458 display th {class => 'labelCentered'}, '#';
1459 display th {class => 'labelCentered'}, 'ID';
1460 display th {class => 'labelCentered'}, 'Task';
1461 display th {class => 'labelCentered'}, 'System';
1462 display th {class => 'labelCentered'}, 'Started';
1463 display th {class => 'labelCentered'}, 'Ended';
1464 display th {class => 'labelCentered'}, 'Status';
1465 display th {class => 'labelCentered'}, 'Message';
1469 $opts{not} ||= 'false';
1471 display start_form {
1473 action => 'runlog.cgi'
1482 }, _makeRunlogSelection ('task', $opts{task});
1485 }, _makeRunlogSelection ('system', $opts{system});
1486 display td {class => 'filter'}, ' ';
1490 }, "Not: ", checkbox {
1493 checked => $opts{not} eq 'true' ? 1 : 0,
1498 }, _makeRunlogSelectionNumeric ('status', $opts{status});
1509 my $i = $opts{start};
1513 if ($opts{status}) {
1514 if ($opts{status} !~ /all/i) {
1515 $status = $opts{not} ne 'true' ? $opts{status} : "!$opts{status}";
1519 for ($clearadm->FindRunlog (
1527 my %runlog = setFields 'N/A', %{$_};
1529 my $class = $runlog{status} == 0
1532 my $classCentered = $runlog{status} == 0
1534 : 'dataAlertCentered';
1535 my $classRight = $runlog{status} == 0
1540 display td {class => 'dataCentered'}, ++$i;
1541 display td {class => 'dataCentered'}, $runlog{id};
1542 display td {class => 'data'}, a {
1543 href => "tasks.cgi?task=$runlog{task}"
1545 display td {class => 'data'}, $runlog{system} eq 'localhost'
1548 href => "systemdetails.cgi?system=$runlog{system}"
1550 display td {class => 'dataCentered'}, $runlog{started};
1551 display td {class => 'dataCentered'}, $runlog{ended};
1552 display td {class => $classRight}, $runlog{status};
1554 my $message = $runlog{message};
1555 $message =~ s/\r\n/<br>/g;
1557 display td {class => $class, width => '50%'}, $message;
1566 sub displaySchedule () {
1567 display start_table {cellspacing => 1};
1570 display th {class => 'labelCentered'}, 'Actions';
1571 display th {class => 'labelCentered'}, 'Active';
1572 display th {class => 'labelCentered'}, 'Name';
1573 display th {class => 'labelCentered'}, 'Task';
1574 display th {class => 'labelCentered'}, 'Notification';
1575 display th {class => 'labelCentered'}, 'Frequency';
1576 display th {class => 'labelCentered'}, 'Category';
1579 for ($clearadm->FindSchedule) {
1580 my %schedule = setFields 'N/A', %{$_};
1583 my $areYouSure = "Are you sure you want to delete the $schedule{name} "
1586 my $actions = start_form {
1588 action => 'processschedule.cgi',
1594 value => $schedule{name},
1597 if (InArray $schedule{name}, @PREDEFINED_SCHEDULES) {
1602 src => 'delete.png',
1605 title => 'Cannot delete predefined schedule',
1614 title => 'Cannot edit predefined schedule',
1620 src => 'delete.png',
1624 onclick => "return AreYouSure ('$areYouSure');",
1638 display td {class => 'dataCentered'}, $actions;
1639 display td {class => 'dataCentered'}, checkbox {
1640 disabled => 'disabled',
1641 checked => $schedule{active} eq 'true' ? 1 : 0,
1643 display td {class => 'data'}, $schedule{name};
1644 display td {class => 'data'}, a {
1645 href => "tasks.cgi?task=$schedule{task}"
1647 display td {class => 'data'}, a {
1648 href => "notifications.cgi?notification=$schedule{notification}"
1649 }, $schedule{notification};
1650 display td {class => 'data'}, $schedule{frequency};
1651 display td {class => 'data'},
1652 (InArray $schedule{name}, @PREDEFINED_SCHEDULES)
1661 display p {class => 'center'}, a {
1662 href => 'processschedule.cgi?action=Add',
1663 }, 'New schedule', img {
1671 sub displaySystem ($) {
1672 my ($systemName) = @_;
1674 my %system = $clearadm->GetSystem ($systemName);
1677 displayError "Nothing known about system $systemName";
1681 my $lastheardfromClass = 'dataCentered';
1682 my $lastheardfromData = $system{lastheardfrom};
1684 my %load = $clearadm->GetLatestLoadavg ($systemName);
1686 unless ($clearadm->SystemAlive (%system)) {
1687 $lastheardfromClass = 'dataCenteredAlert';
1688 $lastheardfromData = a {
1689 href => "alertlog.cgi?system=$system{name}",
1691 title => "Have not heard from $system{name} for a while"
1692 }, $system{lastheardfrom};
1693 $system{notification} = 'Heartbeat';
1696 my $admin = ($system{email})
1697 ? a {-href => "mailto:$system{email}"}, $system{admin}
1700 $system{alias} = setField $system{alias}, 'N/A';
1701 $system{region} = setField $system{region}, 'N/A';
1703 display start_table {cellspacing => 1};
1706 my $areYouSure = 'Are you sure you want to delete this system?\n'
1707 . "Doing so will remove all records related to $system{name}"
1708 . '\nincluding filesystem records and history as well as '
1709 . 'loadavg history.';
1711 my $actions = start_form {
1713 action => 'processsystem.cgi',
1719 value => $system{name},
1725 src => 'delete.png',
1729 onclick => "return AreYouSure ('$areYouSure');",
1739 $actions .= checkbox {
1740 disabled => 'disabled',
1741 checked => $system{active} eq 'true' ? 1 : 0,
1744 if ($system{notification}) {
1746 href => "alertlog.cgi?system=$system{name}"}, img {
1750 title => 'This system has alerts',
1754 display th {class => 'label'}, "$actions Name:";
1756 display td {class => 'dataCentered', colspan => 2}, $system{name};
1757 display th {class => 'label'}, 'Alias:';
1758 display td {class => 'dataCentered'}, $system{alias};
1759 display th {class => 'label'}, 'Admin:';
1760 display td {class => 'dataCentered', colspan => 2}, $admin;
1761 display th {class => 'label', colspan => 2}, 'Type:';
1762 display td {class => 'dataCentered'}, $system{type};
1766 display th {class => 'label'}, 'OS Version:';
1767 display td {class => 'data', colspan => 10}, $system{os};
1771 display th {class => 'label'}, 'Last Contacted:';
1773 class => $lastheardfromClass,
1775 }, "$lastheardfromData ",
1776 font {class => 'dim' }, "<br>Up: $load{uptime}";
1777 display th {class => 'label'}, 'Port:';
1778 display td {class => 'dataCentered'}, $system{port};
1779 display th {class => 'label'}, 'Threshold:';
1780 display td {class => 'dataCentered'}, $system{loadavgThreshold};
1781 display th {class => 'label'}, 'History:';
1782 display td {class => 'dataCentered'}, $system{loadavgHist};
1783 display th {class => 'label'}, 'Load Avg:';
1784 display td {class => 'data'},
1786 "plot.cgi?type=loadavg&system=$system{name}&scaling=Hour&points=24"
1788 src => "plotloadavg.cgi?system=$system{name}&tiny=1",
1792 my $description = $system{description};
1793 $description =~ s/\r\n/<br>/g;
1796 display th {class => 'label'}, 'Description:';
1797 display td {class => 'data', colSpan => 10}, $description;
1802 display p {class => 'center'}, a {
1803 href => 'processsystem.cgi?action=Add',
1804 }, 'New system', img {
1809 display h1 {class => 'center'},
1810 'Filesystem Details: ' . ucfirst $system{name};
1812 display start_table {cellspacing => 1};
1815 display th {class => 'labelCentered'}, 'Action';
1816 display th {class => 'labelCentered'}, 'Filesystem';
1817 display th {class => 'labelCentered'}, 'Type';
1818 display th {class => 'labelCentered'}, 'Mount';
1819 display th {class => 'labelCentered'}, 'Size';
1820 display th {class => 'labelCentered'}, 'Used';
1821 display th {class => 'labelCentered'}, 'Free';
1822 display th {class => 'labelCentered'}, 'Used %';
1823 display th {class => 'labelCentered'}, 'Threshold';
1824 display th {class => 'labelCentered'}, 'History';
1825 display th {class => 'labelCentered'}, 'Usage';
1828 for ($clearadm->FindFilesystem ($system{name})) {
1829 my %filesystem = %{$_};
1831 my %fs = $clearadm->GetLatestFS (
1832 $filesystem{system},
1833 $filesystem{filesystem}
1836 my $size = autoScale $fs{size};
1837 my $used = autoScale $fs{used};
1838 my $free = autoScale $fs{free};
1840 # TODO: Note that this percentages does not agree with df output. I'm not
1842 my $usedPct = $fs{size} == 0 ? 0
1844 (($fs{reserve} + $fs{used}) / $fs{size} * 100));
1846 my $class = $usedPct < $filesystem{threshold}
1849 my $classCentered = $class . 'Centered';
1850 my $classRight = $class . 'Right';
1853 display start_td {class => 'data'};
1855 my $areYouSure = 'Are you sure you want to delete '
1856 . "$system{name}:$filesystem{filesystem}?" . '\n'
1857 . 'Doing so will remove all records related to this\n'
1858 . 'filesystem and its history.';
1860 display start_form {
1862 action => 'processfilesystem.cgi',
1868 value => $system{name},
1872 name => 'filesystem',
1873 value => $filesystem{filesystem},
1879 src => 'delete.png',
1883 onclick => "return AreYouSure ('$areYouSure');"
1894 if ($filesystem{notification}) {
1896 href => "alertlog.cgi?system=$filesystem{system}"}, img {
1900 title => 'This filesystem has alerts',
1905 display td {class => $class}, $filesystem{filesystem};
1906 display td {class => $classCentered}, $filesystem{fstype};
1907 display td {class => $class}, $filesystem{mount};
1908 display td {class => $classRight}, $size;
1909 display td {class => $classRight}, $used;
1910 display td {class => $classRight}, $free;
1911 display td {class => $classRight}, "$usedPct%";
1912 display td {class => $classRight}, "$filesystem{threshold}%";
1913 display td {class => $classCentered}, $filesystem{filesystemHist};
1914 display td {class => $classCentered},
1916 "plot.cgi?type=filesystem&system=$system{name}"
1917 . "&filesystem=$filesystem{filesystem}"
1918 . "&scaling=Day&points=7"
1920 src => "plotfs.cgi?system=$system{name}&"
1921 . "filesystem=$filesystem{filesystem}"
1933 sub displayTask (;$) {
1936 display start_table {cellspacing => 1, width => '98%'};
1939 display th {class => 'labelCentered'}, 'Actions';
1940 display th {class => 'labelCentered'}, 'Name';
1941 display th {class => 'labelCentered'}, 'System';
1942 display th {class => 'labelCentered'}, 'Description';
1943 display th {class => 'labelCentered'}, 'Command';
1944 display th {class => 'labelCentered'}, 'Restartable';
1945 display th {class => 'labelCentered'}, 'Category';
1948 for ($clearadm->FindTask ($task)) {
1951 $task{system} = 'All Systems'
1952 unless $task{system};
1955 my $areYouSure = "Are you sure you want to delete the $task{name} task?";
1957 my $actions = start_form {
1959 action => 'processtask.cgi',
1965 value => $task{name},
1968 if (InArray $task{name}, @PREDEFINED_TASKS) {
1973 src => 'delete.png',
1976 title => 'Cannot delete predefined task',
1985 title => 'Cannot edit predefined task',
1991 src => 'delete.png',
1995 onclick => "return AreYouSure ('$areYouSure');",
2009 display td {class => 'dataCentered'}, $actions;
2010 display td {class => 'data'}, $task{name};
2011 display td {class => 'data'}, $task{system};
2012 display td {class => 'data'}, $task{description};
2013 display td {class => 'data'}, $task{command};
2014 display td {class => 'dataCentered'}, $task{restartable};
2015 display td {class => 'data'},
2016 (InArray $task{name}, @PREDEFINED_TASKS) ? 'Predefined' : 'User Defined';
2022 display p {class => 'center'}, a {
2023 href => 'processtask.cgi?action=Add',
2024 }, 'New task', img {
2032 sub editAlert (;$) {
2035 display start_form (
2037 -action => 'processalert.cgi',
2038 -onsubmit => 'return validateAlert (this);',
2044 %alert = $clearadm->GetAlert ($alert);
2064 display start_table {cellspacing => 1};
2067 display th {class => 'labelCentered'}, 'Name';
2068 display th {class => 'labelCentered'}, 'Type';
2069 display th {class => 'labelCentered'}, 'Who';
2076 class => 'inputfield',
2081 value => $alert ? $alert{name} : '',
2084 class => 'dataCentered',
2087 class => 'dropdown',
2088 values => [ 'email', 'page', 'im' ],
2089 default => $alert ? $alert{type} : 'email',
2094 class => 'inputfield',
2099 value => $alert ? $alert{who} : '',
2105 display p submit ({value => $alert ? 'Update' : 'Add'}), reset;
2106 display '</center>';
2113 sub editFilesystem ($$) {
2114 my ($system, $filesystem) = @_;
2116 display start_form (
2118 -action => 'processfilesystem.cgi',
2121 display start_table {width => '800px', cellspacing => 1};
2124 display th {class => 'labelCentered'}, 'Filesystem';
2125 display th {class => 'labelCentered'}, 'Type';
2126 display th {class => 'labelCentered'}, 'Mount';
2127 display th {class => 'labelCentered'}, 'Size';
2128 display th {class => 'labelCentered'}, 'Used';
2129 display th {class => 'labelCentered'}, 'Free';
2130 display th {class => 'labelCentered'}, 'Used %';
2131 display th {class => 'labelCentered'}, 'History';
2132 display th {class => 'labelCentered'}, 'Threshold';
2135 my %filesystem = $clearadm->GetFilesystem ($system, $filesystem);
2136 my %fs = $clearadm->GetLatestFS ($system, $filesystem);
2146 value => $filesystem{system},
2149 name => 'filesystem',
2151 value => $filesystem{filesystem},
2154 my $size = autoScale $fs{size};
2155 my $used = autoScale $fs{used};
2156 my $free = autoScale $fs{free};
2159 display td {class => 'data'}, $filesystem{filesystem};
2160 display td {class => 'dataCentered'}, $filesystem{fstype};
2161 display td {class => 'data'}, $filesystem{mount};
2162 display td {class => 'dataRight'}, $size;
2163 display td {class => 'dataRight'}, $used;
2164 display td {class => 'dataRight'}, $free;
2165 # TODO: Note that this percentages does not agree with df output. I'm not
2167 display td {class => 'dataCentered'},
2168 sprintf ('%.0f%%', (($fs{reserve} + $fs{used}) / $fs{size} * 100));
2170 my $historyDropdown = popup_menu {
2171 name => 'filesystemHist',
2172 class => 'dropdown',
2187 default => $system ? $filesystem{filesystemHist} : '6 months',
2191 class => 'dataRight',
2192 }, $historyDropdown;
2194 my $thresholdDropdown = popup_menu {
2195 name => 'threshold',
2196 class => 'dropdown',
2197 values => [1 .. 100],
2198 default => $filesystem{threshold},
2200 display td {class => 'dataCentered'}, $thresholdDropdown . '%';
2206 display p submit ({value => 'Update'}), reset;
2207 display '</center>';
2214 sub editNotification (;$) {
2215 my ($notification) = @_;
2217 display start_form (
2219 -action => 'processnotification.cgi',
2220 -onsubmit => 'return validateNotification (this);',
2225 if ($notification) {
2226 %notification = $clearadm->GetNotification ($notification);
2229 unless %notification;
2234 value => $notification,
2246 display start_table {cellspacing => 1};
2249 display th {class => 'labelCentered'}, 'Name';
2250 display th {class => 'labelCentered'}, 'Alert';
2251 display th {class => 'labelCentered'}, 'Condition';
2252 display th {class => 'labelCentered'}, 'Not More Than';
2259 class => 'inputfield',
2264 value => $notification ? $notification{name} : '',
2268 class => 'dataCentered',
2269 }, makeAlertDropdown undef, $notification{alert}
2270 ? $notification{alert}
2276 class => 'inputfield',
2281 value => $notification ? $notification{cond} : '',
2284 class => 'dataCentered',
2285 }, makeNoMoreThanDropdown undef, $notification{nomorethan};
2291 display p submit ({value => $notification ? 'Update' : 'Add'}), reset;
2292 display '</center>';
2297 } # editNotification
2299 sub editSchedule (;$) {
2300 my ($schedule) = @_;
2302 display start_form (
2304 -action => 'processschedule.cgi',
2305 -onsubmit => 'return validateSchedule (this);',
2311 %schedule = $clearadm->GetSchedule ($schedule);
2331 display start_table {cellspacing => 1};
2334 display th {class => 'labelCentered'}, 'Active';
2335 display th {class => 'labelCentered'}, 'Name';
2336 display th {class => 'labelCentered'}, 'Task';
2337 display th {class => 'labelCentered'}, 'Notification';
2338 display th {class => 'labelCentered'}, 'Frequency';
2343 class => 'dataCentered',
2347 checked => $schedule{active} eq 'false' ? 0 : 1,
2353 class => 'inputfield',
2358 value => $schedule ? $schedule{name} : '',
2361 class => 'dataCentered',
2362 }, makeTaskDropdown undef, $schedule{task};
2364 class => 'dataCentered',
2365 }, makeNotificationDropdown undef, $schedule{notification};
2368 my $multiplier = 'minutes';
2370 if ($schedule{frequency} =~ /(\d+)\s(\S+)/ ) {
2374 $multiplier .= 's' if $nbr == 1;
2380 class => 'inputfieldRight',
2388 makeMultiplierDropdown undef, $multiplier;
2394 display p submit ({value => $schedule ? 'Update' : 'Add'}), reset;
2395 display '</center>';
2402 sub editSystem (;$) {
2405 display start_form (
2407 -action => 'processsystem.cgi',
2408 -onsubmit => 'return validateSystem (this);',
2414 %system = $clearadm->GetSystem ($system);
2434 display start_table {cellspacing => 1};
2437 display th {class => 'label'}, checkbox ({
2440 checked => $system{active} eq 'false' ? 0 : 1,
2445 display td {class => 'data'}, $system{name};
2450 class => 'inputfield',
2458 display th {class => 'label'}, 'Alias:';
2462 class => 'inputfield',
2467 value => $system ? $system{alias} : '',
2470 display th {class => 'label'}, 'Port:';
2472 class => 'dataRight',
2474 class => 'inputfieldRight',
2481 : $Clearadm::CLEAROPTS{CLEARADM_PORT},
2484 my $systemTypeDropdown = popup_menu {
2486 class => 'dropdown',
2487 values => ['Unix', 'Linux', 'Windows'],
2488 default => $system ? $system{type} : 'Linux',
2491 display th {class => 'label'}, 'Type:';
2493 class => 'dataRight',
2494 }, $systemTypeDropdown;
2498 display th {class => 'label'}, 'Admin:';
2502 class => 'inputfield',
2507 value => $system ? $system{admin} : '',
2509 display th {class => 'label'}, 'Admin Email:';
2513 class => 'inputfield',
2518 value => $system ? $system{email} : '',
2521 display th {class => 'label'}, 'Threshold:';
2523 class => 'dataRight',
2525 class => 'inputfieldRight',
2527 name => 'loadavgThreshold',
2531 ? $system{loadavgThreshold}
2532 : $Clearadm::CLEAROPTS{CLEARADM_LOADAVG_THRESHOLD},
2535 my $historyDropdown = popup_menu {
2536 name => 'loadavgHist',
2537 class => 'dropdown',
2552 default => $system ? $system{loadavgHist} : '6 months',
2555 display th {class => 'label'}, 'History:';
2557 class => 'dataRight',
2558 }, $historyDropdown;
2560 my $description = $system ? $system{description} : '';
2561 $description =~ s/\r\n/<br>/g;
2564 display th {class => 'label'}, 'Description:';
2569 class => 'inputfield',
2571 name => 'description',
2573 value => $description,
2579 display p submit ({value => $system ? 'Update' : 'Add'}), reset;
2580 display '</center>';
2590 display start_form (
2592 -action => 'processtask.cgi',
2593 -onsubmit => 'return validateTask (this);',
2599 %task = $clearadm->GetTask ($task);
2619 display start_table {cellspacing => 1};
2622 display th {class => 'labelCentered'}, 'Name';
2623 display th {class => 'labelCentered'}, 'System';
2624 display th {class => 'labelCentered'}, 'Description';
2625 display th {class => 'labelCentered'}, 'Command';
2626 display th {class => 'labelCentered'}, 'Restartable';
2633 class => 'inputfield',
2638 value => $task ? $task{name} : '',
2640 my $systemDropdown = makeSystemDropdown (
2642 $task{system} ? $task{system} : 'localhost',
2644 'localhost' => 'localhost',
2648 display td {class => 'data'}, $systemDropdown;
2653 class => 'inputfield',
2655 name => 'description',
2658 value => $task ? $task{description} : '',
2664 class => 'inputfield',
2669 value => $task ? $task{command} : '',
2673 class => 'dataCentered',
2674 }, makeRestartableDropdown undef, $task{restartable};
2680 display p submit ({value => $task ? 'Update' : 'Add'}), reset;
2681 display '</center>';
2689 my $clearscm = a {-href => 'http://clearscm.com'}, 'ClearSCM, Inc.';
2691 # Figure out which script by using CLEARADM_BASE.
2692 my $script = basename (url {-absolute => 1});
2693 $script = 'index.cgi'
2694 if $script eq 'clearadm';
2696 my $scriptFullPath = "$Clearadm::CLEAROPTS{CLEARADM_BASE}/$script";
2698 my ($year, $mon, $mday, $hour, $min, $sec) =
2699 ymdhms ((stat ($scriptFullPath))[9]);
2701 my $dateModified = "$mon/$mday/$year @ $hour:$min";
2704 -href => "http://clearscm.com/php/scm_man.php?file=clearadm/$script"
2709 display start_div {-class => 'copyright'};
2710 display "$script: Last modified: $dateModified";
2711 display br "Copyright © $year, $clearscm - All rights reserved";
2723 =head1 CONFIGURATION AND ENVIRONMENT
2725 DEBUG: If set then $debug is set to this level.
2727 VERBOSE: If set then $verbose is set to this level.
2729 TRACE: If set then $trace is set to this level.
2739 L<File::Basename|File::Basename>
2745 =head2 ClearSCM Perl Modules
2759 <a href="http://clearscm.com/php/scm_man.php?file=lib/Clearadm.pm">Clearadm</a><br>
2760 <a href="http://clearscm.com/php/scm_man.php?file=lib/DateUtils.pm">DateUtils</a><br>
2761 <a href="http://clearscm.com/php/scm_man.php?file=lib/Display.pm">Display</a><br>
2762 <a href="http://clearscm.com/php/scm_man.php?file=lib/Utils.pm">Utils</a><br>
2767 =head1 BUGS AND LIMITATIONS
2769 There are no known bugs in this module.
2771 Please report problems to Andrew DeFaria <Andrew@ClearSCM.com>.
2773 =head1 LICENSE AND COPYRIGHT
2775 Copyright (c) 2010, ClearSCM, Inc. All rights reserved.