diff --git a/CHANGELOG b/CHANGELOG index fe5e435..1f13862 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,7 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - +### Added +- [SNAPSHOT] Add an option to snapshot's command in order to delete a specific snapshot ## [1.0.4] - 2025-10-29 ### Added diff --git a/usr/local/share/vulture-utils/common.sh b/usr/local/share/vulture-utils/common.sh index cc6f879..f909657 100644 --- a/usr/local/share/vulture-utils/common.sh +++ b/usr/local/share/vulture-utils/common.sh @@ -224,6 +224,68 @@ clean_old_BEs() { done } +# Delete a specific boot environment +delete_BE() { + _be_name="$1" + + if [ -z "${_be_name}" ]; then + error "[!] No Boot Environment name provided" + return 1 + fi + + # Check if BE exists in the list of vulture BEs + _existing_bes="$(get_vlt_BEs | cut -f 1)" + if ! contains_word "${_existing_bes}" "${_be_name}"; then + error "[!] Boot Environment '${_be_name}' not found" + return 1 + fi + + # Check if BE is currently active (status contains 'N') + _be_status="$(get_BEs | grep "^${_be_name} " | cut -f 2)" + if printf "%s" "${_be_status}" | grep -q "N"; then + error "[!] Cannot delete active Boot Environment '${_be_name}'" + return 1 + fi + + echo "Destroying Boot Environment: '${_be_name}'" + /sbin/bectl destroy -o "${_be_name}" + return $? +} + +# Delete a specific snapshot from datasets +delete_snapshot_from_datasets() { + _datasets="$1" # space-separated list of datasets + _snap_to_delete="$2" # snapshot name to delete + _zpool="$(get_root_zpool_name)" + _deleted_count=0 + + if [ -z "${_datasets}" ] || [ -z "${_snap_to_delete}" ]; then + error "[!] Missing arguments for snapshot deletion" + return 1 + fi + + for _dataset in ${_datasets}; do + if zfs_dataset_exists "${_dataset}"; then + _existing_snaps="$(list_snapshots "${_dataset}")" + if contains_word "${_existing_snaps}" "${_snap_to_delete}"; then + echo "Deleting snapshot '${_zpool}/${_dataset}@${_snap_to_delete}'" + if /sbin/zfs destroy "${_zpool}/${_dataset}@${_snap_to_delete}"; then + _deleted_count=$((_deleted_count + 1)) + else + error "[!] Failed to delete snapshot '${_zpool}/${_dataset}@${_snap_to_delete}'" + fi + fi + fi + done + + if [ "${_deleted_count}" -eq 0 ]; then + warn "[!] No snapshot '${_snap_to_delete}' found in specified datasets" + return 1 + fi + + return 0 +} + ############################ ## Snapshotting functions ## diff --git a/usr/local/share/vulture-utils/snapshot.sh b/usr/local/share/vulture-utils/snapshot.sh index 9603bf9..55ac99a 100755 --- a/usr/local/share/vulture-utils/snapshot.sh +++ b/usr/local/share/vulture-utils/snapshot.sh @@ -10,6 +10,8 @@ snap_name="${SNAPSHOT_PREFIX}SNAP_$(date -u +%Y%m%d_%H%M%S)" list_snaps=0 snapshot_system=0 keep_previous_snap=-1 +delete_snapshot_mode=0 +delete_snapshot_name="" _mongo_locked=0 _snapshot_datasets_list="" @@ -21,6 +23,7 @@ usage() { echo "This command triggers snapshots on all or specific datasets, those snapshot points are then available for restorations" echo "" echo "OPTIONS:" + echo " -h Show this help banner" echo " -A Snapshot all underlying datasets" echo " -S Snapshot the system dataset(s)" echo " -J Snapshot the jail(s) dataset(s)" @@ -29,6 +32,7 @@ usage() { echo " -T Snapshot the tmp/var dataset(s)" echo " -l Only list datasets" echo " -k Keep snapshots for the targeted datasets" + echo " -d Delete snapshot from targeted datasets (use with -A, -S, -J, -H, -D, -T)" exit 1 } @@ -38,7 +42,7 @@ if [ "$(/usr/bin/id -u)" != "0" ]; then exit 1 fi -while getopts 'hASJDHTlk:' opt; do +while getopts 'hASJDHTlk:d:' opt; do case "${opt}" in A) _snapshot_datasets_list="SYSTEM JAIL DB HOMES TMPVAR"; snapshot_system=1; @@ -58,6 +62,9 @@ while getopts 'hASJDHTlk:' opt; do ;; k) keep_previous_snap=${OPTARG}; ;; + d) delete_snapshot_mode=1; + delete_snapshot_name="${OPTARG}"; + ;; h|*) usage; ;; esac @@ -92,6 +99,17 @@ finalize_early() { finalize 1 "Stopped" } +if [ "${delete_snapshot_mode}" -gt 0 ]; then + if [ -z "${delete_snapshot_name}" ]; then + error "[!] Snapshot name required with -d option" + usage + fi + if [ "${snapshot_system}" -eq 0 ] && [ -z "${_snapshot_datasets_list}" ]; then + error "[!] Delete mode requires at least one dataset selector (-A, -S, -J, -H, -D, or -T)" + usage + fi +fi + if [ "${list_snaps}" -gt 0 ]; then _be_list="$(get_vlt_BEs | cut -f 1)" printf "SYSTEM:\t" @@ -99,6 +117,11 @@ if [ "${list_snaps}" -gt 0 ]; then printf "%s\t" "$_be" done printf "\n" +elif [ "${delete_snapshot_mode}" -gt 0 ] && [ "$snapshot_system" -gt 0 ]; then + echo "Deleting snapshot '${delete_snapshot_name}' from SYSTEM datasets" + if ! delete_BE "${delete_snapshot_name}"; then + finalize 1 "Failed to delete SYSTEM snapshot '${delete_snapshot_name}'" + fi elif [ "$snapshot_system" -gt 0 ]; then echo "making new snapshot for SYSTEM datasets" /sbin/bectl create "$snap_name" @@ -119,7 +142,15 @@ for _type in ${AVAILABLE_DATASET_TYPES}; do printf "%s\t" "$_snap" done printf "\n" - # snapshotting datasets + # Delete snapshots + elif [ "${delete_snapshot_mode}" -gt 0 ]; then + # Ignore datasets not explicitely selected + if ! contains_word "${_snapshot_datasets_list}" "${_type}"; then + continue + fi + echo "Deleting snapshot '${delete_snapshot_name}' from ${_type} datasets" + delete_snapshot_from_datasets "$_type_datasets" "$delete_snapshot_name" + # Snapshotting datasets (normal mode) else # Ignore datasets not explicitely selected if ! contains_word "${_snapshot_datasets_list}" "${_type}"; then @@ -138,4 +169,4 @@ for _type in ${AVAILABLE_DATASET_TYPES}; do fi done -finalize +finalize \ No newline at end of file