Subversion Repositories zfs_utils

Rev

Rev 34 | Rev 37 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 34 Rev 35
Line 44... Line 44...
44
our $VERSION = '0.1';
44
our $VERSION = '0.1';
45
 
45
 
46
use FindBin;
46
use FindBin;
47
use lib "$FindBin::Bin/..";
47
use lib "$FindBin::Bin/..";
48
use Data::Dumper;
48
use Data::Dumper;
49
use ZFS_Utils qw(loadConfig shredFile logMsg makeReplicateCommands mountDriveByLabel mountGeli runCmd $logFileName $displayLogsOnConsole);
49
use ZFS_Utils qw(loadConfig shredFile logMsg makeReplicateCommands mountDriveByLabel mountGeli runCmd sendReport $logFileName $displayLogsOnConsole);
-
 
50
 
-
 
51
my $scriptDirectory = $FindBin::RealBin;
-
 
52
my $scriptFullPath = "$scriptDirectory/" . $FindBin::Script;
-
 
53
 
50
 
54
 
51
# if set, will not actually write files to disk
55
# if set, will not actually write files to disk
52
my $DEBUG = 0;
56
my $DEBUG = 0;
53
 
57
 
54
# set the log file to be next to this script
-
 
55
$logFileName = "$FindBin::Bin/sneakernet.log";
-
 
56
# log only for one run
-
 
57
unlink ( $logFileName ) if -f $logFileName;
-
 
58
 
-
 
59
# display all log messages on console in addition to the log file
58
# display all log messages on console in addition to the log file
60
$displayLogsOnConsole = 1;
59
$displayLogsOnConsole = 1;
61
 
60
 
62
my $configFileName = "$0.conf.yaml";
61
my $configFileName = "$scriptFullPath.conf.yaml";
63
 
62
 
