|
#!/bin/bash |
|
# Create Hetzner virtual server snapshots |
|
# Version 1.2 (build 20190723) |
|
# |
|
# Copyright (C) 2017-2019 Daniel Rudolf <www.daniel-rudolf.de> |
|
# |
|
# 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, version 3 of the License only. |
|
# |
|
# 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. |
|
# |
|
# See <http://www.gnu.org/licenses/> to receive a full-text-copy of |
|
# the GNU General Public License. |
|
|
|
APP_NAME="$(basename "$0")" |
|
set -e |
|
|
|
VERSION="1.2" |
|
BUILD="20190723" |
|
|
|
if [ ! -e /etc/hetzner_api ]; then |
|
echo "$APP_NAME: Unable to read Hetzner API credentials from '/etc/hetzner_api': No such file or directory" >&2 |
|
exit 1 |
|
elif [ ! -f /etc/hetzner_api ]; then |
|
echo "$APP_NAME: Unable to read Hetzner API credentials from '/etc/hetzner_api': Not a regular file" >&2 |
|
exit 1 |
|
elif [ ! -r /etc/hetzner_api ]; then |
|
echo "$APP_NAME: Unable to read Hetzner API credentials from '/etc/hetzner_api': Permission denied" >&2 |
|
exit 1 |
|
fi |
|
|
|
. /etc/hetzner_api |
|
. /usr/local/lib/hetzner_api/lib.sh |
|
|
|
print_usage() { |
|
echo "Usage:" |
|
echo " $APP_NAME [--latest|--oldest] IP_ADDRESS" |
|
} |
|
|
|
# read parameters |
|
SERVER_IP="" |
|
SNAPSHOT_SELECT="latest" |
|
|
|
while [ $# -gt 0 ]; do |
|
if [ "$1" == "--oldest" ] || [ "$1" == "-o" ]; then |
|
SNAPSHOT_SELECT="oldest" |
|
elif [ "$1" == "--latest" ] || [ "$1" == "-l" ]; then |
|
SNAPSHOT_SELECT="latest" |
|
elif [ "$1" == "--help" ] || [ "$1" == "-h" ]; then |
|
print_usage |
|
echo |
|
echo "Application options:" |
|
echo " -l, --latest replace the server's latest snapshot (default)" |
|
echo " -o, --oldest replace the server's oldest snapshot" |
|
echo |
|
echo "Help options:" |
|
echo " -h, --help display this help and exit" |
|
echo " --version output version information and exit" |
|
exit 0 |
|
elif [ "$1" == "--version" ]; then |
|
echo "hetzner-snapshot.sh $VERSION ($BUILD)" |
|
echo "Copyright (C) 2017-2019 Daniel Rudolf" |
|
echo "License GPLv3: GNU GPL version 3 only <http://gnu.org/licenses/gpl.html>." |
|
echo "This is free software: you are free to change and redistribute it." |
|
echo "There is NO WARRANTY, to the extent permitted by law." |
|
echo |
|
echo "Written by Daniel Rudolf <http://www.daniel-rudolf.de/>" |
|
exit 0 |
|
elif [ -z "$SERVER_IP" ]; then |
|
if ! [[ "$1" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then |
|
echo "$APP_NAME: Invalid IP address specified" >&2 |
|
exit 1 |
|
fi |
|
|
|
SERVER_IP="$1" |
|
else |
|
print_usage >&2 |
|
exit 1 |
|
fi |
|
|
|
shift |
|
done |
|
|
|
if [ -z "$SERVER_IP" ]; then |
|
print_usage >&2 |
|
exit 1 |
|
fi |
|
|
|
# function: returns the latest or oldest snapshot |
|
get_snapshot() { |
|
local SNAPSHOT="" |
|
local SNAPSHOT_RESPOSNE="" |
|
|
|
hetzner_api_call --output-var SNAPSHOT_RESPONSE --show-error "GET" "/snapshot/$SERVER_IP" >&2 |
|
|
|
SNAPSHOT="$(echo "$SNAPSHOT_RESPONSE" | php -r ' |
|
$json = json_decode(stream_get_contents(STDIN), true); |
|
if (is_array($json)) { |
|
if (empty($json)) { |
|
exit(0); |
|
} |
|
|
|
$json = (isset($_SERVER["argv"]) && ($_SERVER["argv"] === "--latest")) ? array_reverse($json) : $json; |
|
foreach ($json as $item) { |
|
if (isset($item["snapshot"]) && ($item["snapshot"]["type"] === "snapshot")) { |
|
echo $item["snapshot"]["id"]; |
|
exit(0); |
|
} |
|
} |
|
} |
|
|
|
exit(1); |
|
' -- "--$SNAPSHOT_SELECT")" |
|
|
|
if [ $? -ne 0 ]; then |
|
echo "$APP_NAME: Hetzner API call 'GET /snapshot/$SERVER_IP' failed: Invalid JSON response: $SNAPSHOT_RESPONSE" >&2 |
|
exit 1 |
|
fi |
|
|
|
if [ -n "$SNAPSHOT" ]; then |
|
echo "$SNAPSHOT" |
|
return 0 |
|
fi |
|
|
|
return 1 |
|
} |
|
|
|
# get list of existing snapshots |
|
echo "Retrieving list of snapshots..." |
|
SNAPSHOT="$(get_snapshot || true)" |
|
|
|
# delete the latest snapshot |
|
if [ -n "$SNAPSHOT" ]; then |
|
echo -n "Deleting $SNAPSHOT_SELECT snapshot" |
|
hetzner_api_call --show-error "DELETE" "/snapshot/$SERVER_IP/$SNAPSHOT" |
|
|
|
# deleting a snapshot might take some time |
|
for (( i=1 ; i < 100 ; i++ )); do |
|
echo -n "." |
|
if [ $(($i % 3)) -ne 0 ]; then |
|
sleep 3 |
|
elif [ "$(get_snapshot || true)" != "$SNAPSHOT" ]; then |
|
break |
|
fi |
|
done |
|
echo |
|
|
|
if [ $i -eq 100 ]; then |
|
echo "$APP_NAME: Unable to delete latest snapshot" >&2 |
|
exit 1 |
|
fi |
|
fi |
|
|
|
# create a new snapshot |
|
echo "Creating snapshot..." |
|
hetzner_api_call --show-error "POST" "/snapshot/$SERVER_IP" |
Hi @PhrozenByte
I just came across your shell script here because there is nothing like it out there and I hate all those external backup services, all very complicated and time-consuming, the snapshots is a goldmine people should be using more to do backups.
Your script seems to sweet and promising, here's the catch:
But.. if anyone has a big heart and can write in the comments what a noob like me needs to do in order to get this running, i would appreciate it, I use aaPanel as a Web Server Panel for my cloud instance, i could upload files to /root/ with the uploader in the GUI if that helps?! And I can login to "root" and execute commands if it's not too complex..
You guys are all professionals, but think of me as that user that is between Noob and Pro, that simply needs some help with a step-by-step instruction, if you can update the description of "hetzner-snapshot.md" to include a walkthrough for amateurs like me i would be 1000x grateful :)
Or here in the comments would be helpful too, but I am thinking of all the other users landing here from Google too that were looking for automatic snapshots with hetzner cloud
Have a happy new year!