375 lines
14 KiB
Bash
375 lines
14 KiB
Bash
storageLabel="[Storage]"
|
|
|
|
# Lists all backup directories on external storage with type labels (archive, daily, or unknown).
|
|
function cmdStorageBackupList() {
|
|
local dirList error dirCount archiveList dailyList
|
|
run dirList error storageFileList "/" d || { printDanger "$error"; return 1; }
|
|
dirCount=$(grep -c . <<< "$dirList" || true)
|
|
archiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
|
|
dailyList=$(printf '%s\n' "$dirList" | grep -E -- "$backupDailyDirPattern" | sort || true)
|
|
|
|
printRow
|
|
|
|
local i=0 num dir label
|
|
while read -r dir; do
|
|
((++i))
|
|
num=$(printf "%0${#dirCount}d" "$i")
|
|
label="$num: $fontBlue$dir$fontReset"
|
|
|
|
if listContains "$dir" "$archiveList"; then
|
|
printDotText "$label" "${fontGreen}archive$fontReset"
|
|
elif listContains "$dir" "$dailyList"; then
|
|
printDotText "$label" "${fontGreen}daily$fontReset"
|
|
else
|
|
printDotText "$label" "$labelUnknown"
|
|
fi
|
|
done < <(awk 'NF' <<< "$dirList")
|
|
}
|
|
|
|
# Rotates archive backups on external storage: deletes old entries exceeding $storageArchiveKeep.
|
|
function cmdStorageBackupArchiveDispatch() {
|
|
local dirList error
|
|
run dirList error storageFileList "/" d || { printDanger "$error"; return 1; }
|
|
|
|
printSection "Config"
|
|
printDotText "Type" "$storageType"
|
|
printDotText "Path" "$rsyncPath"
|
|
printDotText "Archive keep" "$storageArchiveKeep"
|
|
printDotText "Archive pattern" "$backupArchiveDirPattern"
|
|
|
|
printSection "Directories found"
|
|
archiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
|
|
local archiveCount archiveRemoveCount i=0 num label dirDate dirDays archiveRemoveList=""
|
|
archiveCount=$(grep -c . <<< "$archiveList" || true)
|
|
if [[ "$archiveCount" -gt 0 ]]; then
|
|
while read -r dir; do
|
|
((++i))
|
|
num=$(printf "%0${#archiveCount}d" "$i")
|
|
label="$num: $fontBlue$dir$fontReset"
|
|
|
|
if (( archiveCount - storageArchiveKeep >= i )); then
|
|
printDotText "$label" "${fontRed}delete$fontReset"
|
|
archiveRemoveList+=$'\n'"$dir"
|
|
else
|
|
printDotText "$label" "${fontGreen}save$fontReset"
|
|
fi
|
|
done < <(awk 'NF' <<< "$archiveList")
|
|
|
|
archiveRemoveCount=$(grep -c . <<< "$archiveRemoveList" || true)
|
|
if [[ "$archiveRemoveCount" -gt 0 ]]; then
|
|
printSection "Deleting Directories"
|
|
while read -r dir; do
|
|
label="$dir"
|
|
if ! runError error storageBackupRemove "$dir"; then
|
|
printDotText "$label" "$labelFail"
|
|
printDanger "$error"
|
|
else
|
|
printDotText "$label" "$labelDone"
|
|
fi
|
|
done < <(awk 'NF' <<< "$archiveRemoveList")
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Rotates daily backups on external storage: deletes old entries exceeding $storageDailyKeep, skips today.
|
|
function cmdStorageBackupDailyDispatch() {
|
|
local dirList error
|
|
run dirList error storageFileList "/" d || { printDanger "$error"; return 1; }
|
|
|
|
printSection "Config"
|
|
printDotText "Type" "$storageType"
|
|
printDotText "Path" "$rsyncPath"
|
|
printDotText "Daily keep" "$storageDailyKeep"
|
|
printDotText "Daily pattern" "$backupDailyDirPattern"
|
|
|
|
printSection "Directories found"
|
|
dailyList=$(printf '%s\n' "$dirList" | grep -v "$appDate" | grep -E -- "$backupDailyDirPattern" | sort || true)
|
|
local dailyCount dailyRemoveCount i=0 num label dailyRemoveList=""
|
|
dailyCount=$(grep -c . <<< "$dailyList" || true)
|
|
if [[ "$dailyCount" -gt 0 ]]; then
|
|
while read -r dir; do
|
|
((++i))
|
|
num=$(printf "%0${#dailyCount}d" "$i")
|
|
label="$num: $fontBlue$dir$fontReset"
|
|
|
|
if [[ "$dir" == "$appDate" ]]; then
|
|
printDotText "$label" "${fontGray}skipped$fontReset"
|
|
elif (( dailyCount - storageDailyKeep >= i )); then
|
|
printDotText "$label" "${fontRed}delete$fontReset"
|
|
dailyRemoveList+=$'\n'"$dir"
|
|
else
|
|
printDotText "$label" "${fontGreen}save$fontReset"
|
|
fi
|
|
done < <(awk 'NF' <<< "$dailyList")
|
|
|
|
dailyRemoveCount=$(grep -c . <<< "$dailyRemoveList" || true)
|
|
if [[ "$dailyRemoveCount" -gt 0 ]]; then
|
|
printSection "Deleting Directories"
|
|
while read -r dir; do
|
|
label="$dir"
|
|
if ! runError error storageBackupRemove "$dir"; then
|
|
printDotText "$label" "$labelFail"
|
|
printDanger "$error"
|
|
else
|
|
printDotText "$label" "$labelDone"
|
|
fi
|
|
done < <(awk 'NF' <<< "$dailyRemoveList")
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Synchronizes the local path to external storage (rsync or SSH).
|
|
# $1 (sourcePath): local path to sync.
|
|
# [$2] (type): storage type (rsync or ssh); defaults to $storageType.
|
|
function cmdStoragePathSync() {
|
|
local sourcePath="$1"
|
|
[[ -n "$sourcePath" ]] || { printDanger "Source path not specified"; return 1; }
|
|
|
|
local type="${2:-$storageType}"
|
|
local error
|
|
|
|
printSection "Config"
|
|
printDotText "Source" "$sourcePath"
|
|
if [[ ! -e "$sourcePath" ]]; then
|
|
printDotText "$sourcePath" "${fontRed}not found$fontReset"
|
|
return 1
|
|
fi
|
|
|
|
case "$type" in
|
|
rsync)
|
|
printDotText "Type" "$type"
|
|
printDotText "rSync URI" "$rsyncUri"
|
|
printDotText "rSync path" "$rsyncPath"
|
|
printSection "Executing"
|
|
runError error storagePathSyncRsync "$sourcePath" || {
|
|
printDotText "synchronization" "$labelFail"
|
|
printDanger "$error"
|
|
return 1
|
|
}
|
|
;;
|
|
ssh)
|
|
printDotText "Type" "$type"
|
|
printDotText "SSH URI" "$sshUser@$sshHost"
|
|
printDotText "SSH path" "$sshPath"
|
|
printSection "Executing"
|
|
runError error storagePathSyncSSH "$sourcePath" || {
|
|
printDotText "synchronization" "$labelFail"
|
|
printDanger "$error"
|
|
return 1
|
|
}
|
|
;;
|
|
*)
|
|
printDotText "Type" "$type $labelUnknown"
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
printDotText "synchronization" "$labelDone"
|
|
}
|
|
|
|
# Syncs today's daily backup ($backupDailyPath/$appDate) to external storage.
|
|
function cmdStorageBackupDailySyncLast() {
|
|
cmdStoragePathSync "$backupDailyPath/$appDate"
|
|
}
|
|
|
|
# Syncs the newest archive backup directory to external storage.
|
|
function cmdStorageBackupArchiveSyncLast() {
|
|
local dirList archiveList archiveDir error
|
|
run dirList error fileList "$backupArchivePath" d || { printDanger "$error"; return 1; }
|
|
archiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
|
|
archiveDir=$(tail -n 1 <<< "$archiveList")
|
|
[[ -n "$archiveDir" ]] || { printDanger "No archive directories found"; return 1; }
|
|
|
|
cmdStoragePathSync "$backupArchivePath/$archiveDir"
|
|
}
|
|
|
|
# Interactively selects a local archive backup directory and syncs it to external storage.
|
|
function cmdStorageBackupArchiveSync() {
|
|
local dirList error archiveList archiveCount
|
|
run dirList error fileList "$backupArchivePath" d || { printDanger "$error"; return 1; }
|
|
archiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
|
|
archiveCount=$(grep -c . <<< "$archiveList" || true)
|
|
|
|
printRow
|
|
|
|
local i=0 num dir
|
|
while read -r dir; do
|
|
((++i))
|
|
num=$(printf "%0${#archiveCount}d" "$i")
|
|
printDotText "$num: $fontBlue$dir$fontReset" "${fontGreen}archive$fontReset"
|
|
done < <(awk 'NF' <<< "$archiveList")
|
|
|
|
printRow
|
|
local input item selected
|
|
read -r -p "Specify the backup number: " input
|
|
|
|
printRow
|
|
[[ -z "$input" ]] && input=0
|
|
[[ "$input" =~ ^[0-9]+$ ]] || { printDanger "Invalid backup number"; return 1; }
|
|
item=$((10#$input))
|
|
selected=$(awk 'NF {n++} n == item {print; exit}' item="$item" <<< "$archiveList")
|
|
[[ -n "$selected" ]] || { printDanger "Unknown backup number"; return 1; }
|
|
|
|
cmdStoragePathSync "$backupArchivePath/$selected"
|
|
}
|
|
|
|
# Interactively selects a local daily backup directory and syncs it to external storage.
|
|
function cmdStorageBackupDailySync() {
|
|
local dirList error dailyList dailyCount
|
|
run dirList error fileList "$backupDailyPath" d || { printDanger "$error"; return 1; }
|
|
dailyList=$(printf '%s\n' "$dirList" | grep -E -- "$backupDailyDirPattern" | sort || true)
|
|
dailyCount=$(grep -c . <<< "$dailyList" || true)
|
|
|
|
printRow
|
|
|
|
local i=0 num dir
|
|
while read -r dir; do
|
|
((++i))
|
|
num=$(printf "%0${#dailyCount}d" "$i")
|
|
printDotText "$num: $fontBlue$dir$fontReset" "${fontGreen}daily$fontReset"
|
|
done < <(awk 'NF' <<< "$dailyList")
|
|
|
|
printRow
|
|
local input item selected
|
|
read -r -p "Specify the backup number: " input
|
|
|
|
printRow
|
|
[[ -z "$input" ]] && input=0
|
|
[[ "$input" =~ ^[0-9]+$ ]] || { printDanger "Invalid backup number"; return 1; }
|
|
item=$((10#$input))
|
|
selected=$(awk 'NF {n++} n == item {print; exit}' item="$item" <<< "$dailyList")
|
|
[[ -n "$selected" ]] || { printDanger "Unknown backup number"; return 1; }
|
|
|
|
cmdStoragePathSync "$backupDailyPath/$selected"
|
|
}
|
|
|
|
# Dispatches interactive backup sync by type.
|
|
# $1 (type): backup type: archive or daily.
|
|
function cmdStorageBackupSync() {
|
|
local type
|
|
type="$1"
|
|
|
|
case "$type" in
|
|
archive)
|
|
cmdStorageBackupArchiveSync
|
|
;;
|
|
daily)
|
|
cmdStorageBackupDailySync
|
|
;;
|
|
*)
|
|
printDanger "Unknown type backup: $type";
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Interactively selects a backup directory on external storage and removes it.
|
|
function cmdStorageBackupRemove() {
|
|
local dirList error dirCount archiveList dailyList dir i=0 num label
|
|
run dirList error storageFileList "/" d || { printDanger "$error"; return 1; }
|
|
|
|
dirCount=$(grep -c . <<< "$dirList" || true)
|
|
archiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
|
|
dailyList=$(printf '%s\n' "$dirList" | grep -E -- "$backupDailyDirPattern" | sort || true)
|
|
|
|
printRow
|
|
|
|
while read -r dir; do
|
|
((++i))
|
|
num=$(printf "%0${#dirCount}d" "$i")
|
|
label="$num: $fontBlue$dir$fontReset"
|
|
|
|
if listContains "$dir" "$archiveList"; then
|
|
printDotText "$label" "${fontGreen}archive$fontReset"
|
|
elif listContains "$dir" "$dailyList"; then
|
|
printDotText "$label" "${fontGreen}daily$fontReset"
|
|
else
|
|
printDotText "$label" "$labelUnknown"
|
|
fi
|
|
done < <(awk 'NF' <<< "$dirList")
|
|
|
|
printRow
|
|
local input item selected
|
|
read -r -p "Specify the backup number: " input
|
|
|
|
printRow
|
|
[[ -z "$input" ]] && input=0
|
|
[[ "$input" =~ ^[0-9]+$ ]] || { printDanger "Invalid backup number"; return 1; }
|
|
item=$((10#$input))
|
|
selected=$(awk 'NF {n++} n == item {print; exit}' item="$item" <<< "$dirList")
|
|
[[ -n "$selected" ]] || { printDanger "Unknown backup number"; return 1; }
|
|
|
|
if ! runError error storageBackupRemove "$selected"; then
|
|
printDotText "$selected" "$labelFail"
|
|
printDanger "$error"
|
|
return 1
|
|
fi
|
|
printDotText "$selected" "$labelDone"
|
|
}
|
|
|
|
# Compares local and remote backup directories, showing which exist on each side.
|
|
function cmdStorageBackupCompare() {
|
|
local dirList error localArchiveList localDailyList remoteArchiveList remoteDailyList
|
|
|
|
run dirList error fileList "$backupArchivePath" d || { printDanger "Archive path: $error"; return 1; }
|
|
localArchiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
|
|
|
|
run dirList error fileList "$backupDailyPath" d || { printDanger "Archive path: $error"; return 1; }
|
|
localDailyList=$(printf '%s\n' "$dirList" | grep -E -- "$backupDailyDirPattern" | sort || true)
|
|
|
|
run dirList error storageFileList "/" d || { printDanger "$error"; return 1; }
|
|
remoteArchiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
|
|
remoteDailyList=$(printf '%s\n' "$dirList" | grep -E -- "$backupDailyDirPattern" | sort || true)
|
|
|
|
printSection "Local"
|
|
printDotText "Archive" "$(grep -c . <<< "$localArchiveList" || true)"
|
|
printDotText "Daily" "$(grep -c . <<< "$localDailyList" || true)"
|
|
|
|
printSection "Remote"
|
|
printDotText "Archive" "$(grep -c . <<< "$remoteArchiveList" || true)"
|
|
printDotText "Daily" "$(grep -c . <<< "$remoteDailyList" || true)"
|
|
|
|
local allDirs
|
|
allDirs=$(printf '%s\n' "$localArchiveList" "$localDailyList" "$remoteArchiveList" "$remoteDailyList" | awk 'NF' | sort -u)
|
|
|
|
printSection "Comparison"
|
|
local dir localStatus remoteStatus
|
|
out="${fontRed}X$fontReset"
|
|
localIn="${fontGreen}L$fontReset"
|
|
remoteIn="${fontGreen}R$fontReset"
|
|
while read -r dir; do
|
|
localStatus="$out"
|
|
remoteStatus="$out"
|
|
|
|
if listContains "$dir" "$localArchiveList"; then
|
|
localStatus="$localIn"
|
|
elif listContains "$dir" "$localDailyList"; then
|
|
localStatus="$localIn"
|
|
fi
|
|
if listContains "$dir" "$remoteArchiveList"; then
|
|
remoteStatus="$remoteIn"
|
|
elif listContains "$dir" "$remoteDailyList"; then
|
|
remoteStatus="$remoteIn"
|
|
fi
|
|
|
|
printDotText "$fontBlue$dir$fontReset" "[ $localStatus : $remoteStatus ]"
|
|
done < <(awk 'NF' <<< "$allDirs")
|
|
}
|
|
|
|
function cmdStorageBackupSize() {
|
|
local fileList file size total
|
|
|
|
printSection "FTP: $ftpPath"
|
|
|
|
total=0
|
|
fileList=$(ftpFileList | sort -n)
|
|
while IFS= read -r file; do
|
|
size=$(ftpPathSize "/$file" "gb")
|
|
printDotText "$file" "$size Gb"
|
|
(( total += size ))
|
|
done <<< "$fileList"
|
|
|
|
printRow
|
|
printDotText "total" "$total Gb"
|
|
}
|