64
my $config = {
63
my $config = {
65
   # file created on source server to track last copyed dataset
64
   # file created on source server to track last copyed dataset
66
   'status_file' => "$0.status",
65
   'status_file' => "$scriptFullPath.status",
-
 
66
   'log_file' => "$scriptFullPath.log",
67
   #information about source server
67
   #information about source server
68
   'source_server' => {
68
   'source_server' => {
69
      'hostname' => '', # used to see if we are on source
69
      'hostname' => '', # used to see if we are on source
70
      'poolname' => 'pool', # name of the ZFS pool to export
70
      'poolname' => 'pool', # name of the ZFS pool to export
71
      # if set, will generate a report via email or by storing on a drive
71
      # if set, will generate a report via email or by storing on a drive
72
      'report' => {
72
      'report' => {
73
         'email' => 'tech@example.org',
73
         'email' => 'tech@example.org',
74
         'subject' => 'AG Transport Report',
74
         'subject' => 'AG Transport Report',
75
         'targetDrive' => {
75
         'targetDrive' => {
-
 
76
            'fstype' => '', # filesystem type of the report drive
-
 
77
            # How often to check for the disk (seconds), message displayed every interval
-
 
78
            'check_interval' => 15,
76
            'label' => '',
79
            'label' => '',
77
            'mount_point' => '',
80
            'mount_point' => '',
78
         }
81
         }
79
      }
82
      }
80
   },
83
   },
Line 83... Line 86...
83
      'hostname' => '', # used to see if we are on target
86
      'hostname' => '', # used to see if we are on target
84
      'poolname' => 'backup', # name of the ZFS pool to import
87
      'poolname' => 'backup', # name of the ZFS pool to import
85
      # if this is set, the dataset uses GELI, so we must decrypt and
88
      # if this is set, the dataset uses GELI, so we must decrypt and
86
      # mount it first
89
      # mount it first
87
      'geli' => {
90
      'geli' => {
-
 
91
         'secureKey ' => {
88
         'keydiskname' => 'replica', # the GPT label of the key disk
92
            'label' => 'replica', # the GPT label of the key disk
-
 
93
            'fstype' => 'ufs', # filesystem type of the key disk
-
 
94
            'check_interval' => 15,
-
 
95
            'wait_timeout' => 300,
89
         'keyfile' => 'geli.key', # the name of the key file on keydiskname
96
            'keyfile' => 'geli.key', # the name of the key file on the secureKey disk
-
 
97
         },
90
         'localKey' => 'e98c660cccdae1226550484d62caa2b72f60632ae0c607528aba1ac9e7bfbc9c', # hex representation of the local key part
98
         'localKey' => 'e98c660cccdae1226550484d62caa2b72f60632ae0c607528aba1ac9e7bfbc9c', # hex representation of the local key part
91
         'target' => '/media/geli.key', # location to create the combined keyfile
99
         'target' => '/media/geli.key', # location to create the combined keyfile
92
         'poolname' => 'backup', # name of the ZFS pool to import
100
         'poolname' => 'backup', # name of the ZFS pool to import
93
         'diskList' => [ 
101
         'diskList' => [ 
94
            'da0',
102
            'da0',
Line 97... Line 105...
97
      },
105
      },
98
      'report' => {
106
      'report' => {
99
         'email' => '',
107
         'email' => '',
100
         'subject' => '',
108
         'subject' => '',
101
         'targetDrive' => {
109
         'targetDrive' => {
-
 
110
            'fstype' => 'msdos', # filesystem type of the report drive
102
            'label' => 'sneakernet_report',
111
            'label' => 'sneakernet',
103
            'mount_point' => '/mnt/sneakernet_report',
112
            'mount_point' => '',
104
         }
113
         }
105
      }
114
      }
106
   },
115
   },
107
   'transport' => {
116
   'transport' => {
108
      # this is the GPT label of the sneakernet disk
117
      # this is the GPT label of the sneakernet disk
109
      'disk_label' => 'sneakernet',
118
      'disk_label' => 'sneakernet',
-
 
119
      # this is the file system type. Not needed if ufs
-
 
120
      'fstype' => 'ufs',
110
      # where we want to mount it
121
      # where we want to mount it
111
      'mount_point' => '/mnt/sneakernet',
122
      'mount_point' => '/mnt/sneakernet',
112
      # amount of time to wait for the disk to appear
123
      # amount of time to wait for the disk to appear
113
      'timeout' => 600,
124
      'timeout' => 600,
-
 
125
      # How often to check for the disk (seconds), message displayed every interval
-
 
126
      'check_interval' => 15,
114
      # if set, all files will be encrypted with this key/IV during transport
127
      # if set, all files will be encrypted with this key/IV during transport
115
      'encryption' => {
128
      'encryption' => {
116
         'key'    => '', # openssl rand 32 | xxd -p | tr -d '\n' > test.key
129
         'key'    => '', # openssl rand 32 | xxd -p | tr -d '\n' > test.key
117
         'IV'     => '00000000000000000000000000000000',
130
         'IV'     => '00000000000000000000000000000000',
118
      },
131
      },
Line 129... Line 142...
129
         'filename' => 'files_share'
142
         'filename' => 'files_share'
130
      },
143
      },
131
   }
144
   }
132
};
145
};
133
 
146
 
