--- /dev/null
+#!/usr/local/bin/perl
+#
+# This object represents an acknowledgment. When a problem happens, you can
+# associate one of these objects with the host to let operators or other staff
+# know that you are aware of the problem, and what you are doing about it.
+#
+# host - string FQDN of the machine its associated with
+# start - time() that the ack was created
+# end - time() that the ack will be over
+# contact - arbitrary contact info
+# services - arbitrary service names (sep by commas is standard)
+# message - arbitrary message
+#
+# + new() - constructor (sets instance vars to arguments passed in)
+# + gets/sets() - magical set/get functions (autoloaded based on func name)
+# + display() - output format and view
+#
+# History:
+# (1) Cleaned up (Ed July 30, 1997);
+
+package Spong::Ack;
+
+# Constructor. This expects all of its instance vars to be passed in when the
+# object is created. See above for specific information on the variables.
+# This object is dependent on the AckList object for loading the information
+# about it from the database.
+
+sub new {
+ my( $class, $host, $start, $end, $contact, $services, $message ) = @_;
+ my $self = {};
+
+ $self->{'host'} = $host;
+ $self->{'start'} = $start;
+ $self->{'end'} = $end;
+ $self->{'contact'} = $contact;
+ $self->{'services'} = $services;
+ $self->{'message'} = $message;
+
+ bless $self;
+ return $self;
+}
+
+
+# Get/Set methods, nothing fancy here...
+
+sub host { my $var = 'host';
+ if( defined $_[1] ) { $_[0]->{$var} = $_[1]; } return $_[0]->{$var}; }
+sub start { my $var = 'start';
+ if( defined $_[1] ) { $_[0]->{$var} = $_[1]; } return $_[0]->{$var}; }
+sub end { my $var = 'end';
+ if( defined $_[1] ) { $_[0]->{$var} = $_[1]; } return $_[0]->{$var}; }
+sub contact { my $var = 'contact';
+ if( defined $_[1] ) { $_[0]->{$var} = $_[1]; } return $_[0]->{$var}; }
+sub services { my $var = 'services';
+ if( defined $_[1] ) { $_[0]->{$var} = $_[1]; } return $_[0]->{$var}; }
+sub message { my $var = 'message';
+ if( defined $_[1] ) { $_[0]->{$var} = $_[1]; } return $_[0]->{$var}; }
+
+
+# Display summary. Does both text and html, doesn't make any calls to
+# sub-objects or other helper objects, just spits out the data that it has.
+#
+# brief: Just shows name of services and time ack is over.
+# standard: Shows detailed record of service information (contact, message)
+# full: (same as standard)
+
+sub display {
+ my( $self, $type, $view ) = @_;
+
+ if( $type eq "text" ) { return $self->display_text( $view ); }
+ if( $type eq "html" ) { return $self->display_html( $view ); }
+}
+
+sub display_text {
+ my( $self, $format ) = @_;
+
+ if( $format eq "brief" ) {
+ print $self->host(), "-", $self->services(), "-", $self->end();
+ } elsif( $format eq "standard" ) {
+ my( $d1, $m1, $y1 ) = (localtime( $self->end()))[3,4,5];
+ my( $d2, $m2, $y2 ) = (localtime())[3,4,5];
+ my $shost = $main::HOSTS{$self->host()}->{'display_name'};
+ my $date;
+ my $message = $self->message();
+
+ $shost = (split( /\./, $self->host() ))[0] unless $shost;
+ print substr( $shost, 0, 12 ), " "x(14-length(substr($shost, 0, 12)));
+
+ print substr( $self->services(), 0, 7 );
+ print " "x(9-length(substr( $self->services(), 0, 7 )));
+
+ if( $d1 == $d2 && $m1 == $m2 && $y1 == $y2 ) {
+ $date = POSIX::strftime( "%H:%M", localtime($self->end()) );
+ } else {
+ $date = POSIX::strftime( "%D", localtime($self->end()) );
+ }
+ print $date, " "x(12-length($date));
+
+ $message =~ s/[\n]*$//;
+ print substr( $message, 0, 45 );
+ } elsif( $format eq "full" ) {
+ $message = $self->message();
+
+ print "Service(s): ", $self->services(), "\n";
+ print "Ack. Until: ", scalar localtime( $self->end() ), "\n";
+ print "Ack. By: ", $self->contact(), "\n";
+
+ if( $message ne "" ) {
+ $message =~ s/[\n]*$/\n/; print "Message: $message"; }
+ } elsif( $format eq "special" ) {
+ print $self->host(), ":", $self->services(), ":", $self->end();
+ }
+}
+
+sub display_html {
+ my( $self, $format ) = @_;
+
+ if( $format eq "brief" ) {
+ print "<table border=0 cellspacing=1 cellpadding=1>\n";
+ print "<tr><td align=center valign=top>";
+ print $self->services();
+ print "</td><td align=left valign=top>";
+ print scalar localtime( $self->end() );
+ print "</td></tr>\n";
+ print "</table>\n";
+
+ } elsif( $format eq "standard" || $format eq "full" ) {
+ my $message = $self->message();
+ my $id = $self->host() . "-" . $self->services() . "-" . $self->end();
+
+ print "<table width=100% border=0 cellspacing=1 cellpadding=1><tr>\n";
+ print "<td colspan=2>";
+ print "<a href=\"" . $main::WWWACK . "/delete/$id\">Delete</a></td>";
+ print "</tr><td nowrap width=15% align=left>Service(s): </td>\n";
+ print "<td width=85% align=left>", $self->services(), "</td></tr>\n";
+
+ print "<tr><td nowrap width=15% align=left>Ack. Until: </td>\n";
+ print "<td width=85% align=left>", scalar localtime( $self->end() );
+ print "</td></tr>\n";
+
+ print "<tr><td nowrap width=15% align=left>Ack. By: </td>\n";
+ print "<td width=85% align=left>", $self->contact(), "</td></tr>\n";
+
+ if( $message ne "" ) {
+ print "<tr><td nowrap width=15% align=left>Message: </td>\n";
+ print "<td width=85% align=left>$message</td></tr>\n";
+ }
+ print "</table>\n";
+ }
+}
+
+1;
--- /dev/null
+#!/usr/local/bin/perl
+#
+# This object represents an list of acknowledgments associated with a host. It
+# knows how to load the Acks associated with a given host (since there can be
+# more then one). Acks do not know how to load themselves, they must be
+# brought to life from an AckList object.
+#
+# host - string FQDN of the machine its associated with
+# acks - list of Ack objects
+#
+# + new() - constructor (sets instance vars to arguments passed in)
+# + gets/sets() - magical set/get functions (autoloaded based on func name)
+# + display() - output format and view
+#
+# History:
+# (1) Cleaned up (Ed July 30, 1997);
+
+use Spong::Ack;
+
+package Spong::AckList;
+
+# Constructor. This requires the FQDN of the host that the Acks are associated
+# with. If there are no Acks associated with that host, then undef is returned
+# otherwise the AckList object is returned and the list of Acks are loaded.
+
+sub new {
+ my( $class, $host ) = @_;
+ my( @list, $file, $message );
+ my $self = {};
+
+
+ if( $host ) {
+ # Go through the list of acknowledgments for this host and add Ack
+ # objects for each one.
+
+ opendir( DIR, "$main::SPONGDB/$host/acks" );
+ while( defined( $file = readdir( DIR ) ) ) {
+ if( $file =~ /^\d+-(\d+)-(\d+)$/ ) {
+ my( $s, $e ) = ($1, $2);
+ if( time() >= $s && time() <= $e ) {
+ $message = "";
+ open( FILE, "$main::SPONGDB/$host/acks/$file" );
+ my $header = <FILE>; chomp $header;
+ my( $user, $serv ) = ( $header =~ /^(\S+) (.*)$/ );
+ while( <FILE> ) { $message .= $_; }
+ close( FILE );
+
+ push(@list,
+ Spong::Ack->new( $host, $s, $e, $user, $serv, $message ));
+ }
+ }
+ }
+ closedir( DIR );
+
+ # If we found any, then continue setting up and return self, otherwise
+ # return undef.
+
+ if( $#list >= 0 ) {
+ $self->{'host'} = $host;
+ $self->{'acks'} = \@list;
+ bless $self;
+ return $self;
+ } else {
+ return undef;
+ }
+ } else {
+ $self->{'host'} = $host;
+ $self->{'acks'} = \@list;
+
+ bless $self;
+ return $self;
+ }
+}
+
+# Get/Set methods, acks() is a little fancy in that it return a list rather
+# then just the list reference (easier for others to deal with).
+
+sub host { my $var = 'host';
+ if( defined $_[1] ) { $_[0]->{$var} = $_[1]; } return $_[0]->{$var}; }
+sub acks { my $var = 'acks';
+ if( defined $_[1] ) { $_[0]->{$var} = $_[1]; } return @{$_[0]->{$var}}; }
+
+
+# Used to add acknowledgements to the list. Typically used in the case where
+# you want to show acknowledgements on a variety of hosts and you start with
+# a blank list.
+
+sub add_acks {
+ my( $self, $add ) = @_;
+
+ if( ref( $add ) eq "Spong::AckList" ) {
+ foreach( $add->acks() ) {
+ push( @{$self->{'acks'}}, $_ );
+ }
+ } elsif( ref( $add ) eq "Spong::Ack" ) {
+ push( @{$self->{'acks'}}, $add );
+ }
+}
+
+
+# Pretty simple display, just iterate over the Acks and tell each of them to
+# display themselves in the way that we were told.
+
+sub display {
+ my( $self, $format, $view ) = @_;
+
+ if( $view eq "standard" && $format eq "text" ) {
+ print "Host Service Until Message\n";
+ print "------------ ------- ---------- ";
+ print "------------------------------------------\n";
+ }
+
+ sub bytime { return $b->end() <=> $a->end(); }
+
+ foreach( sort bytime $self->acks() ) {
+ $_->display( $format, $view );
+
+ if( $format eq "text" ) { print "\n"; }
+ if( $format eq "html" ) { print "<p>"; }
+ }
+}
+
+
+1;
--- /dev/null
+#!/usr/local/bin/perl
+#
+# This object represents an event that has happened that a record should be
+# kept for. The current case is when a service changes state (color change).
+# An record representing that change is written to the database so that you
+# can see a record of these events over time.
+#
+# host - string FQDN of the machine its associated with
+# type - type of history event (status change, etc...)
+# time - time (time() format) that the event occurred
+# service - service name this history is associated with
+# color - color of the service at time history event was created
+# other - summary information about the history event
+#
+# + new() - constructor (sets instance vars to arguments passed in)
+# + gets/sets() - magical set/get functions (autoloaded based on func name)
+# + display() - output format and view
+#
+# History:
+# (1) Cleaned up (Ed July 31, 1997);
+
+package Spong::History;
+
+# Constructor. A simple constructor that just sets all of the instance vars
+# according to what was passed in. All vars should be set on creation.
+
+sub new {
+ my( $class, $host, $type, $time, $service, $color, $other ) = @_;
+ my $self = {};
+
+ $self->{'host'} = $host;
+ $self->{'type'} = $type;
+ $self->{'time'} = $time;
+ $self->{'service'} = $service;
+ $self->{'color'} = $color;
+ $self->{'other'} = $other;
+
+ bless $self;
+ return $self;
+}
+
+# Get/Set methods, nothing fancy here...
+
+sub host { my $var = 'host';
+ if( defined $_[1] ) { $_[0]->{$var} = $_[1]; } return $_[0]->{$var}; }
+sub type { my $var = 'type';
+ if( defined $_[1] ) { $_[0]->{$var} = $_[1]; } return $_[0]->{$var}; }
+sub time { my $var = 'time';
+ if( defined $_[1] ) { $_[0]->{$var} = $_[1]; } return $_[0]->{$var}; }
+sub service { my $var = 'service';
+ if( defined $_[1] ) { $_[0]->{$var} = $_[1]; } return $_[0]->{$var}; }
+sub color { my $var = 'color';
+ if( defined $_[1] ) { $_[0]->{$var} = $_[1]; } return $_[0]->{$var}; }
+sub other { my $var = 'other';
+ if( defined $_[1] ) { $_[0]->{$var} = $_[1]; } return $_[0]->{$var}; }
+
+
+# Display summary. Does both text and html, doesn't make any calls to
+# sub-objects or other helper objects, just spits out the data that it has.
+# This is a little different in that there are not different views of the
+# data. The views come into play with the HistoryList in that more days worth
+# of history is shown with the more detailed views.
+
+sub display {
+ my( $self, $type, $view ) = @_;
+
+ if( $type eq "text" ) { return $self->display_text( $view ); }
+ if( $type eq "html" ) { return $self->display_html( $view ); }
+}
+
+sub display_text {
+ my( $self, $view ) = @_;
+ my( $min, $hour ) = (localtime($self->time()))[1,2];
+ my $shost = $main::HOSTS{$self->host()}->{'display_name'};
+
+ $shost = (split( /\./, $self->host() ))[0] unless $shost;
+
+ print $self->color(), " "x(7-length($self->color()));
+ print sprintf( "%2.2d:%2.2d ", $hour, $min );
+ print substr( $shost, 0, 12 ), " "x(14-length(substr($shost, 0, 12)));
+ print substr( $self->service(), 0, 5 );
+ print " "x(7-length(substr( $self->service(), 0, 5 )));
+ print $self->other(), "\n";
+}
+
+sub display_html {
+ my( $self, $view ) = @_;
+ my( $min, $hour ) = (localtime($self->time()))[1,2];
+ my $shost = $main::HOSTS{$self->host()}->{'display_name'};
+ my $color = $self->color();
+ my $service = $self->service();
+ my $other = $self->other();
+
+ $shost = (split( /\./, $self->host() ))[0] unless $shost;
+
+ print "<tr><td width=7% align=left valign=top>";
+
+ if( $main::WWW_USE_IMAGES == 1 ) {
+ print "<img src=\"!!WWWGIFS!!/$color.gif\" alt=$color border=0>";
+ } else {
+ print "<table width=20 border=0 cellspacing=0 cellpadding=0><tr>";
+ print "<td width=20 bgcolor=\"" . $main::WWW_COLOR{$color} . "\">";
+ print " </td></tr></table>";
+ }
+
+ print "</td><td width=12% align=center valign=top>";
+ printf ( "%2.2d:%2.2d</td>", $hour, $min );
+ print "<td width=15% align=left valign=top>$shost </td>\n";
+ print "<td width=15% align=left valign=top>$service</td>\n";
+ print "<td width=51% align=left valign=top>$other</td></tr>\n";
+}
+
+1;
--- /dev/null
+#!/usr/local/bin/perl
+#
+# This object represents a list of history events that happen on a given host
+# or a set of hosts. HistoryLists are a little unique in that they don't have
+# to be associated with one specific host, they can contain events from a
+# number of hosts, and they can be populated from an empty container.
+#
+# host - string FQDN of the machine its associated with
+# events - list of history objects.
+#
+# + new() - constructor (sets instance vars to arguments passed in)
+# + gets/sets() - magical set/get functions (autoloaded based on func name)
+# + display() - output format and view
+#
+# + add_history() - used to add history objects to the list
+#
+# History:
+# (1) Cleaned up (Ed July 31, 1997);
+
+use Spong::History;
+
+package Spong::HistoryList;
+
+# Constructor. A simple constructor that will load all the history events
+# associated with a particular host (if a hostname is passed in), otherwise
+# it just sets up an empty object that expects to be filled by someone else.
+# If there are no History events associated with a host, then undef is returned
+
+sub new {
+ my( $class, $host ) = @_;
+ my $self = {};
+ my( @list );
+
+ # If we are given a host name, then look up the events for that host,
+ # otherwise, just return a shell that will be filled later.
+
+ if( $host ) {
+ return undef if ! -d "$main::SPONGDB/$host/history";
+
+ # Go through the current history, and build history objects for each
+ # item in the history.
+
+ open( FILE, "$main::SPONGDB/$host/history/current" );
+ while( <FILE> ) {
+ chomp;
+ if( /^status (\d+) (\S+) (green|yellow|red) (.*)$/ ) {
+ push( @list,
+ Spong::History->new( $host, "status", $1, $2, $3, $4 ) );
+ } elsif( /^ack (\d+) (\S+) (.*)$/ ) {
+ push( @list,
+ Spong::History->new( $host, "ack", $1, $2, "blue", $3 ));
+ }
+ }
+ close( FILE );
+ }
+
+ $self->{'host'} = $host;
+ $self->{'events'} = \@list;
+
+ bless $self;
+ return $self;
+}
+
+
+# Get/Set methods, events() is a little fancy in that it return a list rather
+# then just the list reference (easier for others to deal with).
+
+sub host { my $var = 'host';
+ if( defined $_[1] ) { $_[0]->{$var} = $_[1]; } return $_[0]->{$var}; }
+sub events { my $var = 'events';
+ if( defined $_[1] ) { $_[0]->{$var} = $_[1]; } return @{$_[0]->{$var}}; }
+
+
+# Used to add history events to the list. Typically used in the case where
+# you want to show history events on a variety of hosts and you start with
+# a blank list.
+
+sub add_history {
+ my( $self, $add ) = @_;
+
+ if( ref( $add ) eq "Spong::HistoryList" ) {
+ foreach( $add->events() ) {
+ push( @{$self->{'events'}}, $_ );
+ }
+ } elsif( ref( $add ) eq "Spong::History" ) {
+ push( @{$self->{'events'}}, $add );
+ }
+}
+
+
+# Display summary. Does both text and html, does rely on the History object
+# to display itself, but this adds to it by showing header information in
+# tables, and breaking output up by days.
+#
+# brief: Shows events that have happened in the last 24 hours
+# standard: Shows events that have happened in the last 7 days
+# full: Shows events that have happened in the last 30 days
+
+sub display {
+ my( $self, $type, $view ) = @_;
+
+ if( $type eq "text" ) { return $self->display_text( $view ); }
+ if( $type eq "html" ) { return $self->display_html( $view ); }
+}
+
+sub display_text {
+ my( $self, $format ) = @_;
+ my( $event, $date );
+ my( $day ) = 24*60*60;
+
+ sub bytime { return $b->time() <=> $a->time(); }
+
+ foreach $event ( sort bytime $self->events() ) {
+ if( ($format eq "brief" && $event->time() > (time() - $day)) ||
+ ($format eq "standard" && $event->time() > (time() - 7*$day)) ||
+ ($format eq "full" && $event->time() > (time() - 31*$day)) ) {
+
+ my $dstr = POSIX::strftime( "%A, %D", localtime($event->time()) );
+ if( $dstr ne $date ) {
+ if( $date ne "" ) { print "\n"; }
+ print "$dstr\n", "-"x78, "\n";
+ $date = $dstr;
+ }
+
+ $event->display_text();
+ }
+ }
+}
+
+sub display_html {
+ my( $self, $format ) = @_;
+ my( $event, $date );
+ my( $day ) = 24*60*60;
+
+ sub bytime { return $b->time() <=> $a->time(); }
+
+ foreach $event ( sort bytime $self->events() ) {
+ if( ($format eq "brief" && $event->time() > (time() - $day)) ||
+ ($format eq "standard" && $event->time() > (time() - 7*$day)) ||
+ ($format eq "full" && $event->time() > (time() - 31*$day)) ) {
+
+ my $dstr = POSIX::strftime( "%A, %D", localtime($event->time()) );
+ if( $dstr ne $date ) {
+ if( $date ne "" ) { print "</table><br>"; }
+ print "$dstr\n<hr border=1 noshade>\n";
+ print "<table width=100% border=0 cellspacing=1 cellpadding=0>\n";
+ $date = $dstr;
+ }
+
+ $event->display_html();
+ }
+ }
+ print "</table>\n";
+}
+
+1;
--- /dev/null
+#!/usr/local/bin/perl
+#
+# This represents a collection of hosts, either a group (including all hosts),
+# a single host, or a list of hosts by FDQN name.
+#
+# name - name of the group the HostList represents
+# hostnames - a list of string names of the hosts in the list
+# hosthash - a hash of Host objects keyed by hostname.
+#
+# + new() - constructor (sets instance vars to arguments passed in)
+# + gets/sets() - magical set/get functions (autoloaded based on func name)
+# + display() - output format and view
+#
+# + display_problems() - shows information only about hosts that have services
+# that indicate a problem
+#
+# + host - used to get at a specific host by name
+# + hosts - used to get back a generic list of hosts that are stored
+# + add - adds a specific host to the list.
+#
+# History:
+# (1) Cleaned up (Ed July 31, 1997);
+
+use Spong::Host;
+
+package Spong::HostList;
+
+# This constructor expects one of four different types of options. Either an
+# empty string specifying that no actual hosts will be loaded, the string
+# "all" to load all the hosts, the string containing a group name that
+# represents a list of hosts, or a reference to an array that contains a list
+# of hostnames in which case each host will be loaded individually.
+
+sub new {
+ my( $class, $group ) = @_;
+ my( @names, %hosts, $name ) = ();
+ my $self = {};
+
+ if( $group eq "" ) {
+ %hosts = ();
+ @names = ();
+ $self->{'name'} = "";
+ } elsif( ref( $group ) eq "ARRAY" ) {
+ foreach $name ( @$group ) {
+ my $object = Spong::Host->new( $name );
+ if( $object ) { $hosts{$name} = $object; push( @names, $name ); }
+ }
+ $self->{'name'} = "";
+ } else {
+ if( $group eq "all" ) {
+ foreach $name ( @main::HOSTS_LIST ) {
+ my $object = Spong::Host->new( $name );
+ if( $object ) { $hosts{$name} = $object; push( @names, $name ); }
+ }
+ } else {
+ if( ref( $main::GROUPS{$group}->{'members'} ) eq "ARRAY" ) {
+ foreach $name ( @{$main::GROUPS{$group}->{'members'}} ) {
+ my $object = Spong::Host->new( $name );
+ if( $object ) { $hosts{$name} = $object; push( @names, $name );}
+ }
+ }
+ }
+
+ $self->{'name'} = $group;
+ }
+
+ $self->{'hosthash'} = \%hosts;
+ $self->{'hostnames'} = [ @names ];
+
+ bless $self;
+ return $self;
+}
+
+
+# Get/Set methods, hostnames() is a little fancy in that it return a list
+# rather then just the list reference (easier for others to deal with). The
+# hosthash function returns a reference however.
+
+sub name { my $var = 'name';
+ if( defined $_[1] ) { $_[0]->{$var} = $_[1]; } return $_[0]->{$var}; }
+sub hosthash { my $var = 'hosthash';
+ if( defined $_[1] ) { $_[0]->{$var} = $_[1]; } return $_[0]->{$var}; }
+sub hostnames { my $var = 'hostnames';
+ if( defined $_[1] ) { $_[0]->{$var} = $_[1]; } return @{$_[0]->{$var}}; }
+
+# Some specific functions that get at and manipulate the data in the instance
+# vars in more convenient ways
+
+sub host {
+ return $_[0]->{'hosthash'}->{$_[1]}; }
+
+sub hosts {
+ my $self = shift;
+ my( @tmp );
+
+ foreach( $self->hostnames() ) { push( @tmp, $self->host( $_ ) ); }
+ return @tmp;
+}
+
+sub add {
+ my( $self, $host ) = @_;
+ my $name = $host->name();
+
+ $self->{'hosthash'}->{$name} = $host;
+ push( @{$self->{'hostnames'}}, $name );
+}
+
+# Display summary. Does both text and html, does rely on both the Host and
+# Service objects for some help.
+#
+# brief Hostname and "one" color reflecting overall status of the host
+# standard Table showing hosts vs services and the state of each service
+# full Records for each host showing detailed info about its state
+
+sub display {
+ my( $self, $type, $view ) = @_;
+
+ $self->display_text( $view ) if $type eq "text";
+ $self->display_html( $view ) if $type eq "html";
+}
+
+# This displays a summary of all the hosts in this list in a text format
+# suitable for displaying on dumb ascii terminals
+
+sub display_text {
+ my( $self, $format ) = @_;
+
+ if( $format eq "standard" ) {
+ my( %services, $host, $service, @names );
+
+ # Compute the total list of services running on the various hosts, and
+ # sort them alphabetically (except always put ping first).
+
+# foreach $host ( $self->hosts() ) {
+# foreach $service ( $host->service_names() ) { $services{$service}++;}}
+
+ if( grep( /^ping$/, keys %main::SERVICES ) ) { push( @names, "ping" ); }
+ foreach $service ( sort keys %main::SERVICES ) {
+ push( @names, $service ) unless $service eq "ping"; }
+
+ # Print the horizontal axis of the table (names of the services)
+
+ print "-"x30, "-"x(($#names+1)*3), "\n";
+ foreach $cnt ( 0..4 ) {
+ if( $cnt == 0 ) { print "Key: . = green, ? = purple "; }
+ if( $cnt == 1 ) { print " Y = yellow, R = red "; }
+ if( $cnt == 2 ) { print " B = blue "; }
+ if( $cnt == 3 ) { print " "x30; }
+ if( $cnt == 4 ) { print "Host:", " "x25; }
+ foreach $service ( @names ) {
+ if( length($service) - 5 + $cnt >= 0 ) {
+ print substr( $service, (length($service) - 5 + $cnt), 1 )," ";
+ } else {
+ print " ";
+ }
+ }
+ print "\n";
+ }
+ print "-"x30, "-"x(($#names+1)*3), "\n";
+
+ # Now go through each host, and fill in the table.
+
+ foreach $host ( $self->hosts() ) {
+ print substr( $host->name(), 0, 29 );
+ print " "x(30-length(substr($host->name(), 0, 29)));
+ foreach $service ( @names ) {
+ my $servobj = $host->service( $service );
+ if( $servobj ) {
+ $servobj->display_text( "really_brief" );
+ } else {
+ print " ";
+ }
+ }
+ print "\n";
+ }
+ print "\n";
+ } elsif( $format eq "full" ) {
+
+ # This goes through each host, and has each one print a textual record
+ # of the problem it is currently having, this would include a summary
+ # of the problem services, date/time the problem occurred, and contact
+ # information (as well as acknowledgments if they are available).
+
+ foreach $host ( $self->hosts() ) {
+ $host->display_problem( "text" ); print "\n";
+ }
+ }
+}
+
+# This displays a summary of all the hosts in this list in an HTML format
+# suitable for displaying on web clients capable of displaying tables.
+
+sub display_html {
+ my( $self, $format ) = @_;
+
+ if( $format eq "standard" ) {
+ my( %services, $host, $service, @names );
+
+ # Compute the total list of services running on the various hosts, and
+ # sort them alphabetically (except always put ping first).
+
+ if( grep( /^ping$/, keys %main::SERVICES ) ) { push( @names, "ping" ); }
+ foreach $service ( sort keys %main::SERVICES ) {
+ push( @names, $service ) unless $service eq "ping"; }
+
+ # Print the horizontal axis of the table (names of the services)
+
+ print "<table border=1 cellspacing=0 cellpadding=1><tr>";
+ print "<td align=center width=80 nowrap><b>Host</b></td>\n";
+ foreach $service ( @names ) {
+ print "<td align=center valign=bottom width=25>\n";
+ print "<font size=-1><a href=\"!!WWWSPONG!!/help/$service\">";
+ print "$service</a></font></td>\n";
+ }
+
+ print "</tr>\n\n";
+
+ # Now go through each host, and fill in the table.
+
+ foreach $host ( $self->hosts() ) {
+ my $hostname = $host->name();
+ my $short = $main::HOSTS{$hostname}->{'display_name'};
+ $short = (split( /\./, $hostname ))[0] unless $short;
+
+ print "<tr><td align=left nowrap>\n";
+ print "<a href=\"!!WWWSPONG!!/host/$hostname\">$short</a></td>\n";
+
+ foreach $service ( @names ) {
+ my $servobj = $host->service( $service );
+
+ if( $servobj ) {
+ my $col = $servobj->color();
+
+ if( $main::WWW_USE_IMAGES == 1 ) {
+ print "<td align=center><a href=\"!!WWWSPONG!!/service/";
+ print "$hostname/$service\">";
+ print "<img src=\"!!WWWGIFS!!/$col.gif\" alt=$col border=0>";
+ print "</a>";
+ } else {
+ print "<td align=center bgcolor=\"";
+ print $main::WWW_COLOR{$col} . "\" width=25>";
+ print "<a href=\"!!WWWSPONG!!/service/$hostname/$service\">";
+ print "<font color=\"" . $main::WWW_COLOR{$col} . "\">";
+ print "___</font></a>";
+ }
+ print "</td>";
+ } else {
+ if( $main::WWW_USE_IMAGES == 1 ) {
+ print "<td align=center width=25> - </td>\n";
+ } else {
+ print "<td align=center width=25> </td>\n";
+ }
+ }
+ }
+ }
+ print "</tr></table>";
+
+ } elsif( $format eq "full" ) {
+
+ # This goes through each host, and has each one print a textual record
+ # of the problem it is currently having, this would include a summary
+ # of the problem services, date/time the problem occurred, and contact
+ # information (as well as acknowledgments if they are available).
+
+ foreach $host ( $self->hosts() ) {
+ $host->display_problem( "html" ); print "\n";
+ }
+ }
+}
+
+
+# These methods all display summary information about only those machines
+# that are currently registering a problem, or that have services that have
+# not been updated (showing purple) - it also relays information about
+# services that have been acknowledged.
+#
+# brief Hostname and "one" color reflecting overall status of the host
+# standard Table showing hosts vs services and the state of each service
+# full Records for each host showing detailed info about the problems
+
+sub display_problems {
+ my( $self, $type, $view ) = @_;
+ my( $badhosts ) = Spong::HostList->new( "" );
+ my( $host, $problem, $purple );
+
+ foreach $host ( $self->hosts() ) {
+ if( $host->has_problem() ) {
+ $problem = 1;
+ $badhosts->add( $host );
+ } elsif( $host->has_color( "purple" ) ) {
+ $purple = 1;
+ }
+ }
+
+ if( $problem ) {
+ $badhosts->display( $type, $view );
+ } else {
+ if( $view eq "full" ) {
+ if( $purple ) {
+ print "<p><font color=green><b>" if( $type eq "html" );
+ print "No known problems.\n";
+ print "</b></font>" if( $type eq "html" );
+ print "<p><font size=-1 color=purple>" if( $type eq "html" );
+ print "Although some information is out of date which might ";
+ print "indicate a problem.\n";
+ print "</font>" if( $type eq "html" );
+ } else {
+ print "<p><font color=green><b>" if( $type eq "html" );
+ print "No current problems.\n";
+ print "</b></font>" if( $type eq "html" );
+ }
+ }
+ }
+}
+
+1;
--- /dev/null
+#!/usr/local/bin/perl
+#
+# This is a simple class which just represents some information that is
+# provided by the spong administrator about the hosts that are being
+# monitored. The administrator can provide documentation in any number of
+# formats depending on how energetic he/she is. He/she can also choose not to
+# provide this information at all, and nothing will be displayed.
+#
+# There is only one Info class associated with each Host, so this class knows
+# how to load itself from the database.
+#
+# host - string FQDN of the machine its associated with
+#
+# + new() - constructor (sets instance vars to arguments passed in)
+# + gets/sets() - magical set/get functions (autoloaded based on func name)
+# + display() - output format and view
+#
+# History:
+# (1) Cleaned up (Ed July 31, 1997);
+
+package Spong::Info;
+
+# Object creation... Just checks to see if any of the required documentation
+# files have been provided by the spong administrator, and if not it returns
+# undef rather then a real life object...
+
+sub new {
+ my( $class, $host ) = @_;
+ my $self = {};
+ my $ok;
+
+ if( -f "$main::SPONGDB/$host/info/info.txt" ) { $ok = 1; }
+ if( -f "$main::SPONGDB/$host/info/info.html" ) { $ok = 1; }
+
+ if( -f "$main::SPONGDB/$host/info/info.brief.txt" ) { $ok = 1; }
+ if( -f "$main::SPONGDB/$host/info/info.standard.txt" ) { $ok = 1; }
+ if( -f "$main::SPONGDB/$host/info/info.full.txt" ) { $ok = 1; }
+
+ if( -f "$main::SPONGDB/$host/info/info.brief.html" ) { $ok = 1; }
+ if( -f "$main::SPONGDB/$host/info/info.standard.html" ) { $ok = 1; }
+ if( -f "$main::SPONGDB/$host/info/info.full.html" ) { $ok = 1; }
+
+ return undef if ! $ok;
+
+ $self->{'host'} = $host;
+ bless $self;
+ return $self;
+}
+
+
+# Get/Set methods, nothing fancy here...
+
+sub host { my $var = 'host';
+ if( defined $_[1] ) { $_[0]->{$var} = $_[1]; } return $_[0]->{$var}; }
+
+
+# Display summary. Does both text and html, doesn't make any calls to
+# sub-objects or other helper objects, just spits out the data that it has.
+#
+# This object is a little different in that it shows the best possible
+# documentation based on what is provided. It checks to see if the
+# administrator has provided a specific view of documentation, but if it
+# doesn't exist then it just keep looking for documentation until it finds
+# some to print.
+
+sub display {
+ my( $self, $type, $view ) = @_;
+
+ if( $type eq "text" ) { return $self->display_text( $view ); }
+ if( $type eq "html" ) { return $self->display_html( $view ); }
+}
+
+sub display_text {
+ my( $self, $format ) = @_;
+ my $host = $self->host();
+ my( @cycle );
+
+ if( $format eq "brief" ) {
+ @cycle = ( "brief.txt", "txt", "standard.txt", "full.txt" );
+ } elsif( $format eq "standard" ) {
+ @cycle = ( "standard.txt", "txt", "full.txt", "brief.txt" );
+ } elsif( $format eq "full" ) {
+ @cycle = ( "full.txt", "txt", "brief.txt", "standard.txt" );
+ }
+
+ foreach( @cycle ) {
+ if( -f "$main::SPONGDB/$host/info/info.$_" ) {
+ open( FILE, "$main::SPONGDB/$host/info/info.$_" );
+ while( <FILE> ) { print $_; }
+ close( FILE );
+ return;
+ }
+ }
+
+ print "Only information in HTML is provided.\n";
+}
+
+sub display_html {
+ my( $self, $format ) = @_;
+ my $host = $self->host();
+ my( @cycle );
+
+ if( $format eq "brief" ) {
+ @cycle = ( "brief.html", "html", "brief.txt", "standard.html",
+ "standard.txt", "full.html", "full.txt" );
+ } elsif( $format eq "standard" ) {
+ @cycle = ( "standard.html", "html", "standard.txt", "full.html",
+ "full.txt", "breif.html", "brief.txt" );
+ } elsif( $format eq "full" ) {
+ @cycle = ( "full.html", "html", "full.txt", "brief.html", "brief.txt",
+ "standard.html", "standard.txt" );
+ }
+
+ foreach( @cycle ) {
+ if( -f "$main::SPONGDB/$host/info/info.$_" ) {
+ open( FILE, "$main::SPONGDB/$host/info/info.$_" );
+ while( <FILE> ) { print $_; }
+ close( FILE );
+ return;
+ }
+ }
+}
+
+1;
+
--- /dev/null
+#!/usr/local/bin/perl
+#
+# This object represents a service that is running on a given machine. It
+# contains information such as the state of the service (red,green,etc...), the
+# time the service was last updated, summary information about the state, and
+# additional information helpful if there is a problem.
+#
+# host - string FQDN of the machine its associated with
+# name - string name of the service
+# color - computed color of the service (can be purple or blue)
+# rcolor - reported color of the service (only red, yellow, green)
+# rtime - reported last updated time of the service (time() format)
+# stime - reported start of current status (time() format)
+# summary - short summary providing additional info about the service
+# message - more detailed information about the service
+#
+# + new() - constructor (sets instance vars to arguments passed in)
+# + gets/sets() - magical set/get functions (autoloaded based on func name)
+# + display() - output format and view
+#
+# + has_problem() -
+#
+# History:
+# (1) Cleaned up (Ed July 31, 1997);
+
+package Spong::Service;
+
+# Constructor. This is a lazy constructor in that it doesn't get all it's
+# instance vars set at the time of creation. Vars are loaded as they are asked
+# for, this gives us a performance gain. This object is dependent on the
+# ServiceList object for loading the information about it from the database.
+
+sub new {
+ my( $class, $host, $service ) = @_;
+ my $self = {};
+
+ # A hack to get around a local problem.
+
+ if( $service eq "procs" ) { $service = "jobs"; }
+
+ $self->{'name'} = $service;
+ $self->{'host'} = $host;
+ $self->{'color'} = "";
+ $self->{'rcolor'} = "";
+ $self->{'rtime'} = "";
+ $self->{'summary'} = "";
+ $self->{'message'} = "";
+ $self->{'stime'} = "";
+
+ bless $self;
+ return $self;
+}
+
+
+# Get/Set methods, nothing fancy here...
+
+sub host { my $var = 'host';
+ if( defined $_[1] ) { $_[0]->{$var} = $_[1]; } return $_[0]->{$var}; }
+
+sub name {
+ my $var = 'name';
+
+ # A hack to get around a local problem.
+
+ if( defined $_[1] ) {
+ if( $_[1] eq "procs" ) {
+ $_[0]->{$var} = "jobs";
+ } else {
+ $_[0]->{$var} = $_[1];
+ }
+ }
+ return $_[0]->{$var};
+}
+
+sub color { my $var = 'color';
+ if( defined $_[1] ) {
+ $_[0]->{$var} = $_[1];
+ } else {
+ my $name = $_[0]->host();
+
+ # If there is a problem reported, and we are in this machine's down time
+ # then don't report it, instead mark it as blue.
+
+ foreach( @{$main::HOSTS{$name}->{'down'}} ) {
+ my( $day, $shour, $smin, $ehour, $emin ) =
+ ( /^(.):(\d+):(\d+)-(\d+):(\d+)$/ );
+ my( $nmin, $nhour, $nday ) = (localtime(time()))[1,2,6];
+
+ if( $day eq "*" || $nday eq $day ) {
+ my $ntotal = $nhour * 60 + $nmin;
+ my $stotal = $shour * 60 + $smin;
+ my $etotal = $ehour * 60 + $emin;
+
+ if( $ntotal >= $stotal && $ntotal <= $etotal ) {
+ return 'blue';
+ }
+ }
+ }
+ }
+
+ return $_[0]->{$var};
+}
+
+sub rcolor { my $var = 'rcolor';
+ if( defined $_[1] ) { $_[0]->{$var} = $_[1]; } return $_[0]->{$var}; }
+sub rtime { my $var = 'rtime';
+ if( defined $_[1] ) { $_[0]->{$var} = $_[1]; } return $_[0]->{$var}; }
+sub stime { my $var = 'stime';
+ if( $_[0]->{$var} eq "" ) { $_[0]->summary(); } return $_[0]->{$var}; }
+
+
+# These are both lazy loading functions. If we don't have summary and message
+# information already loaded, then go out and grab it from the database and
+# return that information to the user.
+
+sub summary {
+ my $self = shift;
+ my $file;
+
+ # This is a local hack to get around the procs/jobs problems, first look
+ # for the jobs service, if it is not there, look for procs...
+
+ if( $self->name() eq "jobs" ) {
+ $file = $self->host()."/services/".$self->name()."-".$self->rcolor();
+ if( ! -f "$main::SPONGDB/$file" ) {
+ $file = $self->host()."/services/procs-".$self->rcolor();
+ }
+ } else {
+ $file = $self->host()."/services/".$self->name()."-".$self->rcolor();
+ }
+
+
+ if( ! $self->{'summary'} ) {
+ open( FILE, "$main::SPONGDB/$file" );
+ my $header = <FILE>; chomp $header;
+ # If a timestamp is present, read it and set start time
+ if ($header =~ /^timestamp (\d+) (\d+)/ ) {
+ $self->{'stime'} = $1;
+ $header = <FILE>; chomp $header;
+ }
+ ($self->{'rtime'}, $self->{'summary'}) = ( $header =~ /^(\d+) (.*)$/ );
+ while( <FILE> ) { $self->{'message'} .= $_; }
+ close( FILE );
+ }
+
+ return $self->{'summary'};
+}
+
+sub message {
+ my $self = shift;
+ my $file;
+
+ # This is a local hack to get around the procs/jobs problems, first look
+ # for the jobs service, if it is not there, look for procs...
+
+ if( $self->name() eq "jobs" ) {
+ $file = $self->host()."/services/".$self->name()."-".$self->rcolor();
+ if( ! -f $file ) {
+ $file = $self->host()."/services/procs-".$self->rcolor();
+ }
+ } else {
+ $file = $self->host()."/services/".$self->name()."-".$self->rcolor();
+ }
+
+ if( ! $self->{'message'} ) {
+ open( FILE, "$main::SPONGDB/$file" );
+ my $header = <FILE>; chomp $header;
+ # If timestamp header present, read it and set start time
+ if ($header =~ /^timestamp (\d+) (\d+)/ ) {
+ $self->{'stime'} = $1;
+ $header = <FILE>; chomp $header;
+ }
+ ($self->{'rtime'}, $self->{'summary'}) = ( $header =~ /^(\d+) (.*)$/ );
+ while( <FILE> ) { $self->{'message'} .= $_; }
+ close( FILE );
+ }
+
+ return $self->{'message'};
+}
+
+
+# This returns true or false depending on if there is a problem with the
+# service. This allows you to define some smarts into what a problem is.
+
+sub has_problem {
+ my $self = shift;
+
+ if( $self->color() eq "red" ) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+
+# Display summary. Does both text and html, doesn't make any calls to
+# sub-objects or other helper objects, just spits out the data that it has.
+#
+# brief: Just show the color of the service (or a colored ball in html)
+# standard: Shows a line stating the name/color/updated/summary information
+# full: Shows standard information plus any messages the service has
+
+sub display {
+ my( $self, $type, $view ) = @_;
+
+ if( $type eq "text" ) { return $self->display_text( $view ); }
+ if( $type eq "html" ) { return $self->display_html( $view ); }
+}
+
+sub display_text {
+ my( $self, $format ) = @_;
+
+ if( $format eq "really_brief" ) {
+ if( $self->color() eq "green" ) { print ". "; }
+ if( $self->color() eq "purple" ) { print "? "; }
+ if( $self->color() eq "yellow" ) { print "Y "; }
+ if( $self->color() eq "red" ) { print "R "; }
+ if( $self->color() eq "blue" ) { print "B "; }
+ } elsif( $format eq "brief" ) {
+ print $self->color(), "\n";
+ } elsif( $format eq "standard_table" ) {
+ my( $d1, $m1, $y1 ) = (localtime( $self->rtime()))[3,4,5];
+ my( $d2, $m2, $y2 ) = (localtime())[3,4,5];
+ my $name = substr( $self->name(), 0, 7 );
+ my $color = $self->color();
+ my $summary = $self->summary();
+ my $firstline = 1;
+
+ print $name, " "x(8-length($name));
+ print $color, " "x(9-length($color));
+
+ if( $d1 == $d2 && $m1 == $m2 && $y1 == $y2 ) {
+ $date = POSIX::strftime( "%H:%M", localtime($self->rtime()) );
+ } else {
+ $date = POSIX::strftime( "%D", localtime($self->rtime()) );
+ }
+ print $date, " "x(11-length($date));
+
+ do {
+ print " "x30 if ! $firstline++;
+ print substr( $summary, 0, 50 ), "\n";
+ $summary = substr( $summary, 51 );
+ } while( length( $summary > 50 ) );
+ } elsif( $format eq "standard" ) {
+ print "Name Color Updated Summary\n";
+ print "------- -------- ---------- ", "-"x50, "\n";
+ $self->display_text( "standard_table" );
+ } elsif( $format eq "full" ) {
+ $self->display_text( "standard" );
+ print "\nStatus unchanged in " .
+ &format_duration($self->stime(),$self->rtime) . "\n";
+ print "-"x78, "\n";
+ print $self->message(), "\n";
+ }
+}
+
+sub display_html {
+ my( $self, $format ) = @_;
+ my $host = $self->host;
+ my $name = $self->name();
+ my $color = $self->color();
+ my( $d1, $m1, $y1 ) = (localtime( $self->rtime()))[3,4,5];
+ my( $d2, $m2, $y2 ) = (localtime())[3,4,5];
+
+ if( $format eq "brief" ) {
+ print "<a href=\"!!WWWSPONG!!/service/$host/$name\">\n";
+
+ if( $main::WWW_USE_IMAGES == 1 ) {
+ print "<img src=\"!!WWWGIFS!!/$color.gif\" alt=$color border=0>";
+ } else {
+ print "<table border=0 cellspacing=0 cellpadding=0><tr>";
+ print "<td width=20 bgcolor=\"" . $main::WWW_COLOR{$color} ;
+ print "\"><font color=\"" . $main::WWW_COLOR{$color} . "\">";
+ print "___</font></td></tr></table>\n";
+ }
+ print "</a>";
+ } elsif( $format eq "standard_table" ) {
+ print "<tr><td align=left valign=top nowrap>\n";
+ print "<a href=\"!!WWWSPONG!!/service/$host/$name\">$name</a></td>\n";
+ print "<td align=center valign=top>\n";
+
+ if( $main::WWW_USE_IMAGES == 1 ) {
+ print "<a href=\"!!WWWSPONG!!/service/$host/$name\">\n";
+ print "<img src=\"!!WWWGIFS!!/$color.gif\" alt=$color border=0></a>";
+ } else {
+ print "<table border=0 cellspacing=0 cellpadding=0><tr>";
+ print "<td width=20 bgcolor=\"" . $main::WWW_COLOR{$color} . "\">";
+ print "<a href=\"!!WWWSPONG!!/service/$host/$name\">";
+ print "<font color=\"" . $main::WWW_COLOR{$color} . "\">___</font>";
+ print "</a></td></tr></table>\n";
+ }
+
+ print "</td>\n";
+ print "<td align=center valign=top nowrap>";
+
+ if( $d1 == $d2 && $m1 == $m2 && $y1 == $y2 ) {
+ print POSIX::strftime( "%H:%M", localtime($self->rtime()) ), " ";
+ } else {
+ print POSIX::strftime( "%D", localtime($self->rtime()) ), " ";
+ }
+
+ print "</td>\n";
+ print "<td align=left valign=top>", $self->summary(), "</td></tr>\n";
+ } elsif( $format eq "standard" ) {
+ print "<table width=100% border=1 cellspacing=2 cellpadding=2><tr>\n";
+ print "<td width=60 align=center><b>Service</b></td>\n";
+ print "<td width=1%> </td>\n";
+ print "<td width=60 align=center><b>Updated</b></td>\n";
+ print "<td width=100% align=center><b>Summary</b></td></tr>\n";
+
+ $self->display_html( "standard_table" );
+
+ print "</table>\n";
+ } elsif( $format eq "full" ) {
+ print "<font size=+2><b>", $self->host(), "/", $self->name();
+ print "</b></font>\n";
+
+ # This breaks the generic interactive vs non-interactive model
+ $self->add_action_bar();
+
+ print "<table width=100% cellspacing=0 cellpadding=0 border=0>";
+ print "<tr><td bgcolor=\"" . $main::WWW_COLOR{$color} . "\"> </td>";
+ print "</tr></table><p>";
+
+ print "<b>Updated:</b> ";
+ print POSIX::strftime( "%H:%M, %D", localtime($self->rtime()) );
+
+ print "<br><b>Duration:</b> ";
+ print &format_duration($self->stime(),$self->rtime());
+
+ print "<br><b>Summary:</b> ", $self->summary(), "<br>\n";
+ print "<hr noshade><pre>", $self->message(), "</pre>\n";
+ }
+}
+
+
+# Returns a message that can be sent to the person who is on call for this
+# machine which indicates the problem with this machine.
+
+sub _problem_message {
+ my $self = shift;
+
+ if( $self->has_problem() ) {
+ return "Problem: " . $self->name() . ", " . $self->summary();
+ } else {
+ return "No Problem";
+ }
+}
+
+
+sub add_action_bar {
+ my( $self ) = shift;
+ my $name = $self->host();
+ my $message = &escape( "Host: $name, " . $self->_problem_message() );
+
+ print "<hr>";
+ print "<a href=\"telnet://$name\">Connect to Host</a> || ";
+ print "<a href=\"$main::WWWACK/$host/$service\">Acknowledge Problem</a> ";
+
+ if( $main::WWWCONTACT ) {
+ print "|| <a href=\"$main::WWWCONTACT?host=$name&message=$message\">";
+ print "Contact Help</a>";
+ }
+
+ print "<hr>\n";
+}
+
+sub escape {
+ my($toencode) = @_;
+ $toencode=~s/([^a-zA-Z0-9_\-.])/uc sprintf("%%%02x",ord($1))/eg;
+ return $toencode;
+}
+
+sub format_duration {
+ my($stime,$rtime) = @_;
+
+ my $duration = $rtime - $stime;
+ my $units = "seconds";
+ if ($duration > 86400.0) { $duration /= 86400.0; $units = "days"; }
+ elsif ($duration > 3600.0) { $duration /= 3600.0; $units = "hours"; }
+ elsif ($duration > 60.0) { $duration /= 60.0; $units = "minutes"; }
+
+ return sprintf("%.2f %s", $duration, $units);
+}
+
+1;
--- /dev/null
+#!/usr/local/bin/perl
+#
+# This class represents a list of services that run on a given host, allowing
+# you to query individual services for status information. This knows how to
+# load the Service information from the database, and has the smarts to change
+# the service colors based on updates times and Acks.
+#
+# host - string FQDN of the machine its associated with
+# service_hash - a hash containing the services org by service names
+#
+# + new() - constructor (sets instance vars to arguments passed in)
+# + gets/sets() - magical set/get functions (autoloaded based on func name)
+# + display() - output format and view
+#
+# + service() - returns the specific Service object based on name
+# + services() - returns a list of all the Service objects
+# + names() - returns a list of all the Service object names.
+# + color() - returns the LCD color of all the services in the list
+#
+# History:
+# (1) Cleaned up (Ed July 31, 1997);
+
+use Spong::Service;
+use Spong::AckList;
+use Spong::Ack;
+
+package Spong::ServiceList;
+
+# Constructor. This requires the FQDN of the host that the Services are
+# associated with. It goes through and loads the information from the database
+# and also loads any Acks that might be associated with the host. It then
+# computes the colors for each host and builds Service objects for each service
+# running on that host. If there are no services running on the host, then
+# undef is returned.
+
+sub new {
+ my( $class, $host ) = @_;
+ my( %service_hash, @acks, $service, $ack, $service_name, $got_one );
+ my $self = {};
+
+ return undef if ! -d "$main::SPONGDB/$host/services";
+
+ # Go through the database, and grab the name and state of each service
+ # running on this host.
+
+ opendir( DIR, "$main::SPONGDB/$host/services" );
+ while( defined( $_ = readdir( DIR ) ) ) {
+ next unless /^(.*)-(green|yellow|red)$/ ;
+ my( $name, $color ) = ($1, $2);
+
+ # A hack to get around a local problem.
+ if( $name eq "procs" ) { $name = "jobs"; }
+
+ $got_one = 1;
+ my( $rtime ) = (stat( "$main::SPONGDB/$host/services/$_" ))[9];
+ my $service = Spong::Service->new( $host, $name );
+
+ $service->rtime( $rtime );
+ if( $rtime < ( time() - (3 * $main::SPONGSLEEP) ) ) {
+ $service->rcolor( $color ); $service->color( "purple" );
+ } else {
+ $service->rcolor( $color ); $service->color( $color );
+ }
+ $service_hash{$name} = $service;
+ }
+ closedir( DIR );
+
+ return undef unless $got_one;
+
+ # Go through the acknowledgments that we have for this host, and change the
+ # state information on our Services if needed.
+
+ $acklist = Spong::AckList->new( $host );
+ if( $acklist ) {
+ foreach $ack ( $acklist->acks() ) {
+ my $ack_services = $ack->services();
+
+ if( time() >= $ack->start() && time() <= $ack->end() ) {
+ foreach $service_name ( keys %service_hash ) {
+ if( $ack_services eq "all" ||
+ $ack_services =~ /\b$service_name\b/ ) {
+
+ $service_hash{$service_name}->color( "blue" );
+ }
+ }
+ }
+ }
+ }
+
+ $self->{'host'} = $host;
+ $self->{'service_hash'} = \%service_hash;
+
+ bless $self;
+ return $self;
+}
+
+# Get/Set methods, nothing fancy here...
+
+sub host { my $var = 'host';
+ if( defined $_[1] ) { $_[0]->{$var} = $_[1]; } return $_[0]->{$var}; }
+sub service_hash { my $var = 'service_hash';
+ if( defined $_[1] ) { $_[0]->{$var} = $_[1]; } return $_[0]->{$var}; }
+
+
+# Some utility functions that return information based on instance vars, and
+# the state of services that this object holds.
+
+sub service { return $_[0]->{'service_hash'}->{$_[1]}; }
+sub services { return values %{$_[0]->{'service_hash'}}; }
+sub names { return keys %{$_[0]->{'service_hash'}}; }
+
+sub color {
+ my $self = shift;
+ my( %state_hash ) = qw( blue 3 red 4 yellow 2 purple 1 green 0 );
+ my $state = "green";
+
+ foreach $service ( $self->services() ) {
+ if( $state_hash{$service->color()} > $state_hash{$state} ) {
+ $state = $service->color(); } }
+
+ return $state;
+}
+
+
+# Display summary. Does both text and html, it relies on the Service objects
+# to print out information about themselves, but this adds a little to by
+# printing header information in cases.
+#
+# brief: Just shows name of services and what color they are
+# standard: Shows a table of services (name,color,updated,summary)
+# full: Shows standard information about service along with messages
+#
+# ** full is not implemented yet.
+
+sub display {
+ my( $self, $type, $view ) = @_;
+
+ if( $type eq "text" ) { return $self->display_text( $view ); }
+ if( $type eq "html" ) { return $self->display_html( $view ); }
+}
+
+sub display_text {
+ my( $self, $format ) = @_;
+ my( $service );
+
+ if( $format eq "brief" ) {
+ foreach $service ( $self->services() ) {
+ print $service->name, " "x(8-length( $service->name() ));
+ $service->display_text( "brief" );
+ }
+ } elsif( $format eq "standard" ) {
+ print "Name Color Updated Summary\n";
+ print "------- -------- ---------- ", "-"x50, "\n";
+ foreach( $self->services() ) { $_->display_text( "standard_table" ); }
+ } elsif( $format eq "full" ) {
+ foreach $service ( $self->services() ) {
+ $service->display_text( "full" );
+ print "\n\n";
+ }
+ }
+}
+
+sub display_html {
+ my( $self, $format ) = @_;
+ my( $event, $date );
+
+ if( $format eq "brief" ) {
+ print "<table border=0 cellspacing=1 cellpadding=1>\n";
+ foreach $service ( $self->services() ) {
+ my $host = $service->host();
+ my $name = $service->name();
+
+ print "<tr><td align=center valign=center>";
+ $service->display_html( "brief" );
+ print "</td><td align=left valign=center>";
+ print " <a href=\"!!WWWSPONG!!/service/$host/$name\">$name</a>";
+ print "</td></tr>\n";
+ }
+ print "</table>\n";
+
+ } elsif( $format eq "standard" ) {
+ print "<table width=100% border=1 cellspacing=1 cellpadding=1><tr>\n";
+ print "<td width=60 align=center nowrap><b>Service</b></td>\n";
+ print "<td width=1% nowrap> </td>\n";
+ print "<td width=60 align=center nowrap><b>Updated</b></td>\n";
+ print "<td width=100% align=center><b>Summary</b></td></tr>\n";
+
+ foreach( $self->services() ) { $_->display_html( "standard_table" ); }
+
+ print "</table>\n";
+ }
+}
+
+1;