doc: minor markdownlint cleanup and fixes to admonitions

[skip ci]

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2025-08-08 15:21:49 +02:00
parent ce89499518
commit f02048e904
6 changed files with 455 additions and 403 deletions
+137 -136
View File
@@ -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.
![System boot flowchart](img/fail-secure.svg)
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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
----