should have been under svn control
[buildfarm-server.git] / bf-alerts.pl
1 #!/usr/bin/perl
2
3 use strict;
4
5 use Digest::SHA1  qw(sha1_hex);
6 use MIME::Base64;
7 use DBI;
8 use DBD::Pg;
9 use Data::Dumper;
10 use Mail::Send;
11 use Safe;
12
13 use vars qw($dbhost $dbname $dbuser $dbpass $dbport
14        $all_stat $fail_stat $change_stat $green_stat
15 );
16
17 require "$ENV{BFConfDir}/BuildFarmWeb.pl";
18
19 die "no dbname" unless $dbname;
20 die "no dbuser" unless $dbuser;
21
22 # don't use configged dbuser/dbpass
23
24 $dbuser=""; $dbpass="";
25
26 my $dsn="dbi:Pg:dbname=$dbname";
27 $dsn .= ";host=$dbhost" if $dbhost;
28 $dsn .= ";port=$dbport" if $dbport;
29
30 my $db = DBI->connect($dsn,$dbuser,$dbpass);
31
32 die $DBI::errstr unless $db;
33
34 my $clear_old = $db->do(q[
35
36     DELETE FROM alerts
37     WHERE sysname IN
38       (SELECT name FROM buildsystems WHERE no_alerts)
39                            ]);
40
41
42 my $sth = $db->prepare(q[
43
44     SELECT DISTINCT ON (sysname, branch) 
45          sysname, branch, 
46          extract(epoch from snapshot at time zone 'GMT')::int as snapshot, 
47          conf_sum as config
48     FROM build_status s join buildsystems b on (s.sysname = b.name)
49     WHERE NOT b.no_alerts and
50        snapshot > current_timestamp - interval '30 days'
51     ORDER BY sysname, branch, snapshot desc
52
53                           ]);
54
55 $sth->execute;
56
57 my @last_heard;
58
59 while (my $row = $sth->fetchrow_hashref)
60 {
61     push(@last_heard, $row);
62 }
63
64 $sth->finish;
65
66 my $sql = q[
67
68    SELECT sysname, branch, 
69             extract(epoch from first_alert) as first_alert, 
70             extract(epoch from last_notification) as last_notification
71    FROM alerts
72
73             ];
74
75 my $alerts = $db->selectall_hashref($sql,['sysname','branch']);
76
77 my @need_cleared;
78 my @need_alerts;
79
80 my $clear_sth = $db->prepare(q[
81
82   DELETE FROM alerts
83   WHERE sysname = ?
84   AND branch = ?
85                       ]);
86
87 my $update_sth = $db->prepare(q[
88
89   UPDATE alerts
90   SET last_notification = timestamp '1970-01-01' + ( interval '1 second' * $1)
91   WHERE sysname = $2
92   AND branch = $3
93                       ]);
94
95 my $insert_sth = $db->prepare(q[
96
97   INSERT INTO alerts ( sysname, branch, first_alert, last_notification )
98   VALUES ($1, $2,  
99           timestamp '1970-01-01' + ( interval '1 second' * $3),
100           timestamp '1970-01-01' + ( interval '1 second' * $4))
101                       ]);
102
103
104 my $now = time;
105 my $lts = scalar(localtime);
106 print "starting alert run: $lts\n";
107
108 foreach my $sysbranch (@last_heard)
109 {
110     # eval the config in a Safe container to protect ourselves
111     my $container = new Safe;
112     my $sconf = $sysbranch->{config}; 
113     unless ($sconf =~ s/.*(\$Script_Config)/$1/ms )
114     {
115         $sconf = '$Script_Config={};';
116     }
117     my $client_conf = $container->reval("$sconf;");
118
119     my %client_alert_settings = %{ $client_conf->{alerts} || {} };
120     my $setting = $client_alert_settings{$sysbranch->{branch}};
121     unless ($setting && $setting->{alert_after} && $setting->{alert_every})
122     {
123         # if no valid setting, clear any alert and keep going
124         if ($alerts->{$sysbranch->{sysname}}->{$sysbranch->{branch}})
125         {
126             $clear_sth->execute($sysbranch->{sysname},$sysbranch->{branch});
127             push(@need_cleared,[$sysbranch]);
128         }
129         next;
130     }
131     # ok, we have valid settings. should the alert be on?
132     my $hours_since_heard = ($now - $sysbranch->{snapshot}) / 3600;
133     # yep
134     print 
135         "have settings for $sysbranch->{sysname}:$sysbranch->{branch} ",
136         "hours since heard = $hours_since_heard, ",
137         "setting = $setting->{alert_after} / $setting->{alert_every} \n";
138
139     if ($hours_since_heard > $setting->{alert_after})
140     {
141         my $known_alert = 
142             $alerts->{$sysbranch->{sysname}}->{$sysbranch->{branch}};
143         if ($known_alert && 
144             ($now - (3600 * $setting->{alert_every})) >
145             $known_alert->{last_notification})
146         {
147             # check if it's too old - 15 days and twice initial seems plenty
148             if ($hours_since_heard > 360 && 
149                      $hours_since_heard  > 2 * $setting->{alert_after} )
150             {
151                 print "alert is too old ... giving up\n";
152                 next;
153             }
154
155             # old alert, but time to alert again
156             print "alert is on, but time to alert again\n";
157             $update_sth->execute($now,
158                                  $sysbranch->{sysname},
159                                  $sysbranch->{branch},
160                                  );
161             push(@need_alerts,[$sysbranch,$setting]);
162             print "alert updated\n";
163         }
164         elsif ( ! $known_alert )
165         {
166             # new alert
167             print "new alert needed\n";
168             $insert_sth->execute($sysbranch->{sysname},
169                                  $sysbranch->{branch},
170                                  $now,$now);
171             print "new record inserted\n";
172             push(@need_alerts,[$sysbranch,$setting]);
173         }
174     }
175     # nope, so clear the alert if it exists
176     elsif ($alerts->{$sysbranch->{sysname}}->{$sysbranch->{branch}})
177     {
178         print "clear exisiting alerts";
179         $clear_sth->execute($sysbranch->{sysname},$sysbranch->{branch});
180         push(@need_cleared,[$sysbranch,$setting]);
181     }
182     
183 }
184
185 print "start emails\n";
186
187 my $addr_sth = $db->prepare(q[
188
189   SELECT owner_email
190   FROM buildsystems
191   WHERE name = ?
192                  ]);
193
194
195 my $me = `id -un`; chomp $me;
196
197 my $host = `hostname`; chomp $host;
198
199
200
201 foreach my $clearme (@need_cleared)
202 {
203     my ($sysbranch, $setting) = @$clearme;
204     my ($animal, $branch) = ($sysbranch->{sysname},$sysbranch->{branch});
205     my $text;
206     if ($setting)
207     {
208         my $hours = ($now - $sysbranch->{snapshot}) / 3600;
209         $text = "$sysbranch->{sysname} has now reported " .
210             "on $sysbranch->{branch} $hours hours ago.";
211     }
212     else
213     {
214         $text = "$sysbranch->{sysname} has lost alarm settings on branch: " .
215             "$sysbranch->{branch}. Resetting alarm to off.";
216     }
217     my $msg = new Mail::Send;
218
219     $msg->set('From',"PG Build Farm <$me\@$host>");
220
221     $addr_sth->execute($animal);
222
223     my $mailto = $addr_sth->fetchrow_array;
224
225     print "sending clear to $mailto\n";
226
227     # $sth->finish;
228
229     $msg->to($mailto);
230     $msg->subject("PGBuildfarm member $animal Branch $branch Alert cleared");
231     my $fh = $msg->open;
232     print $fh "\n\n$text\n"; 
233     $fh->close;
234
235     print "alert cleared $animal $branch\n";
236 }
237
238 foreach my $clearme (@need_alerts)
239 {
240     my ($sysbranch, $setting) = @$clearme;
241     my ($animal, $branch) = ($sysbranch->{sysname},$sysbranch->{branch});
242     my $hours = ($now - $sysbranch->{snapshot}) / 3600;
243     my $text = "$sysbranch->{sysname} has not reported " .
244         "on $sysbranch->{branch} for $hours hours.";
245     my $msg = new Mail::Send;
246
247     $msg->set('From',"PG Build Farm <$me\@$host>");
248
249     $addr_sth->execute($animal);
250
251     my ($mailto) = $addr_sth->fetchrow_array;
252
253     # $sth->finish;
254
255     print "sending alert to $mailto\n";
256
257     $msg->to($mailto);
258
259     $msg->subject("PGBuildfarm member $animal Branch $branch " .
260                   "Alert notification");
261     my $fh = $msg->open;
262     print $fh "\n\n$text\n"; 
263     $fh->close;
264
265     print "alert sent $animal $branch\n";
266 }
267
268
269 print "=================================\n";
270
271
272