Subversion Repositories DevTools

Rev

Rev 7423 | Rev 7469 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1038 dpurdie 1
#! /usr/bin/perl
2
########################################################################
7397 dpurdie 3
# COPYRIGHT - VIX IP PTY LTD ("VIX"). ALL RIGHTS RESERVED.
1038 dpurdie 4
#
5
# Module name   : blatDaemon.pl
6
# Module type   :
7
# Compiler(s)   : Perl
8
# Environment(s):
9
#
10
# Description   :
11
#
12
# Usage         :   ARGV[0] - Path to config file for this instance
13
#
14
#......................................................................#
15
 
16
require 5.008_002;
17
use strict;
18
use warnings;
19
use Getopt::Long;
20
use File::Basename;
1040 dpurdie 21
use Data::Dumper;
22
use File::Spec::Functions;
1038 dpurdie 23
use POSIX ":sys_wait_h";
24
use File::Temp qw/tempfile/;
6320 dpurdie 25
use Digest::MD5;
1038 dpurdie 26
 
27
use FindBin;                                    # Determine the current directory
28
use lib "$FindBin::Bin/lib";                    # Allow local libraries
29
 
30
use Utils;
31
use StdLogger;                                  # Log to sdtout
32
use Logger;                                     # Log to file
33
 
34
#
35
#   Database interface
36
#   Pinched from jats and modified so that this software is not dependent on JATS
37
#
38
use IO::Handle;
39
use JatsRmApi;
40
use DBI;
41
 
42
#
43
#   Globals
44
#
45
my $logger = StdLogger->new();                  # Stdout logger. Only during config
6475 dpurdie 46
$logger->err("No config file specified") unless (defined $ARGV[0]);
7387 dpurdie 47
$logger->err("Config File does not exist: $ARGV[0]") unless (-f $ARGV[0]);
1038 dpurdie 48
my $name = basename( $ARGV[0]);
49
   $name =~ s~.conf$~~;
50
my $now = 0;
5398 dpurdie 51
my $startTime = 0;
3515 dpurdie 52
my $tar = 'tar';
53
my $gzip = 'gzip';
1038 dpurdie 54
my $tagDirTime = 0;
55
my $lastDirScan = 0;
56
my $lastReleaseScan = 0;
1040 dpurdie 57
my $releaseScanMode = 0;
6779 dpurdie 58
my $lastTagListUpdate = 0;
6776 dpurdie 59
my $lastRmConfRead = 0;
60
my $lastRmConfFullRead = 0;
61
my $lastRmSeqNum = 0;
1038 dpurdie 62
my $mtimeConfig = 0;
63
my $conf;
64
my $extraPkgs;
1040 dpurdie 65
my $excludePkgs;
66
my %releaseData;
3515 dpurdie 67
my $comError = 0;
5398 dpurdie 68
my $yday = -1;
6148 dpurdie 69
my $RemotePkgList = {};
6320 dpurdie 70
my $targetBinDir = "$FindBin::Bin/targetBin";
6776 dpurdie 71
my $server_id;
72
my @projectList;
73
my @releaseList;
7387 dpurdie 74
my $isS3Target;
1038 dpurdie 75
 
76
#
5398 dpurdie 77
#   Contain statisics maintained while operating
78
#       Can be dumped with a kill -USR2
79
#       List here for documentation
80
#  
81
 
82
my %statistics = (
83
    SeqNum => 0,                        # Bumped when $statistics are dumped
84
    timeStamp => 0,                     # DateTime when statistics are dumped
85
    upTime => 0,                        # Seconds since program start
86
    Cycle => 0,                         # Major process loop counter
87
    phase => 'Init',                    # Current phase of operation
88
    state => 'OK',                      # Nagios state
7397 dpurdie 89
    wedged => 0,                        # Wedge indication - main loop not cycling
5398 dpurdie 90
                                        # 
91
                                        # The following are reset each day
92
    dayStart => 0,                      # DateTime when daily data was reset
93
    txCount => 0,                       # Packages Transferred
94
    delCount => 0,                      # Packages marked for deletion
95
    staleTags => 0,                     # Stale Tags
96
    linkErrors => 0,                    # Transfer errors
97
                                        # 
98
                                        # Per Cycle Data - Calculated each processing Cycle
5399 dpurdie 99
    total    => 0,                      # Packages to be synced
5398 dpurdie 100
    delete   => 0,                      # Packages to delete
101
    excluded => 0,                      # Packages excluded    
102
    filtered => 0,                      # Packages filtered out
103
    missing  => 0,                      # Packages missing
104
    transfer => 0,                      # Packages to transfer
105
    writable => 0,                      # Packages still writable - thus not transferred
106
    tagCount => 0,                      # Packages tagged to be transferred
7394 dpurdie 107
    tagDelCount => 0,                   # Packages tagged to be deleted
5404 dpurdie 108
                                        #
109
                                        # Expected from the Target 
110
#   Target.Hostname => '',              # Target Hostname
111
#   Target.avail    => 0,               # Information from 'df' 1Kblocks 
112
#   Target.pcent    => 0,
113
#   Target.size     => 0,
114
#   Target.used     => 0,
115
#   Target.iavail   => 0,               # Inode information from 'df'
116
#   Target.ipcent   => 0,
117
#   Target.isize    => 0,
118
#   Target.iused    => 0,
119
#   Target.Total    => 0,               # Number of Package Versions in the archive
120
#   Target.Damaged  => 0,               # Number that are damaged
121
#   Target.Delete   => 0,               # Number marked for future deletion
122
#   Target.Missing  => 0,               # Number missing
5398 dpurdie 123
);
124
 
1038 dpurdie 125
#
5398 dpurdie 126
#   Describe configuration parameters
127
#
1038 dpurdie 128
my %cdata = (
129
    '.ignore'         => {'pkg\.(.+)' => 'pkgs' },
7387 dpurdie 130
    '.oneOf'          => [['hostname','S3Bucket']] ,
131
 
1048 dpurdie 132
    'piddir'          => {'mandatory' => 1      , 'fmt' => 'dir'},
133
    'sleep'           => {'default'   => 5      , 'fmt' => 'period'},
134
    'dpkg_archive'    => {'mandatory' => 1      , 'fmt' => 'dir'},
135
    'logfile'         => {'mandatory' => 1      , 'fmt' => 'vfile'},
136
    'logfile.size'    => {'default'   => '1M'   , 'fmt' => 'size'},
137
    'logfile.count'   => {'default'   => 9      , 'fmt' => 'int'},
138
    'verbose'         => {'default'   => 0      , 'fmt' => 'int'},
6776 dpurdie 139
    'rmHostName'      => {'default'   => undef  , 'fmt' => 'text'},
3847 dpurdie 140
    'sshport'         => {'default'   => 0      , 'fmt' => 'int'},
7397 dpurdie 141
    'tagdir'          => {'mandatory' => 1      , 'fmt' => 'mkdir'},
1048 dpurdie 142
    'forcedirscan'    => {'default'   => 100    , 'fmt' => 'period'},
143
    'tagage'          => {'default'   => '10m'  , 'fmt' => 'period'},
144
    'tagListUpdate'   => {'default'   => '1h'   , 'fmt' => 'period'},
7387 dpurdie 145
    'tagMaxPackages'  => {'default'   => 10     , 'fmt' => 'int'},
6776 dpurdie 146
    'rmConfigCheck'   => {'default'   => '60'   , 'fmt' => 'period'},
147
    'rmConfFullRead'  => {'default'   => '1h'   , 'fmt' => 'period'},
1048 dpurdie 148
    'synctime'        => {'default'   => '2h'   , 'fmt' => 'period'},
149
    'syncretry'       => {'default'   => '5m'   , 'fmt' => 'period'},
150
    'allProjects'     => {'default'   => 0      , 'fmt' => 'bool'},
4456 dpurdie 151
    'allArchive'      => {'default'   => 0      , 'fmt' => 'bool'},
1048 dpurdie 152
    'project'         => {'mandatory' => 0      , 'fmt' => 'int_list'},
153
    'release'         => {'mandatory' => 0      , 'fmt' => 'int_list'},
154
    'writewindow'     => {'default'   => '3h'   , 'fmt' => 'period'},
155
    'maxpackages'     => {'default'   => 5      , 'fmt' => 'int'},
156
    'deletePackages'  => {'default'   => 0      , 'fmt' => 'bool'},
157
    'deleteImmediate' => {'default'   => 0      , 'fmt' => 'bool'},
158
    'deleteAge'       => {'default'   => 0      , 'fmt' => 'period'},
159
    'packageFilter'   => {'default'   => undef  , 'fmt' => 'text'},
160
    'active'          => {'default'   => 1      , 'fmt' => 'bool'},
6475 dpurdie 161
    'debug'           => {'default'   => 0      , 'fmt' => 'bool'},                 # Log to screen
162
    'txdetail'        => {'default'   => 0      , 'fmt' => 'bool'},
6776 dpurdie 163
    'noTransfers'     => {'default'   => 0      , 'fmt' => 'bool'},                 # Debugging option to prevent transfers
7394 dpurdie 164
    'maxTarZips'      => {'default'   => 5      , 'fmt' => 'int'},
7460 dpurdie 165
    'wedgeTime'       => {'default'   => '30m'  , 'fmt' => 'period'},
7387 dpurdie 166
 
167
    #
168
    #   Transfer via ssh
169
    #   Cannot be used in conjunction with S3Bucket
170
    #    
7460 dpurdie 171
    'hostname'        => {'fmt' => 'text', 'requires' => 'user,identity,bindir'},
172
    'user'            => {'fmt' => 'text', 'requires' => 'hostname'},
7387 dpurdie 173
    'identity'        => {'fmt' => 'file'},
174
    'bindir'          => {'fmt' => 'text'},
175
 
176
    #
177
    #   Transfer to S3 configuration items
178
    #       Cannot be used in conjunction with hostname
179
    #       Many other options will be ignored 
180
    #
7460 dpurdie 181
    'S3Bucket'        => {'fmt' => 'text', 'requires' => 'S3Profile,S3Profile,S3Region'},
182
    'S3Profile'       => {'fmt' => 'text', 'requires' => 'S3Bucket' },
183
    'S3Region'        => {'fmt' => 'text', 'requires' => 'S3Bucket' },
184
    'S3AllowDelete'   => {'default' => 0 , 'fmt' => 'bool'},
1038 dpurdie 185
);
186
 
187
 
188
#
189
#   Read in the configuration
190
#       Set up a logger
191
#       Write a pidfile - thats not used
6776 dpurdie 192
$now = $startTime = time();
1038 dpurdie 193
readConfig();
194
Utils::writepid($conf);
195
$logger->logmsg("Starting...");
5404 dpurdie 196
readStatistics();
7423 dpurdie 197
sighandlers();
1038 dpurdie 198
 
199
#
200
#   Main processing loop
201
#   Will exit when terminated by parent
202
#
6779 dpurdie 203
while (1)
1038 dpurdie 204
{
205
    $logger->verbose3("Processing");
5398 dpurdie 206
    $statistics{Cycle}++;
1038 dpurdie 207
    $now = time();
7460 dpurdie 208
    Utils::resetWedge();
1040 dpurdie 209
 
5398 dpurdie 210
    $statistics{phase} = 'ReadConfig';
1038 dpurdie 211
    readConfig();
1048 dpurdie 212
    if ( $conf->{'active'} )
213
    {
5398 dpurdie 214
        $statistics{phase} = 'ProcessReleaseList';
1048 dpurdie 215
        processReleaseList();
5398 dpurdie 216
        $statistics{phase} = 'processTags';
1048 dpurdie 217
        processTags();
5398 dpurdie 218
        $statistics{phase} = 'maintainTagList';
1048 dpurdie 219
        maintainTagList();
220
    }
1040 dpurdie 221
    %releaseData = ();
222
 
5398 dpurdie 223
    $statistics{phase} = 'Sleep';
1038 dpurdie 224
    sleep( $conf->{'sleep'} );
6779 dpurdie 225
    reapChildren();
1050 dpurdie 226
 
3847 dpurdie 227
    #   If my PID file ceases to be, then exit the daemon
228
    #   Used to force daemon to restart
229
    #
230
    unless ( -f $conf->{'pidfile'} )
231
    {
232
        $logger->logmsg("Terminate. Pid file removed");
233
        last;
234
    }
1038 dpurdie 235
}
5398 dpurdie 236
$statistics{phase} = 'Terminated';
1038 dpurdie 237
$logger->logmsg("Child End");
238
exit 0;
239
 
240
#-------------------------------------------------------------------------------
6779 dpurdie 241
# Function        : reapChildren 
242
#
243
# Description     : Reap any and all dead children
244
#                   Call in major loops to prevent zombies accumulating 
245
#
246
# Inputs          : None
247
#
248
# Returns         : 
249
#
250
sub reapChildren
251
{
252
    my $currentPhase = $statistics{phase};
253
    $statistics{phase} = 'Reaping';
254
 
255
    my $kid;
256
    do {
257
        $kid = waitpid(-1, WNOHANG);
258
    } while ( $kid > 0 );
259
 
260
    $statistics{phase} = $currentPhase;
261
}
262
 
263
 
264
#-------------------------------------------------------------------------------
1038 dpurdie 265
# Function        : readConfig
266
#
267
# Description     : Re read the config file if it modification time has changed
268
#
269
# Inputs          : Nothing
270
#
1289 dpurdie 271
# Returns         : 0       - Config not read
272
#                   1       - Config read
273
#                             Config file has changed
1038 dpurdie 274
#
275
sub readConfig
276
{
277
    my ($mtime) = Utils::mtime($ARGV[0]);
1289 dpurdie 278
    my $rv = 0;
279
 
1038 dpurdie 280
    if ( $mtimeConfig != $mtime )
281
    {
282
        $logger->logmsg("Reading config file: $ARGV[0]");
283
        $mtimeConfig = $mtime;
284
        my $errors;
285
        ($conf, $errors) = Utils::readconf ( $ARGV[0], \%cdata );
286
        if ( scalar @{$errors} > 0 )
287
        {
288
            warn "$_\n" foreach (@{$errors});
289
            die ("Config contained errors\n");
290
        }
7387 dpurdie 291
        $isS3Target = defined $conf->{'S3Bucket'};
7460 dpurdie 292
        if ($isS3Target) {
293
            if (!$conf->{'S3AllowDelete'}) {
294
                    $conf->{deletePackages} = 0;
295
            }
296
        }
1038 dpurdie 297
 
298
        #
299
        #   Reset some information
300
        #   Create a new logger
301
        #
7387 dpurdie 302
        $logger = Logger->new($conf) unless $conf->{debug};
1038 dpurdie 303
        $conf->{logger} = $logger;
304
        $conf->{'pidfile'} = $conf->{'piddir'} . '/' . $name . '.pid';
7387 dpurdie 305
        $logger->setVerbose($conf->{verbose});
1038 dpurdie 306
        $logger->verbose("Log Levl: $conf->{verbose}");
307
 
308
        #
5398 dpurdie 309
        #   Setup statistics filename
310
        $conf->{'statsfile'} = $conf->{'piddir'} . '/' . $name . '.stats';
311
        $conf->{'statsfiletmp'} = $conf->{'piddir'} . '/' . $name . '.stats.tmp';
312
 
313
        #
1038 dpurdie 314
        #   Extract extra package config
6475 dpurdie 315
        #       Ignore ALL and Version info if transferring the entire archive
316
        #       Honor the EXCLUDE - for bandwidth reasons
7387 dpurdie 317
        #       
318
        #   NOTE: Package inclusion will not be processed in allArchive mode
319
        #   BUG:  This processing should be done after the RmConfig has been included
1038 dpurdie 320
        #
321
        $extraPkgs = {};
1040 dpurdie 322
        $excludePkgs = {};
1038 dpurdie 323
        while (my($key, $data) = each ( %{$conf->{pkgs}} ))
324
        {
3846 dpurdie 325
            if ( $data eq 'EXCLUDE' ) {
1040 dpurdie 326
                $excludePkgs->{$key} = 1;
327
                $logger->verbose("Exclude Pkg: $key");
3846 dpurdie 328
 
329
            } elsif ( $data eq 'ALL' ) {
6475 dpurdie 330
                next if ( $conf->{'allArchive'} );
3846 dpurdie 331
                foreach my $pver (getPackageVersions($key))
332
                {
333
                    $extraPkgs->{$key}{$pver} = 1;
334
                    $logger->verbose("Extra Pkg: $key -> $pver");
335
                }
336
            } else {
6475 dpurdie 337
                next if ( $conf->{'allArchive'} );
338
                foreach (split(/[,\s]+/, $data))
339
                {
340
                    $extraPkgs->{$key}{$_} = 1;
341
                    $logger->verbose("Extra Pkg: $key -> $_");
342
                }
1040 dpurdie 343
            }
1038 dpurdie 344
        }
1046 dpurdie 345
 
1048 dpurdie 346
        $logger->verbose("Filter Packages: " . $conf->{'packageFilter'})
347
            if ( defined $conf->{'packageFilter'} );
348
 
3847 dpurdie 349
        $logger->warn("Non standard ssh port: " . $conf->{'sshport'})
350
            if ( $conf->{'sshport'} );
1048 dpurdie 351
 
6776 dpurdie 352
        #
353
        #   Save Text based config for use in RmConfig
354
        #
355
        $conf->{'BaseActive'} = $conf->{'active'};
1048 dpurdie 356
 
6776 dpurdie 357
        #
358
        #   Flag config has changed / been read
359
        #   Force full RM data fetch
360
        #
361
        $rv = 1;
362
        $lastRmSeqNum = 0;
363
        $lastRmConfRead = 0;
364
    }
4456 dpurdie 365
 
6776 dpurdie 366
    #
367
    #   Read the Release Manager configuration too
368
    #
369
    $rv |= ReadRmConfig();
370
 
371
    #
372
    #   When config is read force some actions
373
    #       - Force tagList to be created
374
    #       - Force release scan
375
    if ($rv) {
6779 dpurdie 376
        $lastTagListUpdate = 0;
1050 dpurdie 377
        $lastReleaseScan = 0;
6776 dpurdie 378
 
379
        #
380
        #   Update global Project/Release list - only on change
381
        @projectList = split /[,\s]+/, $conf->{'project'} || '';
382
        @releaseList = split /[,\s]+/, $conf->{'release'} || '';
383
 
384
        $logger->logmsg("projectList: ". join(',',@projectList));
385
        $logger->logmsg("releaseList: ". join(',',@releaseList));
386
 
387
#Utils::DebugDumpData ("Config", $conf);
388
 
389
        $logger->warn("Transfer session configured as not active") unless ( $conf->{'active'} );
390
        $logger->warn("Transfer all project packages") if ( $conf->{'allProjects'} );
391
        $logger->warn("Transfer entire package archive") if ( $conf->{'allArchive'} );
392
        $logger->warn("All Transfers disabled") if ( $conf->{'noTransfers'} );
7387 dpurdie 393
        $logger->warn("Transfer to AWS S3 Bucket") if ( $isS3Target );
6776 dpurdie 394
    }
395
 
396
    return $rv;
397
}
398
 
399
#-------------------------------------------------------------------------------
400
# Function        : ReadRmConfig 
401
#
402
# Description     : Read Configuration information from Release Manager
403
#                   If Rm configuration is to be used then it will override
404
#                   the project/release configuration in the text file
405
#                    
406
#
407
# Inputs          : Nothing
408
#
409
# Returns         : 0       - Config not read
410
#                   1       - Config read
411
#                             Config file has changed
412
#
413
sub ReadRmConfig
414
{
415
    #
416
    #   Time to perform a database read
417
    #   Will do at startup and every time period there after
418
    #
419
    return 0 unless $conf->{'rmHostName'};
420
    return 0 unless $conf->{'BaseActive'};
421
    return 0 unless ( $now > ($lastRmConfRead + $conf->{rmConfigCheck} ));
422
    $logger->verbose("ReadRmConfig");
423
    $lastRmConfRead = $now;
424
 
425
    my $rv = 0;
426
    my $RM_DB;
427
    my ($blat_seqnum, $blat_mode, $found);
428
    my $server_enabled = 1;
429
 
430
    #
431
    #   Read the BLAT_SERVER record from Release Manager
432
    #
433
    my $m_sqlstr = "select blat_id, blat_enable, blat_seqnum, blat_mode from RELEASE_MANAGER.blat_servers WHERE UPPER(BLAT_SERVER_NAME) = UPPER('$conf->{'rmHostName'}')";
434
 
435
    connectRM(\$RM_DB);
436
    my $sth = $RM_DB->prepare($m_sqlstr);
437
    if ( defined($sth) )
438
    {
439
        if ( $sth->execute( ) )
440
        {
441
            if ( $sth->rows )
442
            {
443
                while (my @row = $sth->fetchrow_array )
444
                {
445
                    $logger->verbose2("ReadRmConfig:Data:@row");
446
 
447
                    $server_id = $row[0] || 0;
448
                    $server_enabled = ($row[1] || 'N') eq 'Y'; 
449
                    $blat_seqnum = $row[2] || 0;
450
                    $blat_mode = ($row[3] || 'N');
451
                    $found = 1;
452
                    last;
453
                }
454
            }
455
            $sth->finish();
456
        }
457
        else
458
        {
459
            $logger->warn("ReadRmConfig: SQL Execute failure");
460
        }
461
    }
462
    else
463
    {
464
        $logger->warn("ReadRmConfig: SQL Prepare failure");
465
    }
466
    disconnectRM(\$RM_DB);
467
 
468
    #
469
    #   Process the extracted data
470
    #   
471
    if ( !$found) {
472
        $logger->warn("No Release Manager configuration for:" . $conf->{'rmHostName'});
473
        $server_id = 0;
474
        $lastRmSeqNum = 0;
475
        return 0;
476
    }
477
 
478
    #       Check for a change in config
479
    #
480
    if ($lastRmSeqNum != $blat_seqnum) {
1289 dpurdie 481
        $rv = 1;
6776 dpurdie 482
        $lastRmSeqNum = $blat_seqnum;
1038 dpurdie 483
    }
6776 dpurdie 484
 
485
    #
486
    #   Insert server configuration into the global config table
487
    #   Rm Config will override the text config
488
    #
489
    $conf->{'active'} = $server_enabled;
490
    $conf->{'allProjects'} = $blat_mode eq 'P';
491
    $conf->{'allArchive'} =  $blat_mode eq 'E';
492
 
493
    #
494
    #   If the config has changed, then read the Project and Release records from the database
495
    #   Insert these into the config provided by the text file confing
496
    #
497
    my $forceFullRead;
498
    if ( $now > ($lastRmConfFullRead + $conf->{rmConfFullRead} )) {
499
        $logger->verbose("ForceReadRmConfig");
500
        $lastRmConfFullRead = $now;
501
        $forceFullRead = 1;
502
    }
503
 
504
    if ($rv || $forceFullRead) {
505
        connectRM(\$RM_DB);
506
        my @projects;
507
        my @releases;
508
 
509
        my $m_sqlstr = "select proj_id as proj_id, null as rtag_id from release_manager.blat_projects where blat_id = $server_id and UPPER(bp_enabled) = 'Y'" .
510
                       " UNION " .
511
                       "select null as proj_id, rtag_id rtag_id from release_manager.blat_releases where blat_id = $server_id and UPPER(br_enabled) = 'Y'";
512
 
513
        my $sth = $RM_DB->prepare($m_sqlstr);
514
        if ( defined($sth) )
515
        {
516
            if ( $sth->execute( ) )
517
            {
518
                if ( $sth->rows )
519
                {
520
                    while (my @row = $sth->fetchrow_array )
521
                    {
522
                        $logger->verbose2("ReadRmConfig:Data:@row");
523
                        push (@projects, $row[0]) if ($row[0]);
524
                        push (@releases, $row[1]) if ($row[1] );
525
                    }
526
                }
527
                $sth->finish();
528
            }
529
            else
530
            {
531
                $logger->warn("ReadRmConfig: SQL Execute failure");
532
            }
533
        }
534
        else
535
        {
536
            $logger->warn("ReadRmConfig: SQL Prepare failure");
537
        }
538
        disconnectRM(\$RM_DB);
539
 
540
        #
541
        #   Reform the projects and releases into a comma separated list and detect changes
542
        #
543
        my $projects = join(',', sort @projects); 
544
        my $releases = join(',', sort @releases);
545
 
546
        if ((($projects || '') ne ($conf->{'project'} || '')) || (($releases || '') ne  ($conf->{'release'} || ''))) {
547
            $conf->{'project'} = $projects;
548
            $conf->{'release'} = $releases;
549
            $rv = 1;
550
        }
551
    }
552
 
553
    # Indicate if the config has changed
1289 dpurdie 554
    return $rv;
1038 dpurdie 555
}
556
 
6475 dpurdie 557
#-------------------------------------------------------------------------------
558
# Function        : checkForBasicTools 
559
#
560
# Description     : Check that the target has the basic tools are installed
561
#                   Can populate the target's bin directory with tools
562
#
563
# Inputs          : None 
564
#
565
# Returns         : Nothing
566
#
567
sub checkForBasicTools
568
{
7387 dpurdie 569
    return if $isS3Target;
570
 
6475 dpurdie 571
    my $ph;
572
    my $found;
573
    my $tgt_cmd = "if [ -x  $conf->{'bindir'}/get_plist.pl ] ; then echo :FOUND:; fi";
574
    my $ssh_cmd = sshCmd($tgt_cmd);
1038 dpurdie 575
 
6475 dpurdie 576
    $logger->verbose2("checkForBasicTools:ssh_cmd:$ssh_cmd");
577
    open ($ph, "$ssh_cmd |");
578
    while ( <$ph> )
579
    {
580
        chomp;
581
        if (m~:FOUND:~) {
582
            $found = 1;
583
        }
584
        $logger->verbose2("checkForBasicTools:Data: $_");
585
    }
586
    close ($ph);
587
    my $exitCode = $? >> 8;
588
    $logger->verbose2("checkForBasicTools:End: $exitCode, $?");
589
 
590
    unless ( $found )
591
    {
592
        $logger->warn("checkForBasicTools: None found, $?");
593
 
594
        #
595
        #   The 'get_plist.pl' program was not found
596
        #   Assume that the entire directory does not exist and transfer all
597
        #
598
        transferTargetBin();
599
    }
600
}
601
 
1038 dpurdie 602
#-------------------------------------------------------------------------------
6475 dpurdie 603
# Function        : transferTargetBin 
604
#
605
# Description     : Ensure that the targets 'bin' folder is upto date 
606
#
607
# Inputs          : $blatBinData    - Ref to array of target data file info
608
#
609
# Returns         : 
610
#
611
sub transferTargetBin
612
{
7389 dpurdie 613
    return if $isS3Target;
6475 dpurdie 614
    my ($blatBinData) = @_;
615
 
616
    my $blatBinList = getBlatBin();
617
    foreach my $file ( keys %{$blatBinList} )
618
    {
619
        if (defined $blatBinData && exists $blatBinData->{$file}) {
620
            if ($blatBinData->{$file} eq $blatBinList->{$file}) {
621
                delete $blatBinList->{$file};
622
            }
623
        }
624
    }
625
#Utils::DebugDumpData ("blatBinList", $blatBinList);
626
    transferBlatBin($blatBinList);
627
}
628
 
