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" );
|