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}%";
1217 my $image = $filesystem{fssmall}
1218 ? "data:image/png;base64,$filesystem{fssmall}"
1219 : "plotfs.cgi?system=$system{name}&filesystem=$filesystem{filesystem}&tiny=1";
1221 display td {class => $class},
1223 "plot.cgi?type=filesystem&system=$system{name}"
1224 . "&filesystem=$filesystem{filesystem}&scaling=Day&points=7"
1236 } # displayFilesystem
1238 sub displayNotification (;$) {
1239 my ($notification) = @_;
1241 display start_table {cellspacing => 1};
1244 display th {class => 'labelCentered'}, 'Actions';
1245 display th {class => 'labelCentered'}, 'Name';
1246 display th {class => 'labelCentered'}, 'Alert';
1247 display th {class => 'labelCentered'}, 'Condition';
1248 display th {class => 'labelCentered'}, 'Not More Than';
1249 display th {class => 'labelCentered'}, 'Category';
1252 for ($clearadm->FindNotification ($notification)) {
1253 my %notification= setFields 'N/A', %{$_};
1256 my $areYouSure = "Are you sure you want to delete the $notification{name} "
1259 my $actions = start_form {
1261 action => 'processnotification.cgi',
1267 value => $notification{name},
1270 if (InArray $notification{name}, @PREDEFINED_NOTIFICATIONS) {
1275 src => 'delete.png',
1278 title => 'Cannot delete predefined notification',
1287 title => 'Cannot edit predefined notification',
1293 src => 'delete.png',
1297 onclick => "return AreYouSure ('$areYouSure');",
1311 display td {class => 'dataCentered'}, $actions;
1312 display td {class => 'data'}, $notification{name};
1313 display td {class => 'data'}, a {
1314 href => "alerts.cgi?alert=$notification{alert}"
1315 }, $notification{alert};
1316 display td {class => 'data'}, $notification{cond};
1317 display td {class => 'data'}, $notification{nomorethan};
1318 display td {class => 'data'},
1319 (InArray $notification{name}, @PREDEFINED_NOTIFICATIONS)
1328 display p {class => 'center'}, a {
1329 href => 'processnotification.cgi?action=Add',
1330 }, 'New notification', img {
1336 } # displayNotification
1338 sub displayRunlog (%) {
1343 for (qw(task system not status)) {
1345 if (($opts{$old} and $opts{$_}) and ($opts{$old} ne $opts{$_})) {
1353 unless ($opts{id}) {
1354 if ($opts{task} !~ /all/i) {
1355 $condition = "task like '%";
1356 $condition .= $opts{task} ? $opts{task} : '';
1360 if ($opts{system} !~ /all/i) {
1361 if ($opts{system} eq '<NULL>') {
1362 $condition .= ' and system is null';
1364 } elsif ($opts{system} ne 'All') {
1365 $condition .= " and system like '%$opts{system}%'";;
1369 if ($opts{status} !~ /all/i) {
1370 $condition .= ' and ';
1371 unless ($opts{not}) {
1372 $condition .= "status=$opts{status}";
1374 $condition .= "status<>$opts{status}";
1379 my $total = $clearadm->Count ('runlog', $condition);
1381 $opts{start} = $opts{'nextArrow.x'} ? $opts{next} : $opts{prev};
1386 my $next = $opts{start} + $opts{page} < $total
1387 ? $opts{start} + $opts{page}
1389 my $prev = $opts{start} - $opts{page} >= 0
1390 ? $opts{start} - $opts{page}
1393 my $opts = $opts{'nextArrow.x'} ? $opts{next} + 1 : $opts{prev} + 1;
1395 $opts .= $opts{start} + $opts{page} < $total
1396 ? $opts{start} + $opts{page}
1398 $opts .= " of $total";
1400 display start_form {
1402 action => 'runlog.cgi'
1405 # Hidden fields to pass along
1406 display input {name => 'prev', type => 'hidden', value => $prev};
1407 display input {name => 'next', type => 'hidden', value => $next};
1408 display input {name => 'oldtask', type => 'hidden', value => $opts{task}};
1409 display input {name => 'oldsystem', type => 'hidden', value => $opts{system}};
1410 display input {name => 'oldnot', type => 'hidden', value => $opts{not}};
1411 display input {name => 'oldstatus', type => 'hidden', value => $opts{status}};
1413 my $caption = start_table {
1419 $caption .= start_Tr;
1421 unless ($opts{id}) {
1422 $caption .= td {align => 'left'}, input {
1423 name => 'prevArrow',
1430 $caption .= td {align => 'left'}, img {
1432 disabled => 'disabled',
1436 $caption .= td {align => 'center'}, $opts;
1438 unless ($opts{id}) {
1439 $caption .= td {align => 'right'}, input {
1440 name => 'nextArrow',
1447 $caption .= td {align => 'right'}, img {
1449 disabled => 'disabled',
1455 $caption .= end_table;
1457 display start_table {cellspacing => 1, width => '98%'};
1459 display caption $caption;
1462 display th {class => 'labelCentered'}, '#';
1463 display th {class => 'labelCentered'}, 'ID';
1464 display th {class => 'labelCentered'}, 'Task';
1465 display th {class => 'labelCentered'}, 'System';
1466 display th {class => 'labelCentered'}, 'Started';
1467 display th {class => 'labelCentered'}, 'Ended';
1468 display th {class => 'labelCentered'}, 'Status';
1469 display th {class => 'labelCentered'}, 'Message';
1473 $opts{not} ||= 'false';
1475 display start_form {
1477 action => 'runlog.cgi'
1486 }, _makeRunlogSelection ('task', $opts{task});
1489 }, _makeRunlogSelection ('system', $opts{system});
1490 display td {class => 'filter'}, ' ';
1494 }, "Not: ", checkbox {
1497 checked => $opts{not} eq 'true' ? 1 : 0,
1502 }, _makeRunlogSelectionNumeric ('status', $opts{status});
1513 my $i = $opts{start};
1517 if ($opts{status}) {
1518 if ($opts{status} !~ /all/i) {
1519 $status = $opts{not} ne 'true' ? $opts{status} : "!$opts{status}";
1523 for ($clearadm->FindRunlog (
1531 my %runlog = setFields 'N/A', %{$_};
1533 my $class = $runlog{status} == 0
1536 my $classCentered = $runlog{status} == 0
1538 : 'dataAlertCentered';
1539 my $classRight = $runlog{status} == 0
1544 display td {class => 'dataCentered'}, ++$i;
1545 display td {class => 'dataCentered'}, $runlog{id};
1546 display td {class => 'data'}, a {
1547 href => "tasks.cgi?task=$runlog{task}"
1549 display td {class => 'data'}, $runlog{system} eq 'localhost'
1552 href => "systemdetails.cgi?system=$runlog{system}"
1554 display td {class => 'dataCentered'}, $runlog{started};
1555 display td {class => 'dataCentered'}, $runlog{ended};
1556 display td {class => $classRight}, $runlog{status};
1558 my $message = $runlog{message};
1559 $message =~ s/\r\n/<br>/g;
1561 display td {class => $class, width => '50%'}, $message;
1570 sub displaySchedule () {
1571 display start_table {cellspacing => 1};
1574 display th {class => 'labelCentered'}, 'Actions';
1575 display th {class => 'labelCentered'}, 'Active';
1576 display th {class => 'labelCentered'}, 'Name';
1577 display th {class => 'labelCentered'}, 'Task';
1578 display th {class => 'labelCentered'}, 'Notification';
1579 display th {class => 'labelCentered'}, 'Frequency';
1580 display th {class => 'labelCentered'}, 'Category';
1583 for ($clearadm->FindSchedule) {
1584 my %schedule = setFields 'N/A', %{$_};
1587 my $areYouSure = "Are you sure you want to delete the $schedule{name} "
1590 my $actions = start_form {
1592 action => 'processschedule.cgi',
1598 value => $schedule{name},
1601 if (InArray $schedule{name}, @PREDEFINED_SCHEDULES) {
1606 src => 'delete.png',
1609 title => 'Cannot delete predefined schedule',
1618 title => 'Cannot edit predefined schedule',
1624 src => 'delete.png',
1628 onclick => "return AreYouSure ('$areYouSure');",
1642 display td {class => 'dataCentered'}, $actions;
1643 display td {class => 'dataCentered'}, checkbox {
1644 disabled => 'disabled',
1645 checked => $schedule{active} eq 'true' ? 1 : 0,
1647 display td {class => 'data'}, $schedule{name};
1648 display td {class => 'data'}, a {
1649 href => "tasks.cgi?task=$schedule{task}"
1651 display td {class => 'data'}, a {
1652 href => "notifications.cgi?notification=$schedule{notification}"
1653 }, $schedule{notification};
1654 display td {class => 'data'}, $schedule{frequency};
1655 display td {class => 'data'},
1656 (InArray $schedule{name}, @PREDEFINED_SCHEDULES)
1665 display p {class => 'center'}, a {
1666 href => 'processschedule.cgi?action=Add',
1667 }, 'New schedule', img {
1675 sub displaySystem ($) {
1676 my ($systemName) = @_;
1678 my %system = $clearadm->GetSystem ($systemName);
1681 displayError "Nothing known about system $systemName";
1685 my $lastheardfromClass = 'dataCentered';
1686 my $lastheardfromData = $system{lastheardfrom};
1688 my %load = $clearadm->GetLatestLoadavg ($systemName);
1690 unless ($clearadm->SystemAlive (%system)) {
1691 $lastheardfromClass = 'dataCenteredAlert';
1692 $lastheardfromData = a {
1693 href => "alertlog.cgi?system=$system{name}",
1695 title => "Have not heard from $system{name} for a while"
1696 }, $system{lastheardfrom};
1697 $system{notification} = 'Heartbeat';
1700 my $admin = ($system{email})
1701 ? a {-href => "mailto:$system{email}"}, $system{admin}
1704 $system{alias} = setField $system{alias}, 'N/A';
1705 $system{region} = setField $system{region}, 'N/A';
1707 display start_table {cellspacing => 1};
1710 my $areYouSure = 'Are you sure you want to delete this system?\n'
1711 . "Doing so will remove all records related to $system{name}"
1712 . '\nincluding filesystem records and history as well as '
1713 . 'loadavg history.';
1715 my $actions = start_form {
1717 action => 'processsystem.cgi',
1723 value => $system{name},
1729 src => 'delete.png',
1733 onclick => "return AreYouSure ('$areYouSure');",
1743 $actions .= checkbox {
1744 disabled => 'disabled',
1745 checked => $system{active} eq 'true' ? 1 : 0,
1748 if ($system{notification}) {
1750 href => "alertlog.cgi?system=$system{name}"}, img {
1754 title => 'This system has alerts',
1758 display th {class => 'label'}, "$actions Name:";
1760 display td {class => 'dataCentered', colspan => 2}, $system{name};
1761 display th {class => 'label'}, 'Alias:';
1762 display td {class => 'dataCentered'}, $system{alias};
1763 display th {class => 'label'}, 'Admin:';
1764 display td {class => 'dataCentered', colspan => 2}, $admin;
1765 display th {class => 'label', colspan => 2}, 'Type:';
1766 display td {class => 'dataCentered'}, $system{type};
1770 display th {class => 'label'}, 'OS Version:';
1771 display td {class => 'data', colspan => 10}, $system{os};
1775 display th {class => 'label'}, 'Last Contacted:';
1777 class => $lastheardfromClass,
1779 }, "$lastheardfromData ",
1780 font {class => 'dim' }, "<br>Up: $load{uptime}";
1781 display th {class => 'label'}, 'Port:';
1782 display td {class => 'dataCentered'}, $system{port};
1783 display th {class => 'label'}, 'Threshold:';
1784 display td {class => 'dataCentered'}, $system{loadavgThreshold};
1785 display th {class => 'label'}, 'History:';
1786 display td {class => 'dataCentered'}, $system{loadavgHist};
1787 display th {class => 'label'}, 'Load Avg:';
1788 display td {class => 'data'},
1790 "plot.cgi?type=loadavg&system=$system{name}&scaling=Hour&points=24"
1792 src => "data:image/png;base64,$system{loadavgsmall}",
1796 my $description = $system{description};
1797 $description =~ s/\r\n/<br>/g;
1800 display th {class => 'label'}, 'Description:';
1801 display td {class => 'data', colSpan => 10}, $description;
1806 display p {class => 'center'}, a {
1807 href => 'processsystem.cgi?action=Add',
1808 }, 'New system', img {
1813 display h1 {class => 'center'},
1814 'Filesystem Details: ' . ucfirst $system{name};
1816 display start_table {cellspacing => 1};
1819 display th {class => 'labelCentered'}, 'Action';
1820 display th {class => 'labelCentered'}, 'Filesystem';
1821 display th {class => 'labelCentered'}, 'Type';
1822 display th {class => 'labelCentered'}, 'Mount';
1823 display th {class => 'labelCentered'}, 'Size';
1824 display th {class => 'labelCentered'}, 'Used';
1825 display th {class => 'labelCentered'}, 'Free';
1826 display th {class => 'labelCentered'}, 'Used %';
1827 display th {class => 'labelCentered'}, 'Threshold';
1828 display th {class => 'labelCentered'}, 'History';
1829 display th {class => 'labelCentered'}, 'Usage';
1832 for ($clearadm->FindFilesystem ($system{name})) {
1833 my %filesystem = %{$_};
1835 my %fs = $clearadm->GetLatestFS (
1836 $filesystem{system},
1837 $filesystem{filesystem}
1840 my $size = autoScale $fs{size};
1841 my $used = autoScale $fs{used};
1842 my $free = autoScale $fs{free};
1844 # TODO: Note that this percentages does not agree with df output. I'm not
1846 my $usedPct = $fs{size} == 0 ? 0
1848 (($fs{reserve} + $fs{used}) / $fs{size} * 100));
1850 my $class = $usedPct < $filesystem{threshold}
1853 my $classCentered = $class . 'Centered';
1854 my $classRight = $class . 'Right';
1857 display start_td {class => 'data'};
1859 my $areYouSure = 'Are you sure you want to delete '
1860 . "$system{name}:$filesystem{filesystem}?" . '\n'
1861 . 'Doing so will remove all records related to this\n'
1862 . 'filesystem and its history.';
1864 display start_form {
1866 action => 'processfilesystem.cgi',
1872 value => $system{name},
1876 name => 'filesystem',
1877 value => $filesystem{filesystem},
1883 src => 'delete.png',
1887 onclick => "return AreYouSure ('$areYouSure');"
1898 if ($filesystem{notification}) {
1900 href => "alertlog.cgi?system=$filesystem{system}"}, img {
1904 title => 'This filesystem has alerts',
1910 display td {class => $class}, $filesystem{filesystem};
1911 display td {class => $classCentered}, $filesystem{fstype};
1912 display td {class => $class}, $filesystem{mount};
1913 display td {class => $classRight}, $size;
1914 display td {class => $classRight}, $used;
1915 display td {class => $classRight}, $free;
1916 display td {class => $classRight}, "$usedPct%";
1917 display td {class => $classRight}, "$filesystem{threshold}%";
1918 display td {class => $classCentered}, $filesystem{filesystemHist};
1919 display td {class => $classCentered},
1921 "plot.cgi?type=filesystem&system=$system{name}"
1922 . "&filesystem=$filesystem{filesystem}"
1923 . "&scaling=Day&points=7"
1925 src => "data:image/png;base64,$filesystem{fssmall}",
1936 sub displayTask (;$) {
1939 display start_table {cellspacing => 1, width => '98%'};
1942 display th {class => 'labelCentered'}, 'Actions';
1943 display th {class => 'labelCentered'}, 'Name';
1944 display th {class => 'labelCentered'}, 'System';
1945 display th {class => 'labelCentered'}, 'Description';
1946 display th {class => 'labelCentered'}, 'Command';
1947 display th {class => 'labelCentered'}, 'Restartable';
1948 display th {class => 'labelCentered'}, 'Category';
1951 for ($clearadm->FindTask ($task)) {
1954 $task{system} = 'All Systems'
1955 unless $task{system};
1958 my $areYouSure = "Are you sure you want to delete the $task{name} task?";
1960 my $actions = start_form {
1962 action => 'processtask.cgi',
1968 value => $task{name},
1971 if (InArray $task{name}, @PREDEFINED_TASKS) {
1976 src => 'delete.png',
1979 title => 'Cannot delete predefined task',
1988 title => 'Cannot edit predefined task',
1994 src => 'delete.png',
1998 onclick => "return AreYouSure ('$areYouSure');",
2012 display td {class => 'dataCentered'}, $actions;
2013 display td {class => 'data'}, $task{name};
2014 display td {class => 'data'}, $task{system};
2015 display td {class => 'data'}, $task{description};
2016 display td {class => 'data'}, $task{command};
2017 display td {class => 'dataCentered'}, $task{restartable};
2018 display td {class => 'data'},
2019 (InArray $task{name}, @PREDEFINED_TASKS) ? 'Predefined' : 'User Defined';
2025 display p {class => 'center'}, a {
2026 href => 'processtask.cgi?action=Add',
2027 }, 'New task', img {
2035 sub editAlert (;$) {
2038 display start_form (
2040 -action => 'processalert.cgi',
2041 -onsubmit => 'return validateAlert (this);',
2047 %alert = $clearadm->GetAlert ($alert);
2067 display start_table {cellspacing => 1};
2070 display th {class => 'labelCentered'}, 'Name';
2071 display th {class => 'labelCentered'}, 'Type';
2072 display th {class => 'labelCentered'}, 'Who';
2079 class => 'inputfield',
2084 value => $alert ? $alert{name} : '',
2087 class => 'dataCentered',
2090 class => 'dropdown',
2091 values => [ 'email', 'page', 'im' ],
2092 default => $alert ? $alert{type} : 'email',
2097 class => 'inputfield',
2102 value => $alert ? $alert{who} : '',
2108 display p submit ({value => $alert ? 'Update' : 'Add'}), reset;
2109 display '</center>';
2116 sub editFilesystem ($$) {
2117 my ($system, $filesystem) = @_;
2119 display start_form (
2121 -action => 'processfilesystem.cgi',
2124 display start_table {width => '800px', cellspacing => 1};
2127 display th {class => 'labelCentered'}, 'Filesystem';
2128 display th {class => 'labelCentered'}, 'Type';
2129 display th {class => 'labelCentered'}, 'Mount';
2130 display th {class => 'labelCentered'}, 'Size';
2131 display th {class => 'labelCentered'}, 'Used';
2132 display th {class => 'labelCentered'}, 'Free';
2133 display th {class => 'labelCentered'}, 'Used %';
2134 display th {class => 'labelCentered'}, 'History';
2135 display th {class => 'labelCentered'}, 'Threshold';
2138 my %filesystem = $clearadm->GetFilesystem ($system, $filesystem);
2139 my %fs = $clearadm->GetLatestFS ($system, $filesystem);
2149 value => $filesystem{system},
2152 name => 'filesystem',
2154 value => $filesystem{filesystem},
2157 my $size = autoScale $fs{size};
2158 my $used = autoScale $fs{used};
2159 my $free = autoScale $fs{free};
2162 display td {class => 'data'}, $filesystem{filesystem};
2163 display td {class => 'dataCentered'}, $filesystem{fstype};
2164 display td {class => 'data'}, $filesystem{mount};
2165 display td {class => 'dataRight'}, $size;
2166 display td {class => 'dataRight'}, $used;
2167 display td {class => 'dataRight'}, $free;
2168 # TODO: Note that this percentages does not agree with df output. I'm not
2170 display td {class => 'dataCentered'},
2171 sprintf ('%.0f%%', (($fs{reserve} + $fs{used}) / $fs{size} * 100));
2173 my $historyDropdown = popup_menu {
2174 name => 'filesystemHist',
2175 class => 'dropdown',
2190 default => $system ? $filesystem{filesystemHist} : '6 months',
2194 class => 'dataRight',
2195 }, $historyDropdown;
2197 my $thresholdDropdown = popup_menu {
2198 name => 'threshold',
2199 class => 'dropdown',
2200 values => [1 .. 100],
2201 default => $filesystem{threshold},
2203 display td {class => 'dataCentered'}, $thresholdDropdown . '%';
2209 display p submit ({value => 'Update'}), reset;
2210 display '</center>';
2217 sub editNotification (;$) {
2218 my ($notification) = @_;
2220 display start_form (
2222 -action => 'processnotification.cgi',
2223 -onsubmit => 'return validateNotification (this);',
2228 if ($notification) {
2229 %notification = $clearadm->GetNotification ($notification);
2232 unless %notification;
2237 value => $notification,
2249 display start_table {cellspacing => 1};
2252 display th {class => 'labelCentered'}, 'Name';
2253 display th {class => 'labelCentered'}, 'Alert';
2254 display th {class => 'labelCentered'}, 'Condition';
2255 display th {class => 'labelCentered'}, 'Not More Than';
2262 class => 'inputfield',
2267 value => $notification ? $notification{name} : '',
2271 class => 'dataCentered',
2272 }, makeAlertDropdown undef, $notification{alert}
2273 ? $notification{alert}
2279 class => 'inputfield',
2284 value => $notification ? $notification{cond} : '',
2287 class => 'dataCentered',
2288 }, makeNoMoreThanDropdown undef, $notification{nomorethan};
2294 display p submit ({value => $notification ? 'Update' : 'Add'}), reset;
2295 display '</center>';
2300 } # editNotification
2302 sub editSchedule (;$) {
2303 my ($schedule) = @_;
2305 display start_form (
2307 -action => 'processschedule.cgi',
2308 -onsubmit => 'return validateSchedule (this);',
2314 %schedule = $clearadm->GetSchedule ($schedule);
2334 display start_table {cellspacing => 1};
2337 display th {class => 'labelCentered'}, 'Active';
2338 display th {class => 'labelCentered'}, 'Name';
2339 display th {class => 'labelCentered'}, 'Task';
2340 display th {class => 'labelCentered'}, 'Notification';
2341 display th {class => 'labelCentered'}, 'Frequency';
2346 class => 'dataCentered',
2350 checked => $schedule{active} eq 'false' ? 0 : 1,
2356 class => 'inputfield',
2361 value => $schedule ? $schedule{name} : '',
2364 class => 'dataCentered',
2365 }, makeTaskDropdown undef, $schedule{task};
2367 class => 'dataCentered',
2368 }, makeNotificationDropdown undef, $schedule{notification};
2371 my $multiplier = 'minutes';
2373 if ($schedule{frequency} =~ /(\d+)\s(\S+)/ ) {
2377 $multiplier .= 's' if $nbr == 1;
2383 class => 'inputfieldRight',
2391 makeMultiplierDropdown undef, $multiplier;
2397 display p submit ({value => $schedule ? 'Update' : 'Add'}), reset;
2398 display '</center>';
2405 sub editSystem (;$) {
2408 display start_form (
2410 -action => 'processsystem.cgi',
2411 -onsubmit => 'return validateSystem (this);',
2417 %system = $clearadm->GetSystem ($system);
2437 display start_table {cellspacing => 1};
2440 display th {class => 'label'}, checkbox ({
2443 checked => $system{active} eq 'false' ? 0 : 1,
2448 display td {class => 'data'}, $system{name};
2453 class => 'inputfield',
2461 display th {class => 'label'}, 'Alias:';
2465 class => 'inputfield',
2470 value => $system ? $system{alias} : '',
2473 display th {class => 'label'}, 'Port:';
2475 class => 'dataRight',
2477 class => 'inputfieldRight',
2484 : $Clearadm::CLEAROPTS{CLEARADM_PORT},
2487 my $systemTypeDropdown = popup_menu {
2489 class => 'dropdown',
2490 values => ['Unix', 'Linux', 'Windows'],
2491 default => $system ? $system{type} : 'Linux',
2494 display th {class => 'label'}, 'Type:';
2496 class => 'dataRight',
2497 }, $systemTypeDropdown;
2501 display th {class => 'label'}, 'Admin:';
2505 class => 'inputfield',
2510 value => $system ? $system{admin} : '',
2512 display th {class => 'label'}, 'Admin Email:';
2516 class => 'inputfield',
2521 value => $system ? $system{email} : '',
2524 display th {class => 'label'}, 'Threshold:';
2526 class => 'dataRight',
2528 class => 'inputfieldRight',
2530 name => 'loadavgThreshold',
2534 ? $system{loadavgThreshold}
2535 : $Clearadm::CLEAROPTS{CLEARADM_LOADAVG_THRESHOLD},
2538 my $historyDropdown = popup_menu {
2539 name => 'loadavgHist',
2540 class => 'dropdown',
2555 default => $system ? $system{loadavgHist} : '6 months',
2558 display th {class => 'label'}, 'History:';
2560 class => 'dataRight',
2561 }, $historyDropdown;
2563 my $description = $system ? $system{description} : '';
2564 $description =~ s/\r\n/<br>/g;
2567 display th {class => 'label'}, 'Description:';
2572 class => 'inputfield',
2574 name => 'description',
2576 value => $description,
2582 display p submit ({value => $system ? 'Update' : 'Add'}), reset;
2583 display '</center>';
2593 display start_form (
2595 -action => 'processtask.cgi',
2596 -onsubmit => 'return validateTask (this);',
2602 %task = $clearadm->GetTask ($task);
2622 display start_table {cellspacing => 1};
2625 display th {class => 'labelCentered'}, 'Name';
2626 display th {class => 'labelCentered'}, 'System';
2627 display th {class => 'labelCentered'}, 'Description';
2628 display th {class => 'labelCentered'}, 'Command';
2629 display th {class => 'labelCentered'}, 'Restartable';
2636 class => 'inputfield',
2641 value => $task ? $task{name} : '',
2643 my $systemDropdown = makeSystemDropdown (
2645 $task{system} ? $task{system} : 'localhost',
2647 'localhost' => 'localhost',
2651 display td {class => 'data'}, $systemDropdown;
2656 class => 'inputfield',
2658 name => 'description',
2661 value => $task ? $task{description} : '',
2667 class => 'inputfield',
2672 value => $task ? $task{command} : '',
2676 class => 'dataCentered',
2677 }, makeRestartableDropdown undef, $task{restartable};
2683 display p submit ({value => $task ? 'Update' : 'Add'}), reset;
2684 display '</center>';
2692 my $clearscm = a {-href => 'http://clearscm.com'}, 'ClearSCM, Inc.';
2694 # Figure out which script by using CLEARADM_BASE.
2695 my $script = basename (url {-absolute => 1});
2696 $script = 'index.cgi'
2697 if $script eq 'clearadm';
2699 my $scriptFullPath = "$Clearadm::CLEAROPTS{CLEARADM_BASE}/$script";
2701 my ($year, $mon, $mday, $hour, $min, $sec) =
2702 ymdhms ((stat ($scriptFullPath))[9]);
2704 my $dateModified = "$mon/$mday/$year @ $hour:$min";
2707 -href => "http://clearscm.com/php/scm_man.php?file=clearadm/$script"
2712 display start_div {-class => 'copyright'};
2713 display "$script: Last modified: $dateModified";
2714 display br "Copyright © $year, $clearscm - All rights reserved";
2726 =head1 CONFIGURATION AND ENVIRONMENT
2728 DEBUG: If set then $debug is set to this level.
2730 VERBOSE: If set then $verbose is set to this level.
2732 TRACE: If set then $trace is set to this level.
2742 L<File::Basename|File::Basename>
2748 =head2 ClearSCM Perl Modules
2762 <a href="http://clearscm.com/php/scm_man.php?file=lib/Clearadm.pm">Clearadm</a><br>
2763 <a href="http://clearscm.com/php/scm_man.php?file=lib/DateUtils.pm">DateUtils</a><br>
2764 <a href="http://clearscm.com/php/scm_man.php?file=lib/Display.pm">Display</a><br>
2765 <a href="http://clearscm.com/php/scm_man.php?file=lib/Utils.pm">Utils</a><br>
2770 =head1 BUGS AND LIMITATIONS
2772 There are no known bugs in this module.
2774 Please report problems to Andrew DeFaria <Andrew@ClearSCM.com>.
2776 =head1 LICENSE AND COPYRIGHT
2778 Copyright (c) 2010, ClearSCM, Inc. All rights reserved.