629
#-------------------------------------------------------------------------------
1038 dpurdie 630
# Function        : processReleaseList
631
#
632
# Description     : Process the release list
633
#                       Determine if its time to process release list
634
#                       Determine release list
635
#                       Determine release content
636
#                       Determine new items
637
#
638
# Inputs          : None
639
#
640
# Returns         : Nothing
641
#
642
sub processReleaseList
643
{
644
    #
1044 dpurdie 645
    #   Is Release List Processing active
646
    #   Can configure blat to disable release sync
647
    #   This will then allow 'new' packages to be sent
648
    #
7389 dpurdie 649
    if ( $conf->{maxpackages} == 0 || $conf->{'synctime'} <= 0 )
1044 dpurdie 650
    {
651
        $logger->verbose2("processReleaseList disabled");
7387 dpurdie 652
        $RemotePkgList = {};
1044 dpurdie 653
        return;
654
    }
655
 
656
    #
1038 dpurdie 657
    #   Time to perform the scan
658
    #   Will do at startup and every time period there after
659
    #
1040 dpurdie 660
    my $wtime = $releaseScanMode ? $conf->{'syncretry'} : $conf->{'synctime'};
661
    return unless ( $now > ($lastReleaseScan + $wtime ));
1038 dpurdie 662
    $logger->verbose("processReleaseList");
663
    $lastReleaseScan = $now;
1040 dpurdie 664
    $releaseScanMode = 1;                                   # Assume error
1038 dpurdie 665
 
666
    #
6772 dpurdie 667
    #   Ensure that we have the basic tools for the transfer
668
    #
669
    checkForBasicTools();
670
 
671
    #
1038 dpurdie 672
    #   Get list of packages from Remote site
673
    #
674
    my $remotePkgList;
5404 dpurdie 675
    my $remoteData;
6320 dpurdie 676
    my $blatBinData;
1038 dpurdie 677
 
7389 dpurdie 678
    #
679
    #   Get Data from an S3 bucket
680
    #   Can only get a part of the full data set. The timestamp can't be processed, so set it to -1
681
    #   
682
    if ($isS3Target ) {
683
        my $ph;
7460 dpurdie 684
        my $tgt_cmd = "aws --profile $conf->{'S3Profile'}";
685
        $tgt_cmd .= " --region $conf->{'S3Region'}" if (defined $conf->{'S3Region'});
686
        $tgt_cmd .= " s3 ls $conf->{'S3Bucket'}/";
7389 dpurdie 687
 
688
        $logger->verbose2("processReleaseList:s3_cmd:$tgt_cmd");
689
        open ($ph, "$tgt_cmd |");
690
        while ( <$ph> )
1038 dpurdie 691
        {
7389 dpurdie 692
            chomp;
7460 dpurdie 693
            if (m~.*\s(.*)__(.*).tgz$~ ) {
694
                $remotePkgList->{$1}{$2}{s3} = 1;
695
            }
1038 dpurdie 696
        }
7389 dpurdie 697
        close ($ph);
698
 
699
    } else {
700
        # Get Data from a dpkg_archive maintained via ssh
701
        #   Invoke a program on the remote site and parse the results
702
        #
703
        #   Returned data looks like:
704
        #       Metadata avail="140100452"
705
        #       BlatBin MD5="9e2c6e45af600a20a01dbcb7570da1f1" file="stat.pl"
706
        #       time="1497586865" GMT="Fri Jun 16 04:21:05 2017" pname="ERGissaccounts" pver="1.0.7169.mas"
707
        #       time="1497586865" GMT="Fri Jun 16 04:21:05 2017" pname="ERGissaccounts" pver="1.0.7169.mas" "link=latest"
708
        #       time="1497954104" GMT="Tue Jun 20 10:21:44 2017" pname="ERGissaccounts" pver="1.0.7178.mas" deleted="0"
709
 
710
        my $ph;
711
        my $tgt_cmd = "$conf->{'bindir'}/get_plist.pl";
712
        my $ssh_cmd = sshCmd($tgt_cmd);
713
 
714
        $logger->verbose2("processReleaseList:ssh_cmd:$ssh_cmd");
715
        open ($ph, "$ssh_cmd |");
716
        while ( <$ph> )
6320 dpurdie 717
        {
7389 dpurdie 718
            chomp;
719
            if ($_ =~ m~^Metadata\s+~)
5404 dpurdie 720
            {
7389 dpurdie 721
                parsePkgMetaData($_, \%{$remoteData});
5404 dpurdie 722
            }
7389 dpurdie 723
            elsif ($_ =~ m~^BlatBin\s+~)
724
            {
725
                parseBlatBinData($_, \%{$blatBinData})
726
            }
5404 dpurdie 727
            else
728
            {
7389 dpurdie 729
                if ( parsePkgList($_, \%{$remotePkgList} ) )
730
                {
731
                    $logger->verbose2("processReleaseList:Data: $_");
732
                }
733
                else
734
                {
735
                    $logger->warn("processReleaseList:Bad Data: $_");
736
                }
5404 dpurdie 737
            }
1038 dpurdie 738
        }
7389 dpurdie 739
        close ($ph);
1038 dpurdie 740
    }
741
    $logger->verbose("processReleaseList:End: $?");
6148 dpurdie 742
    $RemotePkgList = $remotePkgList; 
743
 
3515 dpurdie 744
    LogTxError ($?);
1038 dpurdie 745
    if ( $? != 0 )
746
    {
747
        $logger->warn("Cannot retrieve package list: $?");
5398 dpurdie 748
        $statistics{state} = 'No Remote Package List';
1038 dpurdie 749
        return;
750
    }
1042 dpurdie 751
#Utils::DebugDumpData ("remotePkgList", $remotePkgList);
6320 dpurdie 752
 
1038 dpurdie 753
    #
6475 dpurdie 754
    #   Ensure that the target bin folder is up to date
755
    #
756
    transferTargetBin($blatBinData);
6320 dpurdie 757
 
758
    #
1038 dpurdie 759
    #   Determine the set of packages in the releases to be transferred
4456 dpurdie 760
    # 
761
    my $pkgList;
762
    if ( $conf->{'allArchive'} )
1040 dpurdie 763
    {
4456 dpurdie 764
        #   Examine entire archive
765
        #
766
        $pkgList = getArchiveList();
1040 dpurdie 767
    }
4456 dpurdie 768
    else
769
    {
770
        #   Examine Releases
771
        #
772
        my @rlist = getReleaseList();
773
        unless ( @rlist )
774
        {
775
            $logger->verbose2("No Releases to Process");
5398 dpurdie 776
            $statistics{state} = 'No Releases found';
6475 dpurdie 777
 
778
            #   Allow config with just specified packages
779
            #
780
            #   return;
781
        } else {
782
            $pkgList = getPkgList(@rlist);
4456 dpurdie 783
        }
784
    }
1038 dpurdie 785
 
786
    #
787
    #   Append extra packages
788
    #   These are packages that are specifically named by the user
789
    #
6475 dpurdie 790
    #   Note: If they are symbolic links, then the target of the
791
    #         link is also added.
1038 dpurdie 792
    #
793
    #         Symlink MUST be within the same directory
3846 dpurdie 794
    #           Used to transfer jats2_current
1038 dpurdie 795
    #
796
    while ( (my ($pname, $pvers)) = each %{$extraPkgs} ) {
797
        while ( (my ($pver, $pdata) ) = each %{$pvers} ) {
798
 
1040 dpurdie 799
            my $epath = catfile( $conf->{'dpkg_archive'} , $pname, $pver );
1038 dpurdie 800
            if ( -l $epath )
801
            {
802
                my $lver = readlink( $epath );
803
                if ( ! defined $lver )
804
                {
3846 dpurdie 805
                    $logger->warn("Can't resolve symlink: $pname, $pver");
1038 dpurdie 806
                    next;
807
                }
808
 
809
                if ( $lver =~ m ~/~ )
810
                {
811
                    $logger->warn("Won't resolve symlink: $pname, $pver, $lver");
812
                    next;
813
                }
6098 dpurdie 814
 
6475 dpurdie 815
                #
816
                #   Add the package the link points to
817
                #
818
                $logger->verbose2("Add linked package: $pname, $lver, $pdata");
819
                $pkgList->{$pname}{$lver} = $pdata;
7389 dpurdie 820
 
821
                if ($isS3Target) {
822
                    $logger->verbose2("Won't send symlink to S3: $pname, $pver, $lver");
823
                    next;
824
                }
825
 
1038 dpurdie 826
            }
827
 
828
            $logger->verbose2("Add extra package: $pname, $pver, $pdata");
829
            $pkgList->{$pname}{$pver} = $pdata;
830
        }
831
    }
6475 dpurdie 832
#Utils::DebugDumpData ("parsePkgList", $rv);
7389 dpurdie 833
#Utils::DebugDumpData ("remotePkgList", $remotePkgList);
1038 dpurdie 834
 
6475 dpurdie 835
 
1040 dpurdie 836
    #
837
    #   If there are no packages to process, then assume that this is an error
838
    #   condition. Retry the operation soon.
839
    #
840
    unless ( keys %{$pkgList} )
841
    {
842
 
843
        $logger->verbose2("No packages to process");
5398 dpurdie 844
        $statistics{state} = 'No Packages found';
1040 dpurdie 845
        return;
846
    }
847
 
6475 dpurdie 848
#   #
849
#   #   Useful debug code
850
#   #
851
#   while ( (my ($pname, $pvers)) = each %{$pkgList} )
852
#   {
853
#       while ( (my ($pver, $ptime) ) = each %{$pvers} )
854
#       {
855
#           print "L-- $pname, $pver, $ptime \n";
1038 dpurdie 856
#
6475 dpurdie 857
#       }
858
#   }
1038 dpurdie 859
 
860
    #
1040 dpurdie 861
    #   Delete Excess Packages
1038 dpurdie 862
    #       Packages not required on the target
1042 dpurdie 863
    #           KLUDGE: Don't delete links to packages
864
    #           Don't delete packages marked for deletion
1038 dpurdie 865
    #
866
    my $excessPkgList;
1048 dpurdie 867
    my $excessPkgListCount = 0;
1038 dpurdie 868
    if ( $conf->{deletePackages} )
869
    {
870
        while ( (my ($pname, $pvers)) = each %{$remotePkgList} )
871
        {
872
            while ( (my ($pver, $pdata) ) = each %{$pvers} )
873
            {
874
                if ( !exists $pkgList->{$pname}{$pver} )
875
                {
1040 dpurdie 876
                    if ( exists $excludePkgs->{$pname} )
877
                    {
878
                        $logger->verbose2("Keep Excluded package: ${pname}");
879
                        next;
880
                    }
881
 
1042 dpurdie 882
                    if ( exists $pdata->{deleted} )
883
                    {
884
                        if ( $conf->{deleteAge} )
885
                        {
886
                            if ( $pdata->{deleted} <= $conf->{deleteAge} )
887
                            {
888
                                $logger->verbose2("Already marked for future age deletion: ${pname}/${pver}, $pdata->{deleted}");
889
                                next;
890
                            }
891
                            $pdata->{FORCEDELETE} = 1;
892
                        }
893
 
894
                        if ( !$conf->{deleteImmediate} )
895
                        {
896
                            $logger->verbose2("Already marked for deletion: ${pname}/${pver}");
897
                            next;
898
                        }
899
                    }
900
 
901
                    #
902
                    #   Force deletion
903
                    #       deleteImmediate mode
904
                    #       target is a broken link
905
                    #
906
                    $pdata->{FORCEDELETE} = 1
907
                        if ($conf->{deleteImmediate} || $pdata->{broken});
908
 
1038 dpurdie 909
                    $excessPkgList->{$pname}{$pver} = $pdata;
1048 dpurdie 910
                    $excessPkgListCount++;
1038 dpurdie 911
                    $logger->verbose("Excess package: ${pname}/${pver}");
912
                }
1050 dpurdie 913
#                else
914
#                {
915
#                        $logger->verbose3("Retain package: ${pname}/${pver}");
916
#                }
1038 dpurdie 917
            }
918
        }
919
    }
920
 
921
    #
922
    #   Process the remote list and the local list
923
    #   The remote time-stamp is the modification time of the packages descpkg file
924
    #
925
    #   Mark for transfer packages that
1040 dpurdie 926
    #       Are in the local set but not the remote set
1038 dpurdie 927
    #       Have a different time stamp
928
    #
929
    #   Ignore packages not in the local archive
930
    #   Ignore packages that don't have a descpkg
931
    #   Ignore packages that are writable - still being formed
932
    #
933
    my $needPkgList;
1048 dpurdie 934
    my $needPkgListCount = 0;
935
    my $filteredCount = 0;
936
    my $missingCount = 0;
937
    my $writableCount = 0;
938
    my $excludeCount = 0;
5404 dpurdie 939
    my $packageVersionCount = 0;
1038 dpurdie 940
    while ( (my ($pname, $pvers)) = each %{$pkgList} )
941
    {
1040 dpurdie 942
        #
943
        #   Ignore excluded packages
944
        #
1048 dpurdie 945
        if ( exists $excludePkgs->{$pname} )
946
        {
947
            $excludeCount++;
948
            next;
949
        }
1040 dpurdie 950
 
1048 dpurdie 951
        #
952
        #   Ignore packages that are filtered out
953
        #
954
        if ( defined $conf->{'packageFilter'} )
955
        {
956
            unless ( $pname =~ m~$conf->{'packageFilter'}~ )
957
            {
958
                $logger->verbose3("Filtering out: ${pname}");
959
                $filteredCount++;
960
                next;
961
            }
962
        }
963
 
1038 dpurdie 964
        while ( (my ($pver, $pdata) ) = each %{$pvers} )
965
        {
6475 dpurdie 966
            my $must_transfer;
967
            my $existsRemote = exists($remotePkgList->{$pname}) && exists ($remotePkgList->{$pname}{$pver});
968
 
6148 dpurdie 969
            #
970
            #   Take care not to create an entry into $remotePkgList->{$pname}{$pver}
971
            #   if it does not exist. Existence of {$pname}{$pver} is used later
972
            #
7389 dpurdie 973
            my $tmtime = undef;
6475 dpurdie 974
            if ($existsRemote && exists ($remotePkgList->{$pname}{$pver}{time})) {
6148 dpurdie 975
                $tmtime = $remotePkgList->{$pname}{$pver}{time};
976
            }
5404 dpurdie 977
            $packageVersionCount++;
1038 dpurdie 978
 
979
            # Package is present in both list
6475 dpurdie 980
            my $localPackage = catdir( $conf->{'dpkg_archive'} , $pname, $pver );
981
            my ($mtime, $mode) = Utils::mtime( catfile($localPackage, 'descpkg') );
1038 dpurdie 982
            if ( $mtime == 0 )
983
            {
984
                # PackageVersion not in local archive (at least the descpkg file is not)
985
                # Skip now - will pick it up later
986
                $logger->verbose("Package not in dpkg_archive: $pname, $pver");
1048 dpurdie 987
                $missingCount++;
1038 dpurdie 988
                next;
989
            }
990
 
991
            if ( $mode & 0222 )
992
            {
993
                # Descpkg file is writable
994
                # Package may be in the process of being created
1048 dpurdie 995
                # If the package has been writable for a long time, then
1038 dpurdie 996
                # consider for transfer
997
                my $age = $now - $mtime;
998
                if ( $age < ($conf->{'writewindow '} || 600) )
999
                {
1000
                    $logger->verbose("Package is writable: $pname, $pver, ", $now - $mtime);
1048 dpurdie 1001
                    $writableCount++;
1038 dpurdie 1002
                    next;
1003
                }
1004
            }
1005
 
7389 dpurdie 1006
            if (! $existsRemote ) {
1007
                #
1008
                #   Package does not exist in the remote, and is eligible for transfer
1009
                #
1010
                $logger->verbose("Package Needs to be transferred: $pname, $pver");
1011
                $must_transfer = 1;
1012
 
1013
            } elsif ( defined $tmtime && ($mtime != $tmtime) ) {
1014
                #
1015
                #   Package exists in both source and target
1016
                #   If the package-time is known, then ensure that they are the same
1017
                #
6475 dpurdie 1018
                $logger->verbose("Package Needs to be transferred: $pname, $pver, $mtime, $tmtime");
1019
                $must_transfer = 1;
1020
            }
7389 dpurdie 1021
            else
1038 dpurdie 1022
            {
6475 dpurdie 1023
                #
1024
                #   Package exists in both source and target
1025
                #   Symlink test: Ensure symlinks are the same
1026
                #
1027
                my $localIsSymlink = -l $localPackage;
1028
                my $remoteIsSymlink = exists($remotePkgList->{$pname}) && exists ($remotePkgList->{$pname}{$pver}) && exists ($remotePkgList->{$pname}{$pver}{link});
1029
 
1030
                if ($remoteIsSymlink && $localIsSymlink) {
1031
                    #
1032
                    #   Both are symlinks - check that they address the same item
1033
                    #
1034
                    my $targetLink = $remotePkgList->{$pname}{$pver}{link};
1035
                    $logger->verbose2("Package is symlink: $pname, $pver -> $targetLink");
1036
 
1037
                    my $lver = readlink( $localPackage );
1038
                    if ( ! defined $lver ) {
1039
                        $logger->warn("Can't resolve symlink: $pname, $pver");
1040
                        next;
1041
                    }
1042
                    if ($targetLink ne $lver ) {
1043
                        $logger->verbose("Package symlinks differ: $pname, $pver, $targetLink, $lver");
1044
                        $must_transfer = 3;
1045
                    }
1046
 
1047
                } elsif ($remoteIsSymlink || $localIsSymlink ) {
1048
                    #
1049
                    #   Only one is a symlink - force transfer
1050
                    #
1051
                    $logger->warn("Packages versions not both symlink: $pname, $pver, L:$remoteIsSymlink R:$localIsSymlink");
1052
                    $must_transfer = 2;
1053
                }
1054
            }
1055
 
1056
            #
1057
            #   If we are forcing a package transfer then flag it and also remove it from the
1058
            #   RemotePkgList so that it will be transferred - even if its present on target
1059
            #
1060
            if ($must_transfer) {
1038 dpurdie 1061
                # Package not present on target, or timestamps differ
1062
                $needPkgList->{$pname}{$pver} = $pdata;
6475 dpurdie 1063
                delete $RemotePkgList->{$pname}{$pver};
1048 dpurdie 1064
                $needPkgListCount++;
1038 dpurdie 1065
                next;
1066
            }
1067
        }
1068
    }
1069
 
1048 dpurdie 1070
 
1038 dpurdie 1071
    #
1072
    #   Debug output only
1073
    #   Display what we need to transfer
1074
    #
1075
    if ( $conf->{verbose} > 2 )
1076
    {
1077
        while ( (my ($pname, $pvers)) = each %{$needPkgList} )
1078
        {
1079
            while ( (my ($pver, $pdata) ) = each %{$pvers} )
1080
            {
1081
                $logger->verbose("Need to transfer: $pname, $pver, $pdata");
1082
            }
1083
        }
1084
    }
1048 dpurdie 1085
    if ( $conf->{verbose}  )
1086
    {
1087
        $logger->verbose("Packages to transfer: $needPkgListCount");
1088
        $logger->verbose("Packages to delete: $excessPkgListCount");
1089
        $logger->verbose("Packages filtered out: $filteredCount");
1090
        $logger->verbose("Packages missing: $missingCount");
1091
        $logger->verbose("Packages still writable: $writableCount");
1092
        $logger->verbose("Packages excluded: $excludeCount");
1093
    }
1038 dpurdie 1094
 
1095
    #
5398 dpurdie 1096
    #   Update stats
1097
    #   At this point we are looking pretty good
1098
    #   
1099
    $statistics{state} = 'OK';
5404 dpurdie 1100
    $statistics{total} = $packageVersionCount;
5398 dpurdie 1101
    $statistics{transfer} = $needPkgListCount;
1102
    $statistics{delete} = $excessPkgListCount;
1103
    $statistics{filtered} = $filteredCount;
1104
    $statistics{missing} = $missingCount;
1105
    $statistics{writable} = $writableCount;
1106
    $statistics{excluded} = $excludeCount;
1107
 
1108
    #
1038 dpurdie 1109
    #   Time to do the real work
1110
    #   Transfer packages and delete excess packages
1111
    #   Note: Perform the transfers first
1112
    #         Limit the number of packages processed in one pass
1113
    #
1114
    my $txcount = $conf->{maxpackages};
1115
 
1116
    #
1117
    #   Transfer packages that we have identified
1118
    #
1119
    while ( (my ($pname, $pvers)) = each %{$needPkgList} )
1120
    {
1121
        while ( (my ($pver, $pdata) ) = each %{$pvers} )
1122
        {
7394 dpurdie 1123
            tagForTransfer($pname, $pver);
1048 dpurdie 1124
            $needPkgListCount--;
1038 dpurdie 1125
        }
1126
    }
1127
 
1128
    #
1129
    #   Delete packages that have been identified as excess
1130
    #
1131
    while ( (my ($pname, $pvers)) = each %{$excessPkgList} )
1132
    {
1133
        while ( (my ($pver, $pdata) ) = each %{$pvers} )
1134
        {
7394 dpurdie 1135
            tagForDelete ($pname, $pver, $pdata);
1136
            $excessPkgListCount--;
1137
        }
1138
    }
1139
 
1140
    #
1141
    #   Need to transmission remove tags for packages that don't need any more
1142
    #   ie: Tags that have since been made unnessesary
1143
    #
1144
    my $taggedPackages = getTaggedPackages();
1145
    if ($taggedPackages) {
1146
        #
1147
        #   Mark entries thay we still need
1148
        #
1149
        foreach my $pname ( keys %{$pkgList} )
1150
        {
1151
            foreach my $pver ( keys %{$pkgList->{$pname}} )
1038 dpurdie 1152
            {
7394 dpurdie 1153
                if (exists $taggedPackages->{$pname}{$pver}) {
1154
                    $taggedPackages->{$pname}{$pver} = 1;
1155
                }
1038 dpurdie 1156
            }
7394 dpurdie 1157
        }
1289 dpurdie 1158
 
7394 dpurdie 1159
        #
1160
        #   Sweep out entries that we don't need any more
1161
        foreach my $pname ( keys %{$taggedPackages} ) {
1162
            foreach my $pver ( keys %{$taggedPackages->{$pname}} ) {
1163
                unless ($taggedPackages->{$pname}{$pver}) {
1164
$logger->verbose("Untag: $pname $pver");
1165
                    unTagForTransfer($pname, $pver);
1166
                }
1289 dpurdie 1167
            }
1038 dpurdie 1168
        }
7394 dpurdie 1169
 
1038 dpurdie 1170
    }
7394 dpurdie 1171
 
1038 dpurdie 1172
    #
1173
    #   Send package list to the target
1174
    #
1175
    sendPackageList ($pkgList);
1040 dpurdie 1176
 
1177
    #
1178
    #   On a successful transfer
1179
    #       Force tag processing
1180
    #       Set scan Mode to normal
1181
    #
1182
    $tagDirTime = 0;
1183
    $releaseScanMode = 0;
1038 dpurdie 1184
}
1185
 
