Attached is a perl-based re-implementation of backup.sh, which should in
theory run on every platform that OTRS itself runs on.
Features include:
- command-line compatible with backup.sh
- fixes a nasty date-related problem with removing old backups (now uses
Date::Pcalc)
- Additional options and switches to customize operation, including
database-dumper/archiver/compression programs to use, as well as
command-lines
- a --help switch to display a summary
- both long and short option switches so command-lines can be
self-documenting
- ability to specify the number of days of backups to keep
- uses Config.pm directly for pulling additional configuration
information
It will require a few additional perl modules (not attached) that I just
threw into ~otrs/Kernel/cpan-lib/. They are File::Which, File::NCopy,
and File::Remove, all of which are just single .pm files.
Alas, only tested under Linux currently, but if you have command-line DB
dumpers, archivers (ie, zip, rar, etc), and compressors (ie, zip, bzip2,
gzip, etc) for other OSes, you should be able to just specify them on
the
command-line to test the script.
--
E. Michael A. Gurski <[EMAIL PROTECTED]>
AMTI, Inc. <http://www.amti.com/>
#!/usr/bin/perl -w
# --
# scripts/backup.pl - a perl backup script for OTRS based on backup.sh
# Copyright (C) 2005 Michael Gurski <[EMAIL PROTECTED]>
# --
# $Id: backup.pl,v 1.3 2005/05/10 20:54:32 gurski Exp $
# --
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# --
use strict;
# use ../ as lib location
use File::Basename;
use FindBin qw($RealBin);
use lib dirname($RealBin);
use lib dirname($RealBin)."/Kernel/cpan-lib";
use vars qw($VERSION);
$VERSION = '$Revision: 1.3 $';
$VERSION =~ s/^\$.*:\W(.*)\W.+?$/$1/;
use Kernel::Config;
use Kernel::System::Log;
use POSIX;
use Getopt::Long;
use File::Which;
use File::NCopy;
use File::Remove;
use Date::Pcalc qw(Today Today_and_Now Add_Delta_Days);
use Cwd;
my $ConfigObject = Kernel::Config->new();
print "backup.pl - a perl backup script for OTRS <$VERSION>\n";
# removal options
my $REMOVE_OLD = 0;
my $REMOVE_DAYS = 30;
# db dumping options
my $DB_DUMP_PROG = '';
my $DB_DUMP_OPTIONS = '';
my $DB_DUMP_FILE = "database_backup.sql";
# db dump compression options
my $DB_DUMP_COMPRESS_BZIP2 = 1;
my $DB_DUMP_COMPRESS_GZIP = 0;
my $DB_DUMP_COMPRESS_DO = 1;
my $DB_DUMP_COMPRESS_PROG = '';
my $DB_DUMP_COMPRESS_PROG_OPTIONS = '';
# location options
my $BINDIR = '';
my $CFGDIR = '';
my $BACKUPDIR = '';
# archiving options
my $ARCHIVE = 1;
my $ARCHIVE_PROG = '';
my $ARCHIVE_PROG_OPTIONS = '';
my $ARCHIVE_BACKUP = 0;
my $ARCHIVE_BACKUP_COMPRESS_BZIP2 = 0;
my $ARCHIVE_BACKUP_COMPRESS_GZIP = 0;
my $HELP = 0;
my $DB = '';
my($year, $month, $day, $hour, $minute, $secord) = Today_and_Now();
my $SUBBACKUPDIR = sprintf("%04d-%02d-%02d_%02d-%02d",$year,$month,$day,$hour,$minute);
GetOptions(
'bindir|b=s' => \$BINDIR,
'configdir|c=s' => \$CFGDIR,
'backupdir|d=s' => \$BACKUPDIR,
'dbdumper|db-dumper=s' => \$DB_DUMP_PROG,
'dbdumperoptions|db-dumper-options=s' => \$DB_DUMP_OPTIONS,
'dbdumpcompress|db-dump-compress|compressdbdump|compress-db-dump!' => \$DB_DUMP_COMPRESS_DO,
'dbdumpcompressor|db-dump-compressor=s' => \$DB_DUMP_COMPRESS_PROG,
'dbdumpcompressoroptions|db-dump-compressor-options=s' => \$DB_DUMP_COMPRESS_PROG_OPTIONS,
'dbdumpfile|db-dump-file=s' => \$DB_DUMP_FILE,
'archive|a!' => \$ARCHIVE,
'archiver=s' => \$ARCHIVE_PROG,
'archiveroptions|archiver-options=s' => \$ARCHIVE_PROG_OPTIONS,
'removeoldbackups|remove-old-backups|r!' => \$REMOVE_OLD,
'archivebackup|archive-backup' => \$ARCHIVE_BACKUP,
'bzip2|j' => \$ARCHIVE_BACKUP_COMPRESS_BZIP2,
'gzip|z' => \$ARCHIVE_BACKUP_COMPRESS_GZIP,
'age|g=i' => \$REMOVE_DAYS,
'help|h' => \$HELP);
my %substituteTable = (
backupdir => $BACKUPDIR,
subbackupdir => $SUBBACKUPDIR,
database => $ConfigObject->{Database},
dbdumpfile => $DB_DUMP_FILE,
dbhost => $ConfigObject->{DatabaseHost},
dbpassword => $ConfigObject->{DatabasePw},
dbuser => $ConfigObject->{DatabaseUser},
sourcedir => '',
targetfile => '',
);
my $substituteKeys = "%" . join(" %",(sort keys %substituteTable));
if ($HELP || !$BINDIR || !$CFGDIR || !$BACKUPDIR) {
showHelp();
}
# figure out what database to use
determineDatabase();
# figure out how to compress the database
determineDBDumpCompressor();
# figure out how to archive the backup (if needed)
determineArchiver();
# remove old backups if needed
deleteOldBackups();
# create target for backup
createBackupSubdir();
# dump out the db
dumpDatabase();
# compress output of database dump
compressDatabaseDump();
# make backups of OTRS' configuration
backupConfigFiles();
# make backups of any on-disk articles
backupArticles();
# make backup an archive, if necessary
archiveBackup();
# --
# Help function, to explain how to use the script
# --
sub showHelp {
print <<EOE;
Usage: $0 [-j|-z] [-r] [-g <days>] -b <binpath> -c <configpath> -d <backuppath>
Examples:
$0 -b /opt/otrs/bin -c /opt/otrs/Kernel/Config/ -d /data/otrs-backup
Creates a timestamped directory in /data/otrs-backup, with
default options for everything
$0 -b /opt/otrs/bin -c /opt/otrs/Kernel/Config/ -d /data/otrs-backup \\
--archiver zip --archiver-options "-9vr %targetfile.zip %sourcedir" \\
--archive-backup --nocompressdbdump --noarchive
Creates a timestamped zip file in /data/otrs-backup, not \\
compressing the DB dump explicitly before zip file creation, \\
also doesn\'t individually zip subdirectories which are normally \\
archived
Options:
-b <path> => --bindir <path> path to the OTRS bin directory
-c <path> => --configdir <path> path to the OTRS Kernel/Config/ directory
-d <path> => --backupdir <path> path to the backup directory
-j => --bzip2 use bzip2 for compression of archives
-z => --gzip use gzip for compression of archives
-r => --removeoldbackups remove old backups
=> --remove-old-backups
-g <days> => --age <days> number of days of backups to keep
--dbdumper <program> use an alternate database dumper
--db-dumper <program> use an alternate database dumper
--dbdumperoptions <options> commandline to use with alternate db dumper
--db-dumper-options <options> commandline to use with alternate db dumper
--dbdumpcompressor <program> use an alternate compressor for database
--db-dump-compressor <program> use an alternate compressor for database
--dbdumpcompressoroptions <options> commandline to use with alternate db dump compressor
--db-dump-compressor-options <options> commandline to use with alternate db dump compressor
--[no]compressdbdump [don\'t] compress the DB dump file
--[no]compress-db-dump [don\'t] compress the DB dump file
--[no]archive [don\'t] use archiver (tar) to bundle subdirectories
--archiver <program> use an alternate archiving program than tar
--archiveroptions <options> commandline to use with alternate archiver
--archiver-options <options> commandline to use with alternate archiver
tags for --*options options:
$substituteKeys
EOE
;
exit(1);
}
# --
# check for needed programs
# --
sub checkForProg {
my $prog = shift;
if($prog ne "" &&
! which($prog) ) {
die "ERROR: Can't locate $prog!\n";
#exit(1);
}
}
# --
# substitute and expand tags
# --
sub substituteTags {
my $originalString = shift;
foreach my $key (keys %substituteTable) {
$originalString =~ s/%$key/$substituteTable{$key}/g;
}
return $originalString;
}
# --
# determine database type
# --
sub determineDatabase {
$ConfigObject->{DatabaseDSN} =~ /^(DBI:[^:]+):.*/;
if($DB_DUMP_PROG ne '') {
$DB = "User-specified";
# prog and options specified on commandline
}
elsif($1 eq "DBI:mysql") {
$DB = "MySQL";
$DB_DUMP_PROG = "mysqldump";
$DB_DUMP_OPTIONS = "-u %dbuser -p%dbpassword -h %dbhost %database > %backupdir/%subbackupdir/%dbdumpfile";
}
elsif($1 eq "DBI:Pg") {
$DB = "PostgreSQL";
$DB_DUMP_PROG = "pg_dump";
$DB_DUMP_OPTIONS = "-f %backupdir/%subbackupdir/%dbdumpfile -h %dbhost -U %dbuser %database";
}
else {
die "ERROR: Can't run backup script because there is no support for your database. Better start coding now or specify --db-dumper and --db-dumper-options.\n";
}
checkForProg($DB_DUMP_PROG);
}
# --
# determine compression program, if any
# --
sub determineDBDumpCompressor {
if($DB_DUMP_COMPRESS_DO || $DB_DUMP_COMPRESS_PROG ne "") {
if($DB_DUMP_COMPRESS_PROG ne "") {
# options specified on command line
die "Need to specify compressor options when specifying compressor for db dump compression!\n" if($DB_DUMP_COMPRESS_PROG_OPTIONS eq "");
}
elsif($DB_DUMP_COMPRESS_BZIP2) {
$DB_DUMP_COMPRESS_PROG = "bzip2";
$DB_DUMP_COMPRESS_PROG_OPTIONS = "-9 %sourcedir"
if($DB_DUMP_COMPRESS_PROG_OPTIONS eq "");
}
elsif($DB_DUMP_COMPRESS_GZIP) {
$DB_DUMP_COMPRESS_PROG = "gzip";
$DB_DUMP_COMPRESS_PROG_OPTIONS = "-9 %sourcedir"
if($DB_DUMP_COMPRESS_PROG_OPTIONS eq "");
}
checkForProg($DB_DUMP_COMPRESS_PROG);
}
}
# --
# determine archiving program, if any
# --
sub determineArchiver {
if($ARCHIVE_PROG ne "") {
print "ARCHIVE_PROG: $ARCHIVE_PROG\n";
print "ARCHIVE_PROG_OPTIONS: $ARCHIVE_PROG_OPTIONS\n";
# options specified on command line
die "Need to specify compressor options when specifying compressor for db dump compression!\n" if($ARCHIVE_PROG_OPTIONS eq "");
}
else {
$ARCHIVE_PROG = "tar";
$ARCHIVE_PROG_OPTIONS = "cj %sourcedir -f %targetfile.tar.bz2"
if($ARCHIVE_PROG_OPTIONS eq "");
}
if($ARCHIVE_BACKUP_COMPRESS_BZIP2) {
$ARCHIVE_BACKUP = 1;
}
elsif($ARCHIVE_BACKUP_COMPRESS_GZIP) {
$ARCHIVE_BACKUP = 1;
$ARCHIVE_PROG_OPTIONS = "cz %sourcedir -f %targetfile.tar.gz";
}
checkForProg($ARCHIVE_PROG);
}
# --
# delete old backups
# --
sub deleteOldBackups {
if($REMOVE_OLD) {
my($dyear,$dmonth,$dday) = Add_Delta_Days($year, $month, $day, -$REMOVE_DAYS);
my $OLDBACKUP = sprintf("%04d-%02d-%02d*",$dyear,$dmonth,$dday);
print "deleting old backups in ${BACKUPDIR}/${OLDBACKUP}...";
File::Remove::remove \1, "${BACKUPDIR}/${OLDBACKUP}";
print "done\n";
}
}
# --
# create backup sub directory
# --
sub createBackupSubdir {
print "Creating backup directory: ${BACKUPDIR}/${SUBBACKUPDIR}...";
mkdir "${BACKUPDIR}/${SUBBACKUPDIR}" ||
die "Making backup dir ${BACKUPDIR}/${SUBBACKUPDIR}: $!";
print "done\n"
}
# --
# dump database
# --
sub dumpDatabase {
my $expanded_options = substituteTags($DB_DUMP_OPTIONS);
print "Preparing to dump $DB rdbms $ConfigObject->[EMAIL PROTECTED]>{DatabaseHost}...";
system "$DB_DUMP_PROG $expanded_options\n";
if(WIFEXITED($?) && WEXITSTATUS($?)) {
die "Dump process exited with status " . WEXITSTATUS($?) . "\n";
}
print "done\n"
}
# --
# compress database dump
# --
sub compressDatabaseDump {
if($DB_DUMP_COMPRESS_DO) {
print "compressing database dump...";
$substituteTable{'sourcedir'} = "${BACKUPDIR}/${SUBBACKUPDIR}/${DB_DUMP_FILE}";
my $expanded_options = substituteTags("$DB_DUMP_COMPRESS_PROG $DB_DUMP_COMPRESS_PROG_OPTIONS");
system "$expanded_options";
if(WIFEXITED($?) && WEXITSTATUS($?)) {
die "Compression process exited with status " . WEXITSTATUS($?) . "\n";
}
print "done\n";
}
}
# --
# config files backup
# --
sub backupConfigFiles {
print "Backing up config files, ${CFGDIR}/* ${CFGDIR}/../Config.pm ...";
mkdir "${BACKUPDIR}/${SUBBACKUPDIR}/Config/";
File::NCopy::copy \1,"${CFGDIR}/*", "${BACKUPDIR}/${SUBBACKUPDIR}/Config/";
File::NCopy::copy "${CFGDIR}/../Config.pm", "${BACKUPDIR}/${SUBBACKUPDIR}/";
print "done\n";
}
# --
# articles backup
# --
sub backupArticles {
my $articleDir = $ConfigObject->Get('ArticleDir');
my $cwd = getcwd();
if(chdir $articleDir) {
print "Backing up $articleDir...";
$substituteTable{'sourcedir'} = ".";
$substituteTable{'targetfile'} = "${BACKUPDIR}/${SUBBACKUPDIR}/article_backup";
if($ARCHIVE) {
my $expanded_options = substituteTags("$ARCHIVE_PROG $ARCHIVE_PROG_OPTIONS");
system "$expanded_options";
if(WIFEXITED($?) && WEXITSTATUS($?)) {
die "Article backup process exited with status " . WEXITSTATUS($?) . "\n";
}
}
else {
mkdir $substituteTable{'targetfile'};
File::NCopy::copy \1,$substituteTable{'sourcedir'},$substituteTable{'targetfile'};
}
chdir $cwd;
print "done\n";
}
}
# --
# archive backup
# --
sub archiveBackup {
if($ARCHIVE_BACKUP) {
print "Compressing ${SUBBACKUPDIR}...";
my $cwd = getcwd();
if(chdir ${BACKUPDIR}) {
$substituteTable{'sourcedir'} = $SUBBACKUPDIR;
$substituteTable{'targetfile'} = $SUBBACKUPDIR;
my $expanded_options = substituteTags("$ARCHIVE_PROG $ARCHIVE_PROG_OPTIONS");
system "$expanded_options";
if(WIFEXITED($?) && WEXITSTATUS($?)) {
die "Archive compression process exited with status " . WEXITSTATUS($?) . "\n";
}
File::Remove::remove \1, "${BACKUPDIR}/${SUBBACKUPDIR}";
chdir $cwd;
print "done\n";
}
}
}
_______________________________________________
OTRS mailing list: dev - Webpage: http://otrs.org/
Archive: http://lists.otrs.org/pipermail/dev
To unsubscribe: http://lists.otrs.org/cgi-bin/listinfo/dev