MrGeneration revised this gist 10 months ago. Go to revision
No changes
MrGeneration revised this gist 10 months ago. Go to revision
No changes
MrGeneration revised this gist 10 months ago. Go to revision
3 files changed, 426 insertions, 1 deletion
README(file created)
| @@ -0,0 +1,16 @@ | |||
| 1 | + | Last change: 2025-09-10 Marcel Herrguth | |
| 2 | + | As shown on https://youtu.be/P-Pr7Dy6mP8 | |
| 3 | + | Attribution back to this GIST is required! | |
| 4 | + | ||
| 5 | + | Parts of this script are oriented from https://github.com/zammad/zammad/tree/develop/contrib/backup | |
| 6 | + | Only PostGreSQL is supported; No symlinks are supported | |
| 7 | + | ||
| 8 | + | Restoration only works on a pre-installed Zammad installation of the same version (or higher). | |
| 9 | + | During Restore, this script will remove the existing (local) Zammad database without any warning; if it's an external one, ensure it's empty. | |
| 10 | + | ||
| 11 | + | ||
| 12 | + | COMMAND REFERENCE | |
| 13 | + | - backup: creates a new snapshot and backs up database and storage. | |
| 14 | + | - restore <snapshot>: Restore a given snapshot from your backup. If you omit the snapshot ID, latest will be used. | |
| 15 | + | - list_snapshots: List all available snapshot IDs. | |
| 16 | + | - verify_health <percentage>: Verify the health of your backup repository. If you omit percentage, 100% will be assumed. | |
init (file deleted)
| @@ -1 +0,0 @@ | |||
| 1 | - | TODO | |
restic_zammad.sh(file created)
| @@ -0,0 +1,410 @@ | |||
| 1 | + | #!/usr/bin/env bash | |
| 2 | + | # | |
| 3 | + | # 2025-09-06 Marcel Herrguth | |
| 4 | + | # As shown on https://youtu.be/P-Pr7Dy6mP8 | |
| 5 | + | # Attribution back to this GIST is required! | |
| 6 | + | # | |
| 7 | + | # Parts of this script are oriented from https://github.com/zammad/zammad/tree/develop/contrib/backup | |
| 8 | + | # Only PostGreSQL is supported; No symlinks are supported | |
| 9 | + | # | |
| 10 | + | # Restoration only works on a pre-installed Zammad installation of the same version (or higher). | |
| 11 | + | # During Restore, this script will remove the existing (local) Zammad database without any warning; if it's an external one, ensure it's empty. | |
| 12 | + | ||
| 13 | + | #----- CONFIG -------------------------------------------------------------------------------# | |
| 14 | + | # Use either a directory or a supported repository type (like s3:https//domain.tld/bucket) | |
| 15 | + | BACKUP_REPOSITORY='s3:https://s3.domain.tld/resticsample' | |
| 16 | + | ||
| 17 | + | # Zammad Installation directory | |
| 18 | + | ZAMMAD_DIR='/opt/zammad' | |
| 19 | + | ||
| 20 | + | # Backup either the whole Zammad directory (yes) or just the storage folder (attachments; no) | |
| 21 | + | # Storage folder only is best practice as per documentation | |
| 22 | + | # https://docs.zammad.org/en/latest/appendix/backup-and-restore/index.html | |
| 23 | + | FULL_FS_DUMP='no' | |
| 24 | + | ||
| 25 | + | # How many daily and hourly snapshot should stay available? | |
| 26 | + | HOLD_DAYS=14 | |
| 27 | + | HOLD_HOURLY=1 | |
| 28 | + | ||
| 29 | + | # Do you want to cause downtime during backup? (This is the safest backup way) | |
| 30 | + | STOP_DURING_BACKUP='yes' | |
| 31 | + | #----- CONFIG END ---------------------------------------------------------------------------# | |
| 32 | + | ||
| 33 | + | # ---- Magic --------------------------------------------------------------------------------# | |
| 34 | + | function pre_flight () { | |
| 35 | + | # Verify that we have all we need. | |
| 36 | + | if [[ ""$BACKUP_REPOSITORY"" == s3:* ]]; then | |
| 37 | + | if [[ -z "${AWS_ACCESS_KEY_ID}" || -z "${AWS_SECRET_ACCESS_KEY}" ]]; then | |
| 38 | + | echo "You have chosen to backup to S3 (it seems), but either AWS_ACCESS_KEY_ID and/or AWS_SECRET_ACCESS_KEY." | |
| 39 | + | exit 1 | |
| 40 | + | fi | |
| 41 | + | else | |
| 42 | + | echo "This script has been tested with S3 only. This should be fine." | |
| 43 | + | echo "Ensure that the required environment information for restic are available as per their documentation:" | |
| 44 | + | echo "https://restic.readthedocs.io/en/stable/030_preparing_a_new_repo.html" | |
| 45 | + | fi | |
| 46 | + | ||
| 47 | + | if [[ -z "${RESTIC_PASSWORD}" ]]; then | |
| 48 | + | echo "You forgot to set RESTIC_PASSWORD which is mandatory for your backup repository!" | |
| 49 | + | exit 1 | |
| 50 | + | fi | |
| 51 | + | } | |
| 52 | + | ||
| 53 | + | function start_zammad () { | |
| 54 | + | echo "# Starting Zammad" | |
| 55 | + | systemctl start zammad | |
| 56 | + | } | |
| 57 | + | ||
| 58 | + | function stop_zammad () { | |
| 59 | + | echo "# Stopping Zammad" | |
| 60 | + | systemctl stop zammad | |
| 61 | + | } | |
| 62 | + | ||
| 63 | + | function get_db_credentials () { | |
| 64 | + | DB_HOST="$(grep -m 1 '^[[:space:]]*host:' < ${ZAMMAD_DIR}/config/database.yml | sed -e 's/.*host:[[:space:]]*//g')" | |
| 65 | + | DB_PORT="$(grep -m 1 '^[[:space:]]*port:' < ${ZAMMAD_DIR}/config/database.yml | sed -e 's/.*port:[[:space:]]*//g')" | |
| 66 | + | DB_NAME="$(grep -m 1 '^[[:space:]]*database:' < ${ZAMMAD_DIR}/config/database.yml | sed -e 's/.*database:[[:space:]]* //g')" | |
| 67 | + | DB_USER="$(grep -m 1 '^[[:space:]]*username:' < ${ZAMMAD_DIR}/config/database.yml | sed -e 's/.*username:[[:space:]]*//g')" | |
| 68 | + | DB_PASS="$(grep -m 1 '^[[:space:]]*password:' < ${ZAMMAD_DIR}/config/database.yml | sed -e 's/.*password:[[:space:]]*//g')" | |
| 69 | + | ||
| 70 | + | # Ensure that HOST and PORT are not empty, provide defaults if needed. | |
| 71 | + | if [ "${DB_HOST}x" == "x" ]; then | |
| 72 | + | DB_HOST="localhost" | |
| 73 | + | fi | |
| 74 | + | if [ "${DB_PORT}x" == "x" ]; then | |
| 75 | + | DB_PORT="5432" | |
| 76 | + | fi | |
| 77 | + | } | |
| 78 | + | ||
| 79 | + | function create_pgpassfile() { | |
| 80 | + | PGPASSFILE=$(mktemp) | |
| 81 | + | export PGPASSFILE | |
| 82 | + | chmod 600 "$PGPASSFILE" | |
| 83 | + | cat <<EOT > "$PGPASSFILE" | |
| 84 | + | *:*:${DB_NAME}:${DB_USER}:${DB_PASS} | |
| 85 | + | EOT | |
| 86 | + | trap 'rm "$PGPASSFILE"' EXIT | |
| 87 | + | } | |
| 88 | + | ||
| 89 | + | function kind_exit () { | |
| 90 | + | # We're nice to our admin and bring Zammad back up before exiting | |
| 91 | + | start_zammad | |
| 92 | + | exit 1 | |
| 93 | + | } | |
| 94 | + | ||
| 95 | + | function db_helper_alter_user () { | |
| 96 | + | # Get DB credentials | |
| 97 | + | get_db_credentials | |
| 98 | + | ||
| 99 | + | if [ "${DB_PASS}x" == "x" ]; then | |
| 100 | + | echo "# Found an empty password - I'll be fixing this for you ..." | |
| 101 | + | ||
| 102 | + | DB_PASS="$(tr -dc A-Za-z0-9 < /dev/urandom | head -c10)" | |
| 103 | + | ||
| 104 | + | sed -e "s/.*username:.*/ username: ${DB_USER}/" \ | |
| 105 | + | -e "s/.*password:.*/ password: ${DB_PASS}/" \ | |
| 106 | + | -e "s/.*database:.*/ database: ${DB_NAME}/" < ${ZAMMAD_DIR}/contrib/packager.io/database.yml.pkgr > ${ZAMMAD_DIR}/config/database.yml | |
| 107 | + | ||
| 108 | + | echo "# ... Fixing permission database.yml" | |
| 109 | + | chown zammad:zammad ${ZAMMAD_DIR}/config/database.yml | |
| 110 | + | fi | |
| 111 | + | ||
| 112 | + | if [ "${DB_USER}x" == "x" ]; then | |
| 113 | + | ||
| 114 | + | echo "ERROR - Your configuration file does not seem to contain a username." | |
| 115 | + | echo "Aborting the script - double check your installation." | |
| 116 | + | ||
| 117 | + | kind_exit | |
| 118 | + | fi | |
| 119 | + | ||
| 120 | + | if [[ ("${DB_HOST}" == "localhost") || "${DB_HOST}" == "127.0.0.1" || "${DB_HOST}" == "::1" ]]; then | |
| 121 | + | # Looks like a local pgsql installation - let's continue | |
| 122 | + | su -c "psql -c \"ALTER USER ${DB_USER} WITH PASSWORD '${DB_PASS}';\"" postgres | |
| 123 | + | state=$? | |
| 124 | + | ||
| 125 | + | else | |
| 126 | + | echo "You don't seem to be using a local PostgreSQL installation." | |
| 127 | + | echo "This script does not support your installation. No changes were done." | |
| 128 | + | echo "Please ensure that your database.yml contains a database user password and that the users password fits." | |
| 129 | + | ||
| 130 | + | kind_exit | |
| 131 | + | fi | |
| 132 | + | ||
| 133 | + | if [ "${state}" != "0" ]; then | |
| 134 | + | echo "ERROR - Our previous command returned an unhandled error code." | |
| 135 | + | kind_exit | |
| 136 | + | fi | |
| 137 | + | } | |
| 138 | + | ||
| 139 | + | function check_for_empty_password() { | |
| 140 | + | if [ "${DB_PASS}x" == "x" ]; then | |
| 141 | + | echo "# ERROR - Found an empty database password ..." | |
| 142 | + | db_helper_alter_user | |
| 143 | + | exit 2 | |
| 144 | + | fi | |
| 145 | + | } | |
| 146 | + | ||
| 147 | + | function check_database_config_exists () { | |
| 148 | + | if [ -f ${ZAMMAD_DIR}/config/database.yml ]; then | |
| 149 | + | get_db_credentials | |
| 150 | + | else | |
| 151 | + | echo -e "${ZAMMAD_DIR}/config/database.yml is missing. is Zammad installed and configured yet? \nAborting..." | |
| 152 | + | exit 1 | |
| 153 | + | fi | |
| 154 | + | } | |
| 155 | + | ||
| 156 | + | function delete_old_backups () { | |
| 157 | + | echo "# Invoking cleanup as per snapshot rules" | |
| 158 | + | restic -r "$BACKUP_REPOSITORY" forget --group-by '' --keep-hourly $HOLD_HOURLY --keep-daily $HOLD_DAYS --prune | |
| 159 | + | } | |
| 160 | + | ||
| 161 | + | function write_backup () { | |
| 162 | + | stop_zammad if "${STOP_DURING_BACKUP}x" == 'yesx' | |
| 163 | + | echo "# Creating postgresql backup..." | |
| 164 | + | ||
| 165 | + | create_pgpassfile | |
| 166 | + | ||
| 167 | + | pg_dump --dbname "${DB_NAME}" \ | |
| 168 | + | --username "${DB_USER}" \ | |
| 169 | + | ${DB_HOST:+--host $DB_HOST} \ | |
| 170 | + | ${DB_PORT:+--port $DB_PORT} \ | |
| 171 | + | --no-privileges --no-owner > /tmp/zammad_db.psql | |
| 172 | + | ||
| 173 | + | state=$? | |
| 174 | + | ||
| 175 | + | if [ "${state}" == "1" ]; then | |
| 176 | + | echo -e "\n\n # ERROR(${state}) - Database credentials are wrong or database server configuration is invalid." | |
| 177 | + | echo -e " #-> BACKUP WAS NOT SUCCESSFUL" | |
| 178 | + | exit 2 | |
| 179 | + | fi | |
| 180 | + | ||
| 181 | + | if [ "${FULL_FS_DUMP}" == 'no' ]; then | |
| 182 | + | BACKUP_DIR="${ZAMMAD_DIR}/storage/" | |
| 183 | + | ||
| 184 | + | if [ ! -d "${ZAMMAD_DIR}/storage/" ]; then | |
| 185 | + | # Admin has requested an attachment backup only, however, there is no storage | |
| 186 | + | # directory. We'll warn Mr.Admin and create the directory as workaround. | |
| 187 | + | echo " ... WARNING: You don't seem to have any attachments in the file system!" | |
| 188 | + | echo " ... This is FINE if you store your attachments either in database or S3!" | |
| 189 | + | echo " ... Creating empty storage directory so the backup can continue ..." | |
| 190 | + | ||
| 191 | + | mkdir -p ${ZAMMAD_DIR}/storage/ | |
| 192 | + | chown -R zammad:zammad ${ZAMMAD_DIR}/storage/ | |
| 193 | + | fi | |
| 194 | + | else | |
| 195 | + | BACKUP_DIR="${ZAMMAD_DIR}" | |
| 196 | + | fi | |
| 197 | + | ||
| 198 | + | restic -r "$BACKUP_REPOSITORY" migrate | |
| 199 | + | restic --no-scan --read-concurrency=25 --pack-size=128 --compression off -r "$BACKUP_REPOSITORY" backup \ | |
| 200 | + | --exclude "${BACKUP_DIR}/tmp/*" --exclude "${BACKUP_DIR}/node_modules/" \ | |
| 201 | + | --exclude "${BACKUP_DIR}/.git/" --exclude "${BACKUP_DIR}/.yarn/cache/" \ | |
| 202 | + | ${BACKUP_DIR} /tmp/zammad_db.psql | |
| 203 | + | ||
| 204 | + | state=$? | |
| 205 | + | ||
| 206 | + | # clean up temporary database dump | |
| 207 | + | rm -f /tmp/zammad_db.psql | |
| 208 | + | ||
| 209 | + | start_zammad if "${STOP_DURING_BACKUP}x" == 'yesx' | |
| 210 | + | ||
| 211 | + | if [ $state == '1' ]; then | |
| 212 | + | echo "# FATAL - Restic could not create the snapshot." | |
| 213 | + | elif [ $state == '3' ]; then | |
| 214 | + | echo "# WARNING - Files changed during snapshot creation; Snapshot potentially incomplete!" | |
| 215 | + | fi | |
| 216 | + | ||
| 217 | + | if [ $state -gt 0 ]; then | |
| 218 | + | echo "# BACKUP WAS NOT SUCCESSFUL" | |
| 219 | + | exit $state | |
| 220 | + | fi | |
| 221 | + | } | |
| 222 | + | ||
| 223 | + | function list_available_snapshots () { | |
| 224 | + | pre_flight | |
| 225 | + | echo "# Here's your currently available snapshots in your backup repository:" | |
| 226 | + | restic -r "$BACKUP_REPOSITORY" snapshots | |
| 227 | + | } | |
| 228 | + | ||
| 229 | + | function verify_backup_repository_health () { | |
| 230 | + | pre_flight | |
| 231 | + | ||
| 232 | + | VERIFY_PERCENTAGE="${1:-100}" | |
| 233 | + | ||
| 234 | + | echo "# Checking ${VERIFY_PERCENTAGE} of your repository ..." | |
| 235 | + | restic -r "$BACKUP_REPOSITORY" check --read-data-subset="${VERIFY_PERCENTAGE}%" | |
| 236 | + | } | |
| 237 | + | ||
| 238 | + | function get_snapshot_id () { | |
| 239 | + | if [ -n "${1}" ]; then | |
| 240 | + | RESTORE_SNAPSHOT_ID="${1}" | |
| 241 | + | else | |
| 242 | + | # User did not provide snapshot so we'll hard guess 'latest' | |
| 243 | + | RESTORE_SNAPSHOT_ID='latest' | |
| 244 | + | ||
| 245 | + | echo "... No Snapshot provided, guessing you want to restore 'latest'!" | |
| 246 | + | for i in {15..1}; do | |
| 247 | + | echo -ne "... You have $i seconds to abort.\r"; sleep 1 | |
| 248 | + | done | |
| 249 | + | fi | |
| 250 | + | } | |
| 251 | + | ||
| 252 | + | function restore_backup () { | |
| 253 | + | echo "# Checking requirements" | |
| 254 | + | ||
| 255 | + | if ! id "zammad" &> /dev/null; then | |
| 256 | + | echo "# ERROR: User 'zammad' is not present, but should be by default! #" | |
| 257 | + | echo "# Restoration was NOT successful, exiting ..." | |
| 258 | + | exit 1 | |
| 259 | + | fi | |
| 260 | + | ||
| 261 | + | stop_zammad | |
| 262 | + | ||
| 263 | + | echo "# ... Dropping current database ${DB_NAME}" | |
| 264 | + | ||
| 265 | + | # This step is only needed for the majority of pgsql dumps, as they don't provide a drop table statement which will cause | |
| 266 | + | # relation errors during restoration (as on package installation there's always an initialized database) | |
| 267 | + | if command -v zammad > /dev/null; then | |
| 268 | + | zammad config:set DISABLE_DATABASE_ENVIRONMENT_CHECK=1 | |
| 269 | + | zammad run rake db:drop | |
| 270 | + | zammad config:set DISABLE_DATABASE_ENVIRONMENT_CHECK=0 | |
| 271 | + | else | |
| 272 | + | DISABLE_DATABASE_ENVIRONMENT_CHECK=1 ${ZAMMAD_DIR}/bin/rake db:drop | |
| 273 | + | fi | |
| 274 | + | ||
| 275 | + | echo "# ... Creating database ${DB_NAME} for owner ${DB_USER}" | |
| 276 | + | if command -v zammad > /dev/null; then | |
| 277 | + | # We'll skip this part for docker installations | |
| 278 | + | su -c "psql -c \"CREATE DATABASE ${DB_NAME} OWNER ${DB_USER};\"" postgres | |
| 279 | + | else | |
| 280 | + | ${ZAMMAD_DIR}/bin/rake db:create | |
| 281 | + | fi | |
| 282 | + | ||
| 283 | + | echo "# Restoring PostgreSQL DB" | |
| 284 | + | ||
| 285 | + | create_pgpassfile | |
| 286 | + | ||
| 287 | + | # We're removing uncritical dump information that caused "ugly" error | |
| 288 | + | # messages on older script versions. These could safely be ignored. | |
| 289 | + | restic -r "$BACKUP_REPOSITORY" dump latest '/tmp/zammad_db.psql' | \ | |
| 290 | + | sed '/^CREATE EXTENSION IF NOT EXISTS plpgsql/d'| \ | |
| 291 | + | sed '/^COMMENT ON EXTENSION plpgsql/d'| \ | |
| 292 | + | psql -q -b -o /dev/null \ | |
| 293 | + | ${DB_HOST:+--host $DB_HOST} ${DB_PORT:+--port $DB_PORT} ${DB_USER:+--username $DB_USER} --dbname ${DB_NAME} | |
| 294 | + | ||
| 295 | + | state=$? | |
| 296 | + | ||
| 297 | + | if [[ ("${state}" == "1") || ( "${state}" == "2") || ( "${state}" == "3") ]]; then | |
| 298 | + | # We're checking for critical restoration errors | |
| 299 | + | # It may not cover all possible errors which is out of scope of this script | |
| 300 | + | echo -e "\n\n # ERROR(${state}) - Database credentials are wrong or database server configuration is invalid." | |
| 301 | + | echo -e " #-> RESTORE WAS NOT SUCCESSFUL" | |
| 302 | + | exit 2 | |
| 303 | + | fi | |
| 304 | + | ||
| 305 | + | if [ "${FULL_FS_DUMP}" == 'no' ]; then | |
| 306 | + | BACKUP_DIR="${ZAMMAD_DIR}/storage/" | |
| 307 | + | else | |
| 308 | + | BACKUP_DIR="${ZAMMAD_DIR}" | |
| 309 | + | fi | |
| 310 | + | ||
| 311 | + | echo "# Restoring Files" | |
| 312 | + | restic -r "$BACKUP_REPOSITORY" restore $RESTORE_SNAPSHOT_ID --include "$BACKUP_DIR" --target / | |
| 313 | + | ||
| 314 | + | state=$? | |
| 315 | + | ||
| 316 | + | if [[ ($state == '1') || ($state == '2') ]]; then | |
| 317 | + | echo "# ERROR(${state}) - File restore reported an error." | |
| 318 | + | echo "- Check file permissions, and ensure Zammad IS NOT running, and try again." | |
| 319 | + | echo -e " \n# RESTORE WAS NOT SUCCESSFUL" | |
| 320 | + | exit 1 | |
| 321 | + | fi | |
| 322 | + | ||
| 323 | + | echo "# Ensuring correct file permissions ..." | |
| 324 | + | chown -R zammad:zammad ${ZAMMAD_DIR} | |
| 325 | + | ||
| 326 | + | # Ensure all data is loaded from the restored database and not the cache of the previous system state | |
| 327 | + | echo "# Clearing Cache ..." | |
| 328 | + | if command -v zammad > /dev/null; then | |
| 329 | + | zammad run rails r "Rails.cache.clear" | |
| 330 | + | else | |
| 331 | + | ${ZAMMAD_DIR}/bin/rails r "Rails.cache.clear" | |
| 332 | + | fi | |
| 333 | + | ||
| 334 | + | echo "# Attempting to execute database migrations" | |
| 335 | + | if command -v zammad > /dev/null; then | |
| 336 | + | zammad run rake db:migrate | |
| 337 | + | else | |
| 338 | + | ${ZAMMAD_DIR}/bin/rake db:migrate | |
| 339 | + | fi | |
| 340 | + | ||
| 341 | + | start_zammad | |
| 342 | + | } | |
| 343 | + | ||
| 344 | + | function start_backup_message () { | |
| 345 | + | echo -e "\n# Backup script started - $(date)!\n" | |
| 346 | + | } | |
| 347 | + | ||
| 348 | + | function start_restore_message () { | |
| 349 | + | echo -e "\n# Restore script started - $(date)!\n" | |
| 350 | + | } | |
| 351 | + | ||
| 352 | + | function finished_backup_message () { | |
| 353 | + | echo -e "\n# Backup script finished; Check output! - $(date)!\n" | |
| 354 | + | } | |
| 355 | + | ||
| 356 | + | function finished_restore_message () { | |
| 357 | + | echo -e "\n# Restore script finished; Check output! - $(date)!\n" | |
| 358 | + | } | |
| 359 | + | ||
| 360 | + | function execute_backup () { | |
| 361 | + | pre_flight | |
| 362 | + | start_backup_message | |
| 363 | + | check_database_config_exists | |
| 364 | + | check_for_empty_password | |
| 365 | + | write_backup | |
| 366 | + | delete_old_backups | |
| 367 | + | finished_backup_message | |
| 368 | + | } | |
| 369 | + | ||
| 370 | + | function execute_restoration () { | |
| 371 | + | pre_flight | |
| 372 | + | check_database_config_exists | |
| 373 | + | check_for_empty_password | |
| 374 | + | start_restore_message | |
| 375 | + | get_snapshot_id $1 | |
| 376 | + | restore_backup | |
| 377 | + | finished_restore_message | |
| 378 | + | } | |
| 379 | + | ||
| 380 | + | function command_reference () { | |
| 381 | + | echo "COMMAND REFERENCE" | |
| 382 | + | echo "- backup: creates a new snapshot and backs up database and storage." | |
| 383 | + | echo "- restore <snapshot>: Restore a given snapshot from your backup. If you omit the snapshot ID, latest will be used." | |
| 384 | + | echo "- list_snapshots: List all available snapshot IDs." | |
| 385 | + | echo "- verify_health <percentage>: Verify the health of your backup repository. If you omit percentage, 100% will be assumed." | |
| 386 | + | } | |
| 387 | + | ||
| 388 | + | # ---- Option part and control --------------------------------------------------------------# | |
| 389 | + | ||
| 390 | + | case "$1" in | |
| 391 | + | backup) | |
| 392 | + | execute_backup | |
| 393 | + | ;; | |
| 394 | + | ||
| 395 | + | restore) | |
| 396 | + | execute_restoration $2 | |
| 397 | + | ;; | |
| 398 | + | ||
| 399 | + | list_snapshots) | |
| 400 | + | list_available_snapshots | |
| 401 | + | ;; | |
| 402 | + | ||
| 403 | + | verify_health) | |
| 404 | + | verify_backup_repository_health "$2" | |
| 405 | + | ;; | |
| 406 | + | ||
| 407 | + | *) | |
| 408 | + | command_reference | |
| 409 | + | ;; | |
| 410 | + | esac | |
MrGeneration revised this gist 10 months ago. Go to revision
1 file changed, 1 insertion
init(file created)
| @@ -0,0 +1 @@ | |||
| 1 | + | TODO | |