Skip to content

Commit a8947ae

Browse files
feat: Adding get_phy_info functionality
feature: Adding get_phy_info functionality under mfd_network_adapter\network_interface\feature\utils\windows.py Signed-off-by: ThanuThanveer1 [email protected]
1 parent aa5c916 commit a8947ae

File tree

2 files changed

+392
-21
lines changed
  • mfd_network_adapter/network_interface/feature/utils
  • tests/unit/test_mfd_network_adapter/test_network_interface/test_feature/test_utils

2 files changed

+392
-21
lines changed

mfd_network_adapter/network_interface/feature/utils/windows.py

Lines changed: 103 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ def get_advanced_properties(self) -> List[Dict]:
3030
).stdout
3131
return parse_powershell_list(ps_output)
3232

33-
def get_advanced_property(self, advanced_property: str, use_registry: bool = False) -> str:
33+
def get_advanced_property(
34+
self, advanced_property: str, use_registry: bool = False
35+
) -> str:
3436
"""
3537
Get specified interface advanced property.
3638
@@ -42,7 +44,11 @@ def get_advanced_property(self, advanced_property: str, use_registry: bool = Fal
4244
value = "RegistryValue" if use_registry else "DisplayValue"
4345

4446
properties = self.get_advanced_properties()
45-
found_property = [item for item in properties if advanced_property.lower() in item[name].lower()]
47+
found_property = [
48+
item
49+
for item in properties
50+
if advanced_property.lower() in item[name].lower()
51+
]
4652

4753
if not found_property:
4854
raise UtilsException(f"Advanced Property {advanced_property} not found.")
@@ -65,7 +71,9 @@ def get_advanced_property_valid_values(self, registry_keyword: str) -> List:
6571
).stdout
6672
return ps_output.strip().split()
6773

68-
def set_advanced_property(self, registry_keyword: str, registry_value: Union[str, int]) -> None:
74+
def set_advanced_property(
75+
self, registry_keyword: str, registry_value: Union[str, int]
76+
) -> None:
6977
"""
7078
Set interface advanced property accessed by registry_keyword.
7179
@@ -92,6 +100,97 @@ def get_interface_index(self) -> str:
92100
:return: Read interface index
93101
"""
94102
result = self._connection.execute_powershell(
95-
f"(Get-NetAdapter '{self._interface().name}').InterfaceIndex", expected_return_codes={0}
103+
f"(Get-NetAdapter '{self._interface().name}').InterfaceIndex",
104+
expected_return_codes={0},
96105
)
97106
return result.stdout.strip()
107+
108+
def get_phy_info(self, adapter_interface_description: str | None = None) -> dict[str, str | bool]:
109+
"""
110+
Get PHY information and check auto-negotiation bits using WMI.
111+
112+
:param adapter_interface_description: Optional specific adapter description to match
113+
:return: Dictionary containing PHY information and auto-negotiation status
114+
:raises: UtilsException if PHY info command execution failed
115+
"""
116+
cmd = (
117+
'gwmi -namespace "root\\WMI" IntlLan_GetPhyInfo -property InstanceName,PhyInfo | '
118+
'Format-Table InstanceName, @{n="PhyInfo";e={($_ | select -expand PhyInfo) -join ","}} -auto'
119+
)
120+
result = self._connection.execute_powershell(
121+
cmd, custom_exception=UtilsException
122+
)
123+
phy_data = {}
124+
phy_info_found = auto_neg_bits_detected = False
125+
126+
# Only process if we have output
127+
if result.stdout:
128+
lines = [
129+
line.strip()
130+
for line in result.stdout.splitlines()
131+
if line.strip()
132+
and not any(x in line for x in ["InstanceName", "PhyInfo", "----"])
133+
]
134+
135+
# Normalize adapter name (strip "for ..." and trailing #N)
136+
base_name = None
137+
if adapter_interface_description:
138+
base_name = adapter_interface_description.split(" for ")[0].strip()
139+
base_name = re.sub(r"\s+#\d+$", "", base_name).strip().lower()
140+
141+
matching_line = None
142+
if base_name:
143+
for line in lines:
144+
# Extract full InstanceName part (everything before two or more spaces)
145+
instance_match = re.match(r"^(.*?)\s{2,}", line)
146+
if not instance_match:
147+
continue
148+
instance_name = instance_match.group(1).strip()
149+
normalized_name = (
150+
re.sub(r"\s+#\d+$", "", instance_name, flags=re.IGNORECASE)
151+
.strip()
152+
.lower()
153+
)
154+
155+
# Match exact normalized name
156+
if normalized_name == base_name:
157+
matching_line = line
158+
break
159+
160+
# Fallback if no exact match, use first line
161+
if not matching_line and lines:
162+
matching_line = lines[0]
163+
164+
if matching_line and "," in matching_line:
165+
# Extract the comma-separated PHY values (rightmost part)
166+
phy_info_str = matching_line.split(None, 1)[-1]
167+
if "," in phy_info_str:
168+
phy_values = phy_info_str.split(",")
169+
phy_data = {"raw_values": phy_values}
170+
171+
# Extract third value (auto-negotiation bits live here)
172+
if len(phy_values) >= 3:
173+
try:
174+
third_value = int(phy_values[2].strip())
175+
auto_neg_bits = {
176+
f"bit_{bit}": bool(third_value & (1 << bit))
177+
for bit in [0, 1]
178+
}
179+
phy_data.update(
180+
{
181+
"third_value_decimal": third_value,
182+
"third_value_binary": bin(third_value)[2:],
183+
"auto_neg_bits": auto_neg_bits,
184+
}
185+
)
186+
phy_info_found = True
187+
auto_neg_bits_detected = any(auto_neg_bits.values())
188+
except ValueError as e:
189+
phy_data["parse_error"] = str(e)
190+
191+
return {
192+
"phy_info_found": phy_info_found,
193+
"auto_neg_bits_detected": auto_neg_bits_detected,
194+
"phy_data": phy_data,
195+
"raw_output": result.stdout or "",
196+
}

0 commit comments

Comments
 (0)