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";
80 my $clearadm = Clearadm->new;
82 our $APPNAME= 'Clearadm';
83 our $VERSION = '$Revision: 1.46 $';
84 ($VERSION) = ($VERSION =~ /\$Revision: (.*) /);
109 makeFilesystemDropdown
111 makeNotificationDropdown
119 our @PREDEFINED_ALERTS = (
123 our @PREDEFINED_NOTIFICATIONS = (
132 our @PREDEFINED_TASKS = (
140 our @PREDEFINED_SCHEDULES = (
147 our @PREDEFINED_NOTMORETHAN = (
154 our @PREDEFINED_MULTIPLIERS = (
164 display font ({-class => 'error'}, '<br>DEBUG: '). $msg;
169 sub displayError ($) {
172 display font ({-class => 'error'}, 'Error: ') . $msg;
178 my ($field, $label) = @_;
180 $label ||= 'Unknown';
182 my $undef = font {-class => 'unknown'}, $label;
184 return defined $field ? $field : $undef;
188 my ($label, %rec) = @_;
190 $rec{$_} = setField ($rec{$_}, $label)
199 foreach (keys %vars) {
200 dbug "$_: $vars{$_}";
211 # Make the image fit the message. It seems that characters are ~ 7px wide.
212 my $imageLength = length ($msg) * 7;
214 my $errorImage = GD::Image->new ($imageLength, 20);
216 # Allocate some colors
217 my $white = $errorImage->colorAllocate (255, 255, 255);
218 my $red = $errorImage->colorAllocate (255, 0, 0);
220 # Allow the text to shine through
221 $errorImage->transparent($white);
222 $errorImage->interlaced('true');
224 # Now put out the message
225 $errorImage->string (gdMediumBoldFont, 0, 0, $msg, $red);
228 print "Content-type: image/png\n\n";
229 print $errorImage->png;
231 # Since we've "returned" the error in the form of an image, there's nothing
232 # left for us to do so we can exit
240 my $meg = (1024 * $kbyte);
241 my $gig = (1024 * $meg);
243 my $size = $amount > $gig
244 ? sprintf ('%.2f Gig', $amount / $gig)
246 ? sprintf ('%.2f Meg', $amount / $meg)
247 : sprintf ('%.2f Kbyte', $amount / $kbyte);
252 sub _makeAlertlogSelection ($$) {
253 my ($name, $default) = @_;
259 $values{All} = 'All';
261 $values{$$_{$name}} = $$_{$name}
262 foreach ($clearadm->FindAlertlog);
264 my $dropdown = popup_menu {
267 values => [sort keys %values],
272 } # _makeAlertlogSelection
274 sub _makeRunlogSelection ($$) {
275 my ($name, $default) = @_;
279 my @values = sort $clearadm->GetUniqueList ('runlog', $name);
281 unshift @values, 'All';
289 $values{NULL} = '<NULL>';
293 my $dropdown = popup_menu {
302 } # _makeRunlogSelection
304 sub _makeRunlogSelectionNumeric ($$) {
305 my ($name, $default) = @_;
309 my @values = sort {$a <=> $b} $clearadm->GetUniqueList ('runlog', $name);
311 unshift @values, 'All';
313 my $dropdown = popup_menu {
321 } # _makeRunlogSelection
323 sub makeAlertDropdown (;$$) {
324 my ($label, $default) = @_;
330 push @values, $$_{name}
331 foreach ($clearadm->FindAlert);
333 my $dropdown = "$label ";
334 $dropdown .= popup_menu {
337 values => [sort @values],
342 } # makeAlertDropdown
344 sub makeMultiplierDropdown (;$$) {
345 my ($label, $default) = @_;
349 my $dropdown = "$label ";
350 $dropdown .= popup_menu {
351 name => 'multiplier',
353 values => [sort @PREDEFINED_MULTIPLIERS],
358 } # makeMultiplierDropdown
360 sub makeNoMoreThanDropdown (;$$) {
361 my ($label, $default) = @_;
365 my $dropdown = "$label ";
366 $dropdown .= popup_menu {
367 name => 'nomorethan',
369 values => [sort @PREDEFINED_NOTMORETHAN],
374 } # makeNoMorThanDropdown
376 sub makeFilesystemDropdown ($;$$$) {
377 my ($system, $label, $default, $onchange) = @_;
383 foreach ($clearadm->FindFilesystem ($system)) {
384 my %filesystem = %{$_};
386 my $value = "$filesystem{filesystem} ($filesystem{mount})";
388 $filesystems{$filesystem{filesystem}} = $value;
391 my $dropdown .= "$label ";
392 $dropdown .= popup_menu {
393 name => 'filesystem',
395 values => [sort keys %filesystems],
396 labels => \%filesystems,
397 onChange => ($onchange) ? $onchange : '',
401 return span {id => 'filesystems'}, $dropdown;
402 } # makeFilesystemDropdown
404 sub makeIntervalDropdown (;$$$) {
405 my ($label, $default, $onchange) = @_;
416 $default = ucfirst lc $default
419 my $dropdown = "$label ";
420 $dropdown .= popup_menu {
422 id => 'scalingFactor',
424 values => [@intervals],
426 onchange => $onchange,
429 return span {id => 'scaling'}, $dropdown;
430 } # makeIntervalDropdown;
432 sub makeNotificationDropdown (;$$) {
433 my ($label, $default) = @_;
439 push @values, $$_{name}
440 foreach ($clearadm->FindNotification);
442 my $dropdown = "$label ";
443 $dropdown .= popup_menu {
444 name => 'notification',
446 values => [sort @values],
451 } # makeNotificationDropdown
453 sub makeRestartableDropdown (;$$) {
454 my ($label, $default) = @_;
463 my $dropdown = "$label ";
464 $dropdown .= popup_menu {
465 name => 'restartable',
472 } # makeRestartableDropdown
474 sub makeSystemDropdown (;$$$%) {
475 my ($label, $default, $onchange, %systems) = @_;
479 foreach ($clearadm->FindSystem) {
482 my $value = $system{name};
483 $value .= $system{alias} ? " ($system{alias})" : '';
485 $systems{$system{name}} = $value;
488 my $systemDropdown .= "$label ";
489 $systemDropdown .= popup_menu {
492 values => [sort keys %systems],
494 onchange => ($onchange) ? $onchange : '',
498 return span {id => 'systems'}, $systemDropdown;
499 } # makeSystemDropdown
501 sub makeTaskDropdown (;$$) {
502 my ($label, $default) = @_;
508 push @values, $$_{name}
509 foreach ($clearadm->FindTask);
511 my $taskDropdown = "$label ";
512 $taskDropdown .= popup_menu {
515 values => [sort @values],
519 return $taskDropdown;
522 sub makeTimeDropdown ($$$;$$$$$) {
540 push @times, 'Earliest';
542 if ($table =~ /loadavg/i) {
543 push @times, $$_{timestamp}
544 foreach ($clearadm->GetLoadavg ($system, undef, undef, undef, $interval));
545 } elsif ($table =~ /filesystem/i) {
546 push @times, $$_{timestamp}
547 foreach ($clearadm->GetFS ($system, $filesystem, undef, undef, undef, $interval));
550 push @times, 'Latest';
553 $default = $name eq 'start' ? 'Earliest' : 'Latest';
556 my $timeDropdown = "$label ";
557 $timeDropdown .= span {id => $elementID}, popup_menu {
564 return $timeDropdown;
568 my ($title, $type) = @_;
571 $title = "$APPNAME: $title";
579 -author => 'Andrew DeFaria <Andrew@ClearSCM.com>',
581 keywords => 'ClearSCM Clearadm',
582 copyright => 'Copyright (c) ClearSCM, Inc. 2010, All rights reserved',
585 -language => 'JavaScript',
586 -src => 'clearadm.js',
588 -style => ['clearadm.css', 'clearmenu.css'],
593 my $ieTableWrapStart = '<!--[if gt IE 6]><!--></a><!--<![endif]--><!--'
594 . '[if lt IE 7]><table border="0" cellpadding="0" '
595 . 'cellspacing="0"><tr><td><![endif]-->';
596 my $ieTableWrapEnd = '<!--[if lte IE 6]></td></tr></table></a><![endif]-->';
599 display div {id=>'mastheadlogo'}, h1 {class => 'title'}, $APPNAME;
600 display start_div {class => 'menu'};
603 display ul li a {href => '/clearadm'}, 'Home';
605 my @allSystems = $clearadm->FindSystem;
610 display a {href => 'systems.cgi'}, "Systems$ieTableWrapStart";
612 foreach (@allSystems) {
614 my $sysName = ucfirst $system{name};
615 $sysName .= " ($system{alias})"
619 href => "systemdetails.cgi?system=$system{name}"
620 }, ucfirst " $sysName";
623 display $ieTableWrapEnd;
631 display a {href => 'filesystems.cgi'}, "Filesystems$ieTableWrapStart";
633 foreach (@allSystems) {
635 my $sysName = ucfirst $system{name};
636 $sysName .= " ($system{alias})"
640 href => "filesystems.cgi?system=$system{name}"
641 }, ucfirst " $sysName";
644 display $ieTableWrapEnd;
651 display a {href => '#'}, "Servers$ieTableWrapStart";
652 display start_ul {class => 'skinny'};
654 display start_a {href => 'vobs.cgi'};
655 display "<span class='drop'><span>VOB</span>»</span>$ieTableWrapStart";
657 display li a {href => "systemdetails.cgi?system=jupiter"}, ' Jupiter (defaria.com)';
659 display $ieTableWrapEnd;
663 display start_a {href => 'views.cgi'};
664 display "<span class='drop'><span>View</span>»</span>$ieTableWrapStart";
666 display li a {href => "systemdetails.cgi?system=earth"}, ' Earth';
667 display li a {href => "systemdetails.cgi?system=mars"}, ' Mars';
669 display $ieTableWrapEnd;
671 display $ieTableWrapEnd;
678 display a {href => 'vobs.cgi'}, "VOBs$ieTableWrapStart";
680 display li a {href => '#'}, ' /vobs/clearscm';
681 display li a {href => '#'}, ' /vobs/clearadm';
682 display li a {href => '#'}, ' /vobs/test';
683 display li a {href => '#'}, ' /vobs/test2';
685 display $ieTableWrapEnd;
692 display a {href => 'views.cgi'}, "Views$ieTableWrapStart";
694 display li a {href => 'viewager.cgi'}, ' View Ager';
695 display li a {href => '#'}, ' Releast View';
697 display $ieTableWrapEnd;
704 display a {href => '#'}, "Configure$ieTableWrapStart";
706 display li a {href => 'alerts.cgi'}, ' Alerts';
707 display li a {href => 'notifications.cgi'}, ' Notifications';
708 display li a {href => 'schedule.cgi'}, ' Schedule';
709 display li a {href => 'tasks.cgi'}, ' Tasks';
711 display $ieTableWrapEnd;
718 display a {href => '#'}, "Logs$ieTableWrapStart";
720 display li a {href => 'alertlog.cgi'}, ' Alert';
721 display li a {href => 'runlog.cgi'}, ' Run';
723 display $ieTableWrapEnd;
730 display a {href => '#'}, "Help$ieTableWrapStart";
731 display start_ul {class => 'rightmenu'};
732 display li a {href => 'readme.cgi'}, " About: $APPNAME $VERSION";
734 display $ieTableWrapEnd;
739 display start_div {class => 'page'};
744 sub displayAlert (;$) {
747 display start_table {cellspacing => 1};
750 display th {class => 'labelCentered'}, 'Actions';
751 display th {class => 'labelCentered'}, 'Name';
752 display th {class => 'labelCentered'}, 'Type';
753 display th {class => 'labelCentered'}, 'Who';
754 display th {class => 'labelCentered'}, 'Category';
757 foreach ($clearadm->FindAlert ($alert)) {
760 $alert{who} = setField $alert{who}, 'System Administrator';
763 my $areYouSure = "Are you sure you want to delete the $alert{name} alert?";
765 my $actions = start_form {
767 action => 'processalert.cgi',
773 value => $alert{name},
776 if (InArray $alert{name}, @PREDEFINED_ALERTS) {
784 title => 'Cannot delete predefined alert',
793 title => 'Cannot edit predefined alert',
803 onclick => "return AreYouSure ('$areYouSure');",
817 my $who = $alert{who};
819 if ($who =~ /^([a-zA-Z0-9._-]+)@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,4})$/) {
820 $who = a {href => "mailto:$1\@$2"}, $who;
823 display td {class => 'dataCentered'}, $actions;
824 display td {class => 'data'}, $alert{name};
825 display td {class => 'data'}, $alert{type};
826 display td {class => 'data'}, $who;
827 display td {class => 'data'},
828 (InArray $alert{name}, @PREDEFINED_ALERTS) ? 'Predefined' : 'User Defined';
834 display p {class => 'center'}, a {
835 href => 'processalert.cgi?action=Add',
836 }, 'New alert ', img {
844 sub displayAlertlog (%) {
849 unless (($opts{oldalert} and $opts{alert} and
850 $opts{oldalert} eq $opts{alert}) and
851 ($opts{oldsystem} and $opts{system} and
852 $opts{oldsystem} eq $opts{system}) and
853 ($opts{oldnotification} and $opts{notification} and
854 $opts{oldnotification} eq $opts{notification})) {
861 $condition = "alert like '%";
862 $condition .= $opts{alert} ? $opts{alert} : '';
864 $condition .= ' and ';
865 $condition .= "system like '%";
866 $condition .= $opts{system} ? $opts{system} : '';
868 $condition .= ' and ';
869 $condition .= "notification like '%";
870 $condition .= $opts{notification} ? $opts{notification} : '';
874 my $total = $clearadm->Count ('alertlog', $condition);
876 if ($opts{'nextArrow.x'}) {
877 $opts{start} = $opts{next};
878 } elsif ($opts{'prevArrow.x'}) {
879 $opts{start} = $opts{prev};
884 my $next = $opts{start} + $opts{page} < $total
885 ? $opts{start} + $opts{page}
887 my $prev = $opts{start} - $opts{page} >= 0
888 ? $opts{start} - $opts{page}
891 my $opts = $opts{start} + 1;
893 $opts .= $opts{start} + $opts{page} < $total
894 ? $opts{start} + $opts{page}
896 $opts .= " of $total";
900 action => 'alertlog.cgi'
903 # Hidden fields to pass along
904 display input {name => 'prev', type => 'hidden', value => $prev};
905 display input {name => 'next', type => 'hidden', value => $next};
910 value => $opts{alert},
915 value => $opts{system},
918 name => 'oldnotification',
920 value => $opts{notification},
923 my $caption = start_table {
929 $caption .= start_Tr;
932 $caption .= td {align => 'left'}, input {
940 $caption .= td {align => 'left'}, img {
942 disabled => 'disabled',
946 $caption .= td {align => 'center'}, $opts;
949 $caption .= td {align => 'right'}, input {
957 $caption .= td {align => 'right'}, img {
959 disabled => 'disabled',
965 $caption .= end_table;
967 display start_table {cellspacing => 1, width => '98%'};
969 display caption $caption;
972 display th {class => 'labelCentered'}, '#';
973 display th {class => 'labelCentered'}, 'Delete';
974 display th {class => 'labelCentered'}, 'Name';
975 display th {class => 'labelCentered'}, 'System';
976 display th {class => 'labelCentered'}, 'Notification';
977 display th {class => 'labelCentered'}, 'Date/Time';
978 display th {class => 'labelCentered'}, 'Runlog';
979 display th {class => 'labelCentered'}, 'Message';
990 }, _makeAlertlogSelection ('alert', $opts{alert});
993 }, _makeAlertlogSelection ('system', $opts{system});
996 }, _makeAlertlogSelection ('notification', $opts{notification});
1008 # TODO: Would like to have Clear All Alerts be Clear Alerts and for it to
1009 # clear only the alerts that have been filtered.
1011 href => 'deletealertlog.cgi?alertlogid=all'
1014 value => 'Clear All Events',
1015 onclick => "return AreYouSure('Are you sure you want to delete all alerts?');",
1019 my $i = $opts{start};
1021 foreach ($clearadm->FindAlertlog (
1024 $opts{notification},
1028 my %alertlog = setFields 'N/A', %{$_};
1031 my %system = $clearadm->GetSystem ($alertlog{system});
1033 display td {class => 'dataCentered'}, ++$i;
1034 display td {class => 'dataCentered'}, a {
1035 href => "deletealertlog.cgi?alertlogid=$alertlog{id}"
1037 src => 'delete.png',
1041 onclick => "return AreYouSure ('Are you sure you wish to delete this alertlog entry?');",
1043 display td {class => 'data'}, a {
1044 href => "alerts.cgi?alert=$alertlog{alert}"
1045 }, $alertlog{alert};
1046 display td {class => 'data'}, a {
1047 href => "systemdetails.cgi?system=$alertlog{system}"
1048 }, $alertlog{system};
1049 display td {class => 'data'}, a {
1050 href => "notifications.cgi?notification=$alertlog{notification}"
1051 }, $alertlog{notification};
1052 display td {class => 'data'}, $alertlog{timestamp};
1053 display td {class => 'dataCentered'}, a {
1054 href => "runlog.cgi?id=$alertlog{runlog}"
1055 }, $alertlog{runlog};
1056 display td {class => 'data'}, $alertlog{message};
1067 sub displayFilesystem ($) {
1068 my ($systemName) = @_;
1070 display start_table {cellspacing => 1, width => '98%'};
1073 display th {class => 'labelCentered'}, 'Action';
1074 display th {class => 'labelCentered'}, 'Name';
1075 display th {class => 'labelCentered'}, 'Alias';
1076 display th {class => 'labelCentered'}, 'Admin';
1077 display th {class => 'labelCentered'}, 'Filesystem';
1078 display th {class => 'labelCentered'}, 'Mount';
1079 display th {class => 'labelCentered'}, 'Type';
1080 display th {class => 'labelCentered'}, 'History';
1081 display th {class => 'labelCentered'}, 'Used';
1082 display th {class => 'labelCentered'}, 'Threshold';
1083 display th {class => 'labelCentered'}, 'Usage';
1086 foreach ($clearadm->FindSystem ($systemName)) {
1089 %system = setFields ('N/A', %system);
1091 my $admin = ($system{email} !~ 'N/A')
1092 ? a {-href => "mailto:$system{email}"}, $system{admin}
1095 foreach ($clearadm->FindFilesystem ($system{name})) {
1096 my %filesystem = %{$_};
1098 my %fs = $clearadm->GetLatestFS ($system{name}, $filesystem{filesystem});
1100 my $size = autoScale $fs{size};
1101 my $used = autoScale $fs{used};
1102 my $free = autoScale $fs{free};
1104 # TODO: Note that this percentages does not agree with df output. I'm not
1106 my $usedPct = $fs{size} == 0 ? 0
1108 (($fs{reserve} + $fs{used}) / $fs{size} * 100));
1110 my $alias = ($system{alias} !~ 'N/A')
1112 href => "systemdetails.cgi?system=$system{name}"
1116 my $class = $usedPct < $filesystem{threshold}
1119 my $classRight = $usedPct < $filesystem{threshold}
1122 my $classCentered = $usedPct < $filesystem{threshold}
1124 : 'dataCenteredAlert';
1125 my $classRightTop = $usedPct < $filesystem{threshold}
1127 : 'dataRightAlertTop';
1130 display start_td {class => 'dataCentered'};
1132 my $areYouSure = 'Are you sure you want to delete '
1133 . "$system{name}:$filesystem{filesystem}?" . '\n'
1134 . 'Doing so will remove all records related to this\n'
1135 . 'filesystem and its history.';
1137 display start_form {
1139 action => "processfilesystem.cgi",
1145 value => $system{name},
1149 name => 'filesystem',
1150 value => $filesystem{filesystem},
1156 src => 'delete.png',
1160 onclick => "return AreYouSure ('$areYouSure');"
1171 if ($filesystem{notification}) {
1173 href => "alertlog.cgi?system=$filesystem{system}"}, img {
1177 title => 'This filesystem has alerts',
1184 display td {class => $class},
1185 a {-href => "systemdetails.cgi?system=$system{name}"}, $system{name};
1186 display td {class => $class}, $alias;
1187 display td {class => $class}, $admin;
1188 display td {class => $class}, $filesystem{filesystem};
1189 display td {class => $class}, $filesystem{mount};
1190 display td {class => $class}, $filesystem{fstype};
1191 display td {class => $classCentered}, $filesystem{filesystemHist};
1192 display td {class => $classRightTop}, "$used ($usedPct%)<br>",
1193 font {class => 'unknown'}, "$fs{timestamp}";
1194 display td {class => $classRightTop}, "$filesystem{threshold}%";
1195 display td {class => $class},
1197 "plot.cgi?type=filesystem&system=$system{name}"
1198 . "&filesystem=$filesystem{filesystem}&scaling=Day&points=7"
1200 src => "plotfs.cgi?system=$system{name}"
1201 . "&filesystem=$filesystem{filesystem}&tiny=1",
1211 } # displayFilesystem
1213 sub displayNotification (;$) {
1214 my ($notification) = @_;
1216 display start_table {cellspacing => 1};
1219 display th {class => 'labelCentered'}, 'Actions';
1220 display th {class => 'labelCentered'}, 'Name';
1221 display th {class => 'labelCentered'}, 'Alert';
1222 display th {class => 'labelCentered'}, 'Condition';
1223 display th {class => 'labelCentered'}, 'Not More Than';
1224 display th {class => 'labelCentered'}, 'Category';
1227 foreach ($clearadm->FindNotification ($notification)) {
1228 my %notification= setFields 'N/A', %{$_};
1231 my $areYouSure = "Are you sure you want to delete the $notification{name} "
1234 my $actions = start_form {
1236 action => 'processnotification.cgi',
1242 value => $notification{name},
1245 if (InArray $notification{name}, @PREDEFINED_NOTIFICATIONS) {
1250 src => 'delete.png',
1253 title => 'Cannot delete predefined notification',
1262 title => 'Cannot edit predefined notification',
1268 src => 'delete.png',
1272 onclick => "return AreYouSure ('$areYouSure');",
1286 display td {class => 'dataCentered'}, $actions;
1287 display td {class => 'data'}, $notification{name};
1288 display td {class => 'data'}, a {
1289 href => "alerts.cgi?alert=$notification{alert}"
1290 }, $notification{alert};
1291 display td {class => 'data'}, $notification{cond};
1292 display td {class => 'data'}, $notification{nomorethan};
1293 display td {class => 'data'},
1294 (InArray $notification{name}, @PREDEFINED_NOTIFICATIONS)
1303 display p {class => 'center'}, a {
1304 href => 'processnotification.cgi?action=Add',
1305 }, 'New notification', img {
1311 } # displayNotification
1313 sub displayRunlog (%) {
1318 unless (($opts{oldtask} and $opts{task} or
1319 $opts{oldtask} eq $opts{task}) and
1320 ($opts{oldsystem} and $opts{system} or
1321 $opts{oldsystem} eq $opts{system}) and
1322 ($opts{oldnot} and $opts{not} or
1323 $opts{oldnot} eq $opts{not}) and
1324 ($opts{oldstatus} and $opts{status} or
1325 $opts{oldstatus} eq $opts{status})) {
1331 unless ($opts{id}) {
1332 $condition = "task like '%";
1333 $condition .= $opts{task} ? $opts{task} : '';
1336 if ($opts{system}) {
1337 if ($opts{system} eq '<NULL>') {
1338 $condition .= ' and system is null';
1340 } elsif ($opts{system} ne 'All') {
1341 $condition .= " and system like '%$opts{system}%'";;
1345 if (defined $opts{status}) {
1346 $condition .= ' and ';
1347 unless ($opts{not}) {
1348 $condition .= "status=$opts{status}";
1350 $condition .= "status<>$opts{status}";
1355 my $total = $clearadm->Count ('runlog', $condition);
1357 $opts{start} = $opts{'nextArrow.x'} ? $opts{next} : $opts{prev};
1362 my $next = $opts{start} + $opts{page} < $total
1363 ? $opts{start} + $opts{page}
1365 my $prev = $opts{start} - $opts{page} >= 0
1366 ? $opts{start} - $opts{page}
1369 my $opts = $opts{'nextArrow.x'} ? $opts{next} + 1 : $opts{prev} + 1;
1371 $opts .= $opts{start} + $opts{page} < $total
1372 ? $opts{start} + $opts{page}
1374 $opts .= " of $total";
1376 display start_form {
1378 action => 'runlog.cgi'
1381 # Hidden fields to pass along
1382 display input {name => 'prev', type => 'hidden', value => $prev};
1383 display input {name => 'next', type => 'hidden', value => $next};
1384 display input {name => 'oldtask', type => 'hidden', value => $opts{task}};
1385 display input {name => 'oldsystem', type => 'hidden', value => $opts{system}};
1386 display input {name => 'oldnot', type => 'hidden', value => $opts{not}};
1387 display input {name => 'oldstatus', type => 'hidden', value => $opts{status}};
1389 my $caption = start_table {
1395 $caption .= start_Tr;
1397 unless ($opts{id}) {
1398 $caption .= td {align => 'left'}, input {
1399 name => 'prevArrow',
1406 $caption .= td {align => 'left'}, img {
1408 disabled => 'disabled',
1412 $caption .= td {align => 'center'}, $opts;
1414 unless ($opts{id}) {
1415 $caption .= td {align => 'right'}, input {
1416 name => 'nextArrow',
1423 $caption .= td {align => 'right'}, img {
1425 disabled => 'disabled',
1431 $caption .= end_table;
1433 display start_table {cellspacing => 1, width => '98%'};
1435 display caption $caption;
1438 display th {class => 'labelCentered'}, '#';
1439 display th {class => 'labelCentered'}, 'ID';
1440 display th {class => 'labelCentered'}, 'Task';
1441 display th {class => 'labelCentered'}, 'System';
1442 display th {class => 'labelCentered'}, 'Started';
1443 display th {class => 'labelCentered'}, 'Ended';
1444 display th {class => 'labelCentered'}, 'Status';
1445 display th {class => 'labelCentered'}, 'Message';
1449 $opts{not} ||= 'false';
1451 display start_form {
1453 action => 'runlog.cgi'
1462 }, _makeRunlogSelection ('task', $opts{task});
1465 }, _makeRunlogSelection ('system', $opts{system});
1466 display td {class => 'filter'}, ' ';
1470 }, "Not: ", checkbox {
1473 checked => $opts{not} eq 'true' ? 1 : 0,
1478 }, _makeRunlogSelectionNumeric ('status', $opts{status});
1489 my $i = $opts{start};
1493 if (defined $opts{status}) {
1494 if ($opts{status} !~ /all/i) {
1495 $status = $opts{not} ne 'true' ? $opts{status} : "!$opts{status}";
1499 foreach ($clearadm->FindRunlog (
1507 my %runlog = setFields 'N/A', %{$_};
1509 my $class = $runlog{status} == 0
1512 my $classCentered = $runlog{status} == 0
1514 : 'dataAlertCentered';
1515 my $classRight = $runlog{status} == 0
1520 display td {class => 'dataCentered'}, ++$i;
1521 display td {class => 'dataCentered'}, $runlog{id};
1522 display td {class => 'data'}, a {
1523 href => "tasks.cgi?task=$runlog{task}"
1525 display td {class => 'data'}, $runlog{system} eq 'Localhost'
1528 href => "systemdetails.cgi?system=$runlog{system}"
1530 display td {class => 'dataCentered'}, $runlog{started};
1531 display td {class => 'dataCentered'}, $runlog{ended};
1532 display td {class => $classRight}, $runlog{status};
1534 my $message = $runlog{message};
1535 $message =~ s/\r\n/<br>/g;
1537 display td {class => $class, width => '50%'}, $message;
1546 sub displaySchedule () {
1547 display start_table {cellspacing => 1};
1550 display th {class => 'labelCentered'}, 'Actions';
1551 display th {class => 'labelCentered'}, 'Active';
1552 display th {class => 'labelCentered'}, 'Name';
1553 display th {class => 'labelCentered'}, 'Task';
1554 display th {class => 'labelCentered'}, 'Notification';
1555 display th {class => 'labelCentered'}, 'Frequency';
1556 display th {class => 'labelCentered'}, 'Category';
1559 foreach ($clearadm->FindSchedule) {
1560 my %schedule = setFields 'N/A', %{$_};
1563 my $areYouSure = "Are you sure you want to delete the $schedule{name} "
1566 my $actions = start_form {
1568 action => 'processschedule.cgi',
1574 value => $schedule{name},
1577 if (InArray $schedule{name}, @PREDEFINED_SCHEDULES) {
1582 src => 'delete.png',
1585 title => 'Cannot delete predefined schedule',
1594 title => 'Cannot edit predefined schedule',
1600 src => 'delete.png',
1604 onclick => "return AreYouSure ('$areYouSure');",
1618 display td {class => 'dataCentered'}, $actions;
1619 display td {class => 'dataCentered'}, checkbox {
1620 disabled => 'disabled',
1621 checked => $schedule{active} eq 'true' ? 1 : 0,
1623 display td {class => 'data'}, $schedule{name};
1624 display td {class => 'data'}, a {
1625 href => "tasks.cgi?task=$schedule{task}"
1627 display td {class => 'data'}, a {
1628 href => "notifications.cgi?notification=$schedule{notification}"
1629 }, $schedule{notification};
1630 display td {class => 'data'}, $schedule{frequency};
1631 display td {class => 'data'},
1632 (InArray $schedule{name}, @PREDEFINED_SCHEDULES)
1641 display p {class => 'center'}, a {
1642 href => 'processschedule.cgi?action=Add',
1643 }, 'New schedule', img {
1651 sub displaySystem ($) {
1652 my ($systemName) = @_;
1654 my %system = $clearadm->GetSystem ($systemName);
1657 displayError "Nothing known about system $systemName";
1661 my $lastheardfromClass = 'dataCentered';
1662 my $lastheardfromData = $system{lastheardfrom};
1664 my %load = $clearadm->GetLatestLoadavg ($systemName);
1666 unless ($clearadm->SystemAlive (%system)) {
1667 $lastheardfromClass = 'dataCenteredAlert';
1668 $lastheardfromData = a {
1669 href => "alertlog.cgi?system=$system{name}",
1671 title => "Have not heard from $system{name} for a while"
1672 }, $system{lastheardfrom};
1673 $system{notification} = 'Heartbeat';
1676 my $admin = ($system{email})
1677 ? a {-href => "mailto:$system{email}"}, $system{admin}
1680 $system{alias} = setField $system{alias}, 'N/A';
1681 $system{region} = setField $system{region}, 'N/A';
1683 display start_table {cellspacing => 1};
1686 my $areYouSure = 'Are you sure you want to delete this system?\n'
1687 . "Doing so will remove all records related to $system{name}"
1688 . '\nincluding filesystem records and history as well as '
1689 . 'loadavg history.';
1691 my $actions = start_form {
1693 action => 'processsystem.cgi',
1699 value => $system{name},
1705 src => 'delete.png',
1709 onclick => "return AreYouSure ('$areYouSure');",
1719 $actions .= checkbox {
1720 disabled => 'disabled',
1721 checked => $system{active} eq 'true' ? 1 : 0,
1724 if ($system{notification}) {
1726 href => "alertlog.cgi?system=$system{name}"}, img {
1730 title => 'This system has alerts',
1734 display th {class => 'label'}, "$actions Name:";
1736 display td {class => 'dataCentered', colspan => 2}, $system{name};
1737 display th {class => 'label'}, 'Alias:';
1738 display td {class => 'dataCentered'}, $system{alias};
1739 display th {class => 'label'}, 'Admin:';
1740 display td {class => 'dataCentered', colspan => 2}, $admin;
1741 display th {class => 'label', colspan => 2}, 'Type:';
1742 display td {class => 'dataCentered'}, $system{type};
1746 display th {class => 'label'}, 'OS Version:';
1747 display td {class => 'data', colspan => 10}, $system{os};
1751 display th {class => 'label'}, 'Last Contacted:';
1753 class => $lastheardfromClass,
1755 }, "$lastheardfromData ",
1756 font {class => 'dim' }, "<br>Up: $load{uptime}";
1757 display th {class => 'label'}, 'Port:';
1758 display td {class => 'dataCentered'}, $system{port};
1759 display th {class => 'label'}, 'Threshold:';
1760 display td {class => 'dataCentered'}, $system{loadavgThreshold};
1761 display th {class => 'label'}, 'History:';
1762 display td {class => 'dataCentered'}, $system{loadavgHist};
1763 display th {class => 'label'}, 'Load Avg:';
1764 display td {class => 'data'},
1766 "plot.cgi?type=loadavg&system=$system{name}&scaling=Hour&points=24"
1768 src => "plotloadavg.cgi?system=$system{name}&tiny=1",
1772 my $description = $system{description};
1773 $description =~ s/\r\n/<br>/g;
1776 display th {class => 'label'}, 'Description:';
1777 display td {class => 'data', colSpan => 10}, $description;
1782 display p {class => 'center'}, a {
1783 href => 'processsystem.cgi?action=Add',
1784 }, 'New system', img {
1789 display h1 {class => 'center'},
1790 'Filesystem Details: ' . ucfirst $system{name};
1792 display start_table {cellspacing => 1};
1795 display th {class => 'labelCentered'}, 'Action';
1796 display th {class => 'labelCentered'}, 'Filesystem';
1797 display th {class => 'labelCentered'}, 'Type';
1798 display th {class => 'labelCentered'}, 'Mount';
1799 display th {class => 'labelCentered'}, 'Size';
1800 display th {class => 'labelCentered'}, 'Used';
1801 display th {class => 'labelCentered'}, 'Free';
1802 display th {class => 'labelCentered'}, 'Used %';
1803 display th {class => 'labelCentered'}, 'Threshold';
1804 display th {class => 'labelCentered'}, 'History';
1805 display th {class => 'labelCentered'}, 'Usage';
1808 foreach ($clearadm->FindFilesystem ($system{name})) {
1809 my %filesystem = %{$_};
1811 my %fs = $clearadm->GetLatestFS (
1812 $filesystem{system},
1813 $filesystem{filesystem}
1816 my $size = autoScale $fs{size};
1817 my $used = autoScale $fs{used};
1818 my $free = autoScale $fs{free};
1820 # TODO: Note that this percentages does not agree with df output. I'm not
1822 my $usedPct = $fs{size} == 0 ? 0
1824 (($fs{reserve} + $fs{used}) / $fs{size} * 100));
1826 my $class = $usedPct < $filesystem{threshold}
1829 my $classCentered = $class . 'Centered';
1830 my $classRight = $class . 'Right';
1833 display start_td {class => 'data'};
1835 my $areYouSure = 'Are you sure you want to delete '
1836 . "$system{name}:$filesystem{filesystem}?" . '\n'
1837 . 'Doing so will remove all records related to this\n'
1838 . 'filesystem and its history.';
1840 display start_form {
1842 action => 'processfilesystem.cgi',
1848 value => $system{name},
1852 name => 'filesystem',
1853 value => $filesystem{filesystem},
1859 src => 'delete.png',
1863 onclick => "return AreYouSure ('$areYouSure');"
1874 if ($filesystem{notification}) {
1876 href => "alertlog.cgi?system=$filesystem{system}"}, img {
1880 title => 'This filesystem has alerts',
1885 display td {class => $class}, $filesystem{filesystem};
1886 display td {class => $classCentered}, $filesystem{fstype};
1887 display td {class => $class}, $filesystem{mount};
1888 display td {class => $classRight}, $size;
1889 display td {class => $classRight}, $used;
1890 display td {class => $classRight}, $free;
1891 display td {class => $classRight}, "$usedPct%";
1892 display td {class => $classRight}, "$filesystem{threshold}%";
1893 display td {class => $classCentered}, $filesystem{filesystemHist};
1894 display td {class => $classCentered},
1896 "plot.cgi?type=filesystem&system=$system{name}"
1897 . "&filesystem=$filesystem{filesystem}"
1898 . "&scaling=Day&points=7"
1900 src => "plotfs.cgi?system=$system{name}&"
1901 . "filesystem=$filesystem{filesystem}"
1913 sub displayTask (;$) {
1916 display start_table {cellspacing => 1, width => '98%'};
1919 display th {class => 'labelCentered'}, 'Actions';
1920 display th {class => 'labelCentered'}, 'Name';
1921 display th {class => 'labelCentered'}, 'System';
1922 display th {class => 'labelCentered'}, 'Description';
1923 display th {class => 'labelCentered'}, 'Command';
1924 display th {class => 'labelCentered'}, 'Restartable';
1925 display th {class => 'labelCentered'}, 'Category';
1928 foreach ($clearadm->FindTask ($task)) {
1931 $task{system} = 'All Systems'
1932 unless $task{system};
1935 my $areYouSure = "Are you sure you want to delete the $task{name} task?";
1937 my $actions = start_form {
1939 action => 'processtask.cgi',
1945 value => $task{name},
1948 if (InArray $task{name}, @PREDEFINED_TASKS) {
1953 src => 'delete.png',
1956 title => 'Cannot delete predefined task',
1965 title => 'Cannot edit predefined task',
1971 src => 'delete.png',
1975 onclick => "return AreYouSure ('$areYouSure');",
1989 display td {class => 'dataCentered'}, $actions;
1990 display td {class => 'data'}, $task{name};
1991 display td {class => 'data'}, $task{system};
1992 display td {class => 'data'}, $task{description};
1993 display td {class => 'data'}, $task{command};
1994 display td {class => 'dataCentered'}, $task{restartable};
1995 display td {class => 'data'},
1996 (InArray $task{name}, @PREDEFINED_TASKS) ? 'Predefined' : 'User Defined';
2002 display p {class => 'center'}, a {
2003 href => 'processtask.cgi?action=Add',
2004 }, 'New task', img {
2012 sub editAlert (;$) {
2015 display start_form (
2017 -action => 'processalert.cgi',
2018 -onsubmit => 'return validateAlert (this);',
2024 %alert = $clearadm->GetAlert ($alert);
2044 display start_table {cellspacing => 1};
2047 display th {class => 'labelCentered'}, 'Name';
2048 display th {class => 'labelCentered'}, 'Type';
2049 display th {class => 'labelCentered'}, 'Who';
2056 class => 'inputfield',
2061 value => $alert ? $alert{name} : '',
2064 class => 'dataCentered',
2067 class => 'dropdown',
2068 values => [ 'email', 'page', 'im' ],
2069 default => $alert ? $alert{type} : 'email',
2074 class => 'inputfield',
2079 value => $alert ? $alert{who} : '',
2085 display p submit ({value => $alert ? 'Update' : 'Add'}), reset;
2086 display '</center>';
2093 sub editFilesystem ($$) {
2094 my ($system, $filesystem) = @_;
2096 display start_form (
2098 -action => 'processfilesystem.cgi',
2101 display start_table {width => '800px', cellspacing => 1};
2104 display th {class => 'labelCentered'}, 'Filesystem';
2105 display th {class => 'labelCentered'}, 'Type';
2106 display th {class => 'labelCentered'}, 'Mount';
2107 display th {class => 'labelCentered'}, 'Size';
2108 display th {class => 'labelCentered'}, 'Used';
2109 display th {class => 'labelCentered'}, 'Free';
2110 display th {class => 'labelCentered'}, 'Used %';
2111 display th {class => 'labelCentered'}, 'History';
2112 display th {class => 'labelCentered'}, 'Threshold';
2115 my %filesystem = $clearadm->GetFilesystem ($system, $filesystem);
2116 my %fs = $clearadm->GetLatestFS ($system, $filesystem);
2126 value => $filesystem{system},
2129 name => 'filesystem',
2131 value => $filesystem{filesystem},
2134 my $size = autoScale $fs{size};
2135 my $used = autoScale $fs{used};
2136 my $free = autoScale $fs{free};
2139 display td {class => 'data'}, $filesystem{filesystem};
2140 display td {class => 'dataCentered'}, $filesystem{fstype};
2141 display td {class => 'data'}, $filesystem{mount};
2142 display td {class => 'dataRight'}, $size;
2143 display td {class => 'dataRight'}, $used;
2144 display td {class => 'dataRight'}, $free;
2145 # TODO: Note that this percentages does not agree with df output. I'm not
2147 display td {class => 'dataCentered'},
2148 sprintf ('%.0f%%', (($fs{reserve} + $fs{used}) / $fs{size} * 100));
2150 my $historyDropdown = popup_menu {
2151 name => 'filesystemHist',
2152 class => 'dropdown',
2167 default => $system ? $filesystem{filesystemHist} : '6 months',
2171 class => 'dataRight',
2172 }, $historyDropdown;
2174 my $thresholdDropdown = popup_menu {
2175 name => 'threshold',
2176 class => 'dropdown',
2177 values => [1 .. 100],
2178 default => $filesystem{threshold},
2180 display td {class => 'dataCentered'}, $thresholdDropdown . '%';
2186 display p submit ({value => 'Update'}), reset;
2187 display '</center>';
2194 sub editNotification (;$) {
2195 my ($notification) = @_;
2197 display start_form (
2199 -action => 'processnotification.cgi',
2200 -onsubmit => 'return validateNotification (this);',
2205 if ($notification) {
2206 %notification = $clearadm->GetNotification ($notification);
2209 unless %notification;
2214 value => $notification,
2226 display start_table {cellspacing => 1};
2229 display th {class => 'labelCentered'}, 'Name';
2230 display th {class => 'labelCentered'}, 'Alert';
2231 display th {class => 'labelCentered'}, 'Condition';
2232 display th {class => 'labelCentered'}, 'Not More Than';
2239 class => 'inputfield',
2244 value => $notification ? $notification{name} : '',
2248 class => 'dataCentered',
2249 }, makeAlertDropdown undef, $notification{alert}
2250 ? $notification{alert}
2256 class => 'inputfield',
2261 value => $notification ? $notification{cond} : '',
2264 class => 'dataCentered',
2265 }, makeNoMoreThanDropdown undef, $notification{nomorethan};
2271 display p submit ({value => $notification ? 'Update' : 'Add'}), reset;
2272 display '</center>';
2277 } # editNotification
2279 sub editSchedule (;$) {
2280 my ($schedule) = @_;
2282 display start_form (
2284 -action => 'processschedule.cgi',
2285 -onsubmit => 'return validateSchedule (this);',
2291 %schedule = $clearadm->GetSchedule ($schedule);
2311 display start_table {cellspacing => 1};
2314 display th {class => 'labelCentered'}, 'Active';
2315 display th {class => 'labelCentered'}, 'Name';
2316 display th {class => 'labelCentered'}, 'Task';
2317 display th {class => 'labelCentered'}, 'Notification';
2318 display th {class => 'labelCentered'}, 'Frequency';
2323 class => 'dataCentered',
2327 checked => $schedule{active} eq 'false' ? 0 : 1,
2333 class => 'inputfield',
2338 value => $schedule ? $schedule{name} : '',
2341 class => 'dataCentered',
2342 }, makeTaskDropdown undef, $schedule{task};
2344 class => 'dataCentered',
2345 }, makeNotificationDropdown undef, $schedule{notification};
2348 my $multiplier = 'minutes';
2350 if ($schedule{frequency} =~ /(\d+)\s(\S+)/ ) {
2354 $multiplier .= 's' if $nbr == 1;
2360 class => 'inputfieldRight',
2368 makeMultiplierDropdown undef, $multiplier;
2374 display p submit ({value => $schedule ? 'Update' : 'Add'}), reset;
2375 display '</center>';
2382 sub editSystem (;$) {
2385 display start_form (
2387 -action => 'processsystem.cgi',
2388 -onsubmit => 'return validateSystem (this);',
2394 %system = $clearadm->GetSystem ($system);
2414 display start_table {cellspacing => 1};
2417 display th {class => 'label'}, checkbox ({
2420 checked => $system{active} eq 'false' ? 0 : 1,
2425 display td {class => 'data'}, $system{name};
2430 class => 'inputfield',
2438 display th {class => 'label'}, 'Alias:';
2442 class => 'inputfield',
2447 value => $system ? $system{alias} : '',
2450 display th {class => 'label'}, 'Port:';
2452 class => 'dataRight',
2454 class => 'inputfieldRight',
2461 : $Clearadm::CLEAROPTS{CLEARADM_PORT},
2464 my $systemTypeDropdown = popup_menu {
2466 class => 'dropdown',
2467 values => ['Unix', 'Linux', 'Windows'],
2468 default => $system ? $system{type} : 'Linux',
2471 display th {class => 'label'}, 'Type:';
2473 class => 'dataRight',
2474 }, $systemTypeDropdown;
2478 display th {class => 'label'}, 'Admin:';
2482 class => 'inputfield',
2487 value => $system ? $system{admin} : '',
2489 display th {class => 'label'}, 'Admin Email:';
2493 class => 'inputfield',
2498 value => $system ? $system{email} : '',
2501 display th {class => 'label'}, 'Threshold:';
2503 class => 'dataRight',
2505 class => 'inputfieldRight',
2507 name => 'loadavgThreshold',
2511 ? $system{loadavgThreshold}
2512 : $Clearadm::CLEAROPTS{CLEARADM_LOADAVG_THRESHOLD},
2515 my $historyDropdown = popup_menu {
2516 name => 'loadavgHist',
2517 class => 'dropdown',
2532 default => $system ? $system{loadavgHist} : '6 months',
2535 display th {class => 'label'}, 'History:';
2537 class => 'dataRight',
2538 }, $historyDropdown;
2540 my $description = $system ? $system{description} : '';
2541 $description =~ s/\r\n/<br>/g;
2544 display th {class => 'label'}, 'Description:';
2549 class => 'inputfield',
2551 name => 'description',
2553 value => $description,
2559 display p submit ({value => $system ? 'Update' : 'Add'}), reset;
2560 display '</center>';
2570 display start_form (
2572 -action => 'processtask.cgi',
2573 -onsubmit => 'return validateTask (this);',
2579 %task = $clearadm->GetTask ($task);
2599 display start_table {cellspacing => 1};
2602 display th {class => 'labelCentered'}, 'Name';
2603 display th {class => 'labelCentered'}, 'System';
2604 display th {class => 'labelCentered'}, 'Description';
2605 display th {class => 'labelCentered'}, 'Command';
2606 display th {class => 'labelCentered'}, 'Restartable';
2613 class => 'inputfield',
2618 value => $task ? $task{name} : '',
2620 my $systemDropdown = makeSystemDropdown (
2622 $task{system} ? $task{system} : 'All Systems',
2624 'All systems' => undef,
2625 'Localhost' => 'Localhost',
2629 display td {class => 'data'}, $systemDropdown;
2634 class => 'inputfield',
2636 name => 'description',
2639 value => $task ? $task{description} : '',
2645 class => 'inputfield',
2650 value => $task ? $task{command} : '',
2654 class => 'dataCentered',
2655 }, makeRestartableDropdown undef, $task{restartable};
2661 display p submit ({value => $task ? 'Update' : 'Add'}), reset;
2662 display '</center>';
2670 my $clearscm = a {-href => 'http://clearscm.com'}, 'ClearSCM, Inc.';
2672 # Figure out which script by using CLEARADM_BASE.
2673 my $script = basename (url {-absolute => 1});
2674 $script = 'index.cgi'
2675 if $script eq 'clearadm';
2677 my $scriptFullPath = "$Clearadm::CLEAROPTS{CLEARADM_BASE}/$script";
2679 my ($year, $mon, $mday, $hour, $min, $sec) =
2680 ymdhms ((stat ($scriptFullPath))[9]);
2682 my $dateModified = "$mon/$mday/$year @ $hour:$min";
2685 -href => "http://clearscm.com/php/scm_man.php?file=clearadm/$script"
2690 display start_div {-class => 'copyright'};
2691 display "$script: Last modified: $dateModified";
2692 display br "Copyright © $year, $clearscm - All rights reserved";
2704 =head1 CONFIGURATION AND ENVIRONMENT
2706 DEBUG: If set then $debug is set to this level.
2708 VERBOSE: If set then $verbose is set to this level.
2710 TRACE: If set then $trace is set to this level.
2720 L<File::Basename|File::Basename>
2726 =head2 ClearSCM Perl Modules
2740 <a href="http://clearscm.com/php/scm_man.php?file=lib/Clearadm.pm">Clearadm</a><br>
2741 <a href="http://clearscm.com/php/scm_man.php?file=lib/DateUtils.pm">DateUtils</a><br>
2742 <a href="http://clearscm.com/php/scm_man.php?file=lib/Display.pm">Display</a><br>
2743 <a href="http://clearscm.com/php/scm_man.php?file=lib/Utils.pm">Utils</a><br>
2748 =head1 BUGS AND LIMITATIONS
2750 There are no known bugs in this module.
2752 Please report problems to Andrew DeFaria <Andrew@ClearSCM.com>.
2754 =head1 LICENSE AND COPYRIGHT
2756 Copyright (c) 2010, ClearSCM, Inc. All rights reserved.