mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2026-10-31 16:37:42 -05:00
feat: virtual tree nodes + physical CPU site tree
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.
This commit is contained in:
1 parent
1943ae77f4
commit
69cfd6d77b
25 files changed
+1060
-113
No files matched your search
@@ -0,0 +1,80 @@
|
||||
import unittest
|
||||
|
||||
from virtual_tree_nodes import (
|
||||
build_logical_to_physical_node_map,
|
||||
build_physical_network,
|
||||
is_virtual_node,
|
||||
)
|
||||
|
||||
class TestVirtualTreeNodes(unittest.TestCase):
|
||||
def test_is_virtual_node(self):
|
||||
self.assertTrue(is_virtual_node({"virtual": True}))
|
||||
self.assertTrue(is_virtual_node({"type": "virtual"}))
|
||||
self.assertTrue(is_virtual_node({"type": "VIRTUAL"}))
|
||||
self.assertFalse(is_virtual_node({"virtual": False}))
|
||||
self.assertFalse(is_virtual_node({"type": "Site"}))
|
||||
self.assertFalse(is_virtual_node({}))
|
||||
|
||||
def test_logical_to_physical_mapping_and_physical_promotion(self):
|
||||
logical = {
|
||||
"Region": {
|
||||
"downloadBandwidthMbps": 1000,
|
||||
"uploadBandwidthMbps": 1000,
|
||||
"children": {
|
||||
"Town": {
|
||||
"downloadBandwidthMbps": 500,
|
||||
"uploadBandwidthMbps": 500,
|
||||
"virtual": True,
|
||||
"children": {
|
||||
"AP_A": {
|
||||
"downloadBandwidthMbps": 200,
|
||||
"uploadBandwidthMbps": 200,
|
||||
}
|
||||
},
|
||||
},
|
||||
"AP_B": {
|
||||
"downloadBandwidthMbps": 300,
|
||||
"uploadBandwidthMbps": 300,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
mapping, virtual_nodes = build_logical_to_physical_node_map(logical)
|
||||
self.assertIn("Town", virtual_nodes)
|
||||
self.assertEqual(mapping["Town"], "Region")
|
||||
self.assertEqual(mapping["Region"], "Region")
|
||||
self.assertEqual(mapping["AP_A"], "AP_A")
|
||||
|
||||
physical = build_physical_network(logical)
|
||||
self.assertIn("Region", physical)
|
||||
self.assertNotIn("Town", physical["Region"].get("children", {}))
|
||||
self.assertIn("AP_A", physical["Region"].get("children", {}))
|
||||
self.assertIn("AP_B", physical["Region"].get("children", {}))
|
||||
self.assertNotIn("virtual", physical["Region"])
|
||||
|
||||
def test_promotion_collision_raises(self):
|
||||
logical = {
|
||||
"Region": {
|
||||
"downloadBandwidthMbps": 1000,
|
||||
"uploadBandwidthMbps": 1000,
|
||||
"children": {
|
||||
"AP_A": {"downloadBandwidthMbps": 300, "uploadBandwidthMbps": 300},
|
||||
"Town": {
|
||||
"downloadBandwidthMbps": 500,
|
||||
"uploadBandwidthMbps": 500,
|
||||
"virtual": True,
|
||||
"children": {
|
||||
"AP_A": {"downloadBandwidthMbps": 200, "uploadBandwidthMbps": 200}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
build_physical_network(logical)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user