Subversion Repositories sysadmin_scripts

Rev

Rev 12 | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 12 Rev 13
Line 1... Line 1...
1
#! /usr/bin/env perl
1
#! /usr/bin/env perl
2
 
2
 
3
# archiveDirectorys.pl
3
# archiveDirectories.pl
4
# Author: R. W. Rodolico
4
# Author: R. W. Rodolico
5
# Date: 20180603
5
# Date: 20180603
6
# Copyright: 2018, Vanduzen Enterprises, Dallas TX
6
# Copyright: 2018, Vanduzen Enterprises, Dallas TX
7
 
7
 
8
# Script designed to be run from a cron job, which checks if any directorys
8
# Script designed to be run from a cron job, which checks if any directories
9
# are ready to be archived. A directory is defined as a directory under
9
# are ready to be archived. A directory is defined as a directory under
10
# the root of $localDirectoryDirectory.
10
# the root of $config{'local root dir'}.
11
 
11
 
12
# If found, all directories are moved into the staging area and 
12
# If found, all directories are moved into the staging area and 
13
# an md5 checksum is calculated for the entire tree.
13
# an md5 checksum is calculated for the entire tree.
14
# After all directorys are moved, a second process looks in the staging
14
# After all directories are moved, a second process looks in the staging
15
# area and copies the files (using rsync for reliability) into the staging
15
# area and copies the files (using rsync for reliability) into the staging
16
# area of $targetServer. When a directory has been copied, a checksum is
16
# area of $config{'target server'}. When a directory has been copied, a checksum is
17
# calculated on the remote copy and compared to the checksum calculated
17
# calculated on the remote copy and compared to the checksum calculated
18
# in the first stage and, if it passes, the directory is then moved to the 
18
# in the first stage and, if it passes, the directory is then moved to the 
19
# $targetDirectoryDirectory.
19
# $config{'target final directory'}.
20
# After the copy and move, the directory and its MD5 sum file are moved
20
# After the copy and move, the directory and its MD5 sum file are moved
21
# to the $trashDirectory (which is cleaned on the next invocation of
21
# to the $config{'local trash dir'} (which is cleaned on the next invocation of
22
# the script).
22
# the script).
23
 
23
 
24
# Script does NOT handle the situation where directorys are being moved
24
# Script does NOT handle the situation where directories are being moved
25
# while the script is running, so the script should be run at a time
25
# while the script is running, so the script should be run at a time
26
# when there is no other activity on the server.
26
# when there is no other activity on the server.
27
#
27
#
28
# Version: 1.0
28
# Version: 1.0
29
 
29
 
30
use warnings;
30
use warnings;
31
use strict;
31
use strict;
32
use Cwd qw();
32
use Cwd qw();
33
use File::Copy qw(move);
33
use File::Copy qw(move);
34
use File::Basename;
34
use File::Basename;
-
 
35
use File::stat;
35
 
36
 
-
 
37
my $DEBUG = 5;
-
 
38
 
-
 
