Metadata-Version: 2.1
Name: edulution-linbo-vdi
Version: 1.1.10
Summary: VDI management for linuxmuster.net on Proxmox
Author-email: "edulution.io" <info@netzint.de>
License: AGPL-3.0-or-later
Project-URL: Homepage, https://github.com/edulution-io/edulution-linbo-vdi
Project-URL: Issues, https://github.com/edulution-io/edulution-linbo-vdi/issues
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.14
Classifier: Operating System :: POSIX :: Linux
Classifier: Topic :: System :: Systems Administration
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: paramiko >=2.12.0
Requires-Dist: proxmoxer >=2.0.1
Requires-Dist: requests >=2.31.0

# edulution-linbo-vdi

Virtual Desktop Infrastructure (VDI) for [linuxmuster.net](https://linuxmuster.net) on Proxmox VE.

The service builds a master VM from a LINBO image, converts it to a template,
keeps a pool of linked clones running, and assigns desktops to users. The
REST API that edulution-ui talks to lives in
[linuxmuster-api](https://github.com/linuxmuster/linuxmuster-api) (`/v1/vdi/*`)
and imports this package as a library.

Supported: Ubuntu 24.04 and 26.04 (also runs on 22.04 / Python 3.10),
Proxmox VE 8/9, Windows 10/11 and Linux images, BIOS and EFI.

## Documentation

| Document | Content |
|----------|---------|
| [docs/INSTALL.md](docs/INSTALL.md) | Proxmox preparation, package installation, `vdi-setup`, linuxmuster-api |
| [docs/CONFIGURATION.md](docs/CONFIGURATION.md) | `vdiConfig.json`, `start.conf.<group>.vdi`, `devices.csv` |
| [docs/IMAGE-PREPARATION.md](docs/IMAGE-PREPARATION.md) | What the Windows/Linux image needs (drivers, agent, firewall, hostname patch) |
| [docs/OPERATIONS.md](docs/OPERATIONS.md) | Commands, pool behaviour, troubleshooting, release procedure |
| [docs/SECURITY.md](docs/SECURITY.md) | Security model, credentials, stored data, hardening checklist |
| [docs/service-loop.mmd](docs/service-loop.mmd) | Flow chart of the service loop (Mermaid) |

## Quick start

```bash
# linuxmuster.net server
curl -fsSL https://package.edulution.io/pub.gpg | sudo gpg --dearmor -o /usr/share/keyrings/edulution.gpg
echo "deb [signed-by=/usr/share/keyrings/edulution.gpg] https://package.edulution.io/ $(. /etc/os-release && echo "$VERSION_CODENAME") main" \
  | sudo tee /etc/apt/sources.list.d/edulution.list
sudo apt-get update && sudo apt-get install edulution-linbo-vdi

sudo vdi-setup                     # Proxmox token, TLS, VDI groups, devices.csv
sudo linuxmuster-import-devices            # multi-school: -s <school>
journalctl -u edulution-linbo-vdi -f
```

Before that, create a dedicated Proxmox user and token (see
[docs/INSTALL.md](docs/INSTALL.md#2-prepare-proxmox)) and prepare the image
(see [docs/IMAGE-PREPARATION.md](docs/IMAGE-PREPARATION.md)).

## How it works

1. **Master** -- For each active group (`/srv/linbo/start.conf.<group>.vdi`)
   the service creates a VM with the configured hardware, tells LINBO via
   `linbo-remote` to partition, format and sync the image on the next boot,
   PXE-boots the VM, waits until the OS answers on the health-check ports
   (and stays up through Windows' post-install reboot), shuts it down and
   converts it to a template. A changed image or hardware config triggers a
   new master.
2. **Clones** -- Linked clones of the template get their MAC/VLAN from
   `devices.csv`, are started and health-checked. The pool stays between
   `minimum_vms` and `maximum_vms` with `prestarted_vms` idle desktops.
   Stopped clones are powered on, failed or outdated ones replaced, and a
   clone whose user session ended is destroyed and rebuilt
   (`recycleUsedClones`).
3. **Broker** -- `ConnectionBroker.get_connection(group, user)` returns the
   user's current desktop (detected via SMB session or QEMU guest agent), an
   unused one, or one whose previous assignment timed out, records the
   assignment in the VM description and returns a SPICE `.vv` file (path and
   content).

The package layout:

```
src/edulution_linbo_vdi/
  __init__.py        VdiContext -- config + Proxmox client + command runner
  config.py          VdiConfig (vdiConfig.json)
  device_store.py    start.conf, image.info, devices.csv, .vdi validation
  proxmox_client.py  proxmoxer wrapper (token/password, TLS, task waiting)
  command_runner.py  linbo-remote / smbstatus locally or via SSH (argv, no shell)
  smb_status.py      smbstatus -j / text parser -> user per client IP
  health_check.py    TCP connect checks (replaces nmap)
  vm_info.py         VM config/status/description fetching
  master.py          MasterManager: create, remove, get_states
  clone.py           CloneManager: build, start, remove, get_states
  broker.py          ConnectionBroker
  service.py         VdiService main loop + pool decision logic
  cli.py             vdi-service, vdi-states, vdi-remove-clone, vdi-setup
  templates/         example configs
```

## Use as a library

```python
from edulution_linbo_vdi import VdiContext
from edulution_linbo_vdi.broker import ConnectionBroker
from edulution_linbo_vdi.clone import CloneManager
from edulution_linbo_vdi.device_store import get_vdi_groups

ctx = VdiContext.from_config_file()            # /etc/edulution/linbo-vdi/vdiConfig.json
groups = get_vdi_groups()
states = CloneManager(ctx).get_states(groups["groups"]["win11-vdi"], "win11-vdi")
conn = ConnectionBroker(ctx).get_connection("win11-vdi", "lehrer01")
print(conn["vmid"], conn["configFile"])
print(conn["content"])                         # .vv for remote-viewer
```

The broker trusts the user name it is given; authenticate the caller first.
See [examples/usage_as_library.py](examples/usage_as_library.py).

## Development

```bash
git clone https://github.com/edulution-io/edulution-linbo-vdi.git
cd edulution-linbo-vdi
uv sync --group dev
uv run pytest                 # unit tests (no Proxmox needed)
uv run ruff check src tests
uv run bandit -c pyproject.toml -r src

# run against a real server without installing the package
uv run vdi-setup
uv run vdi-states -c
uv run vdi-service --once --debug
```

Build the Debian package in a clean container and test its lifecycle:

```bash
scripts/build-in-docker.sh                                  # -> build/*.deb, lintian
docker run --rm -v "$PWD:/w" ubuntu:26.04 bash /w/scripts/test-package-lifecycle.sh /w/build/edulution-linbo-vdi_*_all.deb
```

CI runs lint, tests on Python 3.10/3.12/3.14, the package build with lintian
and the install test on Ubuntu 24.04 and 26.04 for every push. Tags `v*`
release to GitHub and open an update PR on
[edulution-package-server](https://github.com/edulution-io/edulution-package-server);
see [docs/OPERATIONS.md](docs/OPERATIONS.md#release-procedure-maintainers).

## Security

See [SECURITY.md](SECURITY.md) for reporting and
[docs/SECURITY.md](docs/SECURITY.md) for the security model.

## License

AGPL-3.0-or-later. See [LICENSE](LICENSE).