1186
#-------------------------------------------------------------------------------
7394 dpurdie 1187
# Function        : tagForTransfer  
1188
#
1189
# Description     : Tag a package to be transferred
1190
#
1191
# Inputs          : $pname
1192
#                   $pvers 
1193
#
1194
# Returns         : 
1195
#
1196
sub tagForTransfer
1197
{
1198
    my ($pname, $pver) = @_;
1199
    my $tag = "$pname::$pver";
1200
    my $myTag =  catfile($conf->{'tagdir'} , $tag);
1201
    unless (-f $myTag ) {
1202
        $logger->verbose2("tagForTransfer: $pname, $pver");
1203
        Utils::TouchFile($conf, $myTag);
1204
    }
1205
    unlink 'DELD::' . $myTag;
1206
    unlink 'DELF::' . $myTag;
1207
}
1208
 
1209
#-------------------------------------------------------------------------------
1210
# Function        : unTagForTransfer  
1211
#
1212
# Description     : Un Tag a package to be transferred
1213
#
1214
# Inputs          : $pname
1215
#                   $pvers 
1216
#
1217
# Returns         : 
1218
#
1219
sub unTagForTransfer
1220
{
1221
    my ($pname, $pver) = @_;
1222
    my $tag = "$pname::$pver";
1223
    my $myTag =  catfile($conf->{'tagdir'} , $tag);
1224
    if (-f $myTag ) {
1225
        $logger->verbose2("UnTagForTransfer: $pname, $pver");
1226
        unlink($myTag);
1227
    }
1228
}
1229
 
1230
#-------------------------------------------------------------------------------
1231
# Function        : tagForDelete  
1232
#
1233
# Description     : Tag a package to be transferred
1234
#                   Generate tags of the form 
1235
#                       DELD::pname::pver   - Delayed Delete
1236
#                       DELF::pname::pver   - Forced Delete
1237
#
1238
# Inputs          : $pname
1239
#                   $pver
1240
#                   $pdata  - Type of delete
1241
#
1242
# Returns         : 
1243
#
1244
sub tagForDelete
1245
{
1246
    my ($pname, $pver, $pdata) = @_;
1247
    my $tag = "$pname::$pver";
1248
    my $myTag =  catfile($conf->{'tagdir'} , $tag);
1249
    my $delType = $pdata->{FORCEDELETE}  ? 'D' : 'F';
1250
    my $myDelTag =  'DEL' . $delType . '::' . $myTag;
1251
 
1252
    unless (-f $myDelTag ) {
1253
        $logger->verbose2("tagForDelete: $pname, $pver");
1254
        Utils::TouchFile($conf, $myDelTag);
1255
    }
1256
    unlink $myTag;
1257
}
1258
 
1259
#-------------------------------------------------------------------------------
1260
# Function        : getTaggedPackages  
1261
#
1262
# Description     : Determine the tagged packages 
1263
#
1264
# Inputs          : None
1265
#
1266
# Returns         : Returns a pointer to a hash of tagged packages of the form
1267
#
1268
sub getTaggedPackages
1269
{
1270
    my $taggedPackages = {};
1271
    foreach ( glob ("$conf->{'tagdir'}/*::*")) 
1272
    {
1273
        next if m~/DEL.::~;
1274
        m~.*/(.*)::(.*)~;
1275
        $taggedPackages->{$1}{$2} = 0;
1276
    }
1277
    return $taggedPackages; 
1278
}
1279
 
1280
 
