Skip to content

Commit 874085e

Browse files
committed
Introduce TOML-based configuration to replace data.py
The previous data.py approach had several shortcomings: - No base configuration: users had to start from scratch or copy data.py-dist - No environment management: working with multiple infra required to manually copy the data.py files - No clear default and customization separation: the data.py, once copied from data.py-dist and customized, was mixing the defaults and the customizations The new TOML config system addresses this by: - Providing a base config.toml maintained by the project with sensible defaults, which can be updated without user action - Supporting per-environment override files (config.<name>.toml) so users only specify what differs from the defaults, making their customizations explicit Options have been grouped by sections for clarity. Configuration sections and their contents: - [host]: default XAPI credentials (user, password) - [hosts]: per-host credential overrides - [network]: management network name - [pxe]: PXE server settings (arp_server) - [vm]: VM settings (default_sr, images, equivalents, def_url) - [install]: installer settings (answerfiles, iso_remaster, isos) - [guest_tools]: guest tools packages and installer ISOs (win, other, installed, download_url) - [ssh]: SSH client settings (pubkey, output_max_lines, ignore_banner) - [storage]: storage backend configs (nfs, nfs4, nfs_iso, cifs_iso, cephfs, moosefs, lvmoiscsi) - Root keys: objects_name_prefix, dns_server Also includes config-schema.json, lib/config_loader.py (Pydantic models), and scripts/migrate_data_py.py to help users migrate from data.py. Signed-off-by: Gaëtan Lehmann <gaetan.lehmann@vates.tech>
1 parent 435faac commit 874085e

20 files changed

Lines changed: 663 additions & 157 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ data.py
44
vm_data.py
55
/scripts/guests/windows/id_rsa.pub
66
.envrc
7+
# Config overrides (user environment-specific)
8+
config.*.toml

Makefile

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,5 @@
55

66
all: ruff autopep8 flake8 mypy pyright
77

8-
data.py:
9-
@test -r data.py || echo "File 'data.py' does not exist. Refer to https://github.com/xcp-ng/xcp-ng-tests#configuration." && exit 1
10-
11-
mypy pyright ruff: data.py
12-
138
ruff autopep8 flake8 mypy pyright:
149
uv run prek -a $@

README.md

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -147,10 +147,68 @@ For Guest UEFI Secure Boot tests, the requirements are:
147147
* `efitools` for uefistored (in 8.2) or varstored (in 8.3+) auth var tests
148148
* `util-linux` for uefistored (in 8.2) or varstored (in 8.3+) auth var tests in Alpine VMs
149149

150-
Many tests have specific requirements, detailed in a comment at the top of the test file: minimal number of hosts in a pool, number of pools, VMs with specific characteristics (OS, BIOS vs UEFI, additional tools installed in the VM, additional networks in the pool, presence of an unused disk on one host or every host...). Markers, jobs defined in `jobs.py` (`./jobs.py show JOBNAME` will display the requirements and the reference to a VM or VM group), VMs and VM groups defined in `vm-data.py-dist` may all help understanding what tests can run with what VMs.
150+
Many tests have specific requirements, detailed in a comment at the top of the test file: minimal number of hosts in a pool, number of pools, VMs with specific characteristics (OS, BIOS vs UEFI, additional tools installed in the VM, additional networks in the pool, presence of an unused disk on one host or every host...). Markers, jobs defined in `jobs.py` (`./jobs.py show JOBNAME` will display the requirements and the reference to a VM or VM group), VMs and VM groups defined in `vm_data.py` may all help understanding what tests can run with what VMs.
151151

152152
## Configuration
153-
The main configuration file is data.py. Copy data.py-dist to data.py and modify it if needed.
153+
154+
### Using config.toml
155+
156+
Test configuration is managed via TOML files. The project includes a `config.toml` file with all default settings.
157+
158+
#### Default configuration
159+
160+
The `config.toml` file in the repository contains all default settings for:
161+
- Host SSH credentials and per-host overrides
162+
- Network definitions
163+
- PXE server configuration
164+
- VM images and ISO definitions
165+
- Storage device configurations (NFS, CIFS, CephFS, MooseFS, LVM iSCSI)
166+
- Guest tools (Windows, other)
167+
- Test utilities and SSH keys
168+
169+
#### Custom configuration
170+
171+
To customize settings for your environment:
172+
173+
1. **Create a custom config file** (e.g., `config.local.toml`):
174+
```toml
175+
[host]
176+
default_user = "root"
177+
default_password = "your-password"
178+
179+
[host.per_host]
180+
"192.168.1.10" = { user = "custom_user", password = "custom_pass" }
181+
182+
[pxe]
183+
config_server = "pxe.example.com"
184+
arp_server = "pxe.example.com"
185+
186+
[storage.nfs]
187+
server = "10.0.0.2"
188+
serverpath = "/mnt/shared"
189+
```
190+
191+
2. **Run tests with custom config**:
192+
```bash
193+
pytest --config=local --hosts=10.0.0.1
194+
```
195+
196+
This loads `config.toml` first, then merges `config.local.toml` on top.
197+
198+
#### Configuration file paths
199+
200+
- `config.toml` — default config (always loaded)
201+
- `config.default.toml` — local defaults (auto-loaded if exists and no `--config` specified)
202+
- `config.NAME.toml` — environment-specific overrides (loaded with `--config=NAME`)
203+
204+
The `--config` flag is optional. If not specified:
205+
1. `config.toml` is loaded first
206+
2. If `config.default.toml` exists, it is merged on top (auto-detected)
207+
208+
This allows you to:
209+
- Commit `config.toml` with project defaults to version control
210+
- Create `config.default.toml` locally (ignored by git) for your standard environment
211+
- Create `config.prod.toml`, `config.ci.toml`, etc. for other environments and select with `--config=prod`
154212

155213
## Running tests
156214

@@ -169,8 +227,8 @@ The `--hosts` parameter can be specified several times. Then `pytest` will run t
169227
When a test requires a single pool of several hosts, only mention the master host in the `--hosts` option.
170228

171229
Some tests accept an optional `--vm=OVA_URL|VM_key|IP_address` parameter. Those are tests that will import a VM before testing stuff on it:
172-
* `OVA_URL` is a URL to download an OVA. It can also be simply a filename, if your `data.py`'s `DEF_VM_URL` is correctly defined.
173-
* `VM_key` refers to a key in `data.py`'s `VM_IMAGES` dict. Example: `mini-linux-x86_64-uefi`.
230+
* `OVA_URL` is a URL to download an OVA. It can also be simply a filename, if your `config.toml`'s `vm.def_url` is correctly defined.
231+
* `VM_key` refers to a key in `config.toml`'s `vm.images` section. Example: `mini-linux-x86_64-uefi`.
174232
* `IP_address` allows you to reuse an existing running VM, skipping the whole import, start, wait for VM to be up setup. Can be useful as a development tool. Some tests that accept `--vm` do not support it.
175233
If `--vm` is not specified, defaults defined by the tests will be used.
176234
The `--vm` parameter can be specified several times. Then pytest will run several instances of the tests sequentially, one for each VM.
@@ -237,8 +295,6 @@ We wanted the job definitions to be in this git repository, that's why the job d
237295

238296
To use `./jobs.py`, you also need to populate `vm_data.py` to define the VM groups that are necessary to run jobs (unless `--vm` is provided on the command line to override the defaults).
239297

240-
The output of commands below is given as example and may not reflect the current state of the jobs definitions.
241-
242298
#### List jobs
243299
```
244300
$ ./jobs.py list
@@ -600,10 +656,10 @@ python scripts/test_install_xcpng.py 10.0.0.2 f0f5f010-80c6-25ae-44a2-1fb154e32d
600656
```
601657
Note: in case of restore, the version must be that of the installer (here 8.2.1), not the version of XCP-ng that will be restored.
602658
603-
The script requires the addressable name or IP of the PXE config server to be defined in `data.py`:
604-
```
605-
# PXE config server for automated XCP-ng installation
606-
PXE_CONFIG_SERVER = 'pxe'
659+
The script requires the addressable name or IP of the PXE config server to be defined in `config.toml`:
660+
```toml
661+
[pxe]
662+
config_server = "pxe"
607663
```
608664

609665
The `installer` parameter is optional. If you leave it empty it will be automatically defined as `http://<PXE_CONFIG_SERVER>/installers/xcp-ng/<version>/`.

