feat: add Proxmox provisioning service with CI and deployment tooling
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
# API-Ablauf im Labor
|
||||
|
||||
Die API verwendet Sessioncookies und für alle Verwaltungsänderungen
|
||||
`X-CSRF-Token`. Die Beispiele benötigen Bash, curl und jq. Für PowerShell steht
|
||||
die gleiche API über `Invoke-RestMethod` zur Verfügung. Das vollständige Schema
|
||||
ist nach Anmeldung unter `/openapi.json` und als generierter Repositorystand in
|
||||
[openapi.json](openapi.json) verfügbar.
|
||||
|
||||
## Anmelden und Daten lesen
|
||||
|
||||
```bash
|
||||
export AIS_URL=https://provision.example.net
|
||||
umask 077
|
||||
read -rsp 'Administrator-Passwort: ' AIS_PASSWORD
|
||||
printf '\n'
|
||||
printf '%s' "$AIS_PASSWORD" | curl --fail --silent --show-error \
|
||||
--cookie-jar session.cookies --data-urlencode username=admin \
|
||||
--data-urlencode password@- "$AIS_URL/auth/login" --output /dev/null
|
||||
unset AIS_PASSWORD
|
||||
AIS_CSRF=$(curl --fail --silent --show-error --cookie session.cookies \
|
||||
"$AIS_URL/api/v1/me" | jq -r .csrf_token)
|
||||
curl --fail --silent --show-error --cookie session.cookies "$AIS_URL/api/v1/hosts"
|
||||
```
|
||||
|
||||
Cookies wie Zugangsdaten behandeln und nach Ende der Sitzung löschen.
|
||||
Der Server liefert bei nicht angemeldeten API-Aufrufen HTTP 401 und bei
|
||||
unzulässigen Rollen oder fehlendem CSRF-Token HTTP 403.
|
||||
|
||||
## Profilversion anlegen
|
||||
|
||||
Zuerst einen Root-Passwort-Hash als Geheimnis in der Weboberfläche speichern.
|
||||
Unter Linux kann ein SHA-512-crypt-Hash etwa mit `openssl passwd -6` interaktiv
|
||||
erzeugt werden. Den Hash nicht als Klartextfeld in ein Profil aufnehmen.
|
||||
|
||||
[sample-profile.json](sample-profile.json) kopieren und alle Platzhalter anpassen:
|
||||
Secret-ID, konkret geprüfter Build, Management-Interface, Datenträgerseriennummer
|
||||
und Inventarisierungsnachweis. `9.1-1` dient lediglich als Formatbeispiel und ist
|
||||
keine Kompatibilitätsfreigabe. FQDN und Management-IP kommen aus dem Hostinventar.
|
||||
|
||||
```bash
|
||||
curl --fail --silent --show-error --cookie session.cookies \
|
||||
--header "X-CSRF-Token: $AIS_CSRF" \
|
||||
--json @my-installation-profile.json "$AIS_URL/api/v1/profiles"
|
||||
```
|
||||
|
||||
Die Antwort enthält `id`, `version`, `digest` und `status=draft`. Ein weiterer
|
||||
POST mit demselben Namen und Typ erstellt eine neue unveränderliche Version.
|
||||
Bereits vorbereitete Läufe behalten die zuvor aufgelösten Werte und Versionen.
|
||||
|
||||
Veröffentlichung erfolgt in einer Sitzung einer anderen berechtigten Person:
|
||||
|
||||
```bash
|
||||
curl --fail --silent --show-error --cookie reviewer.cookies \
|
||||
--header "X-CSRF-Token: $REVIEWER_CSRF" \
|
||||
--json '{"test_evidence":"lab-report-2026-09-13","reason":"Labortest erfolgreich geprüft"}' \
|
||||
"$AIS_URL/api/v1/profiles/$PROFILE_ID/publish"
|
||||
```
|
||||
|
||||
Module werden analog über `POST /api/v1/modules` und
|
||||
`POST /api/v1/modules/{id}/publish` versioniert und veröffentlicht. Ausgangsvorlagen
|
||||
stehen unter `GET /api/v1/modules/builtin`. Sie enthalten keinen ausgefüllten
|
||||
Testnachweis und benötigen Anpassung sowie Prüfung auf dem Zielhost.
|
||||
|
||||
Ein Postinstallationsprofil verweist auf unveränderliche Modul-IDs:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "lab-postinstall",
|
||||
"kind": "postinstall",
|
||||
"target_builds": ["9.1-1"],
|
||||
"steps": [
|
||||
{
|
||||
"id": "final-check",
|
||||
"module_id": "REPLACE_WITH_PUBLISHED_MODULE_ID",
|
||||
"parameters": {},
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"reboot_budget": 1
|
||||
}
|
||||
```
|
||||
|
||||
Modulabhängigkeiten müssen bereits in vorherigen Schritten enthalten sein.
|
||||
Mindestens eine Pflichtprüfung wird verlangt. Modulparameter werden gegen das
|
||||
JSON-Schema der gewählten Modulversion validiert.
|
||||
|
||||
## Host prüfen und Installation freigeben
|
||||
|
||||
Gruppentoken, geprüftes Medium und beide veröffentlichten Profile müssen bereits
|
||||
existieren. Der Host verweist über `installation_profile_id`,
|
||||
`postinstall_profile_id` und `iso_id` auf diese Objekte.
|
||||
|
||||
```bash
|
||||
curl --fail --silent --show-error --cookie session.cookies \
|
||||
"$AIS_URL/api/v1/hosts/$HOST_ID/preview"
|
||||
curl --fail --silent --show-error --cookie session.cookies \
|
||||
--header "X-CSRF-Token: $AIS_CSRF" \
|
||||
--json '{"expected_version":1,"valid_minutes":30,"confirmation":"pve01.lab.example.net","disks_confirmed":true,"reason":"Freigegebener dedizierter Labortest"}' \
|
||||
"$AIS_URL/api/v1/hosts/$HOST_ID/approve-install"
|
||||
```
|
||||
|
||||
Vor dem zweiten Aufruf tatsächlichen FQDN, aktuelle Hostversion und dargestellte
|
||||
Zielgeräte prüfen. Die Aktion erzeugt einen vorbereiteten Lauf und bindet die
|
||||
Konfiguration. Ein zweiter gleichzeitiger aktiver Lauf für denselben Host ist gesperrt.
|
||||
|
||||
Der Proxmox-Installer sendet seinen nativen Request selbst. Für einen isolierten
|
||||
API-Test zeigt dieses Beispiel die verwendete Payload-Struktur:
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": {"version": "1.0"},
|
||||
"product": {"product": "pve"},
|
||||
"iso": {"release": "9.1", "build": "1"},
|
||||
"dmi": {
|
||||
"system": {
|
||||
"uuid": "d2e59b03-13cf-4ac9-a390-78c55f6a36d3",
|
||||
"serial": "LAB-HOST-001"
|
||||
}
|
||||
},
|
||||
"network-interfaces": [{"mac": "02:00:00:00:00:01"}]
|
||||
}
|
||||
```
|
||||
|
||||
Der echte Installer darf weitere Hardwarefelder senden. Registrierung und
|
||||
Antwortschema stammen aus den
|
||||
[offiziellen Installer-Typen](https://github.com/proxmox/proxmox-rs/blob/master/proxmox-installer-types/src/answer.rs).
|
||||
Ein unbekannter Host erhält keine TOML-Antwort. Der Gruppentoken muss zum Standort
|
||||
und zum freigegebenen Medium des Hosts passen.
|
||||
|
||||
```bash
|
||||
curl --fail --silent --show-error \
|
||||
--header "Authorization: Bearer $INSTALLER_GROUP_TOKEN" \
|
||||
--json @installer-info.json "$AIS_URL/installer/v1/answer" \
|
||||
--output answer.toml
|
||||
```
|
||||
|
||||
Dieser Abruf verbraucht eine Freigabe. `answer.toml` enthält den Root-Hash und
|
||||
runbezogene Downloadberechtigungen; wie Zugangsdaten schützen. Innerhalb des
|
||||
kurzen Auslieferungsfensters liefern Wiederholungen dieselbe Antwort.
|
||||
|
||||
## Runner und Status
|
||||
|
||||
Der Starthelfer richtet den Runner automatisch ein. Das Gerät erzeugt einen
|
||||
eigenen Ed25519-Schlüssel, registriert dessen öffentlichen Anteil und signiert
|
||||
weitere Requests einschließlich Methode, Pfad, Zeitstempel, Nonce und Body-Digest.
|
||||
Ein gewöhnlicher curl-Aufruf mit Installer-Gruppentoken berechtigt nicht zum
|
||||
Manifest-, Artefakt- oder Secret-Abruf.
|
||||
|
||||
```bash
|
||||
curl --fail --silent --show-error --cookie session.cookies \
|
||||
"$AIS_URL/api/v1/runs/$RUN_ID"
|
||||
```
|
||||
|
||||
Die Laufansicht enthält Schrittzustände, redigierte Logs und die fixierte
|
||||
Konfiguration. Fortsetzung und Abbruch benötigen `expected_version` und einen
|
||||
Begründungstext über `/api/v1/runs/{id}/resume` bzw. `/cancel`. Eine Fortsetzung
|
||||
erst nach Prüfung des lokalen Zustands anfordern. Ein bereits laufender
|
||||
Paketmanager wird beim Abbruch am nächsten sicheren Übergang angehalten.
|
||||
|
||||
Für einen dauerhaft verlassenen Lauf steht `/api/v1/runs/{id}/reconcile` zur
|
||||
Verfügung. Zuerst den Abbruch anfordern, ausgestellte Leases und das
|
||||
Antwortauslieferungsfenster ablaufen lassen sowie Installer und Runner lokal
|
||||
stoppen und prüfen. Danach `expected_version`, `reason`, `confirmation` mit dem
|
||||
Host-FQDN und `execution_stopped: true` senden. Diese gesonderte Bestätigung beendet
|
||||
den Lauf als `cancelled`; eine neue Installation benötigt anschließend eine
|
||||
eigene Freigabe. Der Ablauf ist auch für `needs_review` nach Restore vorgesehen.
|
||||
Reference in New Issue
Block a user