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