Project

General

Profile

« Previous | Next » 

Revision 2674

Added by Matt Jones about 19 years ago

Adding the control-services.pl utility for failover montitoring. From
the README:

This program is designed to run as a script under the Hobbit system
monitoring program (http://hobbitmon.sourceforge.net/). When Hobbit is
configured properly, this script will be called to make changes to the
DNS services that are registered in your DNS server. The changes are
accomplished using Dynamic DNS (DDNS). The system is intended to help
manage a set of services that are running on several hosts in
round-robin DNS fashion and to remove inaccessible hosts when they are
detected as failed, and restore them when they recover.

View differences:

src/perl/control-services/control-services.conf
1
#
2
# Configuration for control-services.pl. All variables are in perl syntax.
3
#
4

  
5
# zones we can manage
6
@main::zones = ( 'example.org.' );
7

  
8
# nameservers for the zone
9
@main::nameservers = ( 'ns1.example.org' );
10

  
11
# allowed record types
12
@main::types = ( 'A', 'PTR', 'CNAME', 'TXT' );
13

  
14
# allowed classes
15
@main::classes = ( 'IN' );
16

  
17
# default TTL for new records
18
$main::default_ttl = 86400;
19

  
20
# Location of the log file; must be writable by the hobbit user
21
$main::logfile = '/tmp/control-services.log';
22

  
23
# DDNS tsig info. Your key that you generated goes here:
24
$main::tsig_keyname = 'example';
25
$main::tsig_key     = 'JUet5dbTvV9OOmWxc4cp8UUSSbct617swzaldieBZuF=';
26

  
27
# Hobbit configuration information
28
$main::hobbit_cgi = "http://www.example.org/hobbit-seccgi/bb-ack.sh";
29
$main::uname = 'cgiuser';
30
$main::password = 'some-cgi-password';
0 31

  
src/perl/control-services/manage-domain.pl
1
#!/usr/bin/perl
2

  
3
# manage-domain.pl -- Manage a domain using DDNS to add or remove hosts
4
#
5
#  '$RCSfile$'
6
#  Copyright: 2005 Regents of the University of California 
7
#
8
#   '$Author$'
9
#     '$Date$'
10
# '$Revision$' 
11
#
12
#  This program is free software; you can redistribute it and/or modify
13
#  it under the terms of the GNU General Public License as published by
14
#  the Free Software Foundation; either version 2 of the License, or
15
#  (at your option) any later version.
16
#
17
#  This program is distributed in the hope that it will be useful,
18
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
19
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
#  GNU General Public License for more details.
21
#
22
#  You should have received a copy of the GNU General Public License
23
#  along with this program; if not, write to the Free Software
24
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
#
26

  
27
use Net::DNS;
28
use Getopt::Long;
29
use strict;
30

  
31
# include the configuration
32
require '/etc/control-services.conf';
33

  
34
# make my pipes piping hot
35
$| = 1;
36

  
37
# run the main routine
38
&main;
39

  
40
sub main {
41
    my ($host, $ip, $command);
42

  
43
    GetOptions( "host=s" => \$host,
44
                "ip=s"   => \$ip,
45
                "command=s"   => \$command );
46
    if (! defined($host) || ! defined ($ip) || !defined($command)) {
47
        usage();
48
    } else {
49
        my $zone = $main::zones[0];
50
        my $class = $main::classes[0];
51
        my $ttl = '60';
52
        my $type = 'A';
53
        my $message = '';
54
        my $success = 1;
55

  
56
        # get a resolver handle and set the dns server to use
57
        my $resref = &get_resolver($main::tsig_keyname,$main::tsig_key);
58
        $$resref->nameservers($main::nameservers[0]);
59

  
60
        if ($command eq 'add') {
61
            # Add a record
62
            ($success,$message) = &add_records( $resref, $zone, $class,
63
                                        $host, $ttl, $type, $ip );
64
            debug("Add records success: " . $success);
65
            debug("Add records message: " . $message);
66
        } elsif ($command eq 'delete') {
67
            # Delete a record
68
            my $record = "$host.$zone $type $ip";
69
            #my $record = "$host.$zone";
70
            my @rr = ($record);
71
            ($success,$message) = &del_records($resref,$zone,$class,@rr);
72
            debug("Del records success: " . $success);
73
            debug("Del records message: " . $message);
74
        } else {
75
            usage();
76
        }
77
    }
78
}
79

  
80
# Get a resolver to be used for DDNS updates
81
sub get_resolver {
82
    my ($tsig_keyname,$tsig_key) = @_;
83
    my $res = Net::DNS::Resolver->new;
84
    $res->tsig($tsig_keyname,$tsig_key);
85
    return \$res;
86
}
87

  
88
# Add a RR using DDNS update
89
sub add_records {
90
    my ($res,$zone,$class,$name,$ttl,$type,$content) = @_;
91
    
92
    # create update packet
93
    my $update = Net::DNS::Update->new($zone,$class);
94
    my $rr = "$name.$zone $ttl $type $content";
95
    debug("Inserting new record: \n    " . $rr);
96
    $update->push(update => rr_add($rr));
97
    my $reply = ${$res}->send($update);
98

  
99
    # initialize return vars
100
    my $success = 0;
101
    my $message = '';
102

  
103
    # Did it work?
104
    if ($reply) {
105
    if ($reply->header->rcode eq 'NOERROR') {
106
            $message = "Update succeeded";
107
        $success = 1;
108
        } else {
109
            $message = 'Update failed: ' . $reply->header->rcode;
110
        }
111
    } else {
112
        $message = 'Update failed: ' . $res->errorstring;
113
    }
114

  
115
    return ($success,$message);
116
}
117

  
118
# Delete one or more RRs using DDNS update
119
sub del_records {
120
    my ($res,$zone,$class,@rr) = @_;
121

  
122
    my $update = Net::DNS::Update->new($zone,$class);
123

  
124
    # build update packet(s)
125
    foreach my $record (@rr) {
126
        $update->push(update => rr_del($record));
127
    }
128

  
129
    # send it
130
    my $reply = ${$res}->send($update);
131

  
132
    my $msg = '';
133
    my $success = 0;
134
    if ($reply) {
135
        if ($reply->header->rcode eq 'NOERROR') {
136
            $msg = "Update succeeded";
137
        $success = 1;
138
        } else {
139
            $msg = 'Update failed: ' . $reply->header->rcode;
140
        $success = 0;
141
        }
142
    } else {
143
        $msg = 'Update failed: ' . $res->errorstring;
144
    $success = 0;
145
    }
146
    return ($success,$msg);
147
}
148

  
149
# Print out debugging messages
150
sub debug {
151
    my $msg = shift;
152
    print $msg, "\n";
153
}
154

  
155
# Print a usage statement
156
sub usage {
157
        print "Usage: manage-domain.pl --host hostname --ip xxx.xxx.xxx.xxx --command [add|delete]\n";
158
}
0 159

  
src/perl/control-services/control-services.pl
1
#!/usr/bin/perl -w
2

  
3
# control-services.pl -- Monitor a listed set of services to be sure
4
#             they are running.  If not running, modify the DNS system
5
#             to remove them from the lookup for that service
6
#
7
#  '$RCSfile$'
8
#  Copyright: 2005 Regents of the University of California 
9
#
10
#   '$Author$'
11
#     '$Date$'
12
# '$Revision$' 
13
#
14
#  This program is free software; you can redistribute it and/or modify
15
#  it under the terms of the GNU General Public License as published by
16
#  the Free Software Foundation; either version 2 of the License, or
17
#  (at your option) any later version.
18
#
19
#  This program is distributed in the hope that it will be useful,
20
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
21
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
#  GNU General Public License for more details.
23
#
24
#  You should have received a copy of the GNU General Public License
25
#  along with this program; if not, write to the Free Software
26
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27
#
28

  
29
use Net::DNS;
30
use LWP::UserAgent;
31
use HTTP::Request;
32
use HTTP::Response;
33
use URI::URL;
34
use strict;
35

  
36
# include the configuration
37
require '/etc/control-services.conf';
38

  
39
# make my pipes piping hot
40
$| = 1;
41

  
42
# run the main routine
43
#&testddns;
44
#print &acknowledgeAlert("223456", "Foo is good");
45
&updateDns;
46

  
47
# When a service becomes unavailable make a DNS change that will take
48
# that service provider out of the DNS system temporarily
49
sub updateDns {
50
    my $recovered = $ENV{"RECOVERED"};
51
    my $fqdn      = $ENV{"BBHOSTNAME"};
52
    my $ip        = $ENV{"MACHIP"};
53
    my $service   = $ENV{"BBSVCNAME"};
54
    my $color     = $ENV{"BBCOLORLEVEL"};
55
    my $message   = $ENV{"BBALPHAMSG"};
56
    my $ackcode   = $ENV{"ACKCODE"};
57
    my $zone = $main::zones[0];
58
    my $class = $main::classes[0];
59
    my $ttl = $main::default_ttl;
60
    my $type = $main::types[0];
61
    my $success = 0;
62

  
63
    # Convert the hobbit IP number format to dotted decimal format
64
    $ip =~ s/(...)(...)(...)(...)/$1.$2.$3.$4/;
65
    $ip =~ s/^0*//;
66
    $ip =~ s/\.0*/\./g;
67

  
68
    # Check if the service went down or recovered
69
    if (!$recovered && $color eq 'red') {
70
        # If it is down, remove the host from the DNS 
71
        my $record = "$service.$zone $type $ip";
72
        my @rr = ($record);
73
        ($success,$message) = &del_records($zone,$class,@rr);
74
        my $response = "";
75
        if ($success) {
76
            $response = "Relying on failover hosts.";
77
            #$response = &acknowledgeAlert($ackcode, $text);
78
        }
79
        &log("Failed:", $recovered, $fqdn, $ip, $service, 
80
                $color, $message, $response);
81
    } elsif ($recovered) {
82
        # If it is being restored, add the host back to the DNS
83
        $ttl      = '60';
84
        ($success,$message) = &add_records($zone, $class, $service, $ttl, 
85
                                           $type, $ip);
86
        my $response = "";
87
        if ($success) {
88
            $response = "Host restored to DNS.";
89
        }
90
        &log("Recovered:", $recovered, $fqdn, $ip, $service, 
91
                $color, $message, $response);
92
    }
93
}
94

  
95
# Acknowledge the failure with Hobbit so that additional notifications 
96
# are supressed 
97
# This seems to not be working properly with hobbit right now  --TODO
98
sub acknowledgeAlert {
99
    my ($ackcode, $message) = @_;
100
    my $action = "Ack";
101
    my $url = url($main::hobbit_cgi);
102
    $url->query_form(ACTION => $action, 
103
                     NUMBER => $ackcode, 
104
                     MESSAGE => $message);
105
    my $ua = LWP::UserAgent->new();
106
    $ua->agent("control-services/0.1");
107
    my $request = HTTP::Request->new(GET => $url);
108
    $request->referer($main::hobbit_cgi);
109
    $request->authorization_basic($main::uname, $main::password);
110
    my $response = $ua->request($request);
111
    if ($response->is_error() ) {
112
        return $response->status_line;
113
    } else {
114
        my $content = $response->content();
115
        return $content;
116
    }
117
}
118

  
119
# Log the run of the script to a temporary log file
120
sub log {
121
    my ($lead, $recovered, $fqdn, $ip, $service, $color, $message, $response) = @_;
122

  
123
    open(LOG,">>$main::logfile") || 
124
        die "Log file could not be opened.";
125
    print LOG $lead;
126
    print LOG " ";
127
    print LOG $ip;
128
    print LOG " ";
129
    print LOG $fqdn;
130
    print LOG " ";
131
    print LOG $service;
132
    print LOG " ";
133
    print LOG $color;
134
    print LOG " ";
135
    print LOG $recovered;
136
    print LOG " ";
137
    print LOG $message;
138
    print LOG " ";
139
    print LOG $response;
140
    print LOG "\n";
141
    close(LOG);
142
}
143

  
144
# test routine to see if DNS update using DDNS is working
145
sub testddns {
146
    my $zone = $main::zones[0];
147
    my $class = $main::classes[0];
148
    my $ttl = $main::default_ttl;
149
    my $type = $main::types[0];
150
    my $message = '';
151
    my $success = 1;
152

  
153
    # Add a record
154
    $ttl      = '60';
155
    $type     = 'A';
156
    my $host     = 'testhost';
157
    my $ip       = '24.237.20.95';
158
    ($success,$message) = &add_records($zone, $class, $host, $ttl, $type, $ip);
159
    debug("Add records success: " . $success);
160
    debug("Add records message: " . $message);
161

  
162
    # Delete a record
163
    my $record = "$host.$zone";
164
    my @rr = ($record);
165
    ($success,$message) = &del_records($zone,$class,@rr);
166
    debug("Del records success: " . $success);
167
    debug("Del records message: " . $message);
168
}
169

  
170
# Get a resolver to be used for DDNS updates
171
sub get_resolver {
172
    my ($tsig_keyname,$tsig_key) = @_;
173
    my $res = Net::DNS::Resolver->new;
174
    $res->tsig($tsig_keyname,$tsig_key);
175
    return \$res;
176
}
177

  
178
# Add a RR using DDNS update
179
sub add_records {
180
    my ($zone,$class,$name,$ttl,$type,$content) = @_;
181
    
182
    # get a resolver handle and set the dns server to use
183
    my $res= &get_resolver($main::tsig_keyname,$main::tsig_key);
184
    $$res->nameservers($main::nameservers[0]);
185

  
186
    # create update packet
187
    my $update = Net::DNS::Update->new($zone,$class);
188
    my $rr = "$name.$zone $ttl $type $content";
189
    $update->push(update => rr_add($rr));
190
    my $reply = ${$res}->send($update);
191

  
192
    # initialize return vars
193
    my $success = 0;
194
    my $message = '';
195

  
196
    # Did it work?
197
    if ($reply) {
198
        if ($reply->header->rcode eq 'NOERROR') {
199
            $message = "Update succeeded";
200
            $success = 1;
201
        } else {
202
            $message = 'Update failed: ' . $reply->header->rcode;
203
        }
204
    } else {
205
        $message = 'Update failed: ' . $res->errorstring;
206
    }
207

  
208
    return ($success,$message);
209
}
210

  
211
# Delete one or more RRs using DDNS update
212
sub del_records {
213
    my ($zone,$class,@rr) = @_;
214

  
215
    # get a resolver handle and set the dns server to use
216
    my $res= &get_resolver($main::tsig_keyname,$main::tsig_key);
217
    $$res->nameservers($main::nameservers[0]);
218

  
219
    my $update = Net::DNS::Update->new($zone,$class);
220

  
221
    # build update packet(s)
222
    foreach my $record (@rr) {
223
        $update->push(update => rr_del($record));
224
    }
225

  
226
    # send it
227
    my $reply = ${$res}->send($update);
228

  
229
    my $msg = '';
230
    my $success = 0;
231
    if ($reply) {
232
        if ($reply->header->rcode eq 'NOERROR') {
233
            $msg = "Update succeeded";
234
            $success = 1;
235
        } else {
236
            $msg = 'Update failed: ' . $reply->header->rcode;
237
        }
238
    } else {
239
        $msg = 'Update failed: ' . $res->errorstring;
240
    }
241
    return ($success,$msg);
242
}
243

  
244
# Print out debugging messages
245
sub debug {
246
    my $msg = shift;
247
    print $msg, "\n";
248
}
0 249

  
src/perl/control-services/README
1
#
2
# control-services.pl -- Monitor a listed set of services to be sure
3
#             they are running.  If not running, modify the DNS system
4
#             to remove them from the lookup for that service
5
#
6
#  '$RCSfile$'
7
#  Copyright: 2005 Regents of the University of California 
8
#
9
#   '$Author$'
10
#     '$Date$'
11
# '$Revision$' 
12
#
13
#  This program is free software; you can redistribute it and/or modify
14
#  it under the terms of the GNU General Public License as published by
15
#  the Free Software Foundation; either version 2 of the License, or
16
#  (at your option) any later version.
17
#
18
#  This program is distributed in the hope that it will be useful,
19
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
20
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
#  GNU General Public License for more details.
22
#
23
#  You should have received a copy of the GNU General Public License
24
#  along with this program; if not, write to the Free Software
25
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26
#
27

  
28
Welcome to the control-services.pl program.
29

  
30
This program is designed to run as a script under the Hobbit system monitoring
31
program (http://hobbitmon.sourceforge.net/).  When Hobbit is configured 
32
properly, this script will be called to make changes to the DNS services that
33
are registered in your DNS server.  The changes are accomplished using 
34
Dynamic DNS (DDNS).  The system is intended to help manage a set of services
35
that are running on several hosts in round-robin DNS fashion and to remove
36
inaccessible hosts when they are detected as failed, and restore them when 
37
they recover.
38

  
39
Configuration consists of:
40
  1) Install and configure Hobbit (see http://hobbitmon.sourceforge.net)
41
  2) Install and configure BIND for DDNS (see below)
42
  3) Install this script (see below)
