Advertisement
Guest User

Untitled

a guest
Nov 24th, 2015
62
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.37 KB | None | 0 0
  1. #!/bin/bash
  2. set -o nounset
  3.  
  4. ##########################################
  5. # Defaults
  6. ##########################################
  7. : ${recipient:=}
  8. : ${host:=localhost}
  9. : ${port:=5432}
  10. : ${databases:=}
  11. : ${user:=$(id -un)}
  12. : ${location:=/tmp}
  13. : ${schemas:=}
  14. : ${num_backups:=1}
  15. : ${password:=}
  16. : ${prompt_for_password:=}
  17.  
  18. PSQL="$(command -v -p psql)"
  19. PGDUMP="$(command -v -p pg_dump)"
  20. GPG="$(command -v -p gpg)"
  21.  
  22. ##########################################
  23. # Utility functions and their setup
  24. ##########################################
  25. tmpdir=$(mktemp -d)
  26. cleanup_pids=""
  27.  
  28. function log () {
  29. local level=${1}
  30. shift
  31.  
  32. local end="" start=""
  33.  
  34. if [[ -x $(which tput) && $(tput setaf 1 &> /dev/null; echo $?) == 0 ]]; then
  35. end="$(tput sgr0)"
  36. if [[ ${level} == "info" ]]; then
  37. start="$(tput setaf 2)"
  38. elif [[ ${level} == "warn" ]]; then
  39. start="$(tput setaf 3)"
  40. elif [[ ${level} == "error" ]]; then
  41. start="$(tput setaf 1)"
  42. fi
  43. fi
  44.  
  45. echo -e "${start}${@}${end}" >&2
  46. }
  47.  
  48. function cleanup () {
  49. for pid in ${cleanup_pids}; do
  50. kill -9 ${pid} &> /dev/null
  51. done
  52. cleanup_pids=""
  53.  
  54. rm -rf ${tmpdir}/*
  55. }
  56. trap cleanup SIGINT SIGQUIT SIGTERM
  57.  
  58. function cleanup-on-exit () {
  59. cleanup
  60. rm -rf ${tmpdir}
  61. }
  62. trap cleanup-on-exit SIGKILL EXIT
  63.  
  64. function rotate-backups () {
  65. # start by removing backups numbered greater than $num_backups
  66. for file in $(ls -1 ${dump_file_base}.[0-9]* 2>/dev/null); do
  67. if (( ${file##*.} >= ${num_backups} )); then
  68. rm -f $file
  69. fi
  70. done
  71.  
  72. # rotate backups up one
  73. for ((num = ${num_backups} - 1; num > 0; num--)); do
  74. if [[ -f ${dump_file_base}.${num} ]]; then
  75. ((numplus = ${num} + 1))
  76. mv ${dump_file_base}.${num} ${dump_file_base}.${numplus}
  77. fi
  78. done
  79.  
  80. # rotate the base file up
  81. if ((${num_backups} > 0)); then
  82. if [[ -f ${dump_file_base} ]]; then
  83. mv ${dump_file_base} ${dump_file_base}.1
  84. fi
  85. fi
  86.  
  87. # move the new backup into place
  88. mv ${dump_file} ${dump_file_base}
  89. }
  90.  
  91. function usage () {
  92. cat <<- EOT
  93. Usage : $(basename ${0}) [options] [--]
  94.  
  95. Options:
  96. -l Directory to place backup file
  97. -e GPG recipent key
  98. -u User
  99. -h Database host
  100. -p Port number
  101. -d Database to backup
  102. Multiple -d options can be given
  103. -n Restrict backup to a given schema (ex: public)
  104. Multiple -n options can be given
  105. Keep in mind that any schemas given will apply for all databases
  106. -w Prompt for password
  107. -k Number of old backups to keep (default is one)
  108.  
  109. EOT
  110. }
  111.  
  112. ##########################################
  113. # Arguement parsing
  114. ##########################################
  115. # Require options...
  116. if [[ ${#} == 0 ]]; then
  117. usage
  118. exit 1
  119. fi
  120.  
  121. while getopts "k:e:h:d:u:l:p:n:w" opt; do
  122. case "${opt}" in
  123. e)
  124. [[ ! ${OPTARG} =~ ^- ]] && recipient="${OPTARG}"
  125. ;;
  126.  
  127. h)
  128. [[ ! ${OPTARG} =~ ^- ]] && host="${OPTARG}"
  129. ;;
  130.  
  131. d)
  132. [[ ! ${OPTARG} =~ ^- ]] && databases="${databases:-} ${OPTARG}"
  133. ;;
  134.  
  135. u)
  136. [[ ! ${OPTARG} =~ ^- ]] && user="${OPTARG}"
  137. ;;
  138.  
  139. l)
  140. [[ ! ${OPTARG} =~ ^- ]] && location="${OPTARG}"
  141. ;;
  142.  
  143. p)
  144. [[ ! ${OPTARG} =~ ^- ]] && port="${OPTARG}"
  145. ;;
  146.  
  147. n)
  148. [[ ! ${OPTARG} =~ ^- ]] && schemas="${schemas:-} ${OPTARG}"
  149. ;;
  150.  
  151. w)
  152. prompt_for_password="-W"
  153. ;;
  154.  
  155. k)
  156. [[ ! ${OPTARG} =~ ^- ]] && num_backups="${OPTARG}"
  157. ;;
  158.  
  159. \?)
  160. usage
  161. exit 1
  162. ;;
  163. esac
  164. done
  165. shift $(($OPTIND-1))
  166.  
  167. ##########################################
  168. # Option checking
  169. ##########################################
  170. if [[ -z ${PGDUMP} || ! -x ${PGDUMP} ]]; then
  171. log error "pg_dump not found"
  172. _exit_error=2
  173. fi
  174.  
  175. if [[ -z ${databases} ]]; then
  176. log error "Database was not specified"
  177. _exit_error=2
  178. fi
  179.  
  180. if [[ -n ${recipient} ]]; then
  181. if [[ ! -x ${GPG} ]]; then
  182. log error "GPG exectuable not available, caonnot encrypt backup."
  183. _exit_error=2
  184. fi
  185. if [[ ! $(${GPG} --list-keys | grep uid | grep "${recipient}") ]]; then
  186. log error "GPG encryption key not found for: ${recipient}"
  187. _exit_error=2
  188. fi
  189. fi
  190.  
  191. if [[ ! -d ${location} ]]; then
  192. log error "Backup location must be a directory"
  193. _exit_error=2
  194. elif [[ ! -w ${location} ]]; then
  195. log error "Backup location is not writable (insufficient permissions)"
  196. _exit_error=2
  197. fi
  198.  
  199. if [[ -n ${_exit_error:-} ]]; then
  200. echo
  201. usage
  202. exit ${_exit_error}
  203. fi
  204.  
  205. ##########################################
  206. # Begin the actual backup
  207. ##########################################
  208. if [[ -n ${prompt_for_password} ]]; then
  209. read -p "Enter psql password for ${user}: " -s password
  210. echo
  211. export PGPASSWORD="${password}"
  212. fi
  213.  
  214. schema_opts=""
  215. for schema in ${schemas}; do
  216. schema_opts="${schema_opts} -n ${schema}"
  217. done
  218.  
  219. if [[ -n ${recipient} ]]; then
  220. log info "Encrytping backups for: ${recipient}\n"
  221. fi
  222.  
  223. for database in ${databases}; do
  224. retcode=0
  225. dump_file_base="${location}/${database}.pg_dump${recipient:+.gpg}"
  226. dump_file="${dump_file_base}.new"
  227.  
  228. log info $(date)
  229. log info "Dumping ${database}@${host}:${port} as ${user} to ${dump_file_base}"
  230.  
  231. PGDUMP_COMMAND="${PGDUMP} --ignore-version --format=custom --username ${user} \
  232. --host ${host} --port ${port} ${schema_opts} ${database}"
  233.  
  234. # Check that we can actually dump
  235. ${PGDUMP_COMMAND} -w --schema-only &> /dev/null
  236. if [[ $? != 0 ]]; then
  237. log error "Cannot connect to ${database}@${host}:${port} as ${user}"
  238. log error " You probably need to provide a password"
  239. log error $(date): Backup failed
  240. continue
  241. fi
  242.  
  243. if [[ -n ${recipient:-} ]]; then
  244. ${GPG} --default-recipient ${recipient} -o ${dump_file} \
  245. -e <(${PGDUMP_COMMAND}) 2> ${tmpdir}/gpg_error.log &
  246. gpg_pid=${!}
  247. cleanup_pids+=" ${gpg_pid}"
  248. while $(ps -p ${gpg_pid} &> /dev/null); do
  249. sleep 1 || { retcode=1; break; }
  250. if $(grep -q -i 'no space left on device' ${tmpdir}/gpg_error.log); then
  251. log error "Could not write to ${location}: insufficient space"
  252. cleanup
  253. retcode=1
  254. fi
  255. done
  256. else
  257. ${PGDUMP_COMMAND} -f ${dump_file}
  258. retcode=${?}
  259. fi
  260.  
  261. if [[ ${retcode} != 0 ]]; then
  262. rm -f ${dump_file}
  263. log error "Encountered error during dump, removing partial dump file"
  264. log error $(date): Backup failed
  265. else
  266. # Rotate the old backups now that we know we can connect
  267. log info "Rotating backups for ${database}, keeping ${num_backups}"
  268. rotate-backups
  269.  
  270. log info "$(date): Backup completed"
  271. fi
  272.  
  273. # make sure that our tempdir is clean for the next db
  274. cleanup
  275. done
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement