Skip to content

Commit f3ff6ac

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 fb55eab commit f3ff6ac

22 files changed

Lines changed: 716 additions & 190 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
@@ -11,10 +11,5 @@ check: ruff autopep8 flake8 mypy pyright
1111
# It runs on all files managed by git (untracked files are not modified)
1212
fix: ruff-fix autopep8-fix
1313

14-
data.py:
15-
@test -r data.py || echo "File 'data.py' does not exist. Refer to https://github.com/xcp-ng/xcp-ng-tests#configuration." && exit 1
16-
17-
mypy pyright ruff: data.py
18-
1914
ruff ruff-fix autopep8 autopep8-fix flake8 mypy pyright:
2015
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
@@ -602,10 +658,10 @@ python scripts/test_install_xcpng.py 10.0.0.2 f0f5f010-80c6-25ae-44a2-1fb154e32d
602658
```
603659
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.
604660
605-
The script requires the addressable name or IP of the PXE config server to be defined in `data.py`:
606-
```
607-
# PXE config server for automated XCP-ng installation
608-
PXE_CONFIG_SERVER = 'pxe'
661+
The script requires the addressable name or IP of the PXE config server to be defined in `config.toml`:
662+
```toml
663+
[pxe]
664+
config_server = "pxe"
609665
```
610666

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

0 commit comments

Comments
 (0)