pmb/netboot: new feature (MR 2064)
pmbootstrap netboot command exposes the generated vendor-codename.img rootfs through nbd interface so that device can mount it and boot postmarketOS without having any storage medium at all. Co-authored-by: Luca Weiss <luca@z3ntu.xyz>
This commit is contained in:
parent
997e3fb1f5
commit
47539f1bef
|
@ -15,7 +15,7 @@ import pmb.parse.apkindex
|
||||||
|
|
||||||
def zap(args, confirm=True, dry=False, pkgs_local=False, http=False,
|
def zap(args, confirm=True, dry=False, pkgs_local=False, http=False,
|
||||||
pkgs_local_mismatch=False, pkgs_online_mismatch=False, distfiles=False,
|
pkgs_local_mismatch=False, pkgs_online_mismatch=False, distfiles=False,
|
||||||
rust=False):
|
rust=False, netboot=False):
|
||||||
"""
|
"""
|
||||||
Shutdown everything inside the chroots (e.g. distccd, adb), umount
|
Shutdown everything inside the chroots (e.g. distccd, adb), umount
|
||||||
everything and then safely remove folders from the work-directory.
|
everything and then safely remove folders from the work-directory.
|
||||||
|
@ -29,6 +29,7 @@ def zap(args, confirm=True, dry=False, pkgs_local=False, http=False,
|
||||||
downloaded from mirrors (e.g. from Alpine)
|
downloaded from mirrors (e.g. from Alpine)
|
||||||
:param distfiles: Clear the downloaded files cache
|
:param distfiles: Clear the downloaded files cache
|
||||||
:param rust: Remove rust related caches
|
:param rust: Remove rust related caches
|
||||||
|
:param netboot: Remove images for netboot
|
||||||
|
|
||||||
NOTE: This function gets called in pmb/config/init.py, with only args.work
|
NOTE: This function gets called in pmb/config/init.py, with only args.work
|
||||||
and args.device set!
|
and args.device set!
|
||||||
|
@ -65,6 +66,8 @@ def zap(args, confirm=True, dry=False, pkgs_local=False, http=False,
|
||||||
patterns += ["cache_distfiles"]
|
patterns += ["cache_distfiles"]
|
||||||
if rust:
|
if rust:
|
||||||
patterns += ["cache_rust"]
|
patterns += ["cache_rust"]
|
||||||
|
if netboot:
|
||||||
|
patterns += ["images_netboot"]
|
||||||
|
|
||||||
# Delete everything matching the patterns
|
# Delete everything matching the patterns
|
||||||
for pattern in patterns:
|
for pattern in patterns:
|
||||||
|
|
|
@ -210,6 +210,7 @@ chroot_mount_bind = {
|
||||||
"$WORK/cache_rust": "/mnt/pmbootstrap-rust",
|
"$WORK/cache_rust": "/mnt/pmbootstrap-rust",
|
||||||
"$WORK/config_abuild": "/mnt/pmbootstrap-abuild-config",
|
"$WORK/config_abuild": "/mnt/pmbootstrap-abuild-config",
|
||||||
"$WORK/config_apk_keys": "/etc/apk/keys",
|
"$WORK/config_apk_keys": "/etc/apk/keys",
|
||||||
|
"$WORK/images_netboot": "/mnt/pmbootstrap-netboot",
|
||||||
"$WORK/packages/$CHANNEL": "/mnt/pmbootstrap-packages",
|
"$WORK/packages/$CHANNEL": "/mnt/pmbootstrap-packages",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ import pmb.helpers.aportupgrade
|
||||||
import pmb.helpers.status
|
import pmb.helpers.status
|
||||||
import pmb.install
|
import pmb.install
|
||||||
import pmb.install.blockdevice
|
import pmb.install.blockdevice
|
||||||
|
import pmb.netboot
|
||||||
import pmb.parse
|
import pmb.parse
|
||||||
import pmb.qemu
|
import pmb.qemu
|
||||||
|
|
||||||
|
@ -140,6 +141,11 @@ def sideload(args):
|
||||||
args.packages)
|
args.packages)
|
||||||
|
|
||||||
|
|
||||||
|
def netboot(args):
|
||||||
|
if args.action_netboot == "serve":
|
||||||
|
pmb.netboot.start_nbd_server(args)
|
||||||
|
|
||||||
|
|
||||||
def chroot(args):
|
def chroot(args):
|
||||||
# Suffix
|
# Suffix
|
||||||
suffix = _parse_suffix(args)
|
suffix = _parse_suffix(args)
|
||||||
|
@ -543,7 +549,7 @@ def zap(args):
|
||||||
distfiles=args.distfiles, pkgs_local=args.pkgs_local,
|
distfiles=args.distfiles, pkgs_local=args.pkgs_local,
|
||||||
pkgs_local_mismatch=args.pkgs_local_mismatch,
|
pkgs_local_mismatch=args.pkgs_local_mismatch,
|
||||||
pkgs_online_mismatch=args.pkgs_online_mismatch,
|
pkgs_online_mismatch=args.pkgs_online_mismatch,
|
||||||
rust=args.rust)
|
rust=args.rust, netboot=args.netboot)
|
||||||
|
|
||||||
# Don't write the "Done" message
|
# Don't write the "Done" message
|
||||||
pmb.helpers.logging.disable()
|
pmb.helpers.logging.disable()
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
# Copyright 2022 Mark Hargreaves, Luca Weiss
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import time
|
||||||
|
|
||||||
|
import pmb.chroot.root
|
||||||
|
import pmb.helpers.run
|
||||||
|
|
||||||
|
|
||||||
|
def start_nbd_server(args, ip="172.16.42.2", port=9999):
|
||||||
|
"""
|
||||||
|
Start nbd server in chroot_native with pmOS rootfs.
|
||||||
|
:param ip: IP address to serve nbd server for
|
||||||
|
:param port: port of nbd server
|
||||||
|
"""
|
||||||
|
|
||||||
|
pmb.chroot.apk.install(args, ['nbd'])
|
||||||
|
|
||||||
|
chroot = f"{args.work}/chroot_native"
|
||||||
|
|
||||||
|
rootfs_path = f"/mnt/pmbootstrap-netboot/{args.device}.img"
|
||||||
|
if not os.path.exists(chroot + rootfs_path) or args.replace:
|
||||||
|
rootfs_path2 = f"/home/pmos/rootfs/{args.device}.img"
|
||||||
|
if not os.path.exists(chroot + rootfs_path2):
|
||||||
|
raise RuntimeError("The rootfs has not been generated yet, please "
|
||||||
|
"run 'pmbootstrap install' first.")
|
||||||
|
if args.replace and not \
|
||||||
|
pmb.helpers.cli.confirm(args, f"Are you sure you want to "
|
||||||
|
f"replace the rootfs for "
|
||||||
|
f"{args.device}?"):
|
||||||
|
return
|
||||||
|
pmb.chroot.root(args, ["cp", rootfs_path2, rootfs_path])
|
||||||
|
logging.info(f"NOTE: Copied device image to {args.work}"
|
||||||
|
f"/images_netboot/. The image will persist \"pmbootstrap "
|
||||||
|
f"zap\" for your convenience. Use \"pmbootstrap netboot "
|
||||||
|
f"serve --help\" for more options.")
|
||||||
|
|
||||||
|
logging.info(f"Running nbd server for {args.device} on {ip} port {port}.")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
logging.info("Waiting for postmarketOS device to appear...")
|
||||||
|
|
||||||
|
# Try to bind to the IP ourselves before handing it to nbd-servere
|
||||||
|
# This is purely to improve the UX as nbd-server just quits when it
|
||||||
|
# cannot bind to an IP address.
|
||||||
|
test_socket = socket.socket()
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
test_socket.bind((ip, 9998))
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno != 99: # Cannot assign requested address
|
||||||
|
raise e
|
||||||
|
# Wait a bit before retrying
|
||||||
|
time.sleep(0.5)
|
||||||
|
continue
|
||||||
|
test_socket.close()
|
||||||
|
break
|
||||||
|
|
||||||
|
logging.info("Found postmarketOS device, serving image...")
|
||||||
|
pmb.chroot.root(
|
||||||
|
args, ["nbd-server", f"{ip}@{port}", rootfs_path, "-d"],
|
||||||
|
check=False, disable_timeout=True)
|
||||||
|
logging.info("nbd-server quit. Connection lost?")
|
||||||
|
# On a reboot nbd-server will quit, but the IP address sticks around
|
||||||
|
# for a bit longer, so wait.
|
||||||
|
time.sleep(5)
|
|
@ -508,6 +508,19 @@ def arguments_status(subparser):
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def arguments_netboot(subparser):
|
||||||
|
ret = subparser.add_parser("netboot",
|
||||||
|
help="launch nbd server with pmOS rootfs")
|
||||||
|
sub = ret.add_subparsers(dest="action_netboot")
|
||||||
|
sub.required = True
|
||||||
|
|
||||||
|
start = sub.add_parser("serve", help="start nbd server")
|
||||||
|
start.add_argument("--replace", action="store_true",
|
||||||
|
help="replace stored netboot image")
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def package_completer(prefix, action, parser=None, parsed_args=None):
|
def package_completer(prefix, action, parser=None, parsed_args=None):
|
||||||
args = parsed_args
|
args = parsed_args
|
||||||
pmb.config.merge_with_args(args)
|
pmb.config.merge_with_args(args)
|
||||||
|
@ -646,6 +659,7 @@ def arguments():
|
||||||
arguments_kconfig(sub)
|
arguments_kconfig(sub)
|
||||||
arguments_export(sub)
|
arguments_export(sub)
|
||||||
arguments_sideload(sub)
|
arguments_sideload(sub)
|
||||||
|
arguments_netboot(sub)
|
||||||
arguments_flasher(sub)
|
arguments_flasher(sub)
|
||||||
arguments_initfs(sub)
|
arguments_initfs(sub)
|
||||||
arguments_qemu(sub)
|
arguments_qemu(sub)
|
||||||
|
@ -682,6 +696,8 @@ def arguments():
|
||||||
dest="pkgs_local_mismatch",
|
dest="pkgs_local_mismatch",
|
||||||
help="also delete locally compiled packages without"
|
help="also delete locally compiled packages without"
|
||||||
" existing aport of same version")
|
" existing aport of same version")
|
||||||
|
zap.add_argument("-n", "--netboot", action="store_true",
|
||||||
|
help="also delete stored images for netboot")
|
||||||
zap.add_argument("-o", "--pkgs-online-mismatch", action="store_true",
|
zap.add_argument("-o", "--pkgs-online-mismatch", action="store_true",
|
||||||
dest="pkgs_online_mismatch",
|
dest="pkgs_online_mismatch",
|
||||||
help="also delete outdated packages from online mirrors"
|
help="also delete outdated packages from online mirrors"
|
||||||
|
|
Loading…
Reference in New Issue