Subversion Repositories DevTools

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
7423 dpurdie 1
#! /usr/bin/perl
2
########################################################################
3
# COPYRIGHT - VIX IP PTY LTD ("VIX"). ALL RIGHTS RESERVED.
4
#
5
# Module name   : blatReleaseNotes.pl
6
# Module type   :
7
# Compiler(s)   : Perl
8
# Environment(s):
9
#
10
# Description   :   This is a blat related task that will generate Release
11
#                   Notes as required by the build system
12
#                   
13
#                   Replaces a cron job that did the same task as cron
14
#                   will only run once a minute.
15
#
16
# Usage         :   ARGV[0] - Path to config file for this instance
17
#
18
#......................................................................#
19
 
20
require 5.008_002;
21
use strict;
22
use warnings;
23
use Getopt::Long;
24
use File::Basename;
25
use Data::Dumper;
26
use File::Spec::Functions;
27
use POSIX ":sys_wait_h";
28
use File::Temp qw/tempfile/;
29
use Digest::MD5;
30
 
31
use FindBin;                                    # Determine the current directory
32
use lib "$FindBin::Bin/lib";                    # Allow local libraries
33
 
34
use Utils;
35
use StdLogger;                                  # Log to sdtout
36
use Logger;                                     # Log to file
37
 
38
#
39
#   Database interface
40
#   Pinched from jats and modified so that this software is not dependent on JATS
41
#
42
use IO::Handle;
43
use JatsRmApi;
44
use DBI;
45
 
46
#
47
#   Globals
48
#
49
my $logger = StdLogger->new();                  # Stdout logger. Only during config
50
$logger->err("No config file specified") unless (defined $ARGV[0]);
51
$logger->err("Config File does not exist: $ARGV[0]") unless (-f $ARGV[0]);
52
my $name = basename( $ARGV[0]);
53
   $name =~ s~.conf$~~;
54
my $now = 0;
55
my $startTime = 0;
56
my $tagDirTime = 0;
57
my $lastDirScan = 0;
58
my $mtimeConfig = 0;
59
my $conf;
60
my $yday = -1;
61
my $tagRoot;
62
my $wedgedCount = 0;
63
my $linkState = 0;
64
 
65
#
66
#   Contain statisics maintained while operating
67
#       Can be dumped with a kill -USR2
68
#       List here for documentation
69
#  
70
 
71
my %statistics = (
72
    SeqNum => 0,                        # Bumped when $statistics are dumped
73
    timeStamp => 0,                     # DateTime when statistics are dumped
74
    upTime => 0,                        # Seconds since program start
75
    Cycle => 0,                         # Major process loop counter
76
    phase => 'Init',                    # Current phase of operation
77
    state => 'OK',                      # Nagios state
78
    wedged => 0,                        # Wedge indication - main loop not cycling
79
                                        # 
80
                                        # The following are reset each day
81
    dayStart => 0,                      # DateTime when daily data was reset
82
    txCount => 0,                       # Packages Transferred - Release Notes Generated
83
    linkErrors => 0,                    # Transfer errors - Errors encountered
84
                                        # 
85
                                        # Per Cycle Data - Calculated each processing Cycle
86
                                        # None for Release Notes
87
);
88
 
89
#
90
#   Describe configuration parameters
91
#
92
my %cdata = (
93
    'piddir'          => {'mandatory' => 1      , 'fmt' => 'dir'},
94
    'sleep'           => {'default'   => 5      , 'fmt' => 'period'},
95
    'dpkg_archive'    => {'mandatory' => 1      , 'fmt' => 'dir'},
96
    'logfile'         => {'mandatory' => 1      , 'fmt' => 'vfile'},
97
    'logfile.size'    => {'default'   => '1M'   , 'fmt' => 'size'},
98
    'logfile.count'   => {'default'   => 9      , 'fmt' => 'int'},
99
    'verbose'         => {'default'   => 0      , 'fmt' => 'int'},
100
    'active'          => {'default'   => 1      , 'fmt' => 'bool'},
101
    'debug'           => {'default'   => 0      , 'fmt' => 'bool'},                 # Log to screen
102
    'txdetail'        => {'default'   => 0      , 'fmt' => 'bool'},
103
    'tagdir'          => {'mandatory' => 1      , 'fmt' => 'mkdir'},
104
    'forcedirscan'    => {'default'   => 100    , 'fmt' => 'period'},
105
    'tagage'          => {'default'   => '10d'  , 'fmt' => 'period'},
106
 
107
    'JIRA_URL'        => {'mandatory' => 1      , 'fmt' => 'text'},
108
    'JIRA_USERNAME'   => {'mandatory' => 1      , 'fmt' => 'text'},
109
    'JIRA_PASSWORD'   => {'mandatory' => 1      , 'fmt' => 'text'},
110
    'RM_USERNAME_RW'  => {'mandatory' => 1      , 'fmt' => 'text'},
111
    'RM_PASSWORD_RW'  => {'mandatory' => 1      , 'fmt' => 'text'},
112
);
113
 
