mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2026-10-24 22:07:40 -05:00
changes
This commit is contained in:
1 parent
a7ef49a3cb
commit
6e82dad7cd
2 files changed
+95
-77
No files matched your search
+4
-77
@@ -664,60 +664,6 @@ def planner_circuit_identity_key(circuit):
|
||||
return circuit_id
|
||||
|
||||
|
||||
def load_minor_state_from_queuing_structure(path=None):
|
||||
if path is None:
|
||||
path = get_queuing_structure_path()
|
||||
data = _load_json_dict(path)
|
||||
network = data.get("Network")
|
||||
if not isinstance(network, dict):
|
||||
return {"sites": {}, "circuits": {}}
|
||||
|
||||
sites = {}
|
||||
circuits = {}
|
||||
|
||||
def walk(node_map, trail=()):
|
||||
for node_name, node in sorted(node_map.items()):
|
||||
if not isinstance(node, dict):
|
||||
continue
|
||||
node_path = trail + (node_name,)
|
||||
site_key = "/".join(node_path)
|
||||
parent_path = "/".join(trail)
|
||||
queue = _parse_int_token(node.get("cpuNum"))
|
||||
class_minor = _parse_int_token(node.get("classMinor"))
|
||||
class_major = _parse_int_token(node.get("classMajor"))
|
||||
up_class_major = _parse_int_token(node.get("up_classMajor"))
|
||||
if queue is not None and class_minor is not None:
|
||||
sites[site_key] = {
|
||||
"class_minor": class_minor,
|
||||
"queue": queue + 1,
|
||||
"parent_path": parent_path,
|
||||
"class_major": class_major,
|
||||
"up_class_major": up_class_major,
|
||||
}
|
||||
|
||||
if isinstance(node.get("circuits"), list):
|
||||
for circuit in node.get("circuits", []):
|
||||
if not isinstance(circuit, dict):
|
||||
continue
|
||||
circuit_id = circuit.get("circuitID")
|
||||
circuit_minor = _parse_int_token(circuit.get("classMinor"))
|
||||
if queue is None or circuit_id is None or circuit_minor is None:
|
||||
continue
|
||||
circuits[str(circuit_id)] = {
|
||||
"class_minor": circuit_minor,
|
||||
"queue": queue + 1,
|
||||
"parent_node": circuit.get("ParentNode", node_name),
|
||||
"class_major": _parse_int_token(circuit.get("classMajor")),
|
||||
"up_class_major": _parse_int_token(circuit.get("up_classMajor")),
|
||||
}
|
||||
|
||||
children = node.get("children")
|
||||
if isinstance(children, dict):
|
||||
walk(children, node_path)
|
||||
|
||||
walk(network)
|
||||
return {"sites": sites, "circuits": circuits}
|
||||
|
||||
def calculateR2q(maxRateInMbps):
|
||||
# So we've learned that r2q defaults to 10, and is used to calculate quantum. Quantum is rateInBytes/r2q by
|
||||
# default. This default gives errors at high rates, and tc clamps the quantum to 200000. Setting a high quantum
|
||||
@@ -2029,9 +1975,8 @@ def refreshShapers():
|
||||
f"Failed to save planner state at {state_path}: {e}", stacklevel=2
|
||||
)
|
||||
|
||||
# Seed persisted site/circuit minor assignments. When planner state is absent,
|
||||
# fall back to the previous queuing structure so the first run after an upgrade
|
||||
# can preserve existing class IDs.
|
||||
# Load persisted top-level planner state. Full reloads rebuild TC, so
|
||||
# site/circuit class minors are runtime artifacts and are not reused below.
|
||||
try:
|
||||
state # noqa: B018
|
||||
except NameError:
|
||||
@@ -2039,24 +1984,6 @@ def refreshShapers():
|
||||
state_path = get_planner_state_path()
|
||||
if not isinstance(state, dict) or len(state.keys()) == 0:
|
||||
state = load_planner_state(state_path, None)
|
||||
try:
|
||||
circuit_state_from_disk = state.get("circuits", {}) if isinstance(state, dict) else {}
|
||||
except Exception:
|
||||
circuit_state_from_disk = {}
|
||||
try:
|
||||
site_state_from_disk = state.get("sites", {}) if isinstance(state, dict) else {}
|
||||
except Exception:
|
||||
site_state_from_disk = {}
|
||||
if not isinstance(circuit_state_from_disk, dict):
|
||||
circuit_state_from_disk = {}
|
||||
if not isinstance(site_state_from_disk, dict):
|
||||
site_state_from_disk = {}
|
||||
if not circuit_state_from_disk or not site_state_from_disk:
|
||||
fallback_minor_state = load_minor_state_from_queuing_structure()
|
||||
if not site_state_from_disk:
|
||||
site_state_from_disk = fallback_minor_state.get("sites", {}) or {}
|
||||
if not circuit_state_from_disk:
|
||||
circuit_state_from_disk = fallback_minor_state.get("circuits", {}) or {}
|
||||
circuit_state_updated = {}
|
||||
site_state_updated = {}
|
||||
planner_site_inputs = []
|
||||
@@ -2126,8 +2053,8 @@ def refreshShapers():
|
||||
identity_plan = plan_class_identities(
|
||||
planner_site_inputs,
|
||||
planner_circuit_groups,
|
||||
site_state=site_state_from_disk,
|
||||
circuit_state=circuit_state_from_disk,
|
||||
site_state={},
|
||||
circuit_state={},
|
||||
stick_offset=stickOffset,
|
||||
circuit_padding=CIRCUIT_PADDING,
|
||||
)
|
||||
|
||||
@@ -834,6 +834,97 @@ mod tests {
|
||||
assert_ne!(result.sites[0].class_minor, 0x29);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn class_identity_planner_compacts_when_full_reload_ignores_saved_minors() {
|
||||
const FRAGMENTED_SITE_MINOR: u16 = 0xff00;
|
||||
const FRAGMENTED_CHILD_MINOR: u16 = 0xff02;
|
||||
const FRAGMENTED_CIRCUIT_MINOR: u16 = 0xff10;
|
||||
const CIRCUIT_PADDING: u32 = 8;
|
||||
|
||||
let site_inputs = vec![
|
||||
SiteIdentityInput {
|
||||
site_key: "CpueQueue0/site-a".to_string(),
|
||||
parent_path: "CpueQueue0".to_string(),
|
||||
queue: 1,
|
||||
has_children: true,
|
||||
},
|
||||
SiteIdentityInput {
|
||||
site_key: "CpueQueue0/site-a/child".to_string(),
|
||||
parent_path: "CpueQueue0/site-a".to_string(),
|
||||
queue: 1,
|
||||
has_children: false,
|
||||
},
|
||||
];
|
||||
let circuit_groups = vec![CircuitIdentityGroupInput {
|
||||
parent_node: "child".to_string(),
|
||||
queue: 1,
|
||||
circuit_ids: vec!["c1".to_string(), "c2".to_string()],
|
||||
}];
|
||||
let fragmented_sites = BTreeMap::from([
|
||||
(
|
||||
"CpueQueue0/site-a".to_string(),
|
||||
PlannerSiteIdentityState {
|
||||
class_minor: FRAGMENTED_SITE_MINOR,
|
||||
queue: 1,
|
||||
parent_path: "CpueQueue0".to_string(),
|
||||
class_major: 1,
|
||||
up_class_major: 0x41,
|
||||
},
|
||||
),
|
||||
(
|
||||
"CpueQueue0/site-a/child".to_string(),
|
||||
PlannerSiteIdentityState {
|
||||
class_minor: FRAGMENTED_CHILD_MINOR,
|
||||
queue: 1,
|
||||
parent_path: "CpueQueue0/site-a".to_string(),
|
||||
class_major: 1,
|
||||
up_class_major: 0x41,
|
||||
},
|
||||
),
|
||||
]);
|
||||
let fragmented_circuits = BTreeMap::from([(
|
||||
"c1".to_string(),
|
||||
PlannerCircuitIdentityState {
|
||||
class_minor: FRAGMENTED_CIRCUIT_MINOR,
|
||||
queue: 1,
|
||||
parent_node: "child".to_string(),
|
||||
class_major: 1,
|
||||
up_class_major: 0x41,
|
||||
},
|
||||
)]);
|
||||
|
||||
let sticky_result = plan_class_identities(
|
||||
&site_inputs,
|
||||
&circuit_groups,
|
||||
&fragmented_sites,
|
||||
&fragmented_circuits,
|
||||
0x40,
|
||||
CIRCUIT_PADDING,
|
||||
);
|
||||
assert!(sticky_result.last_used_minor_by_queue[&1] > u32::from(FRAGMENTED_CIRCUIT_MINOR));
|
||||
|
||||
let full_reload_result = plan_class_identities(
|
||||
&site_inputs,
|
||||
&circuit_groups,
|
||||
&BTreeMap::new(),
|
||||
&BTreeMap::new(),
|
||||
0x40,
|
||||
CIRCUIT_PADDING,
|
||||
);
|
||||
|
||||
let expected_last_minor = u32::from(full_reload_result.circuits[1].class_minor)
|
||||
.saturating_add(1)
|
||||
.saturating_add(CIRCUIT_PADDING);
|
||||
assert_eq!(full_reload_result.sites[0].class_minor, 3);
|
||||
assert_eq!(full_reload_result.sites[1].class_minor, 5);
|
||||
assert_eq!(full_reload_result.circuits[0].class_minor, 6);
|
||||
assert_eq!(full_reload_result.circuits[1].class_minor, 7);
|
||||
assert_eq!(
|
||||
full_reload_result.last_used_minor_by_queue[&1],
|
||||
expected_last_minor
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn class_identity_planner_runtime_constraints_prefer_runtime_band() {
|
||||
let site_inputs = vec![SiteIdentityInput {
|
||||
|
||||
Reference in new issue
Block a user