-
Notifications
You must be signed in to change notification settings - Fork 2
/
backup_zimbra
executable file
·238 lines (202 loc) · 10.8 KB
/
backup_zimbra
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
#!/bin/bash
# Zimbra Backup Script
# may require ncftp to run depending on your wishes
# Copyright (C) 2014-2015 Alexander Swen <alex@swen.nu>
# 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, see <http://www.gnu.org/licenses/>.
# This script is intended to run from the crontab as root
# Date outputs and su vs sudo corrections by other contributors, thanks, sorry I don't have names to attribute!
# Free to use and free of any warranty! Daniel W. Martin, 5 Dec 2008
# croninfo:
# MAILTO=alex@swen.nu
# 0 3 * * * root /netwerk/scripts/backup_zimbra >> /var/log/zimbra_backup_log 2>&1
# Alexander Swen
# Private contact: alex@swen.nu
# CHANGELOG:
# 2009-08-30 A.Swen copied from Daniel W. Martin and modified a little
# 2010-10-09 A.Swen added weekly tar + cleanup
# 2010-10-09 A.Swen added diskspace checks
# 2010-10-12 A.Swen added iscsi mountcheck
# 2010-10-17 A.Swen improved diskspace checks
# 2010-10-19 A.Sweb added rsync to remote option
# TODO
# - Add a rsync to remote host (similar to ncftp) using ssh tunnel for offsite backup. I also consider the use of xdelta to make a diff of the tbz2's and upload that. that consumes the least bandwith. however, on the remote host we have to patch the tbz2 afterwards, or we just keep the patchfiles and patch when we need them. or I do both rsync and xdelta.
# SETTINGS
date=$(date +%Y%m%d)
me=$(basename $0)
mydir=$(dirname $0)
zimbra_dir=/opt/zimbra
short_log=/var/log/zimbra_backup_short_log
mailadm=alex@swen.nu
# bck_root should be the mountpoint of a separate volume where we write backups to. I recommend to use some kind of remote volume for this (either NFS or ISCSI). That is to make sure that whenever you need to restore the complete install of your mailserver you don't have to care about this volume as long as it is not mounted and whenever you want to test a restore on some other box (which is HIGHLY recommended!!!) you simply umount and mount the iscsi volume or even simpler only mount the nfs volume on the testbox.
bck_root=/zimbra_backups #Warning: keep this only one dir deep as I don't know how to tell awk to ignore more slashes in the search
# with the var below we create 7 daily backupdirs so we have can go back any day last week.
bck_dir=/${bck_root}/daily-$(date +%w)
# we create a tbz2 archive of the daily-6 dir every Saturday morning
bck_dir_2=/${bck_root}/weekly
# this long I want to keep versions of the complete weekly tarballs
keepdays=30
# ISCSI SETTINGS
# ISCSI and LVM config (I Used lvm on Iscsi to be able to attach and resize the volume while the mailserver keeps running)
# If you don't use ISCSI please set var ISCSI to something else then on. Otherwise this script fails trying to enable ISCSI
ISCSI=on
iscsi_volume="192.168.4.4:3260,1 cider:zimbra.backups"
vgname=vg_zimbra_backups
lvname=lv_zimbra_backups
vol_label=zimbra_backups
# remote RSYNC settings
remote_rsync=on
remote_rsync_host=
remote_rsync_port=22
remote_rsync_user=root
remote_rsync_id_file=/root/.ssh/id_rsa
remote_rsync_dir=/data/backups/zimbra
rsync_bwlimit=50
# FUNCTIONS
die () {
echo "FATAL: $@" >> ${short_log}
rc=$1
shift
echo "========================================================================" >&2
echo "==== FATAL ERROR ====" >&2
echo "========================================================================" >&2
echo "" >&2
echo $@ >&2
echo $@|mail -s "$(hostname) zimbra backup ERROR" ${mailadm}
exit $rc
}
# SCRIPT
echo "$(date) started ${me}..." |tee -a ${short_log}
[ ${UID} -gt 0 ] && die 1 only root may do that
[ -d ${bck_dir} ] || install -dm 770 -o zimbra -g zimbra ${bck_dir}
[ -d ${bck_dir_2} ] || install -dm 770 -o zimbra -g zimbra ${bck_dir_2}
echo ""
echo "$(date) checking mount status..." |tee -a ${short_log}
echo "Since we have an external volume to write backups to we need to verify its mount status"
if [ "$(mount|grep "${bck_root}")" = "" ];then
echo "mountpoint ${bck_root} not mounted..."
if [ "${ISCSI}" = "on" ];then
echo "ISCSI seems to be turned on so we first check whether ISCSI volume is available"
[ "$(chkconfig --list|awk '/iscsid/ {print $5}')" != "3:on" ] && chkconfig iscsid on
[ "$(service iscsid status|awk '/iscsid/ && /running/ {print $5}')" != "running..." ] && service iscsi start
iscsiinfo="$(iscsiadm -m node show)"
[ "${iscsiinfo}" != "${iscsi_volume}" ] && die 2 "ISCSI volume not available"
echo "check ISCSI: ${isciinfo} OK"
fi
echo "checking LV state"
[ "$(lvscan|awk -v vg=${vgname} -v lv=${lvname} '/vg/ && /lv/ {print $1}')" != "ACTIVE" ] && lvchange -a y ${vgname}
[ "$(blkid|awk -v label=${vol_label} -v vg=${vgname} -v lv=${lvname} '/label/ && /vg/ && /lv/ {print}')" = "" ] && die 3 "After checking ISCSI and LVM there is still no volume available that has label ${vol_label}"
[ "$(awk -v mp=${bck_root} -v label=${vol_label} '/mp/ && /label/ {print}')" = "" ] && echo "LABEL=zimbra_backups /zimbra_backups ext3 defaults 0 0" >> /etc/fstab
echo "now we try to mount the bckroot ${bck_root}"
[ -d ${bck_root} ] && mount ${bck_root} || die 3 "Mount ${bck_root} failed"
echo "As above output might already suggested I tried to (re-)mount the iscsi volume. I did al I can. Hope it works, at least I continue this script now...."
else
echo "$(date) mountpoint ${bck_root} seems to be mounted OK..." |tee -a ${short_log}
df -m ${bck_root}
fi
echo ""
echo "$(date) checking free disk space..." |tee -a ${short_log}
zimbra_dir_sz=$(du -sm ${zimbra_dir}|awk '{print $1}')
today_bck_sz=$([ -d ${bck_dir} ] && du -sm ${bck_dir}|awk '{print $1}' || echo 0)
bck_root_free=$(df -m ${bck_root}|awk '/\'$bck_root'/ {print $3} ' ) # vi doesn't always understand quotes and brackets )
if [ $(date +%w) -eq 6 ];then
# on Saturday we want the difference between last weeks backup and todays zimbra dir size + the size of the biggest weekly tarball (as we might expect we make one of at least that size) + 1024M margin for growth, free on the bckdir.
biggest_tbz2_sz=$(ls -lrS --block-size=1048576 ${bck_dir_2}/*.tbz2|tail -1|awk '{print $5}')
minimal_free=$((zimbra_dir_sz-today_bck_sz+biggest_tbz2_sz+1024))
else
# The rest of the week we just accept a free space as big as the difference between last weeks backup and todays zimbra dir size
minimal_free=$((zimbra_dir_sz-today_bck_sz))
fi
cat << EOF
Zimbradir = ${zimbra_dir_sz}Mb
Todays backupdirsize = ${today_bck_sz}Mb
minimal free = ${minimal_free}Mb
Backupdir ${bck_root} has ${bck_root_free}Mb free
EOF
[ ${minimal_free} -gt ${bck_root_free} ]&& die 4 not enough free diskspace on ${bck_root} to perform backup
# Outputs the time the backup started, for log/tracking purposes
before="$(date +%s)"
# Live sync before stopping Zimbra to minimize sync time with the services down
# Comment out the following line if you want to try single cold-sync only
echo ""
echo "$(date +%T) 1st rsync backup started..." |tee -a ${short_log}
rsync -avHK --delete --exclude=*.pid ${zimbra_dir}/ ${bck_dir}
# which is the same as: /opt/zimbra /backup
# Including --delete option gets rid of files in the dest folder that don't exist at the src # vi doesn't always understand quotes
# this prevents logfile/extraneous bloat from building up overtime.
# Now we need to shut down Zimbra to rsync any files that were/are locked
# whilst backing up when the server was up and running.
before2="$(date +%s)"
# Stop Zimbra Services
echo ""
echo "$(date +%T) Stopping zimbra..." |tee -a ${short_log}
su - zimbra -c"${zimbra_dir}/bin/zmcontrol stop"
sleep 15
# Kill any orphaned Zimbra processes
echo ""
echo "$(date +%T) Kill any orphaned Zimbra processes..." |tee -a ${short_log}
ps -u zimbra -o "pid="|while read pid;do kill -9 ${pid};done
# Only enable the following command if you need all Zimbra user owned
# processes to be killed before syncing
# ps auxww | awk '{print $1" "$2}' | grep zimbra | kill -9 `awk '{print $2}'`
# Sync to backup directory
echo ""
echo "$(date +%T) 2nd rsync backup started..." |tee -a ${short_log}
rsync -avHK --delete --exclude=*.pid ${zimbra_dir}/ ${bck_dir}
# Restart Zimbra Services
echo ""
echo "$(date +%T) Starting Zimbra..." |tee -a ${short_log}
su - zimbra -c "${zimbra_dir}/bin/zmcontrol start"
# Calculates and outputs amount of time the server was down for
after="$(date +%s)"
elapsed="$(expr $after - $before2)"
hours=$(($elapsed / 3600))
elapsed=$(($elapsed - $hours * 3600))
minutes=$(($elapsed / 60))
seconds=$(($elapsed - $minutes * 60))
echo ""
echo Server was down for: "$hours hours $minutes minutes $seconds seconds" |tee -a ${short_log}
# Create a txt file in the backup directory that'll contains the current Zimbra # vi doesn't always understand quotes
# server version. Handy for knowing what version of Zimbra a backup can be restored to.
su - zimbra -c "zmcontrol -v "> ${bck_dir}/zimbra_version.txt
# or examine your /opt/zimbra/.install_history
# Display Zimbra services status
echo ""
echo $(date +%T) Displaying Zimbra services status...
su - zimbra -c "${zimbra_dir}/bin/zmcontrol status"
# Create archive of backed-up directory for offsite transfer
if [ $(date +%w) -eq 6 ];then
echo ""
echo "$(date +%T) Creating weekly tbz2..." |tee -a ${short_log}
tar jcf ${bck_dir_2}/$(date +%Y-%m-%d)-$(hostname).tbz2 -C ${bck_dir} .
echo "$(date +%T) Cleaning up weekly backups older than ${keepdays} days..." |tee -a ${short_log}
find ${bck_dir_2} -type f -mtime +${keepdays} -delete
fi
# Transfer file to external backup server
if [ "${remote_rsync}" = "on" ];then
echo "$(date +%T) starting rsync ${bck_dir} to ${remote_rsync_host}:${remote_rsync_dir}" |tee -a ${short_log}
rsync -zavHK --bwlimit=${rsync_bwlimit} -e "ssh -p${remote_rsync_port} -l ${remote_rsync_user} -i ${remote_rsync_id_file}" --delete --exclude=*.pid ${bck_dir} ${remote_rsync_host}:${remote_rsync_dir}
echo "$(date +%T) done rsync ${bck_dir} to ${remote_rsync_host}:${remote_rsync_dir}" |tee -a ${short_log}
fi
# Outputs the time the backup finished
echo ""
echo "$(date +%T) Backup finished" |tee -a ${short_log}
# Calculates and outputs total time taken
after="$(date +%s)"
elapsed="$(expr $after - $before)"
hours=$(($elapsed / 3600))
elapsed=$(($elapsed - $hours * 3600))
minutes=$(($elapsed / 60))
seconds=$(($elapsed - $minutes * 60))
echo Time taken: "$hours hours $minutes minutes $seconds seconds" |tee -a ${short_log}
cat ${short_log}|/bin/mail -s "$(hostname) Zimbra backup results" ${mailadm}
# END