Converted `system.processes` to the new data model.

This commit is contained in:
Fabio Manganiello 2023-04-23 02:08:43 +02:00
parent 387616ea96
commit 27b1048789
Signed by: blacklight
GPG Key ID: D90FBA7F76362774
8 changed files with 183 additions and 133 deletions

View File

@ -1,77 +0,0 @@
from datetime import datetime
from typing import Optional, List
from platypush.message.response import Response
class SystemResponse(Response):
pass
class ProcessResponse(SystemResponse):
def __init__(
self,
pid: int,
name: str,
started: datetime,
ppid: Optional[int],
children: Optional[List[int]] = None,
exe: Optional[List[str]] = None,
status: Optional[str] = None,
username: Optional[str] = None,
terminal: Optional[str] = None,
cpu_user_time: Optional[float] = None,
cpu_system_time: Optional[float] = None,
cpu_children_user_time: Optional[float] = None,
cpu_children_system_time: Optional[float] = None,
mem_rss: Optional[int] = None,
mem_vms: Optional[int] = None,
mem_shared: Optional[int] = None,
mem_text: Optional[int] = None,
mem_data: Optional[int] = None,
mem_lib: Optional[int] = None,
mem_dirty: Optional[int] = None,
mem_percent: Optional[float] = None,
*args,
**kwargs
):
super().__init__(
*args,
output={
'pid': pid,
'name': name,
'started': started,
'ppid': ppid,
'exe': exe,
'status': status,
'username': username,
'terminal': terminal,
'cpu_user_time': cpu_user_time,
'cpu_system_time': cpu_system_time,
'cpu_children_user_time': cpu_children_user_time,
'cpu_children_system_time': cpu_children_system_time,
'mem_rss': mem_rss,
'mem_vms': mem_vms,
'mem_shared': mem_shared,
'mem_text': mem_text,
'mem_data': mem_data,
'mem_lib': mem_lib,
'mem_dirty': mem_dirty,
'mem_percent': mem_percent,
'children': children or [],
},
**kwargs
)
class SystemResponseList(SystemResponse):
def __init__(self, responses: List[SystemResponse], *args, **kwargs):
super().__init__(output=[r.output for r in responses], *args, **kwargs)
class ProcessResponseList(SystemResponseList):
def __init__(self, responses: List[ProcessResponse], *args, **kwargs):
super().__init__(responses=responses, *args, **kwargs)
# vim:sw=4:ts=4:et:

View File