config.toml

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
# Configuration file for XCP-ng tests
2+
# To be customized for your environment
3+
4+
# The following prefix will be added to the `name-label` parameter of XAPI objects
5+
# that the tests will create or import, such as VMs and SRs.
6+
# Default value: [your login/user]
7+
# Prefix for test-created objects in XAPI (empty = use username)
8+
objects_name_prefix = ""
9+
10+
# This should be a working DNS server that's not used by any VM images.
11+
dns_server = "1.1.1.1"
12+
13+
# Default size of VDIs created for storage tests.
14+
volume_size = "1 GiB"
15+
16+
# Maximum amount of data written to a volume during storage tests.
17+
write_volume_cap = "2 GiB"
18+
19+
[host]
20+
# Default user and password to connect to a host through XAPI
21+
# Note: this won't be used for SSH.
22+
# You need to have an SSH key into the hosts' /root/.ssh/authorized_keys.
23+
default_user = "root"
24+
default_password = ""
25+
26+
[hosts]
27+
# Override settings for specific hosts
28+
# skip_xo_config allows to not touch XO's configuration regarding the host
29+
# Else the default behaviour is to add the host to XO servers at the beginning
30+
# of the testing session and remove it at the end.
31+
# "10.0.0.1" = { user = "root", password = "" }
32+
# "testhost1" = { user = "root", password = "", skip_xo_config = true }
33+
34+
[network]
35+
# Network names and descriptions
36+
mgmt = "Pool-wide network associated with eth0"
37+
38+
[pxe]
39+
# PXE configuration server for automated XCP-ng installation
40+
config_server = "pxe"
41+
# Server on MGMT network, where ARP tables can reveal the MACs
42+
arp_server = "pxe"
43+
44+
[vm]
45+
# Default VM images location
46+
# Values can be either full URLs or only partial URLs that will be automatically appended to def_url
47+
def_url = "http://pxe/images/"
48+
49+
# Whether to cache VMs on the test host, that is import them only if not already
50+
# present in the target SR. This also causes the VM to be cloned at the beginning
51+
# of each test module, so that the original VM remains untouched.
52+
# /!\ The VM identifier in cache is simply the URL where it was imported from.
53+
# No checksum or date is checked.
54+
# A cached VM is just a VM which has a special description.
55+
# Example description: "[Cache for http://example.com/images/filename.xva]"
56+
# Delete the VM to remove it from cache.
57+
# This setting affects VMs managed by the `imported_vm` fixture.
58+
cache_imported = false
59+
60+
# In some cases, we may prefer to favour a local SR to store test VM disks,
61+
# to avoid latency or unstabilities related to network or shared file servers.
62+
# However it's not good practice to make a local SR the default SR for a pool of several hosts.
63+
# Hence this configuration value that you can set to `local` so that our tests use this SR by default.
64+
# This setting affects VMs managed by the `imported_vm` fixture.
65+
# Possible values:
66+
# - 'default': keep using the pool's default SR
67+
# - 'local': use the first local SR found instead
68+
# - A UUID of the SR to be used
69+
default_sr = "default"
70+
71+
[vm.images]
72+
# VM image definitions: name -> filename or URL
73+
# Values can be either full URLs or only partial URLs appended to vm.def_url
74+
"mini-linux-x86_64-bios" = "alpine-minimal-3.12.0.xva"
75+
"mini-linux-x86_64-uefi" = "alpine-uefi-minimal-3.12.0.xva"
76+
77+
[vm.equivalents]
78+
# Image equivalences for caching/deduplication
79+
# Maps test image IDs to other IDs they can be substituted with
80+
# "test_id_1" = "test_id_2"
81+
82+
[install]
83+
# Installation configuration
84+
# Path to the iso-remaster utility script
85+
# iso_remaster = "/home/user/src/xcpng/xcp/scripts/iso-remaster/iso-remaster.sh"
86+
87+
[install.answerfiles]
88+
# Base answer files for installation
89+
# Password hash is computed from host.default_password at config load time
90+
# When used, <PASSWORD_HASH> placeholder will be replaced with the actual hash
91+
INSTALL = { TAG = "installation", CONTENTS = [
92+
{ TAG = "root-password", type = "hash", CONTENTS = "<PASSWORD_HASH>" },
93+
{ TAG = "timezone", CONTENTS = "Europe/Paris" },
94+
{ TAG = "keymap", CONTENTS = "us" }
95+
] }
96+
UPGRADE = { TAG = "installation", mode = "upgrade" }
97+
RESTORE = { TAG = "restore" }
98+
99+
[install.isos]
100+
# Base URL for XCP-ng installer ISOs
101+
base_url = "https://updates.xcp-ng.org/isos/"
102+
# Local cache directory for downloaded ISOs
103+
cache_dir = "/home/user/iso"
104+
105+
[install.isos.definitions]
106+
# XCP-ng installer ISO definitions
107+
# path can be:
108+
# - absolute filename
109+
# - absolute URL
110+
# - path relative to base_url
111+
# Note the dirname part is ignored when looking in cache_dir, abuse this
112+
# for local-only ISO with things like "locally-built/my.iso" or "xs/8.3.iso".
113+
# If 'net-only' is set to 'True' only source of type URL will be possible.
114+
# By default the parameter is set to False.
115+
"83nightly" = { path = "http://unconfigured.iso", unsigned = true }
116+
"830" = { path = "8.3/xcp-ng-8.3.0.iso" }
117+
"82nightly" = { path = "http://unconfigured.iso", unsigned = true }
118+
"821.1" = { path = "8.2/xcp-ng-8.2.1-20231130.iso" }
119+
"821" = { path = "8.2/xcp-ng-8.2.1.iso" }
120+
"820" = { path = "8.2/xcp-ng-8.2.0.iso" }
121+
"81" = { path = "8.1/xcp-ng-8.1.0-2.iso" }
122+
"80" = { path = "8.0/xcp-ng-8.0.0.iso" }
123+
"76" = { path = "7.6/xcp-ng-7.6.0.iso" }
124+
"75" = { path = "7.5/xcp-ng-7.5.0-2.iso" }
125+
"xs8" = { path = "XenServer8_2024-03-18.iso" }
126+
"ch821.1" = { path = "CitrixHypervisor-8.2.1-2306-install-cd.iso" }
127+
"ch821" = { path = "CitrixHypervisor-8.2.1-install-cd.iso" }
128+
129+
[guest_tools]
130+
# Guest tools ISO download location
131+
download_url = "http://pxe/isos/"
132+
133+
[guest_tools.win]
134+
# Definitions of Windows guest tool ISOs to be tested
135+
136+
[guest_tools.win.stable]
137+
# ISO name on SR or subpath of download_url
138+
name = "guest-tools-win.iso"
139+
# Whether ISO should be downloaded from download_url
140+
download = true
141+
# ISO-relative path of MSI file to be installed
142+
package = "package\\XenDrivers-x64.msi"
143+
# ISO-relative path of XenClean script
144+
xenclean_path = "package\\XenClean\\x64\\Invoke-XenClean.ps1"
145+
# ISO-relative path of root cert file to be installed before guest tools (optional)
146+
testsign_cert = "testsign\\XCP-ng_Test_Signer.crt"
147+
# What's the onboard family of our tools? This is equal to the WinPV VENDOR_NAME value
148+
onboard_family = "XCP-ng"
149+
150+
[guest_tools.other]
151+
# Definition of ISO containing other guest tools to be tested
152+
# ISO name on SR or subpath of download_url
153+
name = "other-guest-tools-win.iso"
154+
# Whether ISO should be downloaded from download_url
155+
download = false
156+
157+
[guest_tools.installed]
158+
# Definitions of other guest tools contained in guest_tools.other ISO
159+
160+
[guest_tools.installed."xcp-ng-9.0.9000"]
161+
# Whether we are installing MSI files ("msi"), bare .inf drivers ("inf")
162+
# or nothing in case of Windows Update (absent or null)
163+
type = "msi"
164+
# ISO-relative path of this guest tool
165+
path = "xcp-ng-9.0.9000"
166+
# "path"-relative path of MSI or driver files to be installed
167+
package = "package\\XenDrivers-x64.msi"
168+
# Relative path of root cert file (optional)
169+
testsign_cert = "testsign\\XCP-ng_Test_Signer.crt"
170+
# Whether this guest tool version wants vendor device to be activated (optional, defaults to False)
171+
# Note: other guest tools may not install correctly with this setting enabled
172+
vendor_device = false
173+
# Can we upgrade automatically from this guest tool to our tools?
174+
upgradable = true
175+
# What is the expected onboarding phase after running XenClean when this tool is installed? (optional)
176+
# See test_xenclean.py ONBOARDING_PHASES for details
177+
onboarding_phase = "see test_xenclean.py ONBOARDING_PHASES"
178+
179+
[guest_tools.installed.vendor]
180+
# Vendor device-specific guest tools
181+
vendor_device = true
182+
upgradable = false
183+
184+
[ssh]
185+
# Public keys for a private key available to the test runner
186+
pubkey = """ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMnN/wVdQqHA8KsndfrLS7fktH/IEgxoa533efuXR6rw XCP-ng CI
187+
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDKz9uQOoxq6Q0SQ0XTzQHhDolvuo/7EyrDZsYQbRELhcPJG8MT/o5u3HyJFhIP2+HqBSXXgmqRPJUkwz9wUwb2sUwf44qZm/pyPUWOoxyVtrDXzokU/uiaNKUMhbnfaXMz6Ogovtjua63qld2+ZRXnIgrVtYKtYBeu/qKGVSnf4FTOUKl1w3uKkr59IUwwAO8ay3wVnxXIHI/iJgq6JBgQNHbn3C/SpYU++nqL9G7dMyqGD36QPFuqH/cayL8TjNZ67TgAzsPX8OvmRSqjrv3KFbeSlpS/R4enHkSemhgfc8Z2f49tE7qxWZ6x4Uyp5E6ur37FsRf/tEtKIUJGMRXN XCP-ng CI"""
188+
# Maximum number of SSH output lines to log before truncating
189+
output_max_lines = 20
190+
# If true, strips SSH banners from output
191+
ignore_banner = false
192+
193+
[storage]
194+
# Storage device configurations (all optional, leave empty if not used)
195+
196+
[storage.nfs]
197+
# Default NFS device config:
198+
# URL/Hostname of NFS server
199+
# server = "10.0.0.2"
200+
# Path to shared mountpoint
201+
# serverpath = "/path/to/shared/mount"
202+
203+
[storage.nfs4]
204+
# Default NFS4+ only device config:
205+
# URL/Hostname of NFS server
206+
# server = "10.0.0.2"
207+
# Path to shared mountpoint
208+
# serverpath = "/path_to_shared_mount"
209+
# nfsversion = "4.1"
210+
211+
[storage.nfs_iso]
212+
# Default NFS ISO device config:
213+
# URL/Hostname of NFS server and path to shared mountpoint
214+
# location = "10.0.0.2:/path/to/shared/mount"
215+
216+
[storage.cifs_iso]
217+
# Default CIFS ISO device config:
218+
# location = r"\\10.0.0.2\<shared folder name>"
219+
# username = "<user>"
220+
# cifspassword = "<password>"
221+
# type = "cifs"
222+
# vers = "<1.0> or <3.0>"
223+
224+
[storage.cephfs]
225+
# CephFS storage configuration
226+
# server = "10.0.0.2"
227+
# serverpath = "/vms"
228+
229+
[storage.moosefs]
230+
# MooseFS storage configuration
231+
# masterhost = "mfsmaster"
232+
# masterport = "9421"
233+
# rootpath = "/vms"
234+
235+
[storage.lvmoiscsi]
236+
# LVM over iSCSI storage configuration
237+
# target = "192.168.1.1"
238+
# port = "3260"
239+
# targetIQN = "target.example"
240+
# SCSIid = "id"
241+
242+
[storage.linstor]
243+
# LINSTOR storage configuration
244+
redundancy = 2

0 commit comments

Comments
 (0)