39
my %config = (
36
# location where directorys are put by end users
40
   # location where directories are put by end users   
37
my $localDirectoryDirectory = '/home/samba/transfers/denver_to_dallas/Archive_to_DaVinci';
41
   'local root dir' => '/home/rodolico/scripts/sysadmin_scripts/archiveProjects/temp/ArchiveProjects',
38
# location where directories are moved while processing
42
   # location where directories are moved while processing
39
my $rootWorkDirectory = '/home/transfer_work_area';
43
   'local work dir' => '/home/rodolico/scripts/sysadmin_scripts/archiveProjects/temp/transfer_area',
40
# location where directories are moved when job is completed
44
   # location where directories are moved when job is completed
41
my $trashDirectory = "$localDirectoryDirectory/.Trash";
45
   'local trash dir' => "/home/rodolico/scripts/sysadmin_scripts/archiveProjects/temp/Trash",
42
# location where directories are moved while being transferred
46
   # location where directories are moved while being transferred
43
my $stagingArea = "$localDirectoryDirectory/.Staging";
47
   'local staging area' => '/home/rodolico/scripts/sysadmin_scripts/archiveProjects/temp/Staging',
-
 
48
 
44
# target server name/ip. Must be accessible via ssh with no password
49
   # target server name/ip. Must be accessible via ssh with no password
45
my $targetServer = 'davinci';
50
   'target server' => 'davinci',
46
# location on target server where directories are placed while copying
51
   # location on target server where directories are placed while copying
47
my $targetStagingArea = '/home/samba/archives/fromDenver/.Staging/';
52
   'target staging area' => '/home/samba/archives/fromDenver/.Staging/',
48
# location on target server where directories are finally put
53
   # location on target server where directories are finally put
49
my $targetDirectoryDirectory = '/home/samba/archives/fromDenver/';
54
   'target final directory' => '/home/samba/archives/fromDenver/',
-
 
55
 
50
# suffix of md5 of directories
56
   # suffix of md5 of directories
51
my $md5suffix = '.md5sum';
57
   'md5 suffix' => 'md5sum',
-
 
58
   # suffix of filename to create showing actions
-
 
59
   'log suffix' => 'log',
-
 
60
   # suffix of error log
-
 
61
   'error suffix' => 'err',
-
 
62
   # how long a directory must be undisturbed before it is ready to work on
-
 
63
   'quiesent seconds' => 60*5, # five minutes
-
 
64
   # how long to leave stuff in the trash directory. 0 indicates never do it.
-
 
65
   'trash cleanup' => 86400*7, # 7 days
-
 
66
);
52
 
67
 
53
my @DirectorysToMove;
68
my @DirectoriesToMove;
-
 
69
 
-
 
70
# simply read the entire fiel into a string
-
 
71
sub slurpFile {
-
 
72
   my $filename = shift;
-
 
73
   return '' unless -e $filename;
-
 
74
   open TEMP, "<$filename" or die "could not read $filename: $!\n";
-
 
75
   my @contents = <TEMP>;
-
 
76
   close TEMP;
-
 
77
   return join( '', @contents );
-
 
78
}
-
 
79
 
-
 
80
# print a value to a file
-
 
81
sub writeData {
-
 
82
   my $filename = shift;
-
 
83
   open TEMP, ">$filename" or die "could not write to $filename: $!\n";
-
 
84
   print TEMP join( '', @_ );
-
 
85
   close TEMP;
-
 
86
}
54
 
87
 
55
# look in the directorys to move directory and see if there is anything 
88
# look in the directories to move directory and see if there is anything 
-
 
