test config macthing
This commit is contained in:
@@ -204,6 +204,7 @@ class BridgeLinkStateWatcher:
|
|||||||
now = time.time()
|
now = time.time()
|
||||||
|
|
||||||
with self._lock:
|
with self._lock:
|
||||||
|
previous_states = dict(self._member_states)
|
||||||
self._last_event_ts = now
|
self._last_event_ts = now
|
||||||
self._last_error = None
|
self._last_error = None
|
||||||
self._member_states = states
|
self._member_states = states
|
||||||
@@ -247,7 +248,7 @@ class BridgeLinkStateWatcher:
|
|||||||
)
|
)
|
||||||
|
|
||||||
if failing_members:
|
if failing_members:
|
||||||
config_source = self._find_config_sync_source(states, preferred_ifname=source_ifname)
|
config_source = self._find_config_sync_source(states, previous_states, preferred_ifname=source_ifname)
|
||||||
if config_source is not None and self._has_config_mismatch(config_source, states):
|
if config_source is not None and self._has_config_mismatch(config_source, states):
|
||||||
with self._lock:
|
with self._lock:
|
||||||
self._failing_since = {}
|
self._failing_since = {}
|
||||||
@@ -290,8 +291,9 @@ class BridgeLinkStateWatcher:
|
|||||||
self._failing_since = {}
|
self._failing_since = {}
|
||||||
self._all_clear_since = None
|
self._all_clear_since = None
|
||||||
|
|
||||||
if source_ifname in states and self._is_source_eligible(source_ifname, states):
|
config_source = self._find_config_sync_source(states, previous_states, preferred_ifname=source_ifname)
|
||||||
self._sync_member_configuration(source_ifname, states)
|
if config_source is not None and self._has_config_mismatch(config_source, states):
|
||||||
|
self._sync_member_configuration(config_source, states)
|
||||||
return
|
return
|
||||||
|
|
||||||
self._set_last_action(f"no_restore_needed ({reason})")
|
self._set_last_action(f"no_restore_needed ({reason})")
|
||||||
@@ -323,20 +325,84 @@ class BridgeLinkStateWatcher:
|
|||||||
def _find_config_sync_source(
|
def _find_config_sync_source(
|
||||||
self,
|
self,
|
||||||
states: dict[str, MemberLinkState],
|
states: dict[str, MemberLinkState],
|
||||||
|
previous_states: dict[str, MemberLinkState],
|
||||||
preferred_ifname: Optional[str] = None,
|
preferred_ifname: Optional[str] = None,
|
||||||
) -> Optional[str]:
|
) -> Optional[str]:
|
||||||
"""Pick a healthy member whose configuration should be mirrored to siblings."""
|
"""Pick a healthy member whose configuration should be mirrored to siblings."""
|
||||||
|
changed_candidates = [
|
||||||
|
ifname
|
||||||
|
for ifname in sorted(states)
|
||||||
|
if self._config_changed(ifname, states, previous_states)
|
||||||
|
]
|
||||||
|
if preferred_ifname in changed_candidates:
|
||||||
|
changed_candidates.remove(preferred_ifname)
|
||||||
|
changed_candidates.insert(0, preferred_ifname)
|
||||||
|
|
||||||
|
if changed_candidates:
|
||||||
|
for ifname in changed_candidates:
|
||||||
|
if self._is_changed_source_eligible(ifname, states):
|
||||||
|
return ifname
|
||||||
|
self._set_last_action(
|
||||||
|
f"waiting for changed configuration on {changed_candidates} to settle before syncing"
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
candidates: list[str] = []
|
candidates: list[str] = []
|
||||||
if preferred_ifname:
|
if preferred_ifname:
|
||||||
candidates.append(preferred_ifname)
|
candidates.append(preferred_ifname)
|
||||||
candidates.extend(ifname for ifname in sorted(states) if ifname != preferred_ifname)
|
candidates.extend(ifname for ifname in sorted(states) if ifname != preferred_ifname)
|
||||||
|
|
||||||
|
seen: set[str] = set()
|
||||||
for ifname in candidates:
|
for ifname in candidates:
|
||||||
|
if ifname in seen:
|
||||||
|
continue
|
||||||
|
seen.add(ifname)
|
||||||
if self._is_source_eligible(ifname, states):
|
if self._is_source_eligible(ifname, states):
|
||||||
return ifname
|
return ifname
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _is_changed_source_eligible(self, ifname: str, states: dict[str, MemberLinkState]) -> bool:
|
||||||
|
"""Allow a changed member to drive sync even while the link is transiently renegotiating."""
|
||||||
|
with self._lock:
|
||||||
|
ignored = self._managed_event_deadlines.get(ifname, 0.0) > time.time()
|
||||||
|
suppressed = ifname in self._suppressed_members
|
||||||
|
if ignored or suppressed:
|
||||||
|
return False
|
||||||
|
|
||||||
|
state = states.get(ifname)
|
||||||
|
if state is None:
|
||||||
|
return False
|
||||||
|
if state.link_ready:
|
||||||
|
return True
|
||||||
|
return self._state_has_usable_config(state)
|
||||||
|
|
||||||
|
def _config_changed(
|
||||||
|
self,
|
||||||
|
ifname: str,
|
||||||
|
states: dict[str, MemberLinkState],
|
||||||
|
previous_states: dict[str, MemberLinkState],
|
||||||
|
) -> bool:
|
||||||
|
"""Return whether this member's MTU or link profile changed since the last snapshot."""
|
||||||
|
current = states.get(ifname)
|
||||||
|
previous = previous_states.get(ifname)
|
||||||
|
if current is None or previous is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return current.mtu != previous.mtu or current.ethernet_profile != previous.ethernet_profile
|
||||||
|
|
||||||
|
def _state_has_usable_config(self, state: MemberLinkState) -> bool:
|
||||||
|
"""Return whether this snapshot contains configuration that can be mirrored."""
|
||||||
|
if state.mtu is not None:
|
||||||
|
return True
|
||||||
|
|
||||||
|
profile = state.ethernet_profile
|
||||||
|
if profile is None:
|
||||||
|
return False
|
||||||
|
if profile.autoneg is True:
|
||||||
|
return True
|
||||||
|
return profile.autoneg is False and profile.speed_mbps is not None and profile.duplex is not None
|
||||||
|
|
||||||
def _has_config_mismatch(self, source_ifname: str, states: dict[str, MemberLinkState]) -> bool:
|
def _has_config_mismatch(self, source_ifname: str, states: dict[str, MemberLinkState]) -> bool:
|
||||||
"""Return whether any sibling differs from the chosen source configuration."""
|
"""Return whether any sibling differs from the chosen source configuration."""
|
||||||
source = states.get(source_ifname)
|
source = states.get(source_ifname)
|
||||||
@@ -348,7 +414,7 @@ class BridgeLinkStateWatcher:
|
|||||||
continue
|
continue
|
||||||
if source.mtu is not None and target.mtu is not None and source.mtu != target.mtu:
|
if source.mtu is not None and target.mtu is not None and source.mtu != target.mtu:
|
||||||
return True
|
return True
|
||||||
if source.ethernet_profile is not None and target.ethernet_profile is not None and source.ethernet_profile != target.ethernet_profile:
|
if source.ethernet_profile is not None and source.ethernet_profile != target.ethernet_profile:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|||||||
Reference in New Issue
Block a user