source: src-sh/lpreserver/backend/functions.sh @ 1996f85

9.2-releaseenter/10releng/10.0releng/10.0.1releng/10.0.2releng/10.0.3releng/10.1releng/10.1.1releng/10.1.2
Last change on this file since 1996f85 was 1996f85, checked in by Kris Moore <kris@…>, 22 months ago

Add new "mirror" functionality to the 'lpreserver' command. This will
easily allow the user to add a new disk, such as an external USB drive
to mirror their existing zpool.

Now we have to do the GUI bits and add options to the installer for
restoring from the external disk

  • Property mode set to 100755
File size: 12.8 KB
Line 
1#!/bin/sh
2# Functions / variables for lpreserver
3######################################################################
4# DO NOT EDIT
5
6# Source external functions
7. /usr/local/share/pcbsd/scripts/functions.sh
8
9# Installation directory
10PROGDIR="/usr/local/share/lpreserver"
11
12# Location of settings
13DBDIR="/var/db/lpreserver"
14if [ ! -d "$DBDIR" ] ; then mkdir -p ${DBDIR} ; fi
15
16CMDLOG="${DBDIR}/lp-lastcmdout"
17CMDLOG2="${DBDIR}/lp-lastcmdout2"
18REPCONF="${DBDIR}/replication"
19LOGDIR="/var/log/lpreserver"
20REPLOGSEND="${LOGDIR}/lastrep-send-log"
21REPLOGRECV="${LOGDIR}/lastrep-recv-log"
22MSGQUEUE="${DBDIR}/.lpreserver.msg.$$"
23export DBDIR LOGDIR PROGDIR CMDLOG REPCONF REPLOGSEND REPLOGRECV MSGQUEUE
24
25# Create the logdir
26if [ ! -d "$LOGDIR" ] ; then mkdir -p ${LOGDIR} ; fi
27
28#Set our Options
29setOpts() {
30  if [ -e "${DBDIR}/recursive-off" ] ; then
31    export RECURMODE="OFF"
32  else
33    export RECURMODE="ON"
34  fi
35
36  if [ -e "${DBDIR}/emaillevel" ] ; then
37    export EMAILMODE="`cat ${DBDIR}/emaillevel`"
38  fi
39
40  if [ -e "${DBDIR}/duwarn" ] ; then
41    export DUWARN="`cat ${DBDIR}/duwarn`"
42  else
43    export DUWARN=85
44  fi
45
46  case $EMAILMODE in
47      ALL|WARN|ERROR) ;;
48        *) export EMAILMODE="WARN";;
49  esac
50
51  if [ -e "${DBDIR}/emails" ] ; then
52    export EMAILADDY="`cat ${DBDIR}/emails`"
53  fi
54
55}
56setOpts
57
58
59# Check if a directory is mounted
60isDirMounted() {
61  mount | grep -q "on $1 ("
62  return $?
63}
64
65mkZFSSnap() {
66  if [ "$RECURMODE" = "ON" ] ; then
67     flags="-r"
68  else
69     flags="-r"
70  fi
71  zdate=`date +%Y-%m-%d-%H-%M-%S`
72  zfs snapshot $flags ${1}@$2${zdate} >${CMDLOG} 2>${CMDLOG}
73  return $?
74}
75
76listZFSSnap() {
77  zfs list -t snapshot | grep -e "^NAME" -e "^${1}@"
78}
79
80rmZFSSnap() {
81  `zfs list -t snapshot | grep -q "^$1@$2 "` || exit_err "No such snapshot!"
82  if [ "$RECURMODE" = "ON" ] ; then
83     flags="-r"
84  else
85     flags="-r"
86  fi
87  zfs destroy -r ${1}@${2} >${CMDLOG} 2>${CMDLOG}
88  return $?
89}
90
91revertZFSSnap() {
92  # Make sure this is a valid snapshot
93  `zfs list -t snapshot | grep -q "^$1@$2 "` || exit_err "No such snapshot!"
94
95  # Rollback the snapshot
96  zfs rollback -R -f ${1}@$2
97}
98
99enable_cron()
100{
101   cronscript="${PROGDIR}/backend/runsnap.sh"
102
103   # Make sure we remove any old entries for this dataset
104   cat /etc/crontab | grep -v " $cronscript $1" > /etc/crontab.new
105   mv /etc/crontab.new /etc/crontab
106   if [ "$2" = "OFF" ] ; then
107      return
108   fi
109
110   case $2 in
111       daily) cLine="0       $4      *       *       *" ;;
112      hourly) cLine="0       *       *       *       *" ;;
113       30min) cLine="0,30    *       *       *       *" ;;
114       10min) cLine="*/10    *       *       *       *" ;;
115        5min) cLine="*/5     *       *       *       *" ;;
116          *) exit_err "Invalid time specified" ;;
117   esac
118
119   echo -e "$cLine\troot    ${cronscript} $1 $3" >> /etc/crontab
120}
121
122enable_watcher()
123{
124   cronscript="${PROGDIR}/backend/zfsmon.sh"
125
126   # Check if the zfs monitor is already enabled
127   grep -q " $cronscript" /etc/crontab
128   if [ $? -eq 0 ] ; then return; fi
129
130   cLine="*/30    *       *       *       *"
131
132   echo -e "$cLine\troot    ${cronscript}" >> /etc/crontab
133}
134
135snaplist() {
136  zfs list -t snapshot | grep "^${1}@" | cut -d '@' -f 2 | awk '{print $1}'
137}
138
139echo_log() {
140   echo "`date`: $@" >> ${LOGDIR}/lpreserver.log
141}
142
143# E-Mail a message to the set addresses
144# 1 = subject tag
145# 2 = Message
146email_msg() {
147   if [ -z "$EMAILADDY" ] ; then return ; fi
148   echo -e "$2"  | mail -s "$1 - `hostname`" $EMAILADDY
149}
150
151queue_msg() {
152  echo -e "$1" >> ${MSGQUEUE}
153  if [ -n "$2" ] ; then
154    cat $2 >> ${MSGQUEUE}
155  fi
156}
157
158echo_queue_msg() {
159  if [ ! -e "$MSGQUEUE" ] ; then return ; fi
160  cat ${MSGQUEUE}
161  rm ${MSGQUEUE}
162}
163
164add_rep_task() {
165  # add freenas.8343 backupuser 22 tank1/usr/home/kris tankbackup/backups sync
166  HOST=$1
167  USER=$2
168  PORT=$3
169  LDATA=$4
170  RDATA=$5
171  TIME=$6
172
173  case $TIME in
174     [0-9][0-9]|sync)  ;;
175     *) exit_err "Invalid time: $TIME"
176  esac
177 
178  echo "Adding replication task for local dataset $LDATA"
179  echo "----------------------------------------------------------"
180  echo "   Remote Host: $HOST" 
181  echo "   Remote User: $USER" 
182  echo "   Remote Port: $PORT" 
183  echo "Remote Dataset: $RDATA" 
184  echo "          Time: $TIME" 
185  echo "----------------------------------------------------------"
186  echo "Don't forget to ensure that this user / dataset exists on the remote host"
187  echo "with the correct permissions!"
188
189  rem_rep_task "$LDATA"
190  echo "$LDATA:$TIME:$HOST:$USER:$PORT:$RDATA" >> ${REPCONF}
191
192  if [ "$TIME" != "sync" ] ; then
193    cronscript="${PROGDIR}/backend/runrep.sh"
194    cLine="0    $TIME       *       *       *"
195    echo -e "$cLine\troot    ${cronscript} ${LDATA}" >> /etc/crontab
196  fi
197}
198
199rem_rep_task() {
200  if [ ! -e "$REPCONF" ] ; then return ; fi
201  cat ${REPCONF} | grep -v "^${1}:" > ${REPCONF}.tmp
202  mv ${REPCONF}.tmp ${REPCONF}
203
204  # Make sure we remove any old replication entries for this dataset
205  cronscript="${PROGDIR}/backend/runrep.sh"
206  cat /etc/crontab | grep -v " $cronscript $1" > /etc/crontab.new
207  mv /etc/crontab.new /etc/crontab
208}
209
210list_rep_task() {
211  if [ ! -e "$REPCONF" ] ; then return ; fi
212
213  echo "Scheduled replications:"
214  echo "---------------------------------"
215
216  while read line
217  do
218     LDATA=`echo $line | cut -d ':' -f 1`
219     TIME=`echo $line | cut -d ':' -f 2`
220     HOST=`echo $line | cut -d ':' -f 3`
221     USER=`echo $line | cut -d ':' -f 4`
222     PORT=`echo $line | cut -d ':' -f 5`
223     RDATA=`echo $line | cut -d ':' -f 6`
224
225     echo "$LDATA -> $USER@$HOST[$PORT]:$RDATA Time: $TIME"
226
227  done < ${REPCONF}
228}
229
230check_rep_task() {
231  export DIDREP=0
232  if [ ! -e "$REPCONF" ] ; then return 0; fi
233
234  repLine=`cat ${REPCONF} | grep "^${1}:"`
235  if [ -z "$repLine" ] ; then return 0; fi
236
237  # We have a replication task for this dataset, lets check if we need to do it now
238  LDATA="$1"
239  REPTIME=`echo $repLine | cut -d ':' -f 2`
240
241  # Export the replication variables we will be using
242  export REPHOST=`echo $repLine | cut -d ':' -f 3`
243  export REPUSER=`echo $repLine | cut -d ':' -f 4`
244  export REPPORT=`echo $repLine | cut -d ':' -f 5`
245  export REPRDATA=`echo $repLine | cut -d ':' -f 6`
246
247  if [ "$2" = "force" ] ; then
248     # Ready to do a forced replication
249     export DIDREP=1
250     echo_log "Starting replication MANUAL task on ${DATASET}: ${REPLOGSEND}"
251     queue_msg "`date`: Starting replication MANUAL task on ${DATASET}\n"
252     start_rep_task "$LDATA"
253     return $?
254  fi
255
256  # If we are checking for a sync task, and the rep isn't marked as sync we can return
257  if [ "$2" = "sync" -a "$REPTIME" != "sync" ] ; then return 0; fi
258
259  # Is this a sync-task we do at the time of a snapshot?
260  if [ "$2" = "sync" -a "$REPTIME" = "sync" ] ; then
261     export DIDREP=1
262     echo_log "Starting replication SYNC task on ${DATASET}: ${REPLOGSEND}"
263     queue_msg "`date`: Starting replication SYNC task on ${DATASET}\n"
264     start_rep_task "$LDATA"
265     return $?
266  else
267     # Ready to do a scheduled replication
268     export DIDREP=1
269     echo_log "Starting replication SCHEDULED task on ${DATASET}: ${REPLOGSEND}"
270     queue_msg "`date`: Starting replication SCHEDULED task on ${DATASET}\n"
271     start_rep_task "$LDATA"
272     return $?
273  fi
274}
275
276start_rep_task() {
277  LDATA="$1"
278  hName=`hostname`
279
280  # Check for the last snapshot marked as replicated already
281  lastSEND=`zfs get -r backup:lpreserver ${LDATA} | grep LATEST | awk '{$1=$1}1' OFS=" " | tail -1 | cut -d '@' -f 2 | cut -d ' ' -f 1`
282
283  # Lets get the last snapshot for this dataset
284  lastSNAP=`zfs list -t snapshot -d 1 -H ${LDATA} | tail -1 | awk '{$1=$1}1' OFS=" " | cut -d '@' -f 2 | cut -d ' ' -f 1`
285 
286  if [ "$lastSEND" = "$lastSNAP" ] ; then
287     queue_msg "`date`: Last snapshot $lastSNAP is already marked as replicated!"
288     return 1
289  fi
290
291  # Starting replication, first lets check if we can do an incremental send
292  if [ -n "$lastSEND" ] ; then
293     zFLAGS="-Rv -I $lastSEND $LDATA@$lastSNAP"
294  else
295     zFLAGS="-Rv $LDATA@$lastSNAP"
296
297     # This is a first-time replication, lets create the new target dataset
298     ssh -p ${REPPORT} ${REPUSER}@${REPHOST} zfs create ${REPRDATA}/${hName} >${CMDLOG} 2>${CMDLOG}
299  fi
300
301  zSEND="zfs send $zFLAGS"
302  zRCV="ssh -p ${REPPORT} ${REPUSER}@${REPHOST} zfs receive -dvuF ${REPRDATA}/${hName}"
303
304  queue_msg "Using ZFS send command:\n$zSEND | $zRCV\n\n"
305
306  # Start up our process
307  $zSEND 2>${REPLOGSEND} | $zRCV >${REPLOGRECV} 2>${REPLOGRECV}
308  zStatus=$?
309  queue_msg "ZFS SEND LOG:\n--------------\n" "${REPLOGSEND}"
310  queue_msg "ZFS RCV LOG:\n--------------\n" "${REPLOGRECV}"
311
312  if [ $zStatus -eq 0 ] ; then
313     # SUCCESS!
314     # Lets mark our new latest snapshot and unmark the last one
315     if [ -n "$lastSEND" ] ; then
316       zfs set backup:lpreserver=' ' ${LDATA}@$lastSEND
317     fi
318     zfs set backup:lpreserver=LATEST ${LDATA}@$lastSNAP
319     echo_log "Finished replication task on ${DATASET}"
320     save_rep_props
321     zStatus=$?
322  else
323     # FAILED :-(
324     # Lets save the output for us to look at later
325     FLOG=${LOGDIR}/lpreserver_failed.log
326     echo "Failed with command:\n$zSEND | $zRCV\n" > ${FLOG}
327     echo "\nSending log:\n" >> ${FLOG}
328     cat ${REPLOGSEND} >> ${FLOG}
329     echo "\nRecv log:\n" >> ${FLOG}
330     cat ${REPLOGRECV} >> ${FLOG}
331     echo_log "FAILED replication task on ${DATASET}: LOGFILE: $FLOG"
332  fi
333
334  return $zStatus
335}
336
337save_rep_props() {
338  # If we are not doing a recursive backup / complete dataset we can skip this
339  if [ "$RECURMODE" != "ON" ] ; then return 0; fi
340  if [ "`basename $DATASET`" != "$DATASET" ] ; then return 0; fi
341
342  echo_log "Saving dataset properties for: ${DATASET}"
343  queue_msg "`date`: Saving dataset properties for: ${DATASET}\n"
344
345  # Lets start by building a list of props to keep
346  rProp=".lp-props-`echo ${REPRDATA}/${hName} | sed 's|/|#|g'`"
347
348  zfs get -r all $DATASET | grep ' local$' | awk '{$1=$1}1' OFS=" " | sed 's| local$||g' \
349        | ssh -p ${REPPORT} ${REPUSER}@${REPHOST} "cat > $rProp"
350  if [ $? -eq 0 ] ; then
351    echo_log "Successful save of dataset properties for: ${DATASET}"
352    queue_msg "`date`: Successful save of dataset properties for: ${DATASET}\n"
353    return 0
354  else
355    echo_log "Failed saving dataset properties for: ${DATASET}"
356    queue_msg "`date`: Failed saving dataset properties for: ${DATASET}\n"
357    return 1
358  fi
359}
360
361listStatus() {
362
363  for i in `grep "${PROGDIR}/backend/runsnap.sh" /etc/crontab | awk '{print $8}'`
364  do
365    echo -e "DATASET - SNAPSHOT - REPLICATION"
366    echo "------------------------------------------"
367
368    lastSEND=`zfs get -r backup:lpreserver ${i} | grep LATEST | awk '{$1=$1}1' OFS=" " | tail -1 | cut -d '@' -f 2 | cut -d ' ' -f 1`
369    lastSNAP=`zfs list -t snapshot -d 1 -H ${i} | tail -1 | awk '{$1=$1}1' OFS=" " | cut -d '@' -f 2 | cut -d ' ' -f 1`
370
371    if [ -z "$lastSEND" ] ; then lastSEND="NONE"; fi
372    if [ -z "$lastSNAP" ] ; then lastSNAP="NONE"; fi
373
374    echo "$i - $lastSNAP - $lastSEND"
375  done
376}
377
378add_mirror_disk() {
379   pool="$1"
380   disk="$2"
381
382   if [ -z "$pool" ] ; then
383      exit_err "No pool specified"
384      exit 0
385   fi
386
387   if [ -z "$disk" ] ; then
388      exit_err "No disk specified"
389      exit 0
390   fi
391
392   # Check if pool exists
393   zpool status $pool >/dev/null 2>/dev/null
394   if [ $? -ne 0 ] ; then exit_err "Invalid pool: $pool"; fi
395
396   # Make sure zpool isn't raid
397   zpool list -H -v ${pool} | grep -q "raid"
398   if [ $? -eq 0 ] ; then exit_err "Cannot mirror a raidz pool!" ; fi
399
400   # Grab the first disk in the pool
401   mDisk=`zpool list -H -v | grep -v "^$pool" | awk '{print $1}' | grep -v "^mirror" | head -n 1`
402
403   # Now we can insert the target disk
404   zpool attach $pool $mDisk $disk
405   if [ $? -ne 0 ] ; then
406      exit_err "Failed attaching $disk"
407   fi
408
409   echo "Added $disk to zpool $pool. Resilver will begin automatically."
410   exit 0
411}
412
413list_mirror_disks() {
414   pool="$1"
415
416   if [ -z "$pool" ] ; then
417      exit_err "No pool specified"
418      exit 0
419   fi
420
421   # Check if pool exists
422   zpool status $pool >/dev/null 2>/dev/null
423   if [ $? -ne 0 ] ; then exit_err "Invalid pool: $pool"; fi
424
425   # Make sure zpool isn't raid
426   zpool list -H -v ${pool} | grep -q "raid"
427   if [ $? -eq 0 ] ; then exit_err "Pool: $pool is raidz!" ; fi
428
429   zpool list -H -v | grep -v "^$pool" | awk '{print $1}' | grep -v "^mirror" | tail +2 > /tmp/.mList.$$
430
431   while read line
432   do
433      echo "$line" | grep -q -e "spare" -e "log" -e "cache"
434      if [ $? -eq 0 ] ; then break ; fi
435
436      echo "$line" 
437   done < /tmp/.mList.$$
438   rm /tmp/.mList.$$
439}
440
441rem_mirror_disk() {
442   pool="$1"
443   disk="$2"
444
445   if [ -z "$pool" ] ; then
446      exit_err "No pool specified"
447      exit 0
448   fi
449
450   if [ -z "$disk" ] ; then
451      exit_err "No disk specified"
452      exit 0
453   fi
454
455   # Check if pool exists
456   zpool status $pool >/dev/null 2>/dev/null
457   if [ $? -ne 0 ] ; then exit_err "Invalid pool: $pool"; fi
458
459   # Make sure zpool isn't raid
460   zpool list -H -v ${pool} | grep -q "raid"
461   if [ $? -eq 0 ] ; then exit_err "Cannot remove disks from a raidz pool!" ; fi
462
463   zpool detach $pool $disk
464   if [ $? -ne 0 ] ; then
465      exit_err "Failed detaching $disk"
466   fi
467   echo "$disk was detached successfully!"
468   exit 0
469}
Note: See TracBrowser for help on using the repository browser.