mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2026-11-02 08:10:02 -06:00
changes
This commit is contained in:
1 parent
d27aaf4071
commit
0b5f0e8354
5 files changed
+133
-35
No files matched your search
@@ -12,6 +12,8 @@ StormGuard es el subsistema de ajuste adaptativo de colas de LibreQoS para event
|
||||
- Aplica ajustes acotados a límites de nodos configurados para proteger calidad bajo estrés.
|
||||
- Expone estado y depuración en WebUI.
|
||||
|
||||
Los cambios adaptativos de velocidad por sitio de StormGuard se guardan en la capa de overrides de StormGuard. No se escriben de vuelta en `network.json`.
|
||||
|
||||
## Configuración
|
||||
|
||||
StormGuard se configura en `/etc/lqos.conf` bajo `[stormguard]`.
|
||||
@@ -69,6 +71,7 @@ También verifique:
|
||||
- que los nombres en `targets` aún coincidan con `network.json`
|
||||
- que cambios de integración no hayan renombrado nodos clave
|
||||
- que los pisos mínimos sean razonables para su perfil de tráfico
|
||||
- que `network.json` siga reflejando sus velocidades planificadas/de origen si está investigando una reducción inesperada de StormGuard
|
||||
- que `log_file` (si se usa) sea escribible por el servicio
|
||||
|
||||
## Páginas relacionadas
|
||||
|
||||
@@ -12,6 +12,8 @@ StormGuard is LibreQoS' adaptive queue-adjustment subsystem for congestion and q
|
||||
- Applies bounded adjustments to configured node limits to protect quality under stress.
|
||||
- Exposes status/debug data in the WebUI (Node Manager).
|
||||
|
||||
StormGuard adaptive site-speed changes are stored in the StormGuard override layer. They are not written back into `network.json`.
|
||||
|
||||
## Configuration
|
||||
|
||||
StormGuard is configured in `/etc/lqos.conf` under `[stormguard]`.
|
||||
@@ -71,6 +73,7 @@ Also verify:
|
||||
- target node names still match your current `network.json` hierarchy
|
||||
- integration updates have not renamed key nodes/APs
|
||||
- your minimum percentage floors are reasonable for expected traffic profiles
|
||||
- `network.json` still reflects your planned/source-of-truth site rates if you are investigating an unexpected StormGuard reduction
|
||||
- `log_file` path (if configured) is writable by the service user
|
||||
|
||||
## Related Pages
|
||||
|
||||
@@ -677,6 +677,10 @@ fn liblqos_python(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
overrides_network_adjustments_effective,
|
||||
m
|
||||
)?)?;
|
||||
m.add_function(wrap_pyfunction!(
|
||||
overrides_network_adjustments_materialized,
|
||||
m
|
||||
)?)?;
|
||||
m.add_function(wrap_pyfunction!(is_network_flat, m)?)?;
|
||||
m.add_function(wrap_pyfunction!(blackboard_finish, m)?)?;
|
||||
m.add_function(wrap_pyfunction!(blackboard_submit, m)?)?;
|
||||
@@ -1229,38 +1233,7 @@ fn overrides_network_adjustments(py: Python<'_>) -> PyResult<Vec<PyObject>> {
|
||||
Err(e) => return Err(PyOSError::new_err(e.to_string())),
|
||||
};
|
||||
|
||||
let mut out: Vec<PyObject> = Vec::new();
|
||||
for adj in overrides.network_adjustments().iter() {
|
||||
let d = PyDict::new(py);
|
||||
match adj {
|
||||
lqos_overrides::NetworkAdjustment::AdjustSiteSpeed {
|
||||
site_name,
|
||||
download_bandwidth_mbps,
|
||||
upload_bandwidth_mbps,
|
||||
} => {
|
||||
d.set_item("type", "adjust_site_speed")?;
|
||||
d.set_item("site_name", site_name.clone())?;
|
||||
if let Some(v) = download_bandwidth_mbps {
|
||||
d.set_item("download_bandwidth_mbps", *v)?;
|
||||
}
|
||||
if let Some(v) = upload_bandwidth_mbps {
|
||||
d.set_item("upload_bandwidth_mbps", *v)?;
|
||||
}
|
||||
}
|
||||
lqos_overrides::NetworkAdjustment::SetNodeVirtual {
|
||||
node_name,
|
||||
virtual_node,
|
||||
} => {
|
||||
d.set_item("type", "set_node_virtual")?;
|
||||
d.set_item("node_name", node_name.clone())?;
|
||||
d.set_item("virtual", *virtual_node)?;
|
||||
}
|
||||
}
|
||||
let obj: PyObject = d.unbind().into();
|
||||
out.push(obj);
|
||||
}
|
||||
|
||||
Ok(out)
|
||||
network_adjustments_to_py(py, overrides.network_adjustments())
|
||||
}
|
||||
|
||||
/// Returns the list of network adjustments as Python dicts, using the effective overrides view
|
||||
@@ -1280,8 +1253,33 @@ fn overrides_network_adjustments_effective(py: Python<'_>) -> PyResult<Vec<PyObj
|
||||
Err(e) => return Err(PyOSError::new_err(e.to_string())),
|
||||
};
|
||||
|
||||
network_adjustments_to_py(py, overrides.network_adjustments())
|
||||
}
|
||||
|
||||
/// Returns the list of network adjustments that should be materialized into `network.json`.
|
||||
///
|
||||
/// This includes operator-owned network adjustments and TreeGuard virtual-node changes, but
|
||||
/// intentionally excludes StormGuard site-speed adjustments so adaptive runtime rates do not
|
||||
/// overwrite the operator-authored topology/source-of-truth file.
|
||||
#[pyfunction]
|
||||
fn overrides_network_adjustments_materialized(py: Python<'_>) -> PyResult<Vec<PyObject>> {
|
||||
let config = lqos_config::load_config().map_err(|e| PyOSError::new_err(e.to_string()))?;
|
||||
let apply_treeguard = config.treeguard.enabled;
|
||||
|
||||
let overrides = match lqos_overrides::OverrideStore::load_effective(false, apply_treeguard) {
|
||||
Ok(o) => o,
|
||||
Err(e) => return Err(PyOSError::new_err(e.to_string())),
|
||||
};
|
||||
|
||||
network_adjustments_to_py(py, overrides.network_adjustments())
|
||||
}
|
||||
|
||||
fn network_adjustments_to_py(
|
||||
py: Python<'_>,
|
||||
adjustments: &[lqos_overrides::NetworkAdjustment],
|
||||
) -> PyResult<Vec<PyObject>> {
|
||||
let mut out: Vec<PyObject> = Vec::new();
|
||||
for adj in overrides.network_adjustments().iter() {
|
||||
for adj in adjustments.iter() {
|
||||
let d = PyDict::new(py);
|
||||
match adj {
|
||||
lqos_overrides::NetworkAdjustment::AdjustSiteSpeed {
|
||||
|
||||
+7
-2
@@ -12,7 +12,8 @@ from liblqos_python import automatic_import_uisp, automatic_import_splynx, queue
|
||||
automatic_import_powercode, automatic_import_sonar, influx_db_enabled, get_libreqos_directory, \
|
||||
blackboard_finish, blackboard_submit, automatic_import_wispgate, enable_insight_topology, insight_topology_role, \
|
||||
automatic_import_netzur, automatic_import_visp, calculate_hash, efficiency_core_ids, scheduler_alive, scheduler_error, \
|
||||
overrides_persistent_devices_effective, overrides_circuit_adjustments_effective, overrides_network_adjustments_effective, \
|
||||
overrides_persistent_devices_effective, overrides_circuit_adjustments_effective, \
|
||||
overrides_network_adjustments_materialized, \
|
||||
scheduler_output
|
||||
|
||||
from apscheduler.schedulers.background import BlockingScheduler
|
||||
@@ -455,10 +456,14 @@ def apply_network_adjustments(network: dict) -> bool:
|
||||
Currently supports: adjust_site_speed (by site_name) updating
|
||||
downloadBandwidthMbps and uploadBandwidthMbps at the matching node, and
|
||||
set_node_virtual (by node_name) updating the boolean 'virtual' flag.
|
||||
|
||||
This path intentionally excludes StormGuard adaptive site-speed overrides so
|
||||
runtime StormGuard decisions do not overwrite the operator-authored
|
||||
`network.json` source of truth.
|
||||
Returns True if any changes were applied.
|
||||
"""
|
||||
try:
|
||||
adjustments = overrides_network_adjustments_effective()
|
||||
adjustments = overrides_network_adjustments_materialized()
|
||||
except Exception as e:
|
||||
print(f"Failed to read network adjustments: {e}")
|
||||
return False
|
||||
|
||||
@@ -34,6 +34,7 @@ def install_scheduler_stubs():
|
||||
lqlib.overrides_persistent_devices_effective = lambda: []
|
||||
lqlib.overrides_circuit_adjustments_effective = lambda: []
|
||||
lqlib.overrides_network_adjustments_effective = lambda: []
|
||||
lqlib.overrides_network_adjustments_materialized = lambda: []
|
||||
sys.modules["liblqos_python"] = lqlib
|
||||
|
||||
apscheduler_pkg = types.ModuleType("apscheduler")
|
||||
@@ -282,6 +283,94 @@ class TestSchedulerOverrideMerge(unittest.TestCase):
|
||||
self.assertEqual(written_rows[0][11], "330")
|
||||
self.assertEqual(written_rows[0][13], "fq_codel/fq_codel")
|
||||
|
||||
def test_apply_network_adjustments_uses_materialized_adjustments(self):
|
||||
network = {
|
||||
"Root": {
|
||||
"downloadBandwidthMbps": 1000,
|
||||
"uploadBandwidthMbps": 1000,
|
||||
"children": {
|
||||
"SiteA": {
|
||||
"downloadBandwidthMbps": 100,
|
||||
"uploadBandwidthMbps": 50,
|
||||
"children": {},
|
||||
},
|
||||
"NodeB": {
|
||||
"downloadBandwidthMbps": 200,
|
||||
"uploadBandwidthMbps": 100,
|
||||
"virtual": False,
|
||||
"children": {},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
with patch.object(
|
||||
scheduler,
|
||||
"overrides_network_adjustments_materialized",
|
||||
return_value=[
|
||||
{
|
||||
"type": "adjust_site_speed",
|
||||
"site_name": "SiteA",
|
||||
"download_bandwidth_mbps": 80,
|
||||
"upload_bandwidth_mbps": 40,
|
||||
},
|
||||
{
|
||||
"type": "set_node_virtual",
|
||||
"node_name": "NodeB",
|
||||
"virtual": True,
|
||||
},
|
||||
],
|
||||
):
|
||||
changed = scheduler.apply_network_adjustments(network)
|
||||
|
||||
self.assertTrue(changed)
|
||||
site = network["Root"]["children"]["SiteA"]
|
||||
self.assertEqual(site["downloadBandwidthMbps"], 80)
|
||||
self.assertEqual(site["uploadBandwidthMbps"], 40)
|
||||
self.assertTrue(network["Root"]["children"]["NodeB"]["virtual"])
|
||||
|
||||
def test_apply_network_adjustments_does_not_use_effective_stormguard_speeds(self):
|
||||
network = {
|
||||
"Root": {
|
||||
"downloadBandwidthMbps": 1000,
|
||||
"uploadBandwidthMbps": 1000,
|
||||
"children": {
|
||||
"Pine Hills": {
|
||||
"downloadBandwidthMbps": 940,
|
||||
"uploadBandwidthMbps": 500,
|
||||
"children": {},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
effective_adjustments = [
|
||||
{
|
||||
"type": "adjust_site_speed",
|
||||
"site_name": "Pine Hills",
|
||||
"download_bandwidth_mbps": 4,
|
||||
"upload_bandwidth_mbps": 4,
|
||||
}
|
||||
]
|
||||
|
||||
with patch.dict(
|
||||
scheduler.apply_network_adjustments.__globals__,
|
||||
{"overrides_network_adjustments_effective": lambda: effective_adjustments},
|
||||
clear=False,
|
||||
):
|
||||
with patch.object(
|
||||
scheduler,
|
||||
"overrides_network_adjustments_materialized",
|
||||
return_value=[],
|
||||
) as mock_materialized:
|
||||
changed = scheduler.apply_network_adjustments(network)
|
||||
|
||||
mock_materialized.assert_called_once_with()
|
||||
self.assertFalse(changed)
|
||||
site = network["Root"]["children"]["Pine Hills"]
|
||||
self.assertEqual(site["downloadBandwidthMbps"], 940)
|
||||
self.assertEqual(site["uploadBandwidthMbps"], 500)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user