114
 
115
#
116
#   Read in the configuration
117
#       Set up a logger
118
#       Write a pidfile - thats not used
119
$now = $startTime = time();
120
readConfig();
121
Utils::writepid($conf);
122
$logger->logmsg("Starting...");
123
readStatistics();
124
sighandlers();
125
 
126
#
127
#   Main processing loop
128
#   Will exit when terminated by parent
129
#
130
while (1)
131
{
132
    $logger->verbose3("Processing");
133
    $statistics{Cycle}++;
134
    $wedgedCount = 0;
135
    $now = time();
136
 
137
    $statistics{phase} = 'ReadConfig';
138
    readConfig();
139
    if ( $conf->{'active'} )
140
    {
141
        $statistics{phase} = 'Monitor Tags';
142
        processRequests();
143
    }
144
 
145
    $statistics{phase} = 'Sleep';
146
    sleep( $conf->{'sleep'} );
147
    reapChildren();
148
 
149
    #   If my PID file ceases to be, then exit the daemon
150
    #   Used to force daemon to restart
151
    #
152
    unless ( -f $conf->{'pidfile'} )
153
    {
154
        $logger->logmsg("Terminate. Pid file removed");
155
        last;
156
    }
157
}
158
$statistics{phase} = 'Terminated';
159
$logger->logmsg("Child End");
160
exit 0;
161
 
162
#-------------------------------------------------------------------------------
163
# Function        : reapChildren 
164
#
165
# Description     : Reap any and all dead children
166
#                   Call in major loops to prevent zombies accumulating 
167
#
168
# Inputs          : None
169
#
170
# Returns         : 
171
#
172
sub reapChildren
173
{
174
    my $currentPhase = $statistics{phase};
175
    $statistics{phase} = 'Reaping';
176
 
177
    my $kid;
178
    do {
179
        $kid = waitpid(-1, WNOHANG);
180
    } while ( $kid > 0 );
181
 
182
    $statistics{phase} = $currentPhase;
183
}
184
 
185
 
186
#-------------------------------------------------------------------------------
187
# Function        : readConfig
188
#
189
# Description     : Re read the config file if it modification time has changed
190
#
191
# Inputs          : Nothing
192
#
193
# Returns         : 0       - Config not read
194
#                   1       - Config read
195
#                             Config file has changed
196
#
197
sub readConfig
198
{
199
    my ($mtime) = Utils::mtime($ARGV[0]);
200
    my $rv = 0;
201
 
202
    if ( $mtimeConfig != $mtime )
203
    {
204
        $logger->logmsg("Reading config file: $ARGV[0]");
205
        $mtimeConfig = $mtime;
206
        my $errors;
207
        ($conf, $errors) = Utils::readconf ( $ARGV[0], \%cdata );
208
        if ( scalar @{$errors} > 0 )
209
        {
210
            warn "$_\n" foreach (@{$errors});
211
            die ("Config contained errors\n");
212
        }
213
 
214
        #
215
        #   Reset some information
216
        #   Create a new logger
217
        #
218
        $logger = Logger->new($conf) unless $conf->{debug};
219
        $conf->{logger} = $logger;
220
        $conf->{'pidfile'} = $conf->{'piddir'} . '/' . $name . '.pid';
221
        $logger->setVerbose($conf->{verbose});
222
        $logger->verbose("Log Levl: $conf->{verbose}");
223
 
224
        #
225
        #   Setup statistics filename
226
        $conf->{'statsfile'} = $conf->{'piddir'} . '/' . $name . '.stats';
227
        $conf->{'statsfiletmp'} = $conf->{'piddir'} . '/' . $name . '.stats.tmp';
228
 
229
        #
230
        #   Calculate the base of the tags directory
231
        #   ASSUME all tagdirs are in the same tree as my tags dir
232
        #
233
        $conf->{'tagdir'} =~ m~^(.*)/~;
234
        $tagRoot = $1;
235
    }
236
 
237
    #
238
    #   When config is read force some actions
239
 
240
#Utils::DebugDumpData ("Config", $conf);
241
 
242
    $logger->warn("ReleaseNote is inactive") unless ( $conf->{'active'} );
243
    return $rv;
244
}
245
 
