Compare commits

..

3 commits

Author SHA1 Message Date
Jörg Thalheim
65c6e9d432 disable risv64 on unstable for now 2024-06-27 08:35:30 +02:00
Jörg Thalheim
7582b40ce3 wip: upstream cross fix 2024-06-27 08:32:42 +02:00
Jörg Thalheim
2d72a425a2 add riscv64 kexec image 2024-06-27 08:32:24 +02:00
21 changed files with 241 additions and 524 deletions

View file

@ -19,7 +19,7 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@V27
with: with:
nix_path: nixpkgs=https://github.com/NixOS/nixpkgs/archive/nixpkgs-unstable.tar.gz nix_path: nixpkgs=https://github.com/NixOS/nixpkgs/archive/nixpkgs-unstable.tar.gz
- name: Build image - name: Build image

View file

@ -11,9 +11,9 @@ jobs:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Nix - name: Install Nix
uses: cachix/install-nix-action@v30 uses: cachix/install-nix-action@V27
- name: Update flake.lock - name: Update flake.lock
uses: DeterminateSystems/update-flake-lock@v24 uses: DeterminateSystems/update-flake-lock@v22
with: with:
pr-labels: | pr-labels: |
merge-queue merge-queue

View file

@ -1,7 +1,7 @@
queue_rules: queue_rules:
- name: default - name: default
merge_conditions: merge_conditions:
- check-success=buildbot/nix-build - check-success=buildbot/nix-eval
defaults: defaults:
actions: actions:
queue: queue:

View file

@ -11,10 +11,10 @@ Kexec is a mechanism in Linux to load a new kernel from a running Linux to
replace the current kernel. This is useful for booting the Nixos installer from replace the current kernel. This is useful for booting the Nixos installer from
existing Linux distributions, such as server provider that do not offer a NixOS existing Linux distributions, such as server provider that do not offer a NixOS
option. After running kexec, the NixOS installer exists only in memory. At the option. After running kexec, the NixOS installer exists only in memory. At the
time of writing, this requires secure boot off in BIOS settings and at least 1GB time of writing, this requires at least 1GB of physical RAM (swap does not
of physical RAM (swap does not count) in the system. If not enough RAM is available, count) in the system. If not enough RAM is available, the initrd cannot be
the initrd cannot be loaded. Because the NixOS runs only in RAM, users can reformat loaded. Because the NixOS runs only in RAM, users can reformat all the system's
all the system's discs to prepare for a new NixOS installation. discs to prepare for a new NixOS installation.
It can be booted as follows by running these commands as root: It can be booted as follows by running these commands as root:
@ -36,7 +36,7 @@ integration into automated nixos installation scripts, since you can cleanly
disconnect from the running machine before the kexec takes place. The tarball disconnect from the running machine before the kexec takes place. The tarball
is also designed to be run from NixOS, which can be useful for new installations is also designed to be run from NixOS, which can be useful for new installations
## Iso installer images ## ISO installer images
This image allows to boot a NixOS installer off a USB-Stick. This image allows to boot a NixOS installer off a USB-Stick.
This installer has been optimized for remote installation i.e. This installer has been optimized for remote installation i.e.

View file

