source: src-sh/lpreserver/backend/functions.sh @ 6b017ea

9.2-releasereleng/10.0releng/10.0.1
Last change on this file since 6b017ea was 6b017ea, checked in by Kris Moore <kris@…>, 7 months ago

Add a better check to ensure the disk we are trying to add isn't
already in a zpool

  • Property mode set to 100755
File size: 16.7 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  # Doing a replication task, check if one is in progress
260  export pidFile="${DBDIR}/.reptask-`echo ${LDATA} | sed 's|/|-|g'`"
261  if [ -e "${pidFile}" ] ; then
262     pgrep -F ${pidFile} >/dev/null 2>/dev/null
263     if [ $? -eq 0 ] ; then
264        echo_log "Skipped replication on $LDATA, previous replication is still running."
265        return 0
266     else
267        rm ${pidFile}
268     fi
269  fi
270
271  # Save this PID
272  echo "$$" > ${pidFile}
273
274  # Is this a sync-task we do at the time of a snapshot?
275  if [ "$2" = "sync" -a "$REPTIME" = "sync" ] ; then
276     export DIDREP=1
277     echo_log "Starting replication SYNC task on ${DATASET}: ${REPLOGSEND}"
278     queue_msg "`date`: Starting replication SYNC task on ${DATASET}\n"
279     start_rep_task "$LDATA"
280     return $?
281  else
282     # Ready to do a scheduled replication
283     export DIDREP=1
284     echo_log "Starting replication SCHEDULED task on ${DATASET}: ${REPLOGSEND}"
285     queue_msg "`date`: Starting replication SCHEDULED task on ${DATASET}\n"
286     start_rep_task "$LDATA"
287     return $?
288  fi
289}
290
291start_rep_task() {
292  LDATA="$1"
293  hName=`hostname`
294
295  # Check for the last snapshot marked as replicated already
296  lastSEND=`zfs get -r backup:lpreserver ${LDATA} | grep LATEST | awk '{$1=$1}1' OFS=" " | tail -1 | cut -d '@' -f 2 | cut -d ' ' -f 1`
297
298  # Lets get the last snapshot for this dataset
299  lastSNAP=`zfs list -t snapshot -d 1 -H ${LDATA} | tail -1 | awk '{$1=$1}1' OFS=" " | cut -d '@' -f 2 | cut -d ' ' -f 1`
300 
301  if [ "$lastSEND" = "$lastSNAP" ] ; then
302     queue_msg "`date`: Last snapshot $lastSNAP is already marked as replicated!"
303     rm ${pidFile}
304     return 1
305  fi
306
307  # Starting replication, first lets check if we can do an incremental send
308  if [ -n "$lastSEND" ] ; then
309     zFLAGS="-Rv -I $lastSEND $LDATA@$lastSNAP"
310  else
311     zFLAGS="-Rv $LDATA@$lastSNAP"
312
313     # This is a first-time replication, lets create the new target dataset
314     ssh -p ${REPPORT} ${REPUSER}@${REPHOST} zfs create ${REPRDATA}/${hName} >${CMDLOG} 2>${CMDLOG}
315  fi
316
317  zSEND="zfs send $zFLAGS"
318  zRCV="ssh -p ${REPPORT} ${REPUSER}@${REPHOST} zfs receive -dvuF ${REPRDATA}/${hName}"
319
320  queue_msg "Using ZFS send command:\n$zSEND | $zRCV\n\n"
321
322  # Start up our process
323  $zSEND 2>${REPLOGSEND} | $zRCV >${REPLOGRECV} 2>${REPLOGRECV}
324  zStatus=$?
325  queue_msg "ZFS SEND LOG:\n--------------\n" "${REPLOGSEND}"
326  queue_msg "ZFS RCV LOG:\n--------------\n" "${REPLOGRECV}"
327
328  if [ $zStatus -eq 0 ] ; then
329     # SUCCESS!
330     # Lets mark our new latest snapshot and unmark the last one
331     if [ -n "$lastSEND" ] ; then
332       zfs set backup:lpreserver=' ' ${LDATA}@$lastSEND
333     fi
334     zfs set backup:lpreserver=LATEST ${LDATA}@$lastSNAP
335     echo_log "Finished replication task on ${DATASET}"
336     save_rep_props
337     zStatus=$?
338  else
339     # FAILED :-(
340     # Lets save the output for us to look at later
341     FLOG=${LOGDIR}/lpreserver_failed.log
342     echo "Failed with command:\n$zSEND | $zRCV\n" > ${FLOG}
343     echo "\nSending log:\n" >> ${FLOG}
344     cat ${REPLOGSEND} >> ${FLOG}
345     echo "\nRecv log:\n" >> ${FLOG}
346     cat ${REPLOGRECV} >> ${FLOG}
347     echo_log "FAILED replication task on ${DATASET}: LOGFILE: $FLOG"
348  fi
349
350  rm ${pidFile}
351  return $zStatus
352}
353
354save_rep_props() {
355  # If we are not doing a recursive backup / complete dataset we can skip this
356  if [ "$RECURMODE" != "ON" ] ; then return 0; fi
357  if [ "`basename $DATASET`" != "$DATASET" ] ; then return 0; fi
358
359  echo_log "Saving dataset properties for: ${DATASET}"
360  queue_msg "`date`: Saving dataset properties for: ${DATASET}\n"
361
362  # Lets start by building a list of props to keep
363  rProp=".lp-props-`echo ${REPRDATA}/${hName} | sed 's|/|#|g'`"
364
365  zfs get -r all $DATASET | grep ' local$' | awk '{$1=$1}1' OFS=" " | sed 's| local$||g' \
366        | ssh -p ${REPPORT} ${REPUSER}@${REPHOST} "cat > $rProp"
367  if [ $? -eq 0 ] ; then
368    echo_log "Successful save of dataset properties for: ${DATASET}"
369    queue_msg "`date`: Successful save of dataset properties for: ${DATASET}\n"
370    return 0
371  else
372    echo_log "Failed saving dataset properties for: ${DATASET}"
373    queue_msg "`date`: Failed saving dataset properties for: ${DATASET}\n"
374    return 1
375  fi
376}
377
378listStatus() {
379
380  for i in `grep "${PROGDIR}/backend/runsnap.sh" /etc/crontab | awk '{print $8}'`
381  do
382    echo -e "DATASET - SNAPSHOT - REPLICATION"
383    echo "------------------------------------------"
384
385    lastSEND=`zfs get -r backup:lpreserver ${i} | grep LATEST | awk '{$1=$1}1' OFS=" " | tail -1 | cut -d '@' -f 2 | cut -d ' ' -f 1`
386    lastSNAP=`zfs list -t snapshot -d 1 -H ${i} | tail -1 | awk '{$1=$1}1' OFS=" " | cut -d '@' -f 2 | cut -d ' ' -f 1`
387
388    if [ -z "$lastSEND" ] ; then lastSEND="NONE"; fi
389    if [ -z "$lastSNAP" ] ; then lastSNAP="NONE"; fi
390
391    echo "$i - $lastSNAP - $lastSEND"
392  done
393}
394
395add_zpool_disk() {
396   pool="$1"
397   disk="$2"
398   disk="`echo $disk | sed 's|/dev/||g'`"
399
400   if [ -z "$pool" ] ; then
401      exit_err "No pool specified"
402      exit 0
403   fi
404
405   if [ -z "$disk" ] ; then
406      exit_err "No disk specified"
407      exit 0
408   fi
409
410   if [ ! -e "/dev/$disk" ] ; then
411      exit_err "No such device: $disk"
412      exit 0
413   fi
414
415   zpool list -H -v | awk '{print $1}' | grep -q "^$disk"
416   if [ $? -eq 0 ] ; then
417      exit_err "Error: This disk is already apart of a zpool!"
418   fi
419
420   # Check if pool exists
421   zpool status $pool >/dev/null 2>/dev/null
422   if [ $? -ne 0 ] ; then exit_err "Invalid pool: $pool"; fi
423
424   # Cleanup the target disk
425   echo "Deleting all partitions on: $disk"
426   rc_nohalt "gpart destroy -F $disk" >/dev/null 2>/dev/null
427   rc_nohalt "dd if=/dev/zero of=/dev/${disk} bs=1m count=1" >/dev/null 2>/dev/null
428   rc_nohalt "dd if=/dev/zero of=/dev/${disk} bs=1m oseek=`diskinfo /dev/${disk} | awk '{print int($3 / (1024*1024)) - 4;}'`" >/dev/null 2>/dev/null
429
430   # Grab the first disk in the pool
431   mDisk=`zpool list -H -v | grep -v "^$pool" | awk '{print $1}' | grep -v "^mirror" | grep -v "^raidz" | head -n 1`
432
433   # Is this MBR or GPT?
434   echo $mDisk | grep -q 's[0-4][a-z]$'
435   if [ $? -eq 0 ] ; then
436      # MBR
437      type="MBR"
438      # Strip off the "a-z"
439      rDiskDev=`echo $mDisk | rev | cut -c 2- | rev`
440   else
441      # GPT
442      type="GPT"
443      # Strip off the "p[1-9]"
444      rDiskDev=`echo $mDisk | rev | cut -c 3- | rev`
445   fi
446
447   # Make sure this disk has a layout we can read
448   gpart show $rDiskDev >/dev/null 2>/dev/null
449   if [ $? -ne 0 ] ; then
450      exit_err "failed to get disk device layout $rDiskDev"
451   fi
452
453   # Get the size of "freebsd-zfs & freebsd-swap"
454   sSize=`gpart show ${rDiskDev} | grep freebsd-swap | cut -d "(" -f 2 | cut -d ")" -f 1`
455   zSize=`gpart show ${rDiskDev} | grep freebsd-zfs | cut -d "(" -f 2 | cut -d ")" -f 1`
456   
457   echo "Creating new partitions on $disk"
458   if [ "$type" = "MBR" ] ; then
459      # Create the new MBR layout
460      rc_halt_s "gpart create -s MBR -f active $disk"
461      rc_halt_s "gpart add -a 4k -t freebsd $disk"     
462      rc_halt_s "gpart set -a active -i 1 $disk"
463      rc_halt_s "gpart create -s BSD ${disk}s1"
464      rc_halt_s "gpart add -t freebsd-zfs -s $zSize ${disk}s1"
465      if [ -n "$sSize" ] ; then
466        rc_halt_s "gpart add -t freebsd-swap -s $sSize ${disk}s1"
467      fi
468      aDev="${disk}s1a"
469   else
470      # Creating a GPT disk
471      rc_halt_s "gpart create -s GPT $disk"
472      rc_halt_s "gpart add -b 34 -s 1M -t bios-boot $disk"
473      rc_halt_s "gpart add -t freebsd-zfs -s $zSize ${disk}"
474      if [ -n "$sSize" ] ; then
475        rc_halt_s "gpart add -t freebsd-swap -s $sSize ${disk}"
476      fi
477      aDev="${disk}p2"
478   fi
479
480   # Now we can insert the target disk
481   echo "Attaching to zpool: $aDev"
482   rc_halt_s "zpool attach $pool $mDisk $aDev"
483
484   # Lastly we need to stamp GRUB
485   echo "Stamping GRUB on: $disk"
486   rc_halt_s "grub-install --force /dev/${disk}"
487
488   echo "Added $disk ($aDev) to zpool $pool. Resilver will begin automatically."
489   exit 0
490}
491
492list_zpool_disks() {
493   pool="$1"
494
495   if [ -z "$pool" ] ; then
496      exit_err "No pool specified"
497      exit 0
498   fi
499
500   # Check if pool exists
501   zpool status $pool >/dev/null 2>/dev/null
502   if [ $? -ne 0 ] ; then exit_err "Invalid pool: $pool"; fi
503
504   zpool list -H -v $pool
505}
506
507rem_zpool_disk() {
508   pool="$1"
509   disk="$2"
510
511   if [ -z "$pool" ] ; then
512      exit_err "No pool specified"
513      exit 0
514   fi
515
516   if [ -z "$disk" ] ; then
517      exit_err "No disk specified"
518      exit 0
519   fi
520
521   # Check if pool exists
522   zpool status $pool >/dev/null 2>/dev/null
523   if [ $? -ne 0 ] ; then exit_err "Invalid pool: $pool"; fi
524
525   zpool detach $pool $disk
526   if [ $? -ne 0 ] ; then
527      exit_err "Failed detaching $disk"
528   fi
529   echo "$disk was detached successfully!"
530   exit 0
531}
532
533offline_zpool_disk() {
534   pool="$1"
535   disk="$2"
536
537   if [ -z "$pool" ] ; then
538      exit_err "No pool specified"
539      exit 0
540   fi
541
542   if [ -z "$disk" ] ; then
543      exit_err "No disk specified"
544      exit 0
545   fi
546
547   # Check if pool exists
548   zpool status $pool >/dev/null 2>/dev/null
549   if [ $? -ne 0 ] ; then exit_err "Invalid pool: $pool"; fi
550
551   zpool offline $pool $disk
552   exit $?
553}
554
555online_zpool_disk() {
556   pool="$1"
557   disk="$2"
558
559   if [ -z "$pool" ] ; then
560      exit_err "No pool specified"
561      exit 0
562   fi
563
564   if [ -z "$disk" ] ; then
565      exit_err "No disk specified"
566      exit 0
567   fi
568
569   # Check if pool exists
570   zpool status $pool >/dev/null 2>/dev/null
571   if [ $? -ne 0 ] ; then exit_err "Invalid pool: $pool"; fi
572
573   zpool online $pool $disk
574   exit $?
575}
576
577init_rep_task() {
578
579  LDATA="$1"
580
581  repLine=`cat ${REPCONF} | grep "^${LDATA}:"`
582  if [ -z "$repLine" ] ; then return 0; fi
583 
584  # We have a replication task for this set, get some vars
585  hName=`hostname`
586  REPHOST=`echo $repLine | cut -d ':' -f 3`
587  REPUSER=`echo $repLine | cut -d ':' -f 4`
588  REPPORT=`echo $repLine | cut -d ':' -f 5`
589  REPRDATA=`echo $repLine | cut -d ':' -f 6`
590
591  # First check if we even have a dataset on the remote
592  ssh -p ${REPPORT} ${REPUSER}@${REPHOST} zfs list ${REPRDATA}/${hName} 2>/dev/null >/dev/null
593  if [ $? -eq 0 ] ; then
594     # Lets cleanup the remote side
595     echo "Removing remote dataset: ${REPRDATA}/${hName}"
596     ssh -p ${REPPORT} ${REPUSER}@${REPHOST} zfs destroy -r ${REPRDATA}/${hName}
597     if [ $? -ne 0 ] ; then
598        echo "Warning: Could not delete remote dataset ${REPRDATA}/${hName}"
599     fi
600  fi
601
602  # Now lets mark none of our datasets as replicated
603  lastSEND=`zfs get -r backup:lpreserver ${LDATA} | grep LATEST | awk '{$1=$1}1' OFS=" " | tail -1 | cut -d '@' -f 2 | cut -d ' ' -f 1`
604  if [ -n "$lastSEND" ] ; then
605     zfs set backup:lpreserver=' ' ${LDATA}@$lastSEND
606  fi
607
608}
Note: See TracBrowser for help on using the repository browser.