mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2026-10-25 06:17:39 -05:00
changes
This commit is contained in:
1 parent
aee375bf84
commit
bed2a0535c
2 files changed
+633
-2
No files matched your search
@@ -789,10 +789,147 @@ pub(crate) fn legacy_id_for_name(name: &str) -> String {
|
||||
fn legacy_node_id(node: &Map<String, Value>, fallback_name: &str) -> String {
|
||||
node.get("id")
|
||||
.and_then(Value::as_str)
|
||||
.filter(|id| !id.trim().is_empty())
|
||||
.map(ToOwned::to_owned)
|
||||
.unwrap_or_else(|| legacy_id_for_name(fallback_name))
|
||||
}
|
||||
|
||||
type LegacyIdByTreeParent<'a> = HashMap<(&'a str, &'a str), Option<&'a str>>;
|
||||
type LegacyIdByParentName<'a> = HashMap<(&'a str, Option<&'a str>), Option<&'a str>>;
|
||||
type LegacyIdByName<'a> = HashMap<&'a str, Option<&'a str>>;
|
||||
|
||||
fn legacy_node_id_lookups(
|
||||
nodes: &[TopologyCanonicalNode],
|
||||
) -> (
|
||||
LegacyIdByTreeParent<'_>,
|
||||
LegacyIdByParentName<'_>,
|
||||
LegacyIdByName<'_>,
|
||||
) {
|
||||
let mut by_tree_parent_id = HashMap::new();
|
||||
let mut by_parent_name = HashMap::new();
|
||||
let mut by_name = HashMap::new();
|
||||
|
||||
for node in nodes {
|
||||
let node_id = (!node.node_id.trim().is_empty()).then_some(node.node_id.as_str());
|
||||
if let Some(tree_parent_id) = node.current_attachment_id.as_deref() {
|
||||
by_tree_parent_id
|
||||
.entry((node.node_name.as_str(), tree_parent_id))
|
||||
.and_modify(|entry| *entry = None)
|
||||
.or_insert(node_id);
|
||||
}
|
||||
by_parent_name
|
||||
.entry((
|
||||
node.node_name.as_str(),
|
||||
node.current_parent_node_name.as_deref(),
|
||||
))
|
||||
.and_modify(|entry| *entry = None)
|
||||
.or_insert(node_id);
|
||||
by_name
|
||||
.entry(node.node_name.as_str())
|
||||
.and_modify(|entry| *entry = None)
|
||||
.or_insert(node_id);
|
||||
}
|
||||
|
||||
(by_tree_parent_id, by_parent_name, by_name)
|
||||
}
|
||||
|
||||
fn legacy_node_id_from_lookups<'a>(
|
||||
node_name: &str,
|
||||
tree_parent_id: Option<&str>,
|
||||
parent_node_name: Option<&str>,
|
||||
by_tree_parent_id: &LegacyIdByTreeParent<'a>,
|
||||
by_parent_name: &LegacyIdByParentName<'a>,
|
||||
by_name: &LegacyIdByName<'a>,
|
||||
) -> Option<&'a str> {
|
||||
tree_parent_id
|
||||
.and_then(|parent_id| by_tree_parent_id.get(&(node_name, parent_id)))
|
||||
.copied()
|
||||
.flatten()
|
||||
.or_else(|| {
|
||||
by_parent_name
|
||||
.get(&(node_name, parent_node_name))
|
||||
.copied()
|
||||
.flatten()
|
||||
})
|
||||
.or_else(|| by_name.get(node_name).copied().flatten())
|
||||
}
|
||||
|
||||
fn add_missing_legacy_node_ids(
|
||||
map: &mut Map<String, Value>,
|
||||
parent_node_id: Option<&str>,
|
||||
parent_node_name: Option<&str>,
|
||||
by_tree_parent_id: &LegacyIdByTreeParent<'_>,
|
||||
by_parent_name: &LegacyIdByParentName<'_>,
|
||||
by_name: &LegacyIdByName<'_>,
|
||||
) {
|
||||
for (key, value) in map {
|
||||
let Some(node) = value.as_object_mut() else {
|
||||
continue;
|
||||
};
|
||||
let node_name = node
|
||||
.get("name")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or(key)
|
||||
.to_string();
|
||||
if node
|
||||
.get("id")
|
||||
.and_then(Value::as_str)
|
||||
.is_none_or(|id| id.trim().is_empty())
|
||||
{
|
||||
let node_id = legacy_node_id_from_lookups(
|
||||
&node_name,
|
||||
parent_node_id,
|
||||
parent_node_name,
|
||||
by_tree_parent_id,
|
||||
by_parent_name,
|
||||
by_name,
|
||||
)
|
||||
.map(ToOwned::to_owned)
|
||||
.unwrap_or_else(|| legacy_id_for_name(&node_name));
|
||||
node.insert("id".to_string(), Value::String(node_id));
|
||||
}
|
||||
let node_id = node
|
||||
.get("id")
|
||||
.and_then(Value::as_str)
|
||||
.map(ToOwned::to_owned);
|
||||
if let Some(children) = node.get_mut("children").and_then(Value::as_object_mut) {
|
||||
add_missing_legacy_node_ids(
|
||||
children,
|
||||
node_id.as_deref(),
|
||||
Some(&node_name),
|
||||
by_tree_parent_id,
|
||||
by_parent_name,
|
||||
by_name,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_legacy_canonical_node_ids(nodes: &mut [TopologyCanonicalNode]) {
|
||||
for node in nodes.iter_mut() {
|
||||
if node.node_id.trim().is_empty() {
|
||||
node.node_id = legacy_id_for_name(&node.node_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_legacy_network_json_node_ids(
|
||||
network_json: &mut Value,
|
||||
canonical_nodes: &[TopologyCanonicalNode],
|
||||
) {
|
||||
if let Some(map) = network_json.as_object_mut() {
|
||||
let (by_tree_parent_id, by_parent_name, by_name) = legacy_node_id_lookups(canonical_nodes);
|
||||
add_missing_legacy_node_ids(
|
||||
map,
|
||||
None,
|
||||
None,
|
||||
&by_tree_parent_id,
|
||||
&by_parent_name,
|
||||
&by_name,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn import_legacy_network_children(
|
||||
map: &Map<String, Value>,
|
||||
logical_parent: Option<(String, String)>,
|
||||
@@ -905,7 +1042,9 @@ impl TopologyCanonicalStateFile {
|
||||
return Ok(Self::default());
|
||||
}
|
||||
let raw = std::fs::read_to_string(path)?;
|
||||
Ok(serde_json::from_str(&raw)?)
|
||||
let mut state: Self = serde_json::from_str(&raw)?;
|
||||
state.normalize_legacy_compatibility_network_json_ids();
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
/// Loads canonical topology state, falling back to importing legacy `network.json`
|
||||
@@ -1213,7 +1352,10 @@ impl TopologyCanonicalStateFile {
|
||||
if let Some(map) = network_json.as_object() {
|
||||
import_legacy_network_children(map, None, None, &mut nodes);
|
||||
}
|
||||
normalize_legacy_canonical_node_ids(&mut nodes);
|
||||
nodes.sort_unstable_by(|left, right| left.node_id.cmp(&right.node_id));
|
||||
let mut compatibility_network_json = network_json.clone();
|
||||
normalize_legacy_network_json_node_ids(&mut compatibility_network_json, &nodes);
|
||||
Self {
|
||||
schema_version: default_topology_canonical_schema_version(),
|
||||
source: "legacy/network.json".to_string(),
|
||||
@@ -1221,7 +1363,22 @@ impl TopologyCanonicalStateFile {
|
||||
ingress_identity: topology_ingress_fingerprint_for_network_json(network_json),
|
||||
ingress_kind: TopologyCanonicalIngressKind::LegacyNetworkJson,
|
||||
nodes,
|
||||
compatibility_network_json: network_json.clone(),
|
||||
compatibility_network_json,
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_legacy_compatibility_network_json_ids(&mut self) {
|
||||
if self.ingress_kind == TopologyCanonicalIngressKind::LegacyNetworkJson
|
||||
|| self.source == "legacy/network.json"
|
||||
{
|
||||
self.ingress_kind = TopologyCanonicalIngressKind::LegacyNetworkJson;
|
||||
normalize_legacy_canonical_node_ids(&mut self.nodes);
|
||||
self.nodes
|
||||
.sort_unstable_by(|left, right| left.node_id.cmp(&right.node_id));
|
||||
normalize_legacy_network_json_node_ids(
|
||||
&mut self.compatibility_network_json,
|
||||
&self.nodes,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1597,6 +1754,346 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_network_json_import_adds_missing_ids_to_compatibility_tree() {
|
||||
let canonical = TopologyCanonicalStateFile::from_legacy_network_json(&json!({
|
||||
"Globe": {
|
||||
"children": {},
|
||||
"downloadBandwidthMbps": 500,
|
||||
"type": "site",
|
||||
"uploadBandwidthMbps": 500
|
||||
},
|
||||
"PLDT": {
|
||||
"children": {
|
||||
"Existing AP": {
|
||||
"children": {},
|
||||
"id": "operator-ap-id",
|
||||
"type": "ap"
|
||||
},
|
||||
"Nested AP": {
|
||||
"children": {
|
||||
"Leaf Site": {
|
||||
"children": {},
|
||||
"id": null,
|
||||
"type": "site"
|
||||
}
|
||||
},
|
||||
"type": "ap"
|
||||
},
|
||||
"Bad ID AP": {
|
||||
"children": {},
|
||||
"id": 0,
|
||||
"type": "ap"
|
||||
},
|
||||
"Blank ID AP": {
|
||||
"children": {},
|
||||
"id": "",
|
||||
"type": "ap"
|
||||
}
|
||||
},
|
||||
"downloadBandwidthMbps": 500,
|
||||
"type": "site",
|
||||
"uploadBandwidthMbps": 500
|
||||
}
|
||||
}));
|
||||
|
||||
assert_eq!(
|
||||
canonical.compatibility_network_json["Globe"]["id"].as_str(),
|
||||
Some(legacy_id_for_name("Globe").as_str())
|
||||
);
|
||||
assert_eq!(
|
||||
canonical.compatibility_network_json["PLDT"]["id"].as_str(),
|
||||
Some(legacy_id_for_name("PLDT").as_str())
|
||||
);
|
||||
assert_eq!(
|
||||
canonical.compatibility_network_json["PLDT"]["children"]["Existing AP"]["id"].as_str(),
|
||||
Some("operator-ap-id")
|
||||
);
|
||||
assert_eq!(
|
||||
canonical.compatibility_network_json["PLDT"]["children"]["Nested AP"]["id"].as_str(),
|
||||
Some(legacy_id_for_name("Nested AP").as_str())
|
||||
);
|
||||
assert_eq!(
|
||||
canonical.compatibility_network_json["PLDT"]["children"]["Nested AP"]["children"]
|
||||
["Leaf Site"]["id"]
|
||||
.as_str(),
|
||||
Some(legacy_id_for_name("Leaf Site").as_str())
|
||||
);
|
||||
assert_eq!(
|
||||
canonical.compatibility_network_json["PLDT"]["children"]["Bad ID AP"]["id"].as_str(),
|
||||
Some(legacy_id_for_name("Bad ID AP").as_str())
|
||||
);
|
||||
assert_eq!(
|
||||
canonical.compatibility_network_json["PLDT"]["children"]["Blank ID AP"]["id"].as_str(),
|
||||
Some(legacy_id_for_name("Blank ID AP").as_str())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_normalizes_old_legacy_canonical_state_missing_compatibility_ids() {
|
||||
let lqos_directory = unique_temp_dir("lqos-config-canonical-id-heal");
|
||||
let topology_state_dir = lqos_directory.join("state/topology");
|
||||
fs::create_dir_all(&topology_state_dir).expect("topology state dir should exist");
|
||||
fs::write(
|
||||
topology_state_dir.join(TOPOLOGY_CANONICAL_STATE_FILENAME),
|
||||
serde_json::to_string_pretty(&json!({
|
||||
"schema_version": 1,
|
||||
"source": "legacy/network.json",
|
||||
"nodes": [
|
||||
{
|
||||
"node_id": "operator-globe-id",
|
||||
"node_name": "Globe",
|
||||
"node_kind": "site"
|
||||
},
|
||||
{
|
||||
"node_id": "operator-nested-ap-id",
|
||||
"node_name": "Nested AP",
|
||||
"node_kind": "ap",
|
||||
"current_parent_node_name": "Globe"
|
||||
},
|
||||
{
|
||||
"node_id": "operator-pldt-id",
|
||||
"node_name": "PLDT",
|
||||
"node_kind": "site"
|
||||
},
|
||||
{
|
||||
"node_id": "operator-pldt-nested-ap-id",
|
||||
"node_name": "Nested AP",
|
||||
"node_kind": "ap",
|
||||
"current_parent_node_name": "PLDT"
|
||||
},
|
||||
{
|
||||
"node_id": "operator-alpha-id",
|
||||
"node_name": "Alpha",
|
||||
"node_kind": "site"
|
||||
},
|
||||
{
|
||||
"node_id": "operator-beta-id",
|
||||
"node_name": "Beta",
|
||||
"node_kind": "site"
|
||||
},
|
||||
{
|
||||
"node_id": "operator-alpha-relay-id",
|
||||
"node_name": "Relay",
|
||||
"node_kind": "ap",
|
||||
"current_parent_node_id": "operator-alpha-id",
|
||||
"current_parent_node_name": "Alpha",
|
||||
"current_attachment_id": "operator-alpha-id"
|
||||
},
|
||||
{
|
||||
"node_id": "operator-beta-relay-id",
|
||||
"node_name": "Relay",
|
||||
"node_kind": "ap",
|
||||
"current_parent_node_id": "operator-beta-id",
|
||||
"current_parent_node_name": "Beta",
|
||||
"current_attachment_id": "operator-beta-id"
|
||||
},
|
||||
{
|
||||
"node_id": "operator-alpha-shared-ap-id",
|
||||
"node_name": "Shared AP",
|
||||
"node_kind": "ap",
|
||||
"current_parent_node_id": "operator-alpha-id",
|
||||
"current_parent_node_name": "Alpha",
|
||||
"current_attachment_id": "operator-alpha-relay-id",
|
||||
"current_attachment_name": "Relay"
|
||||
},
|
||||
{
|
||||
"node_id": "operator-beta-shared-ap-id",
|
||||
"node_name": "Shared AP",
|
||||
"node_kind": "ap",
|
||||
"current_parent_node_id": "operator-beta-id",
|
||||
"current_parent_node_name": "Beta",
|
||||
"current_attachment_id": "operator-beta-relay-id",
|
||||
"current_attachment_name": "Relay"
|
||||
},
|
||||
{
|
||||
"node_id": "",
|
||||
"node_name": "Blank Canonical",
|
||||
"node_kind": "site"
|
||||
}
|
||||
],
|
||||
"compatibility_network_json": {
|
||||
"Globe": {
|
||||
"children": {
|
||||
"Nested AP": {
|
||||
"children": {},
|
||||
"id": false,
|
||||
"type": "ap"
|
||||
}
|
||||
},
|
||||
"downloadBandwidthMbps": 500,
|
||||
"type": "site",
|
||||
"uploadBandwidthMbps": 500
|
||||
},
|
||||
"PLDT": {
|
||||
"children": {
|
||||
"Nested AP": {
|
||||
"children": {},
|
||||
"type": "ap"
|
||||
}
|
||||
},
|
||||
"downloadBandwidthMbps": 500,
|
||||
"type": "site",
|
||||
"uploadBandwidthMbps": 500
|
||||
},
|
||||
"Alpha": {
|
||||
"children": {
|
||||
"Relay": {
|
||||
"children": {
|
||||
"Shared AP": {
|
||||
"children": {},
|
||||
"type": "ap"
|
||||
}
|
||||
},
|
||||
"type": "ap"
|
||||
}
|
||||
},
|
||||
"downloadBandwidthMbps": 500,
|
||||
"type": "site",
|
||||
"uploadBandwidthMbps": 500
|
||||
},
|
||||
"Beta": {
|
||||
"children": {
|
||||
"Relay": {
|
||||
"children": {
|
||||
"Shared AP": {
|
||||
"children": {},
|
||||
"type": "ap"
|
||||
}
|
||||
},
|
||||
"type": "ap"
|
||||
}
|
||||
},
|
||||
"downloadBandwidthMbps": 500,
|
||||
"type": "site",
|
||||
"uploadBandwidthMbps": 500
|
||||
},
|
||||
"Blank Canonical": {
|
||||
"children": {},
|
||||
"id": "",
|
||||
"downloadBandwidthMbps": 500,
|
||||
"type": "site",
|
||||
"uploadBandwidthMbps": 500
|
||||
}
|
||||
}
|
||||
}))
|
||||
.expect("canonical state should serialize"),
|
||||
)
|
||||
.expect("canonical state should write");
|
||||
|
||||
let config = Config {
|
||||
lqos_directory: lqos_directory.to_string_lossy().to_string(),
|
||||
state_directory: None,
|
||||
..Config::default()
|
||||
};
|
||||
|
||||
let loaded =
|
||||
TopologyCanonicalStateFile::load(&config).expect("canonical state should load");
|
||||
assert_eq!(
|
||||
loaded.ingress_kind,
|
||||
TopologyCanonicalIngressKind::LegacyNetworkJson
|
||||
);
|
||||
assert_eq!(
|
||||
loaded.compatibility_network_json["Globe"]["id"].as_str(),
|
||||
Some("operator-globe-id")
|
||||
);
|
||||
assert_eq!(
|
||||
loaded.compatibility_network_json["Globe"]["children"]["Nested AP"]["id"].as_str(),
|
||||
Some("operator-nested-ap-id")
|
||||
);
|
||||
assert_eq!(
|
||||
loaded.compatibility_network_json["PLDT"]["id"].as_str(),
|
||||
Some("operator-pldt-id")
|
||||
);
|
||||
assert_eq!(
|
||||
loaded.compatibility_network_json["PLDT"]["children"]["Nested AP"]["id"].as_str(),
|
||||
Some("operator-pldt-nested-ap-id")
|
||||
);
|
||||
assert_eq!(
|
||||
loaded.compatibility_network_json["Alpha"]["children"]["Relay"]["id"].as_str(),
|
||||
Some("operator-alpha-relay-id")
|
||||
);
|
||||
assert_eq!(
|
||||
loaded.compatibility_network_json["Beta"]["children"]["Relay"]["id"].as_str(),
|
||||
Some("operator-beta-relay-id")
|
||||
);
|
||||
assert_eq!(
|
||||
loaded.compatibility_network_json["Alpha"]["children"]["Relay"]["children"]
|
||||
["Shared AP"]["id"]
|
||||
.as_str(),
|
||||
Some("operator-alpha-shared-ap-id")
|
||||
);
|
||||
assert_eq!(
|
||||
loaded.compatibility_network_json["Beta"]["children"]["Relay"]["children"]["Shared AP"]
|
||||
["id"]
|
||||
.as_str(),
|
||||
Some("operator-beta-shared-ap-id")
|
||||
);
|
||||
let blank_canonical_id = legacy_id_for_name("Blank Canonical");
|
||||
assert!(
|
||||
loaded
|
||||
.nodes
|
||||
.iter()
|
||||
.any(|node| node.node_name == "Blank Canonical"
|
||||
&& node.node_id == blank_canonical_id)
|
||||
);
|
||||
assert_eq!(
|
||||
loaded.compatibility_network_json["Blank Canonical"]["id"].as_str(),
|
||||
Some(blank_canonical_id.as_str())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_id_healing_uses_hash_when_name_only_lookup_is_ambiguous() {
|
||||
let lqos_directory = unique_temp_dir("lqos-config-canonical-id-ambiguous");
|
||||
let topology_state_dir = lqos_directory.join("state/topology");
|
||||
fs::create_dir_all(&topology_state_dir).expect("topology state dir should exist");
|
||||
fs::write(
|
||||
topology_state_dir.join(TOPOLOGY_CANONICAL_STATE_FILENAME),
|
||||
serde_json::to_string_pretty(&json!({
|
||||
"schema_version": 1,
|
||||
"source": "legacy/network.json",
|
||||
"nodes": [
|
||||
{
|
||||
"node_id": "operator-shared-ap-a",
|
||||
"node_name": "Shared AP",
|
||||
"node_kind": "ap",
|
||||
"current_parent_node_id": "parent-a",
|
||||
"current_parent_node_name": "Parent A"
|
||||
},
|
||||
{
|
||||
"node_id": "operator-shared-ap-b",
|
||||
"node_name": "Shared AP",
|
||||
"node_kind": "ap",
|
||||
"current_parent_node_id": "parent-b",
|
||||
"current_parent_node_name": "Parent B"
|
||||
}
|
||||
],
|
||||
"compatibility_network_json": {
|
||||
"Shared AP": {
|
||||
"children": {},
|
||||
"type": "ap"
|
||||
}
|
||||
}
|
||||
}))
|
||||
.expect("canonical state should serialize"),
|
||||
)
|
||||
.expect("canonical state should write");
|
||||
|
||||
let config = Config {
|
||||
lqos_directory: lqos_directory.to_string_lossy().to_string(),
|
||||
state_directory: None,
|
||||
..Config::default()
|
||||
};
|
||||
let loaded =
|
||||
TopologyCanonicalStateFile::load(&config).expect("canonical state should load");
|
||||
|
||||
assert_eq!(
|
||||
loaded.compatibility_network_json["Shared AP"]["id"].as_str(),
|
||||
Some(legacy_id_for_name("Shared AP").as_str())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stale_canonical_state_is_quarantined_when_network_ingress_changes() {
|
||||
let lqos_directory = unique_temp_dir("lqos-config-canonical-quarantine");
|
||||
|
||||
@@ -834,6 +834,140 @@
|
||||
}
|
||||
|
||||
|
||||
fn load_stale_idless_legacy_canonical_state() -> (Config, TopologyCanonicalStateFile) {
|
||||
let lqos_directory = unique_temp_dir("lqos-topology-legacy-id-heal");
|
||||
let config = Config {
|
||||
lqos_directory: lqos_directory.to_string_lossy().to_string(),
|
||||
state_directory: None,
|
||||
..Config::default()
|
||||
};
|
||||
let mut stale_canonical = TopologyCanonicalStateFile::from_legacy_network_json(&json!({
|
||||
"Globe": {
|
||||
"children": {
|
||||
"Nested AP": {
|
||||
"children": {},
|
||||
"downloadBandwidthMbps": 250,
|
||||
"type": "ap",
|
||||
"uploadBandwidthMbps": 250
|
||||
}
|
||||
},
|
||||
"downloadBandwidthMbps": 500,
|
||||
"type": "site",
|
||||
"uploadBandwidthMbps": 500
|
||||
},
|
||||
"PLDT": {
|
||||
"children": {},
|
||||
"downloadBandwidthMbps": 500,
|
||||
"type": "site",
|
||||
"uploadBandwidthMbps": 500
|
||||
}
|
||||
}));
|
||||
stale_canonical.compatibility_network_json["Globe"]
|
||||
.as_object_mut()
|
||||
.expect("Globe should be an object")
|
||||
.remove("id");
|
||||
stale_canonical.compatibility_network_json["Globe"]["children"]["Nested AP"]
|
||||
.as_object_mut()
|
||||
.expect("Nested AP should be an object")
|
||||
.remove("id");
|
||||
stale_canonical.compatibility_network_json["PLDT"]
|
||||
.as_object_mut()
|
||||
.expect("PLDT should be an object")
|
||||
.remove("id");
|
||||
stale_canonical
|
||||
.save(&config)
|
||||
.expect("stale canonical state should write");
|
||||
|
||||
let canonical =
|
||||
TopologyCanonicalStateFile::load(&config).expect("stale canonical state should load");
|
||||
(config, canonical)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_idless_sites_load_heals_compatibility_network() {
|
||||
let (_config, canonical) = load_stale_idless_legacy_canonical_state();
|
||||
let globe_id = canonical
|
||||
.nodes
|
||||
.iter()
|
||||
.find(|node| node.node_name == "Globe")
|
||||
.expect("Globe should be imported")
|
||||
.node_id
|
||||
.as_str();
|
||||
let nested_ap_id = canonical
|
||||
.nodes
|
||||
.iter()
|
||||
.find(|node| node.node_name == "Nested AP")
|
||||
.expect("Nested AP should be imported")
|
||||
.node_id
|
||||
.as_str();
|
||||
let pldt_id = canonical
|
||||
.nodes
|
||||
.iter()
|
||||
.find(|node| node.node_name == "PLDT")
|
||||
.expect("PLDT should be imported")
|
||||
.node_id
|
||||
.as_str();
|
||||
|
||||
assert_eq!(
|
||||
canonical.compatibility_network_json["Globe"]["id"].as_str(),
|
||||
Some(globe_id)
|
||||
);
|
||||
assert_eq!(
|
||||
canonical.compatibility_network_json["Globe"]["children"]["Nested AP"]["id"].as_str(),
|
||||
Some(nested_ap_id)
|
||||
);
|
||||
assert_eq!(
|
||||
canonical.compatibility_network_json["PLDT"]["id"].as_str(),
|
||||
Some(pldt_id)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn legacy_idless_sites_publish_valid_effective_network() {
|
||||
let (config, canonical) = load_stale_idless_legacy_canonical_state();
|
||||
let globe_id = canonical
|
||||
.nodes
|
||||
.iter()
|
||||
.find(|node| node.node_name == "Globe")
|
||||
.expect("Globe should be imported")
|
||||
.node_id
|
||||
.as_str();
|
||||
let nested_ap_id = canonical
|
||||
.nodes
|
||||
.iter()
|
||||
.find(|node| node.node_name == "Nested AP")
|
||||
.expect("Nested AP should be imported")
|
||||
.node_id
|
||||
.as_str();
|
||||
let pldt_id = canonical
|
||||
.nodes
|
||||
.iter()
|
||||
.find(|node| node.node_name == "PLDT")
|
||||
.expect("PLDT should be imported")
|
||||
.node_id
|
||||
.as_str();
|
||||
|
||||
let artifacts = build_effective_topology_artifacts_from_canonical(
|
||||
&config,
|
||||
&canonical,
|
||||
&TopologyOverridesFile::default(),
|
||||
&TopologyAttachmentHealthStateFile::default(),
|
||||
)
|
||||
.expect("idless legacy site topology should publish");
|
||||
let effective_network = artifacts
|
||||
.effective_network
|
||||
.expect("legacy topology should publish effective network");
|
||||
|
||||
assert_eq!(effective_network["Globe"]["id"].as_str(), Some(globe_id));
|
||||
assert_eq!(
|
||||
effective_network["Globe"]["children"]["Nested AP"]["id"].as_str(),
|
||||
Some(nested_ap_id)
|
||||
);
|
||||
assert_eq!(effective_network["PLDT"]["id"].as_str(), Some(pldt_id));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn runtime_squashing_collapses_backhaul_pairs_after_attachment_selection() {
|
||||
let mut config = Config::default();
|
||||
|
||||
Reference in new issue
Block a user