@ -1,6 +1,5 @@
import os
from datetime import datetime
from typing import Tuple, Union, List, Optional
from typing_extensions import override
@ -23,10 +22,6 @@ from platypush.entities.system import (
SystemFan,
SystemTemperature,
)
from platypush.message.response.system import (
ProcessResponseList,
ProcessResponse,
)
from platypush.plugins import action
from platypush.plugins.sensor import SensorPlugin
from platypush.schemas.system import (
@ -49,6 +44,8 @@ from platypush.schemas.system import (
MemoryStatsSchema,
NetworkInterface,
NetworkInterfaceSchema,
Process,
ProcessSchema,
SwapStats,
SwapStatsSchema,
SystemInfoSchema,
@ -366,7 +363,8 @@ class SystemPlugin(SensorPlugin, EntityManager):
"""
return TemperatureSchema().dump(self._sensors_temperature(), many=True)
def _sensors_fan(self) -> List[Fan]:
@staticmethod
def _sensors_fan() -> List[Fan]:
return FanSchema().load( # type: ignore
[
{
@ -389,7 +387,8 @@ class SystemPlugin(SensorPlugin, EntityManager):
"""
return FanSchema().dump(self._sensors_fan(), many=True)
def _sensors_battery(self) -> Optional[Battery]:
@staticmethod
def _sensors_battery() -> Optional[Battery]:
battery = psutil.sensors_battery()
return BatterySchema().load(battery) if battery else None # type: ignore
@ -403,7 +402,8 @@ class SystemPlugin(SensorPlugin, EntityManager):
battery = self._sensors_battery()
return BatterySchema().dump(battery) if battery else None # type: ignore
def _connected_users(self) -> List[User]:
@staticmethod
def _connected_users() -> List[User]:
return UserSchema().load(psutil.users(), many=True) # type: ignore
@action
@ -415,64 +415,40 @@ class SystemPlugin(SensorPlugin, EntityManager):
"""
return UserSchema().dump(self._connected_users(), many=True)
@action
def processes(self, filter: Optional[str] = '') -> ProcessResponseList:
@classmethod
def _processes(cls) -> List[Process]:
"""
Get the list of running processes.
:param filter: Filter the list by name.
:return: List of :class:`platypush.message.response.system.ProcessResponse`.
"""
processes = [psutil.Process(pid) for pid in psutil.pids()]
p_list = []
return ProcessSchema().load( # type: ignore
filter( # type: ignore
lambda proc: proc is not None,
[cls._get_process_if_exists(pid) for pid in psutil.pids()],
),
many=True,
)
for p in processes:
if filter and filter not in p.name():
continue
@action
def processes(self) -> List[dict]:
"""
Get the list of running processes.
args = {}
:return: .. schema:: system.ProcessSchema
"""
return ProcessSchema().dump(self._processes(), many=True)
try:
mem = p.memory_info()
times = p.cpu_times()
args.update(
pid=p.pid,
name=p.name(),
started=datetime.fromtimestamp(p.create_time()),
ppid=p.ppid(),
children=[pp.pid for pp in p.children()],
status=p.status(),
username=p.username(),
terminal=p.terminal(),
cpu_user_time=times.user,
cpu_system_time=times.system,
cpu_children_user_time=times.children_user,
cpu_children_system_time=times.children_system,
mem_rss=mem.rss,
mem_vms=mem.vms,
mem_shared=mem.shared,
mem_text=mem.text,
mem_data=mem.data,
mem_lib=mem.lib,
mem_dirty=mem.dirty,
mem_percent=p.memory_percent(),
)
except psutil.Error:
continue
try:
args.update(
exe=p.exe(),
)
except psutil.Error:
pass
p_list.append(ProcessResponse(**args))
return ProcessResponseList(p_list)
@classmethod
def _get_process_if_exists(cls, pid: int) -> Optional[psutil.Process]:
try:
return cls._get_process(pid)
except psutil.NoSuchProcess:
return None
@staticmethod
def _get_process(pid: int):
def _get_process(pid: int) -> psutil.Process:
return psutil.Process(pid)
@action

View File

@ -16,6 +16,7 @@ from ._fan import Fan, FanSchema
from ._memory import MemoryStats, MemoryStatsSchema, SwapStats, SwapStatsSchema
from ._model import SystemInfo
from ._network import NetworkInterface, NetworkInterfaceSchema
from ._process import Process, ProcessSchema
from ._schemas import SystemInfoSchema
from ._temperature import Temperature, TemperatureSchema
from ._user import User, UserSchema
@ -41,6 +42,8 @@ __all__ = [
"FanSchema",
"MemoryStats",
"MemoryStatsSchema",
"Process",
"ProcessSchema",
"SwapStats",
"SwapStatsSchema",
"NetworkInterface",

View File

@ -0,0 +1,4 @@
from ._model import Process
from ._schemas import ProcessSchema
__all__ = ['Process', 'ProcessSchema']

View File

@ -0,0 +1,28 @@
from datetime import datetime
from dateutil.tz import gettz
from marshmallow import pre_load
from .._base import SystemBaseSchema
class ProcessBaseSchema(SystemBaseSchema):
"""
Base schema for system processes.
"""
@pre_load
def pre_load(self, data, **_) -> dict:
if hasattr(data, 'as_dict'):
data = data.as_dict()
started_ts = data.pop('create_time', None)
if started_ts is not None:
data['started'] = datetime.fromtimestamp(started_ts).replace(tzinfo=gettz())
data['command_line'] = data.pop('cmdline', None)
data['cpu_percent'] = data.pop('cpu_percent') / 100
data['current_directory'] = data.pop('cwd', None)
data['memory_percent'] = data.pop('memory_percent') / 100
data['parent_pid'] = data.pop('ppid', None)
return data

View File

@ -0,0 +1,110 @@
from dataclasses import dataclass, field
from datetime import datetime
from typing import List, Optional
from platypush.schemas.dataclasses import percent_field
@dataclass
class Process:
"""
System process data class.
"""
pid: int = field(
metadata={
'metadata': {
'description': 'Process PID',
'example': 12345,
}
}
)
name: str = field(
metadata={
'metadata': {
'description': 'Process name',
'example': 'python',
}
}
)
parent_pid: Optional[int] = field(
metadata={
'metadata': {
'description': 'PID of the parent process',
'example': 1000,
}
}
)
username: str = field(
metadata={
'metadata': {
'description': 'Username that owns the process',
'example': 'root',
}
}
)
command_line: List[str] = field(
metadata={
'metadata': {
'description': 'Command line of the process',
'example': ['/usr/bin/python', '-m', 'platypush'],
}
}
)
status: str = field(
metadata={
'metadata': {
'description': 'Process status',
'example': 'running',
}
}
)
terminal: Optional[str] = field(
metadata={
'metadata': {
'description': 'Terminal the process is running on',
'example': 'pts/1',
}
}
)
current_directory: Optional[str] = field(
metadata={
'metadata': {
'description': 'Current directory of the process',
'example': '/root',
}
}
)
started: Optional[datetime] = field(
metadata={
'metadata': {
'description': 'When the process started',
}
}
)
cpu_percent: float = percent_field(
metadata={
'metadata': {
'description': 'Percentage of CPU used by the process, between 0 and 1',
'example': 0.1,
}
}
)
memory_percent: float = percent_field(
metadata={
'metadata': {
'description': 'Percentage of memory used by the process, between 0 and 1',
'example': 0.05,
}
}
)

View File

@ -0,0 +1,7 @@
from marshmallow_dataclass import class_schema
from ._base import ProcessBaseSchema
from ._model import Process
ProcessSchema = class_schema(Process, base_schema=ProcessBaseSchema)

View File

@ -31,7 +31,6 @@ class User:
metadata={
'metadata': {
'description': 'When the user session started',
'example': 'pts/1',
}
}
)