Add script and documentation to expamd raid6.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
*.swp
|
*.swp
|
||||||
|
*.log
|
||||||
|
425
raid6-expand-interactive.sh
Executable file
425
raid6-expand-interactive.sh
Executable file
@ -0,0 +1,425 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# =========================================
|
||||||
|
# RAID- und FS-Erweiterung (mdadm/Ext4/XFS)
|
||||||
|
# Interaktiv, Dry-Run-, Debug- und Yes-Option
|
||||||
|
# Mit Zeitstempeln, Logfile & farbigen Headern
|
||||||
|
# =========================================
|
||||||
|
|
||||||
|
# ---- Globale Defaults ----
|
||||||
|
DRYRUN=0
|
||||||
|
DEBUG=0
|
||||||
|
ASSUME_YES=0
|
||||||
|
FS_TYPE="ext4" # wird in prompt_inputs gesetzt; hier nur Default
|
||||||
|
LOG_FILE="./raid6-expand-$(date +%F-%H%M%S).log"
|
||||||
|
|
||||||
|
# ---- Zeit & Logging ----
|
||||||
|
ts() { date '+%F %T'; }
|
||||||
|
strip_ansi() { sed -r 's/\x1B\[[0-9;]*[A-Za-z]//g'; }
|
||||||
|
|
||||||
|
log_plain() {
|
||||||
|
# Schreibt *ohne* ANSI-Farben ins Log mit Zeitstempel
|
||||||
|
{ printf "[%s] %s\n" "$(ts)" "$(printf "%s" "$*" | strip_ansi)"; } >> "$LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- Farben/Logging-Ausgaben ----
|
||||||
|
cecho() { # $1=colorcode, $2=prefix, $3=msg...
|
||||||
|
local c="$1"; shift
|
||||||
|
local pfx="$1"; shift
|
||||||
|
printf "\e[%sm[%s] %s\e[0m\n" "$c" "$(ts)" "$pfx $*"
|
||||||
|
log_plain "$pfx $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
info() { cecho "36" "INFO " "$@"; } # cyan
|
||||||
|
warn() { cecho "33" "WARN " "$@"; } # gelb
|
||||||
|
ok() { cecho "32" "OK " "$@"; } # grün
|
||||||
|
err() { cecho "31" "ERROR" "$@"; } # rot
|
||||||
|
note() { cecho "35" "NOTE " "$@"; } # magenta
|
||||||
|
|
||||||
|
header() { # Abschnittsüberschrift
|
||||||
|
local line="============================================================"
|
||||||
|
printf "\e[36m[%s] %s\e[0m\n" "$(ts)" "$line"
|
||||||
|
printf "\e[36m[%s] == %s ==\e[0m\n" "$(ts)" "$*"
|
||||||
|
printf "\e[36m[%s] %s\e[0m\n" "$(ts)" "$line"
|
||||||
|
log_plain "$line"
|
||||||
|
log_plain "== $* =="
|
||||||
|
log_plain "$line"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- Usage ----
|
||||||
|
usage() {
|
||||||
|
cat <<'USAGE'
|
||||||
|
|
||||||
|
raid6-expand-interactive.sh — Vergrößert RAID-Mitglieds-Partitionen und das Array/FS
|
||||||
|
|
||||||
|
Nutzung:
|
||||||
|
./raid6-expand-interactive.sh [Optionen]
|
||||||
|
|
||||||
|
Optionen:
|
||||||
|
-n, --dry-run Keine Änderungen; loggt nur, was passieren würde.
|
||||||
|
(Unterdrückt Rückfragen pro Partition; aktiviert --debug)
|
||||||
|
-d, --debug Zusätzliche Diagnosen (lsblk/blkid/sfdisk-Auszüge).
|
||||||
|
-y, --yes Bestätigt alle Rückfragen automatisch (produktiv praktisch).
|
||||||
|
-L, --log <pfad> Schreibe Log nach <pfad> (Default: ./raid6-expand-YYYY-MM-DD-HHMMSS.log)
|
||||||
|
-h, --help Diese Hilfe anzeigen.
|
||||||
|
|
||||||
|
Ablauf (produktiv):
|
||||||
|
1) Interaktive Eingaben (RAID-Device, Disks, Partitionsnummer, FS, ggf. Mountpoint)
|
||||||
|
2) Status-Checks & Partitionstabellen-Backups
|
||||||
|
3) Pro Platte: Partition bis maximal vergrößern (Start & Typ bleiben gleich)
|
||||||
|
4) Nach jeder Platte: mdadm --grow --size=max + auf Resync warten
|
||||||
|
5) Am Ende: Dateisystem vergrößern (resize2fs oder xfs_growfs)
|
||||||
|
|
||||||
|
Voraussetzungen:
|
||||||
|
mdadm, sfdisk, lsblk, blkid, awk, partprobe, udevadm
|
||||||
|
(xfs_growfs nur, wenn FS_TYPE=xfs)
|
||||||
|
|
||||||
|
USAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- Helfer ----
|
||||||
|
need_bin() { command -v "$1" >/dev/null 2>&1 || { err "Fehlt: $1"; exit 1; }; }
|
||||||
|
|
||||||
|
run() {
|
||||||
|
# Zeigt und loggt den Befehl; führt ihn in produktiv aus
|
||||||
|
printf "[%s] + %s\n" "$(ts)" "$*" | tee -a "$LOG_FILE" >/dev/null
|
||||||
|
if [[ "$DRYRUN" -eq 0 ]]; then
|
||||||
|
"$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
confirm() {
|
||||||
|
# Auto-yes im Dry-Run oder wenn -y/--yes gesetzt ist, sonst interaktiv fragen
|
||||||
|
local prompt="$1"
|
||||||
|
if [[ "$DRYRUN" -eq 1 || "$ASSUME_YES" -eq 1 ]]; then
|
||||||
|
note "(auto) $prompt: yes"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
read -r -p "$prompt [yes/NO] " a
|
||||||
|
if [[ "${a:-}" == "yes" ]]; then
|
||||||
|
log_plain "$prompt: yes"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
log_plain "$prompt: NO"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
confirm_summary() {
|
||||||
|
# Zusammenfassung IMMER bestätigen (auch im Dry-Run), außer -y/--yes
|
||||||
|
local prompt="$1"
|
||||||
|
if [[ "$ASSUME_YES" -eq 1 ]]; then
|
||||||
|
note "(auto) $prompt: yes"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
read -r -p "$prompt [yes/NO] " a
|
||||||
|
if [[ "${a:-}" == "yes" ]]; then
|
||||||
|
log_plain "$prompt: yes"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
log_plain "$prompt: NO"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
check_clean() {
|
||||||
|
local md="$1"
|
||||||
|
if ! grep -q "$(basename "$md")" /proc/mdstat; then
|
||||||
|
err "$md nicht in /proc/mdstat gefunden."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! mdadm --detail "$md" | grep -q "State : clean"; then
|
||||||
|
warn "$md ist nicht 'clean'. Details:"
|
||||||
|
mdadm --detail "$md" | sed 's/^/ /' | tee -a "$LOG_FILE"
|
||||||
|
confirm "Trotzdem fortfahren?" || exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_resync() {
|
||||||
|
local md="$1"
|
||||||
|
info "Warte auf Abschluss von Initialisierung/Resync auf $md ..."
|
||||||
|
if [[ "$DRYRUN" -eq 1 ]]; then
|
||||||
|
note "(Dry-Run) Würde auf Resync/Initialisierung warten und /proc/mdstat beobachten."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
while :; do
|
||||||
|
local blk
|
||||||
|
blk=$(grep -E "^\s*$(basename "$md")" /proc/mdstat || true)
|
||||||
|
if [[ -z "$blk" ]]; then
|
||||||
|
err "$md nicht in /proc/mdstat gefunden!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -Eiq "(resync|reshape|recovery|check)" /proc/mdstat; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
printf "[%s] %s\n" "$(ts)" "$blk" | tee -a "$LOG_FILE" >/dev/null
|
||||||
|
sleep 20
|
||||||
|
done
|
||||||
|
ok "Resync/Initialisierung abgeschlossen."
|
||||||
|
}
|
||||||
|
|
||||||
|
backup_pt() {
|
||||||
|
local disk="$1"
|
||||||
|
local bkup="backup-$(basename "$disk")-$(date +%F-%H%M%S).sfdisk"
|
||||||
|
if [[ "$DRYRUN" -eq 1 ]]; then
|
||||||
|
note "(Dry-Run) Würde Partitionstabelle sichern: $bkup"
|
||||||
|
else
|
||||||
|
sfdisk -d "$disk" > "$bkup"
|
||||||
|
ok "Partitionstabelle gesichert: $bkup"
|
||||||
|
fi
|
||||||
|
if [[ "$DEBUG" -eq 1 ]]; then
|
||||||
|
header "DEBUG: sfdisk -d $disk"
|
||||||
|
sfdisk -d "$disk" | tee -a "$LOG_FILE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- Start & Typ via lsblk/blkid (robuster als sfdisk-parsing) ----
|
||||||
|
get_part_start() {
|
||||||
|
local part="$1"
|
||||||
|
local start
|
||||||
|
start="$(lsblk -no START "$part" 2>/dev/null | head -n1 | tr -d '[:space:]')"
|
||||||
|
[[ -n "$start" ]] || { err "Konnte START für $part nicht ermitteln (lsblk)."; exit 1; }
|
||||||
|
printf "%s" "$start"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_part_type() {
|
||||||
|
local part="$1"
|
||||||
|
local ptype
|
||||||
|
ptype="$(blkid -s PART_ENTRY_TYPE -o value "$part" 2>/dev/null | head -n1 | tr -d '[:space:]' || true)"
|
||||||
|
if [[ -z "$ptype" ]]; then
|
||||||
|
ptype="$(lsblk -no PARTTYPE "$part" 2>/dev/null | head -n1 | tr -d '[:space:]' || true)"
|
||||||
|
fi
|
||||||
|
if [[ -z "$ptype" ]]; then
|
||||||
|
local line
|
||||||
|
line="$(sfdisk -d "${part%[0-9]*}" | awk -v p="$part" '$1==p {print; exit}')"
|
||||||
|
ptype="$(awk -F',' '{
|
||||||
|
for(i=1;i<=NF;i++){
|
||||||
|
gsub(/^ +| +$/,"",$i);
|
||||||
|
if($i ~ /^type=/){sub(/^type=/,"",$i); print $i}
|
||||||
|
}
|
||||||
|
}' <<< "$line" | tr -d '[:space:]')"
|
||||||
|
fi
|
||||||
|
[[ -n "$ptype" ]] || { err "Konnte Partitions-Typ für $part nicht ermitteln."; exit 1; }
|
||||||
|
printf "%s" "$ptype"
|
||||||
|
}
|
||||||
|
|
||||||
|
expand_partition() {
|
||||||
|
local disk="$1" pn="$2"
|
||||||
|
local part="${disk}${pn}"
|
||||||
|
[[ -b "$part" ]] || { err "Partition $part existiert nicht."; exit 1; }
|
||||||
|
|
||||||
|
if [[ "$DEBUG" -eq 1 ]]; then
|
||||||
|
header "DEBUG: lsblk/blkid für $part"
|
||||||
|
lsblk -no NAME,PATH,TYPE,SIZE,START,PARTTYPE "$part" | tee -a "$LOG_FILE"
|
||||||
|
blkid "$part" | tee -a "$LOG_FILE" || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
local pstart ptype
|
||||||
|
pstart="$(get_part_start "$part")"
|
||||||
|
ptype="$(get_part_type "$part")"
|
||||||
|
|
||||||
|
warn "Geplante Änderung auf $part:"
|
||||||
|
note " Startsektor bleibt: $pstart"
|
||||||
|
note " Größe: bis maximal verfügbar"
|
||||||
|
note " Typ bleibt: $ptype"
|
||||||
|
|
||||||
|
if [[ "$DRYRUN" -eq 1 || "$ASSUME_YES" -eq 1 ]]; then
|
||||||
|
note "(Dry-Run) Würde ${part} erweitern: Start=${pstart}, Typ=${ptype}, bis maximal verfügbar."
|
||||||
|
else
|
||||||
|
confirm "Jetzt ${part} erweitern?" || { warn "Übersprungen."; return; }
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "[%s] + %s\n" "$(ts)" "printf ',,type=${ptype}\n' | sfdisk --no-reread --force -N ${pn} ${disk}" | tee -a "$LOG_FILE" >/dev/null
|
||||||
|
if [[ "$DRYRUN" -eq 0 ]]; then
|
||||||
|
printf ",,type=%s\n" "$ptype" | sfdisk --no-reread --force -N "$pn" "$disk"
|
||||||
|
run partprobe "$disk" || true
|
||||||
|
run udevadm settle || true
|
||||||
|
ok "Partition $part wurde bis ans Ende erweitert."
|
||||||
|
else
|
||||||
|
note "(Dry-Run) Würde Kernel neu einlesen: partprobe $disk ; udevadm settle"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
grow_md_size() {
|
||||||
|
local md="$1"
|
||||||
|
run mdadm --grow "$md" --size=max || { :; }
|
||||||
|
if [[ "$DRYRUN" -eq 1 ]]; then
|
||||||
|
note "(Dry-Run) Würde mdadm-Grow ausführen."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
final_fs_grow() {
|
||||||
|
local md="$1" fs="$2" mp="${3:-}"
|
||||||
|
case "$fs" in
|
||||||
|
ext4)
|
||||||
|
run resize2fs "$md" || { :; }
|
||||||
|
if [[ "$DRYRUN" -eq 1 ]]; then
|
||||||
|
note "(Dry-Run) Würde resize2fs ausführen."
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
xfs)
|
||||||
|
[[ -n "$mp" ]] || { err "Für XFS muss ein Mountpoint angegeben werden."; exit 1; }
|
||||||
|
if ! mount | grep -qE "^$md on $mp "; then
|
||||||
|
warn "$md scheint nicht auf $mp gemountet zu sein (laut 'mount')."
|
||||||
|
mount | sed 's/^/ /' | tee -a "$LOG_FILE"
|
||||||
|
confirm "Trotzdem mit xfs_growfs $mp fortfahren?" || exit 1
|
||||||
|
fi
|
||||||
|
run xfs_growfs "$mp" || { :; }
|
||||||
|
if [[ "$DRYRUN" -eq 1 ]]; then
|
||||||
|
note "(Dry-Run) Würde xfs_growfs ausführen."
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
err "Unbekanntes Dateisystem: $fs (erwartet: ext4 oder xfs)"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
ok "Dateisystem-Schritt abgeschlossen."
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- Interaktive Eingabe ----
|
||||||
|
prompt_inputs() {
|
||||||
|
echo
|
||||||
|
header "Interaktiver Modus: Eingaben sammeln"
|
||||||
|
[[ "$DRYRUN" -eq 1 ]] && note "Dry-Run aktiviert: Es werden KEINE Änderungen durchgeführt."
|
||||||
|
[[ "$DEBUG" -eq 1 ]] && note "Debug aktiviert: Zeige zusätzliche Diagnosen."
|
||||||
|
[[ "$ASSUME_YES" -eq 1 ]] && note "--yes aktiviert: Bestätige alle Rückfragen automatisch."
|
||||||
|
note "Logfile: $LOG_FILE"
|
||||||
|
|
||||||
|
read -r -p "RAID-Device (z.B. /dev/md4): " MDDEV
|
||||||
|
[[ -n "${MDDEV:-}" ]] || { err "RAID-Device darf nicht leer sein."; exit 1; }
|
||||||
|
[[ -b "$MDDEV" ]] || { err "$MDDEV ist kein Block-Device."; exit 1; }
|
||||||
|
|
||||||
|
echo "Physikalische Geräte (ohne Partitionsnummer), leerzeichengetrennt (z.B. /dev/sda /dev/sdb /dev/sdc /dev/sdd):"
|
||||||
|
read -r -a DISKS
|
||||||
|
[[ "${#DISKS[@]}" -ge 2 ]] || { err "Mindestens zwei Geräte angeben."; exit 1; }
|
||||||
|
for d in "${DISKS[@]}"; do
|
||||||
|
[[ -b "$d" ]] || { err "$d ist kein Block-Device."; exit 1; }
|
||||||
|
done
|
||||||
|
|
||||||
|
read -r -p "Partitionsnummer der RAID-Partition (Standard 1): " PARTNUM
|
||||||
|
PARTNUM="${PARTNUM:-1}"
|
||||||
|
[[ "$PARTNUM" =~ ^[0-9]+$ ]] || { err "Partitionsnummer muss numerisch sein."; exit 1; }
|
||||||
|
|
||||||
|
read -r -p "Dateisystem auf $MDDEV [ext4|xfs] (Standard ext4): " FS_TYPE
|
||||||
|
FS_TYPE="${FS_TYPE:-ext4}"
|
||||||
|
FS_TYPE="$(echo "$FS_TYPE" | tr '[:upper:]' '[:lower:]')"
|
||||||
|
if [[ "$FS_TYPE" != "ext4" && "$FS_TYPE" != "xfs" ]]; then
|
||||||
|
err "Ungültiges Dateisystem: $FS_TYPE (erlaubt: ext4, xfs)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
MOUNTPOINT=""
|
||||||
|
if [[ "$FS_TYPE" == "xfs" ]]; then
|
||||||
|
read -r -p "Mountpoint des XFS-Dateisystems (z.B. /mnt/raid): " MOUNTPOINT
|
||||||
|
[[ -n "$MOUNTPOINT" ]] || { err "Mountpoint für XFS erforderlich."; exit 1; }
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
header "ZUSAMMENFASSUNG"
|
||||||
|
note "Dry-Run : $([[ "$DRYRUN" -eq 1 ]] && echo "JA" || echo "NEIN")"
|
||||||
|
note "Debug : $([[ "$DEBUG" -eq 1 ]] && echo "JA" || echo "NEIN")"
|
||||||
|
note "Auto-Yes : $([[ "$ASSUME_YES" -eq 1 ]] && echo "JA" || echo "NEIN")"
|
||||||
|
note "RAID-Device: $MDDEV"
|
||||||
|
note "Disks : ${DISKS[*]}"
|
||||||
|
note "Part.-Nr. : $PARTNUM"
|
||||||
|
note "Filesystem : $FS_TYPE"
|
||||||
|
[[ "$FS_TYPE" == "xfs" ]] && note "Mountpoint : $MOUNTPOINT"
|
||||||
|
echo
|
||||||
|
|
||||||
|
confirm_summary "Eingaben korrekt? Fortfahren?" || { warn "Abgebrochen."; exit 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- Argumente parsen ----
|
||||||
|
parse_args() {
|
||||||
|
|
||||||
|
echo
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-n|--dry-run) DRYRUN=1; DEBUG=1; shift ;;
|
||||||
|
-d|--debug) DEBUG=1; shift ;;
|
||||||
|
-y|--yes) ASSUME_YES=1; shift ;;
|
||||||
|
-L|--log)
|
||||||
|
shift || true
|
||||||
|
[[ $# -gt 0 ]] || { err "--log erfordert einen Pfad"; exit 1; }
|
||||||
|
LOG_FILE="$1"; shift
|
||||||
|
;;
|
||||||
|
-h|--help) usage; echo; exit 0 ;;
|
||||||
|
*) err "Unbekannte Option: $1"; usage; echo; exit 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Logfile anlegen & Kopf schreiben
|
||||||
|
touch "$LOG_FILE" 2>/dev/null || { err "Kann Logfile nicht schreiben: $LOG_FILE"; exit 1; }
|
||||||
|
header "Start: raid6-expand-interactive | Log: $LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- Main ----
|
||||||
|
main() {
|
||||||
|
echo
|
||||||
|
|
||||||
|
need_bin mdadm
|
||||||
|
need_bin sfdisk
|
||||||
|
need_bin lsblk
|
||||||
|
need_bin blkid
|
||||||
|
need_bin awk
|
||||||
|
need_bin partprobe
|
||||||
|
need_bin udevadm
|
||||||
|
|
||||||
|
prompt_inputs
|
||||||
|
[[ "$FS_TYPE" == "xfs" ]] && need_bin xfs_growfs || true
|
||||||
|
|
||||||
|
for d in "${DISKS[@]}"; do
|
||||||
|
local p="${d}${PARTNUM}"
|
||||||
|
[[ -b "$p" ]] || { err "Erwartete Partition $p existiert nicht."; exit 1; }
|
||||||
|
done
|
||||||
|
|
||||||
|
header "RAID-Status vor Start"
|
||||||
|
mdadm --detail "$MDDEV" | sed 's/^/ /' | tee -a "$LOG_FILE"
|
||||||
|
echo
|
||||||
|
|
||||||
|
check_clean "$MDDEV"
|
||||||
|
confirm "Letzte Chance vor Änderungen. Weiter?" || exit 0
|
||||||
|
echo
|
||||||
|
|
||||||
|
for d in "${DISKS[@]}"; do
|
||||||
|
local p="${d}${PARTNUM}"
|
||||||
|
|
||||||
|
header "Bearbeite $d / $p"
|
||||||
|
if ! mdadm --detail "$MDDEV" | grep -q " active sync .*$(basename "$p")"; then
|
||||||
|
warn "$p scheint nicht als active sync im $MDDEV gelistet zu sein."
|
||||||
|
mdadm --detail "$MDDEV" | sed 's/^/ /' | tee -a "$LOG_FILE"
|
||||||
|
confirm "Trotzdem mit $p fortfahren?" || { echo; continue; }
|
||||||
|
fi
|
||||||
|
|
||||||
|
backup_pt "$d"
|
||||||
|
expand_partition "$d" "$PARTNUM"
|
||||||
|
grow_md_size "$MDDEV"
|
||||||
|
wait_resync "$MDDEV"
|
||||||
|
ok "Fertig mit $p."
|
||||||
|
echo
|
||||||
|
done
|
||||||
|
|
||||||
|
header "Final: Dateisystem vergrößern"
|
||||||
|
final_fs_grow "$MDDEV" "$FS_TYPE" "${MOUNTPOINT:-}"
|
||||||
|
echo
|
||||||
|
|
||||||
|
header "Abschluss"
|
||||||
|
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT | sed 's/^/ /' | tee -a "$LOG_FILE"
|
||||||
|
echo
|
||||||
|
mdadm --detail "$MDDEV" | sed 's/^/ /' | tee -a "$LOG_FILE"
|
||||||
|
echo
|
||||||
|
df -h | sed 's/^/ /' | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
echo
|
||||||
|
header "Ende"
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- Einstieg ----
|
||||||
|
parse_args "$@"
|
||||||
|
main "$@"
|
||||||
|
|
||||||
|
# (Ende)
|
||||||
|
echo
|
||||||
|
|
140
raid6-expand.md
Normal file
140
raid6-expand.md
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
Perfekt — dann schreibe ich dir jetzt einen **minimal-Risiko-Plan**, damit du dein RAID6 (`/dev/md4`) mit Ext4 sauber und ohne Datenverlust auf die volle Größe bringst.
|
||||||
|
|
||||||
|
Ich gehe davon aus, dass:
|
||||||
|
|
||||||
|
* `/dev/sda1`, `/dev/sdb1`, `/dev/sdc1`, `/dev/sdd1` **die RAID-Mitglieder** sind.
|
||||||
|
* Jede Platte 20 TB groß ist, die Partitionen aktuell nur 10 TB.
|
||||||
|
* Das RAID ist **gesund** (`cat /proc/mdstat` zeigt „clean“).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **Schritt-für-Schritt Anleitung – Minimal Risk**
|
||||||
|
|
||||||
|
### **1. Status checken**
|
||||||
|
|
||||||
|
Bevor wir irgendwas anfassen, prüfen wir den Status:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat /proc/mdstat
|
||||||
|
mdadm --detail /dev/md4
|
||||||
|
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT
|
||||||
|
```
|
||||||
|
|
||||||
|
Es muss **clean** sein, und die Partitionen (`sdX1`) müssen ungefähr 10 TB groß sein.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **2. Backup der Partitionstabelle**
|
||||||
|
|
||||||
|
Falls wir einen falschen Startsektor erwischen, ist das RAID im Eimer.
|
||||||
|
Deshalb sichern wir die Partitionstabellen **aller** Platten:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sfdisk -d /dev/sda > sda.part
|
||||||
|
sfdisk -d /dev/sdb > sdb.part
|
||||||
|
sfdisk -d /dev/sdc > sdc.part
|
||||||
|
sfdisk -d /dev/sdd > sdd.part
|
||||||
|
```
|
||||||
|
|
||||||
|
Damit kannst du im Notfall zurückschreiben:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sfdisk /dev/sda < sda.part
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **3. Eine Platte vergrößern (nacheinander!)**
|
||||||
|
|
||||||
|
Wir machen das **Platte für Platte**, damit immer 3 von 4 Platten voll intakt bleiben.
|
||||||
|
Beispiel für `/dev/sda`:
|
||||||
|
|
||||||
|
1. **Startsektor merken**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
parted /dev/sda unit s print
|
||||||
|
```
|
||||||
|
|
||||||
|
Notiere die **Start-Sektor-Zahl** der Partition 1 (z. B. `2048s`).
|
||||||
|
|
||||||
|
2. **Partition löschen und neu anlegen** (nur Metadaten, keine Daten!):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
parted /dev/sda
|
||||||
|
(parted) unit s
|
||||||
|
(parted) rm 1
|
||||||
|
(parted) mkpart primary 2048s 100%
|
||||||
|
(parted) quit
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Kernel über neue Partition informieren**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
partprobe /dev/sda
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **4. RAID Resync starten**
|
||||||
|
|
||||||
|
Nach dem Anpassen der ersten Platte muss mdadm den neuen Bereich initialisieren:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mdadm --grow /dev/md4 --size=max
|
||||||
|
```
|
||||||
|
|
||||||
|
⚠️ Das kann mehrere Stunden/Tage dauern.
|
||||||
|
Fortschritt beobachten:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
watch cat /proc/mdstat
|
||||||
|
```
|
||||||
|
|
||||||
|
Warte, bis der Status wieder **\[UUUU] clean** ist.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **5. Nächste Platten**
|
||||||
|
|
||||||
|
Wiederhole **Schritt 3** für `/dev/sdb`, dann `/dev/sdc`, dann `/dev/sdd`.
|
||||||
|
Nach jeder Platte läuft ein **Teil-Resync** – auch hier **immer abwarten**, bis es fertig ist, bevor du die nächste Platte anfässt.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **6. Dateisystem vergrößern**
|
||||||
|
|
||||||
|
Wenn alle Platten angepasst und der RAID-Resync komplett abgeschlossen ist:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
resize2fs /dev/md4
|
||||||
|
```
|
||||||
|
|
||||||
|
Das Ext4-Dateisystem wird dann den gesamten neuen Platz nutzen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **7. Kontrolle**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
df -h /mountpunkt
|
||||||
|
lsblk
|
||||||
|
mdadm --detail /dev/md4
|
||||||
|
```
|
||||||
|
|
||||||
|
Jetzt solltest du die volle Kapazität sehen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **Wichtige Sicherheitsregeln**
|
||||||
|
|
||||||
|
* Immer nur **eine Platte gleichzeitig** verändern.
|
||||||
|
* Niemals den **Startsektor** ändern!
|
||||||
|
* Während Resyncs am besten keine großen Schreiblasten.
|
||||||
|
* Falls irgendetwas nicht clean ist → erst reparieren, dann weitermachen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Wenn du magst, kann ich dir **ein fertiges Shell-Skript** bauen, das dir pro Platte die Startsektoren automatisch liest, die Partition neu anlegt und sicherstellt, dass du dich nicht vertippst.
|
||||||
|
Das minimiert Tippfehler und verhindert den Super-GAU.
|
||||||
|
|
||||||
|
Willst du, dass ich dir so ein Skript direkt schreibe?
|
145
raid6-expand.txt
Normal file
145
raid6-expand.txt
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
============================================================
|
||||||
|
Anleitung: RAID-6 Array erweitern (mdadm, GPT, ext4/xfs)
|
||||||
|
============================================================
|
||||||
|
|
||||||
|
Diese Anleitung beschreibt die Erweiterung eines bestehenden
|
||||||
|
RAID-6-Arrays, nachdem die einzelnen RAID-Mitgliedsplatten physisch
|
||||||
|
durch größere Platten ersetzt wurden und ein Resync durchgeführt wurde.
|
||||||
|
|
||||||
|
ACHTUNG:
|
||||||
|
- Nur durchführen, wenn RAID in "clean"-State ist.
|
||||||
|
- Backup wichtiger Daten wird dringend empfohlen.
|
||||||
|
- Root-Rechte erforderlich.
|
||||||
|
- Beispielangaben (Gerätenamen) anpassen!
|
||||||
|
|
||||||
|
------------------------------------------------------------
|
||||||
|
1) Vorbereitung & Kontrolle
|
||||||
|
------------------------------------------------------------
|
||||||
|
1.1 Prüfen, dass alle neuen, größeren Platten erkannt werden:
|
||||||
|
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT
|
||||||
|
|
||||||
|
1.2 RAID-Status prüfen:
|
||||||
|
cat /proc/mdstat
|
||||||
|
mdadm --detail /dev/mdX
|
||||||
|
|
||||||
|
1.3 Sicherstellen, dass der Status "clean" ist.
|
||||||
|
Falls "resync" oder "recovery" läuft → warten, bis fertig.
|
||||||
|
|
||||||
|
------------------------------------------------------------
|
||||||
|
2) Partitionstabellen sichern
|
||||||
|
------------------------------------------------------------
|
||||||
|
Für jedes RAID-Mitgliedsgerät:
|
||||||
|
sfdisk -d /dev/sdX > backup-sdX-YYYYMMDD.sfdisk
|
||||||
|
|
||||||
|
------------------------------------------------------------
|
||||||
|
3) Partitionen auf Mitgliedsplatten vergrößern
|
||||||
|
------------------------------------------------------------
|
||||||
|
Für jede Platte:
|
||||||
|
- Startsektor unverändert lassen!
|
||||||
|
- Partition bis zum maximal verfügbaren Platz vergrößern.
|
||||||
|
- GPT-Partitionstyp unverändert lassen.
|
||||||
|
|
||||||
|
- TYP 'A19D880F-05FC-4D3B-A006-743F0F84911E' für Linux-RAID
|
||||||
|
|
||||||
|
|
||||||
|
1. Vergrößern auf maximal verfügbaren Platz mit sfdisk:
|
||||||
|
|
||||||
|
- GPT-UUID ermitteln z.bsp. für partition 1 von /dev/sda1:
|
||||||
|
|
||||||
|
lsblk -no PARTTYPE "/dev/sda1"
|
||||||
|
|
||||||
|
- Partition 1 con /dev/sdX vergrößern
|
||||||
|
|
||||||
|
sfdisk --no-reread --force -N 1 /dev/sdX <<EOF
|
||||||
|
,,type=A19D880F-05FC-4D3B-A006-743F0F84911E
|
||||||
|
EOF
|
||||||
|
|
||||||
|
--no-reread: Verhindert, dass der Kernel die Partitionstabelle sofort neu einliest.
|
||||||
|
--force: Erzwingt Änderungen auch dann, wenn sfdisk normalerweise abbrechen würde,
|
||||||
|
z. B. weil die Partition in Benutzung ist.
|
||||||
|
N 1: Bearbeitet nur die angegebene Partitionsnummer – hier also Partition 1.
|
||||||
|
|
||||||
|
Eingabe-Redirect <<EOF ... EOF: Damit wird der eigentliche neue Partitions-Eintrag
|
||||||
|
an sfdisk per Here-Document übergeben.
|
||||||
|
Startsektor, Größe, Typ
|
||||||
|
|
||||||
|
- Startsektor: Wenn leer (""), bleibt der bisherige Startsektor erhalten.
|
||||||
|
- Größe: Wenn leer (""), nimmt sfdisk den maximal verfügbaren Platz bis zum Plattenende.
|
||||||
|
- Typ: Hier type=<GPT-UUID>
|
||||||
|
|
||||||
|
oder
|
||||||
|
|
||||||
|
1. Vergrößern auf maximal verfügbaren Platz mit parted:
|
||||||
|
|
||||||
|
parted /dev/sdX
|
||||||
|
(parted) print
|
||||||
|
(parted) resizepart 1 100%
|
||||||
|
(parted) quit
|
||||||
|
|
||||||
|
oder in einer Zeile:
|
||||||
|
|
||||||
|
parted -s /dev/sdX resizepart 1 100%
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
2. Danach Partitionstabelle neu einlesen:
|
||||||
|
partprobe /dev/sdX
|
||||||
|
udevadm settle
|
||||||
|
|
||||||
|
|
||||||
|
Bemerkung 'parted' im Vergleich zu sfdisk:
|
||||||
|
|
||||||
|
- sfdisk ist "roh", arbeitet auf Sektorebene - super für Skripte, aber etwas sperrig für Menschen.
|
||||||
|
|
||||||
|
- parted versteht Größenangaben wie 100%, 10TiB, etc. und ist interaktiv angenehmer. resize verändert
|
||||||
|
nur die Größe, nicht den Typcode - der Linux-Typ bleibt also bestehen.
|
||||||
|
|
||||||
|
- Für ein Skript wäre sfdisk robuster, weil es keine Rückfragen stellt und 100 % skriptfähig ist.
|
||||||
|
|
||||||
|
------------------------------------------------------------
|
||||||
|
4) RAID-Array vergrößern
|
||||||
|
------------------------------------------------------------
|
||||||
|
Nachdem alle Partitionen angepasst sind:
|
||||||
|
mdadm --grow /dev/mdX --size=max
|
||||||
|
|
||||||
|
Anschließend auf Abschluss des internen Resync warten:
|
||||||
|
watch cat /proc/mdstat
|
||||||
|
|
||||||
|
------------------------------------------------------------
|
||||||
|
5) Dateisystem vergrößern
|
||||||
|
------------------------------------------------------------
|
||||||
|
Je nach Dateisystem:
|
||||||
|
|
||||||
|
- ext4:
|
||||||
|
resize2fs /dev/mdX
|
||||||
|
|
||||||
|
- xfs (muss gemountet sein):
|
||||||
|
xfs_growfs <Mountpoint>
|
||||||
|
|
||||||
|
------------------------------------------------------------
|
||||||
|
6) Abschließende Kontrolle
|
||||||
|
------------------------------------------------------------
|
||||||
|
- Blockgeräte prüfen:
|
||||||
|
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT
|
||||||
|
|
||||||
|
- RAID-Details:
|
||||||
|
mdadm --detail /dev/mdX
|
||||||
|
|
||||||
|
- Dateisystemgröße:
|
||||||
|
df -h <Mountpoint>
|
||||||
|
|
||||||
|
------------------------------------------------------------
|
||||||
|
Hinweise
|
||||||
|
------------------------------------------------------------
|
||||||
|
- GPT-Typcode ermitteln - z.bsp. mit
|
||||||
|
|
||||||
|
lsblk -no PARTTYPE /dev/sdX1
|
||||||
|
|
||||||
|
für Linux RAID Partition (bei dir):
|
||||||
|
A19D880F-05FC-4D3B-A006-743F0F84911E
|
||||||
|
|
||||||
|
- Wenn nur einzelne Platten ersetzt/geändert werden:
|
||||||
|
Schrittweise vorgehen: Platte ersetzen - RAID resync - nächste Platte.
|
||||||
|
|
||||||
|
- Bei Einsatz von LUKS oder LVM: Zusätzliche Schritte für LUKS-Resize
|
||||||
|
bzw. LVM PV Resize nach dem RAID-Resize durchführen.
|
Reference in New Issue
Block a user