Neustart sauber statt mit Stromausfall
Beim Umstellen der Anzeige habe ich die Maschine mit stop+start neu gestartet -
also mit "qm stop", einem virtuellen Stromausfall. Bei einer laufenden Maschine
mit eingehaengten Dateisystemen genau das Falsche.
Jetzt uebernimmt das Proxmox' eigener reboot-Endpunkt: er faehrt den Gast per
ACPI herunter, der haengt seine Dateisysteme selbst aus, danach geht er wieder
hoch - und dabei werden vorgemerkte Aenderungen wirksam ("Applies pending
changes"). Antwortet der Gast nicht, schlaegt es fehl statt ihm den Strom zu
ziehen; das ist die richtige Richtung.
Der naheliegende Gedanke, im Gast selbst neu zu starten, hilft hier nicht: dabei
setzt sich nur die Maschine zurueck, der QEMU-Prozess laeuft mit der alten
Grafikkarte weiter und die Aenderung bliebe vorgemerkt liegen. Steht so auch im
Warnhinweis.
Gemessen: eintragen, Neustart in 5s, danach vga=qxl und der SPICE-Kanal da.
Ausserdem reichte spice_file() das nackte "no spice port" von Proxmox durch.
Das heisst schlicht, dass die Maschine ohne qxl laeuft - steht jetzt auch so
da, mitsamt dem Weg dorthin.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
91c23ce2c0
commit
9d5bf50f99
@@ -273,6 +273,20 @@ class Proxmox:
|
|||||||
return self._wait_upid(guest.node, stdout, "Aendern von %s" % guest.label,
|
return self._wait_upid(guest.node, stdout, "Aendern von %s" % guest.label,
|
||||||
guest)
|
guest)
|
||||||
|
|
||||||
|
def reboot_guest(self, guest, timeout=180):
|
||||||
|
"""Sauber neu starten: Proxmox faehrt den Gast per ACPI herunter und
|
||||||
|
startet ihn wieder. Dabei werden vorgemerkte Aenderungen wirksam.
|
||||||
|
|
||||||
|
Bewusst ohne Notabschaltung: antwortet der Gast nicht, schlaegt das
|
||||||
|
hier fehl, statt ihm den Strom zu ziehen. Ein Dateisystem, das gerade
|
||||||
|
beschrieben wird, nimmt einen Stromausfall uebel - und genau davor
|
||||||
|
soll das hier schuetzen.
|
||||||
|
"""
|
||||||
|
return self._guest_task(guest, "create", "status/reboot",
|
||||||
|
"Neustart von %s" % guest.label,
|
||||||
|
["--timeout", str(int(timeout))],
|
||||||
|
timeout=timeout + 120)
|
||||||
|
|
||||||
def resume_guest(self, guest):
|
def resume_guest(self, guest):
|
||||||
return self._guest_task(guest, "create", "status/resume",
|
return self._guest_task(guest, "create", "status/resume",
|
||||||
"Fortsetzen von %s" % guest.label)
|
"Fortsetzen von %s" % guest.label)
|
||||||
|
|||||||
+14
-2
@@ -1209,12 +1209,24 @@ def spice_file(proxmox, instance):
|
|||||||
die virt-viewer erwartet. Sie enthaelt ein Einmal-Kennwort und laeuft nach
|
die virt-viewer erwartet. Sie enthaelt ein Einmal-Kennwort und laeuft nach
|
||||||
kurzer Zeit ab - also frisch erzeugen, wenn sie nicht mehr zieht.
|
kurzer Zeit ab - also frisch erzeugen, wenn sie nicht mehr zieht.
|
||||||
"""
|
"""
|
||||||
|
try:
|
||||||
data = proxmox._json(["create", "/nodes/%s/%s/%d/spiceproxy"
|
data = proxmox._json(["create", "/nodes/%s/%s/%d/spiceproxy"
|
||||||
% (instance.node, instance.type, instance.vmid)])
|
% (instance.node, instance.type, instance.vmid)])
|
||||||
|
except ProxmoxError as exc:
|
||||||
|
# "no spice port" heisst schlicht: die Maschine laeuft ohne qxl. Das
|
||||||
|
# nackt durchzureichen hilft niemandem.
|
||||||
|
if "spice" in str(exc).lower():
|
||||||
|
raise RecoveryError(
|
||||||
|
"%s laeuft ohne SPICE-Anzeige - deshalb gibt es keinen Kanal, "
|
||||||
|
"auf den sich remote-viewer verbinden koennte.\n"
|
||||||
|
"Einschalten in der Detailansicht mit p , danach neu starten "
|
||||||
|
"(die Grafikkarte laesst sich im Betrieb nicht wechseln)."
|
||||||
|
% instance.label)
|
||||||
|
raise RecoveryError("SPICE-Verbindung nicht abrufbar: %s" % exc)
|
||||||
if not isinstance(data, dict) or not data.get("host"):
|
if not isinstance(data, dict) or not data.get("host"):
|
||||||
raise RecoveryError(
|
raise RecoveryError(
|
||||||
"%s liefert keine SPICE-Verbindung. Laeuft die Maschine, und ist "
|
"%s liefert keine SPICE-Verbindung. Laeuft die Maschine ueberhaupt?"
|
||||||
"die SPICE-Anzeige eingeschaltet (vga: qxl)?" % instance.label)
|
% instance.label)
|
||||||
|
|
||||||
# Derselbe Grund wie beim noVNC-Verweis: der Proxy-Eintrag traegt den
|
# Derselbe Grund wie beim noVNC-Verweis: der Proxy-Eintrag traegt den
|
||||||
# Hostnamen des Nodes, und wenn der Arbeitsplatz ihn nicht aufloest,
|
# Hostnamen des Nodes, und wenn der Arbeitsplatz ihn nicht aufloest,
|
||||||
|
|||||||
+15
-7
@@ -589,12 +589,20 @@ def _display(stdscr, proxmox, instance):
|
|||||||
# geladene Arbeitsspeicher ist danach weg - Proxmox gibt ihn schon
|
# geladene Arbeitsspeicher ist danach weg - Proxmox gibt ihn schon
|
||||||
# beim ersten Start frei, ein zweiter startet also kalt.
|
# beim ersten Start frei, ein zweiter startet also kalt.
|
||||||
warnung = ["Dafuer muss %s neu starten - die Grafikkarte laesst sich "
|
warnung = ["Dafuer muss %s neu starten - die Grafikkarte laesst sich "
|
||||||
"im Betrieb nicht wechseln." % instance.label]
|
"im Betrieb nicht wechseln." % instance.label,
|
||||||
|
"",
|
||||||
|
"Der Neustart laeuft sauber: Proxmox faehrt den Gast per ACPI "
|
||||||
|
"herunter, er haengt seine Dateisysteme selbst aus, danach "
|
||||||
|
"geht er wieder hoch. Kein Stromausfall.",
|
||||||
|
"",
|
||||||
|
"Ein Neustart von innen im Gast wuerde nichts bringen - dabei "
|
||||||
|
"setzt sich nur die Maschine zurueck, der QEMU-Prozess laeuft "
|
||||||
|
"weiter mit der alten Grafikkarte."]
|
||||||
if instance.resumed:
|
if instance.resumed:
|
||||||
|
warnung.append("")
|
||||||
warnung.append("ACHTUNG: der geladene Arbeitsspeicher ist danach "
|
warnung.append("ACHTUNG: der geladene Arbeitsspeicher ist danach "
|
||||||
"endgueltig weg. Die Maschine bootet kalt, offene "
|
"endgueltig weg. Die Maschine bootet kalt, offene "
|
||||||
"Programme und ungespeicherte Daten sind verloren.")
|
"Programme und ungespeicherte Daten sind verloren.")
|
||||||
warnung.append("Im Gast vorher sauber herunterfahren, was noch laeuft.")
|
|
||||||
key = _pages(stdscr, [(line, curses.color_pair(C_WARN) | curses.A_BOLD)
|
key = _pages(stdscr, [(line, curses.color_pair(C_WARN) | curses.A_BOLD)
|
||||||
for eintrag in warnung for line in _wrap(eintrag, 74)],
|
for eintrag in warnung for line in _wrap(eintrag, 74)],
|
||||||
"Neustart noetig", "j = trotzdem | andere Taste = abbrechen")
|
"Neustart noetig", "j = trotzdem | andere Taste = abbrechen")
|
||||||
@@ -612,12 +620,12 @@ def _display(stdscr, proxmox, instance):
|
|||||||
if not geaendert:
|
if not geaendert:
|
||||||
message(stdscr, "War schon so eingestellt.")
|
message(stdscr, "War schon so eingestellt.")
|
||||||
return
|
return
|
||||||
if neustart and confirm(stdscr, "%s jetzt neu starten?" % instance.label):
|
if neustart and confirm(stdscr, "%s jetzt sauber neu starten?" % instance.label):
|
||||||
_do(stdscr, "Starte %s neu ..." % instance.label,
|
_do(stdscr, "Fahre %s herunter und wieder hoch ..." % instance.label,
|
||||||
lambda: (proxmox.stop_guest(instance.guest),
|
lambda: proxmox.reboot_guest(instance.guest))
|
||||||
start(proxmox, instance))[1])
|
|
||||||
elif neustart:
|
elif neustart:
|
||||||
message(stdscr, "Eingetragen - wirksam beim naechsten Start.")
|
message(stdscr, "Eingetragen - wirksam beim naechsten Start. Im Gast "
|
||||||
|
"herunterfahren, dann hier mit s starten.")
|
||||||
else:
|
else:
|
||||||
message(stdscr, "Uebernommen.")
|
message(stdscr, "Uebernommen.")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user