134
# read the status file and return as list
147
# read the status file and return as list. If the file doesn't exits, returns an empty list
135
sub getStatusFile {
148
sub getStatusFile {
136
   my $filename = shift;
149
   my $filename = shift;
137
   # read in history/status file
150
   # read in history/status file
138
   my @lines = ();
151
   my @lines = ();
139
   if ( -e $filename && open my $fh, '<', $filename ) {
152
   if ( -e $filename && open my $fh, '<', $filename ) {
Line 238... Line 251...
238
 
251
 
239
# If a YAML config file exists next to the script, load and merge it
252
# If a YAML config file exists next to the script, load and merge it
240
$config = loadConfig($configFileName, $config );
253
$config = loadConfig($configFileName, $config );
241
 
254
 
242
# set some defaults
255
# set some defaults
243
$config->{'status_file'} //= "$0.status";
256
$config->{'status_file'} //= "$scriptFullPath.status";
-
 
257
# set log file name for sub logMsg in ZFS_Utils, and remove the old log if it exists
-
 
258
# Log file is only valid for one run
-
 
259
$logFileName = $config->{'log_file'} //= "$scriptFullPath.log";
-
 
260
# log only for one run
-
 
261
unlink ( $logFileName ) if -f $logFileName;
244
 
262
 
245
fatalError( "Invalid config file: missing source and/or target server" )
263
fatalError( "Invalid config file: missing source and/or target server" )
246
    unless (defined $config->{source_server} && defined $config->{target_server});
264
    unless (defined $config->{source_server} && defined $config->{target_server});
247
 
265
 
248
# mount the transport drive, fatal error if we can not find it
266
# mount the transport drive, fatal error if we can not find it
249
fatalError( "Unable to mount tranport drive with label $config->{transport}->{disk_label}" )
267
fatalError( "Unable to mount tranport drive with label $config->{transport}->{disk_label}" )
250
   unless $config->{transport}->{mount_point} =  mountDriveByLabel( $config->{transport}->{disk_label}, $config->{transport}->{mount_point}, $config->{transport}->{timeout} );
268
   unless $config->{transport}->{mount_point} =  mountDriveByLabel( $config->{transport} );
251
 
269
 
252
my $servername = `hostname -s`;
270
my $servername = `hostname -s`;
253
chomp $servername;
271
chomp $servername;
254
if ( $servername eq $config->{source_server}->{hostname} ) {
272
my $runningAs = $servername eq $config->{source_server}->{hostname} ? 'source' :
-
 
273
                $servername eq $config->{target_server}->{hostname} ? 'target' : 'unknown';
-
 
274
if ( $runningAs eq 'source' ) {
255
    logMsg "Running as source server";
275
    logMsg "Running as source server";
256
    # remove all files from transport disk, but leave all subdirectories alone
276
    # remove all files from transport disk, but leave all subdirectories alone
257
    cleanDirectory( $config->{transport}->{mount_point} );
277
    cleanDirectory( $config->{transport}->{mount_point} );
258
    my $statusList = getStatusFile($config->{status_file});
278
    my $statusList = getStatusFile($config->{status_file});
259
    $statusList = doSourceReplication($config, $statusList);
279
    $statusList = doSourceReplication($config, $statusList);
260
    writeStatusFile($config->{status_file}, $statusList);
280
    writeStatusFile($config->{status_file}, $statusList);
261
    # source server logic here
-
 
262
} elsif ( $servername eq $config->{target_server}->{hostname} ) {
281
} elsif ( $runningAs eq 'target' ) {
263
    logMsg "Running as target server";
282
    logMsg "Running as target server";
264
    die "Target Server code not complete\n";
283
    die "Target Server code not complete\n";
265
    die "GELI target server logic not yet implemented\n" if ( defined $config->{target_server}->{geli} );
284
    die "GELI target server logic not yet implemented\n" if ( defined $config->{target_server}->{geli} );
266
    mountGeli( $config->{target_server}->{geli} ) if ( defined $config->{target_server}->{geli} );
285
    mountGeli( $config->{target_server}->{geli} ) if ( defined $config->{target_server}->{geli} );
267
} else {
286
} else {
268
    logMsg "This server ($servername) is neither source nor target server as per config\n";
287
    logMsg "This server ($servername) is neither source nor target server as per config\n";
269
    die;
288
    die;
270
}
289
}
271
 
290
 
-
 
291
# add disk space utilization information on transport to the log
-
 
292
logMsg( "Disk space utilization on transport disk:\n" . runCmd( "df -h $config->{transport}->{mount_point}" ) . "\n" );
-
 
293
# add information about the server (zpools) to the log
-
 
294
logMsg( "Zpools on server $servername:\n" . join( "\n", runCmd( "zpool list" ) ) . "\n" );
-
 
295
 
272
# unmount the sneakernet drive
296
# unmount the sneakernet drive
273
`umount $config->{transport}->{mount_point}`;
297
`umount $config->{transport}->{mount_point}`;
274
# and remove the directory
298
# and remove the directory
275
rmdir $config->{transport}->{mount_point};
299
rmdir $config->{transport}->{mount_point};
-
 
300
sendReport( $config->{$runningAs}->{report}, "sneakernet replication completed on server $servername", $config->{log_file} );
-
 
301
 
276
 
302
 
277
1;
303
1;
278
 
304
 
279
 
305
 
280
#`cat $config->{input} | openssl enc -aes-256-cbc -K $config->{key} -iv $config->{IV} > $config->{output}`;
306
#`cat $config->{input} | openssl enc -aes-256-cbc -K $config->{key} -iv $config->{IV} > $config->{output}`;