Add LXC shell via ssh and pct enter
Press t on a running container to suspend the TUI and run `ssh -t root@<node> pct enter <vmid>`. cv4pve-cli has no shell or exec for LXC, so this goes around it and needs key-based root SSH to the PVE node. - VMs, stopped containers and busy guests get a warning instead. - Only exit code 255 (ssh failed) is reported as an error. Other codes are the last status of the container's shell. - OSError is caught inside suspend(). Textual's suspend() has no try/finally, so an exception escaping it would leave the app suspended. - The guest table reloads when the shell returns. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
62b79ab29e
commit
40feb125b4
4 files changed
+272
-1
No files matched your search
+37
-1
@@ -1,7 +1,9 @@
|
||||
"""Textual app for managing Proxmox guests through cv4pve-cli."""
|
||||
|
||||
import subprocess
|
||||
|
||||
from textual import work
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.app import App, ComposeResult, SuspendNotSupported
|
||||
from textual.containers import Horizontal, Vertical
|
||||
from textual.screen import ModalScreen
|
||||
from textual.widgets import (
|
||||
@@ -132,6 +134,7 @@ class GuestApp(App):
|
||||
("x", "act('stop')", "Stop"),
|
||||
("r", "act('reboot')", "Reboot"),
|
||||
("e", "edit", "Edit"),
|
||||
("t", "shell", "Shell"),
|
||||
("g", "reload", "Refresh"),
|
||||
("c", "context", "Context"),
|
||||
("q", "quit", "Quit"),
|
||||
@@ -336,6 +339,39 @@ class GuestApp(App):
|
||||
return
|
||||
self._open_edit(guest)
|
||||
|
||||
def action_shell(self) -> None:
|
||||
guest = self._selected()
|
||||
if guest is None:
|
||||
return
|
||||
who = f"{guest['name']} ({guest['vmid']})"
|
||||
if guest["type"] != "lxc":
|
||||
self.notify(f"{who} is not a container", severity="warning")
|
||||
return
|
||||
if guest["status"] != "running":
|
||||
self.notify(f"{who} is not running", severity="warning")
|
||||
return
|
||||
if guest["vmid"] in self.busy:
|
||||
self.notify(f"{who} is busy", severity="warning")
|
||||
return
|
||||
cmd = ["ssh", "-t", f"root@{guest['node']}", "pct", "enter", str(guest["vmid"])]
|
||||
error = None
|
||||
try:
|
||||
with self.suspend():
|
||||
try: # inside: Textual's suspend() skips the resume if its body raises
|
||||
code = subprocess.run(cmd, check=False).returncode
|
||||
except OSError as exc:
|
||||
error = _message(exc)
|
||||
except SuspendNotSupported as exc:
|
||||
error = _message(exc)
|
||||
else:
|
||||
if (
|
||||
error is None and code == 255
|
||||
): # other codes are the shell's own last status
|
||||
error = "ssh failed (exit 255)"
|
||||
if error:
|
||||
self.notify(f"Shell in {who} failed: {error}", severity="error")
|
||||
self.action_reload()
|
||||
|
||||
@work(thread=True)
|
||||
def _open_edit(self, guest: dict) -> None:
|
||||
try:
|
||||
|
||||
Reference in new issue
Block a user