Implements logical-only (virtual) nodes in network.json to improve CPU spreading while preserving monitoring hierarchy (NLNet milestones a/b/c).
Virtual nodes
- network.json supports "virtual": true (and legacy type=virtual) as logical-only nodes.
- Physical HTB topology skips virtual nodes and promotes their children to the nearest real ancestor.
- Writes logical->physical mapping + virtual node list into queuingStructure.json for debugging/inspection.
CPU placement + binpacking
- When binpacking is enabled but Insight/planner is unavailable, fall back to deterministic round-robin CPU assignment (still honors virtual promotion).
- Adds sanity checks/fallbacks to avoid degenerate single-bin assignments collapsing the physical tree.
UI + diagnostics
- Config/network UI supports toggling virtual state and shows a ghost icon for virtual nodes.
- Adds cpu_tree.html (ECharts) and websocket endpoint to visualize the PHYSICAL per-CPU site tree from queuingStructure.json.
Overrides
- lqos_overrides: add SetNodeVirtual adjustment plus CLI commands to view/edit/delete by node name.
- Scheduler applies virtual overrides onto network.json so periodic rebuilds don’t clobber them.
Config loader hardening
- lqos_config errors now reference /etc/lqos.conf (or LQOS_CONFIG) instead of deprecated ispConfig.py.
- Avoids caching initial config-load failures; can recover if config appears later.
- Treats versionless v1.5 configs as 1.5 by injecting version key; avoids accidental 1.4 migration.
- Removes a panic() in legacy TOML parsing path.
Misc
- Fix LibreQoS.py exit calls (os.exit -> os._exit) and add Python unittest coverage for virtual node promotion.