Skip to content

Commit 307a53b

Browse files
committed
WIP answerfile generation
This process has several steps: - building of a data structure holding all of the answerfile data, from a customizable base in data.py and from tests-specific items - serialization as XML to be read by host-installer - necessary changes to the ISO for host-installer to use it This is needed so: - different tests can use different parameters without the need for provisionning every answerfile to be used - tests can dynamically add contents for their own needs, before the XML gets actualy written FIXME: - this covers boot from remastered ISO and not PXE, install on UEFI/NVMe disk (xml needs generation there too) and not BIOS/ATA (needs clever parametrization) - doc
1 parent 7b39d7d commit 307a53b

File tree

5 files changed

+88
-4
lines changed

5 files changed

+88
-4
lines changed

data.py-dist

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
# You need to have an SSH key into the hosts' /root/.ssh/authorized_keys.
66
HOST_DEFAULT_USER = "root"
77
HOST_DEFAULT_PASSWORD = ""
8+
HOST_DEFAULT_PASSWORD_HASH = "" # FIXME
89

910
# Public key for a private key available to the test runner
1011
TEST_SSH_PUBKEY = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDKz9uQOoxq6Q0SQ0XTzQHhDolvuo/7EyrDZsYQbRELhcPJG8MT/o5u3HyJFhIP2+HqBSXXgmqRPJUkwz9wUwb2sUwf44qZm/pyPUWOoxyVtrDXzokU/uiaNKUMhbnfaXMz6Ogovtjua63qld2+ZRXnIgrVtYKtYBeu/qKGVSnf4FTOUKl1w3uKkr59IUwwAO8ay3wVnxXIHI/iJgq6JBgQNHbn3C/SpYU++nqL9G7dMyqGD36QPFuqH/cayL8TjNZ67TgAzsPX8OvmRSqjrv3KFbeSlpS/R4enHkSemhgfc8Z2f49tE7qxWZ6x4Uyp5E6ur37FsRf/tEtKIUJGMRXN XCP-ng CI"
@@ -119,6 +120,16 @@ LVMOISCSI_DEVICE_CONFIG = {
119120
# 'SCSIid': 'id'
120121
}
121122

123+
BASE_ANSWERFILES = dict(
124+
INSTALL={
125+
"root": {"tag": "installation"},
126+
"root-password": {"type": "hash", "text": HOST_DEFAULT_PASSWORD_HASH},
127+
"admin-interface": {"name": "eth0", "proto": "dhcp"},
128+
"timezone": {"text": "Europe/Paris"},
129+
"keymap": {"text": "us"},
130+
},
131+
)
132+
122133
# compatibility settings for older tests
123134
DEFAULT_NFS_DEVICE_CONFIG = NFS_DEVICE_CONFIG
124135
DEFAULT_NFS4_DEVICE_CONFIG = NFS4_DEVICE_CONFIG

pytest.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ markers =
2323
vm_definitions: dicts of VM defs for create_vms fixture.
2424

2525
# * installation-related markers to customize installer run
26+
answerfile: dict defining an answerfile
2627
installer_iso: key of data.ISO_IMAGES identifying an installer ISO
2728

2829
# * Test targets related to VMs

tests/install/conftest.py

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,52 @@
1+
from copy import deepcopy
12
import logging
23
import os
34
import pytest
45
import tempfile
56

67
from lib.commands import local_cmd, scp, ssh
78

9+
@pytest.fixture(scope='function')
10+
def answerfile(request):
11+
marker = request.node.get_closest_marker("answerfile")
12+
13+
if marker is None:
14+
yield None # no answerfile to generate
15+
return
16+
17+
# construct answerfile definition from option "base", and explicit bits
18+
marker_def = deepcopy(marker.args[0])
19+
if "base" in marker_def:
20+
from data import BASE_ANSWERFILES
21+
answerfile_def = deepcopy(BASE_ANSWERFILES[marker_def["base"]])
22+
del marker_def["base"]
23+
else:
24+
answerfile_def = {}
25+
answerfile_def.update(marker_def)
26+
27+
# convert to a ElementTree.Element tree suitable for further
28+
# modification before we serialize it to XML
29+
import xml.etree.ElementTree as ET
30+
31+
# root element special case
32+
root_def = answerfile_def.pop("root")
33+
root_tag = root_def.pop("tag")
34+
root = ET.Element(root_tag, **root_def)
35+
36+
# contents of root element
37+
for name, defn in answerfile_def.items():
38+
text = defn.pop("text", None)
39+
element = ET.SubElement(root, name, **defn)
40+
if text:
41+
element.text = text
42+
43+
yield ET.ElementTree(root)
44+
845
@pytest.fixture(scope='function')
946
def iso_remaster(request, answerfile):
1047
marker = request.node.get_closest_marker("installer_iso")
1148
assert marker is not None, "iso_remaster fixture requires 'installer_iso' marker"
1249
iso_key = marker.args[0]
13-
ANSWERFILE_URL = "http://pxe/configs/custom/ydi/install-8.2-uefi-iso-ext.xml" # FIXME
1450

