fix loop endless
This commit is contained in:
@@ -199,12 +199,17 @@ class BridgeLinkStateWatcher:
|
|||||||
self._evaluate_bridge_state(reason="recovery_deadline")
|
self._evaluate_bridge_state(reason="recovery_deadline")
|
||||||
|
|
||||||
def _evaluate_bridge_state(self, reason: str, source_ifname: Optional[str] = None) -> None:
|
def _evaluate_bridge_state(self, reason: str, source_ifname: Optional[str] = None) -> None:
|
||||||
|
with self._lock:
|
||||||
|
previous_states = dict(self._member_states)
|
||||||
|
|
||||||
members = [iface for iface in get_bridge_ports_once(self.bridge_name) if check_interface_exists(iface)]
|
members = [iface for iface in get_bridge_ports_once(self.bridge_name) if check_interface_exists(iface)]
|
||||||
states = {iface: self._read_member_state(iface) for iface in members}
|
states = {
|
||||||
|
iface: self._read_member_state(iface, previous_states.get(iface))
|
||||||
|
for iface in members
|
||||||
|
}
|
||||||
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
|
||||||
@@ -389,7 +394,10 @@ class BridgeLinkStateWatcher:
|
|||||||
if current is None or previous is None:
|
if current is None or previous is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return current.mtu != previous.mtu or current.ethernet_profile != previous.ethernet_profile
|
return current.mtu != previous.mtu or self._profiles_differ(
|
||||||
|
current.ethernet_profile,
|
||||||
|
previous.ethernet_profile,
|
||||||
|
)
|
||||||
|
|
||||||
def _state_has_usable_config(self, state: MemberLinkState) -> bool:
|
def _state_has_usable_config(self, state: MemberLinkState) -> bool:
|
||||||
"""Return whether this snapshot contains configuration that can be mirrored."""
|
"""Return whether this snapshot contains configuration that can be mirrored."""
|
||||||
@@ -469,26 +477,126 @@ class BridgeLinkStateWatcher:
|
|||||||
except OSError:
|
except OSError:
|
||||||
return
|
return
|
||||||
|
|
||||||
def _read_member_state(self, ifname: str) -> MemberLinkState:
|
def _read_member_state(
|
||||||
|
self,
|
||||||
|
ifname: str,
|
||||||
|
previous_state: Optional[MemberLinkState] = None,
|
||||||
|
) -> MemberLinkState:
|
||||||
|
admin_up = read_interface_admin_up(ifname)
|
||||||
|
carrier_up = read_interface_carrier(ifname)
|
||||||
|
operstate = read_interface_operstate(ifname)
|
||||||
|
link_ready = self._link_looks_ready(admin_up, carrier_up, operstate)
|
||||||
|
|
||||||
return MemberLinkState(
|
return MemberLinkState(
|
||||||
ifname=ifname,
|
ifname=ifname,
|
||||||
admin_up=read_interface_admin_up(ifname),
|
admin_up=admin_up,
|
||||||
carrier_up=read_interface_carrier(ifname),
|
carrier_up=carrier_up,
|
||||||
operstate=read_interface_operstate(ifname),
|
operstate=operstate,
|
||||||
mtu=read_interface_mtu(ifname),
|
mtu=read_interface_mtu(ifname),
|
||||||
ethernet_profile=self._read_ethernet_profile(ifname),
|
ethernet_profile=self._read_ethernet_profile(
|
||||||
|
ifname,
|
||||||
|
previous_state=previous_state,
|
||||||
|
link_ready=link_ready,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _read_ethernet_profile(self, ifname: str) -> Optional[EthernetProfile]:
|
def _read_ethernet_profile(
|
||||||
|
self,
|
||||||
|
ifname: str,
|
||||||
|
previous_state: Optional[MemberLinkState] = None,
|
||||||
|
link_ready: Optional[bool] = None,
|
||||||
|
) -> Optional[EthernetProfile]:
|
||||||
"""Read ethtool speed/duplex/autoneg for one interface if supported."""
|
"""Read ethtool speed/duplex/autoneg for one interface if supported."""
|
||||||
profile = read_interface_ethernet_profile(ifname)
|
profile = read_interface_ethernet_profile(ifname)
|
||||||
if profile is None:
|
if profile is None:
|
||||||
return None
|
current_profile = None
|
||||||
|
else:
|
||||||
|
current_profile = EthernetProfile(
|
||||||
|
speed_mbps=profile.get("speed_mbps"),
|
||||||
|
duplex=profile.get("duplex"),
|
||||||
|
autoneg=profile.get("autoneg"),
|
||||||
|
)
|
||||||
|
|
||||||
|
if self._should_keep_previous_profile(ifname, current_profile, previous_state, link_ready):
|
||||||
|
return previous_state.ethernet_profile if previous_state is not None else None
|
||||||
|
|
||||||
|
previous_profile = previous_state.ethernet_profile if previous_state is not None else None
|
||||||
|
return self._materialize_profile(current_profile, previous_profile)
|
||||||
|
|
||||||
|
def _should_keep_previous_profile(
|
||||||
|
self,
|
||||||
|
ifname: str,
|
||||||
|
current_profile: Optional[EthernetProfile],
|
||||||
|
previous_state: Optional[MemberLinkState],
|
||||||
|
link_ready: Optional[bool],
|
||||||
|
) -> bool:
|
||||||
|
"""Keep the previous non-null profile during transient renegotiation windows."""
|
||||||
|
if previous_state is None or previous_state.ethernet_profile is None:
|
||||||
|
return False
|
||||||
|
if current_profile is not None and self._partial_profile_is_usable(current_profile):
|
||||||
|
return False
|
||||||
|
if link_ready is True:
|
||||||
|
return False
|
||||||
|
|
||||||
|
now = time.time()
|
||||||
|
with self._lock:
|
||||||
|
settling = self._settle_deadlines.get(ifname, 0.0) > now
|
||||||
|
managed = self._managed_event_deadlines.get(ifname, 0.0) > now
|
||||||
|
|
||||||
|
return settling or managed or link_ready is False
|
||||||
|
|
||||||
|
def _link_looks_ready(
|
||||||
|
self,
|
||||||
|
admin_up: Optional[bool],
|
||||||
|
carrier_up: Optional[bool],
|
||||||
|
operstate: Optional[str],
|
||||||
|
) -> bool:
|
||||||
|
"""Evaluate link health from sysfs fields before the MemberLinkState is built."""
|
||||||
|
if admin_up is not True:
|
||||||
|
return False
|
||||||
|
if carrier_up is False:
|
||||||
|
return False
|
||||||
|
if operstate in {"down", "lowerlayerdown", "notpresent"}:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _profiles_differ(
|
||||||
|
self,
|
||||||
|
current_profile: Optional[EthernetProfile],
|
||||||
|
previous_profile: Optional[EthernetProfile],
|
||||||
|
) -> bool:
|
||||||
|
"""Ignore transient non-null to null drops when detecting config changes."""
|
||||||
|
if current_profile is None:
|
||||||
|
return False
|
||||||
|
if previous_profile is None:
|
||||||
|
return True
|
||||||
|
return current_profile != previous_profile
|
||||||
|
|
||||||
|
def _partial_profile_is_usable(self, profile: Optional[EthernetProfile]) -> bool:
|
||||||
|
"""Return whether a profile contains enough data to drive synchronization."""
|
||||||
|
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 _materialize_profile(
|
||||||
|
self,
|
||||||
|
current_profile: Optional[EthernetProfile],
|
||||||
|
previous_profile: Optional[EthernetProfile],
|
||||||
|
) -> Optional[EthernetProfile]:
|
||||||
|
"""Fill transiently missing profile fields from the last stable snapshot."""
|
||||||
|
if current_profile is None:
|
||||||
|
return previous_profile
|
||||||
|
if previous_profile is None:
|
||||||
|
return current_profile
|
||||||
|
if self._partial_profile_is_usable(current_profile):
|
||||||
|
return current_profile
|
||||||
|
|
||||||
return EthernetProfile(
|
return EthernetProfile(
|
||||||
speed_mbps=profile.get("speed_mbps"),
|
speed_mbps=current_profile.speed_mbps if current_profile.speed_mbps is not None else previous_profile.speed_mbps,
|
||||||
duplex=profile.get("duplex"),
|
duplex=current_profile.duplex if current_profile.duplex is not None else previous_profile.duplex,
|
||||||
autoneg=profile.get("autoneg"),
|
autoneg=current_profile.autoneg if current_profile.autoneg is not None else previous_profile.autoneg,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _sync_member_configuration(self, source_ifname: str, states: dict[str, MemberLinkState]) -> None:
|
def _sync_member_configuration(self, source_ifname: str, states: dict[str, MemberLinkState]) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user