forked from platypush/platypush
[#344] Removed marshmallow_dataclass
dependency from system
plugin.
This commit is contained in:
parent
63d9c1e348
commit
a7f03a1af9
35 changed files with 1327 additions and 1185 deletions
|
@ -154,6 +154,8 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
|
||||
@staticmethod
|
||||
def _cpu_frequency_avg() -> CpuFrequency:
|
||||
# Dummy call to ensure the CPU frequency is updated
|
||||
psutil.cpu_freq(percpu=False)
|
||||
return CpuFrequencySchema().load(psutil.cpu_freq(percpu=False)) # type: ignore
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
from dataclasses import field
|
||||
from datetime import date, datetime
|
||||
from uuid import UUID
|
||||
|
||||
|
@ -18,8 +17,7 @@ def percent_field(**kwargs):
|
|||
"""
|
||||
Field used to model percentage float fields between 0 and 1.
|
||||
"""
|
||||
return field(
|
||||
default_factory=float,
|
||||
return fields.Float(
|
||||
metadata={
|
||||
'validate': Range(min=0, max=1),
|
||||
**kwargs,
|
||||
|
|
|
@ -5,7 +5,7 @@ from platypush.schemas.dataclasses import DataClassSchema
|
|||
|
||||
class SystemBaseSchema(DataClassSchema):
|
||||
"""
|
||||
Base schema for system info.
|
||||
Base schema for system information.
|
||||
"""
|
||||
|
||||
@pre_load
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
from enum import Enum
|
||||
|
||||
from marshmallow import pre_load
|
||||
|
||||
from .._base import SystemBaseSchema
|
||||
|
||||
|
||||
class BatteryBaseSchema(SystemBaseSchema):
|
||||
"""
|
||||
Base schema for system battery sensors.
|
||||
"""
|
||||
|
||||
@pre_load
|
||||
def pre_load(self, data: dict, **_) -> dict:
|
||||
data = super().pre_load(data)
|
||||
percent = data.pop('percent', data.pop('value', None))
|
||||
seconds_left = data.pop('secsleft', data.pop('seconds_left', None))
|
||||
data['value'] = percent / 100 if percent is not None else None
|
||||
data['seconds_left'] = None if isinstance(seconds_left, Enum) else seconds_left
|
||||
return data
|
|
@ -1,39 +1,13 @@
|
|||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
from platypush.schemas.dataclasses import percent_field
|
||||
|
||||
|
||||
@dataclass
|
||||
class Battery:
|
||||
"""
|
||||
System battery sensor wrapper.
|
||||
System battery sensor representation.
|
||||
"""
|
||||
|
||||
seconds_left: Optional[float] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'High threshold for the temperature sensor, in Celsius',
|
||||
'example': 75,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
power_plugged: Optional[bool] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Whether the battery is plugged in or not',
|
||||
'example': False,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
value: Optional[float] = percent_field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Current charge left, as a percentage value '
|
||||
'between 0 and 1',
|
||||
'example': 0.5,
|
||||
}
|
||||
}
|
||||
)
|
||||
seconds_left: Optional[float] = None
|
||||
power_plugged: Optional[bool] = None
|
||||
value: Optional[float] = None
|
||||
|
|
|
@ -1,7 +1,45 @@
|
|||
from marshmallow_dataclass import class_schema
|
||||
from enum import Enum
|
||||
|
||||
from ._base import BatteryBaseSchema
|
||||
from ._model import Battery
|
||||
from marshmallow import fields, pre_load
|
||||
|
||||
from platypush.schemas.dataclasses import percent_field
|
||||
|
||||
from .._base import SystemBaseSchema
|
||||
|
||||
|
||||
BatterySchema = class_schema(Battery, base_schema=BatteryBaseSchema)
|
||||
class BatterySchema(SystemBaseSchema):
|
||||
"""
|
||||
System battery sensor schema.
|
||||
"""
|
||||
|
||||
seconds_left = fields.Float(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'Number of seconds left before the battery runs out',
|
||||
'example': 7200,
|
||||
},
|
||||
)
|
||||
|
||||
power_plugged = fields.Boolean(
|
||||
metadata={
|
||||
'description': 'Whether the battery is currently plugged in',
|
||||
'example': False,
|
||||
}
|
||||
)
|
||||
|
||||
value = percent_field(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'Current battery charge level, as a percentage between 0 and 1',
|
||||
'example': 0.5,
|
||||
},
|
||||
)
|
||||
|
||||
@pre_load
|
||||
def pre_load(self, data: dict, **_) -> dict:
|
||||
data = super().pre_load(data)
|
||||
percent = data.pop('percent', data.pop('value', None))
|
||||
seconds_left = data.pop('secsleft', data.pop('seconds_left', None))
|
||||
data['value'] = percent / 100 if percent is not None else None
|
||||
data['seconds_left'] = None if isinstance(seconds_left, Enum) else seconds_left
|
||||
return data
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
from marshmallow import pre_load
|
||||
|
||||
from .._base import SystemBaseSchema
|
||||
|
||||
|
||||
class ConnectionBaseSchema(SystemBaseSchema):
|
||||
"""
|
||||
Base schema for connections.
|
||||
"""
|
||||
|
||||
@pre_load
|
||||
def pre_load(self, data, **_) -> dict:
|
||||
data = super().pre_load(data)
|
||||
addr_mapping = {
|
||||
'laddr': ('local_address', 'local_port'),
|
||||
'raddr': ('remote_address', 'remote_port'),
|
||||
}
|
||||
|
||||
# Parse laddr/raddr attributes
|
||||
for ext_attr, (addr_attr, port_attr) in addr_mapping.items():
|
||||
value = data.pop(ext_attr, None)
|
||||
if not value:
|
||||
data[addr_attr] = data[port_attr] = None
|
||||
elif isinstance(value, tuple):
|
||||
data[addr_attr], data[port_attr] = value
|
||||
elif isinstance(value, str):
|
||||
data[addr_attr] = value
|
||||
data[port_attr] = None
|
||||
|
||||
# Handle enum values
|
||||
for attr in ['type', 'family']:
|
||||
value = data.pop(attr, None)
|
||||
if value is not None:
|
||||
data[attr] = value.name
|
||||
|
||||
if data.get('status') == 'NONE':
|
||||
data['status'] = None
|
||||
|
||||
return data
|
|
@ -1,4 +1,4 @@
|
|||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
from socket import AddressFamily, SocketKind
|
||||
from typing import Optional
|
||||
|
||||
|
@ -9,88 +9,12 @@ class Connection:
|
|||
Network/UNIX socket data class.
|
||||
"""
|
||||
|
||||
fd: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'File descriptor',
|
||||
'example': 3,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
family: AddressFamily = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Socket family',
|
||||
'example': AddressFamily.AF_INET.name,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
type: SocketKind = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Socket type',
|
||||
'example': SocketKind.SOCK_STREAM.name,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
local_address: str = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Local address, as an IP address for network '
|
||||
'connections and a socket path for a UNIX socket',
|
||||
'example': '192.168.1.2',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
local_port: Optional[int] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Local port, if this is a TCP/UDP connection, '
|
||||
'otherwise null',
|
||||
'example': 12345,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
remote_address: Optional[str] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Remote address, if this is a network '
|
||||
'connection, otherwise null',
|
||||
'example': '192.168.1.1',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
remote_port: Optional[int] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Local port, if this is a TCP/UDP connection, '
|
||||
'otherwise null',
|
||||
'example': 443,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
status: Optional[str] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Connection status, if this is a network '
|
||||
'connection, otherise null',
|
||||
'example': 'ESTABLISHED',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
pid: Optional[int] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'ID of the process that owns the connection',
|
||||
'example': 4321,
|
||||
}
|
||||
}
|
||||
)
|
||||
fd: int
|
||||
family: AddressFamily
|
||||
type: SocketKind
|
||||
local_address: str
|
||||
local_port: Optional[int] = None
|
||||
remote_address: Optional[str] = None
|
||||
remote_port: Optional[int] = None
|
||||
status: Optional[str] = None
|
||||
pid: Optional[int] = None
|
||||
|
|
|
@ -1,7 +1,111 @@
|
|||
from marshmallow_dataclass import class_schema
|
||||
from marshmallow import fields, pre_load
|
||||
|
||||
from ._base import ConnectionBaseSchema
|
||||
from ._model import Connection
|
||||
from .._base import SystemBaseSchema
|
||||
|
||||
|
||||
ConnectionSchema = class_schema(Connection, base_schema=ConnectionBaseSchema)
|
||||
class ConnectionSchema(SystemBaseSchema):
|
||||
"""
|
||||
Base schema for connections.
|
||||
"""
|
||||
|
||||
fd = fields.Int(
|
||||
required=True,
|
||||
metadata={
|
||||
'description': 'File descriptor number.',
|
||||
'example': 3,
|
||||
},
|
||||
)
|
||||
|
||||
family = fields.String(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'Address family.',
|
||||
'example': 'AF_INET',
|
||||
},
|
||||
)
|
||||
|
||||
type = fields.String(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'Socket type.',
|
||||
'example': 'SOCK_STREAM',
|
||||
},
|
||||
)
|
||||
|
||||
local_address = fields.String(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'Local address.',
|
||||
'example': '192.168.1.3',
|
||||
},
|
||||
)
|
||||
|
||||
local_port = fields.Int(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'Local port.',
|
||||
'example': 1234,
|
||||
},
|
||||
)
|
||||
|
||||
remote_address = fields.String(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'Remote address.',
|
||||
'example': '192.168.1.4',
|
||||
},
|
||||
)
|
||||
|
||||
remote_port = fields.Int(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'Remote port.',
|
||||
'example': 5678,
|
||||
},
|
||||
)
|
||||
|
||||
status = fields.String(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'Connection status.',
|
||||
'example': 'ESTABLISHED',
|
||||
},
|
||||
)
|
||||
|
||||
pid = fields.Int(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'ID of the process that owns the connection.',
|
||||
'example': 1234,
|
||||
},
|
||||
)
|
||||
|
||||
@pre_load
|
||||
def pre_load(self, data, **_) -> dict:
|
||||
data = super().pre_load(data)
|
||||
addr_mapping = {
|
||||
'laddr': ('local_address', 'local_port'),
|
||||
'raddr': ('remote_address', 'remote_port'),
|
||||
}
|
||||
|
||||
# Parse laddr/raddr attributes
|
||||
for ext_attr, (addr_attr, port_attr) in addr_mapping.items():
|
||||
value = data.pop(ext_attr, None)
|
||||
if not value:
|
||||
data[addr_attr] = data[port_attr] = None
|
||||
elif isinstance(value, tuple):
|
||||
data[addr_attr], data[port_attr] = value
|
||||
elif isinstance(value, str):
|
||||
data[addr_attr] = value
|
||||
data[port_attr] = None
|
||||
|
||||
# Handle enum values
|
||||
for attr in ['type', 'family']:
|
||||
value = data.pop(attr, None)
|
||||
if value is not None:
|
||||
data[attr] = value.name
|
||||
|
||||
if data.get('status') == 'NONE':
|
||||
data['status'] = None
|
||||
|
||||
return data
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
from ._model import Cpu, CpuFrequency, CpuInfo, CpuStats, CpuTimes
|
||||
from ._schemas import CpuFrequencySchema, CpuInfoSchema, CpuStatsSchema, CpuTimesSchema
|
||||
from ._schemas import (
|
||||
CpuFrequencySchema,
|
||||
CpuInfoSchema,
|
||||
CpuSchema,
|
||||
CpuStatsSchema,
|
||||
CpuTimesSchema,
|
||||
)
|
||||
|
||||
|
||||
__all__ = [
|
||||
|
@ -8,6 +14,7 @@ __all__ = [
|
|||
"CpuFrequencySchema",
|
||||
"CpuInfo",
|
||||
"CpuInfoSchema",
|
||||
"CpuSchema",
|
||||
"CpuStats",
|
||||
"CpuStatsSchema",
|
||||
"CpuTimes",
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
from marshmallow import pre_load
|
||||
|
||||
from .._base import SystemBaseSchema
|
||||
|
||||
|
||||
class CpuInfoBaseSchema(SystemBaseSchema):
|
||||
"""
|
||||
Base schema for CPU info.
|
||||
"""
|
||||
|
||||
@pre_load
|
||||
def pre_load(self, data: dict, **_) -> dict:
|
||||
data = super().pre_load(data)
|
||||
if data.get('hz_advertised'):
|
||||
data['frequency_advertised'] = data.pop('hz_advertised')[0]
|
||||
if data.get('hz_actual'):
|
||||
data['frequency_actual'] = data.pop('hz_actual')[0]
|
||||
|
||||
for key, value in data.items():
|
||||
if key.endswith("_cache_size") and isinstance(value, str):
|
||||
tokens = value.split(" ")
|
||||
unit = None
|
||||
if len(tokens) > 1:
|
||||
unit = tokens[1]
|
||||
|
||||
value = int(tokens[0])
|
||||
if unit == "KiB":
|
||||
value *= 1024
|
||||
elif unit == "MiB":
|
||||
value *= 1024 * 1024
|
||||
elif unit == "GiB":
|
||||
value *= 1024 * 1024 * 1024
|
||||
|
||||
data[key] = value
|
||||
|
||||
return data
|
||||
|
||||
|
||||
class CpuTimesBaseSchema(SystemBaseSchema):
|
||||
"""
|
||||
Base schema for CPU times.
|
||||
"""
|
||||
|
||||
@pre_load
|
||||
def pre_load(self, data, **_) -> dict:
|
||||
"""
|
||||
Convert the underlying object to dict and normalize all the percentage
|
||||
values from [0, 100] to [0, 1].
|
||||
"""
|
||||
data = super().pre_load(data)
|
||||
return {
|
||||
key: value / 100.0
|
||||
for key, value in (
|
||||
data if isinstance(data, dict) else data._asdict()
|
||||
).items()
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
from dataclasses import dataclass, field
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
from platypush.schemas.dataclasses import percent_field
|
||||
|
||||
|
||||
@dataclass
|
||||
class CpuInfo:
|
||||
|
@ -10,117 +8,18 @@ class CpuInfo:
|
|||
CPU info data class.
|
||||
"""
|
||||
|
||||
architecture: Optional[str] = field(
|
||||
metadata={
|
||||
'data_key': 'arch_string_raw',
|
||||
'metadata': {
|
||||
'description': 'CPU architecture',
|
||||
'example': 'x86_64',
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
bits: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'CPU bits / register size',
|
||||
'example': 64,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
cores: int = field(
|
||||
metadata={
|
||||
'data_key': 'count',
|
||||
'metadata': {
|
||||
'description': 'Number of cores',
|
||||
'example': 4,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
vendor: Optional[str] = field(
|
||||
metadata={
|
||||
'data_key': 'vendor_id_raw',
|
||||
'metadata': {
|
||||
'description': 'Vendor string',
|
||||
'example': 'GenuineIntel',
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
brand: Optional[str] = field(
|
||||
metadata={
|
||||
'data_key': 'brand_raw',
|
||||
'metadata': {
|
||||
'description': 'CPU brand string',
|
||||
'example': 'Intel(R) Core(TM) i7-5500U CPU @ 2.40GHz',
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
frequency_advertised: Optional[float] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Advertised CPU frequency, in Hz',
|
||||
'example': 2400000000,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
frequency_actual: Optional[float] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Actual CPU frequency, in Hz',
|
||||
'example': 2350000000,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
flags: List[str] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'CPU flags',
|
||||
'example': ['acpi', 'aes', 'cpuid'],
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
l1_instruction_cache_size: Optional[float] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Size of the L1 instruction cache, in bytes',
|
||||
'example': 65536,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
l1_data_cache_size: Optional[float] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Size of the L1 data cache, in bytes',
|
||||
'example': 65536,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
l2_cache_size: Optional[float] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Size of the L2 cache, in bytes',
|
||||
'example': 524288,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
l3_cache_size: Optional[float] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Size of the L2 cache, in bytes',
|
||||
'example': 4194304,
|
||||
}
|
||||
}
|
||||
)
|
||||
bits: int
|
||||
cores: int
|
||||
architecture: Optional[str] = None
|
||||
vendor: Optional[str] = None
|
||||
brand: Optional[str] = None
|
||||
frequency_advertised: Optional[float] = None
|
||||
frequency_actual: Optional[float] = None
|
||||
flags: List[str] = field(default_factory=list)
|
||||
l1_instruction_cache_size: Optional[float] = None
|
||||
l1_data_cache_size: Optional[float] = None
|
||||
l2_cache_size: Optional[float] = None
|
||||
l3_cache_size: Optional[float] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -129,16 +28,16 @@ class CpuTimes:
|
|||
CPU times data class.
|
||||
"""
|
||||
|
||||
user: Optional[float] = percent_field()
|
||||
nice: Optional[float] = percent_field()
|
||||
system: Optional[float] = percent_field()
|
||||
idle: Optional[float] = percent_field()
|
||||
iowait: Optional[float] = percent_field()
|
||||
irq: Optional[float] = percent_field()
|
||||
softirq: Optional[float] = percent_field()
|
||||
steal: Optional[float] = percent_field()
|
||||
guest: Optional[float] = percent_field()
|
||||
guest_nice: Optional[float] = percent_field()
|
||||
user: Optional[float] = None
|
||||
nice: Optional[float] = None
|
||||
system: Optional[float] = None
|
||||
idle: Optional[float] = None
|
||||
iowait: Optional[float] = None
|
||||
irq: Optional[float] = None
|
||||
softirq: Optional[float] = None
|
||||
steal: Optional[float] = None
|
||||
guest: Optional[float] = None
|
||||
guest_nice: Optional[float] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -175,4 +74,4 @@ class Cpu:
|
|||
frequency: CpuFrequency
|
||||
stats: CpuStats
|
||||
load_avg: Tuple[float, float, float]
|
||||
percent: float = percent_field()
|
||||
percent: float
|
||||
|
|
|
@ -1,11 +1,312 @@
|
|||
from marshmallow_dataclass import class_schema
|
||||
from marshmallow import fields, pre_load
|
||||
|
||||
from platypush.schemas.dataclasses import percent_field
|
||||
from .._base import SystemBaseSchema
|
||||
from ._base import CpuInfoBaseSchema, CpuTimesBaseSchema
|
||||
from ._model import CpuFrequency, CpuInfo, CpuStats, CpuTimes
|
||||
|
||||
|
||||
CpuFrequencySchema = class_schema(CpuFrequency, base_schema=SystemBaseSchema)
|
||||
CpuInfoSchema = class_schema(CpuInfo, base_schema=CpuInfoBaseSchema)
|
||||
CpuTimesSchema = class_schema(CpuTimes, base_schema=CpuTimesBaseSchema)
|
||||
CpuStatsSchema = class_schema(CpuStats, base_schema=SystemBaseSchema)
|
||||
class CpuInfoSchema(SystemBaseSchema):
|
||||
"""
|
||||
Base schema for CPU info.
|
||||
"""
|
||||
|
||||
architecture = fields.String(
|
||||
data_key='arch_string_raw',
|
||||
metadata={
|
||||
'description': 'CPU architecture.',
|
||||
'example': 'x86_64',
|
||||
},
|
||||
)
|
||||
|
||||
bits = fields.Int(
|
||||
metadata={
|
||||
'description': 'CPU architecture bits.',
|
||||
'example': 64,
|
||||
}
|
||||
)
|
||||
|
||||
cores = fields.Int(
|
||||
data_key='count',
|
||||
metadata={
|
||||
'description': 'Number of CPU cores.',
|
||||
'example': 4,
|
||||
},
|
||||
)
|
||||
|
||||
vendor = fields.String(
|
||||
data_key='vendor_id_raw',
|
||||
metadata={
|
||||
'description': 'CPU vendor.',
|
||||
'example': 'GenuineIntel',
|
||||
},
|
||||
)
|
||||
|
||||
brand = fields.String(
|
||||
data_key='brand_raw',
|
||||
metadata={
|
||||
'description': 'CPU brand.',
|
||||
'example': 'Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz',
|
||||
},
|
||||
)
|
||||
|
||||
frequency_advertised = fields.Float(
|
||||
metadata={
|
||||
'description': 'CPU advertised frequency.',
|
||||
'example': 2800000000.0,
|
||||
}
|
||||
)
|
||||
|
||||
frequency_actual = fields.Float(
|
||||
metadata={
|
||||
'description': 'CPU actual frequency.',
|
||||
'example': 2650000000.0,
|
||||
}
|
||||
)
|
||||
|
||||
flags = fields.List(
|
||||
fields.String(),
|
||||
metadata={
|
||||
'description': 'CPU flags.',
|
||||
'example': ['fpu', 'vme', 'de', 'pse', 'tsc', 'msr', 'pae'],
|
||||
},
|
||||
)
|
||||
|
||||
l1_instruction_cache_size = fields.Int(
|
||||
metadata={
|
||||
'description': 'L1 instruction cache size.',
|
||||
'example': 65536,
|
||||
}
|
||||
)
|
||||
|
||||
l1_data_cache_size = fields.Int(
|
||||
metadata={
|
||||
'description': 'L1 data cache size.',
|
||||
'example': 65536,
|
||||
}
|
||||
)
|
||||
|
||||
l2_cache_size = fields.Int(
|
||||
metadata={
|
||||
'description': 'L2 cache size.',
|
||||
'example': 524288,
|
||||
}
|
||||
)
|
||||
|
||||
l3_cache_size = fields.Int(
|
||||
metadata={
|
||||
'description': 'L3 cache size.',
|
||||
'example': 6291456,
|
||||
}
|
||||
)
|
||||
|
||||
@pre_load
|
||||
def pre_load(self, data: dict, **_) -> dict:
|
||||
data = super().pre_load(data)
|
||||
if data.get('hz_advertised'):
|
||||
data['frequency_advertised'] = data.pop('hz_advertised')[0]
|
||||
if data.get('hz_actual'):
|
||||
data['frequency_actual'] = data.pop('hz_actual')[0]
|
||||
|
||||
for key, value in data.items():
|
||||
if key.endswith("_cache_size") and isinstance(value, str):
|
||||
tokens = value.split(" ")
|
||||
unit = None
|
||||
if len(tokens) > 1:
|
||||
unit = tokens[1]
|
||||
|
||||
value = int(tokens[0])
|
||||
if unit == "KiB":
|
||||
value *= 1024
|
||||
elif unit == "MiB":
|
||||
value *= 1024 * 1024
|
||||
elif unit == "GiB":
|
||||
value *= 1024 * 1024 * 1024
|
||||
|
||||
data[key] = value
|
||||
|
||||
return data
|
||||
|
||||
|
||||
class CpuTimesSchema(SystemBaseSchema):
|
||||
"""
|
||||
Base schema for CPU times.
|
||||
"""
|
||||
|
||||
user = percent_field(
|
||||
metadata={
|
||||
'description': 'Time spent in user mode, '
|
||||
'as a percentage between 0 and 1 of the total CPU time.',
|
||||
'example': 0.0,
|
||||
}
|
||||
)
|
||||
|
||||
nice = percent_field(
|
||||
metadata={
|
||||
'description': 'Time spent in nice mode, '
|
||||
'as a percentage between 0 and 1 of the total CPU time.',
|
||||
'example': 0.0,
|
||||
}
|
||||
)
|
||||
|
||||
system = percent_field(
|
||||
metadata={
|
||||
'description': 'Time spent in system mode, '
|
||||
'as a percentage between 0 and 1 of the total CPU time.',
|
||||
'example': 0.0,
|
||||
}
|
||||
)
|
||||
|
||||
idle = percent_field(
|
||||
metadata={
|
||||
'description': 'Time spent in idle mode, '
|
||||
'as a percentage between 0 and 1 of the total CPU time.',
|
||||
'example': 0.0,
|
||||
}
|
||||
)
|
||||
|
||||
iowait = percent_field(
|
||||
metadata={
|
||||
'description': 'Time spent in I/O wait mode, '
|
||||
'as a percentage between 0 and 1 of the total CPU time.',
|
||||
'example': 0.0,
|
||||
}
|
||||
)
|
||||
|
||||
irq = percent_field(
|
||||
metadata={
|
||||
'description': 'Time spent in IRQ mode, '
|
||||
'as a percentage between 0 and 1 of the total CPU time.',
|
||||
'example': 0.0,
|
||||
}
|
||||
)
|
||||
|
||||
softirq = percent_field(
|
||||
metadata={
|
||||
'description': 'Time spent in soft IRQ mode, '
|
||||
'as a percentage between 0 and 1 of the total CPU time.',
|
||||
'example': 0.0,
|
||||
}
|
||||
)
|
||||
|
||||
steal = percent_field(
|
||||
metadata={
|
||||
'description': 'Time spent in steal mode, '
|
||||
'as a percentage between 0 and 1 of the total CPU time.',
|
||||
'example': 0.0,
|
||||
}
|
||||
)
|
||||
|
||||
guest = percent_field(
|
||||
metadata={
|
||||
'description': 'Time spent in guest mode, '
|
||||
'as a percentage between 0 and 1 of the total CPU time.',
|
||||
'example': 0.0,
|
||||
}
|
||||
)
|
||||
|
||||
guest_nice = percent_field(
|
||||
metadata={
|
||||
'description': 'Time spent in guest nice mode, '
|
||||
'as a percentage between 0 and 1 of the total CPU time.',
|
||||
'example': 0.0,
|
||||
}
|
||||
)
|
||||
|
||||
@pre_load
|
||||
def pre_load(self, data, **_) -> dict:
|
||||
"""
|
||||
Convert the underlying object to dict and normalize all the percentage
|
||||
values from [0, 100] to [0, 1].
|
||||
"""
|
||||
data = super().pre_load(data)
|
||||
return {
|
||||
key: value / 100.0
|
||||
for key, value in (
|
||||
data if isinstance(data, dict) else data._asdict()
|
||||
).items()
|
||||
}
|
||||
|
||||
|
||||
class CpuFrequencySchema(SystemBaseSchema):
|
||||
"""
|
||||
Base schema for CPU frequency.
|
||||
"""
|
||||
|
||||
current = fields.Float(
|
||||
metadata={
|
||||
'description': 'Current CPU frequency.',
|
||||
'example': 2800000000.0,
|
||||
}
|
||||
)
|
||||
|
||||
min = fields.Float(
|
||||
metadata={
|
||||
'description': 'Minimum CPU frequency.',
|
||||
'example': 800000000.0,
|
||||
}
|
||||
)
|
||||
|
||||
max = fields.Float(
|
||||
metadata={
|
||||
'description': 'Maximum CPU frequency.',
|
||||
'example': 2800000000.0,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class CpuStatsSchema(SystemBaseSchema):
|
||||
"""
|
||||
Base schema for CPU stats.
|
||||
"""
|
||||
|
||||
ctx_switches = fields.Int(
|
||||
metadata={
|
||||
'description': 'Number of context switches.',
|
||||
'example': 1234,
|
||||
}
|
||||
)
|
||||
|
||||
interrupts = fields.Int(
|
||||
metadata={
|
||||
'description': 'Number of interrupts.',
|
||||
'example': 1234,
|
||||
}
|
||||
)
|
||||
|
||||
soft_interrupts = fields.Int(
|
||||
metadata={
|
||||
'description': 'Number of soft interrupts.',
|
||||
'example': 1234,
|
||||
}
|
||||
)
|
||||
|
||||
syscalls = fields.Int(
|
||||
metadata={
|
||||
'description': 'Number of system calls.',
|
||||
'example': 1234,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class CpuSchema(SystemBaseSchema):
|
||||
"""
|
||||
Base schema for CPU results.
|
||||
"""
|
||||
|
||||
info = fields.Nested(CpuInfoSchema)
|
||||
times = fields.Nested(CpuTimesSchema)
|
||||
frequency = fields.Nested(CpuFrequencySchema)
|
||||
stats = fields.Nested(CpuStatsSchema)
|
||||
load_avg = fields.Tuple(
|
||||
[fields.Float(), fields.Float(), fields.Float()],
|
||||
metadata={
|
||||
'description': 'CPU load average.',
|
||||
'example': (0.0, 0.0, 0.0),
|
||||
},
|
||||
)
|
||||
|
||||
percent = percent_field(
|
||||
metadata={
|
||||
'description': 'CPU usage percentage, as a value between 0 and 1.',
|
||||
'example': 0.0,
|
||||
}
|
||||
)
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
from marshmallow import pre_load
|
||||
|
||||
from .._base import SystemBaseSchema
|
||||
|
||||
|
||||
class DiskBaseSchema(SystemBaseSchema):
|
||||
"""
|
||||
Base schema for disk stats.
|
||||
"""
|
||||
|
||||
@pre_load
|
||||
def pre_load(self, data: dict, **_) -> dict:
|
||||
data = super().pre_load(data)
|
||||
|
||||
# Convert read/write/busy times from milliseconds to seconds
|
||||
for attr in ['read_time', 'write_time', 'busy_time']:
|
||||
if data.get(attr) is not None:
|
||||
data[attr] /= 1000
|
||||
|
||||
# Normalize the percentage between 0 and 1
|
||||
if data.get('percent') is not None:
|
||||
data['percent'] /= 100
|
||||
|
||||
return data
|
|
@ -1,8 +1,6 @@
|
|||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
from platypush.schemas.dataclasses import percent_field
|
||||
|
||||
|
||||
@dataclass
|
||||
class Disk:
|
||||
|
@ -10,120 +8,18 @@ class Disk:
|
|||
Disk data class.
|
||||
"""
|
||||
|
||||
device: str = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Path/identifier of the disk/partition',
|
||||
'example': '/dev/sda1',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
mountpoint: Optional[str] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Where the disk is mounted',
|
||||
'example': '/home',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
fstype: Optional[str] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Filesystem type',
|
||||
'example': 'ext4',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
opts: Optional[str] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Extra mount options passed to the partition',
|
||||
'example': 'rw,relatime,fmask=0022,dmask=0022,utf8',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
total: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Total available space, in bytes',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
used: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Used disk space, in bytes',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
free: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Free disk space, in bytes',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
read_count: Optional[int] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Number of recorded read operations',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
write_count: Optional[int] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Number of recorded write operations',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
read_bytes: Optional[int] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Number of read bytes',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
write_bytes: Optional[int] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Number of written bytes',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
read_time: Optional[float] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Time spent reading, in seconds',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
write_time: Optional[float] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Time spent writing, in seconds',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
busy_time: Optional[float] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Total disk busy time, in seconds',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
percent: float = percent_field()
|
||||
device: str
|
||||
mountpoint: Optional[str] = None
|
||||
fstype: Optional[str] = None
|
||||
opts: Optional[str] = None
|
||||
total: Optional[int] = None
|
||||
used: Optional[int] = None
|
||||
free: Optional[int] = None
|
||||
read_count: Optional[int] = None
|
||||
write_count: Optional[int] = None
|
||||
read_bytes: Optional[int] = None
|
||||
write_bytes: Optional[int] = None
|
||||
read_time: Optional[float] = None
|
||||
write_time: Optional[float] = None
|
||||
busy_time: Optional[float] = None
|
||||
percent: Optional[float] = None
|
||||
|
|
|
@ -1,7 +1,131 @@
|
|||
from marshmallow_dataclass import class_schema
|
||||
from marshmallow import fields, pre_load
|
||||
|
||||
from ._base import DiskBaseSchema
|
||||
from ._model import Disk
|
||||
from platypush.schemas.dataclasses import percent_field
|
||||
from .._base import SystemBaseSchema
|
||||
|
||||
|
||||
DiskSchema = class_schema(Disk, base_schema=DiskBaseSchema)
|
||||
class DiskSchema(SystemBaseSchema):
|
||||
"""
|
||||
Base schema for disk stats.
|
||||
"""
|
||||
|
||||
device = fields.String(
|
||||
required=True,
|
||||
metadata={
|
||||
'description': 'Path/identifier of the disk/partition',
|
||||
'example': '/dev/sda1',
|
||||
},
|
||||
)
|
||||
|
||||
mountpoint = fields.String(
|
||||
metadata={
|
||||
'description': 'Mountpoint of the disk/partition',
|
||||
'example': '/mnt/data',
|
||||
}
|
||||
)
|
||||
|
||||
fstype = fields.String(
|
||||
metadata={
|
||||
'description': 'Filesystem type',
|
||||
'example': 'ext4',
|
||||
}
|
||||
)
|
||||
|
||||
opts = fields.String( # type: ignore
|
||||
metadata={
|
||||
'description': 'Mount options',
|
||||
'example': 'rw,relatime',
|
||||
}
|
||||
)
|
||||
|
||||
total = fields.Integer(
|
||||
metadata={
|
||||
'description': 'Total disk space in bytes',
|
||||
'example': 1024**3,
|
||||
}
|
||||
)
|
||||
|
||||
used = fields.Integer(
|
||||
metadata={
|
||||
'description': 'Used disk space in bytes',
|
||||
'example': 1024**2,
|
||||
}
|
||||
)
|
||||
|
||||
free = fields.Integer(
|
||||
metadata={
|
||||
'description': 'Free disk space in bytes',
|
||||
'example': (1024**3) - (1024**2),
|
||||
}
|
||||
)
|
||||
|
||||
read_count = fields.Integer(
|
||||
metadata={
|
||||
'description': 'Number of read operations',
|
||||
'example': 100,
|
||||
}
|
||||
)
|
||||
|
||||
write_count = fields.Integer(
|
||||
metadata={
|
||||
'description': 'Number of write operations',
|
||||
'example': 50,
|
||||
}
|
||||
)
|
||||
|
||||
read_bytes = fields.Integer(
|
||||
metadata={
|
||||
'description': 'Number of bytes read',
|
||||
'example': 1024**3,
|
||||
}
|
||||
)
|
||||
|
||||
write_bytes = fields.Integer(
|
||||
metadata={
|
||||
'description': 'Number of bytes written',
|
||||
'example': 1024**2,
|
||||
}
|
||||
)
|
||||
|
||||
read_time = fields.Float(
|
||||
metadata={
|
||||
'description': 'Time spent reading in seconds',
|
||||
'example': 10.5,
|
||||
}
|
||||
)
|
||||
|
||||
write_time = fields.Float(
|
||||
metadata={
|
||||
'description': 'Time spent writing in seconds',
|
||||
'example': 5.5,
|
||||
}
|
||||
)
|
||||
|
||||
busy_time = fields.Float(
|
||||
metadata={
|
||||
'description': 'Time spent doing I/Os in seconds',
|
||||
'example': 20.5,
|
||||
}
|
||||
)
|
||||
|
||||
percent = percent_field(
|
||||
metadata={
|
||||
'description': 'Percentage of disk space used, normalized between 0 and 1',
|
||||
'example': 0.1,
|
||||
}
|
||||
)
|
||||
|
||||
@pre_load
|
||||
def pre_load(self, data: dict, **_) -> dict:
|
||||
data = super().pre_load(data)
|
||||
|
||||
# Convert read/write/busy times from milliseconds to seconds
|
||||
for attr in ['read_time', 'write_time', 'busy_time']:
|
||||
if data.get(attr) is not None:
|
||||
data[attr] /= 1000
|
||||
|
||||
# Normalize the percentage between 0 and 1
|
||||
if data.get('percent') is not None:
|
||||
data['percent'] /= 100
|
||||
|
||||
return data
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
from marshmallow import pre_load
|
||||
|
||||
from .._base import SystemBaseSchema
|
||||
|
||||
|
||||
class FanBaseSchema(SystemBaseSchema):
|
||||
"""
|
||||
Base schema for system fan sensors.
|
||||
"""
|
||||
|
||||
@pre_load
|
||||
def pre_load(self, data: dict, **_) -> dict:
|
||||
data = super().pre_load(data)
|
||||
data['value'] = data.pop('current', data.pop('value', None))
|
||||
return data
|
|
@ -1,4 +1,5 @@
|
|||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -7,29 +8,6 @@ class Fan:
|
|||
System fan sensor data class.
|
||||
"""
|
||||
|
||||
id: str = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Unique ID for the sensor',
|
||||
'example': 'acpi_1',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
label: str = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Name of the sensor',
|
||||
'example': 'CPU',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
value: float = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Current fan speed, in RPM',
|
||||
'example': 3000,
|
||||
}
|
||||
}
|
||||
)
|
||||
id: str
|
||||
label: Optional[str] = None
|
||||
value: Optional[float] = None
|
||||
|
|
|
@ -1,7 +1,39 @@
|
|||
from marshmallow_dataclass import class_schema
|
||||
from marshmallow import fields, pre_load
|
||||
|
||||
from ._base import FanBaseSchema
|
||||
from ._model import Fan
|
||||
from .._base import SystemBaseSchema
|
||||
|
||||
|
||||
FanSchema = class_schema(Fan, base_schema=FanBaseSchema)
|
||||
class FanSchema(SystemBaseSchema):
|
||||
"""
|
||||
Base schema for system fan sensors.
|
||||
"""
|
||||
|
||||
id = fields.String(
|
||||
required=True,
|
||||
metadata={
|
||||
'description': 'Unique ID for the sensor',
|
||||
'example': 'acpi_1',
|
||||
},
|
||||
)
|
||||
|
||||
label = fields.String(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'Label for the sensor',
|
||||
'example': 'CPU Fan',
|
||||
},
|
||||
)
|
||||
|
||||
value = fields.Float(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'Current fan speed in RPM',
|
||||
'example': 1200,
|
||||
},
|
||||
)
|
||||
|
||||
@pre_load
|
||||
def pre_load(self, data: dict, **_) -> dict:
|
||||
data = super().pre_load(data)
|
||||
data['value'] = data.pop('current', data.pop('value', None))
|
||||
return data
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
from marshmallow import pre_load
|
||||
|
||||
from .._base import SystemBaseSchema
|
||||
|
||||
|
||||
class MemoryStatsBaseSchema(SystemBaseSchema):
|
||||
"""
|
||||
Base schema for memory stats.
|
||||
"""
|
||||
|
||||
@pre_load
|
||||
def pre_load(self, data: dict, **_) -> dict:
|
||||
data = super().pre_load(data)
|
||||
|
||||
# Normalize the percentage between 0 and 1
|
||||
if data.get('percent') is not None:
|
||||
data['percent'] /= 100
|
||||
return data
|
|
@ -1,6 +1,4 @@
|
|||
from dataclasses import dataclass, field
|
||||
|
||||
from platypush.schemas.dataclasses import percent_field
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -9,79 +7,16 @@ class MemoryStats:
|
|||
Memory stats data class.
|
||||
"""
|
||||
|
||||
total: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Total available memory, in bytes',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
available: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Available memory, in bytes',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
used: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Used memory, in bytes',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
free: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Free memory, in bytes',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
active: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Size of the active memory, in bytes',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
inactive: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Size of the inactive memory, in bytes',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
buffers: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Size of the buffered memory, in bytes',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
cached: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Size of the cached memory, in bytes',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
shared: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Size of the shared memory, in bytes',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
percent: float = percent_field()
|
||||
total: int
|
||||
available: int
|
||||
used: int
|
||||
free: int
|
||||
active: int
|
||||
inactive: int
|
||||
buffers: int
|
||||
cached: int
|
||||
shared: int
|
||||
percent: float
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -90,28 +25,7 @@ class SwapStats:
|
|||
Swap memory stats data class.
|
||||
"""
|
||||
|
||||
total: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Total available memory, in bytes',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
used: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Used memory, in bytes',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
free: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Free memory, in bytes',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
percent: float = percent_field()
|
||||
total: int
|
||||
used: int
|
||||
free: int
|
||||
percent: float
|
||||
|
|
|
@ -1,8 +1,133 @@
|
|||
from marshmallow_dataclass import class_schema
|
||||
from marshmallow import fields, pre_load
|
||||
|
||||
from ._base import MemoryStatsBaseSchema
|
||||
from ._model import MemoryStats, SwapStats
|
||||
from platypush.schemas.dataclasses import percent_field
|
||||
|
||||
from .._base import SystemBaseSchema
|
||||
|
||||
|
||||
MemoryStatsSchema = class_schema(MemoryStats, base_schema=MemoryStatsBaseSchema)
|
||||
SwapStatsSchema = class_schema(SwapStats, base_schema=MemoryStatsBaseSchema)
|
||||
class MemoryStatsSchema(SystemBaseSchema):
|
||||
"""
|
||||
Base schema for memory stats.
|
||||
"""
|
||||
|
||||
total = fields.Integer(
|
||||
metadata={
|
||||
'description': 'Total memory available in bytes.',
|
||||
'example': 8589934592,
|
||||
}
|
||||
)
|
||||
|
||||
available = fields.Integer(
|
||||
metadata={
|
||||
'description': 'Memory available in bytes.',
|
||||
'example': 2147483648,
|
||||
}
|
||||
)
|
||||
|
||||
used = fields.Integer(
|
||||
metadata={
|
||||
'description': 'Memory used in bytes.',
|
||||
'example': 6442450944,
|
||||
}
|
||||
)
|
||||
|
||||
free = fields.Integer(
|
||||
metadata={
|
||||
'description': 'Memory free in bytes.',
|
||||
'example': 2147483648,
|
||||
}
|
||||
)
|
||||
|
||||
active = fields.Integer(
|
||||
metadata={
|
||||
'description': 'Memory active in bytes.',
|
||||
'example': 4294967296,
|
||||
}
|
||||
)
|
||||
|
||||
inactive = fields.Integer(
|
||||
metadata={
|
||||
'description': 'Memory inactive in bytes.',
|
||||
'example': 2147483648,
|
||||
}
|
||||
)
|
||||
|
||||
buffers = fields.Integer(
|
||||
metadata={
|
||||
'description': 'Memory buffers in bytes.',
|
||||
'example': 2147483648,
|
||||
}
|
||||
)
|
||||
|
||||
cached = fields.Integer(
|
||||
metadata={
|
||||
'description': 'Memory cached in bytes.',
|
||||
'example': 2147483648,
|
||||
}
|
||||
)
|
||||
|
||||
shared = fields.Integer(
|
||||
metadata={
|
||||
'description': 'Memory shared in bytes.',
|
||||
'example': 3221225472,
|
||||
}
|
||||
)
|
||||
|
||||
percent = percent_field(
|
||||
metadata={
|
||||
'description': 'Memory usage percentage between 0 and 1.',
|
||||
'example': 0.75,
|
||||
}
|
||||
)
|
||||
|
||||
@pre_load
|
||||
def pre_load(self, data: dict, **_) -> dict:
|
||||
data = super().pre_load(data)
|
||||
|
||||
# Normalize the percentage between 0 and 1
|
||||
if data.get('percent') is not None:
|
||||
data['percent'] /= 100
|
||||
return data
|
||||
|
||||
|
||||
class SwapStatsSchema(SystemBaseSchema):
|
||||
"""
|
||||
Base schema for swap stats.
|
||||
"""
|
||||
|
||||
total = fields.Integer(
|
||||
metadata={
|
||||
'description': 'Total memory available in bytes.',
|
||||
'example': 8589934592,
|
||||
}
|
||||
)
|
||||
|
||||
used = fields.Integer(
|
||||
metadata={
|
||||
'description': 'Memory used in bytes.',
|
||||
'example': 6442450944,
|
||||
}
|
||||
)
|
||||
|
||||
free = fields.Integer(
|
||||
metadata={
|
||||
'description': 'Memory free in bytes.',
|
||||
'example': 2147483648,
|
||||
}
|
||||
)
|
||||
|
||||
percent = percent_field(
|
||||
metadata={
|
||||
'description': 'Memory usage percentage between 0 and 1.',
|
||||
'example': 0.75,
|
||||
}
|
||||
)
|
||||
|
||||
@pre_load
|
||||
def pre_load(self, data: dict, **_) -> dict:
|
||||
data = super().pre_load(data)
|
||||
|
||||
# Normalize the percentage between 0 and 1
|
||||
if data.get('percent') is not None:
|
||||
data['percent'] /= 100
|
||||
return data
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
from enum import Enum
|
||||
from socket import AddressFamily
|
||||
|
||||
from marshmallow import pre_load
|
||||
|
||||
from .._base import SystemBaseSchema
|
||||
|
||||
|
||||
class NetworkInterfaceBaseSchema(SystemBaseSchema):
|
||||
"""
|
||||
Base schema for network interface stats.
|
||||
"""
|
||||
|
||||
@pre_load
|
||||
def pre_load(self, data: dict, **_) -> dict:
|
||||
data = super().pre_load(data)
|
||||
|
||||
# Custom attribute mappings
|
||||
for in_attr, out_attr in {
|
||||
'errin': 'errors_in',
|
||||
'errout': 'errors_out',
|
||||
'dropin': 'drop_in',
|
||||
'dropout': 'drop_out',
|
||||
'isup': 'is_up',
|
||||
}.items():
|
||||
if in_attr in data:
|
||||
data[out_attr] = data.pop(in_attr)
|
||||
|
||||
# Serialize enum values
|
||||
for i, addr in enumerate(data.get('addresses', [])):
|
||||
if hasattr(addr, '_asdict'):
|
||||
addr = addr._asdict()
|
||||
if isinstance(addr.get('family'), AddressFamily):
|
||||
addr['family'] = addr['family'].name
|
||||
data['addresses'][i] = addr
|
||||
|
||||
if isinstance(data.get('duplex'), Enum):
|
||||
data['duplex'] = data['duplex'].name.split('_')[-1]
|
||||
|
||||
# Split the flags string
|
||||
data['flags'] = data.get('flags', '').split(',')
|
||||
|
||||
return data
|
|
@ -2,154 +2,6 @@ from dataclasses import dataclass, field
|
|||
from socket import AddressFamily
|
||||
from typing import List, Optional
|
||||
|
||||
from marshmallow.validate import OneOf
|
||||
|
||||
|
||||
@dataclass
|
||||
class NetworkInterface:
|
||||
"""
|
||||
Network interface statistics data class.
|
||||
"""
|
||||
|
||||
interface: Optional[str] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Network interface identifier',
|
||||
'example': 'eth0',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
bytes_sent: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Number of bytes sent',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
bytes_recv: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Number of bytes received',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
packets_sent: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Number of packets sent',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
packets_recv: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Number of packets received',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
errors_in: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Number of errors on incoming traffic',
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
errors_out: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Number of errors on outgoing traffic',
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
drop_in: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Number of packets dropped on incoming traffic',
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
drop_out: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Number of packets dropped on outgoing traffic',
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
is_up: bool = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Whether the interface is active',
|
||||
'example': True,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
speed: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Interface reported speed in Mbps',
|
||||
'example': 10000,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
mtu: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Interface maximum transmission unit expressed '
|
||||
'in bytes',
|
||||
'example': 65535,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
duplex: str = field(
|
||||
metadata={
|
||||
'validate': OneOf(['FULL', 'HALF', 'UNKNOWN']),
|
||||
'metadata': {
|
||||
'description': 'Interface duplex configuration. Can be FULL, '
|
||||
'HALF or UNKNOWN',
|
||||
'example': 'FULL',
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
flags: List[str] = field(
|
||||
default_factory=list,
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'List of flags associated to the interface',
|
||||
'example': ['up', 'broadcast', 'running'],
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
addresses: List['NetworkInterfaceAddress'] = field(
|
||||
default_factory=list,
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'List of addresses associated to the interface',
|
||||
'example': [
|
||||
{
|
||||
'family': AddressFamily.AF_INET.name,
|
||||
'address': '192.168.1.2',
|
||||
'netmask': '255.255.255.0',
|
||||
'broadcast': '192.168.1.255',
|
||||
}
|
||||
],
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class NetworkInterfaceAddress:
|
||||
|
@ -157,38 +9,30 @@ class NetworkInterfaceAddress:
|
|||
Network interface address data class.
|
||||
"""
|
||||
|
||||
family: AddressFamily = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Address family',
|
||||
'example': AddressFamily.AF_INET.name,
|
||||
}
|
||||
}
|
||||
)
|
||||
family: Optional[AddressFamily] = None
|
||||
address: Optional[str] = None
|
||||
netmask: Optional[str] = None
|
||||
broadcast: Optional[str] = None
|
||||
|
||||
address: Optional[str] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'IPv4 or IPv6 address of the interface',
|
||||
'example': '192.168.1.2',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
netmask: Optional[str] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Netmask for the interface address',
|
||||
'example': '255.255.255.0',
|
||||
}
|
||||
}
|
||||
)
|
||||
@dataclass
|
||||
class NetworkInterface:
|
||||
"""
|
||||
Network interface statistics data class.
|
||||
"""
|
||||
|
||||
broadcast: Optional[str] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Broadcast address for the interface',
|
||||
'example': '192.168.1.255',
|
||||
}
|
||||
}
|
||||
)
|
||||
interface: Optional[str] = None
|
||||
bytes_sent: int = 0
|
||||
bytes_recv: int = 0
|
||||
packets_sent: int = 0
|
||||
packets_recv: int = 0
|
||||
errors_in: int = 0
|
||||
errors_out: int = 0
|
||||
drop_in: int = 0
|
||||
drop_out: int = 0
|
||||
is_up: bool = False
|
||||
speed: int = 0
|
||||
mtu: int = 0
|
||||
duplex: Optional[str] = None
|
||||
flags: List[str] = field(default_factory=list)
|
||||
addresses: List[NetworkInterfaceAddress] = field(default_factory=list)
|
||||
|
|
|
@ -1,9 +1,207 @@
|
|||
from marshmallow_dataclass import class_schema
|
||||
from enum import Enum
|
||||
from socket import AddressFamily
|
||||
|
||||
from ._base import NetworkInterfaceBaseSchema
|
||||
from ._model import NetworkInterface
|
||||
from marshmallow import fields, pre_load
|
||||
from marshmallow.validate import OneOf
|
||||
|
||||
from .._base import SystemBaseSchema
|
||||
|
||||
|
||||
NetworkInterfaceSchema = class_schema(
|
||||
NetworkInterface, base_schema=NetworkInterfaceBaseSchema
|
||||
)
|
||||
class NetworkInterfaceAddressSchema(SystemBaseSchema):
|
||||
"""
|
||||
Schema for network interface address.
|
||||
"""
|
||||
|
||||
family = fields.String(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'The address family.',
|
||||
'example': 'AF_INET',
|
||||
},
|
||||
)
|
||||
|
||||
address = fields.String(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'The IP address associated with the interface.',
|
||||
'example': '192.168.1.4',
|
||||
},
|
||||
)
|
||||
|
||||
netmask = fields.String(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'The netmask associated with the interface.',
|
||||
'example': '255.255.255.0',
|
||||
},
|
||||
)
|
||||
|
||||
broadcast = fields.String(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'The broadcast address associated with the interface.',
|
||||
'example': '192.168.1.255',
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class NetworkInterfaceSchema(SystemBaseSchema):
|
||||
"""
|
||||
Schema for network interface stats.
|
||||
"""
|
||||
|
||||
interface = fields.String(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'The name of the network interface.',
|
||||
'example': 'eth0',
|
||||
},
|
||||
)
|
||||
|
||||
bytes_sent = fields.Integer(
|
||||
missing=0,
|
||||
metadata={
|
||||
'description': 'The number of bytes sent.',
|
||||
'example': 123456,
|
||||
},
|
||||
)
|
||||
|
||||
bytes_recv = fields.Integer(
|
||||
missing=0,
|
||||
metadata={
|
||||
'description': 'The number of bytes received.',
|
||||
'example': 654321,
|
||||
},
|
||||
)
|
||||
|
||||
packets_sent = fields.Integer(
|
||||
missing=0,
|
||||
metadata={
|
||||
'description': 'The number of packets sent.',
|
||||
'example': 123,
|
||||
},
|
||||
)
|
||||
|
||||
packets_recv = fields.Integer(
|
||||
missing=0,
|
||||
metadata={
|
||||
'description': 'The number of packets received.',
|
||||
'example': 321,
|
||||
},
|
||||
)
|
||||
|
||||
errors_in = fields.Integer(
|
||||
missing=0,
|
||||
metadata={
|
||||
'description': 'The number of errors on the input side.',
|
||||
'example': 10,
|
||||
},
|
||||
)
|
||||
|
||||
errors_out = fields.Integer(
|
||||
missing=0,
|
||||
metadata={
|
||||
'description': 'The number of errors on the output side.',
|
||||
'example': 5,
|
||||
},
|
||||
)
|
||||
|
||||
drop_in = fields.Integer(
|
||||
missing=0,
|
||||
metadata={
|
||||
'description': 'The number of dropped packets on the input side.',
|
||||
'example': 1,
|
||||
},
|
||||
)
|
||||
|
||||
drop_out = fields.Integer(
|
||||
missing=0,
|
||||
metadata={
|
||||
'description': 'The number of dropped packets on the output side.',
|
||||
'example': 2,
|
||||
},
|
||||
)
|
||||
|
||||
is_up = fields.Boolean(
|
||||
metadata={
|
||||
'description': 'Whether the interface is up.',
|
||||
'example': True,
|
||||
},
|
||||
)
|
||||
|
||||
speed = fields.Integer(
|
||||
metadata={
|
||||
'description': 'The advertised speed of the interface in Mbps.',
|
||||
'example': 1000,
|
||||
},
|
||||
)
|
||||
|
||||
mtu = fields.Integer(
|
||||
metadata={
|
||||
'description': 'The maximum transmission unit of the interface in bytes.',
|
||||
'example': 1500,
|
||||
},
|
||||
)
|
||||
|
||||
duplex = fields.String(
|
||||
validate=OneOf(['FULL', 'HALF', 'UNKNOWN']),
|
||||
metadata={
|
||||
'description': 'Interface duplex configuration. Can be FULL, '
|
||||
'HALF or UNKNOWN',
|
||||
'example': 'FULL',
|
||||
},
|
||||
)
|
||||
|
||||
flags = fields.List(
|
||||
fields.String(),
|
||||
metadata={
|
||||
'description': 'A list of flags associated with the interface.',
|
||||
'example': ['up', 'broadcast', 'running', 'multicast'],
|
||||
},
|
||||
)
|
||||
|
||||
addresses = fields.List(
|
||||
fields.Nested(NetworkInterfaceAddressSchema),
|
||||
metadata={
|
||||
'description': 'A list of addresses associated with the interface.',
|
||||
'example': [
|
||||
{
|
||||
'family': 'AF_INET',
|
||||
'address': '192.168.1.4',
|
||||
'netmask': '255.255.255.0',
|
||||
'broadcast': '192.168.1.255',
|
||||
},
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
@pre_load
|
||||
def pre_load(self, data: dict, **_) -> dict:
|
||||
data = super().pre_load(data)
|
||||
|
||||
# Custom attribute mappings
|
||||
for in_attr, out_attr in {
|
||||
'errin': 'errors_in',
|
||||
'errout': 'errors_out',
|
||||
'dropin': 'drop_in',
|
||||
'dropout': 'drop_out',
|
||||
'isup': 'is_up',
|
||||
}.items():
|
||||
if in_attr in data:
|
||||
data[out_attr] = data.pop(in_attr)
|
||||
|
||||
# Serialize enum values
|
||||
for i, addr in enumerate(data.get('addresses', [])):
|
||||
if hasattr(addr, '_asdict'):
|
||||
addr = addr._asdict()
|
||||
if isinstance(addr.get('family'), AddressFamily):
|
||||
addr['family'] = addr['family'].name
|
||||
data['addresses'][i] = addr
|
||||
|
||||
if isinstance(data.get('duplex'), Enum):
|
||||
data['duplex'] = data['duplex'].name.split('_')[-1]
|
||||
|
||||
# Split the flags string
|
||||
data['flags'] = data.get('flags', '').split(',')
|
||||
|
||||
return data
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
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
|
|
@ -2,8 +2,6 @@ from dataclasses import dataclass, field
|
|||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
|
||||
from platypush.schemas.dataclasses import percent_field
|
||||
|
||||
|
||||
@dataclass
|
||||
class Process:
|
||||
|
@ -11,100 +9,14 @@ 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,
|
||||
}
|
||||
}
|
||||
)
|
||||
pid: int
|
||||
name: str
|
||||
parent_pid: Optional[int] = None
|
||||
username: Optional[str] = None
|
||||
command_line: List[str] = field(default_factory=list)
|
||||
status: Optional[str] = None
|
||||
terminal: Optional[str] = None
|
||||
current_directory: Optional[str] = None
|
||||
started: Optional[datetime] = None
|
||||
cpu_percent: float = 0
|
||||
memory_percent: float = 0
|
||||
|
|
|
@ -1,7 +1,124 @@
|
|||
from marshmallow_dataclass import class_schema
|
||||
from datetime import datetime
|
||||
|
||||
from ._base import ProcessBaseSchema
|
||||
from ._model import Process
|
||||
from dateutil.tz import gettz
|
||||
from marshmallow import fields, pre_load
|
||||
|
||||
from platypush.schemas import DateTime
|
||||
from platypush.schemas.dataclasses import percent_field
|
||||
from .._base import SystemBaseSchema
|
||||
|
||||
|
||||
ProcessSchema = class_schema(Process, base_schema=ProcessBaseSchema)
|
||||
class ProcessSchema(SystemBaseSchema):
|
||||
"""
|
||||
Schema for system processes.
|
||||
"""
|
||||
|
||||
pid = fields.Int(
|
||||
missing=-1,
|
||||
metadata={
|
||||
'description': 'The process ID.',
|
||||
'example': 1234,
|
||||
},
|
||||
)
|
||||
|
||||
name = fields.String(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'The name of the process.',
|
||||
'example': 'python',
|
||||
},
|
||||
)
|
||||
|
||||
parent_pid = fields.Int(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'The parent process ID.',
|
||||
'example': 1000,
|
||||
},
|
||||
)
|
||||
|
||||
username = fields.String(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'The username of the process owner.',
|
||||
'example': 'root',
|
||||
},
|
||||
)
|
||||
|
||||
command_line = fields.List(
|
||||
fields.String(),
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'The command line arguments of the process.',
|
||||
'example': ['python', 'script.py'],
|
||||
},
|
||||
)
|
||||
|
||||
status = fields.String(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'The status of the process.',
|
||||
'example': 'running',
|
||||
},
|
||||
)
|
||||
|
||||
terminal = fields.String(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'The terminal of the process.',
|
||||
'example': 'tty1',
|
||||
},
|
||||
)
|
||||
|
||||
current_directory = fields.String(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'The current working directory of the process.',
|
||||
'example': '/home/user',
|
||||
},
|
||||
)
|
||||
|
||||
started = DateTime(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'The timestamp when the process was started.',
|
||||
'example': '2021-01-01T00:00:00+00:00',
|
||||
},
|
||||
)
|
||||
|
||||
cpu_percent = percent_field(
|
||||
missing=0,
|
||||
metadata={
|
||||
'description': 'The CPU usage percentage of the process, in the range [0, 1].',
|
||||
'example': 0.5,
|
||||
},
|
||||
)
|
||||
|
||||
memory_percent = percent_field(
|
||||
missing=0,
|
||||
metadata={
|
||||
'description': 'The memory usage percentage of the process, in the range [0, 1].',
|
||||
'example': 0.5,
|
||||
},
|
||||
)
|
||||
|
||||
@pre_load
|
||||
def pre_load(self, data, **_) -> dict:
|
||||
import psutil
|
||||
|
||||
if hasattr(data, 'as_dict'):
|
||||
try:
|
||||
data = data.as_dict()
|
||||
except psutil.NoSuchProcess:
|
||||
return {}
|
||||
|
||||
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
|
||||
|
|
|
@ -1,8 +1,25 @@
|
|||
from marshmallow_dataclass import class_schema
|
||||
from marshmallow import fields
|
||||
|
||||
from platypush.schemas.dataclasses import DataClassSchema
|
||||
|
||||
from ._model import SystemInfo
|
||||
from ._base import SystemBaseSchema
|
||||
from ._battery import BatterySchema
|
||||
from ._cpu import CpuSchema
|
||||
from ._disk import DiskSchema
|
||||
from ._fan import FanSchema
|
||||
from ._memory import MemoryStatsSchema, SwapStatsSchema
|
||||
from ._network import NetworkInterfaceSchema
|
||||
from ._temperature import TemperatureSchema
|
||||
|
||||
|
||||
SystemInfoSchema = class_schema(SystemInfo, base_schema=DataClassSchema)
|
||||
class SystemInfoSchema(SystemBaseSchema):
|
||||
"""
|
||||
Schema for system info.
|
||||
"""
|
||||
|
||||
cpu = fields.Nested(CpuSchema)
|
||||
memory = fields.Nested(MemoryStatsSchema)
|
||||
swap = fields.Nested(SwapStatsSchema)
|
||||
disks = fields.List(fields.Nested(DiskSchema))
|
||||
network = fields.List(fields.Nested(NetworkInterfaceSchema))
|
||||
temperature = fields.List(fields.Nested(TemperatureSchema))
|
||||
fans = fields.List(fields.Nested(FanSchema))
|
||||
battery = fields.Nested(BatterySchema)
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
from marshmallow import pre_load
|
||||
|
||||
from .._base import SystemBaseSchema
|
||||
|
||||
|
||||
class TemperatureBaseSchema(SystemBaseSchema):
|
||||
"""
|
||||
Base schema for system temperature sensors.
|
||||
"""
|
||||
|
||||
@pre_load
|
||||
def pre_load(self, data: dict, **_) -> dict:
|
||||
data = super().pre_load(data)
|
||||
data['value'] = data.pop('current', data.pop('value', None))
|
||||
return data
|
|
@ -1,54 +1,15 @@
|
|||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class Temperature:
|
||||
"""
|
||||
System temperature sensor wrapper.
|
||||
System temperature sensor data class.
|
||||
"""
|
||||
|
||||
id: str = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Unique ID for the sensor',
|
||||
'example': 'acpi_1',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
label: str = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Name of the sensor',
|
||||
'example': 'CPU',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
value: float = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Current temperature value, in Celsius',
|
||||
'example': 55,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
high: Optional[float] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'High threshold for the temperature sensor, in Celsius',
|
||||
'example': 75,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
critical: Optional[float] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Critical threshold for the temperature sensor, in Celsius',
|
||||
'example': 95,
|
||||
}
|
||||
}
|
||||
)
|
||||
id: str
|
||||
label: Optional[str] = None
|
||||
value: Optional[float] = None
|
||||
high: Optional[float] = None
|
||||
critical: Optional[float] = None
|
||||
|
|
|
@ -1,7 +1,54 @@
|
|||
from marshmallow_dataclass import class_schema
|
||||
from marshmallow import fields, pre_load
|
||||
|
||||
from ._base import TemperatureBaseSchema
|
||||
from ._model import Temperature
|
||||
from .._base import SystemBaseSchema
|
||||
|
||||
|
||||
TemperatureSchema = class_schema(Temperature, base_schema=TemperatureBaseSchema)
|
||||
class TemperatureSchema(SystemBaseSchema):
|
||||
"""
|
||||
Schema for system temperature sensors.
|
||||
"""
|
||||
|
||||
id = fields.String(
|
||||
metadata={
|
||||
'description': 'The unique identifier of the temperature sensor.',
|
||||
'example': 'acpi_1',
|
||||
}
|
||||
)
|
||||
|
||||
label = fields.String(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'The label of the temperature sensor.',
|
||||
'example': 'CPU Temperature',
|
||||
},
|
||||
)
|
||||
|
||||
value = fields.Float(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'The current temperature value of the sensor, in degrees Celsius.',
|
||||
'example': 56.0,
|
||||
},
|
||||
)
|
||||
|
||||
high = fields.Float(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'The high temperature threshold of the sensor, in degrees Celsius.',
|
||||
'example': 90.0,
|
||||
},
|
||||
)
|
||||
|
||||
critical = fields.Float(
|
||||
allow_none=True,
|
||||
metadata={
|
||||
'description': 'The critical temperature threshold of the sensor, in degrees Celsius.',
|
||||
'example': 100.0,
|
||||
},
|
||||
)
|
||||
|
||||
@pre_load
|
||||
def pre_load(self, data: dict, **_) -> dict:
|
||||
data = super().pre_load(data)
|
||||
data['value'] = data.pop('current', data.pop('value', None))
|
||||
return data
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
from datetime import datetime
|
||||
|
||||
from dateutil.tz import gettz
|
||||
from marshmallow import pre_load
|
||||
|
||||
from .._base import SystemBaseSchema
|
||||
|
||||
|
||||
class UserBaseSchema(SystemBaseSchema):
|
||||
"""
|
||||
Base schema for system users.
|
||||
"""
|
||||
|
||||
@pre_load
|
||||
def pre_load(self, data: dict, **_) -> dict:
|
||||
data = super().pre_load(data)
|
||||
started_ts = data.pop('started', None)
|
||||
if started_ts is not None:
|
||||
data['started'] = datetime.fromtimestamp(started_ts).replace(tzinfo=gettz())
|
||||
|
||||
data['username'] = data.pop('name', data.pop('username', None))
|
||||
return data
|
|
@ -1,4 +1,4 @@
|
|||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
|
@ -9,37 +9,7 @@ class User:
|
|||
System user wrapper.
|
||||
"""
|
||||
|
||||
username: str = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Username',
|
||||
'example': 'root',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
terminal: Optional[str] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Identifier of the terminal the user is connected to',
|
||||
'example': 'pts/1',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
started: Optional[datetime] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'When the user session started',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
pid: Optional[int] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'PID of the process that holds the session',
|
||||
'example': 12345,
|
||||
}
|
||||
}
|
||||
)
|
||||
username: str
|
||||
terminal: Optional[str] = None
|
||||
started: Optional[datetime] = None
|
||||
pid: Optional[int] = None
|
||||
|
|
|
@ -1,7 +1,47 @@
|
|||
from marshmallow_dataclass import class_schema
|
||||
from datetime import datetime
|
||||
|
||||
from ._base import UserBaseSchema
|
||||
from ._model import User
|
||||
from dateutil.tz import gettz
|
||||
from marshmallow import fields, pre_load
|
||||
|
||||
from platypush.schemas import DateTime
|
||||
|
||||
from .._base import SystemBaseSchema
|
||||
|
||||
|
||||
UserSchema = class_schema(User, base_schema=UserBaseSchema)
|
||||
class UserSchema(SystemBaseSchema):
|
||||
"""
|
||||
Schema for system users.
|
||||
"""
|
||||
|
||||
username = fields.String(
|
||||
required=True,
|
||||
metadata={'description': 'The username of the user.', 'example': 'johndoe'},
|
||||
)
|
||||
|
||||
terminal = fields.String(
|
||||
metadata={
|
||||
'description': 'The terminal the user is currently using.',
|
||||
'example': 'tty1',
|
||||
}
|
||||
)
|
||||
|
||||
started = DateTime(
|
||||
metadata={
|
||||
'description': 'The timestamp when the user session started.',
|
||||
'example': '2021-01-01T00:00:00+00:00',
|
||||
}
|
||||
)
|
||||
|
||||
pid = fields.Integer(
|
||||
metadata={'description': 'The PID of the user session.', 'example': 1234}
|
||||
)
|
||||
|
||||
@pre_load
|
||||
def pre_load(self, data: dict, **_) -> dict:
|
||||
data = super().pre_load(data)
|
||||
started_ts = data.pop('started', None)
|
||||
if started_ts is not None:
|
||||
data['started'] = datetime.fromtimestamp(started_ts).replace(tzinfo=gettz())
|
||||
|
||||
data['username'] = data.pop('name', data.pop('username', None))
|
||||
return data
|
||||
|
|
Loading…
Reference in a new issue