line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package DBA::Backup; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
=head1 NAME |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
DBA::Backup - Core module for managing automated database backups. |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 SYNOPSIS |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
NOTICE! This is currently a broken partial port from the origal working |
11
|
|
|
|
|
|
|
MySQL specific module. I hope to have the port finished and a functional |
12
|
|
|
|
|
|
|
version uploaded soon. Email me or the list for more information. |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
The mailing list for the DBA modules is perl-dba@fini.net. See |
15
|
|
|
|
|
|
|
http://lists.fini.net/mailman/listinfo/perl-dba to subscribe. |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
use DBA::Backup; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
my $dba = new DBA::Backup(%params); |
20
|
|
|
|
|
|
|
die "Can't initiate backups: $dba" unless ref $dba; |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
$dba->run(%conf_overides); |
23
|
|
|
|
|
|
|
$dba->log_messages(); |
24
|
|
|
|
|
|
|
$dba->send_email_notification(); |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
=begin dev |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
Required methods (can be void if appropriate): |
29
|
|
|
|
|
|
|
_flush_logs # so all logs are current |
30
|
|
|
|
|
|
|
_rotate_logs # rotate specified log types |
31
|
|
|
|
|
|
|
_dump_databases # as server optimized SQL file |
32
|
|
|
|
|
|
|
_stop_server |
33
|
|
|
|
|
|
|
_start_server |
34
|
|
|
|
|
|
|
_lock_database # (s)? |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
=end dev |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=cut |
40
|
|
|
|
|
|
|
|
41
|
1
|
|
|
1
|
|
53538
|
use 5.006001; |
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
51
|
|
42
|
1
|
|
|
1
|
|
5
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
41
|
|
43
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
1
|
|
|
|
|
15
|
|
|
1
|
|
|
|
|
50
|
|
44
|
|
|
|
|
|
|
|
45
|
1
|
|
|
1
|
|
2396
|
use Sys::Hostname; # provides hostname() |
|
1
|
|
|
|
|
3834
|
|
|
1
|
|
|
|
|
225
|
|
46
|
1
|
|
|
1
|
|
6791
|
use File::Copy qw/move/; |
|
1
|
|
|
|
|
12751
|
|
|
1
|
|
|
|
|
98
|
|
47
|
1
|
|
|
1
|
|
10
|
use File::Path qw/rmtree/; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
65
|
|
48
|
1
|
|
|
1
|
|
863
|
use Mail::Sendmail; # for sending mail notifications |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
use YAML qw(LoadFile); # config file processing |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
our $VERSION = '0.2_1'; |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
# prevent this module from granting any privilege to all (other users) |
55
|
|
|
|
|
|
|
umask(0117); |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
=head1 new() |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
Create new DBA::Backup object. Use this object to initiate backups. |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
OPTIONS: |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
CONF_FILE: Location of configuration file to use. Default is |
64
|
|
|
|
|
|
|
/etc/dba-backup.yml. Please keep in mind that conf files for any specific |
65
|
|
|
|
|
|
|
servers to be backup will need to be in the same location. |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
LOG_FILE: Location to write process log file. |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
BACKUP: If true will force full database backups. |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
ADD_DATABASES: Specify additional databases to be backed up. ** broken |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=cut |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
sub new { |
76
|
|
|
|
|
|
|
my $class = shift; |
77
|
|
|
|
|
|
|
my %params = ref($_[0]) eq 'HASH' ? %{$_[0]} : @_; |
78
|
|
|
|
|
|
|
$params{CONF_FILE} ||= '/etc/dba-backup.yml'; |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
# exits with usage statement if the config file is not valid |
81
|
|
|
|
|
|
|
_is_config_file_valid($params{CONF_FILE}) or usage($params{CONF_FILE}); |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
# Read the YAML formatted configuration file |
84
|
|
|
|
|
|
|
my $HR_conf = LoadFile($params{CONF_FILE}) |
85
|
|
|
|
|
|
|
or return ("Problem reading conf file $params{CONF_FILE}"); |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
# now lets modify for certain passed parameters |
88
|
|
|
|
|
|
|
$HR_conf->{LOG_FILE} = $params{LOG_FILE} if $params{LOG_FILE}; |
89
|
|
|
|
|
|
|
my $cur_day = substr(localtime,0,3); |
90
|
|
|
|
|
|
|
$HR_conf->{backup}{days} = $cur_day if $params{BACKUP}; |
91
|
|
|
|
|
|
|
if ($params{ADD_DATABASES}) { |
92
|
|
|
|
|
|
|
my $AR_dbs = $HR_conf->{backup}{databases}; |
93
|
|
|
|
|
|
|
foreach my $db (split(/ ?, ?/,$params{ADD_DATABASES})) { |
94
|
|
|
|
|
|
|
push(@{$AR_dbs},$db) unless grep (/$db/, @{$AR_dbs}); |
95
|
|
|
|
|
|
|
} # for each db to add |
96
|
|
|
|
|
|
|
} # if backing up additional databases |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
# this opens the log file for writing, turns off the buffer and redirects |
99
|
|
|
|
|
|
|
# STDERR to the log |
100
|
|
|
|
|
|
|
$HR_conf->{LOG} = _open_log_file($HR_conf->{LOG_FILE}); |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
# Stores the name of the current config file in the object |
103
|
|
|
|
|
|
|
$HR_conf->{backup_params}{CONF_FILE} = $params{CONF_FILE}; |
104
|
|
|
|
|
|
|
$HR_conf->{HOSTNAME} = Sys::Hostname::hostname(); |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
# list of servers to back up |
107
|
|
|
|
|
|
|
my @servers = grep(/^server_/, keys %{$HR_conf}); |
108
|
|
|
|
|
|
|
$HR_conf->{backup_servers} = \@servers; |
109
|
|
|
|
|
|
|
# generate list of unique server types |
110
|
|
|
|
|
|
|
my %extensions = (); |
111
|
|
|
|
|
|
|
foreach my $server (@servers) { |
112
|
|
|
|
|
|
|
my $type = lc($HR_conf->{$server}{server_type}); |
113
|
|
|
|
|
|
|
$extensions{$type} = 1; |
114
|
|
|
|
|
|
|
} # foreach server |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
# load extension for each server type |
117
|
|
|
|
|
|
|
foreach my $ext (keys %extensions) { |
118
|
|
|
|
|
|
|
my $mod = "DBA/Backup/$ext.pm"; |
119
|
|
|
|
|
|
|
require $mod && import $mod; |
120
|
|
|
|
|
|
|
push(@ISA,"DBA::Backup::$ext"); |
121
|
|
|
|
|
|
|
} # foreach class to load |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
# Yes, I'm currently being very lazy and using the configuration |
124
|
|
|
|
|
|
|
# hash as self and letting my methods access directly. |
125
|
|
|
|
|
|
|
return bless $HR_conf, $class; |
126
|
|
|
|
|
|
|
} # end new() |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
sub _open_log_file { |
130
|
|
|
|
|
|
|
my $logfile = shift; |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
open(my $LOG,"> $logfile") or die "Can't write logfile $logfile: $!"; |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
# sig warn and die handlers to write errors to log before sending to STDERR |
135
|
|
|
|
|
|
|
sub catch_sig { |
136
|
|
|
|
|
|
|
my $signame = shift; |
137
|
|
|
|
|
|
|
my $msg = shift; |
138
|
|
|
|
|
|
|
print $LOG "$signame: $msg\n"; |
139
|
|
|
|
|
|
|
die "$signame: $msg\n"; |
140
|
|
|
|
|
|
|
} # catch_sig |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
$SIG{INT} = \&catch_sig; |
143
|
|
|
|
|
|
|
$SIG{QUIT} = \&catch_sig; |
144
|
|
|
|
|
|
|
$SIG{warn} = \&catch_sig; |
145
|
|
|
|
|
|
|
$SIG{die} = \&catch_sig; |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
$self->_debug( "Testing sig handlers"); |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
$|++; |
150
|
|
|
|
|
|
|
return $LOG; |
151
|
|
|
|
|
|
|
} # _open_log_file |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=head1 usage() |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
Prints an usage message for the program on the screen and then exits. |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
=cut |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
sub usage { |
160
|
|
|
|
|
|
|
my $file = shift; |
161
|
|
|
|
|
|
|
die "Usage: $0 /path/backup.cnf\n" |
162
|
|
|
|
|
|
|
. "Please make sure you specified a config file with proper format." |
163
|
|
|
|
|
|
|
. "$file provided.\n"; |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
sub _is_config_file_valid { |
168
|
|
|
|
|
|
|
my $file = shift; |
169
|
|
|
|
|
|
|
return 0 unless $file; |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
# if file name does not start with ./ or /, then append ./ |
172
|
|
|
|
|
|
|
# unless ($file =~ m{^[./]} ) { |
173
|
|
|
|
|
|
|
# $_[0] = "./$_[0]"; |
174
|
|
|
|
|
|
|
# } |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
unless (-f $file && -r $file) { |
177
|
|
|
|
|
|
|
return 0; |
178
|
|
|
|
|
|
|
} # unless file exists and is reabable |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
return 1; |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
## |
185
|
|
|
|
|
|
|
## The above section sets up and handles configuration parsing |
186
|
|
|
|
|
|
|
## The section below actually manages the backups |
187
|
|
|
|
|
|
|
## |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=head1 run() |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
This is where most of the work in the program is done. |
193
|
|
|
|
|
|
|
It logs some messages to the log file and invokes the subroutines |
194
|
|
|
|
|
|
|
for database backup and log backup and rotation. |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
=cut |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
sub run { |
199
|
|
|
|
|
|
|
my $self = shift; |
200
|
|
|
|
|
|
|
my $time = localtime(); |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
my $conf_file = $self->{backup_params}{CONF_FILE}; |
203
|
|
|
|
|
|
|
$self->_debug("Starting log"); |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
# Greeting message |
206
|
|
|
|
|
|
|
print $self->{LOG} <
|
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
*** DBA::Backup process started [$time] *** |
209
|
|
|
|
|
|
|
Backup config file: $conf_file |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
LOG |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
# manage backups for each server |
214
|
|
|
|
|
|
|
foreach my $server (@{$HR_conf->{backup_servers}}) { |
215
|
|
|
|
|
|
|
# set server params to global vals if undefined |
216
|
|
|
|
|
|
|
foreach my $param (keys %{$self->{backup_params}}) { |
217
|
|
|
|
|
|
|
$self->_debug("Setting $server $param to default if value false",3); |
218
|
|
|
|
|
|
|
$self->{$server}{backup_params}{$param} |
219
|
|
|
|
|
|
|
||= $self->{backup_params}{$param}; |
220
|
|
|
|
|
|
|
} # for each backup param global |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
my $server_type = $self->{$server}{server_type}; |
223
|
|
|
|
|
|
|
my $host = $self->{$server}{connect}{RDBMS_HOST}; |
224
|
|
|
|
|
|
|
my $log_dir = $self->{$server}{backup_params}{LOG_DIR}; |
225
|
|
|
|
|
|
|
my $dump_dir = $self->{$server}{backup_params}{DUMP_DIR} |
226
|
|
|
|
|
|
|
|| $self->{backup_params}{DUMP_DIR}; |
227
|
|
|
|
|
|
|
my $dump_copies = $self->{$server}{backup_params}{DUMP_COPIES} |
228
|
|
|
|
|
|
|
|| $self->{backup_params}{DUMP_COPIES}; |
229
|
|
|
|
|
|
|
$time = localtime(); |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
# Greeting message |
232
|
|
|
|
|
|
|
print $self->{LOG} <
|
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
$server backup parameters: |
235
|
|
|
|
|
|
|
RDBMS: $server_type |
236
|
|
|
|
|
|
|
Host: $host |
237
|
|
|
|
|
|
|
Log dir: $log_dir |
238
|
|
|
|
|
|
|
Dump dir: $dump_dir |
239
|
|
|
|
|
|
|
Dumps to keep: $dump_copies |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
*Tidying up dump dirs* |
242
|
|
|
|
|
|
|
LOG |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
# the only arg usually passed to extension? |
245
|
|
|
|
|
|
|
my $HR_server_conf = $self->{$server}; |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
$self->_debug( "_tidy_dump_dirs"); |
248
|
|
|
|
|
|
|
# clean up dump dirs |
249
|
|
|
|
|
|
|
$self->_tidy_dump_dirs(); |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
# check the disk space on the dump directory |
252
|
|
|
|
|
|
|
# for now, use the unix df command, later use perl equivalent |
253
|
|
|
|
|
|
|
my $disk_usage = `df '$dump_dir'`; |
254
|
|
|
|
|
|
|
print $self->{LOG} "\n\n*Disk space report for $dump_dir*\n$disk_usage"; |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
## |
257
|
|
|
|
|
|
|
## example server method call |
258
|
|
|
|
|
|
|
## |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
my $flush_logs = "$server_flush_logs"; |
261
|
|
|
|
|
|
|
$self->$flush_logs($HR_server_conf); |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
## |
264
|
|
|
|
|
|
|
## Below here is unmodified from port |
265
|
|
|
|
|
|
|
## |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
# check the list of currently running mysql queries |
269
|
|
|
|
|
|
|
print $self->{LOG} "\n\n*Current processlist*\n"; |
270
|
|
|
|
|
|
|
$self->_debug( "_get_process_list"); |
271
|
|
|
|
|
|
|
print $self->{LOG} $self->_get_process_list(); |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
# rotate the logs. most text log files need to be renamed manually |
274
|
|
|
|
|
|
|
# before the "flush logs" command is issued to mysqld |
275
|
|
|
|
|
|
|
# we rotate logs daily (well, every time script is run) |
276
|
|
|
|
|
|
|
print $self->{LOG} "\n\n*Rotating logs*\n"; |
277
|
|
|
|
|
|
|
$self->_debug("_rotate_general_query_log"); |
278
|
|
|
|
|
|
|
$self->_rotate_general_query_log(); |
279
|
|
|
|
|
|
|
$self->_debug("_rotate_slow_query_log"); |
280
|
|
|
|
|
|
|
$self->_rotate_slow_query_log(); |
281
|
|
|
|
|
|
|
$self->_debug("_cycle_bin_logs"); |
282
|
|
|
|
|
|
|
$self->_cycle_bin_logs(); |
283
|
|
|
|
|
|
|
$self->_debug("_rotate_error_log"); |
284
|
|
|
|
|
|
|
$self->_rotate_error_log(); |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
# Backup the databases only if today is the day which is |
287
|
|
|
|
|
|
|
# specified in the config file |
288
|
|
|
|
|
|
|
my $cur_day = substr(localtime,0,3); |
289
|
|
|
|
|
|
|
my @backup_days = split(/ ?, ?/,$self->{backup}{days}); |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
if (grep(/$cur_day/i, @backup_days)) { |
292
|
|
|
|
|
|
|
print $self->{LOG} "\n\n*Starting dumps*\n"; |
293
|
|
|
|
|
|
|
$self->_debug( '_backup_databases'); |
294
|
|
|
|
|
|
|
my $b_errs = $self->_backup_databases(); |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
# if there were no problems backing up dbs, rotate the dump dirs |
297
|
|
|
|
|
|
|
if (not $b_errs) { |
298
|
|
|
|
|
|
|
print $self->{LOG} "\n\n*Rotating dump dirs*\n"; |
299
|
|
|
|
|
|
|
$self->_debug( '_rotate_dump_dirs'); |
300
|
|
|
|
|
|
|
$self->_rotate_dump_dirs(); |
301
|
|
|
|
|
|
|
} # if no backup errors |
302
|
|
|
|
|
|
|
} # if today is a database backup day |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
} # for each server to backup |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
# Goodbye message |
307
|
|
|
|
|
|
|
$time = localtime(); |
308
|
|
|
|
|
|
|
print $self->{LOG} "\n\n*** DBA::Backup finished [$time] ***\n\n"; |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
} # end run() |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
=head1 _test_create_dirs |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
Test for the existence and writeability of specified directories. |
316
|
|
|
|
|
|
|
If the directories do not exist, attempt to create them. If unable |
317
|
|
|
|
|
|
|
to create writeable directories, fail with error. |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
=cut |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
sub _test_create_dirs { |
322
|
|
|
|
|
|
|
my $self = shift; |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
# check the existence and proper permissions on all dirs |
325
|
|
|
|
|
|
|
foreach my $dir (@_) { |
326
|
|
|
|
|
|
|
# if it doesn't exist, create it |
327
|
|
|
|
|
|
|
unless (-d $dir) { |
328
|
|
|
|
|
|
|
print $self->{LOG} "Directory $dir does not exist, creating it...\n"; |
329
|
|
|
|
|
|
|
my $mask = umask; |
330
|
|
|
|
|
|
|
umask(0007); |
331
|
|
|
|
|
|
|
unless (mkdir($dir) and -d $dir) { |
332
|
|
|
|
|
|
|
$self->error("Cannot create $dir.\n"); |
333
|
|
|
|
|
|
|
} # unless dir created |
334
|
|
|
|
|
|
|
umask($mask); |
335
|
|
|
|
|
|
|
} # if directory doesn't exist |
336
|
|
|
|
|
|
|
# check that we can write to it |
337
|
|
|
|
|
|
|
unless (-w $dir) { |
338
|
|
|
|
|
|
|
$self->error("Directory $dir is not writable by the user running $0\n"); |
339
|
|
|
|
|
|
|
} # if directory isn't writable |
340
|
|
|
|
|
|
|
} # foreach directory to be tested |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
} # end _test_create_dirs() |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
=head1 _rotate_dump_dirs() |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
The dump directories contain output from both the full, weekly mysql |
348
|
|
|
|
|
|
|
dump as well as the incremental binary update logs that follow the |
349
|
|
|
|
|
|
|
dump (possibly multiple binlogs per day). Rotate these directory |
350
|
|
|
|
|
|
|
names to conform to convention: |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
[dump_dir]/00/ - most recent dump |
353
|
|
|
|
|
|
|
[dump_dir]/01/ - next most recent |
354
|
|
|
|
|
|
|
... |
355
|
|
|
|
|
|
|
[dump_dir]/_NN/ - oldest |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
Where N is [dump_copies] - 1 (in the config file). [dump_dir]/new/ |
358
|
|
|
|
|
|
|
is a temporary directory created from _backup_databases. This will |
359
|
|
|
|
|
|
|
be renamed 00/, 00/ will be renamed 01/, and so on. |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
=cut |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
sub _rotate_dump_dirs { |
364
|
|
|
|
|
|
|
my $self = shift; |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
my $dump_root = $self->{backup_params}{DUMP_DIR}; |
367
|
|
|
|
|
|
|
my $max_old = $self->{backup_params}{DUMP_COPIES} -1; |
368
|
|
|
|
|
|
|
$max_old = 0 if $max_old < 0; |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
# grab list of files/dirs within dump_root - @dump_root_files |
371
|
|
|
|
|
|
|
# create hash of dirs we care about w/in dump_root - %dump_root_hash |
372
|
|
|
|
|
|
|
opendir(DIR, $dump_root) |
373
|
|
|
|
|
|
|
or $self->error("Cannot get listing of files in $dump_root\n"); |
374
|
|
|
|
|
|
|
my @dump_dirs = grep {-d "$dump_root/$_"} readdir(DIR); |
375
|
|
|
|
|
|
|
closedir(DIR); |
376
|
|
|
|
|
|
|
my %dump_root_hash = (); |
377
|
|
|
|
|
|
|
foreach my $dir (@dump_dirs) { |
378
|
|
|
|
|
|
|
$dump_root_hash{$dir} = 1 if $dir =~ /^\d+$/; |
379
|
|
|
|
|
|
|
$dump_root_hash{$dir} = 1 if $dir =~ /^00$/; |
380
|
|
|
|
|
|
|
$dump_root_hash{$dir} = 1 if $dir =~ /^new$/; |
381
|
|
|
|
|
|
|
} # for each dump root file |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
# prepare instructions on how to rename directories, and in what order |
384
|
|
|
|
|
|
|
# do not rename a dir unless it "needs" to be renamed |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
# this seems wicked kludgy, but I want to understand reasoning before I |
387
|
|
|
|
|
|
|
# throw away or improve - spq |
388
|
|
|
|
|
|
|
my @dir_order; |
389
|
|
|
|
|
|
|
my %dir_map; |
390
|
|
|
|
|
|
|
if (exists $dump_root_hash{'new'}) { |
391
|
|
|
|
|
|
|
push @dir_order, 'new'; |
392
|
|
|
|
|
|
|
$dir_map{'new'} = '00'; |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
if (exists $dump_root_hash{'00'}) { |
395
|
|
|
|
|
|
|
push @dir_order, '00'; |
396
|
|
|
|
|
|
|
$dir_map{'00'} = '01'; |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
if ($max_old) { |
399
|
|
|
|
|
|
|
foreach my $idx (1 .. $max_old) { |
400
|
|
|
|
|
|
|
my $name = sprintf("%02d", $idx); |
401
|
|
|
|
|
|
|
last unless exists $dump_root_hash{$name}; |
402
|
|
|
|
|
|
|
push @dir_order, $name; |
403
|
|
|
|
|
|
|
$dir_map{$name} = sprintf("%02d", $idx+1); |
404
|
|
|
|
|
|
|
} # foreach archival iteration |
405
|
|
|
|
|
|
|
} # if we're keeping archival copies |
406
|
|
|
|
|
|
|
} # if there is a current directory as well |
407
|
|
|
|
|
|
|
} # if there is a new directory |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
if (@dir_order) { |
410
|
|
|
|
|
|
|
print $self->{LOG} "The following dump dirs will be renamed: " |
411
|
|
|
|
|
|
|
. join(", ", @dir_order) . ".\n"; |
412
|
|
|
|
|
|
|
} # if there are directories to rename |
413
|
|
|
|
|
|
|
else { |
414
|
|
|
|
|
|
|
print $self->{LOG} "No dump dirs will be renamed.\n"; |
415
|
|
|
|
|
|
|
} # else note there are none |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
# rotate names of the dump dirs we want to keep |
418
|
|
|
|
|
|
|
foreach my $old_dname (reverse @dir_order) { |
419
|
|
|
|
|
|
|
my $new_dname = $dir_map{$old_dname}; |
420
|
|
|
|
|
|
|
next if $old_dname eq $new_dname; |
421
|
|
|
|
|
|
|
next unless (-d "$dump_root/$old_dname"); |
422
|
|
|
|
|
|
|
next if (-f "$dump_root/$new_dname"); |
423
|
|
|
|
|
|
|
print $self->{LOG} "Renaming dump dir $old_dname/ to $new_dname/ in $dump_root/ ...\n"; |
424
|
|
|
|
|
|
|
File::Copy::move("$dump_root/$old_dname", "$dump_root/$new_dname") |
425
|
|
|
|
|
|
|
or $self->error("Cannot rename $old_dname/ to $new_dname/\n"); |
426
|
|
|
|
|
|
|
} # for each directory to rename |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
# delete oldest dump dir if it exceeds the specified number of copies |
429
|
|
|
|
|
|
|
# can't this just be made a delete above if last or such? |
430
|
|
|
|
|
|
|
my $oldest_dir = $dump_root . '/' . sprintf("%02d", $max_old+1); |
431
|
|
|
|
|
|
|
if (-d $oldest_dir) { |
432
|
|
|
|
|
|
|
print $self->{LOG} "Deleting $oldest_dir/ ...\n"; |
433
|
|
|
|
|
|
|
eval { File::Path::rmtree($oldest_dir) }; |
434
|
|
|
|
|
|
|
$self->error("Cannot delete $oldest_dir/ - $@\n") if ($@); |
435
|
|
|
|
|
|
|
$self->error("$oldest_dir/ deleted, but still exists!\n") if (-d $oldest_dir); |
436
|
|
|
|
|
|
|
} # delete past-max oldest archive |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
} # end _rotate_dump_dirs |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
=head1 _tidy_dump_dirs() |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
The dump directories contain output from both the full, weekly mysql |
444
|
|
|
|
|
|
|
dump as well as the incremental binary update logs that follow the |
445
|
|
|
|
|
|
|
dump (possibly multiple binlogs per day). Sometimes a user might |
446
|
|
|
|
|
|
|
delete a directory between backup runs (particularly if it has bad |
447
|
|
|
|
|
|
|
dumps). |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
This function is intended to be run before backups start. It will |
450
|
|
|
|
|
|
|
Attempt to make directory names to conform to convention: |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
[dump_dir]/00/ - most recent dump |
453
|
|
|
|
|
|
|
[dump_dir]/01/ - next most recent |
454
|
|
|
|
|
|
|
... |
455
|
|
|
|
|
|
|
[dump_dir]/NN/ - oldest |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
If there are missing directories, _tidy_dump_dirs will create a |
458
|
|
|
|
|
|
|
directory to take its place, such that 00/ should always exist |
459
|
|
|
|
|
|
|
and there should be no gaps in the numbering of old directories. In |
460
|
|
|
|
|
|
|
other words, N+1 should be the total number of directories in [dump_dir]. |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
If there are no gaps to begin with, _tidy_dump_dirs does not rename |
463
|
|
|
|
|
|
|
anything. |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
This function will also delete any xx directories that exceed the |
466
|
|
|
|
|
|
|
[dump_copies] config variable. |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
It will never touch [dump_dir]/new/. It will never modify the contents |
469
|
|
|
|
|
|
|
of any of these subdirectories (unless its deleting the whole subdir). |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
It will create [dump_dir] and [dump_dir]/00/ if they do not exist. |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
=cut |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
# is this routine doing redundant work with above?!?! |
476
|
|
|
|
|
|
|
sub _tidy_dump_dirs { |
477
|
|
|
|
|
|
|
my $self = shift; |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
my $dump_copies = $self->{backup_params}{DUMP_COPIES}; |
480
|
|
|
|
|
|
|
my $dump_root = $self->{backup_params}{DUMP_DIR}; |
481
|
|
|
|
|
|
|
$self->_test_create_dirs($dump_root); |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
# grab list of files/dirs within dump_root - @dump_root_files |
484
|
|
|
|
|
|
|
# create hash of dirs we care about w/in dump_root - %dump_root_hash |
485
|
|
|
|
|
|
|
opendir(DIR, $dump_root) |
486
|
|
|
|
|
|
|
or $self->error("Cannot get listing of files in $dump_root\n"); |
487
|
|
|
|
|
|
|
my @dump_dirs = grep {-d "$dump_root/$_"} readdir(DIR); |
488
|
|
|
|
|
|
|
closedir(DIR); |
489
|
|
|
|
|
|
|
my %dump_root_hash; |
490
|
|
|
|
|
|
|
foreach my $dump_dir (@dump_dirs) { |
491
|
|
|
|
|
|
|
$dump_root_hash{$dump_dir} = 1 if $dump_dir =~ /^\d+$/; |
492
|
|
|
|
|
|
|
$dump_root_hash{$dump_dir} = 1 if $dump_dir =~ /^00$/; |
493
|
|
|
|
|
|
|
} # for each dump directory |
494
|
|
|
|
|
|
|
# (the next line requires that [dump_copies] is <= 100) # huh?! why? - spq |
495
|
|
|
|
|
|
|
my @dump_root_dirs = sort keys %dump_root_hash; |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
# prepare instructions on how to rename directories, and in what order |
498
|
|
|
|
|
|
|
# also prep instructions on which directories to delete (in case user |
499
|
|
|
|
|
|
|
# has reduced [dump_copies] in the config file since the last time |
500
|
|
|
|
|
|
|
# this script was run) |
501
|
|
|
|
|
|
|
my %dir_map; |
502
|
|
|
|
|
|
|
my @ren_queue; |
503
|
|
|
|
|
|
|
my @del_queue; |
504
|
|
|
|
|
|
|
my $idx=0; |
505
|
|
|
|
|
|
|
foreach my $dir (@dump_root_dirs) { |
506
|
|
|
|
|
|
|
if ($idx < $dump_copies) { |
507
|
|
|
|
|
|
|
$dir_map{$dir} = sprintf("%02d", $idx); |
508
|
|
|
|
|
|
|
push @ren_queue, $dir |
509
|
|
|
|
|
|
|
unless ($dir eq $dir_map{$dir}) or ($dir eq '00'); |
510
|
|
|
|
|
|
|
} # if dump dir < max copies |
511
|
|
|
|
|
|
|
else { |
512
|
|
|
|
|
|
|
push @del_queue, $dir; |
513
|
|
|
|
|
|
|
} # else prepare delete queue |
514
|
|
|
|
|
|
|
$idx++; |
515
|
|
|
|
|
|
|
} # foreach dump dir |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
$dir_map{$dump_root_dirs[0]} = '00' if @dump_root_dirs; |
518
|
|
|
|
|
|
|
print $self->{LOG} "The following dump dirs will be renamed: " |
519
|
|
|
|
|
|
|
. join(", ", @ren_queue) . ".\n" if @ren_queue; |
520
|
|
|
|
|
|
|
print $self->{LOG} "The following dump dirs will be deleted: " |
521
|
|
|
|
|
|
|
. join(", ", @del_queue) . ".\n" if @del_queue; |
522
|
|
|
|
|
|
|
print $self->{LOG} "Dump dirs look good, not much to tidy up\n" |
523
|
|
|
|
|
|
|
if not @ren_queue and not @del_queue; |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
# shuffle names of the dump dirs |
526
|
|
|
|
|
|
|
foreach my $old_dname (@ren_queue) { |
527
|
|
|
|
|
|
|
my $new_dname = $dir_map{$old_dname}; |
528
|
|
|
|
|
|
|
next if $old_dname eq $new_dname; |
529
|
|
|
|
|
|
|
next unless (-d "$dump_root/$old_dname"); |
530
|
|
|
|
|
|
|
next if (-f "$dump_root/$new_dname"); |
531
|
|
|
|
|
|
|
print $self->{LOG} "Renaming dump dir $old_dname/ to $new_dname/ in " |
532
|
|
|
|
|
|
|
. "$dump_root/ ...\n"; |
533
|
|
|
|
|
|
|
File::Copy::move("$dump_root/$old_dname", "$dump_root/$new_dname") |
534
|
|
|
|
|
|
|
or $self->error("Cannot rename $old_dname/ to $new_dname/\n"); |
535
|
|
|
|
|
|
|
} # foreach old name? |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
# delete excess dump dirs |
538
|
|
|
|
|
|
|
foreach my $dname (@del_queue) { |
539
|
|
|
|
|
|
|
$dname = "$dump_root/$dname"; |
540
|
|
|
|
|
|
|
print $self->{LOG} "Deleting $dname/ (exceeds dump_copies=$dump_copies) ...\n"; |
541
|
|
|
|
|
|
|
eval { File::Path::rmtree($dname) }; |
542
|
|
|
|
|
|
|
$self->error("Cannot delete $dname/ - $@\n") if ($@); |
543
|
|
|
|
|
|
|
$self->error("$dname/ deleted, but still exists!\n") if (-d $dname); |
544
|
|
|
|
|
|
|
} # for each excess dir to delete |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
# if not @dump_root_files, create a 00/ dir |
547
|
|
|
|
|
|
|
$self->_test_create_dirs("$dump_root/00"); |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
} # end _tidy_dump_dirs |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
=head1 send_email_notification() |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
Sends the data from the 00 run of the program |
555
|
|
|
|
|
|
|
which gets stored in the log file by email. The exact |
556
|
|
|
|
|
|
|
behaviour for this subroutine is controlled by the |
557
|
|
|
|
|
|
|
varibles in [mail-setup] section in the config file |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
=cut |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
sub send_email_notification { |
562
|
|
|
|
|
|
|
my $self = shift; |
563
|
|
|
|
|
|
|
$self->_debug("self? $self"); |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
# Email notifications can be turned off although this |
566
|
|
|
|
|
|
|
# is usually not a good idea |
567
|
|
|
|
|
|
|
my $notify = $self->{mail_setup}{mail_notification}; |
568
|
|
|
|
|
|
|
return unless $notify =~ /yes/i; |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
# send LOG by mail |
571
|
|
|
|
|
|
|
# get the varibles below from a config file |
572
|
|
|
|
|
|
|
my $hostname = $self->{db_connect}{HOSTNAME}; |
573
|
|
|
|
|
|
|
my $subject = "MySQL dump log from $hostname at " . localtime; |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
my $to = join(', ', @{$self->{mail_setup}{mail_to}}); |
576
|
|
|
|
|
|
|
my $cc = join(', ', @{$self->{mail_setup}{mail_cc}}); |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
sendmail(To => $to, |
579
|
|
|
|
|
|
|
CC => $cc, |
580
|
|
|
|
|
|
|
Subject => $subject, |
581
|
|
|
|
|
|
|
From => $self->{mail_setup}{mail_from}, |
582
|
|
|
|
|
|
|
Message => (join '', ''), # we no longer store @LOG - what here? |
583
|
|
|
|
|
|
|
Server => $self->{mail_setup}{mail_server}, |
584
|
|
|
|
|
|
|
delay => 1, |
585
|
|
|
|
|
|
|
retries => 3 ); |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
} # end send_email_notification() |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
# Log debug messages. Supress die on _gripe at high debug by |
591
|
|
|
|
|
|
|
# setting die to -1 |
592
|
|
|
|
|
|
|
sub _debug { |
593
|
|
|
|
|
|
|
my $self = shift; |
594
|
|
|
|
|
|
|
my $msg = shift; |
595
|
|
|
|
|
|
|
my $level = shift || 1; |
596
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
$self->_gripe($msg,-1) if $self->{DEBUG} >= $level; |
598
|
|
|
|
|
|
|
} # _debug |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
# uses gripes message controls, but instructs exit |
602
|
|
|
|
|
|
|
sub _error { |
603
|
|
|
|
|
|
|
my $self = shift; |
604
|
|
|
|
|
|
|
$self->_gripe(shift,1); |
605
|
|
|
|
|
|
|
} # _error |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
# debug level modified warn replacement using Carp |
608
|
|
|
|
|
|
|
sub _gripe { |
609
|
|
|
|
|
|
|
my $self = shift; |
610
|
|
|
|
|
|
|
my $msg = shift || confess("gripe without message"); |
611
|
|
|
|
|
|
|
my $die = shift || 0; |
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
my @call = caller; |
614
|
|
|
|
|
|
|
@call = caller(1) if $die; # -1 from debug should be true here as well |
615
|
|
|
|
|
|
|
my $pkg = $call[0]; |
616
|
|
|
|
|
|
|
$call[1] =~ s{.+/}{}; |
617
|
|
|
|
|
|
|
$msg = "$call[1]" . "[$call[2]]: $msg"; |
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
my $debug = $self->{DEBUG}; |
621
|
|
|
|
|
|
|
$die++ if $debug > 3; |
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
$self->log_messages(); |
624
|
|
|
|
|
|
|
$self->send_email_notification(); |
625
|
|
|
|
|
|
|
exit 1; |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
print $self->{LOG} $msg; |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
if ($die && $debug) { |
631
|
|
|
|
|
|
|
print $self->{LOG} "FATAL: $msg\n"; |
632
|
|
|
|
|
|
|
confess("$msg\n"); |
633
|
|
|
|
|
|
|
} # if we're dying and debug is on |
634
|
|
|
|
|
|
|
elsif ($die) { |
635
|
|
|
|
|
|
|
print $self->{LOG} "FATAL: $msg\n"; |
636
|
|
|
|
|
|
|
croak("$msg\n"); |
637
|
|
|
|
|
|
|
} # or die with just the message |
638
|
|
|
|
|
|
|
elsif ($debug > 1) { |
639
|
|
|
|
|
|
|
print $self->{LOG} "$msg\n"; |
640
|
|
|
|
|
|
|
cluck("$msg\n"); |
641
|
|
|
|
|
|
|
} # verbose warn |
642
|
|
|
|
|
|
|
else { |
643
|
|
|
|
|
|
|
print $self->{LOG} "$msg\n"; |
644
|
|
|
|
|
|
|
carp("$msg\n"); |
645
|
|
|
|
|
|
|
} # just let em know the basics |
646
|
|
|
|
|
|
|
} # _gripe |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
1; |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
=head1 INSTALLATION |
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
To install this module type the following: |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
perl Makefile.PL |
657
|
|
|
|
|
|
|
make |
658
|
|
|
|
|
|
|
make test |
659
|
|
|
|
|
|
|
make install |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
=head1 DEPENDENCIES |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
This module requires these other modules and libraries: |
664
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
Mail::Sendmail # if you want email reports |
666
|
|
|
|
|
|
|
YAML |
667
|
|
|
|
|
|
|
Sys::Hostname |
668
|
|
|
|
|
|
|
File::Copy |
669
|
|
|
|
|
|
|
File::Path |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
=head1 DESCRIPTION |
673
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
Manages rotation of mysql database logs and database backups. Reads information |
675
|
|
|
|
|
|
|
on which databases to back up on what days for the week from the configuration |
676
|
|
|
|
|
|
|
file. If no file is specified, it will look for one at /etc/mysql-backup.conf. |
677
|
|
|
|
|
|
|
If no configuration file is found program will exit with a failure. |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
This program assumes a MySQL 4.0.x server that is at least 4.0.10. |
680
|
|
|
|
|
|
|
It will likely work with current 1.23.xx server, but that has not been tested. |
681
|
|
|
|
|
|
|
Please let the maintainers know if you use this tool succesfully with other |
682
|
|
|
|
|
|
|
versions of MySQL or Perl so we can note what systems it works with. |
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
The expected usage of this program is for it to be run daily by a cron job, |
685
|
|
|
|
|
|
|
at a time of day convienient to have the backups occur. This program uses the |
686
|
|
|
|
|
|
|
administrative tools provided with MySQL (mysqladmin and mysqldump) as well |
687
|
|
|
|
|
|
|
as gzip for compression of backups. |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
Every time this program is run it will flush the MySQL logs. The binary update |
690
|
|
|
|
|
|
|
log will be moved into /path/to/dump/dir/00. Error log and slow query log files |
691
|
|
|
|
|
|
|
are rotated only if they exceeded the size limit specified in the confguration |
692
|
|
|
|
|
|
|
file. |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
If it is run on a day when full database backups are specified, then |
695
|
|
|
|
|
|
|
all databases specified in the config file are dumped and |
696
|
|
|
|
|
|
|
written to the directory specified in dump_dir variable in the config |
697
|
|
|
|
|
|
|
file. If there are no problems with this operation, previous full backups |
698
|
|
|
|
|
|
|
from dump_dir/00 are moved to directory dump_dir/01 and all the |
699
|
|
|
|
|
|
|
files in dump_dir/01 (full database backups and log files) are deleted |
700
|
|
|
|
|
|
|
from it or moved to dump_dir/02 etc. to the archival depth specified in the |
701
|
|
|
|
|
|
|
config file. This way there always [dump_copies] full database backups - |
702
|
|
|
|
|
|
|
one in 00/ and [dump_copies]-1 in the xx directories. |
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
Detailed information about the different configuration parameters |
705
|
|
|
|
|
|
|
can be found in the comments in the configuration file |
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
log-slow-queries |
708
|
|
|
|
|
|
|
log-long-format |
709
|
|
|
|
|
|
|
log-bin # required |
710
|
|
|
|
|
|
|
log-error # should be required? |
711
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
=head1 OPTIONS |
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
=over 4 |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
=item B |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
Filename for logging backup proceedure. Overrides conf file. |
720
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
=item B |
722
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
Additional databases to back up. These will be backed up I to any |
724
|
|
|
|
|
|
|
databases specified in the conf file. B - this adds databases to the list |
725
|
|
|
|
|
|
|
of those to be backed up. If the program is being run on a day when database |
726
|
|
|
|
|
|
|
backups are not scheduled, the extra databases specified will B be backed |
727
|
|
|
|
|
|
|
up. |
728
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
=item B |
730
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
If present this option forces full database backups to be done, even if not |
732
|
|
|
|
|
|
|
normally scheduled. |
733
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
=item B |
735
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
Outputs this help file. |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
=item B |
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
** NOT IMPLIMENTED ** |
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
Turn on debugging. Optionally takes a filename to store debugging and any error |
743
|
|
|
|
|
|
|
messages to. |
744
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
=item B |
746
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
** NOT IMPLIMENTED ** |
748
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
Increases debugging vebosity. If three or more v's provided (-v -v -v) |
750
|
|
|
|
|
|
|
than program will exit on warnings. |
751
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
=back |
753
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
=head1 TO DO |
755
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
Impliment debugging output options. |
757
|
|
|
|
|
|
|
|
758
|
|
|
|
|
|
|
Streamline config process - can we avoid using multiple config files? |
759
|
|
|
|
|
|
|
|
760
|
|
|
|
|
|
|
Support multiple servers of the same type. |
761
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
=head1 HISTORY |
763
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
=over 8 |
765
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
=item 0.1 |
767
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
Partial port from original MySQL specific version. |
769
|
|
|
|
|
|
|
|
770
|
|
|
|
|
|
|
=item 0.1.1 |
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
Some more work on getting port working and some comments. Early release |
773
|
|
|
|
|
|
|
for boston.pm mailing list. |
774
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
=item 0.1.2 |
776
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
Improved configuration handling allowing multiple servers per RDBMS type. |
778
|
|
|
|
|
|
|
Draft example of calling extension method and providing only server specific |
779
|
|
|
|
|
|
|
conf hash explicitly instead of making extension find it. |
780
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
=back |
782
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
|
785
|
|
|
|
|
|
|
=head1 SEE ALSO |
786
|
|
|
|
|
|
|
|
787
|
|
|
|
|
|
|
The mailing list for the DBA modules is perl-dba@fini.net. See |
788
|
|
|
|
|
|
|
http://lists.fini.net/mailman/listinfo/perl-dba to subscribe. |
789
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
dba-backup.yml |
791
|
|
|
|
|
|
|
dba-backup |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
=head1 AUTHOR |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
Sean P. Quinlan, Egilant@gmail.comE |
796
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
798
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
Copyright (C) 2004 by Sean P. Quinlan |
800
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify |
802
|
|
|
|
|
|
|
it under the same terms as Perl itself, either Perl version 5.8.3 or, |
803
|
|
|
|
|
|
|
at your option, any later version of Perl 5 you may have available. |
804
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
=cut |
806
|
|
|
|
|
|
|
|