1281
#-------------------------------------------------------------------------------
1038 dpurdie 1282
# Function        : sendPackageList
1283
#
1284
# Description     : Transfer package list to the target
1285
#
1286
# Inputs          : $pkgList            - Ref to hash of package names and versions
1287
#
1040 dpurdie 1288
# Returns         : Nothing
1289
#                   Don't really care about any errors from this process
1290
#                   Its not essential
1038 dpurdie 1291
#
1292
sub sendPackageList
1293
{
7389 dpurdie 1294
    return if $isS3Target;
1295
 
1038 dpurdie 1296
    my ($pkgList) = @_;
1297
    my ($fh, $filename) = tempfile( "/tmp/blat.$$.XXXX", SUFFIX => '.txt');
1298
    $logger->verbose("sendPackageList:TmpFile: $filename");
6776 dpurdie 1299
 
1300
    return if $conf->{'noTransfers'};
1038 dpurdie 1301
 
1302
    #
1303
    #   Create a temp file with data
1304
    #
1305
    foreach my $pname ( sort keys %{$pkgList} )
1306
    {
1307
        foreach my $pver ( sort keys %{$pkgList->{$pname}} )
1308
        {
1309
            print $fh "$pname/$pver\n";
1310
        }
1311
    }
1312
    close $fh;
1313
 
1314
    #
1315
    #   Transfer to target
1316
    #   Create the process pipe to transfer the file
1317
    #   gzip the file and pipe the result through a ssh session to the target machine
3515 dpurdie 1318
    #   gzip -c filename |  ssh  ... "./receive_file filename"
1038 dpurdie 1319
    #
1320
    my $ph;
3515 dpurdie 1321
    my $gzip_cmd = "$gzip --no-name -c \"$filename\"";
1040 dpurdie 1322
    my $tgt_cmd = "$conf->{'bindir'}/receive_file \"ArchiveList\"";
3515 dpurdie 1323
    my $ssh_cmd = sshCmd($tgt_cmd);
1038 dpurdie 1324
 
1325
    $logger->verbose2("sendPackageList:gzip_cmd:$gzip_cmd");
1326
    $logger->verbose2("sendPackageList:tgt_cmd:$tgt_cmd");
1327
    $logger->verbose2("sendPackageList:ssh_cmd:$ssh_cmd");
1328
 
1329
    open ($ph, "$gzip_cmd | $ssh_cmd |");
1330
    while ( <$ph> )
1331
    {
1332
        chomp;
1333
        $logger->verbose2("sendPackageList:Data: $_");
1334
    }
1335
    close ($ph);
3515 dpurdie 1336
    unlink $filename;
1038 dpurdie 1337
    $logger->verbose("sendPackageList:End: $?");
3515 dpurdie 1338
    LogTxError ($?);
1038 dpurdie 1339
}
1340
 
1341
 
1342
#-------------------------------------------------------------------------------
1343
# Function        : getPkgList
1344
#
1345
# Description     : Determine a set of package versions within the list
1346
#                   of provided releases
1347
#
1348
# Inputs          : @rlist              - A list of releases to examine
1349
#
1350
# Returns         : Ref to a hask of package versions
1351
#
1352
sub getPkgList
1353
{
1354
    my %pdata;
1355
    my $RM_DB;
1356
    connectRM(\$RM_DB);
1357
    $logger->verbose("getPkgList");
1358
 
1359
    #
1360
    #   Determine the releases that are in this project
1361
    #   Build up an sql query
1362
    #
6776 dpurdie 1363
    my $m_rlist = join ',', @_;
1048 dpurdie 1364
    my $m_sqlstr = "SELECT DISTINCT pv.PV_ID, pkg.PKG_NAME, pv.PKG_VERSION, pv.IS_DEPLOYABLE" .
1038 dpurdie 1365
                    " FROM RELEASE_MANAGER.RELEASE_CONTENT rc, RELEASE_MANAGER.PACKAGE_VERSIONS pv, RELEASE_MANAGER.PACKAGES pkg" .
6776 dpurdie 1366
                    " WHERE ( RTAG_ID in ($m_rlist) ) AND rc.PV_ID = pv.PV_ID AND pv.PKG_ID = pkg.PKG_ID" .
1038 dpurdie 1367
                    " ORDER by PKG_NAME DESC";
1368
    $logger->verbose3("getPkgList:Sql:$m_sqlstr");
1369
 
1370
    my $sth = $RM_DB->prepare($m_sqlstr);
1371
    if ( defined($sth) )
1372
    {
1373
        if ( $sth->execute( ) )
1374
        {
1375
            if ( $sth->rows )
1376
            {
1377
                while (my @row = $sth->fetchrow_array )
1378
                {
1379
                    $logger->verbose2("getPkgList:Data:@row");
1380
                    $pdata{$row[1]}{$row[2]} = 1;
1381
                }
1382
            }
1383
            $sth->finish();
1384
        }
1385
    }
1386
    else
1387
    {
1388
        $logger->warn("getPkgList: SQL Prepare failure");
1389
    }
1050 dpurdie 1390
 
1391
   disconnectRM(\$RM_DB);
1038 dpurdie 1392
   return \%pdata;
1393
}
1394
 
1395
 
1396
#-------------------------------------------------------------------------------
1397
# Function        : getReleaseList
1398
#
1399
# Description     : Determine the list of releases to be proccessed
1040 dpurdie 1400
#                   From:
1401
#                       Convert projects to a list of releases
1402
#                       Configured list of releases
1038 dpurdie 1403
#
1404
# Inputs          : None
1405
#
1406
# Returns         : A list of releases to be processed
1407
#
1408
sub getReleaseList
1409
{
1410
    my $RM_DB;
1411
    my %rlist;
1048 dpurdie 1412
    my $m_sqlstr;
1038 dpurdie 1413
    $logger->verbose("getReleaseList");
1414
 
1415
    #
1040 dpurdie 1416
    #   Cache data
1417
    #   Only for one cycle of the main loop
1418
    #
1419
    if ( exists $releaseData{getReleaseList} )
1420
    {
1421
        $logger->verbose3("getReleaseList:Cache hit");
1422
        return @{$releaseData{getReleaseList}};
1423
    }
1424
 
1425
    #
1048 dpurdie 1426
    #   All projects
1038 dpurdie 1427
    #
1048 dpurdie 1428
    if ( $conf->{'allProjects'} )
1038 dpurdie 1429
    {
1048 dpurdie 1430
        $m_sqlstr = "SELECT rt.RTAG_ID" .
1431
                    " FROM RELEASE_MANAGER.RELEASE_TAGS rt" .
1050 dpurdie 1432
                    " WHERE rt.OFFICIAL != 'A'";
1433
                    #" AND rt.OFFICIAL != 'Y'";
1048 dpurdie 1434
    }
1435
    else
1436
    {
1038 dpurdie 1437
        #
1048 dpurdie 1438
        #   Convert list of projects into a list of releases
1038 dpurdie 1439
        #
6776 dpurdie 1440
        if ( @projectList )
1048 dpurdie 1441
        {
1442
            #
1443
            #   Determine the releases that are in this project
1444
            #   Build up an sql query
1445
            #
6776 dpurdie 1446
            my $m_plist = join ',', @projectList;
1048 dpurdie 1447
            $m_sqlstr = "SELECT rt.RTAG_ID" .
1448
                        " FROM RELEASE_MANAGER.RELEASE_TAGS rt" .
6776 dpurdie 1449
                        " WHERE ( PROJ_ID in ( $m_plist) ) AND rt.OFFICIAL != 'A'";
5300 dpurdie 1450
                        #" AND rt.OFFICIAL != 'Y'";
1048 dpurdie 1451
        }
1452
    }
1038 dpurdie 1453
 
1048 dpurdie 1454
    if ( defined $m_sqlstr )
1455
    {
1038 dpurdie 1456
        $logger->verbose3("getReleaseList:Sql:$m_sqlstr");
1048 dpurdie 1457
        connectRM(\$RM_DB);
1038 dpurdie 1458
        my $sth = $RM_DB->prepare($m_sqlstr);
1459
        if ( defined($sth) )
1460
        {
1461
            if ( $sth->execute( ) )
1462
            {
1463
                if ( $sth->rows )
1464
                {
1465
                    while (my @row = $sth->fetchrow_array )
1466
                    {
1467
                        $logger->verbose2("getReleaseList:Data:@row");
1468
                        $rlist{$row[0]} = 1;
1469
                    }
1470
                }
1471
                $sth->finish();
6776 dpurdie 1472
            } else {
1473
                $logger->warn("getReleaseList: SQL Execute failure");
1038 dpurdie 1474
            }
1475
        }
1476
        else
1477
        {
1478
            $logger->warn("getReleaseList: SQL Prepare failure");
1479
        }
1050 dpurdie 1480
        disconnectRM(\$RM_DB);
1038 dpurdie 1481
    }
1482
 
1483
    #
1484
    #   Add in the user specified list of releases
1485
    #
6776 dpurdie 1486
    $rlist{$_} = 1 foreach(@releaseList);
1038 dpurdie 1487
 
1488
    #
1489
    #   Sort for pretty display only
1490
    #
1040 dpurdie 1491
    @{$releaseData{getReleaseList}} = sort {$a <=> $b} keys %rlist;
1492
 
1493
    return @{$releaseData{getReleaseList}};
1038 dpurdie 1494
}
1495
 
1040 dpurdie 1496
#-------------------------------------------------------------------------------
3846 dpurdie 1497
# Function        : getPackageVersions
1498
#
1499
# Description     : Get the list of package-versions available in the package
1500
#                   store.
1501
#
1502
# Inputs          : pkgName             - The package name
1503
#
1504
# Returns         : Array of versions
1505
#
1506
sub getPackageVersions
1507
{
1508
    my ($pkgName) = @_;
1509
    my @versionList;
1510
 
1511
    my $pkgDir = catfile($conf->{'dpkg_archive'} , $pkgName );
1512
    my $dh;
1513
 
1514
    unless (opendir($dh, $pkgDir))
1515
    {
1516
        $logger->warn ("Can't opendir $pkgDir: $!");
1517
        return @versionList;
1518
    }
1519
 
1520
    #
1521
    #   Process each entry
1522
    #   Ignore those that start with a .
1523
    #
1524
    while (my $version = readdir($dh) )
1525
    {
1526
        next if ( $version =~ m~^\.~ );
1527
        my $file = catfile($pkgDir, $version);
1528
 
1529
        next unless ( -d $file );
1530
 
1531
        push @versionList, $version;
1532
        $logger->verbose3("getPackageVersions: $pkgName, $version");
1533
    }
1534
    closedir $dh;
1535
    return @versionList;
1536
}
1537
 
1538
#-------------------------------------------------------------------------------
4456 dpurdie 1539
# Function        : getArchiveList 
1540
#
1541
# Description     : Get the entire set of package versions in the archive
1542
#
1543
# Inputs          : 
1544
#
6475 dpurdie 1545
# Returns         : Ref to a hash of package versions
4456 dpurdie 1546
#
1547
sub getArchiveList
1548
{
1549
    my $pkgDir = $conf->{'dpkg_archive'};
1550
    my %archiveList;
1551
    my $dh;
1552
    my @pkgList;
1553
 
1554
    unless (opendir($dh, $pkgDir))
1555
    {
1556
        $logger->warn ("Can't opendir $pkgDir: $!");
1557
        return \%archiveList;
1558
    }
1559
 
1560
    #
1561
    #   Process each entry
1562
    #   Ignore those that start with a .
1563
    #   Ignore files
1564
    #
1565
    while (my $pkgName = readdir($dh) )
1566
    {
1567
        next if ( $pkgName =~ m~^\.~ );
1568
        my $file = catfile($pkgDir, $pkgName);
1569
 
1570
        next unless ( -d $file );
1571
        $logger->verbose3("getArchiveList: $pkgName");
1572
        push @pkgList, $pkgName;
1573
    }
1574
    closedir $dh;
1575
 
1576
    #   Now get the package versions
1577
    #       Sort for pretty display
1578
    foreach my $pname (sort @pkgList)
1579
    {
1580
        foreach my $pver (getPackageVersions($pname))
1581
        {
1582
            $archiveList{$pname}{$pver} = 1;
1583
        }
1584
    }
1585
 
1586
    return \%archiveList;
1587
}
1588
 
1589
#-------------------------------------------------------------------------------
6320 dpurdie 1590
# Function        : getBlatBin  
1591
#
1592
# Description     : Get the list of files that should be in the targetbin directory
1593
#
1594
# Inputs          : Nothing 
1595
#
1596
# Returns         : A hash of data 
1597
#
1598
sub getBlatBin
1599
{
1600
    my $data;
1601
    $logger->verbose("getBlatBin: $targetBinDir");
1602
    if (opendir(DIR, $targetBinDir ) ) {
1603
        my @vlist = readdir(DIR);
1604
        closedir DIR;
1605
 
1606
        foreach my $vname ( sort @vlist )
1607
        {
1608
            next if ( $vname eq '.' );
1609
            next if ( $vname eq '..' );
1610
            next unless ( -f "$targetBinDir/$vname" );
1611
 
1612
            if (open FILE, "$targetBinDir/$vname") {
1613
                $data->{$vname} = Digest::MD5->new->addfile(*FILE)->hexdigest;
1614
                close (FILE);
1615
            }
1616
        }
1617
    } else {
1618
        $logger->warn("BlatBin Not Found: $targetBinDir");
1619
    }
1620
    return $data;
1621
}
1622
 
1623
 
1624
#-------------------------------------------------------------------------------
1040 dpurdie 1625
# Function        : maintainTagList
1626
#
1627
# Description     : Maintain a data structure for the maintenance of the
1628
#                   tags directory
1629
#
1630
# Inputs          : None
1631
#
1632
# Returns         : Nothing
1633
#
1634
sub maintainTagList
1635
{
1636
    #
1637
    #   Time to perform the scan
1638
    #   Will do at startup and every time period there after
1639
    #
6779 dpurdie 1640
    return unless ( $now > ($lastTagListUpdate + $conf->{tagListUpdate} ));
1040 dpurdie 1641
    $logger->verbose("maintainTagList");
6779 dpurdie 1642
    $lastTagListUpdate = $now;
1038 dpurdie 1643
 
1040 dpurdie 1644
    #
1645
    #   Get list of things
1646
    #
1647
    my %config;
7394 dpurdie 1648
 
1649
    #
1650
    #   Is Tag Processing active
1651
    #   Can configure blat to disable tag sync
1652
    #
1653
    if ( $conf->{'tagMaxPackages'} > 0 )
4457 dpurdie 1654
    {
7394 dpurdie 1655
 
1656
        if ($conf->{'allArchive'} )
1657
        {
1658
            $config{allArchive} = 1
1659
        }
1660
        elsif ($conf->{'allProjects'} )
1661
        {
1662
            $config{allProjects} = 1;
1663
        }
1664
        else
1665
        {
1666
            %{$config{projects}} = map { $_ => 1 } @projectList;
1667
            %{$config{releases}} = map { $_ => 1 } getReleaseList();
1668
        }
1669
    } else {
1670
        $config{disableTagTx} = 1
4457 dpurdie 1671
    }
1040 dpurdie 1672
 
1673
    #
1674
    #   Save data
1675
    #
1676
    my $dump =  Data::Dumper->new([\%config], [qw(*config)]);
1677
#print $dump->Dump;
1678
#$dump->Reset;
1679
 
1680
    #
1681
    #   Save config data
1682
    #
1683
    my $conf_file = catfile( $conf->{'tagdir'},'.config' );
1684
    $logger->verbose3("maintainTagList: Writting $conf_file");
1685
 
1686
    my $fh;
1687
    open ( $fh, '>', $conf_file ) or $logger->err("Can't create $conf_file: $!");
1688
    print $fh $dump->Dump;
1689
    close $fh;
1690
}
1691
 