89
# new in there. If so, check MD5 Sum file (create if necessary) and ensure
56
# new in there.
90
# we have waited long enough and the sums match
57
sub getDirectorys {
91
sub getDirectories {
58
   my $rootDir = shift;
92
   my $rootDir = shift;
-
 
93
   print "In getDirectories with dir of $rootDir\n" if $DEBUG;
59
   opendir( my $dh, $rootDir ) or die "Could not open directory $rootDir: $!\n";
94
   opendir( my $dh, $rootDir ) or die "Could not open directory $rootDir: $!\n";
60
   my @dirs = grep { ! /^\./ && -d "$rootDir/$_" } readdir( $dh );
95
   my @dirs = grep { ! /^\./ && -d "$rootDir/$_" } readdir( $dh );
-
 
96
   closedir ( $dh );
-
 
97
   print "Directories Found\n" . join( "\n", @dirs ) . "\n" if $DEBUG > 1;
-
 
98
   my @dirsToMove;
-
 
99
   foreach my $thisDir ( @dirs ) {
-
 
100
      my $fullyQualified = "$rootDir/$thisDir";
-
 
101
      my $md5 = calcMD5( $fullyQualified );
-
 
102
      print "\tFound Dir $fullyQualified with MD5 of $md5\n" if $DEBUG > 2;
-
 
103
      # let's look for the md5 checksum file and compare if it exist
-
 
104
      my $md5Name = "$fullyQualified.$config{'md5 suffix'}";
-
 
105
      if ( -e $md5Name ) {
-
 
106
         # find out when it was last written to
-
 
107
         my $lastModification = stat( $md5Name );
-
 
108
         $lastModification = $$lastModification[9];
-
 
109
         my $howOld = time - $lastModification;
-
 
110
         print "\tFound existing MD5 file $md5Name written to at $lastModification, or $howOld seconds ago\n" if $DEBUG > 3;
-
 
111
         # and blow it off if it is too recent
-
 
112
         if ( $howOld < $config{'quiesent seconds'} ) {
-
 
113
            print "\t\tBlowing it off because $howOld is less than $config{'quiesent seconds'}\n" if $DEBUG > 4;
-
 
114
            next;
-
 
115
         }
-
 
116
         my $oldMD5 = &slurpFile( $md5Name );
-
 
117
         if ( $md5 eq $oldMD5 ) {
-
 
118
            print "\t\tAdding, md5 not changed, $md5 same as $oldMD5\n" if $DEBUG > 4;
-
 
119
            push @dirsToMove, $thisDir;
-
 
120
         } else {
-
 
121
            print "\t\tWaiting, md5 changed, $md5 and $oldMD5\n" if $DEBUG > 4;
-
 
122
            # overwrite if the checksum has changed
-
 
123
            &writeData( $md5Name, $md5 ) if $md5 ne &slurpFile( $md5Name );
-
 
124
         }
-
 
125
      } else { # doesn't exist, so create it
-
 
126
         print "\t\tCreating MD5 File $md5Name with value $md5\n" if $DEBUG > 4;
-
 
127
         &writeData( $md5Name, $md5 );
-
 
128
      }
-
 
129
   } # foreach
61
   return @dirs;
130
   return @dirsToMove;
62
}
131
}
63
 
132
 
64
# calculate the checksum of a directory by
133
# calculate the checksum of a directory by
65
# 1. calculate checksum of each individual file in the entire tree
134
# 1. calculate checksum of each individual file in the entire tree
66
# 2. Grab the first column, which is the checksum
135
# 2. Grab the first column, which is the checksum
Line 78... Line 147...
78
}
147
}
79
 
148
 
80
# moves directory to staging area and puts the md5 sum into a file
149
# moves directory to staging area and puts the md5 sum into a file
81
# with the same name, but a .md5sum suffix
150
# with the same name, but a .md5sum suffix
82
sub moveToStaging {
151
sub moveToStaging {
83
   my ( $directory, $stagingArea, $md5 ) = @_;
152
   my ( $directory, $fullPath, $staging ) = @_;
-
 
153
   # and let's get the md5 file name also
-
 
154
   my $md5File = $fullPath . ".$config{'md5 suffix'}";
84
   mkdir $stagingArea unless -d $stagingArea;
155
   mkdir $staging unless -d $staging;
85
   move( "$localDirectoryDirectory/$directory", "$stagingArea/$directory" );
156
   return 'Directory already exists in staging' if -e "$staging/$directory";
86
   my $md5File = "$stagingArea/$directory" . $md5suffix;
157
   move( $fullPath, "$staging/$directory" ) or die "Error moving $fullPath to $staging/$directory: $!\n";
87
   open DATA,">$md5File" or die "Could not create md5sum file [$md5File]: $!\n";
158
   move( $md5File, $staging ) or die "Error moving $md5File to $staging: $!\n";
88
   print DATA "$md5\n";
-
 
89
   close DATA;
-
 
90
   return;
159
   return '';
91
}
160
}
92
   
161
   
93
# verifies the directory is correct on the server by comparing the checksums
162
# verifies the directory is correct on the server by comparing the checksums
94
# calculated locally and remote server. If valid, moves it into the final
163
# calculated locally and remote server. If valid, moves it into the final
95
# location on the remote server
164
# location on the remote server
Line 112... Line 181...
112
}
181
}
113
 
182
 
