feat(profiles): support automatic ZFS RAID0 disk selection
This commit is contained in:
@@ -58,8 +58,7 @@ def environment(tmp_path):
|
||||
yield app, client, csrf
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def prepared(environment):
|
||||
def prepare_host(environment, disk_setup=None, approve=True):
|
||||
app, client, csrf = environment
|
||||
secret = post(client, "/api/v1/secrets", {"name": "test-root", "value": ROOT_HASH}, csrf)
|
||||
group = post(client, "/api/v1/groups", {"name": "test-lab", "site": "lab", "valid_hours": 1}, csrf)
|
||||
@@ -74,6 +73,8 @@ def prepared(environment):
|
||||
post(client, f"/api/v1/modules/{module['id']}/publish", publication, csrf)
|
||||
profile_data = json.loads((Path(__file__).parents[1] / "docs" / "sample-profile.json").read_text())
|
||||
profile_data["values"]["root_secret_id"] = secret["id"]
|
||||
if disk_setup is not None:
|
||||
profile_data["values"]["disk_setup"] = deepcopy(disk_setup)
|
||||
installation = post(client, "/api/v1/profiles", profile_data, csrf)
|
||||
post(client, f"/api/v1/profiles/{installation['id']}/publish", publication, csrf)
|
||||
postinstall = post(client, "/api/v1/profiles", {"name": "test-postinstall", "kind": "postinstall",
|
||||
@@ -86,7 +87,7 @@ def prepared(environment):
|
||||
"installation_profile_id": installation["id"], "postinstall_profile_id": postinstall["id"], "iso_id": iso["id"]}, csrf)
|
||||
run = post(client, f"/api/v1/hosts/{host['id']}/approve-install", {
|
||||
"expected_version": host["version"], "valid_minutes": 30, "confirmation": host["fqdn"],
|
||||
"disks_confirmed": True, "reason": "Dedicated simulated test host"}, csrf)
|
||||
"disks_confirmed": True, "reason": "Dedicated simulated test host"}, csrf) if approve else None
|
||||
payload = {"$schema": {"version": "1.0"}, "product": {"product": "pve"},
|
||||
"iso": {"release": "9.1", "build": "1"}, "dmi": {"system": {"uuid": HOST_UUID, "serial": "LAB-HOST-001"}},
|
||||
"network-interfaces": [{"mac": HOST_MAC}]}
|
||||
@@ -95,6 +96,11 @@ def prepared(environment):
|
||||
"host": host, "run": run, "payload": payload, "identities": identities}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def prepared(environment):
|
||||
return prepare_host(environment)
|
||||
|
||||
|
||||
def answer(prepared, payload=None):
|
||||
return prepared["client"].post("/installer/v1/answer", json=payload or prepared["payload"],
|
||||
headers={"Authorization": f"Bearer {prepared['group']['token']}"})
|
||||
@@ -185,12 +191,98 @@ def test_concurrent_installer_retries_reserve_one_immutable_answer(prepared):
|
||||
native = tomllib.loads(responses[0].text)
|
||||
assert native["global"]["fqdn"] == prepared["host"]["fqdn"]
|
||||
assert native["disk-setup"]["filter"] == {"ID_SERIAL_SHORT": "LAB_SYSTEM_DISK_001"}
|
||||
assert native["disk-setup"]["filter-match"] == "all"
|
||||
assert "selection" not in native["disk-setup"]
|
||||
assert "expected_count" not in native["disk-setup"]
|
||||
assert prepared["run"]["snapshot"]["warnings"] == [
|
||||
"Die Hardwarekennung dient der Zuordnung im kontrollierten Provisionierungsnetz."]
|
||||
with prepared["app"].state.db.connection() as connection:
|
||||
assert connection.execute("SELECT count(*) FROM runs").fetchone()[0] == 1
|
||||
assert connection.execute("SELECT status FROM approvals").fetchone()[0] == "consumed"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("evidence", [None, "Single-target-disk laboratory inventory"])
|
||||
def test_automatic_zfs_raid0_preview_approval_and_native_answer(environment, evidence):
|
||||
disk_setup = {"filesystem": "zfs", "selection": "all", "zfs": {"raid": "raid0", "ashift": 12}}
|
||||
if evidence is not None:
|
||||
disk_setup["inventory_evidence"] = evidence
|
||||
configured = prepare_host(environment, disk_setup, approve=False)
|
||||
client, csrf, host = configured["client"], configured["csrf"], configured["host"]
|
||||
preview = client.get(f"/api/v1/hosts/{host['id']}/preview")
|
||||
assert preview.status_code == 200, preview.text
|
||||
snapshot = preview.json()
|
||||
assert snapshot["disks"] == disk_setup
|
||||
assert snapshot["warnings"][-1] == (
|
||||
"Automatische Datenträgerwahl: Alle vom Installer erkannten Zielplatten werden verwendet. "
|
||||
"Dieses Profil ist für Server mit genau einer Zielplatte vorgesehen.")
|
||||
approval = {"expected_version": host["version"], "valid_minutes": 30,
|
||||
"confirmation": host["fqdn"], "disks_confirmed": False, "reason": "Single-target-disk simulation"}
|
||||
endpoint = f"/api/v1/hosts/{host['id']}/approve-install"
|
||||
assert client.post(endpoint, json=approval, headers=csrf).status_code == 422
|
||||
configured["run"] = post(client, endpoint, {**approval, "disks_confirmed": True}, csrf)
|
||||
assert configured["run"]["snapshot"] == snapshot
|
||||
response = answer(configured)
|
||||
assert response.status_code == 200, response.text
|
||||
assert tomllib.loads(response.text)["disk-setup"] == {
|
||||
"filesystem": "zfs", "zfs": {"raid": "raid0", "ashift": 12},
|
||||
"filter": {"DEVTYPE": "disk"}, "filter-match": "all"}
|
||||
assert answer(configured).text == response.text
|
||||
|
||||
|
||||
@pytest.mark.parametrize("override", [
|
||||
{"filter": {"ID_SERIAL_SHORT": "LAB_SYSTEM_DISK_001"}},
|
||||
{"filter": {}},
|
||||
{"filter_match": "all"},
|
||||
{"expected_count": 1},
|
||||
{"expected_serials": []},
|
||||
{"filesystem": "ext4"},
|
||||
{"filesystem": "xfs"},
|
||||
{"zfs": {"raid": "raid1"}},
|
||||
{"zfs": {"raid": "raid10"}},
|
||||
{"zfs": {"raid": "raidz-1"}},
|
||||
{"zfs": {}},
|
||||
{"zfs": {"raid": "raid0", "ashift": True}},
|
||||
{"zfs": {"raid": "raid0", "copies": 4}},
|
||||
{"lvm": {}},
|
||||
{"selection": "ALL"},
|
||||
{"selection": None},
|
||||
{"selection": True},
|
||||
{"selection": ["all"]},
|
||||
{"inventory_evidence": ""},
|
||||
{"inventory_evidence": " "},
|
||||
{"inventory_evidence": None},
|
||||
{"disk_list": ["sda"]},
|
||||
])
|
||||
def test_automatic_disk_selection_rejects_ambiguous_or_unsupported_profiles(environment, override):
|
||||
disk_setup = {"filesystem": "zfs", "selection": "all", "zfs": {"raid": "raid0"}, **override}
|
||||
configured = prepare_host(environment, disk_setup, approve=False)
|
||||
client, csrf, host = configured["client"], configured["csrf"], configured["host"]
|
||||
assert client.get(f"/api/v1/hosts/{host['id']}/preview").status_code == 422
|
||||
response = client.post(f"/api/v1/hosts/{host['id']}/approve-install", json={
|
||||
"expected_version": host["version"], "valid_minutes": 30, "confirmation": host["fqdn"],
|
||||
"disks_confirmed": True, "reason": "Invalid selection must not authorize an install"}, headers=csrf)
|
||||
assert response.status_code == 422, response.text
|
||||
with configured["app"].state.db.connection() as connection:
|
||||
assert connection.execute("SELECT count(*) FROM approvals").fetchone()[0] == 0
|
||||
assert connection.execute("SELECT count(*) FROM runs").fetchone()[0] == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize("disk_setup", [
|
||||
{"filesystem": "zfs", "zfs": {"raid": "raid0"}},
|
||||
{"filesystem": "zfs", "zfs": {"raid": "raid0"}, "filter": {"DEVTYPE": "disk"}},
|
||||
{"filesystem": "zfs", "zfs": {"raid": "raid0"},
|
||||
"filter": {"ID_SERIAL_SHORT": "LAB_SYSTEM_DISK_001"}, "expected_count": 1,
|
||||
"expected_serials": ["LAB_SYSTEM_DISK_001"]},
|
||||
{"filesystem": "zfs", "zfs": {"raid": "raid1"},
|
||||
"filter": {"ID_SERIAL_SHORT": "LAB_SYSTEM_DISK_001"}, "expected_count": 1,
|
||||
"expected_serials": ["LAB_SYSTEM_DISK_001"], "inventory_evidence": "Verified one-disk inventory"},
|
||||
])
|
||||
def test_filtered_disk_selection_keeps_existing_requirements(environment, disk_setup):
|
||||
configured = prepare_host(environment, disk_setup, approve=False)
|
||||
response = configured["client"].get(f"/api/v1/hosts/{configured['host']['id']}/preview")
|
||||
assert response.status_code == 422, response.text
|
||||
|
||||
|
||||
def test_new_profile_version_cannot_change_prepared_run(prepared):
|
||||
original = prepared["run"]["snapshot"]
|
||||
data = deepcopy(prepared["profile_data"])
|
||||
|
||||
Reference in New Issue
Block a user