#!/usr/bin/perl -w # control-services.pl -- Monitor a listed set of services to be sure # they are running. If not running, modify the DNS system # to remove them from the lookup for that service # # '$RCSfile$' # Copyright: 2005 Regents of the University of California # # '$Author: jones $' # '$Date: 2005-10-13 22:44:45 +0000 (Thu, 13 Oct 2005) $' # '$Revision: 2675 $' # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # use Net::DNS; use LWP::UserAgent; use HTTP::Request; use HTTP::Response; use URI::URL; use strict; # include the configuration require '/etc/control-services.conf'; # make my pipes piping hot $| = 1; # run the main routine &updateDns; # When a service becomes unavailable make a DNS change that will take # that service provider out of the DNS system temporarily sub updateDns { my $recovered = $ENV{"RECOVERED"}; my $fqdn = $ENV{"BBHOSTNAME"}; my $ip = $ENV{"MACHIP"}; my $service = $ENV{"BBSVCNAME"}; my $color = $ENV{"BBCOLORLEVEL"}; my $message = $ENV{"BBALPHAMSG"}; my $ackcode = $ENV{"ACKCODE"}; my $zone = $main::zones[0]; my $class = $main::classes[0]; my $ttl = $main::default_ttl; my $type = $main::types[0]; my $success = 0; # Convert the hobbit IP number format to dotted decimal format $ip =~ s/(...)(...)(...)(...)/$1.$2.$3.$4/; $ip =~ s/^0*//; $ip =~ s/\.0*/\./g; # Check if the service went down or recovered if (!$recovered && $color eq 'red') { # If it is down, remove the host from the DNS my $record = "$service.$zone $type $ip"; my @rr = ($record); ($success,$message) = &del_records($zone,$class,@rr); my $response = ""; if ($success) { $response = "Relying on failover hosts."; #$response = &acknowledgeAlert($ackcode, $text); } &log("Failed:", $recovered, $fqdn, $ip, $service, $color, $message, $response); } elsif ($recovered) { # If it is being restored, add the host back to the DNS $ttl = '60'; ($success,$message) = &add_records($zone, $class, $service, $ttl, $type, $ip); my $response = ""; if ($success) { $response = "Host restored to DNS."; } &log("Recovered:", $recovered, $fqdn, $ip, $service, $color, $message, $response); } } # Acknowledge the failure with Hobbit so that additional notifications # are supressed # This seems to not be working properly with hobbit right now --TODO sub acknowledgeAlert { my ($ackcode, $message) = @_; my $action = "Ack"; my $url = url($main::hobbit_cgi); $url->query_form(ACTION => $action, NUMBER => $ackcode, MESSAGE => $message); my $ua = LWP::UserAgent->new(); $ua->agent("control-services/0.1"); my $request = HTTP::Request->new(GET => $url); $request->referer($main::hobbit_cgi); $request->authorization_basic($main::uname, $main::password); my $response = $ua->request($request); if ($response->is_error() ) { return $response->status_line; } else { my $content = $response->content(); return $content; } } # Log the run of the script to a temporary log file sub log { my ($lead, $recovered, $fqdn, $ip, $service, $color, $message, $response) = @_; open(LOG,">>$main::logfile") || die "Log file could not be opened."; print LOG $lead; print LOG " "; print LOG $ip; print LOG " "; print LOG $fqdn; print LOG " "; print LOG $service; print LOG " "; print LOG $color; print LOG " "; print LOG $recovered; print LOG " "; print LOG $message; print LOG " "; print LOG $response; print LOG "\n"; close(LOG); } # Get a resolver to be used for DDNS updates sub get_resolver { my ($tsig_keyname,$tsig_key) = @_; my $res = Net::DNS::Resolver->new; $res->tsig($tsig_keyname,$tsig_key); return \$res; } # Add a RR using DDNS update sub add_records { my ($zone,$class,$name,$ttl,$type,$content) = @_; # get a resolver handle and set the dns server to use my $res= &get_resolver($main::tsig_keyname,$main::tsig_key); $$res->nameservers($main::nameservers[0]); # create update packet my $update = Net::DNS::Update->new($zone,$class); my $rr = "$name.$zone $ttl $type $content"; $update->push(update => rr_add($rr)); my $reply = ${$res}->send($update); # initialize return vars my $success = 0; my $message = ''; # Did it work? if ($reply) { if ($reply->header->rcode eq 'NOERROR') { $message = "Update succeeded"; $success = 1; } else { $message = 'Update failed: ' . $reply->header->rcode; } } else { $message = 'Update failed: ' . $res->errorstring; } return ($success,$message); } # Delete one or more RRs using DDNS update sub del_records { my ($zone,$class,@rr) = @_; # get a resolver handle and set the dns server to use my $res= &get_resolver($main::tsig_keyname,$main::tsig_key); $$res->nameservers($main::nameservers[0]); my $update = Net::DNS::Update->new($zone,$class); # build update packet(s) foreach my $record (@rr) { $update->push(update => rr_del($record)); } # send it my $reply = ${$res}->send($update); my $msg = ''; my $success = 0; if ($reply) { if ($reply->header->rcode eq 'NOERROR') { $msg = "Update succeeded"; $success = 1; } else { $msg = 'Update failed: ' . $reply->header->rcode; } } else { $msg = 'Update failed: ' . $res->errorstring; } return ($success,$msg); } # Print out debugging messages sub debug { my $msg = shift; print $msg, "\n"; }