1692
 
1038 dpurdie 1693
#-------------------------------------------------------------------------------
1694
# Function        : processTags
1695
#
1696
# Description     : Process tags and send marked package versions to the target
1697
#                       Determine if new tags are present
1698
#                       Process each tag
7394 dpurdie 1699
#                       
1700
#                       Two types of tag
1701
#                           Transfer Requests
1702
#                           Delete Request
1703
#                       Send packages before deleting packages    
1038 dpurdie 1704
#
1705
# Inputs          : None
1706
#
1707
# Returns         : Nothing
1708
#
1709
sub processTags
1710
{
6779 dpurdie 1711
 
1038 dpurdie 1712
    #
1713
    #   Determine if new tags are present by examining the time
1714
    #   that the directory was last modified.
1715
    #
1716
    #   Allow for a forced scan to catch packages that did not transfer
1717
    #   on the first attempt
1718
    #
5398 dpurdie 1719
    my $tagCount = 0;
7394 dpurdie 1720
    my $delCount = 0;
1721
 
1038 dpurdie 1722
    my ($mtime) = Utils::mtime($conf->{'tagdir'} );
1723
    if ( ($mtime > $tagDirTime) || ($now > ($lastDirScan + $conf->{'forcedirscan'})) )
1724
    {
1042 dpurdie 1725
        $logger->verbose2("processTags: ",$conf->{'tagdir'}, $mtime - $tagDirTime, $now - $lastDirScan);
1038 dpurdie 1726
        $tagDirTime = $mtime;
1727
        $lastDirScan = $now;
6779 dpurdie 1728
        my $txcount = $conf->{'tagMaxPackages'};
1038 dpurdie 1729
 
1730
        my $dh;
1731
        unless (opendir($dh, $conf->{'tagdir'}))
1732
        {
1733
            $logger->warn ("can't opendir $conf->{'tagdir'}: $!");
1734
            return;
1735
        }
1736
 
1737
        #
1738
        #   Process each entry
1739
        #   Ignore those that start with a .
7460 dpurdie 1740
        #   Attempt to keep the entries in time of creation order.
1038 dpurdie 1741
        #
7460 dpurdie 1742
        my @tagPkgList;
7394 dpurdie 1743
        my %deleteTags;
7460 dpurdie 1744
        my @sortedFiles = sort {(stat($conf->{'tagdir'} .'/'. $a))[10] <=> (stat($conf->{'tagdir'} .'/'. $b))[10]} readdir($dh);
1745
        closedir $dh;
1746
 
1747
        foreach my $tag (@sortedFiles)
1038 dpurdie 1748
        {
1749
            next if ( $tag =~ m~^\.~ );
1750
            my $file = "$conf->{'tagdir'}/$tag";
1042 dpurdie 1751
            $logger->verbose3("processTags: $file");
1752
 
1038 dpurdie 1753
            next unless ( -f $file );
1754
            next if ( $tag  eq 'ReleaseList' );
1755
 
7394 dpurdie 1756
            if ( $tag  =~ m~^DEL(.)::(.+)::(.+)~) {
1757
                $deleteTags {$2}{$3}{file} = $file;
1758
                $deleteTags {$2}{$3}{mode} = $1 eq 'F' ? 1 : 0;
1759
                $delCount++;
1760
            }
1761
 
1038 dpurdie 1762
            if ( $tag =~ m~(.+)::(.+)~  )
1763
            {
7460 dpurdie 1764
                push @tagPkgList, join($;,  $1, $2, $file);
5398 dpurdie 1765
                $tagCount++;
6779 dpurdie 1766
            }
1767
        }
1768
        $statistics{tagCount} = $tagCount;
7394 dpurdie 1769
        $statistics{tagDelCount} = $delCount;
6098 dpurdie 1770
 
6779 dpurdie 1771
        #
7394 dpurdie 1772
        #   Process delete requests after all transfers have occured
1773
        #   
1774
        unless ($tagCount) {
1775
            delete_pkgs:
1776
            while ( (my ($pname, $pvers)) = each %deleteTags )
1777
            {
1778
                while ( (my ($pver, $pdata) ) = each %{$pvers} )
1779
                {
1780
                    if ( --$txcount <= 0 )
1781
                    {
1782
                        $logger->warn("Max tag transfer count exceeded: $delCount deletion remaining");
1783
                        $tagDirTime = 0;
1784
                        last delete_pkgs;
1785
                    }
1786
 
1787
                    if ( readConfig() )
1788
                    {
1789
                        $logger->warn("Config file changed");
1790
                        $txcount = 0;
1791
                        $tagDirTime = 0;
1792
                        last delete_pkgs;
1793
                    }
1794
 
1795
                    deletePackage ($pname, $pver, $pdata->{mode});
1796
                    unlink $pdata->{file};
1797
                    $delCount--;
1798
                    reapChildren();
7460 dpurdie 1799
                    Utils::resetWedge();
7394 dpurdie 1800
                }
1801
            }
1802
        }
1803
 
1804
        #
6779 dpurdie 1805
        #   Process the packages located in the tags area
7460 dpurdie 1806
        #   Have attempted to keep them in creation order
6779 dpurdie 1807
        #
1808
        send_tags:
7460 dpurdie 1809
        foreach my $entry ( @tagPkgList )
6779 dpurdie 1810
        {
7460 dpurdie 1811
            my ($package, $version, $file) = split ($;, $entry);
1812
            if ( --$txcount <= 0 )
6779 dpurdie 1813
            {
7460 dpurdie 1814
                $logger->warn("Max tag transfer count exceeded: $tagCount transfer remaining");
1815
                $logger->warn("Max tag transfer count exceeded: $delCount deletion remaining");
1816
                $tagDirTime = 0;
1817
                last send_tags;
1818
            }
6779 dpurdie 1819
 
7460 dpurdie 1820
            if ( readConfig() )
1821
            {
1822
                $logger->warn("Config file changed");
1823
                $txcount = 0;
1824
                $tagDirTime = 0;
1825
                last send_tags;
1826
            }
6779 dpurdie 1827
 
7389 dpurdie 1828
#               #
1829
#               #   Don't transfer 'extra' packages
1830
#               #   Removed. It was casuing a tarZip to be triggered, but never transferred
1831
#               #
1832
#               if (exists ($extraPkgs->{$package}) )
1833
#               {
1834
#                   $logger->warn ("Delete excess package tag: $package::$version");
1835
#                   unlink $file;
1836
#
1837
#               } else
7460 dpurdie 1838
            if ( transferPackage( $package, $version )) {
1839
                unlink $file;
1840
            }
1841
            else
1842
            {
1843
                if ($conf->{'tagage'} > 0) {
1844
                    my ($mtime) = Utils::mtime( $file );
1845
                    if ( $now - $mtime > $conf->{'tagage'} )
1846
                    {
1847
                        $logger->warn ("Delete unsatisfied tag: $package::$version after $conf->{'tagage'}" );
1848
                        unlink $file;
1849
                        $statistics{staleTags}++;
1038 dpurdie 1850
                    }
1851
                }
7460 dpurdie 1852
            }
6779 dpurdie 1853
 
7460 dpurdie 1854
            $tagCount--;
1855
            reapChildren();
1856
            Utils::resetWedge();
1038 dpurdie 1857
        }
1858
    }
1859
}
1860
 
1861
#-------------------------------------------------------------------------------
6320 dpurdie 1862
# Function        : transferBlatBin 
1863
#
1864
# Description     : Transfer any of the Blat Bin files that are out of date
1865
#                   on the target
1866
#
1867
# Inputs          : $fileHash       - A hash whose files are those that need
1868
#                                     to be updated 
1869
#
1870
# Returns         : 
1871
#
1872
sub transferBlatBin
1873
{
1874
    my ($hash) = @_;
1875
    $logger->verbose("transferBlatBin");
6776 dpurdie 1876
 
1877
    return if $conf->{'noTransfers'};
1878
 
6320 dpurdie 1879
    foreach my $file ( sort keys %{$hash})
1880
    {
1881
        $logger->logmsg("transferBlatBin: $file");
1882
 
1883
        #
1884
        #   Transfer one file using only 'ssh'
1885
        #   Create the target directory on the fly
1886
        #   Manipulate file permissions
1887
        #   Report errors
1888
 
1889
        my $tar_cmd = "cat \"$targetBinDir/$file\"";
6475 dpurdie 1890
        my $tgt_cmd = "mkdir -p ~/bin && if [ -f \"~/bin/$file\" ] ; then chmod +x+w \"~/bin/$file\"; fi && cat > \"~/bin/$file\" && chmod +x-w \"~/bin/$file\" || exit 1";
6320 dpurdie 1891
        my $ssh_cmd = sshCmd($tgt_cmd);
1892
        my $cat_cmd = 
1893
 
1894
        $logger->verbose2("transferBlatBin:tar_cmd:$tar_cmd");
1895
        $logger->verbose2("transferBlatBin:tgt_cmd:$tgt_cmd");
1896
        $logger->verbose2("transferBlatBin:ssh_cmd:$ssh_cmd");
1897
 
1898
        my $ph;
1899
        open ($ph, "$tar_cmd | $ssh_cmd |");
1900
        while ( <$ph> )
1901
        {
1902
            chomp;
1903
            $logger->verbose2("transferBlatBin:Data: $_");
1904
        }
1905
        close ($ph);
1906
        $logger->verbose("transferBlatBin:End: $?");
1907
 
1908
        if ( $? != 0 )
1909
        {
1910
            $logger->warn("transferBlatBin:Transfer Error: $file, $?");
1911
        }
1912
        LogTxError ($?);
1913
    }
1914
}
1915
 
7394 dpurdie 1916
#-------------------------------------------------------------------------------
1917
# Function        : calcZipRequests 
1918
#
1919
# Description     : Determine the number of packages that I need that have
1920
#                   outstanding ZIP requests
1921
#                   
1922
#                   Purpose is to limit the number of ZIP requests that are
1923
#                   outstanding at any one time to reduce disc space
1924
#                   
1925
#                   ie: Iff there 1000 outstanding transfers, we don't need to zip all
1926
#                   of the packages right now.
1927
#
1928
# Inputs          : 
1929
#
1930
# Returns         : 
1931
#
1932
#
1933
sub calcZipRequests
1934
{
1935
    my $zipRequests = 0;
6320 dpurdie 1936
 
7394 dpurdie 1937
    $conf->{'tagdir'} =~ m~^(.*)/~;
1938
    my $tagRoot = $1;
1939
 
1940
    my $dh;
1941
    if ( opendir($dh, $conf->{'tagdir'}) )
1942
    {
1943
        while (my $tag = readdir($dh) )
1944
        {
1945
            next if ( $tag =~ m~^\.~ );
1946
            my $file = "$conf->{'tagdir'}/$tag";
1947
            $logger->verbose3("calcZipRequests: $file");
1948
 
1949
            next unless ( -f $file );
1950
            next if ( $tag  eq 'ReleaseList' );
1951
            next if ( $tag =~ m~^DEL(.)::~);
1952
 
1953
            my $zipTag = catfile($tagRoot, 'tarZip', $tag);
1954
            if (-f $zipTag ) {
1955
                $zipRequests++;
1956
            }
1957
        }
1958
        closedir $dh;
1959
    }
1960
    $logger->verbose("zipRequests: $zipRequests");
1961
    return $zipRequests;
1962
}
1963
 
1964
 
