Subversion Repositories sysadmin_scripts

Rev

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

Rev 170 Rev 171
Line 1... Line 1...
1
#! /usr/bin/env perl
1
#! /usr/bin/env perl
2
 
2
 
3
use strict;
3
use strict;
4
use warnings;
4
use warnings;
5
 
5
 
6
use YAML::Tiny; # pkg install p5-YAML-Tiny-1.74
-
 
7
 
6
 
8
BEGIN {
7
BEGIN {
9
   use FindBin;
8
   use FindBin;
10
   use File::Spec;
9
   use File::Spec;
11
   # use libraries from the directory this script is in
10
   # use libraries from the directory this script is in
12
   use Cwd 'abs_path';
11
   use Cwd 'abs_path';
13
   use File::Basename;
12
   use File::Basename;
14
   use lib dirname( abs_path( __FILE__ ) );
13
   use lib dirname( abs_path( __FILE__ ) );
15
}
14
}
16
 
15
 
-
 
16
use YAML::Tiny; # pkg install p5-YAML-Tiny-1.74
-
 
17
use Data::Dumper;
-
 
18
use Email::Simple; # cpan install Email::Simple
-
 
19
 
17
my $cwd = $FindBin::RealBin;
20
my $cwd = $FindBin::RealBin;
18
my $configFileName = $cwd . '/sync.yaml';
21
my $configFileName = $cwd . '/sync.yaml';
19
my $replicateScript = $cwd . '/replicate';
22
my $replicateScript = $cwd . '/replicate';
20
 
23
 
21
my $TESTING=0;
-
 
22
 
-
 
23
my $configuration;
24
my $configuration;
24
   
25
   
25
use Data::Dumper;
-
 
26
use Email::Simple; # cpan install Email::Simple
-
 
27
 
26
 
