Skip to content

Commit b3bf4d1

Browse files
committed
Add VNC console bridge and expose API
1 parent ae5f382 commit b3bf4d1

9 files changed

Lines changed: 378 additions & 1 deletion

File tree

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,12 @@ sudo systemctl restart firecracker-cloudstack-agent.service
215215
- Each VM runs inside a detached tmux session named `fc-<vm_name>`; list active sessions with `tmux ls`.
216216
- Attach to the microVM console using `tmux attach -t fc-<vm_name>` and detach without stopping it via `Ctrl-b d`.
217217
- If a session is missing, the agent recreates it when the VM boots; use `tmux kill-session -t fc-<vm_name>` only for advanced troubleshooting.
218+
219+
### VNC Console Bridge
220+
- `POST /v1/vms/{name}/console` spawns an `Xvfb` + `xterm` + `x11vnc` bridge bound to the VM's tmux session and returns `{host, port, password}` ready for the CloudStack console proxy. `DELETE /v1/vms/{name}/console` tears it down.
221+
- The CLI helper now supports `firecracker.py console <payload.json>` to fetch the same tuple programmatically.
222+
- Runtime assets live under `/var/run/firecracker/vnc/` (state JSON, password files). Adjust `defaults.console` in the agent config to tune port ranges, bind address, window geometry, fonts, or read-only mode.
223+
- Ensure the host has `x11vnc`, `xterm`, and `xvfb` installed; the Debian packaging pulls these dependencies.
218224
---
219225
## CloudStack Integration
220226
1. **Install extension on the management server**:

firecracker.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,12 @@ def op_recover(ctx):
362362
data = _json_or_fail(_req("POST", url, ctx.agent, json_body=payload))
363363
_ok(data)
364364

365+
def op_console(ctx):
366+
"""POST /v1/vms/{name}/console — obtain VNC bridge connection info."""
367+
url = f"{ctx.agent.base_url}/vms/{ctx.vm_name}/console"
368+
data = _json_or_fail(_req("POST", url, ctx.agent))
369+
_ok(data)
370+
365371
# -------------------------- main --------------------------
366372
def main():
367373
"""Parse CLI, build context, dispatch operation over HTTP."""
@@ -382,6 +388,7 @@ def main():
382388
"status": op_status,
383389
"state": op_status,
384390
"recover": op_recover,
391+
"console": op_console,
385392
}
386393

387394
if operation not in ops:

host-agent/api/handlers.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from state import StateManager
2424
from utils.filesystem import paths
2525
from utils.validation import validate_name
26+
from utils.vnc_console import VNCConsoleManager
2627

2728
logger = logging.getLogger("fc-agent")
2829