43

  
44
3) Setting up BIND for DDNS
45
-----------------------------
46

  
47
You need to configure BIND to accept signed dynamic updates for your zone.  
48

  
49
    a) Create a key using dnssec-keygen.
50

  
51
        % dnssec-keygen  -b 256 -n HOST -a HMAC-MD5 example
52

  
53
        This will create 2 key files in different formats
54

  
55
    b) Add the key to your named.conf file in a block (copy it from either 
56
       of the two generated key files):
57

  
58
        key "examplekey" {
59
            algorithm       hmac-md5;
60
            secret "0QI/OEYtZA5aI6rpMTwrodfMpg1xxCaDHdR/tvI9Lgc=";
61
        };
62

  
63
    c) For each zone that you want to enable updates, add this key to
64
       your 'allow-update' and 'allow-transfer' blocks:
65
    
66
       zone "example.com" {
67
           type master;
68
           file "/var/named/example.com.zone"
69
           allow-update ( key examplekey; };
70
           allow-transfer { key examplekey; };
71
       }
72

  
73
    d) Reload named to reload your configuration (killall -HUP named).
74

  
75
3) Install the control-services.pl script
76
-----------------------------------------
77
Prerequisites:
78

  
79
    The following Perl Modules are required:
80
        Net::DNS
81
        LWP::UserAgent
