Preserve existing Bitcoin data drive during reinstall
Agent-Logs-Url: https://github.com/naturallaw777/staging_alpha/sessions/8a51f052-83d0-4079-8338-5cfdbb849aa2 Co-authored-by: naturallaw777 <99053422+naturallaw777@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
c8eb452a70
commit
846e2af705
@@ -156,6 +156,7 @@ class InstallerWindow(Adw.ApplicationWindow):
|
||||
self.boot_size = None
|
||||
self.data_disk = None
|
||||
self.data_size = None
|
||||
self.data_drive_has_timechain = False
|
||||
self.free_password = None
|
||||
|
||||
# Root navigation view
|
||||
@@ -667,10 +668,18 @@ class InstallerWindow(Adw.ApplicationWindow):
|
||||
|
||||
def push_disk_confirm(self):
|
||||
"""Show the selected drives and ask the user to type ERASE to confirm."""
|
||||
self.data_drive_has_timechain = False
|
||||
if self.data_disk:
|
||||
data_path = f"/dev/{self.data_disk}"
|
||||
self.data_drive_has_timechain = self.detect_existing_timechain_data(data_path)
|
||||
|
||||
outer = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
|
||||
|
||||
# Disk info group
|
||||
disk_group = Adw.PreferencesGroup()
|
||||
if self.data_disk and self.data_drive_has_timechain:
|
||||
disk_group.set_title("OS drive to be erased (data drive preserved)")
|
||||
else:
|
||||
disk_group.set_title("Drives to be erased")
|
||||
disk_group.set_margin_top(24)
|
||||
disk_group.set_margin_start(40)
|
||||
@@ -688,11 +697,20 @@ class InstallerWindow(Adw.ApplicationWindow):
|
||||
data_row.set_subtitle(f"/dev/{self.data_disk} — {human_size(self.data_size)}")
|
||||
data_row.add_prefix(symbolic_icon("drive-harddisk-symbolic"))
|
||||
disk_group.add(data_row)
|
||||
if self.data_drive_has_timechain:
|
||||
note_row = Adw.ActionRow()
|
||||
note_row.set_title(f"✓ Existing Bitcoin timechain detected on /dev/{self.data_disk}")
|
||||
note_row.set_subtitle("Data will be preserved and mounted as-is.")
|
||||
note_row.add_prefix(symbolic_icon("emblem-ok-symbolic"))
|
||||
disk_group.add(note_row)
|
||||
|
||||
outer.append(disk_group)
|
||||
|
||||
# Warning banner
|
||||
banner = Adw.Banner()
|
||||
if self.data_disk and self.data_drive_has_timechain:
|
||||
banner.set_title("⚠ All data on the OS disk will be permanently destroyed. Existing Bitcoin data disk will be preserved.")
|
||||
else:
|
||||
banner.set_title("⚠ All data on the above disk(s) will be permanently destroyed.")
|
||||
banner.set_revealed(True)
|
||||
banner.set_margin_top(16)
|
||||
@@ -775,9 +793,60 @@ class InstallerWindow(Adw.ApplicationWindow):
|
||||
|
||||
# ── Worker: partition ─────────────────────────────────────────────────
|
||||
|
||||
def partition_path(self, dev_path, num):
|
||||
return f"{dev_path}p{num}" if "nvme" in dev_path else f"{dev_path}{num}"
|
||||
|
||||
def detect_existing_timechain_data(self, data_path, buf=None):
|
||||
data_p1 = self.partition_path(data_path, 1)
|
||||
if not os.path.exists(data_p1):
|
||||
return False
|
||||
|
||||
label = ""
|
||||
for cmd in (
|
||||
["sudo", "lsblk", "-no", "LABEL", data_p1],
|
||||
["sudo", "blkid", "-o", "value", "-s", "LABEL", data_p1],
|
||||
):
|
||||
proc = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if proc.returncode == 0:
|
||||
label = proc.stdout.strip().splitlines()[0] if proc.stdout.strip() else ""
|
||||
if label:
|
||||
break
|
||||
|
||||
if label != "BTCEcoandBackup":
|
||||
return False
|
||||
|
||||
check_mount = "/tmp/sovran-installer-data-check"
|
||||
mounted = False
|
||||
try:
|
||||
run(["sudo", "mkdir", "-p", check_mount])
|
||||
run(["sudo", "mount", "-o", "ro", data_p1, check_mount])
|
||||
mounted = True
|
||||
|
||||
has_bitcoin = os.path.isdir(f"{check_mount}/BTCEcoandBackup/Bitcoin_Node")
|
||||
has_electrs = os.path.isdir(f"{check_mount}/BTCEcoandBackup/Electrs_Data")
|
||||
if has_bitcoin and has_electrs:
|
||||
if buf is not None:
|
||||
GLib.idle_add(
|
||||
append_text,
|
||||
buf,
|
||||
"=== Existing Bitcoin timechain detected on data drive — preserving data ===\n",
|
||||
)
|
||||
return True
|
||||
return False
|
||||
except Exception:
|
||||
return False
|
||||
finally:
|
||||
if mounted:
|
||||
subprocess.run(["sudo", "umount", check_mount], capture_output=True, text=True)
|
||||
subprocess.run(["sudo", "rmdir", check_mount], capture_output=True, text=True)
|
||||
|
||||
def do_partition(self, buf):
|
||||
boot_path = f"/dev/{self.boot_disk}"
|
||||
data_path = f"/dev/{self.data_disk}" if self.data_disk else None
|
||||
self.data_drive_has_timechain = False
|
||||
|
||||
if data_path:
|
||||
self.data_drive_has_timechain = self.detect_existing_timechain_data(data_path, buf)
|
||||
|
||||
# ── Wipe disk(s) ──
|
||||
GLib.idle_add(append_text, buf, "=== Wiping disk(s) ===\n")
|
||||
@@ -785,12 +854,12 @@ class InstallerWindow(Adw.ApplicationWindow):
|
||||
run_stream(["sudo", "sgdisk", "--zap-all", boot_path], buf)
|
||||
run_stream(["sudo", "wipefs", "--all", "--force", boot_path], buf)
|
||||
|
||||
if data_path:
|
||||
if data_path and not self.data_drive_has_timechain:
|
||||
run_stream(["sudo", "sgdisk", "--zap-all", data_path], buf)
|
||||
run_stream(["sudo", "wipefs", "--all", "--force", data_path], buf)
|
||||
|
||||
run_stream(["sudo", "partprobe", boot_path], buf)
|
||||
if data_path:
|
||||
if data_path and not self.data_drive_has_timechain:
|
||||
run_stream(["sudo", "partprobe", data_path], buf)
|
||||
|
||||
time.sleep(2)
|
||||
@@ -806,7 +875,7 @@ class InstallerWindow(Adw.ApplicationWindow):
|
||||
time.sleep(2)
|
||||
|
||||
# ── Partition data disk (if selected) ──
|
||||
if data_path:
|
||||
if data_path and not self.data_drive_has_timechain:
|
||||
GLib.idle_add(append_text, buf, "\n=== Partitioning data disk ===\n")
|
||||
run_stream(["sudo", "sgdisk",
|
||||
"-n", "1:1M:0", "-t", "1:8300", "-c", "1:primary",
|
||||
@@ -817,14 +886,14 @@ class InstallerWindow(Adw.ApplicationWindow):
|
||||
|
||||
# ── Format partitions ──
|
||||
GLib.idle_add(append_text, buf, "\n=== Formatting partitions ===\n")
|
||||
boot_p1 = f"{boot_path}p1" if "nvme" in boot_path else f"{boot_path}1"
|
||||
boot_p2 = f"{boot_path}p2" if "nvme" in boot_path else f"{boot_path}2"
|
||||
boot_p1 = self.partition_path(boot_path, 1)
|
||||
boot_p2 = self.partition_path(boot_path, 2)
|
||||
|
||||
run_stream(["sudo", "mkfs.vfat", "-F", "32", boot_p1], buf)
|
||||
run_stream(["sudo", "mkfs.ext4", "-F", "-L", "sovran_systemsos", boot_p2], buf)
|
||||
|
||||
if data_path:
|
||||
data_p1 = f"{data_path}p1" if "nvme" in data_path else f"{data_path}1"
|
||||
if data_path and not self.data_drive_has_timechain:
|
||||
data_p1 = self.partition_path(data_path, 1)
|
||||
run_stream(["sudo", "mkfs.ext4", "-F", "-L", "BTCEcoandBackup", data_p1], buf)
|
||||
|
||||
# ── Mount filesystems ──
|
||||
@@ -834,7 +903,7 @@ class InstallerWindow(Adw.ApplicationWindow):
|
||||
run_stream(["sudo", "mount", "-o", "umask=0077,defaults", boot_p1, "/mnt/boot/efi"], buf)
|
||||
|
||||
if data_path:
|
||||
data_p1 = f"{data_path}p1" if "nvme" in data_path else f"{data_path}1"
|
||||
data_p1 = self.partition_path(data_path, 1)
|
||||
run_stream(["sudo", "mkdir", "-p", "/mnt/run/media/Second_Drive"], buf)
|
||||
run_stream(["sudo", "mount", data_p1, "/mnt/run/media/Second_Drive"], buf)
|
||||
run_stream(["sudo", "mkdir", "-p", "/mnt/run/media/Second_Drive/BTCEcoandBackup/Bitcoin_Node"], buf)
|
||||
|
||||
@@ -24,6 +24,7 @@ ROLE="server"
|
||||
DEPLOY_KEY=""
|
||||
HEADSCALE_SERVER=""
|
||||
HEADSCALE_KEY=""
|
||||
DATA_DISK_HAS_TIMECHAIN=false
|
||||
|
||||
FLAKE="/etc/sovran/flake"
|
||||
LOG="/tmp/sovran-headless-install.log"
|
||||
@@ -102,19 +103,42 @@ part_suffix() {
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Detect existing Bitcoin timechain data on data disk ───────────────────────
|
||||
if [[ -n "$DATA_DISK" ]]; then
|
||||
DATA_P1=$(part_suffix "$DATA_DISK" 1)
|
||||
if [[ -b "$DATA_P1" ]]; then
|
||||
DATA_LABEL=$(lsblk -no LABEL "$DATA_P1" 2>/dev/null | head -n1 || true)
|
||||
if [[ "$DATA_LABEL" != "BTCEcoandBackup" ]]; then
|
||||
DATA_LABEL=$(blkid -o value -s LABEL "$DATA_P1" 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
if [[ "$DATA_LABEL" == "BTCEcoandBackup" ]]; then
|
||||
CHECK_MOUNT=$(mktemp -d /tmp/sovran-data-check.XXXXXX)
|
||||
if mount -o ro "$DATA_P1" "$CHECK_MOUNT" 2>/dev/null; then
|
||||
if [[ -d "$CHECK_MOUNT/BTCEcoandBackup/Bitcoin_Node" && -d "$CHECK_MOUNT/BTCEcoandBackup/Electrs_Data" ]]; then
|
||||
DATA_DISK_HAS_TIMECHAIN=true
|
||||
log "Existing Bitcoin timechain detected on data drive — preserving data"
|
||||
fi
|
||||
umount "$CHECK_MOUNT" || true
|
||||
fi
|
||||
rmdir "$CHECK_MOUNT" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Step 1: Wipe disks ────────────────────────────────────────────────────────
|
||||
log "=== Wiping disk(s) ==="
|
||||
|
||||
sgdisk --zap-all "$DISK"
|
||||
wipefs --all --force "$DISK"
|
||||
|
||||
if [[ -n "$DATA_DISK" ]]; then
|
||||
if [[ -n "$DATA_DISK" && "$DATA_DISK_HAS_TIMECHAIN" != true ]]; then
|
||||
sgdisk --zap-all "$DATA_DISK"
|
||||
wipefs --all --force "$DATA_DISK"
|
||||
fi
|
||||
|
||||
partprobe "$DISK"
|
||||
[[ -n "$DATA_DISK" ]] && partprobe "$DATA_DISK"
|
||||
[[ -n "$DATA_DISK" && "$DATA_DISK_HAS_TIMECHAIN" != true ]] && partprobe "$DATA_DISK"
|
||||
sleep 2
|
||||
|
||||
# ── Step 2: Partition OS disk ─────────────────────────────────────────────────
|
||||
@@ -129,7 +153,7 @@ partprobe "$DISK"
|
||||
sleep 2
|
||||
|
||||
# ── Step 3: Partition data disk (if present) ──────────────────────────────────
|
||||
if [[ -n "$DATA_DISK" ]]; then
|
||||
if [[ -n "$DATA_DISK" && "$DATA_DISK_HAS_TIMECHAIN" != true ]]; then
|
||||
log "=== Partitioning data disk ==="
|
||||
sgdisk \
|
||||
-n "1:1M:0" -t "1:8300" -c "1:primary" \
|
||||
@@ -147,7 +171,7 @@ BOOT_P2=$(part_suffix "$DISK" 2)
|
||||
mkfs.vfat -F 32 "$BOOT_P1"
|
||||
mkfs.ext4 -F -L sovran_systemsos "$BOOT_P2"
|
||||
|
||||
if [[ -n "$DATA_DISK" ]]; then
|
||||
if [[ -n "$DATA_DISK" && "$DATA_DISK_HAS_TIMECHAIN" != true ]]; then
|
||||
DATA_P1=$(part_suffix "$DATA_DISK" 1)
|
||||
mkfs.ext4 -F -L BTCEcoandBackup "$DATA_P1"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user