1551
from data import ISO_IMAGES, ISOSR_SRV, ISOSR_PATH, TEST_SSH_PUBKEY, TOOLS
1652
assert "iso-remaster" in TOOLS
@@ -24,6 +60,13 @@ def iso_remaster(request, answerfile):
2460
remastered_iso = os.path.join(isotmp, "image.iso")
2561
iso_patcher_script = os.path.join(isotmp, "iso-patcher")
2662
img_patcher_script = os.path.join(isotmp, "img-patcher")
63+
answerfile_xml = os.path.join(isotmp, "answerfile.xml")
64+
65+
if answerfile:
66+
logging.info("generating answerfile %s", answerfile_xml)
67+
answerfile.write(answerfile_xml)
68+
else:
69+
logging.info("no answerfile")
2770

2871
logging.info("Remastering %s to %s", SOURCE_ISO, remastered_iso)
2972

@@ -36,7 +79,8 @@ def iso_remaster(request, answerfile):
3679
mkdir -p "$INSTALLIMG/root/.ssh"
3780
echo "{TEST_SSH_PUBKEY}" > "$INSTALLIMG/root/.ssh/authorized_keys"
3881
39-
curl {ANSWERFILE_URL} -o "$INSTALLIMG/root/answerfile.xml"
82+
test ! -e "{answerfile_xml}" ||
83+
cp "{answerfile_xml}" "$INSTALLIMG/root/answerfile.xml"
4084
""",
4185
file=patcher_fd)
4286
os.chmod(patcher_fd.fileno(), 0o755)
@@ -47,8 +91,9 @@ def iso_remaster(request, answerfile):
4791
print(f"""#!/bin/bash
4892
set -ex
4993
ISODIR="$1"
50-
SED_COMMANDS=(-e "s@/vmlinuz@/vmlinuz sshpassword={passwd} atexit=shell@")
51-
SED_COMMANDS+=(-e "s@/vmlinuz@/vmlinuz install answerfile=file:///root/answerfile.xml network_device=all@")
94+
SED_COMMANDS=(-e "s@/vmlinuz@/vmlinuz sshpassword={passwd} atexit=shell network_device=all@")
95+
test ! -e "{answerfile_xml}" ||
96+
SED_COMMANDS+=(-e "s@/vmlinuz@/vmlinuz install answerfile=file:///root/answerfile.xml@")
5297
5398
sed -i "${{SED_COMMANDS[@]}}" \
5499
"$ISODIR"/*/*/grub*.cfg \

tests/install/test.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ class TestNested:
2525
vdis=[dict(name="vm 1 system disk", size="100GiB", device="xvda", userdevice="0")],
2626
vifs=[dict(index=0, network_uuid="eabc1038-e40f-2ae5-0781-a3adbec1cae8")], # FIXME
2727
))
28+
@pytest.mark.answerfile(
29+
{
30+
"base": "INSTALL",
31+
"source": {"type": "local"},
32+
"primary-disk": {"text": "nvme0n1"},
33+
})
2834
@pytest.mark.installer_iso("xcpng-8.2.1-2023")
2935
def test_install_821_uefi(self, iso_remaster, create_vms):
3036
assert len(create_vms) == 1

tests/install/test_fixtures.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import logging
2+
import pytest
3+
4+
class TestFixtures:
5+
# test the answerfile fixture can run on 2 parametrized instances
6+
# of the test in one run
7+
@pytest.mark.answerfile(
8+
{
9+
"base": "INSTALL",
10+
"source": {"type": "local"},
11+
"primary-disk": {"text": "nvme0n1"},
12+
})
13+
@pytest.mark.parametrize("parm", [
14+
1,
15+
pytest.param(2, marks=[
16+
pytest.mark.dependency(depends=["TestFixtures::test_parametrized_answerfile[1]"]),
17+
]),
18+
])
19+
@pytest.mark.dependency
20+
def test_parametrized_answerfile(self, answerfile, parm):
21+
logging.debug("test_parametrized_answerfile with parm=%s", parm)

0 commit comments

Comments
 (0)