114
# reads the checksum file
183
# reads the checksum file
115
sub getCheckSum {
184
sub getCheckSum {
116
   my ( $directory, $staging )  = @_;
185
   my ( $directory, $staging )  = @_;
117
   $directory .= $md5suffix;
186
   $directory .= $config{'md5 suffix'};
118
   if ( open DATA, "<$staging/$directory" ) {
187
   if ( open DATA, "<$staging/$directory" ) {
119
      my $cksum = <DATA>;
188
      my $cksum = <DATA>;
120
      chomp $cksum;
189
      chomp $cksum;
121
      close DATA;
190
      close DATA;
122
      return $cksum;
191
      return $cksum;
Line 125... Line 194...
125
   return '';
194
   return '';
126
}
195
}
127
   
196
   
128
# simple little logger that records some information   
197
# simple little logger that records some information   
129
sub logit {
198
sub logit {
130
   my $message = shift;
199
   my $logfile = shift;
131
   my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
200
   my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
132
   my $now = sprintf( "%04d-%02d-%02d %02d:%-2d:%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec );
201
   my $now = sprintf( "%04d-%02d-%02d %02d:%-2d:%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec );
133
   open LOG, ">>/tmp/archiveDirectorys.log" or die "could not write to archiveDirectorys.log: $!\n";
202
   open LOG, ">>$logfile" or die "could not write to $logfile: $!\n";
-
 
203
   while ( my $message = shift ) {
134
   print LOG "$now\t$message\n";   
204
      print LOG "$now\t$message\n";
-
 
205
   }
135
   close LOG;
206
   close LOG;
136
}
207
}
137
   
208
   
138
# simply remove everything from the trash directory
209
# simply remove everything from the trash directory
139
sub cleanTrash {
210
sub cleanTrash {
140
   my $trashDir = shift;
211
   my ( $trashDir, $age ) = @_;
-
 
212
   `mkdir -p $trashDir` unless -d $trashDir;
141
   `rm -fR $trashDir/*`;
213
   `rm -fR $trashDir/*`;
142
}
214
}
143
 
215
 
-
 
216
unless ( -d $config{'local root dir'} ) {
-
 
217
   `mkdir -p $config{'local root dir'}`;
-
 
218
   `chmod 777 $config{'local root dir'}`;
144
 
219
}
-
 
220
# clean the trash if $config{ 'trash cleanup' } is non-zero
145
#&cleanTrash( $trashDirectory ) if &getDirectorysToMove( $trashDirectory );
221
&cleanTrash( $config{'local trash dir'}, $config{ 'trash cleanup' } ) if $config{ 'trash cleanup' };
146
   
-
 
147
   
222
   
148
# first, check and see if we have any directorys we need to move
223
# Check if we have any directories which are ready to be moved.
149
@DirectorysToMove = &getDirectorys( $localDirectoryDirectory );
224
@DirectoriesToMove = &getDirectories( $config{'local root dir'} );
-
 
225
 
-
 
226
print "Processing\n\t" . join( "\n\t", @DirectoriesToMove ) . "\n";
150
 
227
 
151
foreach my $directory ( @DirectorysToMove ) {
228
foreach my $directory ( @DirectoriesToMove ) {
152
   my $md5 = &calcMD5( "$localDirectoryDirectory/$directory" );
229
   my $fullPath = $config{'local root dir'} . "/$directory";
-
 
230
   my $logFile = "$fullPath.$config{'log suffix'}";
-
 
231
   my $errorFile = "$fullPath.$config{'error suffix'}";
-
 
232
   print "Path for $directory is $fullPath\n\tLog File is $logFile\n\tError file is $errorFile\n" if $DEBUG > 3;
-
 
233
   if ( -e $errorFile ) {
-
 
234
      &logit( $logFile, "Aborting because we have a pre-existing error" );
-
 
235
      print "\tAborting because we have a pre-existing error\n" if $DEBUG > 3;
-
 
236
      next;
-
 
237
   }
153
   &logit( "New Directory $md5\t$directory" );
238
   &logit( $logFile, "Processing $directory" );
154
   &moveToStaging( $directory, $stagingArea, $md5 );
239
   my $error = &moveToStaging( $directory, $fullPath, $config{'local staging area'} );
-
 
240
   if ( ! $error ) {
-
 
241
      print "\tMoved to $config{'local staging area'}\n" if $DEBUG > 3;
-
 
242
      &logit( $logFile, "Successfully moved to $config{'local staging area'}" );
-
 
243
   } else {
-
 
244
      &logit( $logFile, "Error, move aborted" );
-
 
245
      &logit( $errorFile, $error );
-
 
246
   }
155
}
247
}
156
 
248
 
157
# done with that, now we need to see if there is anything in the staging area
249
# done with that, now we need to see if there is anything in the staging area
158
# that needs to be sent to the remote server
250
# that needs to be sent to the remote server
159
opendir( my $dh, $stagingArea ) or die "Could not read $stagingArea: $!\n";
251
opendir( my $dh, $config{'local staging area'} ) or die "Could not read $config{'local staging area'}: $!\n";
160
my @directories;
252
my @directories;
161
my @toMove = grep { /$md5suffix$/ } readdir( $dh );
253
my @toMove = grep { /$config{'md5 suffix'}$/ } readdir( $dh );
-
 
254
my $targetPath = "$config{'target server'}:$config{'target staging area'}/";
-
 
255
print "Copying the following to $targetPath\n\t" . join ("\n\t", @toMove ) . "\n";
-
 
256
die;
162
foreach my $directory ( @toMove ) {
257
foreach my $directory ( @toMove ) {
163
   $directory =~ m/^(.*)\.md5sum/;
258
   $directory =~ m/^(.*)\.md5sum/;
164
   $directory = $1;
259
   $directory = $1;
165
   my $md5sum = &getCheckSum( $directory, $stagingArea );
260
   my $md5sum = &getCheckSum( $directory, $config{'local staging area'} );
166
   next unless $md5sum;
261
   next unless $md5sum;
167
   my $rsync = "rsync -av '$stagingArea/$directory' $targetServer:$targetStagingArea/ > /tmp/lastrsync.log";
262
   my $rsync = "rsync -av '$config{'local staging area'}/$directory' $config{'target server'}:$config{'target staging area'}/ > /tmp/lastrsync.log";
168
   &logit( $rsync );
263
   &logit( $rsync );
169
   if ( system ( $rsync ) == 0 ) { # we succeeded
264
   if ( system ( $rsync ) == 0 ) { # we succeeded
170
      if ( &validateTarget( $targetServer, $targetStagingArea, $targetDirectoryDirectory, $directory, $md5sum ) ) {
265
      if ( &validateTarget( $config{'target server'}, $config{'target staging area'}, $config{'target final directory'}, $directory, $md5sum ) ) {
171
         `mkdir -p $trashDirectory` unless -d $trashDirectory;
266
         `mkdir -p $config{'local trash dir'}` unless -d $config{'local trash dir'};
172
         move( "$stagingArea/$directory", "$trashDirectory/$directory" );
267
         move( "$config{'local staging area'}/$directory", "$config{'local trash dir'}/$directory" );
173
         $directory .= $md5suffix;
268
         $directory .= $config{'md5 suffix'};
174
         move( "$stagingArea/$directory", "$trashDirectory/$directory" );
269
         move( "$config{'local staging area'}/$directory", "$config{'local trash dir'}/$directory" );
175
         &logit( "Successfully moved directory $directory to $targetServer" );
270
         &logit( "Successfully moved directory $directory to $config{'target server'}" );
176
      } else {
271
      } else {
177
         &logit( "Unable to validate target for $directory" );
272
         &logit( "Unable to validate target for $directory" );
178
      }
273
      }
179
   } else {
274
   } else {
180
      &logit( "Unknown error attempting to rsync $directory" );
275
      &logit( "Unknown error attempting to rsync $directory" );