6320 dpurdie 1965
#-------------------------------------------------------------------------------
1038 dpurdie 1966
# Function        : transferPackage
1967
#
1968
# Description     : Transfer specified package to target system
6475 dpurdie 1969
#                   If a symlink, then a symlink will be transferred
1038 dpurdie 1970
#
1971
# Inputs          : $pname          - Name of the package
1972
#                   $pver           - Package version
1973
#
1974
# Returns         : true    - Package transferred
1975
#                   false   - Package not transferred
7460 dpurdie 1976
#                   
1038 dpurdie 1977
#
7460 dpurdie 1978
#                   PackageExcluded         - Assume that its been transferred
1979
#                   PackageAtTarget         - Package known to be on the target
1980
#                   PackageNotInArchive     - Package not found in dpkg_archive
1981
#                   PackageZipRequested     - Requested a Zipped version of the package
1982
#                   PackageZipNotRequested  - Need a Zipped version, but I have too many requests outstanding
1983
#                   PackageTransferOK       - Package has been transferred
1984
#                   PackageTransferError    - Error in the package transfer
1985
#                   
1986
#
1987
#
1038 dpurdie 1988
sub transferPackage
1989
{
6475 dpurdie 1990
    my ($pname, $pver ) = @_;
1038 dpurdie 1991
    my $rv = 0;
6776 dpurdie 1992
    my $cmdRv = 0;
7389 dpurdie 1993
    $logger->verbose("Enter transferPackage: @_");
6148 dpurdie 1994
    my $startTime = time;
1038 dpurdie 1995
 
1996
    #
1040 dpurdie 1997
    #   Do not transfer excluded files
1998
    #
1999
    if ( exists $excludePkgs->{$pname} )
2000
    {
1048 dpurdie 2001
        $logger->warn("transferPackage: Excluded package not transferred: $pname, $pver");
1040 dpurdie 2002
        return 1;
2003
    }
1048 dpurdie 2004
 
1040 dpurdie 2005
    #
1048 dpurdie 2006
    #   Apply package filter
2007
    #
2008
    if ( defined $conf->{'packageFilter'} )
2009
    {
2010
        unless ( $pname =~ m~$conf->{'packageFilter'}~ )
2011
        {
2012
            $logger->warn("transferPackage: Filtered out package not transferred: $pname, $pver");
2013
            return 1;
2014
        }
2015
    }
2016
 
2017
    #
6148 dpurdie 2018
    #   If its known to be in the target archive, then we don't need to transfer it again
2019
    #       It may have been transferred in this cycle
2020
    #       It may have been in the archive anyway
1038 dpurdie 2021
    #
6148 dpurdie 2022
    if ( exists($RemotePkgList->{$pname}) && exists ($RemotePkgList->{$pname}{$pver})) {
2023
        $logger->verbose("transferPackage: Already in archive");
6320 dpurdie 2024
        #$logger->logmsg("transferPackage: $pname, $pver : Already in archive");
1038 dpurdie 2025
        return 1;
2026
    }
2027
 
6475 dpurdie 2028
    my $sdir = catfile( $conf->{'dpkg_archive'} , $pname );
2029
    my $sfile = catfile( $sdir, $pver );
1038 dpurdie 2030
    unless ( -d $sfile )
2031
    {
2032
        $logger->warn("transferPackage:Package not found: $pname, $pver");
2033
        return $rv;
2034
    }
2035
 
7387 dpurdie 2036
    my $tzdir = catfile( $conf->{'dpkg_archive'} , '.dpkg_archive', 'tarStore' );
2037
    my $tzfile = $pname . '__' . $pver . '.tgz';
2038
    my $tzpath = catfile($tzdir, $tzfile);
2039
    unless (-f $tzpath) {
7394 dpurdie 2040
        $logger->verbose("transferPackage: tarZip not found - $tzfile");
7387 dpurdie 2041
 
2042
        $conf->{'tagdir'} =~ m~^(.*)/~;
2043
        my $tagRoot = $1;
2044
        my $tag = "$pname::$pver";
7389 dpurdie 2045
        my $zipTag = catfile($tagRoot, 'tarZip', $tag);
2046
        my $myTag =  catfile($conf->{'tagdir'} , $tag);
2047
 
7460 dpurdie 2048
        # Ensure I have an outstanding request, so that the tarZip process can notify me
7394 dpurdie 2049
        Utils::TouchFile($conf, $myTag) unless -f $myTag;
7389 dpurdie 2050
 
7394 dpurdie 2051
        unless (-f $zipTag) {
2052
            if (calcZipRequests() < $conf->{maxTarZips} ) {
7460 dpurdie 2053
                $logger->logmsg("transferPackage. Request Zip: @_");
7394 dpurdie 2054
                Utils::TouchFile($conf, $zipTag);
2055
            } else {
2056
                $logger->verbose("transferPackage: Max outstanding tarZip Requests");
2057
            }
2058
        }
7387 dpurdie 2059
        return 0;
2060
    }
2061
 
2062
 
6475 dpurdie 2063
    ###########################################################################
2064
    #   Transfer the package / symlink
1038 dpurdie 2065
    #
7389 dpurdie 2066
    $logger->logmsg("transferPackage: @_");
2067
    my $tzfsize = -s $tzpath; 
7387 dpurdie 2068
    if ($isS3Target) {
2069
        $cmdRv = transferPackageS3($tzdir, $tzfile, $pname, $pver);
2070
    } else {
2071
        $cmdRv = transferPackageSsh($tzdir, $tzfile, $sfile, $pname, $pver);
2072
    }
2073
 
2074
    #
7389 dpurdie 2075
    #   Display the size of the package (tarZipped)
7387 dpurdie 2076
    #       Diagnostic use
2077
    #
2078
    if ($conf->{txdetail}) {
7389 dpurdie 2079
        my $size = sprintf "%.3f", $tzfsize / 1024 / 1024 / 1024 ;
7387 dpurdie 2080
        my $duration = time - $startTime;
2081
        $logger->logmsg("transferPackage: Stats: $pname, $pver, $size Gb, $duration Secs");
2082
    }
2083
 
2084
    if ( $cmdRv == 0 ) {           
2085
        #
2086
        #   Mark has having been transferred in the current cycle
2087
        #
2088
        $RemotePkgList->{$pname}{$pver}{transferred} = 1;
2089
        $rv = 1;
2090
        $statistics{txCount}++;
2091
 
2092
    } else {
2093
        $logger->warn("transferPackage:Transfer Error: $pname, $pver, $?");
2094
    }
2095
 
2096
    LogTxError ($?);
2097
    return $rv;
2098
}
2099
 
2100
#-------------------------------------------------------------------------------
2101
# Function        : transferPackageSsh
2102
#
2103
# Description     : Transfer a package via an ssh connection 
2104
#
2105
# Inputs          : $tzdir  - Directory that contains the tarZip file
2106
#                   $tzname - Name of tarZip File
2107
#                   $sfile  - Full path to the source file
2108
#                   $pname  - Package Name
2109
#                   $pver   - Package Version 
2110
#
2111
# Returns         : Result Code
2112
#                   0   - Transfer OK
2113
#                   <0  - Skip transfer
2114
#                   >0  - Command error code
2115
#
2116
sub transferPackageSsh
2117
{
2118
    my ($tzdir, $tzname, $sfile, $pname, $pver) = @_;
6475 dpurdie 2119
    my $tgt_cmd;
2120
    my $ssh_cmd;
7387 dpurdie 2121
    my $cmdRv = 0;
2122
    my $tzfile = catfile($tzdir, $tzname);
2123
 
6475 dpurdie 2124
    if (-l $sfile) {
1038 dpurdie 2125
 
6475 dpurdie 2126
        #
2127
        #   Determine the value of the symlink
7387 dpurdie 2128
        #   Only support simple symlinks - that are in the same directory
6475 dpurdie 2129
        #
2130
        my $lver = readlink( $sfile );
2131
        if ( ! defined $lver ) {
2132
            $logger->warn("Can't resolve symlink: $pname, $pver");
7387 dpurdie 2133
            return -1;
6475 dpurdie 2134
        }
2135
 
2136
        if ( $lver =~ m ~/~ ) {
2137
            $logger->warn("Won't resolve symlink: $pname, $pver, $lver");
7387 dpurdie 2138
            return -1;
6475 dpurdie 2139
        }
2140
 
2141
        $tgt_cmd = "$conf->{'bindir'}/receive_symlink \"$pname\" \"$pver\" \"$lver\"";
2142
        $ssh_cmd = sshCmd($tgt_cmd);
2143
 
2144
    } else {
2145
        #
2146
        #   Create the process pipe to transfer the package
7387 dpurdie 2147
        #   Pipe the tarZip of the package through a ssh session to the target machine
2148
        #   cat $tzpath | ssh  ... "./receive_package pname pver"
6475 dpurdie 2149
        #
2150
        $tgt_cmd = "$conf->{'bindir'}/receive_package \"$pname\" \"$pver\"";
2151
        $ssh_cmd = sshCmd($tgt_cmd);
7387 dpurdie 2152
        $ssh_cmd .= " <$tzfile"
6475 dpurdie 2153
    }
2154
 
1038 dpurdie 2155
    $logger->verbose2("transferPackage:tgt_cmd:$tgt_cmd");
2156
    $logger->verbose2("transferPackage:ssh_cmd:$ssh_cmd");
2157
 
6776 dpurdie 2158
    unless ($conf->{'noTransfers'}) {
2159
        my $ph;
7387 dpurdie 2160
        open ($ph, "$ssh_cmd |");
6776 dpurdie 2161
        while ( <$ph> )
2162
        {
2163
            chomp;
2164
            $logger->verbose2("transferPackage:Data: $_");
2165
        }
2166
        close ($ph);
2167
        $cmdRv = $?;
2168
        $logger->verbose("transferPackage:End: $?");
1038 dpurdie 2169
    }
6148 dpurdie 2170
 
7387 dpurdie 2171
    return $cmdRv;
2172
}
2173
 
2174
#-------------------------------------------------------------------------------
2175
# Function        : transferPackageS3
2176
#
2177
# Description     : Transfer a package to an AWS S3 bucket
2178
#                   Requires that the package already be tarZip-ed
2179
#
2180
# Inputs          : $tzdir  - Directory that contains the tarZip file
2181
#                   $tzfile - Name of tarZip File
2182
#                   $pname  - Package Name
2183
#                   $pver   - Package Version 
2184
#
2185
# Returns         : Result Code
2186
#                   0   - Transfer OK
2187
#                   <0  - Skip transfer
2188
#                   >0  - Command error code
2189
#
2190
sub transferPackageS3
2191
{
2192
    my ($tzdir, $tzfile, $pname, $pver) = @_;
2193
    my $cmdRv = 0;
2194
 
6148 dpurdie 2195
    #
7387 dpurdie 2196
    #   Locate the file on the dpkgArchive tarZip store
2197
    #   
2198
    my $sfile = catfile($tzdir, $tzfile);
2199
    if (-l $sfile) {
2200
        $logger->warn("Will not transfer symlink: $pname, $pver");
2201
        return -1;
2202
    }
2203
 
6148 dpurdie 2204
    #
7387 dpurdie 2205
    #   Create a command to transfer the file to AWS use the cli tools
2206
    #   Note: Ive seen problem with this when used from Perth to AWS (Sydney)
2207
    #         If this is an issue use curl - see the savePkgToS3.sh for an implementation
2208
    #   
7460 dpurdie 2209
    my $s3_cmd = "aws --profile $conf->{'S3Profile'}";
2210
    $s3_cmd .= " --region $conf->{'S3Region'}" if (defined $conf->{'S3Region'});
2211
    $s3_cmd .= " s3 cp $sfile s3://$conf->{'S3Bucket'}/$tzfile";
7387 dpurdie 2212
    $logger->verbose2("transferPackage:s3_cmd:$s3_cmd");
2213
 
2214
    unless ($conf->{'noTransfers'}) {
6776 dpurdie 2215
        my $ph;
7387 dpurdie 2216
        open ($ph, "$s3_cmd |");
2217
        while ( <$ph> )
2218
        {
2219
            chomp;
2220
            $logger->verbose2("transferPackage:Data: $_");
2221
        }
2222
        close ($ph);
2223
        $cmdRv = $?;
2224
        $logger->verbose("transferPackage:End: $?");
6475 dpurdie 2225
    }
6148 dpurdie 2226
 
7387 dpurdie 2227
    return $cmdRv;
1038 dpurdie 2228
}
2229
 
7387 dpurdie 2230
 
1038 dpurdie 2231
#-------------------------------------------------------------------------------
2232
# Function        : deletePackage
2233
#
2234
# Description     : Delete specified package to target system
2235
#
2236
# Inputs          : $pname          - Name of the package
2237
#                   $pver           - Package version
7394 dpurdie 2238
#                   $mode           - 1 ForcedDelete 0: Tag for delayed Delete
1038 dpurdie 2239
#
7389 dpurdie 2240
# Returns         : true    - Package deleted
2241
#                   false   - Package not deleted
1038 dpurdie 2242
#
2243
sub deletePackage
2244
{
7394 dpurdie 2245
    my ($pname, $pver, $mode ) = @_;
1038 dpurdie 2246
    my $rv = 0;
6776 dpurdie 2247
    my $cmdRv = 0;
1042 dpurdie 2248
    $logger->logmsg("deletePackage: $pname, $pver");
1038 dpurdie 2249
 
7389 dpurdie 2250
    if ($isS3Target) {
7460 dpurdie 2251
        #
2252
        #   S3 Transfer is being used to store packages forever
2253
        #   Don't normally want to be able to delete such packages, so the default
2254
        #   is to prevent deletion of S3 transferred packages.
2255
        if ($conf->{'S3AllowDelete'}) {
1038 dpurdie 2256
 
7460 dpurdie 2257
            #   Create the process pipe to delete the package
2258
            my $tzfile = $pname . '__' . $pver . '.tgz';
2259
            my $s3_cmd = "aws --profile $conf->{'S3Profile'}";
2260
            $s3_cmd .= " --region $conf->{'S3Region'}" if (defined $conf->{'S3Region'});
2261
            $s3_cmd .=   " s3 rm s3://$conf->{'S3Bucket'}/$tzfile";
2262
            $logger->verbose2("deletePackage:s3_cmd:$s3_cmd");
1038 dpurdie 2263
 
7460 dpurdie 2264
            my $ph;
2265
            open ($ph, "$s3_cmd |");
2266
            while ( <$ph> )
2267
            {
2268
                chomp;
2269
                $logger->verbose2("deletePackage:Data: $_");
2270
            }
2271
            close ($ph);
2272
            $cmdRv = $?;
2273
 
2274
        } else {
2275
            $logger->warn("deletePackage: S3Deletion disabled: $pname, $pver");
6776 dpurdie 2276
        }
2277
 
7389 dpurdie 2278
    } else {
2279
        #
2280
        #   Create the process pipe to delete the package
2281
        #   ssh  ... "./delete_package ${rx_opts} \"$pname\" \"$pver\""
2282
        #
2283
        unless ($conf->{'noTransfers'}) {
2284
            my $ph;
7394 dpurdie 2285
            my $flags = $mode  ? '-T' : '';
7389 dpurdie 2286
            my $tgt_cmd = "$conf->{'bindir'}/delete_package $flags \"$pname\" \"$pver\"";
2287
            my $ssh_cmd = sshCmd($tgt_cmd);
2288
 
2289
            $logger->verbose2("deletePackage:tgt_cmd:$tgt_cmd");
2290
            $logger->verbose2("deletePackage:ssh_cmd:$ssh_cmd");
2291
 
2292
            open ($ph, "$ssh_cmd |");
2293
            while ( <$ph> )
2294
            {
2295
                chomp;
2296
                $logger->verbose2("deletePackage:Data: $_");
2297
            }
2298
            close ($ph);
2299
            $cmdRv = $?;
2300
        }
1038 dpurdie 2301
    }
6776 dpurdie 2302
 
7389 dpurdie 2303
    #
2304
    #   Common code
2305
    #
2306
    $logger->verbose("deletePackage:End: $cmdRv");
6776 dpurdie 2307
    if ( $cmdRv == 0 )
1038 dpurdie 2308
    {
2309
        $rv = 1;
5398 dpurdie 2310
        $statistics{delCount}++;
6148 dpurdie 2311
        delete $RemotePkgList->{$pname}{$pver};
1038 dpurdie 2312
    }
2313
    else
2314
    {
2315
        $logger->warn("deletePackage:Error: $pname, $pver, $?");
2316
    }
3515 dpurdie 2317
    LogTxError ($?);
1038 dpurdie 2318
    return $rv;
2319
}
2320
 