246
 
247
#-------------------------------------------------------------------------------
248
# Function        : processRequests
249
#
250
# Description     : Process tags and generate Release Notes as required
251
#                       Determine if new tags are present - really just
252
#                       a trigger mechanism
253
#
254
# Inputs          : None
255
#
256
# Returns         : Nothing
257
#
258
sub processRequests
259
{
260
    #
261
    #   Determine if new tags are present by examining the time
262
    #   that the directory was last modified.
263
    #
264
    #   Allow for a forced scan to catch packages that did not transfer
265
    #   on the first attempt
266
    #
267
    my ($mtime) = Utils::mtime($conf->{'tagdir'} );
268
    if ( ($mtime > $tagDirTime) || ($now > ($lastDirScan + $conf->{'forcedirscan'})) )
269
    {
270
        $logger->verbose2("processTags: ,$conf->{'tagdir'}");
271
        $tagDirTime = $mtime;
272
        $lastDirScan = $now;
273
 
274
 
275
        #
276
        #   Delete any tags that we find
277
        #   
278
        my @tags = glob (catdir($conf->{'tagdir'}, '*::*'));
279
        unlink @tags;
280
 
281
        #
282
        #   Release notes generation is done my an exernal program
283
        #   Need to set up some EnvVars for config
284
        #
285
        foreach  (qw (JIRA_URL JIRA_USERNAME JIRA_PASSWORD RM_USERNAME_RW RM_PASSWORD_RW) ) {
286
            $ENV{$_} = $conf->{$_};
287
        }
288
 
289
        my $jats = '/usr/local/bin/jats';
290
        my $releaseNotes = "$conf->{dpkg_archive}/generate_release_notes/latest/scripts/process_release_notes.pl";
291
        my $opts = "-status";
292
        $opts .= ' -v' if ($conf->{verbose} > 1);
293
        $opts .= ' -v' if ($conf->{verbose} > 2);
294
 
295
        $logger->err("Jats not found: $jats") unless (-f $jats);
296
        $logger->err("ReleaseNote program not found: $releaseNotes") unless (-f $releaseNotes);
297
 
298
        #
299
        #   Execute the command and grab the output for logging purposes
300
        #   
301
        my $rnCmd = "$jats -abt=1 eprog $releaseNotes $opts";
302
        my $ph;
303
        open ($ph, "$rnCmd |");
304
        while ( <$ph> )
305
        {
306
            chomp;
307
            # Detect a package being processed
308
            if (m~\(M\)\s+---~) {
309
                $logger->logmsg($_);
310
                $statistics{txCount}++;
311
            }
312
            if (m~\]\s\(E\)\s~) {
313
                $logger->logmsg($_);
314
            }
315
            $logger->verbose2("PRN:Data: $_");
316
        }
317
        close ($ph);
318
        my $cmdRv = $?;
319
        $logger->verbose("PRN:End: $cmdRv");
320
        $logger->warn("ReleaseNote return Code: $cmdRv") if $cmdRv;
321
        $statistics{linkErrors}++ if $cmdRv;
322
        $linkState = ($cmdRv eq 0);
323
 
324
    }
325
}
326
 
327
#-------------------------------------------------------------------------------
328
# Function        : resetDailyStatistics 
329
#
330
# Description     : Called periodically to reset the daily statistics
331
#
332
# Inputs          : $time       - Current time
333
#
334
# Returns         : 
335
#
336
sub resetDailyStatistics
337
{
338
    my ($time) = @_;
339
 
340
    #
341
    #   Detect a new day
342
    #
343
    my $today = (localtime($time))[7];
344
    if ($yday != $today)
345
    {
346
        $yday = $today;
347
        $logger->logmsg('Resetting daily statistics' );
348
 
349
        # Note: Must match @recoverTags in readStatistics
350
        $statistics{dayStart} = $time;
351
        $statistics{txCount} = 0;
352
        $statistics{linkErrors} = 0;
353
    }
354
}
355
 
