[#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
|
@staticmethod
|
||||||
def _cpu_frequency_avg() -> CpuFrequency:
|
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
|
return CpuFrequencySchema().load(psutil.cpu_freq(percpu=False)) # type: ignore
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
from dataclasses import field
|
|
||||||
from datetime import date, datetime
|
from datetime import date, datetime
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
|
@ -18,8 +17,7 @@ def percent_field(**kwargs):
|
||||||
"""
|
"""
|
||||||
Field used to model percentage float fields between 0 and 1.
|
Field used to model percentage float fields between 0 and 1.
|
||||||
"""
|
"""
|
||||||
return field(
|
return fields.Float(
|
||||||
default_factory=float,
|
|
||||||
metadata={
|
metadata={
|
||||||
'validate': Range(min=0, max=1),
|
'validate': Range(min=0, max=1),
|
||||||
**kwargs,
|
**kwargs,
|
||||||
|
|
|
@ -5,7 +5,7 @@ from platypush.schemas.dataclasses import DataClassSchema
|
||||||
|
|
||||||
class SystemBaseSchema(DataClassSchema):
|
class SystemBaseSchema(DataClassSchema):
|
||||||
"""
|
"""
|
||||||
Base schema for system info.
|
Base schema for system information.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@pre_load
|
@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 typing import Optional
|
||||||
|
|
||||||
from platypush.schemas.dataclasses import percent_field
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Battery:
|
class Battery:
|
||||||
"""
|
"""
|
||||||
System battery sensor wrapper.
|
System battery sensor representation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
seconds_left: Optional[float] = field(
|
seconds_left: Optional[float] = None
|
||||||
metadata={
|
power_plugged: Optional[bool] = None
|
||||||
'metadata': {
|
value: Optional[float] = None
|
||||||
'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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,7 +1,45 @@
|
||||||
from marshmallow_dataclass import class_schema
|
from enum import Enum
|
||||||
|
|
||||||
from ._base import BatteryBaseSchema
|
from marshmallow import fields, pre_load
|
||||||
from ._model import Battery
|
|
||||||
|
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 socket import AddressFamily, SocketKind
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
@ -9,88 +9,12 @@ class Connection:
|
||||||
Network/UNIX socket data class.
|
Network/UNIX socket data class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
fd: int = field(
|
fd: int
|
||||||
metadata={
|
family: AddressFamily
|
||||||
'metadata': {
|
type: SocketKind
|
||||||
'description': 'File descriptor',
|
local_address: str
|
||||||
'example': 3,
|
local_port: Optional[int] = None
|
||||||
},
|
remote_address: Optional[str] = None
|
||||||
}
|
remote_port: Optional[int] = None
|
||||||
)
|
status: Optional[str] = None
|
||||||
|
pid: Optional[int] = None
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,7 +1,111 @@
|
||||||
from marshmallow_dataclass import class_schema
|
from marshmallow import fields, pre_load
|
||||||
|
|
||||||
from ._base import ConnectionBaseSchema
|
from .._base import SystemBaseSchema
|
||||||
from ._model import Connection
|
|
||||||
|
|
||||||
|
|
||||||
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 ._model import Cpu, CpuFrequency, CpuInfo, CpuStats, CpuTimes
|
||||||
from ._schemas import CpuFrequencySchema, CpuInfoSchema, CpuStatsSchema, CpuTimesSchema
|
from ._schemas import (
|
||||||
|
CpuFrequencySchema,
|
||||||
|
CpuInfoSchema,
|
||||||
|
CpuSchema,
|
||||||
|
CpuStatsSchema,
|
||||||
|
CpuTimesSchema,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
@ -8,6 +14,7 @@ __all__ = [
|
||||||
"CpuFrequencySchema",
|
"CpuFrequencySchema",
|
||||||
"CpuInfo",
|
"CpuInfo",
|
||||||
"CpuInfoSchema",
|
"CpuInfoSchema",
|
||||||
|
"CpuSchema",
|
||||||
"CpuStats",
|
"CpuStats",
|
||||||
"CpuStatsSchema",
|
"CpuStatsSchema",
|
||||||
"CpuTimes",
|
"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 dataclasses import dataclass, field
|
||||||
from typing import List, Optional, Tuple
|
from typing import List, Optional, Tuple
|
||||||
|
|
||||||
from platypush.schemas.dataclasses import percent_field
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class CpuInfo:
|
class CpuInfo:
|
||||||
|
@ -10,117 +8,18 @@ class CpuInfo:
|
||||||
CPU info data class.
|
CPU info data class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
architecture: Optional[str] = field(
|
bits: int
|
||||||
metadata={
|
cores: int
|
||||||
'data_key': 'arch_string_raw',
|
architecture: Optional[str] = None
|
||||||
'metadata': {
|
vendor: Optional[str] = None
|
||||||
'description': 'CPU architecture',
|
brand: Optional[str] = None
|
||||||
'example': 'x86_64',
|
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
|
||||||
bits: int = field(
|
l2_cache_size: Optional[float] = None
|
||||||
metadata={
|
l3_cache_size: Optional[float] = None
|
||||||
'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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -129,16 +28,16 @@ class CpuTimes:
|
||||||
CPU times data class.
|
CPU times data class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
user: Optional[float] = percent_field()
|
user: Optional[float] = None
|
||||||
nice: Optional[float] = percent_field()
|
nice: Optional[float] = None
|
||||||
system: Optional[float] = percent_field()
|
system: Optional[float] = None
|
||||||
idle: Optional[float] = percent_field()
|
idle: Optional[float] = None
|
||||||
iowait: Optional[float] = percent_field()
|
iowait: Optional[float] = None
|
||||||
irq: Optional[float] = percent_field()
|
irq: Optional[float] = None
|
||||||
softirq: Optional[float] = percent_field()
|
softirq: Optional[float] = None
|
||||||
steal: Optional[float] = percent_field()
|
steal: Optional[float] = None
|
||||||
guest: Optional[float] = percent_field()
|
guest: Optional[float] = None
|
||||||
guest_nice: Optional[float] = percent_field()
|
guest_nice: Optional[float] = None
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -175,4 +74,4 @@ class Cpu:
|
||||||
frequency: CpuFrequency
|
frequency: CpuFrequency
|
||||||
stats: CpuStats
|
stats: CpuStats
|
||||||
load_avg: Tuple[float, float, float]
|
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 SystemBaseSchema
|
||||||
from ._base import CpuInfoBaseSchema, CpuTimesBaseSchema
|
|
||||||
from ._model import CpuFrequency, CpuInfo, CpuStats, CpuTimes
|
|
||||||
|
|
||||||
|
|
||||||
CpuFrequencySchema = class_schema(CpuFrequency, base_schema=SystemBaseSchema)
|
class CpuInfoSchema(SystemBaseSchema):
|
||||||
CpuInfoSchema = class_schema(CpuInfo, base_schema=CpuInfoBaseSchema)
|
"""
|
||||||
CpuTimesSchema = class_schema(CpuTimes, base_schema=CpuTimesBaseSchema)
|
Base schema for CPU info.
|
||||||
CpuStatsSchema = class_schema(CpuStats, base_schema=SystemBaseSchema)
|
"""
|
||||||
|
|
||||||
|
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 typing import Optional
|
||||||
|
|
||||||
from platypush.schemas.dataclasses import percent_field
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Disk:
|
class Disk:
|
||||||
|
@ -10,120 +8,18 @@ class Disk:
|
||||||
Disk data class.
|
Disk data class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
device: str = field(
|
device: str
|
||||||
metadata={
|
mountpoint: Optional[str] = None
|
||||||
'metadata': {
|
fstype: Optional[str] = None
|
||||||
'description': 'Path/identifier of the disk/partition',
|
opts: Optional[str] = None
|
||||||
'example': '/dev/sda1',
|
total: Optional[int] = None
|
||||||
}
|
used: Optional[int] = None
|
||||||
}
|
free: Optional[int] = None
|
||||||
)
|
read_count: Optional[int] = None
|
||||||
|
write_count: Optional[int] = None
|
||||||
mountpoint: Optional[str] = field(
|
read_bytes: Optional[int] = None
|
||||||
metadata={
|
write_bytes: Optional[int] = None
|
||||||
'metadata': {
|
read_time: Optional[float] = None
|
||||||
'description': 'Where the disk is mounted',
|
write_time: Optional[float] = None
|
||||||
'example': '/home',
|
busy_time: Optional[float] = None
|
||||||
}
|
percent: Optional[float] = None
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
|
@ -1,7 +1,131 @@
|
||||||
from marshmallow_dataclass import class_schema
|
from marshmallow import fields, pre_load
|
||||||
|
|
||||||
from ._base import DiskBaseSchema
|
from platypush.schemas.dataclasses import percent_field
|
||||||
from ._model import Disk
|
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
|
@dataclass
|
||||||
|
@ -7,29 +8,6 @@ class Fan:
|
||||||
System fan sensor data class.
|
System fan sensor data class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
id: str = field(
|
id: str
|
||||||
metadata={
|
label: Optional[str] = None
|
||||||
'metadata': {
|
value: Optional[float] = None
|
||||||
'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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,7 +1,39 @@
|
||||||
from marshmallow_dataclass import class_schema
|
from marshmallow import fields, pre_load
|
||||||
|
|
||||||
from ._base import FanBaseSchema
|
from .._base import SystemBaseSchema
|
||||||
from ._model import Fan
|
|
||||||
|
|
||||||
|
|
||||||
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 dataclasses import dataclass
|
||||||
|
|
||||||
from platypush.schemas.dataclasses import percent_field
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -9,79 +7,16 @@ class MemoryStats:
|
||||||
Memory stats data class.
|
Memory stats data class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
total: int = field(
|
total: int
|
||||||
metadata={
|
available: int
|
||||||
'metadata': {
|
used: int
|
||||||
'description': 'Total available memory, in bytes',
|
free: int
|
||||||
}
|
active: int
|
||||||
}
|
inactive: int
|
||||||
)
|
buffers: int
|
||||||
|
cached: int
|
||||||
available: int = field(
|
shared: int
|
||||||
metadata={
|
percent: float
|
||||||
'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()
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -90,28 +25,7 @@ class SwapStats:
|
||||||
Swap memory stats data class.
|
Swap memory stats data class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
total: int = field(
|
total: int
|
||||||
metadata={
|
used: int
|
||||||
'metadata': {
|
free: int
|
||||||
'description': 'Total available memory, in bytes',
|
percent: float
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
used: int = field(
|
|
||||||
metadata={
|
|
||||||
'metadata': {
|
|
||||||
'description': 'Used memory, in bytes',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
free: int = field(
|
|
||||||
metadata={
|
|
||||||
'metadata': {
|
|
||||||
'description': 'Free memory, in bytes',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
percent: float = percent_field()
|
|
||||||
|
|
|
@ -1,8 +1,133 @@
|
||||||
from marshmallow_dataclass import class_schema
|
from marshmallow import fields, pre_load
|
||||||
|
|
||||||
from ._base import MemoryStatsBaseSchema
|
from platypush.schemas.dataclasses import percent_field
|
||||||
from ._model import MemoryStats, SwapStats
|
|
||||||
|
from .._base import SystemBaseSchema
|
||||||
|
|
||||||
|
|
||||||
MemoryStatsSchema = class_schema(MemoryStats, base_schema=MemoryStatsBaseSchema)
|
class MemoryStatsSchema(SystemBaseSchema):
|
||||||
SwapStatsSchema = class_schema(SwapStats, base_schema=MemoryStatsBaseSchema)
|
"""
|
||||||
|
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 socket import AddressFamily
|
||||||
from typing import List, Optional
|
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
|
@dataclass
|
||||||
class NetworkInterfaceAddress:
|
class NetworkInterfaceAddress:
|
||||||
|
@ -157,38 +9,30 @@ class NetworkInterfaceAddress:
|
||||||
Network interface address data class.
|
Network interface address data class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
family: AddressFamily = field(
|
family: Optional[AddressFamily] = None
|
||||||
metadata={
|
address: Optional[str] = None
|
||||||
'metadata': {
|
netmask: Optional[str] = None
|
||||||
'description': 'Address family',
|
broadcast: Optional[str] = None
|
||||||
'example': AddressFamily.AF_INET.name,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
address: Optional[str] = field(
|
|
||||||
metadata={
|
|
||||||
'metadata': {
|
|
||||||
'description': 'IPv4 or IPv6 address of the interface',
|
|
||||||
'example': '192.168.1.2',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
netmask: Optional[str] = field(
|
@dataclass
|
||||||
metadata={
|
class NetworkInterface:
|
||||||
'metadata': {
|
"""
|
||||||
'description': 'Netmask for the interface address',
|
Network interface statistics data class.
|
||||||
'example': '255.255.255.0',
|
"""
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
broadcast: Optional[str] = field(
|
interface: Optional[str] = None
|
||||||
metadata={
|
bytes_sent: int = 0
|
||||||
'metadata': {
|
bytes_recv: int = 0
|
||||||
'description': 'Broadcast address for the interface',
|
packets_sent: int = 0
|
||||||
'example': '192.168.1.255',
|
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 marshmallow import fields, pre_load
|
||||||
from ._model import NetworkInterface
|
from marshmallow.validate import OneOf
|
||||||
|
|
||||||
|
from .._base import SystemBaseSchema
|
||||||
|
|
||||||
|
|
||||||
NetworkInterfaceSchema = class_schema(
|
class NetworkInterfaceAddressSchema(SystemBaseSchema):
|
||||||
NetworkInterface, base_schema=NetworkInterfaceBaseSchema
|
"""
|
||||||
|
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 datetime import datetime
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
from platypush.schemas.dataclasses import percent_field
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Process:
|
class Process:
|
||||||
|
@ -11,100 +9,14 @@ class Process:
|
||||||
System process data class.
|
System process data class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pid: int = field(
|
pid: int
|
||||||
metadata={
|
name: str
|
||||||
'metadata': {
|
parent_pid: Optional[int] = None
|
||||||
'description': 'Process PID',
|
username: Optional[str] = None
|
||||||
'example': 12345,
|
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
|
||||||
name: str = field(
|
cpu_percent: float = 0
|
||||||
metadata={
|
memory_percent: float = 0
|
||||||
'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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,7 +1,124 @@
|
||||||
from marshmallow_dataclass import class_schema
|
from datetime import datetime
|
||||||
|
|
||||||
from ._base import ProcessBaseSchema
|
from dateutil.tz import gettz
|
||||||
from ._model import Process
|
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 ._base import SystemBaseSchema
|
||||||
|
from ._battery import BatterySchema
|
||||||
from ._model import SystemInfo
|
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
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Temperature:
|
class Temperature:
|
||||||
"""
|
"""
|
||||||
System temperature sensor wrapper.
|
System temperature sensor data class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
id: str = field(
|
id: str
|
||||||
metadata={
|
label: Optional[str] = None
|
||||||
'metadata': {
|
value: Optional[float] = None
|
||||||
'description': 'Unique ID for the sensor',
|
high: Optional[float] = None
|
||||||
'example': 'acpi_1',
|
critical: Optional[float] = None
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,7 +1,54 @@
|
||||||
from marshmallow_dataclass import class_schema
|
from marshmallow import fields, pre_load
|
||||||
|
|
||||||
from ._base import TemperatureBaseSchema
|
from .._base import SystemBaseSchema
|
||||||
from ._model import Temperature
|
|
||||||
|
|
||||||
|
|
||||||
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 datetime import datetime
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
@ -9,37 +9,7 @@ class User:
|
||||||
System user wrapper.
|
System user wrapper.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
username: str = field(
|
username: str
|
||||||
metadata={
|
terminal: Optional[str] = None
|
||||||
'metadata': {
|
started: Optional[datetime] = None
|
||||||
'description': 'Username',
|
pid: Optional[int] = None
|
||||||
'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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,7 +1,47 @@
|
||||||
from marshmallow_dataclass import class_schema
|
from datetime import datetime
|
||||||
|
|
||||||
from ._base import UserBaseSchema
|
from dateutil.tz import gettz
|
||||||
from ._model import User
|
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