mirror of
https://github.com/home-assistant/core.git
synced 2025-08-30 09:51:37 +02:00
Add python3 open file descriptor sensor to Systemmonitor
This commit is contained in:
@@ -118,6 +118,17 @@ def get_ip_address(
|
||||
return None
|
||||
|
||||
|
||||
def get_python3_num_fds(
|
||||
entity: SystemMonitorSensor,
|
||||
) -> int | None:
|
||||
"""Return num_fds from python3 process."""
|
||||
processes = entity.coordinator.data.processes
|
||||
for proc in processes:
|
||||
if proc.name() == "python3":
|
||||
return proc.num_fds()
|
||||
return None
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class SysMonitorSensorEntityDescription(SensorEntityDescription):
|
||||
"""Describes System Monitor sensor entities."""
|
||||
@@ -369,6 +380,13 @@ SENSOR_TYPES: dict[str, SysMonitorSensorEntityDescription] = {
|
||||
value_fn=lambda entity: entity.coordinator.data.swap.percent,
|
||||
add_to_update=lambda entity: ("swap", ""),
|
||||
),
|
||||
"python3_num_fds": SysMonitorSensorEntityDescription(
|
||||
key="python3_num_fds",
|
||||
translation_key="python3_num_fds",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value_fn=get_python3_num_fds,
|
||||
add_to_update=lambda entity: ("processes", ""),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@@ -566,6 +584,18 @@ async def async_setup_entry(
|
||||
is_enabled,
|
||||
)
|
||||
)
|
||||
if _type == "python3_num_fds":
|
||||
argument = ""
|
||||
loaded_resources.add(slugify(f"{_type}_{argument}"))
|
||||
entities.append(
|
||||
SystemMonitorSensor(
|
||||
coordinator,
|
||||
sensor_description,
|
||||
entry.entry_id,
|
||||
argument,
|
||||
True, # Enabled by default
|
||||
)
|
||||
)
|
||||
|
||||
# Ensure legacy imported disk_* resources are loaded if they are not part
|
||||
# of mount points automatically discovered
|
||||
|
@@ -100,6 +100,9 @@
|
||||
},
|
||||
"swap_use_percent": {
|
||||
"name": "Swap usage"
|
||||
},
|
||||
"python3_num_fds": {
|
||||
"name": "Python3 open file descriptors"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -27,12 +27,13 @@ def mock_sys_platform() -> Generator[None]:
|
||||
class MockProcess(Process):
|
||||
"""Mock a Process class."""
|
||||
|
||||
def __init__(self, name: str, ex: bool = False) -> None:
|
||||
def __init__(self, name: str, ex: bool = False, num_fds: int = 3) -> None:
|
||||
"""Initialize the process."""
|
||||
super().__init__(1)
|
||||
self._name = name
|
||||
self._ex = ex
|
||||
self._create_time = 1708700400
|
||||
self._num_fds = num_fds
|
||||
|
||||
def name(self):
|
||||
"""Return a name."""
|
||||
@@ -40,6 +41,10 @@ class MockProcess(Process):
|
||||
raise NoSuchProcess(1, self._name)
|
||||
return self._name
|
||||
|
||||
def num_fds(self):
|
||||
"""Return open file descriptors."""
|
||||
return self._num_fds
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_setup_entry() -> Generator[AsyncMock]:
|
||||
|
@@ -114,16 +114,6 @@
|
||||
# name: test_sensor[System Monitor Last boot - state]
|
||||
'2024-02-24T15:00:00+00:00'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Load (15 min) - attributes]
|
||||
ReadOnlyDict({
|
||||
'friendly_name': 'System Monitor Load (15 min)',
|
||||
'icon': 'mdi:cpu-64-bit',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Load (15 min) - state]
|
||||
'3'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Load (1 min) - attributes]
|
||||
ReadOnlyDict({
|
||||
'friendly_name': 'System Monitor Load (1 min)',
|
||||
@@ -134,6 +124,16 @@
|
||||
# name: test_sensor[System Monitor Load (1 min) - state]
|
||||
'1'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Load (15 min) - attributes]
|
||||
ReadOnlyDict({
|
||||
'friendly_name': 'System Monitor Load (15 min)',
|
||||
'icon': 'mdi:cpu-64-bit',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Load (15 min) - state]
|
||||
'3'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Load (5 min) - attributes]
|
||||
ReadOnlyDict({
|
||||
'friendly_name': 'System Monitor Load (5 min)',
|
||||
@@ -322,6 +322,15 @@
|
||||
# name: test_sensor[System Monitor Processor use - state]
|
||||
'10'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Python3 open file descriptors - attributes]
|
||||
ReadOnlyDict({
|
||||
'friendly_name': 'System Monitor Python3 open file descriptors',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Python3 open file descriptors - state]
|
||||
'3'
|
||||
# ---
|
||||
# name: test_sensor[System Monitor Swap free - attributes]
|
||||
ReadOnlyDict({
|
||||
'device_class': 'data_size',
|
||||
|
@@ -18,6 +18,8 @@ from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .conftest import MockProcess
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
|
||||
|
||||
@@ -453,7 +455,7 @@ async def test_remove_obsolete_entities(
|
||||
mock_added_config_entry.entry_id
|
||||
)
|
||||
)
|
||||
== 37
|
||||
== 38
|
||||
)
|
||||
|
||||
entity_registry.async_update_entity(
|
||||
@@ -494,7 +496,7 @@ async def test_remove_obsolete_entities(
|
||||
mock_added_config_entry.entry_id
|
||||
)
|
||||
)
|
||||
== 38
|
||||
== 39
|
||||
)
|
||||
|
||||
assert (
|
||||
@@ -544,3 +546,56 @@ async def test_no_duplicate_disk_entities(
|
||||
assert disk_sensor.state == "60.0"
|
||||
|
||||
assert "Platform systemmonitor does not generate unique IDs." not in caplog.text
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_python3_num_fds(
|
||||
hass: HomeAssistant,
|
||||
mock_psutil: Mock,
|
||||
mock_os: Mock,
|
||||
entity_registry: er.EntityRegistry,
|
||||
snapshot: SnapshotAssertion,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test python3 open file descriptors sensor."""
|
||||
mock_config_entry = MockConfigEntry(
|
||||
title="System Monitor",
|
||||
domain=DOMAIN,
|
||||
data={},
|
||||
options={
|
||||
"resources": [
|
||||
"disk_use_percent_/",
|
||||
"disk_use_percent_/home/notexist/",
|
||||
"memory_free_",
|
||||
"network_out_eth0",
|
||||
"process_python3",
|
||||
],
|
||||
},
|
||||
)
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
num_fds_sensor = hass.states.get(
|
||||
"sensor.system_monitor_python3_open_file_descriptors"
|
||||
)
|
||||
assert num_fds_sensor is not None
|
||||
assert num_fds_sensor.state == "3"
|
||||
assert num_fds_sensor.attributes == {
|
||||
"state_class": "measurement",
|
||||
"friendly_name": "System Monitor Python3 open file descriptors",
|
||||
}
|
||||
|
||||
_process = MockProcess("python3", num_fds=5)
|
||||
assert _process.num_fds() == 5
|
||||
mock_psutil.process_iter.return_value = [_process]
|
||||
|
||||
freezer.tick(timedelta(minutes=1))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
num_fds_sensor = hass.states.get(
|
||||
"sensor.system_monitor_python3_open_file_descriptors"
|
||||
)
|
||||
assert num_fds_sensor is not None
|
||||
assert num_fds_sensor.state == "5"
|
||||
|
Reference in New Issue
Block a user