Last active
December 4, 2023 19:19
-
-
Save SmartFinn/013dc2670f6605826acfae8e25c11178 to your computer and use it in GitHub Desktop.
A script for creating/rotating snapshots of LVM volumes via (ana)cron
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# | |
# Creating and rotating snapshots of LVM volumes | |
# | |
# https://gist.github.com/SmartFinn/013dc2670f6605826acfae8e25c11178 | |
# | |
# Copyright (c) 2023 Serhii Yeremenko (https://github.com/SmartFinn) | |
# | |
# Permission to use, copy, modify, and/or distribute this software for any | |
# purpose with or without fee is hereby granted. | |
# | |
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | |
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | |
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | |
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | |
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | |
# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | |
# PERFORMANCE OF THIS SOFTWARE. | |
set -eu -o pipefail | |
readonly PROGNAME="$(basename "${BASH_SOURCE[0]}" .sh)" | |
readonly VERSION="0.0.3" | |
msg() { | |
printf "%s: %s\n" "$PROGNAME" "$*" | |
} | |
err() { | |
printf "%s: Error: %s\n" "$PROGNAME" "$*" >&2 | |
} | |
usage() { | |
cat <<- EOF | |
usage: | |
$PROGNAME [options] LogicalVolumePath... | |
OPTIONS: | |
-l LogicalExtentsNumber[%{VG|PVS|FREE|ORIGIN}] | |
Default value: 100%ORIGIN | |
Gives the number of logical extents to allocate for | |
the new logical volume. | |
See lvcreate(8) for details. | |
-L LogicalVolumeSize[b|B|s|S|k|K|m|M|g|G|t|T|p|P|e|E] | |
Gives the size to allocate for the new logical volume. | |
Default unit is megabytes. | |
See lvcreate(8) for details. | |
-N NumberOfSnapshots | |
Default value: 2 | |
Specify how many of the last snapshots to keep. | |
-D Delete all snapshots of the specified logical volume. | |
-V print $PROGNAME version and exit | |
-h show this help | |
EOF | |
exit "${1:-0}" | |
} | |
create_snapshot() { | |
local snap_name="${1?snap_name is required}" | |
local lv_path="${2?lv_path is required}" | |
[ "$DO_NOT_CREATE" -eq 0 ] || return 0 | |
sync | |
lvcreate "${SNAP_SIZE[@]}" --snapshot --name "$snap_name" "$lv_path" | |
} | |
clear_snapshots() { | |
local lv_name="${1?lv_name is required}" | |
local snap_prefix="${2?no prefix is specified}" | |
local -i snap_count | |
local -i excess_snaps | |
local -a snapshots=() | |
read -ra snapshots < <(lvs --noheadings -o lv_path -S "origin=$lv_name" \ | |
-S 'lv_attr=~^s' -S "lv_name=~^$snap_prefix" | xargs) | |
snap_count=${#snapshots[@]} | |
excess_snaps=$(( snap_count - KEEP )) | |
for (( i=0; i < excess_snaps; i++ )); do | |
[ -n "${snapshots[$i]}" ] || continue | |
lvremove --force "${snapshots[$i]}" | |
done | |
} | |
declare -a SNAP_SIZE=(-l 100%ORIGIN) | |
declare -i KEEP=2 | |
declare -i DO_NOT_CREATE="${DO_NOT_CREATE:-0}" | |
while getopts ":DL:l:N:Vh" opt; do | |
case "$opt" in | |
D ) DO_NOT_CREATE=1 | |
KEEP=0 | |
;; | |
L | \ | |
l ) SNAP_SIZE=("-$opt" "$OPTARG") ;; | |
N ) KEEP="$OPTARG" ;; | |
V ) printf "%s %s\n" "$PROGNAME" "$VERSION" | |
exit 0 | |
;; | |
h ) usage 0 ;; | |
: ) err "option requires an argument -- '-$OPTARG'" | |
usage 2 | |
;; | |
\?) err "illegal option -- '-$OPTARG'" | |
usage 2 | |
;; | |
esac | |
done | |
shift $((OPTIND-1)) | |
# Return an error if positional parameters are not found | |
if [ -z "${1:-}" ]; then | |
err "no logical volume is specified" | |
usage 2 | |
fi | |
for lvol in "$@"; do | |
unset LVM2_LV_NAME LVM2_LV_PATH | |
eval "$(lvs --noheadings --nameprefixes -o lv_name,lv_path "$lvol")" | |
[ -b "$LVM2_LV_PATH" ] || exit 1 | |
timestamp="$(date +%Y%m%d_%H%M%S)" | |
snap_prefix="${LVM2_LV_NAME}_snap_" | |
snap_name="${snap_prefix}${timestamp}" | |
create_snapshot "$snap_name" "$LVM2_LV_PATH" | |
clear_snapshots "$LVM2_LV_NAME" "$snap_prefix" | |
done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi, thanks for sharing your script 👍
Just one thing, your options are wrong:
Took me some reading through lvcreate so see why it wasn't working on my side :P !