82
        HTTP::Request
83
        HTTP::Response
84
        URI::URL
85
    These modules are available through CPAN, and are most easily gotten using
86
    the CPAN shell (as root, "perl -MCPAN -e shell").
87

  
88
Installation:
89
  1) copy the file to a commonly accessible location (typically /usr/local/bin)
90
  2) change ownership to the web-server user
91
  3) copy the configuration file to /etc and customize for your situation
92
     -- set the key name and value from your DDNS server
93
     -- set domain and other parameters properly
94
  4) change ownership of the config file to the web-server user
95
  5) restrict access to the config file to only the web server user
96
  6) Create the log file directory, writable by the hobbit user
97
  7) Place alert rules in the hobbit/server/etc/hobbit-alerts.cfg file that
98
     use the script.  See the hobbit-alerts.cfg man page for details. An
99
     example rule might be:
100

  
101
     HOST=ldap.example.com SERVICE=ldap RECOVERED
102
          SCRIPT /usr/local/bin/control-services.pl ldap FORMAT=SCRIPT REPEAT=1
103

  
104

  
105
IMPORTANT NOTE
106
----------------
107
Make sure that the configuration file is only readable by the webserver user
108
and writable by root. The config file contains the key to allow updates
109
to your DNS server, so be sure the config file is secure.
110

  
111
Enjoy!
0 112

  

Also available in: Unified diff