@ -2,11 +2,11 @@
"nodes": { "nodes": {
"nixos-stable": { "nixos-stable": {
"locked": { "locked": {
"lastModified": 1729181673, "lastModified": 1719253556,
"narHash": "sha256-LDiPhQ3l+fBjRATNtnuDZsBS7hqoBtPkKBkhpoBHv3I=", "narHash": "sha256-A/76RFUVxZ/7Y8+OMVL1Lc8LRhBxZ8ZE2bpMnvZ1VpY=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "4eb33fe664af7b41a4c446f87d20c9a0a6321fa3", "rev": "fc07dc3bdf2956ddd64f24612ea7fc894933eb2e",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -18,16 +18,16 @@
}, },
"nixos-unstable": { "nixos-unstable": {
"locked": { "locked": {
"lastModified": 1729450260, "lastModified": 1719456624,
"narHash": "sha256-3GNZr0V4b19RZ5mlyiY/4F8N2pzitvjDU6aHMWjAqLI=", "narHash": "sha256-j0iox16+eMO2ptZ/IfM57R/1OpnPu0H03wXwggmLBfg=",
"owner": "NixOS", "owner": "jmbaur",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "e3f55158e7587c5a5fdb0e86eb7ca4f455f0928f", "rev": "560cd874a37396968aea0bd9abc54fb1122424b7",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "jmbaur",
"ref": "nixos-unstable-small", "ref": "etc-overlay-cross",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }

130
flake.nix
View file

@ -1,53 +1,90 @@
{ {
description = "NixOS images"; description = "NixOS images";
inputs.nixos-unstable.url = "github:NixOS/nixpkgs/nixos-unstable-small"; #inputs.nixos-unstable.url = "github:NixOS/nixpkgs/nixos-unstable-small";
inputs.nixos-unstable.url = "github:jmbaur/nixpkgs/etc-overlay-cross";
inputs.nixos-stable.url = "github:NixOS/nixpkgs/nixos-24.05"; inputs.nixos-stable.url = "github:NixOS/nixpkgs/nixos-24.05";
nixConfig.extra-substituters = [ "https://nix-community.cachix.org" ]; nixConfig.extra-substituters = [ "https://nix-community.cachix.org" ];
nixConfig.extra-trusted-public-keys = [ "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" ]; nixConfig.extra-trusted-public-keys = [
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
];
outputs = { self, nixos-unstable, nixos-stable }: outputs =
let
supportedSystems = [ "riscv64-linux" ];
forAllSystems = nixos-unstable.lib.genAttrs supportedSystems;
in
{ {
hydraJobs = { inherit (self) checks; }; self,
packages = forAllSystems (system: nixos-unstable,
nixos-stable,
}:
let
supportedSystems = [
"aarch64-linux"
"x86_64-linux"
];
forAllSystems = nixos-unstable.lib.genAttrs supportedSystems;
packages = forAllSystems (
system:
let let
netboot = nixpkgs: (import (nixpkgs + "/nixos/release.nix") { }).netboot.${system}; netboot = nixpkgs: (import (nixpkgs + "/nixos/release.nix") { }).netboot.${system};
kexec-installer = nixpkgs: module: (nixpkgs.legacyPackages.${system}.nixos [ module self.nixosModules.kexec-installer ]).config.system.build.kexecTarball; kexec-installer =
netboot-installer = nixpkgs: (nixpkgs.legacyPackages.${system}.nixos [ self.nixosModules.netboot-installer ]).config.system.build.netboot; nixpkgs: modules:
image-installer = nixpkgs: (nixpkgs.legacyPackages.${system}.nixos [ self.nixosModules.image-installer ]).config.system.build.isoImage; (nixpkgs.legacyPackages.${system}.nixos (modules ++ [ self.nixosModules.kexec-installer ]))
.config.system.build.kexecTarball;
netboot-installer =
nixpkgs:
(nixpkgs.legacyPackages.${system}.nixos [ self.nixosModules.netboot-installer ])
.config.system.build.netboot;
image-installer =
nixpkgs:
(nixpkgs.legacyPackages.${system}.nixos [ self.nixosModules.image-installer ])
.config.system.build.isoImage;
in in
{ {
netboot-nixos-unstable = netboot nixos-unstable; netboot-nixos-unstable = netboot nixos-unstable;
netboot-nixos-stable = netboot nixos-stable; netboot-nixos-stable = netboot nixos-stable;
kexec-installer-nixos-unstable = kexec-installer nixos-unstable {}; kexec-installer-nixos-unstable = kexec-installer nixos-unstable [ ];
kexec-installer-nixos-stable = kexec-installer nixos-stable {}; kexec-installer-nixos-stable = kexec-installer nixos-stable [ ];
image-installer-nixos-unstable = image-installer nixos-unstable; image-installer-nixos-unstable = image-installer nixos-unstable;
image-installer-nixos-stable = image-installer nixos-stable; image-installer-nixos-stable = image-installer nixos-stable;
kexec-installer-nixos-unstable-noninteractive = kexec-installer nixos-unstable { kexec-installer-nixos-unstable-noninteractive = kexec-installer nixos-unstable [
_file = __curPos.file; { system.kexec-installer.name = "nixos-kexec-installer-noninteractive"; }
system.kexec-installer.name = "nixos-kexec-installer-noninteractive"; self.nixosModules.noninteractive
imports = [ ];
self.nixosModules.noninteractive kexec-installer-nixos-stable-noninteractive = kexec-installer nixos-stable [
]; { system.kexec-installer.name = "nixos-kexec-installer-noninteractive"; }
}; self.nixosModules.noninteractive
kexec-installer-nixos-stable-noninteractive = kexec-installer nixos-stable { ];
_file = __curPos.file;
system.kexec-installer.name = "nixos-kexec-installer-noninteractive";
imports = [
self.nixosModules.noninteractive
];
};
netboot-installer-nixos-unstable = netboot-installer nixos-unstable; netboot-installer-nixos-unstable = netboot-installer nixos-unstable;
netboot-installer-nixos-stable = netboot-installer nixos-stable; netboot-installer-nixos-stable = netboot-installer nixos-stable;
}); }
);
crossPackages = {
x86_64-linux =
let
kexec-installer =
nixpkgs: modules:
(nixpkgs.legacyPackages.x86_64-linux.pkgsCross.riscv64.nixos (
modules
++ [
self.nixosModules.kexec-installer
self.nixosModules.noninteractive
]
)).config.system.build.kexecTarball;
in
{
kexec-installer-nixos-unstable-noninteractive-riscv64 = kexec-installer nixos-unstable [ ];
# needs a backport to nixpkgs-24.05
#kexec-installer-nixos-stable-noninteractive-riscv64 = kexec-installer nixos-stable [ ];
};
};
in
{
packages = nixos-unstable.lib.recursiveUpdate packages crossPackages;
nixosModules = { nixosModules = {
kexec-installer = ./nix/kexec-installer/module.nix; kexec-installer = ./nix/kexec-installer/module.nix;
noninteractive = ./nix/noninteractive.nix; noninteractive = ./nix/noninteractive.nix;
@ -58,28 +95,29 @@
checks = checks =
let let
# re-export the packages as checks # re-export the packages as checks
packages = forAllSystems (system: nixos-unstable.lib.mapAttrs' (n: nixos-unstable.lib.nameValuePair "package-${n}") self.packages.${system}); packages = forAllSystems (
system:
nixos-unstable.lib.mapAttrs' (
n: nixos-unstable.lib.nameValuePair "package-${n}"
) self.packages.${system}
);
checks = checks =
let let
pkgsUnstable = nixos-unstable.legacyPackages.riscv64-linux; pkgs = nixos-unstable.legacyPackages.x86_64-linux;
pkgsStable = nixos-stable.legacyPackages.riscv64-linux;
bootTests = pkgs: channel: suffix: pkgs.lib.mapAttrs' (name: pkgs.lib.nameValuePair "${name}${suffix}") (pkgs.callPackages ./nix/image-installer/tests.nix {
nixpkgs = channel;
nixosModules = self.nixosModules;
});
in in
{ {
kexec-installer-unstable = pkgsUnstable.callPackage ./nix/kexec-installer/test.nix { kexec-installer-unstable = pkgs.callPackage ./nix/kexec-installer/test.nix {
kexecTarball = self.packages.riscv64-linux.kexec-installer-nixos-unstable-noninteractive; kexecTarball = self.packages.x86_64-linux.kexec-installer-nixos-unstable-noninteractive;
}; };
shellcheck = pkgs.runCommand "shellcheck" { nativeBuildInputs = [ pkgs.shellcheck ]; } ''
kexec-installer-stable = nixos-stable.legacyPackages.riscv64-linux.callPackage ./nix/kexec-installer/test.nix { shellcheck ${(pkgs.nixos [ self.nixosModules.kexec-installer ]).config.system.build.kexecRun}
kexecTarball = self.packages.riscv64-linux.kexec-installer-nixos-stable-noninteractive; touch $out
}; '';
} // (bootTests pkgsUnstable nixos-unstable "-nixos-unstable") kexec-installer-stable =
// (bootTests pkgsStable nixos-stable "-nixos-stable"); nixos-stable.legacyPackages.x86_64-linux.callPackage ./nix/kexec-installer/test.nix
{ kexecTarball = self.packages.x86_64-linux.kexec-installer-nixos-stable-noninteractive; };
};
in in
nixos-unstable.lib.recursiveUpdate packages { riscv64-linux = checks; }; nixos-unstable.lib.recursiveUpdate packages { x86_64-linux = checks; };
}; };
} }

5
garnix.yaml Normal file
View file

@ -0,0 +1,5 @@
builds:
include:
- '*.x86_64-linux.*'
- '*.aarch64-linux.*'
- nixosConfigurations.*

View file

@ -40,9 +40,8 @@ in
imports = [ imports = [
(modulesPath + "/installer/cd-dvd/installation-cd-base.nix") (modulesPath + "/installer/cd-dvd/installation-cd-base.nix")
../installer.nix ../installer.nix
../noveau-workaround.nix
./hidden-ssh-announcement.nix
./wifi.nix ./wifi.nix
./hidden-ssh-announcement.nix
]; ];
systemd.tmpfiles.rules = [ "d /var/shared 0777 root root - -" ]; systemd.tmpfiles.rules = [ "d /var/shared 0777 root root - -" ];
services.openssh.settings.PermitRootLogin = "yes"; services.openssh.settings.PermitRootLogin = "yes";

View file

@ -1,105 +0,0 @@
{
pkgs,
lib,
nixpkgs,
nixos,
nixosModules,
}:
let
testConfig = (
nixos [
(
{ modulesPath, ... }:
{
imports = [
nixosModules.image-installer
"${modulesPath}/testing/test-instrumentation.nix"
];
}
)
]
);
iso = testConfig.config.system.build.isoImage;
mkStartCommand =
{
memory ? 2048,
cdrom ? null,
usb ? null,
uefi ? false,
extraFlags ? [ ],
}:
let
qemu-common = import (nixpkgs + "/nixos/lib/qemu-common.nix") { inherit lib pkgs; };
qemu = qemu-common.qemuBinary pkgs.qemu_test;
flags =
[
"-m"
(toString memory)
"-netdev"
"user,id=net0"
"-device"
"virtio-net-pci,netdev=net0"
]
++ lib.optionals (cdrom != null) [
"-cdrom"
cdrom
]
++ lib.optionals (usb != null) [
"-device"
"usb-ehci"
"-drive"
"id=usbdisk,file=${usb},if=none,readonly"
"-device"
"usb-storage,drive=usbdisk"
]
++ lib.optionals uefi [
"-drive"
"if=pflash,format=raw,unit=0,readonly=on,file=${pkgs.OVMF.firmware}"
"-drive"
"if=pflash,format=raw,unit=1,readonly=on,file=${pkgs.OVMF.variables}"
]
++ extraFlags;
flagsStr = lib.concatStringsSep " " flags;
in
"${qemu} ${flagsStr}";
makeBootTest =
name: config:
let
startCommand = mkStartCommand config;
in
pkgs.testers.runNixOSTest {
name = "boot-${name}";
nodes = { };
testScript = ''
machine = create_machine("${startCommand}")
machine.start()
machine.wait_for_unit("multi-user.target")
machine.succeed("nix store verify --no-trust -r --option experimental-features nix-command /run/current-system")
machine.shutdown()
'';
};
in
{
uefi-cdrom = makeBootTest "uefi-cdrom" {
uefi = true;
cdrom = "${iso}/iso/nixos-installer-${pkgs.hostPlatform.system}.iso";
};
uefi-usb = makeBootTest "uefi-usb" {
uefi = true;
usb = "${iso}/iso/nixos-installer-${pkgs.hostPlatform.system}.iso";
};
bios-cdrom = makeBootTest "bios-cdrom" {
cdrom = "${iso}/iso/nixos-installer-${pkgs.hostPlatform.system}.iso";
};
bios-usb = makeBootTest "bios-usb" {
usb = "${iso}/iso/nixos-installer-${pkgs.hostPlatform.system}.iso";
};
}

View file

@ -2,42 +2,39 @@
config, config,
lib, lib,
pkgs, pkgs,
modulesPath,
... ...
}: }:
{ {
imports = [
# ./latest-zfs-kernel.nix
./nix-settings.nix
];
# more descriptive hostname than just "nixos" # more descriptive hostname than just "nixos"
networking.hostName = lib.mkDefault "nixos-installer"; networking.hostName = lib.mkDefault "nixos-installer";
# We are stateless, so just default to latest. # We are stateless, so just default to latest.
system.stateVersion = config.system.nixos.version; system.stateVersion = config.system.nixos.version;
# Enable bcachefs support
boot.supportedFilesystems.bcachefs = lib.mkDefault true;
# use latest kernel we can support to get more hardware support # use latest kernel we can support to get more hardware support
# boot.zfs.package = pkgs.zfsUnstable; boot.kernelPackages =
lib.mkDefault
(pkgs.zfs.override { removeLinuxDRM = pkgs.hostPlatform.isAarch64; }).latestCompatibleLinuxPackages;
boot.zfs.removeLinuxDRM = lib.mkDefault pkgs.hostPlatform.isAarch64;
documentation.enable = false; documentation.enable = false;
documentation.man.man-db.enable = false; documentation.man.man-db.enable = false;
# make it easier to debug boot failures
boot.initrd.systemd.emergencyAccess = true;
environment.systemPackages = [ environment.systemPackages = [
pkgs.nixos-install-tools pkgs.nixos-install-tools
# for zapping of disko # for zapping of disko
pkgs.jq pkgs.jq
# for copying extra files of nixos-anywhere # for copying extra files of nixos-anywhere
pkgs.rsync pkgs.rsync
# alternative to nixos-generate-config ];
# TODO: use nixpkgs again after next nixos release
(pkgs.callPackage ./nixos-facter.nix {})
pkgs.disko imports = [
./nix-settings.nix
# reduce closure size by removing perl
"${modulesPath}/profiles/perlless.nix"
# FIXME: we still are left with nixos-generate-config due to nixos-install-tools
{ system.forbiddenDependenciesRegexes = lib.mkForce []; }
]; ];
# Don't add nixpkgs to the image to save space, for our intended use case we don't need it # Don't add nixpkgs to the image to save space, for our intended use case we don't need it

View file

@ -1,26 +0,0 @@
#!/usr/bin/env -S nix shell --inputs-from .# nixos-unstable#bash nixos-unstable#iproute2 nixos-unstable#findutils nixos-unstable#coreutils nixos-unstable#python3 nixos-unstable#jq --command bash
set -eu
SCRIPT_DIR=$(dirname "$(readlink -f "$0")")
# This script can be used to see what network configuration would be restored by the restore_routes.py script for the current system.
tmp=$(mktemp -d)
trap "rm -rf $tmp" EXIT
ip --json address >"$tmp/addrs.json"
ip -6 --json route >"$tmp/routes-v6.json"
ip -4 --json route >"$tmp/routes-v4.json"
python3 "$SCRIPT_DIR/restore_routes.py" "$tmp/addrs.json" "$tmp/routes-v4.json" "$tmp/routes-v6.json" "$tmp"
ls -la "$tmp"
find "$tmp" -type f -name "*.json" -print0 | while IFS= read -r -d '' file; do
echo -e "\033[0;31m$(basename "$file")\033[0m"
jq . "$file"
echo ""
done
find "$tmp" -type f -name "*.network" -print0 | while IFS= read -r -d '' file; do
echo -e "\033[0;31m$(basename "$file")\033[0m"
cat "$file"
echo ""
done

View file

@ -1,17 +1,33 @@
{ config, lib, modulesPath, pkgs, ... }: {
config,
lib,
modulesPath,
pkgs,
...
}:
let let
writePython3 = pkgs.writers.makePythonWriter restore-network = pkgs.writers.writePython3 "restore-network" {
pkgs.python3Minimal pkgs.python3Packages pkgs.buildPackages.python3Packages; flakeIgnore = [ "E501" ];
# writePython3Bin takes the same arguments as writePython3 but outputs a directory (like writeScriptBin)
writePython3Bin = name: writePython3 "/bin/${name}";
restore-network = writePython3Bin "restore-network" {
flakeIgnore = [ "E501" ];
} ./restore_routes.py; } ./restore_routes.py;
# does not link with iptables enabled # does not link with iptables enabled
iprouteStatic = pkgs.pkgsStatic.iproute2.override { iptables = null; }; iprouteStatic = pkgs.pkgsStatic.iproute2.override { iptables = null; };
kexec-tools = pkgs.pkgsStatic.kexec-tools.overrideAttrs (old: {
patches = old.patches ++ [
(pkgs.fetchpatch {
url = "https://marc.info/?l=kexec&m=166636009110699&q=mbox";
hash = "sha256-wi0/Ajy/Ac+7npKEvDsMzgNhEWhOMFeoUWcpgGrmVDc=";
})
];
nativeBuildInputs = (old.nativeBuildInputs or []) ++ [
pkgs.pkgsStatic.buildPackages.autoreconfHook
];
meta = old.meta // {
badPlatforms = [ ]; # allow riscv64
};
});
in in
{ {
imports = [ imports = [
@ -21,7 +37,6 @@ in
../serial.nix ../serial.nix
../restore-remote-access.nix ../restore-remote-access.nix
]; ];
options = { options = {
system.kexec-installer.name = lib.mkOption { system.kexec-installer.name = lib.mkOption {
type = lib.types.str; type = lib.types.str;
@ -33,26 +48,27 @@ in
}; };
config = { config = {
boot.initrd.compressor = "xz";
# This is a variant of the upstream kexecScript that also allows embedding # This is a variant of the upstream kexecScript that also allows embedding
# a ssh key. # a ssh key.
system.build.kexecRun = pkgs.runCommand "kexec-run" { } '' system.build.kexecRun =
install -D -m 0755 ${./kexec-run.sh} $out pkgs.runCommand "kexec-run" { nativeBuildInputs = [ pkgs.buildPackages.shellcheck ]; }
''
install -D -m 0755 ${./kexec-run.sh} $out
sed -i \ sed -i \
-e 's|@init@|${config.system.build.toplevel}/init|' \ -e 's|@init@|${config.system.build.toplevel}/init|' \
-e 's|@kernelParams@|${lib.escapeShellArgs config.boot.kernelParams}|' \ -e 's|@kernelParams@|${lib.escapeShellArgs config.boot.kernelParams}|' \
$out $out
${pkgs.shellcheck}/bin/shellcheck $out shellcheck $out
''; '';
system.build.kexecTarball = pkgs.runCommand "kexec-tarball" { } '' system.build.kexecTarball = pkgs.runCommand "kexec-tarball" { } ''
mkdir kexec $out mkdir kexec $out
cp "${config.system.build.netbootRamdisk}/initrd" kexec/initrd cp "${config.system.build.netbootRamdisk}/initrd" kexec/initrd
cp "${config.system.build.kernel}/${config.system.boot.loader.kernelFile}" kexec/bzImage cp "${config.system.build.kernel}/${config.system.boot.loader.kernelFile}" kexec/bzImage
cp "${config.system.build.kexecRun}" kexec/run cp "${config.system.build.kexecRun}" kexec/run
cp "${pkgs.pkgsStatic.kexec-tools}/bin/kexec" kexec/kexec cp "${kexec-tools}/bin/kexec" kexec/kexec
cp "${iprouteStatic}/bin/ip" kexec/ip cp "${iprouteStatic}/bin/ip" kexec/ip
${lib.optionalString (pkgs.hostPlatform == pkgs.buildPlatform) '' ${lib.optionalString (pkgs.hostPlatform == pkgs.buildPlatform) ''
kexec/ip -V kexec/ip -V
@ -73,7 +89,7 @@ in
Type = "oneshot"; Type = "oneshot";
RemainAfterExit = true; RemainAfterExit = true;
ExecStart = [ ExecStart = [
"${restore-network}/bin/restore-network /root/network/addrs.json /root/network/routes-v4.json /root/network/routes-v6.json /etc/systemd/network" "${restore-network} /root/network/addrs.json /root/network/routes-v4.json /root/network/routes-v6.json /etc/systemd/network"
]; ];
}; };

View file

@ -1,80 +1,34 @@
import json import json
import sys import sys
from pathlib import Path from pathlib import Path
from typing import Any, Iterator from typing import Any
from dataclasses import dataclass
@dataclass def filter_interfaces(network: list[dict[str, Any]]) -> list[dict[str, Any]]:
class Address: output = []
address: str
family: str
prefixlen: int
preferred_life_time: int = 0
valid_life_time: int = 0
@dataclass
class Interface:
name: str
ifname: str | None
mac_address: str
dynamic_addresses: list[Address]
static_addresses: list[Address]
static_routes: list[dict[str, Any]]
def filter_interfaces(network: list[dict[str, Any]]) -> list[Interface]:
interfaces = []
for net in network: for net in network:
if net.get("link_type") == "loopback": if net.get("link_type") == "loopback":
continue continue
if not (mac_address := net.get("address")): if not net.get("address"):
# We need a mac address to match devices reliable # We need a mac address to match devices reliable
continue continue
static_addresses = [] addr_info = []
dynamic_addresses = [] has_dynamic_address = False
for info in net.get("addr_info", []): for addr in net.get("addr_info", []):
# no link-local ipv4/ipv6 # no link-local ipv4/ipv6
if info.get("scope") == "link": if addr.get("scope") == "link":
continue continue
if (preferred_life_time := info.get("preferred_life_time")) is None: # do not explicitly configure addresses from dhcp or router advertisement
if addr.get("dynamic", False):
has_dynamic_address = True
continue continue
if (valid_life_time := info.get("valid_life_time")) is None:
continue
if (prefixlen := info.get("prefixlen")) is None:
continue
if (family := info.get("family")) not in ["inet", "inet6"]:
continue
if (local := info.get("local")) is None:
continue
if (dynamic := info.get("dynamic", False)) is None:
continue
address = Address(
address=local,
family=family,
prefixlen=prefixlen,
preferred_life_time=preferred_life_time,
valid_life_time=valid_life_time,
)
if dynamic:
dynamic_addresses.append(address)
else: else:
static_addresses.append(address) addr_info.append(addr)
interfaces.append( if addr_info != [] or has_dynamic_address:
Interface( net["addr_info"] = addr_info
name=net.get("ifname", mac_address.replace(":", "-")), output.append(net)
ifname=net.get("ifname"),
mac_address=mac_address,
dynamic_addresses=dynamic_addresses,
static_addresses=static_addresses,
static_routes=[],
)
)
return interfaces return output
def filter_routes(routes: list[dict[str, Any]]) -> list[dict[str, Any]]: def filter_routes(routes: list[dict[str, Any]]) -> list[dict[str, Any]]:
@ -88,54 +42,38 @@ def filter_routes(routes: list[dict[str, Any]]) -> list[dict[str, Any]]:
return filtered return filtered
def find_most_recent_v4_lease(addresses: list[Address]) -> Address | None:
most_recent_address = None
most_recent_lifetime = -1
for addr in addresses:
if addr.family == "inet6":
continue
lifetime = max(addr.preferred_life_time, addr.valid_life_time)
if lifetime > most_recent_lifetime:
most_recent_lifetime = lifetime
most_recent_address = addr
return most_recent_address
def generate_routes(
interface: Interface, routes: list[dict[str, Any]]
) -> Iterator[str]:
for route in routes:
if interface.ifname is None or route.get("dev") != interface.ifname:
continue
# we may ignore on-link default routes here, but I don't see how
# they would be useful for internet connectivity anyway
yield "[Route]"
if route.get("dst") != "default":
# can be skipped for default routes
yield f"Destination = {route['dst']}"
gateway = route.get("gateway")
# route v4 via v6
route_via = route.get("via")
if route_via and route_via.get("family") == "inet6":
gateway = route_via.get("host")
if route.get("dst") == "default":
yield "Destination = 0.0.0.0/0"
if gateway:
yield f"Gateway = {gateway}"
def generate_networkd_units( def generate_networkd_units(
interfaces: list[Interface], routes: list[dict[str, Any]], directory: Path interfaces: list[dict[str, Any]], routes: list[dict[str, Any]], directory: Path
) -> None: ) -> None:
directory.mkdir(exist_ok=True) directory.mkdir(exist_ok=True)
for interface in interfaces: for interface in interfaces:
name = f"00-{interface['ifname']}.network"
addresses = [
f"Address = {addr['local']}/{addr['prefixlen']}"
for addr in interface.get("addr_info", [])
]
route_sections = []
for route in routes:
if route.get("dev", "nodev") != interface.get("ifname", "noif"):
continue
route_section = "[Route]\n"
if route.get("dst") != "default":
# can be skipped for default routes
route_section += f"Destination = {route['dst']}\n"
gateway = route.get("gateway")
if gateway:
route_section += f"Gateway = {gateway}\n"
# we may ignore on-link default routes here, but I don't see how
# they would be useful for internet connectivity anyway
route_sections.append(route_section)
# FIXME in some networks we might not want to trust dhcp or router advertisements # FIXME in some networks we might not want to trust dhcp or router advertisements
unit_sections = [ unit = f"""
f"""
[Match] [Match]
MACAddress = {interface.mac_address} MACAddress = {interface["address"]}
[Network] [Network]
# both ipv4 and ipv6 # both ipv4 and ipv6
@ -145,24 +83,12 @@ LLDP = yes
# ipv6 router advertisements # ipv6 router advertisements
IPv6AcceptRA = yes IPv6AcceptRA = yes
# allows us to ping "nixos.local" # allows us to ping "nixos.local"
MulticastDNS = yes""" MulticastDNS = yes
]
unit_sections.extend(
f"Address = {addr.address}/{addr.prefixlen}"
for addr in interface.static_addresses
)
unit_sections.extend(generate_routes(interface, routes))
most_recent_v4_lease = find_most_recent_v4_lease(interface.dynamic_addresses)
if most_recent_v4_lease:
unit_sections.append("[DHCPv4]")
unit_sections.append(f"RequestAddress = {most_recent_v4_lease.address}")
# trailing newline at the end """
unit_sections.append("") unit += "\n".join(addresses)
unit += "\n" + "\n".join(route_sections)
(directory / f"00-{interface.name}.network").write_text( (directory / name).write_text(unit)
"\n".join(unit_sections)
)
def main() -> None: def main() -> None:

View file

@ -1,18 +1,25 @@
{ pkgs { pkgs
, lib , lib
, kexecTarball , kexecTarball
, nixos-facter ? null
}: }:
pkgs.testers.runNixOSTest { let
makeTest = import (pkgs.path + "/nixos/tests/make-test-python.nix");
makeTest' = args: makeTest args {
inherit pkgs;
inherit (pkgs) system;
};
in
makeTest' {
name = "kexec-installer"; name = "kexec-installer";
meta = with pkgs.lib.maintainers; { meta = with pkgs.lib.maintainers; {
maintainers = [ mic92 ]; maintainers = [ mic92 ];
}; };
nodes = { nodes = {
node1 = { modulesPath, pkgs, ... }: { node1 = { modulesPath, ... }: {
virtualisation.vlans = [ ]; virtualisation.vlans = [ ];
environment.noXlibs = false; # avoid recompilation
imports = [ imports = [
(modulesPath + "/profiles/minimal.nix") (modulesPath + "/profiles/minimal.nix")
]; ];
@ -47,17 +54,10 @@ pkgs.testers.runNixOSTest {
"192.168.42.1/24" "192.168.42.1/24"
"42::1/64" "42::1/64"
]; ];
routes = if pkgs.lib.versionAtLeast lib.version "24.11" then [ routes = [
{ Destination = "192.168.43.0/24"; }
{ Destination = "192.168.44.0/24"; Gateway = "192.168.43.1"; }
{ Destination = "192.168.45.0/24"; Gateway = "43::1"; }
{ Destination = "43::0/64"; }
{ Destination = "44::1/64"; Gateway = "43::1"; }
] else [
# Some static routes that we want to see in the kexeced image # Some static routes that we want to see in the kexeced image
{ routeConfig = { Destination = "192.168.43.0/24"; }; } { routeConfig = { Destination = "192.168.43.0/24"; }; }
{ routeConfig = { Destination = "192.168.44.0/24"; Gateway = "192.168.43.1"; }; } { routeConfig = { Destination = "192.168.44.0/24"; Gateway = "192.168.43.1"; }; }
{ routeConfig = { Destination = "192.168.45.0/24"; Gateway = "43::1"; }; }
{ routeConfig = { Destination = "43::0/64"; }; } { routeConfig = { Destination = "43::0/64"; }; }
{ routeConfig = { Destination = "44::1/64"; Gateway = "43::1"; }; } { routeConfig = { Destination = "44::1/64"; Gateway = "43::1"; }; }
]; ];
@ -65,14 +65,10 @@ pkgs.testers.runNixOSTest {
}; };
}; };
}; };
} // lib.optionalAttrs (lib.versionOlder lib.version "24.11pre") {
# avoid second overlay
environment.noXlibs = false;
}; };
}; };
testScript = /*python*/ '' testScript = ''
import json
import time import time
import subprocess import subprocess
import socket import socket
@ -167,11 +163,6 @@ pkgs.testers.runNixOSTest {
host = ssh(["hostname"], stdout=subprocess.PIPE).stdout.strip() host = ssh(["hostname"], stdout=subprocess.PIPE).stdout.strip()
assert host == "nixos-installer", f"hostname is {host}, not nixos-installer" assert host == "nixos-installer", f"hostname is {host}, not nixos-installer"
has_nixos_facter=${if nixos-facter != null then "True" else "False"}
if has_nixos_facter == True:
data = json.loads(ssh(["nixos-facter"], stdout=subprocess.PIPE).stdout)
assert data["virtualisation"] == "kvm", f"virtualisation is {data['virtualisation']}, not kvm"
host_ed25519_after = ssh(["cat", "/etc/ssh/ssh_host_ed25519_key.pub"], stdout=subprocess.PIPE).stdout.strip() host_ed25519_after = ssh(["cat", "/etc/ssh/ssh_host_ed25519_key.pub"], stdout=subprocess.PIPE).stdout.strip()
assert host_ed25519_before == host_ed25519_after, f"'{host_ed25519_before}' != '{host_ed25519_after}'" assert host_ed25519_before == host_ed25519_after, f"'{host_ed25519_before}' != '{host_ed25519_after}'"
@ -194,10 +185,6 @@ pkgs.testers.runNixOSTest {
print(out) print(out)
assert "192.168.44.2 via 192.168.43.1" in out, f"route to `192.168.44.2 via 192.168.43.1` not found: {out}" assert "192.168.44.2 via 192.168.43.1" in out, f"route to `192.168.44.2 via 192.168.43.1` not found: {out}"
out = ssh(["ip", "route", "get", "192.168.45.2"], stdout=subprocess.PIPE).stdout
print(out)
assert "192.168.45.2 via inet6 43::1" in out, f"route to `192.168.45.2 via inet6 43::1` not found: {out}"
out = ssh(["ip", "route", "get", "43::2"], stdout=subprocess.PIPE).stdout out = ssh(["ip", "route", "get", "43::2"], stdout=subprocess.PIPE).stdout
print(out) print(out)
assert "43::2 from :: dev" in out, f"route `43::2 from dev` not found: {out}" assert "43::2 from :: dev" in out, f"route `43::2 from dev` not found: {out}"

View file

@ -1,26 +0,0 @@
{
lib,
pkgs,
config,
...
}:
let
isUnstable = config.boot.zfs.package == pkgs.zfsUnstable;
zfsCompatibleKernelPackages = lib.filterAttrs (
name: kernelPackages:
(builtins.match "linux_[0-9]+_[0-9]+" name) != null
&& (builtins.tryEval kernelPackages).success
&& (
(!isUnstable && !kernelPackages.zfs.meta.broken)
|| (isUnstable && !kernelPackages.zfs_unstable.meta.broken)
)
) pkgs.linuxKernel.packages;
latestKernelPackage = lib.last (
lib.sort (a: b: (lib.versionOlder a.kernel.version b.kernel.version)) (builtins.attrValues zfsCompatibleKernelPackages)
);
in
{
# Note this might jump back and worth as kernel get added or removed.
boot.kernelPackages = latestKernelPackage;
}

View file

@ -5,7 +5,11 @@
nix.settings.connect-timeout = 5; nix.settings.connect-timeout = 5;
# Enable flakes # Enable flakes
nix.settings.experimental-features = [ "nix-command" "flakes" ]; nix.settings.experimental-features = [
"nix-command"
"flakes"
"repl-flake"
];
# The default at 10 is rarely enough. # The default at 10 is rarely enough.
nix.settings.log-lines = lib.mkDefault 25; nix.settings.log-lines = lib.mkDefault 25;

View file

@ -1,71 +0,0 @@
{
lib,
buildGoModule,
fetchFromGitHub,
hwinfo,
libusb1,
gcc,
pkg-config,
util-linux,
pciutils,
stdenv,
}:
let
# We are waiting on some changes to be merged upstream: https://github.com/openSUSE/hwinfo/pulls
hwinfoOverride = hwinfo.overrideAttrs {
src = fetchFromGitHub {
owner = "numtide";
repo = "hwinfo";
rev = "a559f34934098d54096ed2078e750a8245ae4044";
hash = "sha256-3abkWPr98qXXQ17r1Z43gh2M5hl/DHjW2hfeWl+GSAs=";
};
};
in
buildGoModule rec {
pname = "nixos-facter";
version = "0.1.1";
src = fetchFromGitHub {
owner = "numtide";
repo = "nixos-facter";
rev = "v${version}";
hash = "sha256-vlPmvCrgX64dcf//BPtQszBt7dkq35JpgQg+/LW0AqM=";
};
vendorHash = "sha256-5leiTNp3FJmgFd0SKhu18hxYZ2G9SuQPhZJjki2SDVs=";
CGO_ENABLED = 1;
buildInputs = [
libusb1
hwinfoOverride
];
nativeBuildInputs = [
gcc
pkg-config
];
runtimeInputs = [
libusb1
util-linux
pciutils
];
ldflags = [
"-s"
"-w"
"-X git.numtide.com/numtide/nixos-facter/build.Name=nixos-facter"
"-X git.numtide.com/numtide/nixos-facter/build.Version=v${version}"
"-X github.com/numtide/nixos-facter/pkg/build.System=${stdenv.hostPlatform.system}"
];
meta = {
description = "Declarative hardware configuration for NixOS";
homepage = "https://github.com/numtide/nixos-facter";
license = lib.licenses.gpl3Plus;
maintainers = [ lib.maintainers.brianmcgee ];
mainProgram = "nixos-facter";
platforms = lib.platforms.linux;
};
}

View file

@ -1,7 +1,7 @@
# This module optimizes for non-interactive deployments by remove some store paths # This module optimizes for non-interactive deployments by remove some store paths
# which are primarily useful for interactive installations. # which are primarily useful for interactive installations.
{ lib, pkgs, modulesPath, ... }: { lib, pkgs, ... }:
{ {
disabledModules = [ disabledModules = [
# This module adds values to multiple lists (systemPackages, supportedFilesystems) # This module adds values to multiple lists (systemPackages, supportedFilesystems)
@ -10,14 +10,8 @@
]; ];
imports = [ imports = [
# ./zfs-minimal.nix ./zfs-minimal.nix
./no-bootloaders.nix ./no-bootloaders.nix
./python-minimal.nix
./noveau-workaround.nix
# reduce closure size by removing perl
"${modulesPath}/profiles/perlless.nix"
# FIXME: we still are left with nixos-generate-config due to nixos-install-tools
{ system.forbiddenDependenciesRegexes = lib.mkForce []; }
]; ];
# among others, this prevents carrying a stdenv with gcc in the image # among others, this prevents carrying a stdenv with gcc in the image
@ -30,29 +24,10 @@
programs.nano.enable = false; programs.nano.enable = false;
# prevents strace # prevents strace
environment.defaultPackages = lib.mkForce [ environment.defaultPackages = lib.mkForce [ pkgs.rsync pkgs.parted pkgs.gptfdisk ];
pkgs.rsync
pkgs.parted
pkgs.gptfdisk
pkgs.e2fsprogs
];
# normal users are not allowed with sys-users
# see https://github.com/NixOS/nixpkgs/pull/328926
users.users.nixos = {
isSystemUser = true;
isNormalUser = lib.mkForce false;
shell = "/run/current-system/sw/bin/bash";
group = "nixos";
};
users.groups.nixos = {};
# we prefer root as this is also what we use in nixos-anywhere
services.getty.autologinUser = lib.mkForce "root";
# we are missing this from base.nix # we are missing this from base.nix
boot.supportedFilesystems = [ boot.supportedFilesystems = [
"ext4"
"btrfs" "btrfs"
# probably not needed but does not seem to increase closure size # probably not needed but does not seem to increase closure size
"cifs" "cifs"

View file

@ -1,4 +0,0 @@
{
# fixes blank screen on boot for some cards
boot.kernelParams = [ "nouveau.modeset=0" ];
}

View file

@ -1,10 +0,0 @@
{
nixpkgs.overlays = [
(final: prev: {
bcachefs-tools = prev.bcachefs-tools.override { python3 = final.python3Minimal; };
cifs-utils = prev.cifs-utils.override { python3 = final.python3Minimal; };
nfs-utils = prev.nfs-utils.override { python3 = final.python3Minimal; };
talloc = prev.talloc.override { python3 = final.python3Minimal; };
})
];
}

View file

@ -1,21 +1,33 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
# incorperate a space-optimized version of zfs # incorperate a space-optimized version of zfs
let let
zfs = pkgs.zfsUnstable.override { zfs = pkgs.zfs.override {
# this overrides saves 10MB # this overrides saves 10MB
samba = pkgs.coreutils; samba = pkgs.coreutils;
python3 = pkgs.python3Minimal;
}; };
hasZfs = lib.meta.availableOn pkgs.stdenv.hostPlatform config.boot.zfs.package;
in in
{ lib.mkIf hasZfs {
services.udev.packages = [ zfs ]; # to hook zvol naming, etc. services.udev.packages = [ zfs ]; # to hook zvol naming, etc.
# unsure if need this, but in future udev rules could potentially point to systemd services. # unsure if need this, but in future udev rules could potentially point to systemd services.
systemd.packages = [ zfs ]; systemd.packages = [ zfs ];
environment.defaultPackages = lib.mkForce [ zfs ]; # this merges with outer noninteractive module. environment.defaultPackages = lib.mkForce [ zfs ]; # this merges with outer noninteractive module.
boot.kernelModules = [ "zfs" ]; boot.kernelModules = [ "zfs" ];
boot.extraModulePackages = [ config.boot.kernelPackages.zfs_unstable ]; boot.extraModulePackages = [
(config.boot.kernelPackages.zfs.override {
inherit (config.boot.zfs) removeLinuxDRM;
})
];
boot.kernelPatches = lib.optional (config.boot.zfs.removeLinuxDRM && pkgs.stdenv.hostPlatform.system == "aarch64-linux") {
name = "export-neon-symbols-as-gpl";
patch = pkgs.fetchpatch {
url = "https://github.com/torvalds/linux/commit/aaeca98456431a8d9382ecf48ac4843e252c07b3.patch";
hash = "sha256-L2g4G1tlWPIi/QRckMuHDcdWBcKpObSWSRTvbHRIwIk=";
revert = true;
};
};
networking.hostId = lib.mkDefault "8425e349"; networking.hostId = lib.mkDefault "8425e349";
} }