Reuse an existing install or backup in router-setup
router-setup finds earlier installs and router-backup-*.tar.gz files and reinstalls them with everything in /var/lib/router carried over. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AeFqV9dZrK6nipHkPfyM4F
This commit is contained in:
+156
-5
@@ -4,6 +4,11 @@
|
|||||||
# (offline: the closure of `reference` brings the sources and everything the
|
# (offline: the closure of `reference` brings the sources and everything the
|
||||||
# build needs) and installs it with `router-install`. Everything else is done
|
# build needs) and installs it with `router-install`. Everything else is done
|
||||||
# in the web UI afterwards.
|
# in the web UI afterwards.
|
||||||
|
#
|
||||||
|
# An existing router (an installed disk, or a backup from the web UI on any
|
||||||
|
# disk or USB stick) can be reinstalled instead: its whole /var/lib/router
|
||||||
|
# (settings, secrets, passwords, certificate, dashboard history) carries over
|
||||||
|
# and only interfaces this machine lacks are asked for.
|
||||||
{
|
{
|
||||||
config,
|
config,
|
||||||
lib,
|
lib,
|
||||||
@@ -13,15 +18,20 @@
|
|||||||
let
|
let
|
||||||
cfg = config.router.setup;
|
cfg = config.router.setup;
|
||||||
webui = pkgs.callPackage ../../pkgs/router-webui/package.nix { };
|
webui = pkgs.callPackage ../../pkgs/router-webui/package.nix { };
|
||||||
|
migrate = pkgs.callPackage ../../pkgs/router-migrate.nix { };
|
||||||
|
|
||||||
setup = pkgs.writeShellApplication {
|
setup = pkgs.writeShellApplication {
|
||||||
name = "router-setup";
|
name = "router-setup";
|
||||||
runtimeInputs = [
|
runtimeInputs = [
|
||||||
config.nix.package
|
config.nix.package
|
||||||
webui
|
webui
|
||||||
|
migrate
|
||||||
pkgs.coreutils
|
pkgs.coreutils
|
||||||
|
pkgs.findutils
|
||||||
pkgs.gawk
|
pkgs.gawk
|
||||||
pkgs.gnugrep
|
pkgs.gnugrep
|
||||||
|
pkgs.gnutar
|
||||||
|
pkgs.gzip
|
||||||
pkgs.jq
|
pkgs.jq
|
||||||
pkgs.mkpasswd
|
pkgs.mkpasswd
|
||||||
pkgs.util-linux
|
pkgs.util-linux
|
||||||
@@ -83,9 +93,23 @@ let
|
|||||||
for o in "''${BASH_REMATCH[@]:1}"; do [ "$o" -le 255 ] || return 1; done
|
for o in "''${BASH_REMATCH[@]:1}"; do [ "$o" -le 255 ] || return 1; done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# summary DIR: one line about the router whose state is in DIR.
|
||||||
|
summary() {
|
||||||
|
jq -r '"\(.system.hostName // "router"): WAN \(.router.wan.interface) (\(.router.wan.ipv4.type // "dhcp")), LAN \([.router.lans[]? | "\(.interface) \(.ipv4.address)"] | join(", "))"' \
|
||||||
|
"$1/config/settings.json" 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
# Set by ask/choose (printf -v).
|
# Set by ask/choose (printf -v).
|
||||||
disk="" wan="" lan="" wan_type="" host="" lan_address="" ssh_key="" answer=""
|
disk="" wan="" lan="" wan_type="" host="" lan_address="" ssh_key="" answer=""
|
||||||
webui_password="" webui_password2=""
|
webui_password="" webui_password2="" reuse="" reuse_label="" new=""
|
||||||
|
|
||||||
|
work=$(mktemp -d /run/router-setup.XXXXXX)
|
||||||
|
chmod 0700 "$work"
|
||||||
|
cleanup() {
|
||||||
|
umount "$work/mnt" 2>/dev/null || true
|
||||||
|
rm -rf "$work"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "nixos-router setup"
|
echo "nixos-router setup"
|
||||||
@@ -98,6 +122,62 @@ let
|
|||||||
if src=$(findmnt -no SOURCE /iso 2>/dev/null) && [ -n "$src" ]; then
|
if src=$(findmnt -no SOURCE /iso 2>/dev/null) && [ -n "$src" ]; then
|
||||||
boot_disk=/dev/$(lsblk -no PKNAME "$src" | head -n1)
|
boot_disk=/dev/$(lsblk -no PKNAME "$src" | head -n1)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Existing routers: installed systems and web UI backups, on any
|
||||||
|
# filesystem but the installer's own. Each found state is copied to
|
||||||
|
# $work/found/N (the disk may be the one about to be erased).
|
||||||
|
found=() found_labels=()
|
||||||
|
mkdir -p "$work/mnt" "$work/found"
|
||||||
|
# A stick plugged in a moment ago may not be probed yet.
|
||||||
|
udevadm settle --timeout=10 || true
|
||||||
|
while read -r dev fstype; do
|
||||||
|
case $fstype in ext2|ext3|ext4|vfat|exfat|ntfs|ntfs3|btrfs|xfs) ;; *) continue ;; esac
|
||||||
|
parent=$(lsblk -no PKNAME "$dev" 2>/dev/null | head -n1)
|
||||||
|
[ -n "$boot_disk" ] && [ "/dev/$parent" = "$boot_disk" ] && continue
|
||||||
|
[ -n "$boot_disk" ] && [ "$dev" = "$boot_disk" ] && continue
|
||||||
|
opts=ro
|
||||||
|
case $fstype in ext*) opts=ro,noload ;; esac
|
||||||
|
mount -o "$opts" "$dev" "$work/mnt" >/dev/null 2>&1 || continue
|
||||||
|
if [ -f "$work/mnt/var/lib/router/config/settings.json" ]; then
|
||||||
|
d=$work/found/''${#found[@]}
|
||||||
|
if cp -a "$work/mnt/var/lib/router" "$d" 2>/dev/null && line=$(summary "$d"); then
|
||||||
|
found+=("$d") found_labels+=("installed on $dev: $line")
|
||||||
|
else
|
||||||
|
rm -rf "$d"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
while IFS= read -r -d "" backup; do
|
||||||
|
d=$work/found/''${#found[@]}
|
||||||
|
mkdir -p "$d.x"
|
||||||
|
if tar -xzf "$backup" -C "$d.x" --no-same-owner 2>/dev/null && [ -d "$d.x/router" ] \
|
||||||
|
&& mv "$d.x/router" "$d" && line=$(summary "$d"); then
|
||||||
|
found+=("$d") found_labels+=("backup ''${backup#"$work/mnt/"} on $dev: $line")
|
||||||
|
else
|
||||||
|
rm -rf "$d"
|
||||||
|
fi
|
||||||
|
rm -rf "$d.x"
|
||||||
|
done < <(find "$work/mnt" -maxdepth 2 -type f -name 'router-backup-*.tar.gz' -print0 2>/dev/null)
|
||||||
|
umount "$work/mnt"
|
||||||
|
done < <(lsblk -rnpo NAME,FSTYPE | awk 'NF == 2')
|
||||||
|
|
||||||
|
if [ ''${#found[@]} -gt 0 ]; then
|
||||||
|
echo "Existing router configurations:"
|
||||||
|
i=1
|
||||||
|
for l in "''${found_labels[@]}"; do echo " $i) $l"; i=$((i + 1)); done
|
||||||
|
echo " n) none, set up a new router"
|
||||||
|
while :; do
|
||||||
|
ask answer "Reinstall one of them (everything carries over: settings, passwords, keys)" "1"
|
||||||
|
[ "$answer" = n ] && break
|
||||||
|
if [[ $answer =~ ^[0-9]+$ ]] && [ "$answer" -ge 1 ] && [ "$answer" -le ''${#found[@]} ]; then
|
||||||
|
reuse=''${found[$((answer - 1))]}
|
||||||
|
reuse_label=''${found_labels[$((answer - 1))]}
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
echo " a number from the list, or n" >&2
|
||||||
|
done
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
|
||||||
mapfile -t disks < <(lsblk -dnpo NAME,SIZE,MODEL,TYPE | awk '$NF == "disk" { $NF = ""; print }' \
|
mapfile -t disks < <(lsblk -dnpo NAME,SIZE,MODEL,TYPE | awk '$NF == "disk" { $NF = ""; print }' \
|
||||||
| grep -v "^$boot_disk " || true)
|
| grep -v "^$boot_disk " || true)
|
||||||
[ ''${#disks[@]} -gt 0 ] || { echo "router-setup: no disk to install to" >&2; exit 1; }
|
[ ''${#disks[@]} -gt 0 ] || { echo "router-setup: no disk to install to" >&2; exit 1; }
|
||||||
@@ -112,6 +192,66 @@ let
|
|||||||
nics+=("$n $(cat "$dev/address") link $state")
|
nics+=("$n $(cat "$dev/address") link $state")
|
||||||
done
|
done
|
||||||
[ ''${#nics[@]} -gt 0 ] || { echo "router-setup: no network interfaces found" >&2; exit 1; }
|
[ ''${#nics[@]} -gt 0 ] || { echo "router-setup: no network interfaces found" >&2; exit 1; }
|
||||||
|
|
||||||
|
if [ -n "$reuse" ]; then
|
||||||
|
settings=$reuse/config/settings.json
|
||||||
|
# Interfaces this machine does not have (other hardware, other names).
|
||||||
|
# (The list comes in on fd 3: stdin is for the answers.)
|
||||||
|
while IFS= read -r old <&3; do
|
||||||
|
[ -e "/sys/class/net/$old" ] && continue
|
||||||
|
echo
|
||||||
|
echo "This machine has no interface $old ($(jq -r --arg i "$old" '
|
||||||
|
[(if .router.wan.interface == $i then "WAN" else empty end),
|
||||||
|
(.router.lans // {} | to_entries[] | select(.value.interface == $i) | "LAN \(.key)")] | join(", ")' "$settings")). Which one takes its place?"
|
||||||
|
choose new "Interface" "''${nics[@]}"
|
||||||
|
jq --arg o "$old" --arg n "$new" '
|
||||||
|
.router.wan |= (if .interface == $o then .interface = $n else . end)
|
||||||
|
| .router.lans |= ((. // {}) | map_values(if .interface == $o then .interface = $n else . end))' \
|
||||||
|
"$settings" > "$settings.new"
|
||||||
|
mv "$settings.new" "$settings"
|
||||||
|
done 3< <(jq -r '[.router.wan.interface] + [.router.lans[]?.interface] | unique | .[]' "$settings")
|
||||||
|
|
||||||
|
# Older installs may lack the password files.
|
||||||
|
if [ ! -s "$reuse/webui-password" ] || [ ! -s "$reuse/root-password" ]; then
|
||||||
|
echo
|
||||||
|
echo "The configuration has no password for the web UI and root; choose one (at least 8 characters):"
|
||||||
|
while :; do
|
||||||
|
ask_secret webui_password "Password"
|
||||||
|
ask_secret webui_password2 "Again"
|
||||||
|
[ "$webui_password" = "$webui_password2" ] && [ ''${#webui_password} -ge 8 ] && break
|
||||||
|
echo " the passwords differ or are too short" >&2
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
lan_address=$(jq -r '[.router.lans[]?.ipv4.address][0] // empty' "$settings")
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Summary"
|
||||||
|
echo " disk: $disk (will be erased)"
|
||||||
|
echo " reinstall: $reuse_label"
|
||||||
|
echo " now: $(summary "$reuse")"
|
||||||
|
ask answer "Type the disk name ($disk) to install"
|
||||||
|
[ "$answer" = "$disk" ] || { echo "aborted" >&2; exit 1; }
|
||||||
|
|
||||||
|
# The state as it was, with this installer's flake (the old one pins a
|
||||||
|
# nixos-router store path that will not exist on the new system).
|
||||||
|
mv "$reuse" "$work/secrets"
|
||||||
|
config=$work/secrets/config
|
||||||
|
install -m 0644 ${cfg.flake} "$config/flake.nix"
|
||||||
|
rm -f "$config/flake.lock" "$config/result"
|
||||||
|
if [ -n "$local_nix" ]; then cp "$local_nix" "$config/local.nix"; fi
|
||||||
|
${lib.optionalString (cfg.release != null) ''
|
||||||
|
# An older configuration may need its settings brought up to date.
|
||||||
|
router-migrate-settings ${cfg.release} "$config/settings.json"
|
||||||
|
''}
|
||||||
|
# The new system is confirmed from settings.json as installed.
|
||||||
|
rm -f "$work/secrets/settings.confirmed.json"
|
||||||
|
rm -rf "$work/found"
|
||||||
|
if [ -n "$webui_password" ]; then
|
||||||
|
printf '%s\n' "$webui_password" | router-webui passwd "$work/secrets/webui-password"
|
||||||
|
(umask 077; printf '%s\n' "$webui_password" | mkpasswd -s > "$work/secrets/root-password")
|
||||||
|
fi
|
||||||
|
else # a new router
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "Interface towards the internet (WAN):"
|
echo "Interface towards the internet (WAN):"
|
||||||
choose wan "WAN interface" "''${nics[@]}"
|
choose wan "WAN interface" "''${nics[@]}"
|
||||||
@@ -169,9 +309,7 @@ let
|
|||||||
ask answer "Type the disk name ($disk) to install"
|
ask answer "Type the disk name ($disk) to install"
|
||||||
[ "$answer" = "$disk" ] || { echo "aborted" >&2; exit 1; }
|
[ "$answer" = "$disk" ] || { echo "aborted" >&2; exit 1; }
|
||||||
|
|
||||||
work=$(mktemp -d /run/router-setup.XXXXXX)
|
rm -rf "$work/found"
|
||||||
chmod 0700 "$work"
|
|
||||||
trap 'rm -rf "$work"' EXIT
|
|
||||||
mkdir -p "$work/secrets/config"
|
mkdir -p "$work/secrets/config"
|
||||||
config=$work/secrets/config
|
config=$work/secrets/config
|
||||||
cp ${cfg.flake} "$config/flake.nix"
|
cp ${cfg.flake} "$config/flake.nix"
|
||||||
@@ -181,9 +319,11 @@ let
|
|||||||
keys=$( [ -n "$ssh_key" ] && jq -n --arg k "$ssh_key" '[$k]' || echo '[]')
|
keys=$( [ -n "$ssh_key" ] && jq -n --arg k "$ssh_key" '[$k]' || echo '[]')
|
||||||
jq -n \
|
jq -n \
|
||||||
--arg host "$host" --arg state "${cfg.stateVersion}" --argjson keys "$keys" \
|
--arg host "$host" --arg state "${cfg.stateVersion}" --argjson keys "$keys" \
|
||||||
|
--argjson schema ${toString cfg.schemaVersion} \
|
||||||
--arg wan "$wan" --arg type "$wan_type" --arg user "$pppoe_user" --arg vlan "$pppoe_vlan" \
|
--arg wan "$wan" --arg type "$wan_type" --arg user "$pppoe_user" --arg vlan "$pppoe_vlan" \
|
||||||
--arg lan "$lan" --arg addr "$lan_address" --arg net "$net" '
|
--arg lan "$lan" --arg addr "$lan_address" --arg net "$net" '
|
||||||
{
|
{
|
||||||
|
version: $schema,
|
||||||
system: { hostName: $host, stateVersion: $state, sshKeys: $keys },
|
system: { hostName: $host, stateVersion: $state, sshKeys: $keys },
|
||||||
router: {
|
router: {
|
||||||
wan: ({ interface: $wan, ipv4: { type: $type } }
|
wan: ({ interface: $wan, ipv4: { type: $type } }
|
||||||
@@ -204,6 +344,7 @@ let
|
|||||||
fi
|
fi
|
||||||
printf '%s\n' "$webui_password" | router-webui passwd "$work/secrets/webui-password"
|
printf '%s\n' "$webui_password" | router-webui passwd "$work/secrets/webui-password"
|
||||||
(umask 077; printf '%s\n' "$webui_password" | mkpasswd -s > "$work/secrets/root-password")
|
(umask 077; printf '%s\n' "$webui_password" | mkpasswd -s > "$work/secrets/root-password")
|
||||||
|
fi # new router
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo ">> building the router system (no internet needed; takes a few minutes)"
|
echo ">> building the router system (no internet needed; takes a few minutes)"
|
||||||
@@ -211,7 +352,7 @@ let
|
|||||||
"path:$config#nixosConfigurations.router.config.system.build.toplevel")
|
"path:$config#nixosConfigurations.router.config.system.build.toplevel")
|
||||||
router-install --yes --secrets "$work/secrets" --system "$system" "$disk"
|
router-install --yes --secrets "$work/secrets" --system "$system" "$disk"
|
||||||
echo
|
echo
|
||||||
echo "After rebooting, open https://$lan_address from the LAN."
|
echo "After rebooting, open https://''${lan_address:-the LAN address} from the LAN."
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
@@ -230,6 +371,16 @@ in
|
|||||||
is what lets the installer build the chosen configuration offline.
|
is what lets the installer build the chosen configuration offline.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
release = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.package;
|
||||||
|
default = null;
|
||||||
|
description = "The release (lib.mkRelease) these installs run; its migrations update reused settings.";
|
||||||
|
};
|
||||||
|
schemaVersion = lib.mkOption {
|
||||||
|
type = lib.types.ints.unsigned;
|
||||||
|
default = 0;
|
||||||
|
description = "The settings schema of the nixos-router these installs run (settings.json's \"version\").";
|
||||||
|
};
|
||||||
stateVersion = lib.mkOption {
|
stateVersion = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
default = config.system.nixos.release;
|
default = config.system.nixos.release;
|
||||||
|
|||||||
+79
-1
@@ -2,6 +2,12 @@
|
|||||||
# LAN address, web UI password), builds the router offline and installs it.
|
# LAN address, web UI password), builds the router offline and installs it.
|
||||||
# The installed box must come up with those settings, serve the web UI with
|
# The installed box must come up with those settings, serve the web UI with
|
||||||
# that password, and rebuild to exactly itself.
|
# that password, and rebuild to exactly itself.
|
||||||
|
#
|
||||||
|
# Then the same box is reinstalled twice, everything carrying over (settings
|
||||||
|
# with an unapplied change, passwords, the web UI's certificate): once from
|
||||||
|
# the installed disk itself, once from a web UI backup on a USB stick after
|
||||||
|
# the disk was wiped. The settings name an interface this machine lacks, so
|
||||||
|
# setup asks which one takes its place.
|
||||||
{ self, pkgs }:
|
{ self, pkgs }:
|
||||||
let
|
let
|
||||||
common = import ./common.nix;
|
common = import ./common.nix;
|
||||||
@@ -35,6 +41,15 @@ let
|
|||||||
|
|
||||||
/dev/vda
|
/dev/vda
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
# Reinstall the configuration found first, on /dev/vda; eth1 replaces the
|
||||||
|
# missing enp9s0.
|
||||||
|
reuseAnswers = pkgs.writeText "reuse-answers" ''
|
||||||
|
1
|
||||||
|
/dev/vda
|
||||||
|
eth1
|
||||||
|
/dev/vda
|
||||||
|
'';
|
||||||
in
|
in
|
||||||
pkgs.testers.runNixOSTest {
|
pkgs.testers.runNixOSTest {
|
||||||
name = "router-setup";
|
name = "router-setup";
|
||||||
@@ -48,6 +63,7 @@ pkgs.testers.runNixOSTest {
|
|||||||
(self.lib.setupModule { inherit local; })
|
(self.lib.setupModule { inherit local; })
|
||||||
common.tools
|
common.tools
|
||||||
];
|
];
|
||||||
|
environment.systemPackages = [ pkgs.dosfstools ];
|
||||||
environment.etc."router-test-local.nix".source = local;
|
environment.etc."router-test-local.nix".source = local;
|
||||||
# The interfaces router-setup offers.
|
# The interfaces router-setup offers.
|
||||||
virtualisation.vlans = [
|
virtualisation.vlans = [
|
||||||
@@ -55,8 +71,15 @@ pkgs.testers.runNixOSTest {
|
|||||||
2
|
2
|
||||||
];
|
];
|
||||||
virtualisation.fileSystems."/".autoFormat = true;
|
virtualisation.fileSystems."/".autoFormat = true;
|
||||||
virtualisation.emptyDiskImages = [ 1024 ];
|
# vdb: the installer's root; vdc: a USB stick for a backup.
|
||||||
|
virtualisation.emptyDiskImages = [
|
||||||
|
1024
|
||||||
|
64
|
||||||
|
];
|
||||||
virtualisation.rootDevice = "/dev/vdb";
|
virtualisation.rootDevice = "/dev/vdb";
|
||||||
|
# The installer boots several times on the same root disk, whose Nix
|
||||||
|
# database would list store paths left behind in a tmpfs overlay.
|
||||||
|
virtualisation.writableStoreUseTmpfs = false;
|
||||||
nix.settings.substituters = lib.mkForce [ ];
|
nix.settings.substituters = lib.mkForce [ ];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -118,5 +141,60 @@ pkgs.testers.runNixOSTest {
|
|||||||
with subtest("the box rebuilds to exactly the installed system"):
|
with subtest("the box rebuilds to exactly the installed system"):
|
||||||
out = target.succeed("router-apply 2>&1")
|
out = target.succeed("router-apply 2>&1")
|
||||||
assert "no changes" in out, out
|
assert "no changes" in out, out
|
||||||
|
|
||||||
|
def install_again(prepare=None):
|
||||||
|
installer.start()
|
||||||
|
installer.wait_for_unit("multi-user.target")
|
||||||
|
if prepare:
|
||||||
|
prepare()
|
||||||
|
out = installer.succeed("router-setup --local /etc/router-test-local.nix < ${reuseAnswers} 2>&1")
|
||||||
|
installer.shutdown()
|
||||||
|
target.start()
|
||||||
|
target.wait_for_unit("multi-user.target")
|
||||||
|
return out
|
||||||
|
|
||||||
|
def check_carried_over():
|
||||||
|
assert target.succeed("hostname").strip() == "gw2"
|
||||||
|
settings = target.succeed("cat /var/lib/router/config/settings.json")
|
||||||
|
assert '"enp9s0"' not in settings and '"eth1"' in settings, settings
|
||||||
|
assert target.succeed("sha256sum /var/lib/router/webui/cert.pem").split()[0] == cert
|
||||||
|
target.wait_for_unit("router-webui.service")
|
||||||
|
target.wait_until_succeeds("curl -sk -o /dev/null https://127.0.0.1/login")
|
||||||
|
code = target.succeed("curl -sk -o /dev/null -w '%{http_code}' -d password=hunter2hunter2 https://127.0.0.1/login")
|
||||||
|
assert code == "303", code
|
||||||
|
assert "no changes" in target.succeed("router-apply 2>&1")
|
||||||
|
|
||||||
|
with subtest("reinstalling from the installed disk carries everything over"):
|
||||||
|
# Saved but not applied; and an interface this machine does not have.
|
||||||
|
target.succeed("sed -i 's/\"hostName\": \"gw\"/\"hostName\": \"gw2\"/; s/\"interface\": \"eth1\"/\"interface\": \"enp9s0\"/'"
|
||||||
|
" /var/lib/router/config/settings.json")
|
||||||
|
cert = target.succeed("sha256sum /var/lib/router/webui/cert.pem").split()[0]
|
||||||
|
# A backup from the web UI, for the next subtest.
|
||||||
|
target.succeed("curl -sk -c /tmp/jar -o /dev/null -d password=hunter2hunter2 https://127.0.0.1/login")
|
||||||
|
token = target.succeed("curl -sk -b /tmp/jar https://127.0.0.1/advanced | grep -o 'name=\"csrf\" value=\"[^\"]*' | head -1 | cut -d'\"' -f4").strip()
|
||||||
|
target.succeed(f"curl -sfk -b /tmp/jar -d csrf={token} -o /tmp/router-backup-gw2.tar.gz https://127.0.0.1/backup")
|
||||||
|
assert "router/config/settings.json" in target.succeed("tar -tzf /tmp/router-backup-gw2.tar.gz").split()
|
||||||
|
# The installed system has no shared folder with the host: pass it as text.
|
||||||
|
backup = target.succeed("base64 -w0 /tmp/router-backup-gw2.tar.gz").strip()
|
||||||
|
target.shutdown()
|
||||||
|
|
||||||
|
out = install_again()
|
||||||
|
assert "installed on /dev/vda2: gw2" in out, out
|
||||||
|
assert "no interface enp9s0 (WAN)" in out, out
|
||||||
|
check_carried_over()
|
||||||
|
|
||||||
|
with subtest("a backup on a USB stick is found after the disk was wiped"):
|
||||||
|
target.shutdown()
|
||||||
|
|
||||||
|
def wipe_and_plug_in_stick():
|
||||||
|
installer.succeed("wipefs -a /dev/vda2 /dev/vda1 /dev/vda && blockdev --rereadpt /dev/vda && udevadm settle")
|
||||||
|
installer.succeed("mkfs.vfat /dev/vdc && udevadm settle && mount /dev/vdc /mnt")
|
||||||
|
installer.succeed(f"echo {backup} | base64 -d > /mnt/router-backup-gw2.tar.gz")
|
||||||
|
installer.succeed("umount /mnt")
|
||||||
|
|
||||||
|
out = install_again(wipe_and_plug_in_stick)
|
||||||
|
assert "backup router-backup-gw2.tar.gz on /dev/vdc: gw2" in out, out
|
||||||
|
assert "installed on" not in out, out
|
||||||
|
check_carried_over()
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user