28
# load Configuration File
27
# load Configuration File
29
# read the config file and return it
28
# read the config file and return it
30
sub readConfig {
29
sub readConfig {
31
   my $filename = shift;
30
   my $filename = shift;
Line 68... Line 67...
68
   
67
   
69
# grabs the encryption key from the remote server, and uses it to unlock the 
68
# grabs the encryption key from the remote server, and uses it to unlock the 
70
# datasets, then mount the drives.
69
# datasets, then mount the drives.
71
# a return of '' is success, anything else is an error
70
# a return of '' is success, anything else is an error
72
sub mountDrives {
71
sub mountDrives {
73
   my $config = shift;
72
   my $configuration = shift;
-
 
73
   return (0, 'No encrypted target found' ) unless defined( $configuration->{'target'}->{'encryptionKeyPath'} ) && $configuration->{'target'}->{'encryptionKeyPath'};
74
   # try to grab the file from the remote machine
74
   # try to grab the file from the remote machine
75
   &runCommand( "scp $config->{remoteMachine}->{ip}:$config->{remoteMachine}->{encryptionKeyPath} $config->{localMachine}->{encryptionKeyPath}" );
75
   &runCommand( "scp $configuration->{remoteMachine}->{ip}:$configuration->{remoteMachine}->{encryptionKeyPath} $configuration->{localMachine}->{encryptionKeyPath}" );
76
   # If we do not have the encryption key, we need to abort
76
   # If we do not have the encryption key, we need to abort
77
   return "Could not copy file $config->{remoteMachine}->{ip}:$config->{remoteMachine}->{encryptionKeyPath}, aborting" 
77
   return "Could not copy file $configuration->{remoteMachine}->{ip}:$configuration->{remoteMachine}->{encryptionKeyPath}, aborting" 
78
      unless -f $config->{'localMachine'}->{'encryptionKeyPath'};
78
      unless -f $configuration->{'target'}->{'encryptionKeyPath'};
79
   my $error = '';
79
   my $error = '';
80
   my $output = '';
80
   my $output = '';
81
   # load the key into zfs and unlock all volumes
81
   # load the key into zfs and unlock all volumes
82
   ($error,$output) = &runCommand( "zfs load-key -a" );
82
   ($error,$output) = &runCommand( "zfs load-key -a" );
83
   # finally, remount all of the zfs shares which need the key
83
   # finally, remount all of the zfs shares which need the key
84
   ($error,$output) = &runCommand( "zfs mount -a" ) unless $error;
84
   ($error,$output) = &runCommand( "zfs mount -a" ) unless $error;
85
   # if we succeeded, we want to shred the keyfile
85
   # if we succeeded, we want to shred the keyfile
86
   &shredFile( $config->{localMachine}->{encryptionKeyPath} ) if -f $config->{localMachine}->{encryptionKeyPath};
86
   &shredFile( $configuration->{localMachine}->{encryptionKeyPath} ) if -f $configuration->{localMachine}->{encryptionKeyPath};
87
   return $error;
87
   return $error;
88
}
88
}
89
 
89
 
90
# a very simple mailer, using Email::Simple to just get status messages out
90
# a very simple mailer, using Email::Simple to just get status messages out
91
sub sendMail {
91
sub sendMail {
92
   my ($message, $config, $subject ) = @_;
92
   my ($message, $configuration, $subject ) = @_;
93
   $config->{'email'}->{'notify'} = 'root' unless $config->{'email'}->{'notify'};
93
   $configuration->{'email'}->{'notify'} = 'root' unless $configuration->{'email'}->{'notify'};
94
   die "No message in outgoing message\n" unless $message;
94
   die "No message in outgoing message\n" unless $message;
95
   my $email = Email::Simple->create(
95
   my $email = Email::Simple->create(
96
      header => [
96
      header => [
97
         To     => $config->{'email'}->{'notify'},
97
         To     => $configuration->{'email'}->{'notify'},
98
         Subject=> $config->{'email'}->{'subject'} . ( $subject ? " - $subject" : '' ),
98
         Subject=> $configuration->{'email'}->{'subject'} . ( $subject ? " - $subject" : '' ),
99
         From   => $config->{'email'}->{'from'}
99
         From   => $configuration->{'email'}->{'from'}
100
      ],
100
      ],
101
      body => $message
101
      body => $message
102
   );
102
   );
103
   $message = $email->as_string;
103
   $message = $email->as_string;
-
 
104
   if ( $configuration->{'testing'} > 1 ) {
-
 
105
      print "$message\n";
-
 
106
   } else {
104
   `echo '$message' | sendmail $config->{'email'}->{'notify'}`;
107
      `echo '$message' | sendmail $configuration->{'email'}->{'notify'}`;
-
 
108
   }
105
}
109
}
106
 
110
 
107
# checks to see if we should be in maintenance mode
111
# checks to see if we should be in maintenance mode
108
# if $remoteMachine->{'maintenanceMode'} exists, set mode
112
# if $remoteMachine->{'maintenanceMode'} exists, set mode
109
# otherwise, wait localMachine->{'waittime'} minutes, then check
113
# otherwise, wait localMachine->{'waittime'} minutes, then check
110
# $localMachine->{'maintenanceMode'}.
114
# $localMachine->{'maintenanceMode'}.
111
# if neither exists, begin sync
115
# if neither exists, begin sync
112
sub checkMaintenance {
116
sub checkMaintenance {
113
   my $config = shift;
117
   my $configuration = shift;
-
 
118
   return 0 unless # exit if maintenanceFlag has not been set at all
-
 
119
     ( defined( $configuration->{'target'}->{'maintenanceFlag'} ) && $configuration->{'target'}->{'maintenanceFlag'} ) ||
-
 
120
     ( defined( $configuration->{'source'}->{'maintenanceFlag'} ) && $configuration->{'source'}->{'maintenanceFlag'} );
114
   # see if maintenance is set on remote. If so, simply return the message
121
   # see if maintenance is set on remote. If so, simply return the message
115
   if ( $config->{'remoteMachine'}->{'up'} ) {
122
   if ( $configuration->{'source'}->{'up'} ) {
116
      my ($error, $output) = &runCommand( "ssh $configuration->{remoteMachine}->{ip} 'ls $configuration->{remoteMachine}->{maintenanceFlag}'" );
123
      my ($error, $output) = &runCommand( "ssh $configuration->{remoteMachine}->{ip} 'ls $configuration->{remoteMachine}->{maintenanceFlag}'" );
117
      if ( ! $error ) {
124
      if ( ! $error ) {
118
         # remove the file from the remote server
125
         # remove the file from the remote server
119
         &runCommand( "ssh $configuration->{remoteMachine}->{ip} 'rm $configuration->{remoteMachine}->{maintenanceFlag}'" );
126
         &runCommand( "ssh $configuration->{remoteMachine}->{ip} 'rm $configuration->{remoteMachine}->{maintenanceFlag}'" );
120
         # create a valid return, which will exit the program
127
         # create a valid return, which will exit the program
121
         return "Maintenance Flag found on remote machine";
128
         return "Maintenance Flag found on remote machine";
122
      }
129
      }
123
   }
130
   }
124
   # not on remote machine, so give them waitTime seconds to put it here
131
   # not on remote machine, so give them waitTime seconds to put it here
125
   # we'll loop, checking every $sleepTime seconds until our wait time
132
   # we'll loop, checking every $sleepTime seconds until our wait time
126
   # ($config->{'localMachine'}->{'waitTime'}) has expired
133
   # ($configuration->{'target'}->{'waitTime'}) has expired
127
   my $sleepTime = 60;
134
   my $sleepTime = 60;
128
   for ( my $i = $config->{'localMachine'}->{'waitTime'}; $i > 0; $i -= $sleepTime ) {
135
   for ( my $i = $configuration->{'target'}->{'waitTime'}; $i > 0; $i -= $sleepTime ) {
129
      sleep $sleepTime;
136
      sleep $sleepTime;
130
      # then look for the maintenance flag file on the local machine
137
      # then look for the maintenance flag file on the local machine
131
      return "Maintenance Flag found on local machine" if -f $config->{'localMachine'}->{'maintenanceFlag'};
138
      return "Maintenance Flag found on local machine" if -f $configuration->{'target'}->{'maintenanceFlag'};
132
   }
139
   }
133
   # no maintenance flags found, so return false
140
   # no maintenance flags found, so return false
134
   return 0;
141
   return 0;
135
}
142
}
136
 
143
 
137
sub shutdownMachine {
144
sub shutdownMachine {
138
   my $config = shift;
145
   my $configuration = shift;
139
   my $subject = shift;
146
   my $subject = shift;
140
   push @_, "Shutting down";
147
   push @_, "Shutting down" if $configuration->{'shutdown'};
141
   &sendMail( join( "\n", @_), $configuration, $subject );   
148
   &sendMail( join( "\n", @_), $configuration, $subject );
-
 
149
   # do not actually shut down the server unless we are told to
-
 
150
   exit unless $configuration->{'shutdown'};
142
   &runCommand( "poweroff" ) unless $TESTING;
151
   &runCommand( "poweroff" ) unless $configuration->{'testing'};
143
   die "Shutting down machine now\n";
152
   die "Shutting down machine now\n";
144
}
153
}
145
 
154
 
146
# returns the current time as a string
155
# returns the current time as a string
147
sub currentTime {
156
sub currentTime {
Line 150... Line 159...
150
   $format = '%Y-%m-%d %H-%M-%S' unless $format;
159
   $format = '%Y-%m-%d %H-%M-%S' unless $format;
151
   use POSIX;
160
   use POSIX;
152
   return POSIX::strftime( $format, localtime() );
161
   return POSIX::strftime( $format, localtime() );
153
}
162
}
154
 
163
 
-
 
164
sub checkRemoteUp {
-
 
165
   my $configuration = shift;
-
 
166
   my $ip;
-
 
167
   if ( defined( $configuration->{'target'}->{'server'} ) && $configuration->{'target'}->{'server'} ) {
-
 
168
      $ip = $configuration->{'target'}->{'server'};
-
 
169
   } else {
-
 
170
      $ip = $configuration->{'source'}->{'server'};
-
 
171
   }
-
 
172
   my ($error, $message ) =  $ip ? &runCommand( "ping -c 1 -t 5 $ip" ) : (0,'No address defined for either target or server' );
-
 
173
   $message = "Checking IP $ip\n"  . $message;
-
 
174
   #die "error is $error, message is $message for $ip\n";
-
 
175
   return ($error, $message);
-
 
176
}
-
 
177
 
155
my @status;   
178
my @status;   
-
 
179
my $error = 0;
-
 
180
my $output = '';
156
 
181
 
157
$configuration = &readConfig($configFileName);
182
$configuration = &readConfig($configFileName);
158
 
183
 
-
 
184
# die Dumper( $configuration ) . "\n";
-
 
185
 
-
 
186
my $servername = `hostname`;
-
 
187
chomp $servername;
-
 
188
 
159
&sendMail( "mc-009 has been started, " . &currentTime() . " checking for maintenance mode", $configuration );
189
&sendMail( "Replication on $servername has been started, " . &currentTime(), $configuration, "Replication on $servername started" );
-
 
190
 
160
# see if remote machine is up by sending one ping. Expect response in 5 seconds
191
# see if remote machine is up by sending one ping. Expect response in 5 seconds
161
my ( $error,$output) = &runCommand( "ping -c 1 -t 5 " . $configuration->{'remoteMachine'}->{'ip'} );
192
( $error,$output) = &checkRemoteUp( $configuration );
162
$configuration->{'remoteMachine'}->{'up'} = ! $error;
193
$configuration->{'up'} = ! $error;
-
 
194
push @status, "remote machine is " . ( $configuration->{'up'} ? 'Up' : 'Down' ) . "\n";
-
 
195
# we can not connect to the remote server, so just shut down
-
 
196
&shutdownMachine( $configuration, "No connection to remote machine", @status ) unless $configuration->{'up'};
-
 
197
 
163
 
198
 
164
push @status, "remote machine ($configuration->{'remoteMachine'}->{'ip'}) is " . ( $configuration->{'remoteMachine'}->{'up'} ? 'Up' : 'Down' ) . "\n";
-
 
165
# check for maintenance flags, exit if we should go into mainteannce mode
199
# check for maintenance flags, exit if we should go into mainteance mode
166
if ( my $result = &checkMaintenance( $configuration ) ) {
200
if ( my $result = &checkMaintenance( $configuration ) ) {
167
   push @status,$result;
201
   push @status,$result;
168
   &sendMail( join( "\n", @status), $configuration, "Maintenance Mode" );
202
   &sendMail( join( "\n", @status), $configuration, "Maintenance Mode" );
169
   die;
203
   die;
170
}
204
}
171
 
205
 
172
# we can not connect to the remote server, so just shut down
-
 
173
&shutdownMachine( $configuration, "No connection to remote machine", @status ) unless $configuration->{'remoteMachine'}->{'up'};
-
 
174
 
-
 
175
# try to mount the datasets
206
# try to mount the datasets if they are encrypted
176
($error,$output) = &mountDrives( $configuration );
207
($error,$output) = &mountDrives( $configuration );
177
if ( $error ) { # could not mount datasets
208
if ( $error ) { # could not mount datasets
178
   push @status, $error;
209
   push @status, $error;
179
   &shutdownMachine( $configuration, "Mount Drive Error: [$output]", @status );
210
   &shutdownMachine( $configuration, "Mount Drive Error: [$output]", @status );
180
}
211
}
181
 
212
 
182
&sendMail( "Backup has been started at " . &currentTime(), $configuration, "Backup Starting" );
213
&sendMail( "Backup has been started at " . &currentTime(), $configuration, "Backup Starting" );
183
push @status, "Backup started at: " . &currentTime();
214
push @status, "Backup started at: " . &currentTime();
184
 
215
 
-
 
216
$configuration->{'source'}->{'server'} = $configuration->{'source'}->{'server'} ? $configuration->{'source'}->{'server'} . ':' : '';
-
 
217
$configuration->{'target'}->{'server'} = $configuration->{'target'}->{'server'} ? $configuration->{'target'}->{'server'} . ':' : '';
185
 
218
 
186
# For each dataset, let's find the snapshots we need
219
# For each dataset, let's find the snapshots we need
187
foreach my $sourceDir ( keys %{$configuration->{'remoteMachine'}->{'dataset'}} ) {
220
foreach my $sourceDir ( keys %{$configuration->{'source'}->{'dataset'}} ) {
-
 
221
   print "Looking for $sourceDir\n";
-
 
222
   print "syncing to $configuration->{target}->{targetDataset}\n";
188
   my $command = $replicateScript . ' ' .
223
   my $command = $replicateScript . ' ' .
189
                 $configuration->{'remoteMachine'}->{'ip'} . ':' . 
224
                 $configuration->{'source'}->{'server'} . 
190
                 $configuration->{'remoteMachine'}->{'dataset'}->{$sourceDir} . '/' . $sourceDir . ' ' .
225
                 $configuration->{'source'}->{'dataset'}->{$sourceDir} . '/' . $sourceDir . ' ' .
-
 
226
                 $configuration->{'target'}->{'server'} . 
191
                 $configuration->{'localMachine'}->{'targetDataset'} . '/' . $sourceDir;
227
                 $configuration->{'target'}->{'targetDataset'} . '/' . $sourceDir;
192
   push @status, "=== Running $command at " . &currentTime();
228
   push @status, "=== Running $command at " . &currentTime();
193
   my ($error, $output) = &runCommand( $command );
229
   ($error, $output) = &runCommand( $command ) unless $configuration->{'testing'};
194
   push @status, $output;
230
   push @status, $output;
195
   push @status, "=== Completed $command with status $error at " . &currentTime();
231
   push @status, "=== Completed $command with status $error at " . &currentTime();
196
}
232
}
197
 
233
 
198
push @status, "==== Backup finished at: " . &currentTime();
234
push @status, "==== Backup finished at: " . &currentTime();
199
 
235
 
-
 
236
if ($configuration->{'testing'}) {
-
 
237
   print join( "\n", @status ) . "\n";
-
 
238
} else {
200
&shutdownMachine( $configuration, "Backup Complete", @status );
239
   &shutdownMachine( $configuration, "Backup Complete", @status );
-
 
240
}
201
 
241
 
202
1;
242
1;