mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-22 01:13:00 +02:00
doc: minor markdownlint cleanup and fixes to admonitions
[skip ci] Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
+137
-136
@@ -1,37 +1,36 @@
|
||||
Boot Procedure
|
||||
==============
|
||||
# Boot Procedure
|
||||
|
||||
Systems running Infix will typically boot in multiple phases, forming
|
||||
a boot chain. Each link in the chain has three main responsibilities:
|
||||
|
||||
1. Ensuring the integrity of the next link before passing control to
|
||||
it. This avoids silent failures stemming from data corruption.
|
||||
|
||||
2. Ensuring the authenticity of the next link before passing control
|
||||
1. Ensuring the authenticity of the next link before passing control
|
||||
to it, commonly referred to as _Secure Boot_. This protects against
|
||||
malicious attempts to modify a system's firmware.
|
||||
|
||||
3. Preparing the system state according to the requirements of the
|
||||
1. Preparing the system state according to the requirements of the
|
||||
next link. E.g. the Linux kernel requires the system's RAM to be
|
||||
operational.
|
||||
|
||||
A typical chain consists of four stages:
|
||||
|
||||
.---------.
|
||||
| ROM >---. Determine the location of and load the SPL
|
||||
'---------' |
|
||||
.-----------------'
|
||||
| .---------.
|
||||
'---> SPL >---. Perform DDR training and load the TPL
|
||||
'---------' |
|
||||
.-----------------'
|
||||
| .---------.
|
||||
'---> TPL >---. Load Linux kernel, device tree, and root filesystem
|
||||
'---------' |
|
||||
.-----------------'
|
||||
| .---------.
|
||||
'---> Infix | Get down to business
|
||||
'---------'
|
||||
```
|
||||
.---------.
|
||||
| ROM >---. Determine the location of and load the SPL
|
||||
'---------' |
|
||||
.-----------------'
|
||||
| .---------.
|
||||
'---> SPL >---. Perform DDR training and load the TPL
|
||||
'---------' |
|
||||
.-----------------'
|
||||
| .---------.
|
||||
'---> TPL >---. Load Linux kernel, device tree, and root filesystem
|
||||
'---------' |
|
||||
.-----------------'
|
||||
| .---------.
|
||||
'---> Infix | Get down to business
|
||||
'---------'
|
||||
```
|
||||
|
||||
After a reset, hardware will pass control to a program (_ROM_) which
|
||||
is almost always programmed into the SoC by the vendor. This program
|
||||
@@ -52,9 +51,7 @@ This document's focus is to describe the final two phases of the boot
|
||||
chain, as the initial phases are very hardware dependent, better
|
||||
described by existing documentation provided by the SoC vendor.
|
||||
|
||||
|
||||
Bootloader
|
||||
----------
|
||||
## Bootloader
|
||||
|
||||
### Configuration
|
||||
|
||||
@@ -62,33 +59,42 @@ To mitigate the risk of a malicious user being able to circumvent the
|
||||
bootloader's validation procedure, user configuration is kept to a
|
||||
minimum. Two settings are available:
|
||||
|
||||
- **Boot order**: Since Infix maintains two copies of its software image,
|
||||
and as some bootloaders support [netbooting][2], the order in which boot
|
||||
sources are considered can be configured. To select the active
|
||||
source, use [RAUC][]:
|
||||
**Boot order**: Since Infix maintains two copies of its software image,
|
||||
and as some bootloaders support [netbooting][2], the order in which boot
|
||||
sources are considered can be configured. To select the active
|
||||
source, use [RAUC][]:
|
||||
|
||||
rauc status mark-active <slot>
|
||||
```
|
||||
root@example:~# rauc status mark-active <slot>
|
||||
...
|
||||
```
|
||||
|
||||
Where `<slot>` is one of:
|
||||
Where `<slot>` is one of:
|
||||
|
||||
| `<slot>` | Source |
|
||||
|------------|---------------------------|
|
||||
| `rootfs.0` | Primary partition |
|
||||
| `rootfs.1` | Secondary partition |
|
||||
| `net.0` | Netboot (where supported) |
|
||||
| **`<slot>`** | **Source** |
|
||||
|--------------|---------------------------|
|
||||
| `rootfs.0` | Primary partition |
|
||||
| `rootfs.1` | Secondary partition |
|
||||
| `net.0` | Netboot (where supported) |
|
||||
|
||||
- **Debug**: By default, the kernel will only output errors to the
|
||||
console during boot. Optionally, this can be altered such that all
|
||||
enabled messages are logged.
|
||||
**Debug**: By default, the kernel will only output errors to the
|
||||
console during boot. Optionally, this can be altered such that all
|
||||
enabled messages are logged.
|
||||
|
||||
On systems using _U-Boot_, this can be enabled by running `fw_setenv
|
||||
DEBUG 1`. To restore the default behavior, run `fw_setenv DEBUG`.
|
||||
On systems using _U-Boot_, this can be enabled by running `fw_setenv
|
||||
DEBUG 1`. To restore the default behavior, run `fw_setenv DEBUG`.
|
||||
|
||||
On systems running _GRUB_, this can be enabled by running
|
||||
`grub-editenv /mnt/aux/grub/grubenv set DEBUG=1`. To restore the
|
||||
default behavior, run `grub-editenv /mnt/aux/grub/grubenv unset
|
||||
DEBUG`
|
||||
On systems running _GRUB_, this can be enabled by running:
|
||||
|
||||
```
|
||||
root@example:~# grub-editenv /mnt/aux/grub/grubenv set DEBUG=1
|
||||
```
|
||||
|
||||
To restore the default behavior, run:
|
||||
|
||||
```
|
||||
root@example:~# grub-editenv /mnt/aux/grub/grubenv unset DEBUG
|
||||
```
|
||||
|
||||
### U-Boot
|
||||
|
||||
@@ -109,7 +115,6 @@ Access to U-Boot's shell is disabled to prevent side-loading of
|
||||
malicious software. To configure the active boot partition, refer to
|
||||
the [Bootloader Configuration](#configuration) section.
|
||||
|
||||
|
||||
### GRUB
|
||||
|
||||
Used on _x86_64_ based systems. Neither the _integrity_ nor the
|
||||
@@ -123,7 +128,6 @@ partition can be selected interactively at boot using the arrow keys. It
|
||||
is also possible to permanently configure the default partition from
|
||||
Infix using the [Bootloader Configuration](#configuration).
|
||||
|
||||
|
||||
## System Boot
|
||||
|
||||
After the system firmware (BIOS or and [boot loader](boot.md) start
|
||||
@@ -132,49 +136,50 @@ password in VPD, are detailed later in this section.
|
||||
|
||||

|
||||
|
||||
1. Before mounting `/cfg` and `/var` partitions, hosting read-writable
|
||||
data like `startup-config` and container images, the system first
|
||||
checks if a factory reset has been requested by the user, if so it
|
||||
wipes the contents of these partitions
|
||||
2. Linux boots with a device tree which is used for detecting generic
|
||||
make and model of the device, e.g., number of interfaces. It may
|
||||
also reference an EEPROM with [Vital Product Data](vpd.md). That is
|
||||
where the base MAC address and per-device password hash is stored.
|
||||
(Generic builds use the same MAC address and password)
|
||||
3. On every boot the system's `factory-config` and `failure-config` are
|
||||
generated from the YANG[^2] models of the current firmware version.
|
||||
This ensures that a factory reset device can always boot, and that
|
||||
there is a working fail safe, or rather *fail secure*, mode
|
||||
4. On first power-on, and after a factory reset, the system does not
|
||||
have a `startup-config`, in which case `factory-config` is copied
|
||||
to `startup-config` -- if a per-product specific version exists it
|
||||
is preferred over the generated one
|
||||
5. Provided the integrity of the `startup-config` is OK, a system
|
||||
service loads and activates the configuration
|
||||
1. Before mounting `/cfg` and `/var` partitions, hosting read-writable
|
||||
data like `startup-config` and container images, the system first
|
||||
checks if a factory reset has been requested by the user, if so it
|
||||
wipes the contents of these partitions
|
||||
1. Linux boots with a device tree which is used for detecting generic
|
||||
make and model of the device, e.g., number of interfaces. It may
|
||||
also reference an EEPROM with [Vital Product Data](vpd.md). That is
|
||||
where the base MAC address and per-device password hash is stored.
|
||||
(Generic builds use the same MAC address and password)
|
||||
1. On every boot the system's `factory-config` and `failure-config` are
|
||||
generated from the YANG[^2] models of the current firmware version.
|
||||
This ensures that a factory reset device can always boot, and that
|
||||
there is a working fail safe, or rather *fail secure*, mode
|
||||
1. On first power-on, and after a factory reset, the system does not
|
||||
have a `startup-config`, in which case `factory-config` is copied
|
||||
to `startup-config` -- if a per-product specific version exists it
|
||||
is preferred over the generated one
|
||||
1. Provided the integrity of the `startup-config` is OK, a system
|
||||
service loads and activates the configuration
|
||||
|
||||
### Failure Modes
|
||||
|
||||
So, what happens if any of the steps above fail?
|
||||
|
||||
**VPD Fail**
|
||||
#### VPD Fail
|
||||
|
||||
The per-device password cannot be read, or is corrupt, so the system
|
||||
`factory-config` and `failure-config` are not generated:
|
||||
|
||||
1. First boot, or after factory reset: `startup-config` cannot be
|
||||
created or loaded, and `failure-config` cannot be loaded. The
|
||||
system ends up in an unrecoverable state, i.e., **RMA[^3] Mode**
|
||||
2. The system has booted (at least) once with correct VPD and password
|
||||
and already has a `startup-config`. Provided the `startup-config`
|
||||
is OK (see below), it is loaded and system boots successfully
|
||||
1. First boot, or after factory reset: `startup-config` cannot be
|
||||
created or loaded, and `failure-config` cannot be loaded. The
|
||||
system ends up in an unrecoverable state, i.e., **RMA[^3] Mode**
|
||||
1. The system has booted (at least) once with correct VPD and password
|
||||
and already has a `startup-config`. Provided the `startup-config`
|
||||
is OK (see below), it is loaded and system boots successfully
|
||||
|
||||
In both cases, external factory reset modes/button will not help, and
|
||||
in the second case will cause the device to fail on the next boot.
|
||||
|
||||
> [!NOTE]
|
||||
> The second case does not yet have any warning or event that can be
|
||||
> detected from the outside. This is planned for a later release.
|
||||
|
||||
**Broken startup-config**
|
||||
#### Broken startup-config
|
||||
|
||||
If loading `startup-config` fails for some reason, e.g., invalid JSON
|
||||
syntax, failed validation against the system's YANG model, or a bug in
|
||||
@@ -194,9 +199,7 @@ link-local, and device discovery protocols: LLDP, mDNS-SD. The login
|
||||
and shell prompt are set to `failure-c0-ff-ee`, the last three octets of
|
||||
the device's base MAC address.
|
||||
|
||||
|
||||
System Upgrade
|
||||
--------------
|
||||
## System Upgrade
|
||||
|
||||
Much of the minutiae of software upgrades is delegated to [RAUC][],
|
||||
which offers lots of benefits out-of-the-box:
|
||||
@@ -204,18 +207,19 @@ which offers lots of benefits out-of-the-box:
|
||||
- Upgrade Bundles are always signed, such that their authenticity can
|
||||
be verified by the running operating system, before the new one is
|
||||
installed.
|
||||
|
||||
- The bureaucracy of interfacing with different bootloaders, manage
|
||||
the boot order, is a simple matter of providing a compatible
|
||||
configuration.
|
||||
|
||||
- Updates can be sourced from the local filesystem (including external
|
||||
media like USB sticks or SD-cards) and from remote servers using FTP
|
||||
or HTTP(S).
|
||||
|
||||
To initiate a system upgrade from the shell[^1], run:
|
||||
|
||||
rauc install <file|url>
|
||||
```
|
||||
root@example:~# rauc install <file|url>
|
||||
...
|
||||
```
|
||||
|
||||
Where the file or URL points to a [RAUC Upgrade Bundle](#rauc-upgrade-bundle).
|
||||
|
||||
@@ -225,9 +229,7 @@ will then boot from the newly installed image. Since the partition
|
||||
from which you were originally running is now inactive, running the
|
||||
same upgrade command again will bring both partitions into sync.
|
||||
|
||||
|
||||
Image Formats
|
||||
-------------
|
||||
## Image Formats
|
||||
|
||||
### SquashFS Image
|
||||
|
||||
@@ -279,7 +281,6 @@ validate the SquashFS's contents. This path was chosen because:
|
||||
In its full form, it can be used to netboot Infix, as it contains all
|
||||
the information needed by U-Boot in a single file.
|
||||
|
||||
|
||||
### RAUC Upgrade Bundle
|
||||
|
||||
**Canonical Name**: `infix-${ARCH}.pkg`
|
||||
@@ -293,7 +294,6 @@ system.
|
||||
When performing a [System Upgrade](#system-upgrade), this is the format
|
||||
to use.
|
||||
|
||||
|
||||
### Disk Image
|
||||
|
||||
**Canonical Name**: `disk.img`
|
||||
@@ -302,35 +302,37 @@ Infix runs from a block device (e.g. eMMC or virtio disk) with the
|
||||
following layout. The disk is expected to use the GPT partitioning
|
||||
scheme. Partitions marked with an asterisk are optional.
|
||||
|
||||
.-----------.
|
||||
| GPT Table |
|
||||
:-----------:
|
||||
| boot* |
|
||||
:-----------:
|
||||
| aux |
|
||||
:-----------:
|
||||
| |
|
||||
| primary |
|
||||
| |
|
||||
:-----------:
|
||||
| |
|
||||
| secondary |
|
||||
| |
|
||||
:-----------:
|
||||
| cfg |
|
||||
:-----------:
|
||||
| |
|
||||
| var* |
|
||||
| |
|
||||
'-----------'
|
||||
```
|
||||
.-----------.
|
||||
| GPT Table |
|
||||
:-----------:
|
||||
| boot* |
|
||||
:-----------:
|
||||
| aux |
|
||||
:-----------:
|
||||
| |
|
||||
| primary |
|
||||
| |
|
||||
:-----------:
|
||||
| |
|
||||
| secondary |
|
||||
| |
|
||||
:-----------:
|
||||
| cfg |
|
||||
:-----------:
|
||||
| |
|
||||
| var* |
|
||||
| |
|
||||
'-----------'
|
||||
```
|
||||
|
||||
#### `boot` - Bootloader
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-----------------------------------------|
|
||||
| Required | No |
|
||||
| Size | 4 MiB |
|
||||
| Format | Raw binary, as dictated by the hardware |
|
||||
| **Parameter** | **Value** |
|
||||
|---------------|-----------------------------------------|
|
||||
| Required | No |
|
||||
| Size | 4 MiB |
|
||||
| Format | Raw binary, as dictated by the hardware |
|
||||
|
||||
Optional partition containing the system's bootloader. May also reside
|
||||
in a separate storage device, e.g. a serial FLASH.
|
||||
@@ -340,11 +342,11 @@ the GRUB bootloader.
|
||||
|
||||
#### `aux` - Auxiliary Data
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-----------------|
|
||||
| Required | Yes |
|
||||
| Size | 4 MiB |
|
||||
| Format | EXT4 filesystem |
|
||||
| **Parameter** | **Value** |
|
||||
|---------------|-----------------|
|
||||
| Required | Yes |
|
||||
| Size | 4 MiB |
|
||||
| Format | EXT4 filesystem |
|
||||
|
||||
Holds information that is shared between Infix and its bootloader,
|
||||
such as image signatures required to validate the chain of trust,
|
||||
@@ -353,10 +355,10 @@ bootloader configuration etc.
|
||||
Typical layout when using U-Boot bootloader:
|
||||
|
||||
```
|
||||
/
|
||||
├ primary.itbh
|
||||
├ secondary.itbh
|
||||
└ uboot.env
|
||||
/
|
||||
├ primary.itbh
|
||||
├ secondary.itbh
|
||||
└ uboot.env
|
||||
```
|
||||
|
||||
During boot, an ITB header along with the corresponding root
|
||||
@@ -370,11 +372,11 @@ required to configure the boot order.
|
||||
|
||||
#### `primary`/`secondary` - Root Filesystems
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------------------|
|
||||
| Required | Yes |
|
||||
| Size | >= 256 MiB |
|
||||
| Format | Squash filesystem |
|
||||
| **Parameter** | **Value** |
|
||||
|---------------|-------------------|
|
||||
| Required | Yes |
|
||||
| Size | >= 256 MiB |
|
||||
| Format | Squash filesystem |
|
||||
|
||||
Holds the [SquashFS Image](#squashfs-image). Two copies exist so that
|
||||
an incomplete upgrade does not brick the system, and to allow fast
|
||||
@@ -382,22 +384,22 @@ rollbacks when upgrading to a new version.
|
||||
|
||||
#### `cfg` - Configuration Data
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-----------------|
|
||||
| Required | Yes |
|
||||
| Size | >= 16 MiB |
|
||||
| Format | EXT4 filesystem |
|
||||
| **Parameter** | **Value** |
|
||||
|---------------|-----------------|
|
||||
| Required | Yes |
|
||||
| Size | >= 16 MiB |
|
||||
| Format | EXT4 filesystem |
|
||||
|
||||
Non-volatile storage of the system configuration and user data.
|
||||
Concretely, user data is everything stored under `/root` and `/home`.
|
||||
|
||||
#### `var` - Variable Data
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-----------------|
|
||||
| Required | No |
|
||||
| Size | >= 16 MiB |
|
||||
| Format | EXT4 filesystem |
|
||||
| **Parameter** | **Value** |
|
||||
|---------------|-----------------|
|
||||
| Required | No |
|
||||
| Size | >= 16 MiB |
|
||||
| Format | EXT4 filesystem |
|
||||
|
||||
Persistent storage for everything under `/var`. This is maintained as
|
||||
a separate filesystem from the data in `cfg`, because while the system
|
||||
@@ -410,7 +412,7 @@ If `var` is not available, Infix will still persist `/var/lib` using
|
||||
[^1]: See [Upgrade & Boot Order](upgrade.md) for more information.
|
||||
[^2]: YANG is a modeling language from IETF, replacing that used for
|
||||
SNMP (MIB), used to describe the subsystems and properties of
|
||||
the system.
|
||||
the system.
|
||||
[^3]: Return Merchandise Authorization (RMA), i.e., broken beyond repair
|
||||
by end-user and eligible for return to manufacturer.
|
||||
[^4]: Customer specific builds can define their own `failure-config`.
|
||||
@@ -420,7 +422,6 @@ If `var` is not available, Infix will still persist `/var/lib` using
|
||||
security problems on the network. E.g., start forwarding traffic
|
||||
between previously isolated VLANs.
|
||||
|
||||
|
||||
[2]: netboot.md
|
||||
[FIT]: https://u-boot.readthedocs.io/en/latest/usage/fit.html
|
||||
[RAUC]: https://rauc.io
|
||||
|
||||
+88
-92
@@ -1,11 +1,9 @@
|
||||
Branding & Releases
|
||||
===================
|
||||
# Branding & Releases
|
||||
|
||||
This document is for projects using Infix as a br2-external, i.e., OEMs.
|
||||
This document is for projects using Infix as a br2-external, i.e., OEMs
|
||||
that want to create *their own "Spin"* of Infix.
|
||||
|
||||
|
||||
Branding
|
||||
--------
|
||||
## Branding
|
||||
|
||||
Branding is done in menuconfig, there are several settings affecting
|
||||
it, most are in the Infix external subsection called "Branding", but
|
||||
@@ -24,25 +22,18 @@ check this for you and you may end up with odd results.
|
||||
|
||||
Verify the result after a build by inspecting:
|
||||
|
||||
- `output/images/*`: names, missing prefix, etc.
|
||||
- `output/target/etc/os-release`: this file is sourced by
|
||||
other build scripts, e.g., `mkgns3a.sh`. For reference, see
|
||||
https://www.freedesktop.org/software/systemd/man/os-release.html
|
||||
- `output/images/*`: names, missing prefix, etc.
|
||||
- `output/target/etc/os-release`: this file is sourced by other build
|
||||
scripts, e.g., `mkgns3a.sh`. For reference, see [os-release(5)][]
|
||||
|
||||
> **Note:** to get proper GIT revision (hash) from your composed OS,
|
||||
> remember in menuconfig to set `INFIX_OEM_PATH`. When unset the
|
||||
> Infix `post-build.sh` script defaults to the Infix base path. The
|
||||
> revision is stored in the file `/etc/os-release` as BUILD_ID and
|
||||
> is also in the file `/etc/version`. See below for more info.
|
||||
> [!IMPORTANT]
|
||||
> To get a proper GIT revision (hash) from your OS spin, remember to set
|
||||
> in menuconfig `INFIX_OEM_PATH`. When unset, the Infix `post-build.sh`
|
||||
> script defaults to the Infix base path. The revision is stored in the
|
||||
> file `/etc/os-release` as `BUILD_ID`, also in the file `/etc/version`.
|
||||
> See below for more info.
|
||||
|
||||
[^1]: The base MAC address is defined in the device's Vital Product
|
||||
Data (VPD) EEPROM, or similar, which is used by the kernel to
|
||||
create the system interfaces. This MAC address is usually also
|
||||
printed on a label on the device.
|
||||
|
||||
|
||||
Factory & Failure Config
|
||||
------------------------
|
||||
## Factory & Failure Config
|
||||
|
||||
To support booting the same image (CPU architecture) on multiple boards,
|
||||
Infix by default generates the device's initial configuration every time
|
||||
@@ -56,7 +47,6 @@ However, for custom builds of Infix it is possible to override this with
|
||||
a single static `/etc/factory-config.cfg` (and failure-config) in your
|
||||
rootfs overlay -- with a [VPD](vpd.md) you can even support several!
|
||||
|
||||
|
||||
### Variables & Format Specifiers
|
||||
|
||||
Parts of the configuration you likely always want to generated, like the
|
||||
@@ -65,22 +55,22 @@ unique (per-device with a VPD) password hash. This section lists the
|
||||
available keywords, see the next section for examples of how to use
|
||||
them:
|
||||
|
||||
- **Default password hash:** `$factory$` (from VPD, .dtb, or built-in)
|
||||
XPath: `/ietf-system:system/authentication/user/password`
|
||||
- **Default SSH and NETCONF hostkey:** `genkey` (regenerated at factory reset)
|
||||
XPath: `/ietf-keystore:keystore/asymmetric-keys/asymmetric-key[name='genkey']`
|
||||
- **Hostname format specifiers:**
|
||||
XPath: `/ietf-system:system/hostname`
|
||||
- **Default password hash:** `$factory$` (from VPD, .dtb, or built-in)
|
||||
XPath: `/ietf-system:system/authentication/user/password`
|
||||
- **Default SSH and NETCONF hostkey:** `genkey` (regenerated at factory reset)
|
||||
XPath: `/ietf-keystore:keystore/asymmetric-keys/asymmetric-key[name='genkey']`
|
||||
- **Hostname format specifiers:**
|
||||
XPath: `/ietf-system:system/hostname`
|
||||
- `%i`: OS ID, from `/etc/os-release`, from Menuconfig branding
|
||||
- `%h`: Default hostname, from `/etc/os-release`, from branding
|
||||
- `%m`: NIC specific part of base MAC, e.g., to `c0-ff-ee`
|
||||
- `%%`: Literal %
|
||||
|
||||
|
||||
### Static Files
|
||||
|
||||
> **Caveat:** maintaining static a factory-config and failure-config may
|
||||
> seem like an obvious choice, but as YANG models evolve (even the IETF
|
||||
> [!CAUTION]
|
||||
> Maintaining a static `factory-config` and `failure-config` may seem
|
||||
> like an obvious choice, but as YANG models evolve (even the IETF
|
||||
> models get upgraded), you may need to upgrade your static files.
|
||||
|
||||
First, for one-off builds (one image per product), the simplest way is
|
||||
@@ -103,9 +93,8 @@ after `00-probe` has run. The lower case version of the string is used.
|
||||
|
||||
I.e., create a rootfs overlay that provides any combination of:
|
||||
|
||||
- `/usr/share/product/<PRODUCT>/etc/factory-config.cfg`
|
||||
- `/usr/share/product/<PRODUCT>/etc/failure-config.cfg`
|
||||
|
||||
- `/usr/share/product/<PRODUCT>/etc/factory-config.cfg`
|
||||
- `/usr/share/product/<PRODUCT>/etc/failure-config.cfg`
|
||||
|
||||
### Dynamically Generated
|
||||
|
||||
@@ -113,14 +102,14 @@ The generated `factory-config` and `failure-config` files consist of
|
||||
both static JSON files and part generated files at runtime for each
|
||||
device. The resulting files are written to the RAM disk in `/run`:
|
||||
|
||||
- `/run/confd/factory-config.gen`
|
||||
- `/run/confd/failure-config.gen`
|
||||
- `/run/confd/factory-config.gen`
|
||||
- `/run/confd/failure-config.gen`
|
||||
|
||||
Provided no custom overrides (see above) have been installed already,
|
||||
these files are then copied to:
|
||||
|
||||
- `/etc/factory-config.cfg`
|
||||
- `/etc/failure-config.cfg`
|
||||
- `/etc/factory-config.cfg`
|
||||
- `/etc/failure-config.cfg`
|
||||
|
||||
... where the bootstrap process expects them to be in the next step.
|
||||
|
||||
@@ -132,29 +121,33 @@ base hostname, set `BR2_TARGET_GENERIC_HOSTNAME` in your defconfig.
|
||||
The static files are installed by Infix `confd` in `/usr/share/confd/`
|
||||
at build time. It contains two subdirectories:
|
||||
|
||||
/usr/share/confd/
|
||||
|- factory.d/
|
||||
| |- 10-foo.json
|
||||
| |- 10-bar.json
|
||||
| `- 10-qux.json
|
||||
`- failure.d/
|
||||
|- 10-xyzzy.json
|
||||
`- 10-garply.json
|
||||
```
|
||||
/usr/share/confd/
|
||||
|- factory.d/
|
||||
| |- 10-foo.json
|
||||
| |- 10-bar.json
|
||||
| `- 10-qux.json
|
||||
`- failure.d/
|
||||
|- 10-xyzzy.json
|
||||
`- 10-garply.json
|
||||
```
|
||||
|
||||
To override, or extend, these files in you br2-external, set up a rootfs
|
||||
overlay and add it last in `BR2_ROOTFS_OVERLAY`. Your overlay can look
|
||||
something like this:
|
||||
|
||||
./board/common/rootfs/
|
||||
|- etc/
|
||||
| |- confdrc # See below
|
||||
| `- confdrc.local
|
||||
`- usr/
|
||||
`- share/
|
||||
`- confd/
|
||||
|- 10-foo.json # Override Infix foo
|
||||
|- 30-bar.json # Extend, probably 10-bar.json
|
||||
`- 30-fred.json # Extend, your own defaults
|
||||
```
|
||||
./board/common/rootfs/
|
||||
|- etc/
|
||||
| |- confdrc # See below
|
||||
| `- confdrc.local
|
||||
`- usr/
|
||||
`- share/
|
||||
`- confd/
|
||||
|- 10-foo.json # Override Infix foo
|
||||
|- 30-bar.json # Extend, probably 10-bar.json
|
||||
`- 30-fred.json # Extend, your own defaults
|
||||
```
|
||||
|
||||
Using the same filename in your overlay, here `10-foo.json`, completely
|
||||
replaces the contents of the same file provided by Infix. If you just
|
||||
@@ -168,17 +161,17 @@ provide a few custom ones that the `bootstrap` knows about, e.g.,
|
||||
`gen-ifs-custom` that overrides `20-interfaces.json`. See the
|
||||
bootstrap script for more help, and up-to-date information.
|
||||
|
||||
> **Note:** you may not need to provide your own `/etc/confdrc`. The
|
||||
> one installed by `confd` is usually enough. However, if you want to
|
||||
> adjust the behavior of `bootstrap` you may want to override it. There
|
||||
> is also `confdrc.local`, which usually is enough to change arguments
|
||||
> to scripts like `gen-interfaces`, e.g., to create a bridge by default,
|
||||
> you may want to look into `GEN_IFACE_OPTS`.
|
||||
|
||||
> [!TIP]
|
||||
> You may not need to provide your own `/etc/confdrc` for your spin.
|
||||
> The one installed by `confd` is usually enough. However, if you want
|
||||
> to adjust the behavior of `bootstrap` you may want to override it.
|
||||
> There is also `confdrc.local`, which usually is enough to change
|
||||
> arguments to scripts like `gen-interfaces`, e.g., to create a bridge
|
||||
> by default, you may want to look into `GEN_IFACE_OPTS`.
|
||||
|
||||
### Example Snippets
|
||||
|
||||
**IETF System:**
|
||||
#### IETF System
|
||||
|
||||
```hsib
|
||||
"ietf-system:system": {
|
||||
@@ -211,15 +204,16 @@ bootstrap script for more help, and up-to-date information.
|
||||
The `motd-banner` is a binary type, which is basically a Base64 encoded
|
||||
text file without line breaks (`-w0`):
|
||||
|
||||
```bash
|
||||
$ echo "Li0tLS0tLS0uCnwgIC4gLiAgfCBJbmZpeCAtLSBhIE5ldHdvcmsgT3BlcmF0aW5nIFN5c3RlbQp8LS4gdiAuLXwgaHR0cHM6Ly9rZXJuZWxraXQuZ2l0aHViLmlvCictJy0tLSctJwo=" |base64 -d
|
||||
```
|
||||
$ echo "Li0tLS0tLS0uCnwgIC4gLiAgfCBJbmZpeCAtLSBhIE5ldHdvcmsgT3BlcmF0aW5nIFN5c3RlbQp8LS4gdiAuLXwgaHR0cHM6Ly9rZXJuZWxraXQuZ2l0aHViLmlvCictJy0tLSctJwo=" \
|
||||
| base64 -d
|
||||
.-------.
|
||||
| . . | Infix OS — Immutable.Friendly.Secure
|
||||
|-. v .-| https://kernelkit.github.io
|
||||
'-'---'-'
|
||||
```
|
||||
|
||||
**IETF Keystore**
|
||||
#### IETF Keystore
|
||||
|
||||
Notice how both the public and private keys are left empty here, this
|
||||
cause them to be always automatically regenerated after each factory reset.
|
||||
@@ -245,7 +239,7 @@ use-case and not documented here.
|
||||
},
|
||||
```
|
||||
|
||||
**IETF NETCONF Server**
|
||||
#### IETF NETCONF Server
|
||||
|
||||
```json
|
||||
"ietf-netconf-server:netconf-server": {
|
||||
@@ -278,7 +272,8 @@ use-case and not documented here.
|
||||
},
|
||||
```
|
||||
|
||||
**Infix Services**
|
||||
#### Infix Services
|
||||
|
||||
```json
|
||||
"infix-services:ssh": {
|
||||
"enabled": true,
|
||||
@@ -300,10 +295,7 @@ use-case and not documented here.
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
Integration
|
||||
-----------
|
||||
## Integration
|
||||
|
||||
When integrating your software stack with Infix there may be protocols
|
||||
that want to change system settings like hostname and dynamically set
|
||||
@@ -330,7 +322,7 @@ root@infix-00-00-00:~# cat hostnm.xml
|
||||
</system>
|
||||
root@infix-00-00-00:~# edit hostnm.xml
|
||||
root@infix-00-00-00:~# sysrepocfg -Ehostnm.xml
|
||||
root@example:~#
|
||||
root@example:~#
|
||||
```
|
||||
|
||||
Second, perform all changes on `running-config`, the running datastore.
|
||||
@@ -344,8 +336,8 @@ activate all.)
|
||||
|
||||
You can consider the system composed of two entities:
|
||||
|
||||
- NETCONF starts up the system using `startup-config`, then
|
||||
- Hands over control to your application at runtime
|
||||
- NETCONF starts up the system using `startup-config`, then
|
||||
- Hands over control to your application at runtime
|
||||
|
||||
Infix is prepared for this by already having two "runlevels" for these
|
||||
two states. The `startup-config` is applied in runlevel S (bootstrap)
|
||||
@@ -353,15 +345,13 @@ and the system then enters runlevel 2 for normal operation.
|
||||
|
||||
This allow you to keep a set of functionality that is provided by the
|
||||
underlying system, and another managed by your application. You can
|
||||
of course in your br2-external provide a sysrepo plugin that block
|
||||
of course in your br2-external provide a sysrepo plugin that block
|
||||
operations on certain datastores when your application is enabled.
|
||||
E.g., to prevent changes to startup after initial deployment. In
|
||||
that case a proper factory reset would be needed to get back to a
|
||||
"pre-deployment" state where you can reconfigure your baseline.
|
||||
|
||||
|
||||
Releases
|
||||
--------
|
||||
## Releases
|
||||
|
||||
A release build requires the global variable `INFIX_RELEASE` to be set.
|
||||
It can be derived from GIT, if the source tree is kept in GIT VCS. First,
|
||||
@@ -371,14 +361,14 @@ let us talk about versioning in general.
|
||||
|
||||
Two popular scheme for versioning a product derived from Infix:
|
||||
|
||||
1. Track Infix major.minor, e.g. *Foobar v23.08.z*, where `z` is
|
||||
your patch level. I.e., Foobar v23.08.0 could be based on Infix
|
||||
v23.08.0, or v23.08.12, it is up to you. Maybe you based it on
|
||||
v23.08.12 and then back ported changes from v23.10.0, but it was
|
||||
the first release you made to your customer(s).
|
||||
2. Start from v1.0.0 and step the major number every time you sync
|
||||
with a new Infix release, or every time Infix bumps to the next
|
||||
Buildroot LTS.
|
||||
1. Track Infix major.minor, e.g. *Foobar v23.08.z*, where `z` is
|
||||
your patch level. I.e., Foobar v23.08.0 could be based on Infix
|
||||
v23.08.0, or v23.08.12, it is up to you. Maybe you based it on
|
||||
v23.08.12 and then back ported changes from v23.10.0, but it was
|
||||
the first release you made to your customer(s).
|
||||
1. Start from v1.0.0 and step the major number every time you sync
|
||||
with a new Infix release, or every time Infix bumps to the next
|
||||
Buildroot LTS.
|
||||
|
||||
The important thing is to be consistent, not only for your own sake,
|
||||
but also for your end customers. The *major.minor.patch* style is
|
||||
@@ -386,10 +376,10 @@ the most common and often recommended style, which usually maps well
|
||||
to other systems, e.g. PROFINET GSDML files require this (*VX.Y.Z*).
|
||||
But you can of course use only two numbers, *major.minor*, as well.
|
||||
|
||||
> [!WARNING]
|
||||
> What could be confusing, however, is if you use the name *Infix*
|
||||
> with your own versioning scheme.
|
||||
|
||||
|
||||
### Specifying Versioning Information
|
||||
|
||||
Two optional environment variables control the version information
|
||||
@@ -416,4 +406,10 @@ generated file names like disk images, etc.
|
||||
|
||||
**Default:** `${INFIX_BUILD_ID}`
|
||||
|
||||
[NanoPi R2S]: https://github.com/kernelkit/infix/blob/main/board/aarch64/r2s/rootfs/etc/factory-config.cfg
|
||||
[^1]: The base MAC address is defined in the device's Vital Product
|
||||
Data (VPD) EEPROM, or similar, which is used by the kernel to
|
||||
create the system interfaces. This MAC address is usually also
|
||||
printed on a label on the device.
|
||||
|
||||
[NanoPi R2S]: https://github.com/kernelkit/infix/blob/main/board/aarch64/r2s/rootfs/etc/factory-config.cfg
|
||||
[os-release(5)]: https://www.freedesktop.org/software/systemd/man/os-release.html
|
||||
|
||||
+4
-4
@@ -51,9 +51,10 @@ Linux to use.
|
||||
|
||||
### Configure USB port
|
||||
|
||||
> **Note:** You can only configure USB ports known to the system. See
|
||||
> `show hardware` in admin-exec context. (Use `do` prefix in configure
|
||||
> context.)
|
||||
> [!NOTE]
|
||||
> You can only configure USB ports known to the system. See the CLI
|
||||
> command `show hardware` in admin-exec context. (Use `do` prefix in
|
||||
> configure context.)
|
||||
|
||||
```
|
||||
admin@example:/> configure
|
||||
@@ -84,5 +85,4 @@ cached data to disk before returning the prompt:
|
||||
admin@example:~$ sudo umount /media/log
|
||||
```
|
||||
|
||||
|
||||
[1]: https://www.rfc-editor.org/rfc/rfc8348.html
|
||||
|
||||
+38
-35
@@ -8,10 +8,10 @@ details how to log to external media or remote syslog servers.
|
||||
It is also possible to set up the device to act as a syslog server (log
|
||||
sink), this is covered briefly at the very end of this document.
|
||||
|
||||
> **Note:** the native logging cannot be modified, only the log file
|
||||
> rotation can be changed. Please see the `dir` admin-exec command for
|
||||
> a listing of existing native log files.
|
||||
|
||||
> [!NOTE]
|
||||
> The default logging setup in the system cannot be modified, only the
|
||||
> log file rotation. Please see the `dir` admin-exec command for a
|
||||
> listing of existing log files.
|
||||
|
||||
## Log to File
|
||||
|
||||
@@ -21,7 +21,7 @@ an example.
|
||||
|
||||
For a list of available log facilities, see the table in a later section.
|
||||
|
||||
```bash
|
||||
```
|
||||
admin@example:/> configure
|
||||
admin@example:/config/> edit syslog
|
||||
admin@example:/config/syslog/> edit actions log-file file:/media/log/mylog
|
||||
@@ -37,11 +37,11 @@ admin@example:/config/syslog/actions/log-file/file:/media/log/mylog/> leave
|
||||
admin@example:/>
|
||||
```
|
||||
|
||||
> **Note:** the `log-file` syntax requires the leading prefix `file:`.
|
||||
> If the path is not absolute, e.g., `file:mylog`, the file is saved to
|
||||
> the system default path, i.e., `/log/mylog`. In this case, verify
|
||||
> that the filename is not already in use.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> The `log-file` syntax requires the leading prefix `file:`. If the
|
||||
> path is not absolute, e.g., `file:mylog`, the file is saved to the
|
||||
> system default path, i.e., `/log/mylog`. In this case, verify that
|
||||
> the filename is not already in use.
|
||||
|
||||
## Log Rotation
|
||||
|
||||
@@ -51,13 +51,16 @@ disk with outdated logs. A rotated file is saved in stages and older
|
||||
ones are also compressed (using `gzip`). Use the `show log` command in
|
||||
admin-exec context to start the log file viewer:
|
||||
|
||||
admin@example:/config/syslog/> do show log
|
||||
log log.0 log.1.gz log.2.gz log.3.gz log.4.gz log.5.gz
|
||||
admin@example:/config/syslog/> do show log log.1.gz
|
||||
```
|
||||
admin@example:/config/syslog/> do show log
|
||||
log log.0 log.1.gz log.2.gz log.3.gz log.4.gz log.5.gz
|
||||
admin@example:/config/syslog/> do show log log.1.gz
|
||||
```
|
||||
|
||||
> **Tip:** use the Tab key on your keyboard list available log files.
|
||||
> The `do` prefix is also very useful in configure context to access
|
||||
> commands from admin-exec context.
|
||||
> [!TIP]
|
||||
> Use the Tab key on your keyboard list available log files. The `do`
|
||||
> prefix is also very useful in configure context to access commands
|
||||
> from admin-exec context.
|
||||
|
||||
By default 10 compressed older files are saved. Here the oldest is
|
||||
`log.5.gz` and the most recently rotated one is `log.0`.
|
||||
@@ -66,8 +69,8 @@ Log file rotation can be configured both globally and per log file.
|
||||
Here we show the global settings, the set up is the same for per log
|
||||
file, which if unset inherit the global settings:
|
||||
|
||||
```bash
|
||||
admin@example:/> configure
|
||||
```
|
||||
admin@example:/> configure
|
||||
admin@example:/config/> edit syslog file-rotation
|
||||
admin@example:/config/syslog/file-rotation/> show
|
||||
admin@example:/config/syslog/file-rotation/>
|
||||
@@ -76,7 +79,7 @@ admin@example:/config/syslog/file-rotation/>
|
||||
The defaults are not shown. We can inspect them by asking the YANG
|
||||
model for the help texts:
|
||||
|
||||
```bash
|
||||
```
|
||||
admin@example:/config/syslog/file-rotation/> help
|
||||
max-file-size number-of-files
|
||||
admin@example:/config/syslog/file-rotation/> help max-file-size
|
||||
@@ -102,17 +105,16 @@ DEFAULT
|
||||
To change the defaults to something smaller, 512 kiB and 20 (remember
|
||||
everything after .0 is compressed, and text compresses well):
|
||||
|
||||
```bash
|
||||
```
|
||||
admin@example:/config/syslog/file-rotation/> set max-file-size 512
|
||||
admin@example:/config/syslog/file-rotation/> set number-of-files 20
|
||||
admin@example:/config/syslog/file-rotation/> show
|
||||
number-of-files 20;
|
||||
max-file-size 512;
|
||||
admin@example:/config/syslog/file-rotation/> leave
|
||||
admin@example:/>
|
||||
admin@example:/>
|
||||
```
|
||||
|
||||
|
||||
## Log Format
|
||||
|
||||
There are three major syslog log formats, the default is [RFC3164][] for
|
||||
@@ -121,9 +123,11 @@ and remote log server capabilities, or policies, the [RFC5424][] format
|
||||
is often preferred since it not only has better time resolution but also
|
||||
supports structured logging:
|
||||
|
||||
BSD : myproc[8710]: Kilroy was here.
|
||||
RFC3164 : Aug 24 05:14:15 192.0.2.1 myproc[8710]: Kilroy was here.
|
||||
RFC5424 : 2003-08-24T05:14:15.000003-07:00 192.0.2.1 myproc 8710 - - Kilroy was here.
|
||||
```
|
||||
BSD : myproc[8710]: Kilroy was here.
|
||||
RFC3164 : Aug 24 05:14:15 192.0.2.1 myproc[8710]: Kilroy was here.
|
||||
RFC5424 : 2003-08-24T05:14:15.000003-07:00 192.0.2.1 myproc 8710 - - Kilroy was here.
|
||||
```
|
||||
|
||||
The BSD format is only applicable to remote logging. It remains the
|
||||
default for compatibility reasons, and is recommended since the device
|
||||
@@ -132,7 +136,7 @@ perform time stamping at the time of arrival.
|
||||
|
||||
Configuring the log format is the same for log files and remotes:
|
||||
|
||||
```bash
|
||||
```
|
||||
admin@example:/config/> edit syslog actions log-file file:foobar
|
||||
admin@example:/config/syslog/actions/log-file/file:foobar/> set log-format
|
||||
bsd rfc3164 rfc5424
|
||||
@@ -141,9 +145,6 @@ admin@example:/config/syslog/actions/log-file/file:foobar/> leave
|
||||
admin@example:/>
|
||||
```
|
||||
|
||||
[RFC3164]: https://datatracker.ietf.org/doc/html/rfc3164
|
||||
[RFC5424]: https://datatracker.ietf.org/doc/html/rfc5424
|
||||
|
||||
## Log to Remote Server
|
||||
|
||||
Logging to a remote syslog server is the recommended way of supervising
|
||||
@@ -158,7 +159,7 @@ remote syslog servers do not support receiving time stamped log messages
|
||||
-- this is of course entirely dependent on how the remote server is set
|
||||
up, as well as local policy.
|
||||
|
||||
```bash
|
||||
```
|
||||
admin@example:/config/> edit syslog
|
||||
actions file-rotation server
|
||||
admin@example:/config/> edit syslog actions destination moon
|
||||
@@ -172,16 +173,16 @@ admin@example:/config/syslog/actions/destination/moon/> leave
|
||||
admin@example:/>
|
||||
```
|
||||
|
||||
> **Note:** the alternatives shown below each prompt in the example
|
||||
> above can be found by tapping the Tab key.
|
||||
|
||||
> [!TIP]
|
||||
> The alternatives shown below each prompt in the example above can be
|
||||
> found by tapping the Tab key.
|
||||
|
||||
## Acting as a Log Server
|
||||
|
||||
The syslog server can act as a log sink for other devices on a LAN. For
|
||||
this to work you need a static IP address, here we use 10.0.0.1/24.
|
||||
|
||||
```bash
|
||||
```
|
||||
admin@example:/> configure
|
||||
admin@example:/config/> edit syslog server
|
||||
admin@example:/config/syslog/server/> set enabled true
|
||||
@@ -194,7 +195,6 @@ See the above [Log to File](#log-to-file) section on how to set up
|
||||
filtering of received logs to local files. Please note, filtering based
|
||||
on property, e.g., hostname, is not supported yet.
|
||||
|
||||
|
||||
### Facilities
|
||||
|
||||
| **Code** | **Facility** | **Description** |
|
||||
@@ -225,3 +225,6 @@ on property, e.g., hostname, is not supported yet.
|
||||
| 23 | web* | local7, reserved for nginx web server |
|
||||
|
||||
Facilities marked `*` are local augments to the model.
|
||||
|
||||
[RFC3164]: https://datatracker.ietf.org/doc/html/rfc3164
|
||||
[RFC5424]: https://datatracker.ietf.org/doc/html/rfc5424
|
||||
|
||||
+173
-126
@@ -1,19 +1,16 @@
|
||||
Regression Testing with Infamy
|
||||
==============================
|
||||
# Regression Testing with Infamy
|
||||
|
||||
Infix comes with a test suite that is intended to provide end-to-end
|
||||
verification of supported features. Generally speaking, this means
|
||||
that one or more DUTs are configured over NETCONF; the resulting
|
||||
verification of supported features. Generally speaking, this means that
|
||||
one or more DUTs are configured over NETCONF or RESTCONF; the resulting
|
||||
network is then black-box tested by injecting and inspecting network
|
||||
traffic at various points.
|
||||
|
||||
This document is intended to be a practical guide on how to run,
|
||||
develop and debug tests. There is a separate document describing the
|
||||
[Test System Architecture](test-arch.md).
|
||||
This document is intended to be a practical guide on how to run, develop
|
||||
and debug tests. There is a separate document describing the [Test
|
||||
System Architecture](test-arch.md).
|
||||
|
||||
|
||||
Modes of Testing
|
||||
----------------
|
||||
## Modes of Testing
|
||||
|
||||
### Virtual Devices
|
||||
|
||||
@@ -21,23 +18,33 @@ By default, tests are run on a topology made up of virtual Infix nodes
|
||||
using [Qeneth][]. To run the full regression test suite: build Infix
|
||||
for `x86_64`:
|
||||
|
||||
$ make x86_64_defconfig
|
||||
$ make
|
||||
$ make test
|
||||
```
|
||||
$ make x86_64_defconfig
|
||||
...
|
||||
$ make
|
||||
...
|
||||
$ make test
|
||||
...
|
||||
```
|
||||
|
||||
### Physical Devices
|
||||
|
||||
To run the tests on a preexisting topology from the host's network
|
||||
namespace, specify the `host` `TEST_MODE`:
|
||||
|
||||
$ make TEST_MODE=host test
|
||||
```
|
||||
$ make TEST_MODE=host test
|
||||
...
|
||||
```
|
||||
|
||||
This typically used when testing on physical hardware. By default the
|
||||
topology will be sourced from `/etc/infamy.dot`, but this can be
|
||||
overwritten by setting the `TOPOLOGY` variable:
|
||||
|
||||
$ make TEST_MODE=host TOPOLOGY=~/my-topology.dot test
|
||||
|
||||
```
|
||||
$ make TEST_MODE=host TOPOLOGY=~/my-topology.dot test
|
||||
...
|
||||
```
|
||||
|
||||
### `make run` Devices
|
||||
|
||||
@@ -48,26 +55,29 @@ instance is configured to use TAP networking.
|
||||
When the instance is running, you can open a separate terminal and run
|
||||
the subset of the test suite that can be mapped to it:
|
||||
|
||||
$ make TEST_MODE=run test
|
||||
```
|
||||
$ make TEST_MODE=run test
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
Interactive Usage
|
||||
-----------------
|
||||
## Interactive Usage
|
||||
|
||||
When developing and debugging tests, the overhead of repeatedly
|
||||
setting up and tearing down the test environment can quickly start to
|
||||
weigh you down. In these situation, you can start an interactive test
|
||||
environment:
|
||||
|
||||
$ make test-sh
|
||||
Info: Generating topology
|
||||
Info: Generating node YAML
|
||||
Info: Generating executables
|
||||
Info: Launching dut1
|
||||
Info: Launching dut2
|
||||
Info: Launching dut3
|
||||
Info: Launching dut4
|
||||
11:42:52 infamy0:test #
|
||||
```
|
||||
$ make test-sh
|
||||
Info: Generating topology
|
||||
Info: Generating node YAML
|
||||
Info: Generating executables
|
||||
Info: Launching dut1
|
||||
Info: Launching dut2
|
||||
Info: Launching dut3
|
||||
Info: Launching dut4
|
||||
11:42:52 infamy0:test #
|
||||
```
|
||||
|
||||
The example above uses the default mode (`qeneth`), but the `host` and
|
||||
`run` modes also support the `test-sh` target.
|
||||
@@ -77,22 +87,27 @@ inside the container running Infamy. It's a very limited environment,
|
||||
but it has enough to easily run single tests, connect to the virtual
|
||||
devices, and *step* your code.
|
||||
|
||||
|
||||
### Running Subsets of Tests
|
||||
|
||||
Each test case is a separate executable, which can be run without
|
||||
arguments:
|
||||
|
||||
11:42:53 infamy0:test # ./case/infix_dhcp/dhcp_basic.py
|
||||
```
|
||||
11:42:53 infamy0:test # ./case/infix_dhcp/dhcp_basic.py
|
||||
```
|
||||
|
||||
To run a suite of tests, e.g., only the DHCP client tests, pass the
|
||||
suite as an argument to [9PM][]:
|
||||
|
||||
11:42:53 infamy0:test # ./9pm/9pm.py case/infix_dhcp/infix_dhcp.yaml
|
||||
```
|
||||
11:42:53 infamy0:test # ./9pm/9pm.py case/infix_dhcp/infix_dhcp.yaml
|
||||
```
|
||||
|
||||
To run the suite of all tests:
|
||||
|
||||
11:42:53 infamy0:test # ./9pm/9pm.py case/all.yaml
|
||||
```
|
||||
11:42:53 infamy0:test # ./9pm/9pm.py case/all.yaml
|
||||
```
|
||||
|
||||
### Connecting to Infamy
|
||||
|
||||
@@ -100,65 +115,76 @@ The test system runs in a Docker container, so to get a shell prompt in
|
||||
*another terminal* you need to connect to that container. Infamy comes
|
||||
with a helper script for this:
|
||||
|
||||
$ ./test/shell
|
||||
11:42:53 infamy0:test #
|
||||
```
|
||||
$ ./test/shell
|
||||
11:42:53 infamy0:test #
|
||||
```
|
||||
|
||||
By default it connect to the latest started Infamy instance. If you for
|
||||
some reason run multiple instances of Infamy the `shell` script takes an
|
||||
optional argument "system", which is the hostname of the container you
|
||||
want to connect to:
|
||||
|
||||
$ ./test/shell infamy2
|
||||
11:42:53 infamy2:test #
|
||||
|
||||
```
|
||||
$ ./test/shell infamy2
|
||||
11:42:53 infamy2:test #
|
||||
```
|
||||
|
||||
### Connecting to a DUT
|
||||
|
||||
> [!TIP]
|
||||
> The `console` script uses `telnet` to connect to a port forwarded to
|
||||
> `localhost` by the Docker container. To exit Telnet, use Ctrl-] and
|
||||
> then 'q' followed by enter. This can be customized in `~/.telnetrc`
|
||||
|
||||
All DUTs in a virtual Infamy topology are emulated in Qemu instances
|
||||
managed by qeneth. If you want to watch what's happening on one of the
|
||||
target systems, e.g., tail a log file or run `tcpdump` during the test,
|
||||
there is another helper script in Infamy for this:
|
||||
|
||||
$ ./test/console 1
|
||||
Trying 127.0.0.1...
|
||||
Connected to 127.0.0.1.
|
||||
```
|
||||
$ ./test/console 1
|
||||
Trying 127.0.0.1...
|
||||
Connected to 127.0.0.1.
|
||||
|
||||
Infix OS — Immutable.Friendly.Secure v23.11.0-226-g0c144da (console)
|
||||
infix-00-00-00 login: admin
|
||||
Password:
|
||||
.-------.
|
||||
| . . | Infix OS — Immutable.Friendly.Secure
|
||||
|-. v .-| https://kernelkit.org
|
||||
'-'---'-'
|
||||
Infix OS — Immutable.Friendly.Secure v23.11.0-226-g0c144da (console)
|
||||
infix-00-00-00 login: admin
|
||||
Password:
|
||||
.-------.
|
||||
| . . | Infix OS — Immutable.Friendly.Secure
|
||||
|-. v .-| https://kernelkit.org
|
||||
'-'---'-'
|
||||
|
||||
Run the command 'cli' for interactive OAM
|
||||
Run the command 'cli' for interactive OAM
|
||||
|
||||
admin@infix-00-00-00:~$
|
||||
admin@infix-00-00-00:~$
|
||||
```
|
||||
|
||||
From here we can observe `dut1` freely while running tests.
|
||||
|
||||
> The `console` script uses `telnet` to connect to a port forwarded to
|
||||
> `localhost` by the Docker container. To exit Telnet, use Ctrl-] and
|
||||
> then 'q' followed by enter. This can be customized in `~/.telnetrc`
|
||||
|
||||
Like the `shell` script, `console` takes an optional "system" argument
|
||||
in case you run multiple instances of Infamy:
|
||||
|
||||
$ ./test/console 1 infamy2
|
||||
```
|
||||
$ ./test/console 1 infamy2
|
||||
...
|
||||
```
|
||||
|
||||
You can also connect to the console of a DUT from within a `shell`:
|
||||
|
||||
$ ./test/shell
|
||||
11:42:54 infamy0:test # qeneth status
|
||||
11:42:54 infamy0:test # qeneth console dut1
|
||||
login: admin
|
||||
password: *****
|
||||
admin@infix-00-00-00:~$
|
||||
|
||||
> **Note:** disconnect from the qeneth console by using bringing up the
|
||||
> old Telnet "menu" using Ctrl-], compared to standard Telnet this is
|
||||
> the BusyBox version so you press 'e' + enter instead of 'q' to quit.
|
||||
```
|
||||
$ ./test/shell
|
||||
11:42:54 infamy0:test # qeneth status
|
||||
11:42:54 infamy0:test # qeneth console dut1
|
||||
login: admin
|
||||
password: *****
|
||||
admin@infix-00-00-00:~$
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> Disconnect from the qeneth console by bringing up the old Telnet
|
||||
> "menu" using Ctrl-], compared to standard Telnet this is the BusyBox
|
||||
> version so you press 'e' + enter instead of 'q' to quit.
|
||||
|
||||
### Attaching to MACVLANs
|
||||
|
||||
@@ -171,27 +197,32 @@ interactively inject traffic into the test setup.
|
||||
|
||||
First, connect to the Infamy instance in question:
|
||||
|
||||
$ ./test/shell
|
||||
11:43:19 infamy0:test #
|
||||
```
|
||||
$ ./test/shell
|
||||
11:43:19 infamy0:test #
|
||||
```
|
||||
|
||||
Then, attach to the MACVLAN namespace by running the `nsenter` helper
|
||||
script from the test directory, supplying the base interface name as
|
||||
the first argument:
|
||||
|
||||
11:43:19 infamy0:test # ./nsenter d1b
|
||||
11:43:20 infamy0(d1b):test #
|
||||
```
|
||||
11:43:19 infamy0:test # ./nsenter d1b
|
||||
11:43:20 infamy0(d1b):test #
|
||||
```
|
||||
|
||||
By default, an interactive shell is started, but you can also supply
|
||||
another command:
|
||||
|
||||
11:43:19 infamy0:test # ./nsenter d1b ip -br addr
|
||||
lo UNKNOWN 127.0.0.1/8 ::1/128
|
||||
iface@if7 UP 10.0.0.1/24 fe80::38d0:88ff:fe77:b7cd/64
|
||||
```
|
||||
11:43:19 infamy0:test # ./nsenter d1b ip -br addr
|
||||
lo UNKNOWN 127.0.0.1/8 ::1/128
|
||||
iface@if7 UP 10.0.0.1/24 fe80::38d0:88ff:fe77:b7cd/64
|
||||
```
|
||||
|
||||
You can now freely debug the network activity of your test and the
|
||||
responses from the DUT.
|
||||
|
||||
|
||||
### Using the Python Debugger
|
||||
|
||||
The built in `breakpoint()` function in Python is very useful when you
|
||||
@@ -202,13 +233,15 @@ Simply insert a call to `breakpoint()` at the point of interest in
|
||||
your test and run it as normal. Once Python executes the call, it will
|
||||
drop you into the Python debugger:
|
||||
|
||||
11:42:58 infamy0:test # ./case/infix_dhcp/dhcp_basic.py
|
||||
# Starting (2024-02-10 11:42:59)
|
||||
# Probing dut1 on port d1a for IPv6LL mgmt address ...
|
||||
# Connecting to mgmt IP fe80::ff:fe00:0%d1a:830 ...
|
||||
ok 1 - Initialize
|
||||
> /home/jocke/src/infix/test/case/infix_dhcp/dhcp_basic.py(44)<module>()
|
||||
(Pdb)
|
||||
```
|
||||
11:42:58 infamy0:test # ./case/infix_dhcp/dhcp_basic.py
|
||||
# Starting (2024-02-10 11:42:59)
|
||||
# Probing dut1 on port d1a for IPv6LL mgmt address ...
|
||||
# Connecting to mgmt IP fe80::ff:fe00:0%d1a:830 ...
|
||||
ok 1 - Initialize
|
||||
> /home/jocke/src/infix/test/case/infix_dhcp/dhcp_basic.py(44)<module>()
|
||||
(Pdb)
|
||||
```
|
||||
|
||||
At this point you have full access to the test's state, but it is also
|
||||
an opportunity to inspect the state of the DUTs (e.g. via their
|
||||
@@ -218,8 +251,9 @@ It is also possible to run a test under Pdb from the get-go, if you
|
||||
want to setup breakpoints without modifying the source, or simply step
|
||||
through the code:
|
||||
|
||||
11:42:58 infamy0:test # python -m pdb case/infix_dhcp/dhcp_basic.py
|
||||
|
||||
```
|
||||
11:42:58 infamy0:test # python -m pdb case/infix_dhcp/dhcp_basic.py
|
||||
```
|
||||
|
||||
### Deterministic Topology Mappings
|
||||
|
||||
@@ -230,34 +264,40 @@ between phyical nodes. In such cases, supplying a `PYTHONHASHSEED`
|
||||
variable (set to any 32-bit unsigned integer) when launching the test
|
||||
environment will make sure that topology mappings are deterministic:
|
||||
|
||||
$ make PYTHONHASHSEED=0 test-sh
|
||||
```
|
||||
$ make PYTHONHASHSEED=0 test-sh
|
||||
```
|
||||
|
||||
If a seed is not supplied, a random value is chosen. This seed is
|
||||
logged by the `meta/reproducible.py` test case when running a test
|
||||
suite:
|
||||
|
||||
$ make test
|
||||
Info: Generating topology
|
||||
Info: Generating node YAML
|
||||
Info: Generating executables
|
||||
Info: Launching dut1
|
||||
Info: Launching dut2
|
||||
Info: Launching dut3
|
||||
Info: Launching dut4
|
||||
9PM - Simplicity is the ultimate sophistication
|
||||
```
|
||||
$ make test
|
||||
Info: Generating topology
|
||||
Info: Generating node YAML
|
||||
Info: Generating executables
|
||||
Info: Launching dut1
|
||||
Info: Launching dut2
|
||||
Info: Launching dut3
|
||||
Info: Launching dut4
|
||||
9PM - Simplicity is the ultimate sophistication
|
||||
|
||||
Starting test 0002-reproducible.py
|
||||
2024-05-03 10:40:30 # Starting (2024-05-03 10:40:30)
|
||||
2024-05-03 10:40:30 # Specify PYTHONHASHSEED=3773822171 to reproduce this test environment
|
||||
2024-05-03 10:40:30 ok 1 - $PYTHONHASHSEED is set
|
||||
2024-05-03 10:40:30 # Exiting (2024-05-03 10:40:30)
|
||||
2024-05-03 10:40:30 1..1
|
||||
...
|
||||
Starting test 0002-reproducible.py
|
||||
2024-05-03 10:40:30 # Starting (2024-05-03 10:40:30)
|
||||
2024-05-03 10:40:30 # Specify PYTHONHASHSEED=3773822171 to reproduce this test environment
|
||||
2024-05-03 10:40:30 ok 1 - $PYTHONHASHSEED is set
|
||||
2024-05-03 10:40:30 # Exiting (2024-05-03 10:40:30)
|
||||
2024-05-03 10:40:30 1..1
|
||||
...
|
||||
```
|
||||
|
||||
This is useful because this value can then be used to rerun a test (or
|
||||
the whole suite) with identical topology mappings:
|
||||
|
||||
$ make PYTHONHASHSEED=3773822171 TESTS=case/ietf_system/hostname.py test
|
||||
```
|
||||
$ make PYTHONHASHSEED=3773822171 TESTS=case/ietf_system/hostname.py test
|
||||
```
|
||||
|
||||
### Deterministic Transport Protocol
|
||||
|
||||
@@ -266,13 +306,16 @@ chosen randomly. If you supply a `PYTHONHASHSEED` as described above,
|
||||
you get the same protocol used for that hash. But if you want to choose
|
||||
the protocol, add extra arguments to Infamy:
|
||||
|
||||
$ make INFAMY_EXTRA_ARGS="--transport=restconf" TESTS=case/ietf_system/hostname.py test
|
||||
```
|
||||
$ make INFAMY_EXTRA_ARGS="--transport=restconf" TESTS=case/ietf_system/hostname.py test
|
||||
```
|
||||
|
||||
or, when running interactively:
|
||||
|
||||
$ make test-sh
|
||||
09:08:17 infamy0:test # ./9pm/9pm.py -o"--transport=restconf" case/ietf_system/hostname.py
|
||||
|
||||
```
|
||||
$ make test-sh
|
||||
09:08:17 infamy0:test # ./9pm/9pm.py -o"--transport=restconf" case/ietf_system/hostname.py
|
||||
```
|
||||
|
||||
### Test specification
|
||||
|
||||
@@ -287,13 +330,13 @@ for example:
|
||||
```python
|
||||
test.step("Set IPv4 address on the interface dut:cross")
|
||||
test.step("Verify the IP address is set on dut:cross")
|
||||
|
||||
```
|
||||
where dut, cross and the IPv4 address could be found inside the
|
||||
|
||||
where *dut*, *cross* and the IPv4 address could be found inside the
|
||||
logical topology for the test.
|
||||
|
||||
Each test case must begin with the test name followed by a empty line and
|
||||
the description of the test. For example:
|
||||
Each test case must begin with the test name followed by a empty line
|
||||
and the description of the test. For example:
|
||||
|
||||
```
|
||||
"""
|
||||
@@ -305,32 +348,35 @@ Verify that it it possible to change hostname
|
||||
|
||||
The test specifaction can be genererated with:
|
||||
|
||||
$ make test-spec
|
||||
```
|
||||
$ make test-spec
|
||||
...
|
||||
```
|
||||
|
||||
### Test Development
|
||||
|
||||
For adding a new test to the automated regression test suite, it's best
|
||||
to start by reviewing an existing test case.
|
||||
|
||||
All tests are located in the `infix/test/case` repository and are
|
||||
grouped by the features they verify. For example,
|
||||
`infix/test/case/infix_services` contains tests for various Infix
|
||||
All tests are located in the `infix/test/case` repository and are
|
||||
grouped by the features they verify. For example,
|
||||
`infix/test/case/infix_services` contains tests for various Infix
|
||||
services, such as LLDP and mDNS.
|
||||
|
||||
While test grouping is flexible, each test should be placed in a
|
||||
While test grouping is flexible, each test should be placed in a
|
||||
logically relevant category.
|
||||
|
||||
When creating a new test group, add it to `infix/test/case/all.yaml`,
|
||||
to enable it to run as a
|
||||
[subset of the test suite](#running-subsets-of-tests):
|
||||
When creating a new test group, add it to `infix/test/case/all.yaml`, to
|
||||
enable it to run as a [subset of the test
|
||||
suite](#running-subsets-of-tests):
|
||||
|
||||
```
|
||||
- name: infix-services
|
||||
suite: infix_services/infix_services.yaml
|
||||
```
|
||||
|
||||
A new test (e.g., lldp_enable_disable) should be added to the
|
||||
corresponding test group .yaml file, such as
|
||||
A new test (e.g., lldp_enable_disable) should be added to the
|
||||
corresponding test group .yaml file, such as
|
||||
`infix/test/cases/infix_services.yaml`:
|
||||
|
||||
```
|
||||
@@ -338,21 +384,22 @@ corresponding test group .yaml file, such as
|
||||
case: lldp_enable_disable/test.py
|
||||
```
|
||||
|
||||
It is necessary to include the test in
|
||||
`infix/test/case/infix_services/Readme.adoc` to ensure proper test
|
||||
It is necessary to include the test in
|
||||
`infix/test/case/infix_services/Readme.adoc` to ensure proper test
|
||||
specification generation:
|
||||
|
||||
```
|
||||
include::lldp_enable_disable/Readme.adoc[]
|
||||
```
|
||||
|
||||
Each test case should have its own directory under,
|
||||
`infix/test/case/infix_services`, containing:
|
||||
- `test.py` - the test script
|
||||
- `topology.dot` - the logical topology definition.
|
||||
Each test case should have its own directory under,
|
||||
`infix/test/case/infix_services`, containing:
|
||||
|
||||
When the [test specification](#test-specification) is generated,
|
||||
`topology.svg` and `Readme.adoc` should also be created.
|
||||
- `test.py` - the test script
|
||||
- `topology.dot` - the logical topology definition.
|
||||
|
||||
When the [test specification](#test-specification) is generated,
|
||||
`topology.svg` and `Readme.adoc` should also be created.
|
||||
|
||||
[9PM]: https://github.com/rical/9pm
|
||||
[Qeneth]: https://github.com/wkz/qeneth
|
||||
|
||||
+15
-10
@@ -9,33 +9,38 @@ These images also work with the Graphical Network Simulator ([GNS3][]),
|
||||
which is a very user-friendly tool for playing around with simple to
|
||||
complex network setups, verifying interoperability between vendors, etc.
|
||||
|
||||
|
||||
QEMU
|
||||
----
|
||||
|
||||
> **Note:** installation for Debian/Ubuntu based systems: <kbd>sudo apt
|
||||
> install virt-manager</kbd> -- dependencies ensure the relevant Qemu
|
||||
> packages are pulled in as well. Installing [virt-manager][virt] helps
|
||||
> set up Qemu networking on your system.
|
||||
> [!TIP]
|
||||
> Installation for Debian/Ubuntu based systems can be done by "simply":
|
||||
> <kbd>sudo apt install virt-manager</kbd> -- dependencies ensure the
|
||||
> relevant Qemu packages are pulled in as well. This trick, installing
|
||||
> [virt-manager][virt], helps set up Qemu networking on your system.
|
||||
|
||||
A virtualized Infix x86_64 instance can easily be launched from a Linux
|
||||
system, with [Qemu][] installed, by issuing:
|
||||
|
||||
./qemu.sh
|
||||
```
|
||||
$ ./qemu.sh
|
||||
...
|
||||
```
|
||||
|
||||
from an unpacked [release tarball][rels]. From a built source tree of
|
||||
Infix the same functionality is bundled as:
|
||||
|
||||
make run
|
||||
```
|
||||
$ make run
|
||||
...
|
||||
```
|
||||
|
||||
To change settings, e.g. networking, <kbd>make run-menuconfig</kbd>, or
|
||||
from a pre-built Infix release tarball, using <kbd>./qemu.sh -c</kbd>
|
||||
|
||||
The Infix test suite is built around Qemu and [Qeneth][qeth], see:
|
||||
|
||||
* [Regression Testing with Infamy](testing.md)
|
||||
* [Infamy Docker Image](https://github.com/kernelkit/infix/blob/main/test/docker/README.md)
|
||||
|
||||
* [Regression Testing with Infamy](testing.md)
|
||||
* [Infamy Docker Image](https://github.com/kernelkit/infix/blob/main/test/docker/README.md)
|
||||
|
||||
GNS3
|
||||
----
|
||||
|
||||
Reference in New Issue
Block a user