diff --git a/pvesnap/proxmox.py b/pvesnap/proxmox.py index 406153e..2e270fd 100644 --- a/pvesnap/proxmox.py +++ b/pvesnap/proxmox.py @@ -273,6 +273,20 @@ class Proxmox: return self._wait_upid(guest.node, stdout, "Aendern von %s" % guest.label, 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): return self._guest_task(guest, "create", "status/resume", "Fortsetzen von %s" % guest.label) diff --git a/pvesnap/recovery.py b/pvesnap/recovery.py index b0cba1f..81b70a5 100644 --- a/pvesnap/recovery.py +++ b/pvesnap/recovery.py @@ -1209,12 +1209,24 @@ def spice_file(proxmox, instance): die virt-viewer erwartet. Sie enthaelt ein Einmal-Kennwort und laeuft nach kurzer Zeit ab - also frisch erzeugen, wenn sie nicht mehr zieht. """ - data = proxmox._json(["create", "/nodes/%s/%s/%d/spiceproxy" - % (instance.node, instance.type, instance.vmid)]) + try: + data = proxmox._json(["create", "/nodes/%s/%s/%d/spiceproxy" + % (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"): raise RecoveryError( - "%s liefert keine SPICE-Verbindung. Laeuft die Maschine, und ist " - "die SPICE-Anzeige eingeschaltet (vga: qxl)?" % instance.label) + "%s liefert keine SPICE-Verbindung. Laeuft die Maschine ueberhaupt?" + % instance.label) # Derselbe Grund wie beim noVNC-Verweis: der Proxy-Eintrag traegt den # Hostnamen des Nodes, und wenn der Arbeitsplatz ihn nicht aufloest, diff --git a/pvesnap/recovery_ui.py b/pvesnap/recovery_ui.py index 3d8129a..1e12cac 100644 --- a/pvesnap/recovery_ui.py +++ b/pvesnap/recovery_ui.py @@ -589,12 +589,20 @@ def _display(stdscr, proxmox, instance): # geladene Arbeitsspeicher ist danach weg - Proxmox gibt ihn schon # beim ersten Start frei, ein zweiter startet also kalt. 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: + warnung.append("") warnung.append("ACHTUNG: der geladene Arbeitsspeicher ist danach " "endgueltig weg. Die Maschine bootet kalt, offene " "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) for eintrag in warnung for line in _wrap(eintrag, 74)], "Neustart noetig", "j = trotzdem | andere Taste = abbrechen") @@ -612,12 +620,12 @@ def _display(stdscr, proxmox, instance): if not geaendert: message(stdscr, "War schon so eingestellt.") return - if neustart and confirm(stdscr, "%s jetzt neu starten?" % instance.label): - _do(stdscr, "Starte %s neu ..." % instance.label, - lambda: (proxmox.stop_guest(instance.guest), - start(proxmox, instance))[1]) + if neustart and confirm(stdscr, "%s jetzt sauber neu starten?" % instance.label): + _do(stdscr, "Fahre %s herunter und wieder hoch ..." % instance.label, + lambda: proxmox.reboot_guest(instance.guest)) 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: message(stdscr, "Uebernommen.")