3515 dpurdie 2321
#-------------------------------------------------------------------------------
2322
# Function        : sshCmd
2323
#
2324
# Description     : Generate a ssh based command
2325
#
2326
# Inputs          : Target command
2327
#
2328
# Returns         : An shh command string
2329
#
2330
sub sshCmd
2331
{
2332
    my ($tgt_cmd) = @_;
3847 dpurdie 2333
    my $sshPort = '';
2334
    $sshPort = "-p $conf->{'sshport'}"
2335
        if ($conf->{'sshport'});
2336
 
2337
    return "ssh -o \"BatchMode yes\" -i $conf->{'identity'} ${sshPort} $conf->{'user'}\@$conf->{'hostname'} \"$tgt_cmd\" 2>&1";
3515 dpurdie 2338
}
1038 dpurdie 2339
 
3515 dpurdie 2340
 
1038 dpurdie 2341
#-------------------------------------------------------------------------------
1042 dpurdie 2342
# Function        : parsePkgList
2343
#
2344
# Description     : Parse one line from a pkgList
2345
#                   Lines are multiple item="data" items
6148 dpurdie 2346
#                       time="1497586865" GMT="Fri Jun 16 04:21:05 2017" pname="ERGissaccounts" pver="1.0.7169.mas"
6475 dpurdie 2347
#                       time="1497586865" GMT="Fri Jun 16 04:21:05 2017" pname="ERGissaccounts" pver="1.0.7169.mas" link="latest"
1042 dpurdie 2348
#
2349
# Inputs          : $line                   - Line of data
2350
#                   $hashp                  - Ref to hash to populate
2351
#
2352
# Returns         : A hash of data items
2353
#
2354
sub parsePkgList
2355
{
2356
    my ($line, $hashp) = @_;
2357
    my $rv;
2358
 
2359
    while ( $line =~ m~\s*(.+?)="(.+?)"~ )
2360
    {
2361
        $rv->{$1} = $2;
2362
        $line = $';
2363
    }
2364
#Utils::DebugDumpData ("parsePkgList", $rv);
2365
 
2366
    my $pname = $rv->{pname};
2367
    my $pver =  $rv->{pver};
2368
    return undef unless ( $pname && $pver );
2369
 
2370
    delete $rv->{pname};
2371
    delete $rv->{pver};
2372
    delete $rv->{GMT};
2373
 
2374
    $hashp->{$pname}{$pver} = $rv;
2375
    return $hashp;
2376
}
2377
 
5398 dpurdie 2378
#-------------------------------------------------------------------------------
5404 dpurdie 2379
# Function        : parsePkgMetaData
2380
#
2381
# Description     : Parse one line of meta data from a pkgList
2382
#                   Lines are multiple item="data" items
2383
#
2384
# Inputs          : $line                   - Line of data
2385
#                   $hashp                  - Ref to hash to populate
2386
#
2387
# Returns         : Nothing
2388
#
2389
sub parsePkgMetaData
2390
{
2391
    my ($line, $hashp) = @_;
2392
 
2393
    if ( $line =~ m~\s+(.+?)="(.+?)"~ )
2394
    {
2395
        $hashp->{$1} = $2;
2396
        $statistics{'Target.' . $1} = $2;
2397
        $line = $';
2398
 
2399
        $logger->verbose2("parsePkgMetaData: $1 = $2");
2400
    }
2401
}
2402
 
6320 dpurdie 2403
#-------------------------------------------------------------------------------
2404
# Function        : parseBlatBinData
2405
#
2406
# Description     : Parse one line of Blat Bin data from a pkgList
2407
#                   Lines are of the form:
2408
#                   BlatBin MD5="dbc4507f4db5b41f7358b28bce65a15d" file="ddp-gtar"
2409
#
2410
# Inputs          : $line                   - Line of data
2411
#                   $hashp                  - Ref to hash to populate
2412
#
2413
# Returns         : Nothing
2414
#
2415
sub parseBlatBinData
2416
{
2417
    my ($line, $hashp) = @_;
5404 dpurdie 2418
 
6320 dpurdie 2419
    my $rv;
2420
    $line =~ s~^\S+~~;
2421
    while ( $line =~ m~\s*(.+?)="(.+?)"~ )
2422
    {
2423
        $rv->{$1} = $2;
2424
        $line = $';
2425
    }
2426
#Utils::DebugDumpData ("parseBlatBinData", $rv);
2427
 
2428
    my $fname = $rv->{file};
2429
    my $md5 =  $rv->{MD5};
2430
    return undef unless ( $fname && $md5 );
2431
 
2432
    $logger->verbose2("parseBlatBinData: $fname : $md5");
2433
    $hashp->{$fname} = $md5;
2434
}
2435
 
5404 dpurdie 2436
#-------------------------------------------------------------------------------
5398 dpurdie 2437
# Function        : resetDailyStatistics 
2438
#
2439
# Description     : Called periodically to reset the daily statistics
2440
#
2441
# Inputs          : $time       - Current time
2442
#
2443
# Returns         : 
2444
#
2445
sub resetDailyStatistics
2446
{
2447
    my ($time) = @_;
1042 dpurdie 2448
 
5398 dpurdie 2449
    #
2450
    #   Detect a new day
2451
    #
2452
    my $today = (localtime($time))[7];
2453
    if ($yday != $today)
2454
    {
2455
        $yday = $today;
2456
        $logger->logmsg('Resetting daily statistics' );
2457
 
5404 dpurdie 2458
        # Note: Must match @recoverTags in readStatistics
5398 dpurdie 2459
        $statistics{dayStart} = $time;
2460
        $statistics{txCount} = 0;
2461
        $statistics{delCount} = 0;
2462
        $statistics{staleTags} = 0;
2463
        $statistics{linkErrors} = 0;
2464
    }
2465
}
2466
 
1042 dpurdie 2467
#-------------------------------------------------------------------------------
5404 dpurdie 2468
# Function        : readStatistics 
2469
#
2470
# Description     : Read in the last set of stats
2471
#                   Used after a restart to recover daily statistics
2472
#
2473
# Inputs          : 
2474
#
2475
# Returns         : 
2476
#
2477
sub readStatistics
2478
{
2479
    my @recoverTags = qw(dayStart txCount delCount staleTags linkErrors);
2480
 
2481
    if ($conf->{'statsfile'} and -f $conf->{'statsfile'})
2482
    {
2483
        if (open my $fh, $conf->{'statsfile'})
2484
        {
2485
            while (<$fh>)
2486
            {
2487
                m~(.*):(.*)~;
2488
                if ( grep( /^$1$/, @recoverTags ) ) 
2489
                {
2490
                    $statistics{$1} = $2;
6475 dpurdie 2491
                    $logger->verbose("readStatistics $1, $2");
5404 dpurdie 2492
                }
2493
            }
2494
            close $fh;
2495
            $yday = (localtime($statistics{dayStart}))[7];
2496
        }
2497
    }
2498
}
2499
 
2500
 
2501
#-------------------------------------------------------------------------------
5398 dpurdie 2502
# Function        : periodicStatistics 
2503
#
2504
# Description     : Called on a regular basis to write out statistics
2505
#                   Used to feed information into Nagios
2506
#                   
2507
#                   This function is called via an alarm and may be outside the normal
2508
#                   processing loop. Don't make assumptions on the value of $now
2509
#
2510
# Inputs          : 
2511
#
2512
# Returns         : 
2513
#
2514
sub periodicStatistics
2515
{
2516
    #
2517
    #   A few local stats
2518
    #
2519
    $statistics{SeqNum}++;
2520
    $statistics{timeStamp} = time();
2521
    $statistics{upTime} = $statistics{timeStamp} - $startTime;
7460 dpurdie 2522
    $statistics{wedged} = Utils::isWedged($conf);
7397 dpurdie 2523
    $statistics{state} = $statistics{wedged} ? 'Wedged' : $statistics{state}; 
2524
 
5398 dpurdie 2525
 
2526
    #   Reset daily accumulations - on first use each day
2527
    resetDailyStatistics($statistics{timeStamp});
2528
 
2529
    #
2530
    #   Write statistics to a file
2531
    #       Write to a tmp file, then rename.
2532
    #       Attempt to make the operation atomic - so that the file consumer
2533
    #       doesn't get a badly formed file.
2534
    #   
2535
    if ($conf->{'statsfiletmp'})
2536
    {
2537
        my $fh;
2538
        unless (open ($fh, '>', $conf->{'statsfiletmp'}))
2539
        {
2540
            $fh = undef;
2541
            $logger->warn("Cannot create temp stats file: $!");
2542
        }
2543
        else
2544
        {
5404 dpurdie 2545
            foreach my $key ( sort { lc($a) cmp lc($b) } keys %statistics)
5398 dpurdie 2546
            {
2547
                print $fh $key . ':' . $statistics{$key} . "\n";
2548
                $logger->verbose2('Statistics:'. $key . ':' . $statistics{$key});
2549
            }
2550
            close $fh;
2551
 
2552
            # Rename temp to real file
2553
            rename  $conf->{'statsfiletmp'},  $conf->{'statsfile'} ;
2554
        }
2555
    }
2556
}
2557
 
2558
#-------------------------------------------------------------------------------
1038 dpurdie 2559
# Function        : sighandlers
2560
#
2561
# Description     : Install signal handlers
2562
#
2563
# Inputs          : $conf           - System config
2564
#
2565
# Returns         : Nothing
2566
#
2567
sub sighandlers
2568
{
5398 dpurdie 2569
    $SIG{TERM} = sub {
2570
        # On shutdown
2571
        $logger->logmsg('Received SIGTERM. Shutting down....' );
2572
        unlink $conf->{'pidfile'} if (-f $conf->{'pidfile'});
2573
        exit 0;
2574
    };
1038 dpurdie 2575
 
5398 dpurdie 2576
    $SIG{HUP} = sub {
2577
        # On logrotate
2578
        $logger->logmsg('Received SIGHUP.');
2579
        $logger->rotatelog();
2580
    };
1038 dpurdie 2581
 
5398 dpurdie 2582
    $SIG{USR1} = sub {
2583
        # On Force Archive Sync
2584
        $logger->logmsg('Received SIGUSR1.');
1046 dpurdie 2585
        $lastReleaseScan = 0;
6779 dpurdie 2586
        $lastTagListUpdate = 0;
6776 dpurdie 2587
        $lastRmConfRead = 0;
5398 dpurdie 2588
    };
1038 dpurdie 2589
 
7387 dpurdie 2590
    alarm 60 unless $conf->{debug};
5398 dpurdie 2591
    $SIG{ALRM} = sub {
2592
        # On Dump Statistics
2593
        $logger->verbose2('Received SIGUSR2.');
2594
        periodicStatistics();
2595
        alarm 60;
2596
    };
2597
 
1038 dpurdie 2598
    $SIG{__WARN__} = sub { $logger->warn("@_") };
2599
    $SIG{__DIE__} = sub { $logger->err("@_") };
2600
}
2601
 
2602
#-------------------------------------------------------------------------------
3515 dpurdie 2603
# Function        : LogTxError
2604
#
2605
# Description     : Detect restoration of communication and log such
2606
#                   Don't log failures as the user will do that
2607
#
2608
# Inputs          : $state                  - 0 - All is well
2609
#                                           !0  - Error
2610
#
2611
# Returns         : Nothing
2612
#
2613
sub LogTxError
2614
{
2615
    my ($state) = $@;
2616
    if ( $state )
2617
    {
5398 dpurdie 2618
        $statistics{linkErrors}++ unless($comError);
3515 dpurdie 2619
        $comError++;
5398 dpurdie 2620
        $statistics{state} = 'No Communication';
3515 dpurdie 2621
    }
2622
    elsif ( $comError )
2623
    {
2624
        $logger->warn("Communication Restored");
2625
        $comError = 0;
5398 dpurdie 2626
        $statistics{state} = 'OK';
3515 dpurdie 2627
    }
2628
}
2629
 
2630
 
2631
#-------------------------------------------------------------------------------
1038 dpurdie 2632
# Function        : Error, Verbose, Warning
2633
#
2634
# Description     : Support for JatsRmApi
2635
#
2636
# Inputs          : Message
2637
#
2638
# Returns         : Nothing
2639
#
2640
sub Error
2641
{
2642
    $logger->err("@_");
2643
}
2644
 
2645
sub Verbose
2646
{
1042 dpurdie 2647
    $logger->verbose2("@_");
1038 dpurdie 2648
}
2649
 
2650
sub Warning
2651
{
2652
    $logger->warn("@_");
2653
}
2654