@@ -36,6 +37,7 @@ def __init__(self, agent_defaults: Dict[str, Any], ui_config: Optional[Dict[str,
3637
self.vm_lifecycle = VMLifecycle(agent_defaults)
3738
self.config_manager = ConfigManager(agent_defaults)
3839
self.state_manager = StateManager(agent_defaults)
40+
self.vnc_console = VNCConsoleManager(agent_defaults)
3941

4042
def v1_ui_config(self) -> Dict[str, Any]:
4143
cfg = self.ui_config or {}
@@ -471,6 +473,40 @@ def v1_vm_reboot_by_name(self, vm_name: str) -> Dict[str, Any]:
471473
logger.exception("Reboot VM failed: %s", e)
472474
raise HTTPException(status_code=500, detail=f"Reboot VM failed: {e}")
473475

476+
def v1_vm_console_start(self, vm_name: str) -> Dict[str, Any]:
477+
"""Start (or reuse) the VNC bridge for a VM console."""
478+
try:
479+
info = self.vnc_console.ensure_console(vm_name)
480+
return {
481+
"status": "success",
482+
"message": f"VNC console ready for VM {vm_name}",
483+
"vm_name": vm_name,
484+
"host": info.get("host"),
485+
"port": info.get("port"),
486+
"password": info.get("password"),
487+
"created_at": info.get("created_at"),
488+
}
489+
except RuntimeError as exc:
490+
raise HTTPException(status_code=400, detail=str(exc)) from exc
491+
except HTTPException:
492+
raise
493+
except Exception as exc:
494+
logger.exception("Failed to start VNC console for VM %s: %s", vm_name, exc)
495+
raise HTTPException(status_code=500, detail=f"Failed to start VNC console: {exc}")
496+
497+
def v1_vm_console_stop(self, vm_name: str) -> Dict[str, Any]:
498+
"""Stop the VNC bridge for a VM console."""
499+
try:
500+
info = self.vnc_console.stop_console(vm_name)
501+
if isinstance(info, dict) and "vm_name" not in info:
502+
info["vm_name"] = vm_name
503+
return info
504+
except HTTPException:
505+
raise
506+
except Exception as exc:
507+
logger.exception("Failed to stop VNC console for VM %s: %s", vm_name, exc)
508+
raise HTTPException(status_code=500, detail=f"Failed to stop VNC console: {exc}")
509+
474510
def v1_vm_start_by_name(self, vm_name: str, req: SpecRequest) -> Dict[str, Any]:
475511
"""Start VM by name (uses existing config on disk)."""
476512
try:
@@ -504,6 +540,7 @@ def v1_index(self) -> Dict[str, Any]:
504540
"/v1/vms/{name}/start",
505541
"/v1/vms/{name}/stop",
506542
"/v1/vms/{name}/reboot",
543+
"/v1/vms/{name}/console",
507544
"/v1/vms/{name}",
508545
"/v1/vms/{name}/recover",
509546
"/v1/vms/{name}/status",

host-agent/api/routes.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@ def v1_vm_reboot_by_name(vm_name: str):
8686
def v1_vm_recover_by_name(vm_name: str, req: Optional[SpecRequest] = None):
8787
return handlers.v1_vm_recover_by_name(vm_name, req)
8888

89+
@app.post("/v1/vms/{vm_name}/console", **protected)
90+
def v1_vm_console_start(vm_name: str):
91+
return handlers.v1_vm_console_start(vm_name)
92+
93+
@app.delete("/v1/vms/{vm_name}/console", **protected)
94+
def v1_vm_console_stop(vm_name: str):
95+
return handlers.v1_vm_console_stop(vm_name)
96+
8997
# System management endpoints
9098
@app.post("/v1/graceful-shutdown", **protected)
9199
def v1_graceful_shutdown():

host-agent/debian/changelog

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
firecracker-cloudstack-agent (0.4.0-1) unstable; urgency=medium
2+
3+
* Add a VNC console bridge leveraging Xvfb/xterm/x11vnc and expose it via
4+
the new `/v1/vms/{name}/console` API endpoints.
5+
* Extend the firecracker.py client with a `console` action and update the
6+
documentation with instructions for integrating the console proxy.
7+
* Ship the new console manager module in the Debian package and pull in the
8+
required x11vnc/xterm/xvfb packages as dependencies.
9+
10+
-- Marco Sinhoreli <msinhore@gmail.com> Fri, 17 Oct 2025 10:15:00 +0000
11+
112
firecracker-cloudstack-agent (0.3.2-1) unstable; urgency=medium
213

314
* Fix VM provisioning to honor `maxRam` from CloudStack payloads when

host-agent/debian/control

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Build-Depends: debhelper-compat (= 13), nodejs, npm
88

99
Package: firecracker-cloudstack-agent
1010
Architecture: all
11-
Depends: ${misc:Depends}, jq, curl, iproute2, bridge-utils, tmux, openssl, python3-pamela, python3-uvicorn, python3-psutil, python3-fastapi, python3-libtmux, python3-pyroute2, python3-typer, python3-openvswitch, python3-ovsdbapp
11+
Depends: ${misc:Depends}, jq, curl, iproute2, bridge-utils, tmux, openssl, python3-pamela, python3-uvicorn, python3-psutil, python3-fastapi, python3-libtmux, python3-pyroute2, python3-typer, python3-openvswitch, python3-ovsdbapp, x11vnc, xterm, xvfb
1212
Recommends: openvswitch-switch
1313
Description: Firecracker agent with pluggable storage and networking backends for CloudStack
1414
A local HTTP API/CLI to manage Firecracker microVMs with file/LVM/LVM-thin storage backends

host-agent/debian/firecracker-agent.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@
1818
"net": {
1919
"driver": "linux-bridge-vlan",
2020
"host_bridge": "cloudbr1"
21+
},
22+
"console": {
23+
"bind_host": "0.0.0.0",
24+
"port_min": 5900,
25+
"port_max": 5999,
26+
"geometry": "1024x768x24",
27+
"xterm_geometry": "132x44",
28+
"font_family": "Monospace",
29+
"font_size": 14
2130
}
2231
},
2332
"security": {

host-agent/debian/install

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ host-agent/utils/auth.py usr/lib/firecracker-cloudsta
2424
host-agent/utils/validation.py usr/lib/firecracker-cloudstack-agent/utils/
2525
host-agent/utils/filesystem.py usr/lib/firecracker-cloudstack-agent/utils/
2626
host-agent/utils/tmux.py usr/lib/firecracker-cloudstack-agent/utils/
27+
host-agent/utils/vnc_console.py usr/lib/firecracker-cloudstack-agent/utils/
2728
host-agent/tools/fc_shutdown_service.py usr/lib/firecracker-cloudstack-agent/tools/
2829
host-agent/api/__init__.py usr/lib/firecracker-cloudstack-agent/api/
2930
host-agent/api/handlers.py usr/lib/firecracker-cloudstack-agent/api/

0 commit comments

Comments
 (0)