356
#-------------------------------------------------------------------------------
357
# Function        : readStatistics 
358
#
359
# Description     : Read in the last set of stats
360
#                   Used after a restart to recover daily statistics
361
#
362
# Inputs          : 
363
#
364
# Returns         : 
365
#
366
sub readStatistics
367
{
368
    my @recoverTags = qw(dayStart txCount linkErrors);
369
 
370
    if ($conf->{'statsfile'} and -f $conf->{'statsfile'})
371
    {
372
        if (open my $fh, $conf->{'statsfile'})
373
        {
374
            while (<$fh>)
375
            {
376
                m~(.*):(.*)~;
377
                if ( grep( /^$1$/, @recoverTags ) ) 
378
                {
379
                    $statistics{$1} = $2;
380
                    $logger->verbose("readStatistics $1, $2");
381
                }
382
            }
383
            close $fh;
384
            $yday = (localtime($statistics{dayStart}))[7];
385
        }
386
    }
387
}
388
 
389
 
390
#-------------------------------------------------------------------------------
391
# Function        : periodicStatistics 
392
#
393
# Description     : Called on a regular basis to write out statistics
394
#                   Used to feed information into Nagios
395
#                   
396
#                   This function is called via an alarm and may be outside the normal
397
#                   processing loop. Don't make assumptions on the value of $now
398
#
399
# Inputs          : 
400
#
401
# Returns         : 
402
#
403
sub periodicStatistics
404
{
405
    #
406
    #   A few local stats
407
    #
408
    $statistics{SeqNum}++;
409
    $statistics{timeStamp} = time();
410
    $statistics{upTime} = $statistics{timeStamp} - $startTime;
411
    $statistics{wedged} = $wedgedCount++ > 30  ? 1 : 0;
412
 
413
    if ( $statistics{wedged}) {
414
         $statistics{state} = 'Wedged';
415
    } elsif(!$linkState){
416
        $statistics{state} = 'ReleaseNote generation Error';
417
    } else {
418
        $statistics{state} = 'OK';
419
    }
420
 
421
    #   Reset daily accumulations - on first use each day
422
    resetDailyStatistics($statistics{timeStamp});
423
 
424
    #
425
    #   Write statistics to a file
426
    #       Write to a tmp file, then rename.
427
    #       Attempt to make the operation atomic - so that the file consumer
428
    #       doesn't get a badly formed file.
429
    #   
430
    if ($conf->{'statsfiletmp'})
431
    {
432
        my $fh;
433
        unless (open ($fh, '>', $conf->{'statsfiletmp'}))
434
        {
435
            $fh = undef;
436
            $logger->warn("Cannot create temp stats file: $!");
437
        }
438
        else
439
        {
440
            foreach my $key ( sort { lc($a) cmp lc($b) } keys %statistics)
441
            {
442
                print $fh $key . ':' . $statistics{$key} . "\n";
443
                $logger->verbose2('Statistics:'. $key . ':' . $statistics{$key});
444
            }
445
            close $fh;
446
 
447
            # Rename temp to real file
448
            rename  $conf->{'statsfiletmp'},  $conf->{'statsfile'} ;
449
        }
450
    }
451
}
452
 
453
#-------------------------------------------------------------------------------
454
# Function        : sighandlers
455
#
456
# Description     : Install signal handlers
457
#
458
# Inputs          : Nothing
459
#
460
# Returns         : Nothing
461
#
462
sub sighandlers
463
{
464
    $SIG{TERM} = sub {
465
        # On shutdown
466
        $logger->logmsg('Received SIGTERM. Shutting down....' );
467
        unlink $conf->{'pidfile'} if (-f $conf->{'pidfile'});
468
        exit 0;
469
    };
470
 
471
    $SIG{HUP} = sub {
472
        # On logrotate
473
        $logger->logmsg('Received SIGHUP.');
474
        $logger->rotatelog();
475
    };
476
 
477
    $SIG{USR1} = sub {
478
        # On Force - nothing yet
479
        $logger->logmsg('Received SIGUSR1.');
480
    };
481
 
482
    alarm 60;
483
    $SIG{ALRM} = sub {
484
        # On Dump Statistics
485
        $logger->verbose2('Received SIGUSR2.');
486
        periodicStatistics();
487
        alarm 60;
488
    };
489
 
490
    $SIG{__WARN__} = sub { $logger->warn("@_") };
491
    $SIG{__DIE__} = sub { $logger->err("@_") };
492
}
493
 
494
 
495
#-------------------------------------------------------------------------------
496
# Function        : Error, Verbose, Warning
497
#
498
# Description     : Support for JatsRmApi
499
#
500
# Inputs          : Message
501
#
502
# Returns         : Nothing
503
#
504
sub Error
505
{
506
    $logger->err("@_");
507
}
508
 
509
sub Verbose
510
{
511
    $logger->verbose2("@_");
512
}
513
 
514
sub Warning
515
{
516
    $logger->warn("@_");
517
}
518
 
519