forked from platypush/platypush
Added NetworkInterface
entities to system
plugin.
Plus, `platypush.schemas.system` has now been split into multiple submodules to avoid a single-file mega-module with all the system schemas definitions.
This commit is contained in:
parent
44b8fd4b34
commit
98a300c4b1
26 changed files with 1006 additions and 595 deletions
|
@ -0,0 +1,167 @@
|
||||||
|
<template>
|
||||||
|
<div class="entity network-interface-container">
|
||||||
|
<div class="head" @click.stop="isCollapsed = !isCollapsed">
|
||||||
|
<div class="col-1 icon">
|
||||||
|
<EntityIcon
|
||||||
|
:entity="value"
|
||||||
|
:loading="loading"
|
||||||
|
:error="error" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-10 label">
|
||||||
|
<div class="name" v-text="value.name" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-1 collapse-toggler" @click.stop="isCollapsed = !isCollapsed">
|
||||||
|
<i class="fas"
|
||||||
|
:class="{'fa-chevron-down': isCollapsed, 'fa-chevron-up': !isCollapsed}" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="body children attributes fade-in" v-if="!isCollapsed">
|
||||||
|
<div class="child" v-if="value.bytes_sent">
|
||||||
|
<div class="col-s-12 col-m-6 label">
|
||||||
|
<div class="name">Bytes sent</div>
|
||||||
|
</div>
|
||||||
|
<div class="value">
|
||||||
|
<div class="name" v-text="convertSize(value.bytes_sent)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="child" v-if="value.bytes_recv">
|
||||||
|
<div class="col-s-12 col-m-6 label">
|
||||||
|
<div class="name">Bytes received</div>
|
||||||
|
</div>
|
||||||
|
<div class="value">
|
||||||
|
<div class="name" v-text="convertSize(value.bytes_recv)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="child" v-if="value.packets_sent">
|
||||||
|
<div class="col-s-12 col-m-6 label">
|
||||||
|
<div class="name">Packets sent</div>
|
||||||
|
</div>
|
||||||
|
<div class="value">
|
||||||
|
<div class="name" v-text="value.packets_sent" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="child" v-if="value.packets_recv">
|
||||||
|
<div class="col-s-12 col-m-6 label">
|
||||||
|
<div class="name">Packets received</div>
|
||||||
|
</div>
|
||||||
|
<div class="value">
|
||||||
|
<div class="name" v-text="value.packets_recv" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="child" v-if="value.errors_in">
|
||||||
|
<div class="col-s-12 col-m-6 label">
|
||||||
|
<div class="name">Inbound errors</div>
|
||||||
|
</div>
|
||||||
|
<div class="value">
|
||||||
|
<div class="name" v-text="value.errors_in" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="child" v-if="value.errors_out">
|
||||||
|
<div class="col-s-12 col-m-6 label">
|
||||||
|
<div class="name">Outbound errors</div>
|
||||||
|
</div>
|
||||||
|
<div class="value">
|
||||||
|
<div class="name" v-text="value.errors_out" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="child" v-if="value.drop_in">
|
||||||
|
<div class="col-s-12 col-m-6 label">
|
||||||
|
<div class="name">Dropped inbound packets</div>
|
||||||
|
</div>
|
||||||
|
<div class="value">
|
||||||
|
<div class="name" v-text="value.drop_in" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="child" v-if="value.drop_out">
|
||||||
|
<div class="col-s-12 col-m-6 label">
|
||||||
|
<div class="name">Dropped outbound packets</div>
|
||||||
|
</div>
|
||||||
|
<div class="value">
|
||||||
|
<div class="name" v-text="value.drop_out" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import EntityMixin from "./EntityMixin"
|
||||||
|
import EntityIcon from "./EntityIcon"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'NetworkInterface',
|
||||||
|
components: {EntityIcon},
|
||||||
|
mixins: [EntityMixin],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isCollapsed: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "common";
|
||||||
|
|
||||||
|
.entity {
|
||||||
|
.head {
|
||||||
|
padding: 0.25em;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapse-toggler {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 3em;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $default-hover-fg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.attributes .child {
|
||||||
|
margin: 0 -0.5em;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
border-bottom: 1px solid $border-color-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-weight: bold;
|
||||||
|
@include from($tablet) {
|
||||||
|
@extend .col-m-6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
font-size: 0.95em;
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
@include from($tablet) {
|
||||||
|
@extend .col-m-6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -71,6 +71,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"network_interface": {
|
||||||
|
"name": "System",
|
||||||
|
"name_plural": "System",
|
||||||
|
"icon": {
|
||||||
|
"class": "fas fa-ethernet"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"current_sensor": {
|
"current_sensor": {
|
||||||
"name": "Sensor",
|
"name": "Sensor",
|
||||||
"name_plural": "Sensors",
|
"name_plural": "Sensors",
|
||||||
|
|
|
@ -175,3 +175,30 @@ if 'disk' not in Base.metadata:
|
||||||
__mapper_args__ = {
|
__mapper_args__ = {
|
||||||
'polymorphic_identity': __tablename__,
|
'polymorphic_identity': __tablename__,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if 'network_interface' not in Base.metadata:
|
||||||
|
|
||||||
|
class NetworkInterface(Entity):
|
||||||
|
"""
|
||||||
|
``NetworkInterface`` ORM model.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__tablename__ = 'network_interface'
|
||||||
|
|
||||||
|
id = Column(
|
||||||
|
Integer, ForeignKey(Entity.id, ondelete='CASCADE'), primary_key=True
|
||||||
|
)
|
||||||
|
|
||||||
|
bytes_sent = Column(Integer)
|
||||||
|
bytes_recv = Column(Integer)
|
||||||
|
packets_sent = Column(Integer)
|
||||||
|
packets_recv = Column(Integer)
|
||||||
|
errors_in = Column(Integer)
|
||||||
|
errors_out = Column(Integer)
|
||||||
|
drop_in = Column(Integer)
|
||||||
|
drop_out = Column(Integer)
|
||||||
|
|
||||||
|
__mapper_args__ = {
|
||||||
|
'polymorphic_identity': __tablename__,
|
||||||
|
}
|
||||||
|
|
|
@ -28,38 +28,6 @@ class SensorResponse(SystemResponse):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class NetworkIoCountersResponse(NetworkResponse):
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
bytes_sent: int,
|
|
||||||
bytes_recv: int,
|
|
||||||
packets_sent: int,
|
|
||||||
packets_recv: int,
|
|
||||||
errin: int,
|
|
||||||
errout: int,
|
|
||||||
dropin: int,
|
|
||||||
dropout: int,
|
|
||||||
nic: Optional[str] = None,
|
|
||||||
*args,
|
|
||||||
**kwargs
|
|
||||||
):
|
|
||||||
super().__init__(
|
|
||||||
*args,
|
|
||||||
output={
|
|
||||||
'bytes_sent': bytes_sent,
|
|
||||||
'bytes_recv': bytes_recv,
|
|
||||||
'packets_sent': packets_sent,
|
|
||||||
'packets_recv': packets_recv,
|
|
||||||
'errin': errin,
|
|
||||||
'errout': errout,
|
|
||||||
'dropin': dropin,
|
|
||||||
'dropout': dropout,
|
|
||||||
'nic': nic,
|
|
||||||
},
|
|
||||||
**kwargs
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class NetworkConnectionResponse(NetworkResponse):
|
class NetworkConnectionResponse(NetworkResponse):
|
||||||
# noinspection PyShadowingBuiltins
|
# noinspection PyShadowingBuiltins
|
||||||
def __init__(
|
def __init__(
|
||||||
|
|
|
@ -18,10 +18,10 @@ from platypush.entities.system import (
|
||||||
CpuTimes as CpuTimesModel,
|
CpuTimes as CpuTimesModel,
|
||||||
Disk as DiskModel,
|
Disk as DiskModel,
|
||||||
MemoryStats as MemoryStatsModel,
|
MemoryStats as MemoryStatsModel,
|
||||||
|
NetworkInterface as NetworkInterfaceModel,
|
||||||
SwapStats as SwapStatsModel,
|
SwapStats as SwapStatsModel,
|
||||||
)
|
)
|
||||||
from platypush.message.response.system import (
|
from platypush.message.response.system import (
|
||||||
NetworkIoCountersResponse,
|
|
||||||
NetworkResponseList,
|
NetworkResponseList,
|
||||||
NetworkConnectionResponse,
|
NetworkConnectionResponse,
|
||||||
NetworkAddressResponse,
|
NetworkAddressResponse,
|
||||||
|
@ -50,6 +50,8 @@ from platypush.schemas.system import (
|
||||||
DiskSchema,
|
DiskSchema,
|
||||||
MemoryStats,
|
MemoryStats,
|
||||||
MemoryStatsSchema,
|
MemoryStatsSchema,
|
||||||
|
NetworkInterface,
|
||||||
|
NetworkInterfaceSchema,
|
||||||
SwapStats,
|
SwapStats,
|
||||||
SwapStatsSchema,
|
SwapStatsSchema,
|
||||||
SystemInfoSchema,
|
SystemInfoSchema,
|
||||||
|
@ -257,48 +259,45 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
||||||
"""
|
"""
|
||||||
return DiskSchema().dump(self._disk_info(), many=True)
|
return DiskSchema().dump(self._disk_info(), many=True)
|
||||||
|
|
||||||
@action
|
def _net_io_counters(self) -> List[NetworkInterface]:
|
||||||
def net_io_counters(
|
return NetworkInterfaceSchema().load( # type: ignore
|
||||||
self, nic: Optional[str] = None, per_nic: bool = False
|
[
|
||||||
) -> Union[NetworkIoCountersResponse, NetworkResponseList]:
|
{'interface': interface, **stats._asdict()}
|
||||||
"""
|
for interface, stats in psutil.net_io_counters(pernic=True).items()
|
||||||
Get the I/O counters stats for the network interfaces.
|
if any(bool(val) for val in stats._asdict().values())
|
||||||
|
],
|
||||||
:param nic: Select the stats for a specific network device (e.g. 'eth0'). Default: get stats for all NICs.
|
many=True,
|
||||||
:param per_nic: Return the stats broken down per interface (default: False).
|
|
||||||
:return: :class:`platypush.message.response.system.NetIoCountersResponse` or list of
|
|
||||||
:class:`platypush.message.response.system.NetIoCountersResponse`.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def _expand_response(_nic, _stats):
|
|
||||||
return NetworkIoCountersResponse(
|
|
||||||
bytes_sent=_stats.bytes_sent,
|
|
||||||
bytes_recv=_stats.bytes_recv,
|
|
||||||
packets_sent=_stats.packets_sent,
|
|
||||||
packets_recv=_stats.packets_recv,
|
|
||||||
errin=_stats.errin,
|
|
||||||
errout=_stats.errout,
|
|
||||||
dropin=_stats.dropin,
|
|
||||||
dropout=_stats.dropout,
|
|
||||||
nic=_nic,
|
|
||||||
)
|
|
||||||
|
|
||||||
if nic:
|
|
||||||
per_nic = True
|
|
||||||
|
|
||||||
io = psutil.net_io_counters(pernic=per_nic)
|
|
||||||
if nic:
|
|
||||||
stats = [d for name, d in io.items() if name == nic]
|
|
||||||
assert stats, 'No such network interface: {}'.format(nic)
|
|
||||||
return _expand_response(nic, stats[0])
|
|
||||||
|
|
||||||
if not per_nic:
|
|
||||||
return _expand_response(nic, io)
|
|
||||||
|
|
||||||
return NetworkResponseList(
|
|
||||||
[_expand_response(nic, stats) for nic, stats in io.items()]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _net_io_counters_avg(self) -> NetworkInterface:
|
||||||
|
stats = psutil.net_io_counters(pernic=False)
|
||||||
|
return NetworkInterfaceSchema().load( # type: ignore
|
||||||
|
{
|
||||||
|
'interface': None,
|
||||||
|
**stats._asdict(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def net_io_counters(self, per_nic: bool = False):
|
||||||
|
"""
|
||||||
|
Get the information and statistics for the network interfaces.
|
||||||
|
|
||||||
|
:param per_nic: Return the stats grouped by interface (default: False).
|
||||||
|
:return: If ``per_nic=False``:
|
||||||
|
|
||||||
|
.. schema:: system.NetworkInterfaceSchema
|
||||||
|
|
||||||
|
If ``per_nic=True`` then a list will be returned, where each item
|
||||||
|
identifies the statistics per network interface:
|
||||||
|
|
||||||
|
.. schema:: system.NetworkInterfaceSchema(many=True)
|
||||||
|
"""
|
||||||
|
|
||||||
|
if per_nic:
|
||||||
|
return NetworkInterfaceSchema().dump(self._net_io_counters(), many=True)
|
||||||
|
return NetworkInterfaceSchema().dump(self._net_io_counters_avg())
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def net_connections(
|
def net_connections(
|
||||||
self, type: Optional[str] = None
|
self, type: Optional[str] = None
|
||||||
|
@ -688,6 +687,7 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
||||||
'memory': self._mem_virtual(),
|
'memory': self._mem_virtual(),
|
||||||
'swap': self._mem_swap(),
|
'swap': self._mem_swap(),
|
||||||
'disks': self._disk_info(),
|
'disks': self._disk_info(),
|
||||||
|
'network': self._net_io_counters(),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -772,6 +772,14 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
||||||
)
|
)
|
||||||
for disk in entities['disks']
|
for disk in entities['disks']
|
||||||
],
|
],
|
||||||
|
*[
|
||||||
|
NetworkInterfaceModel(
|
||||||
|
id=f'system:network_interface:{nic["interface"]}',
|
||||||
|
name=nic.pop('interface'),
|
||||||
|
**nic,
|
||||||
|
)
|
||||||
|
for nic in entities['network']
|
||||||
|
],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,522 +0,0 @@
|
||||||
from dataclasses import dataclass, field
|
|
||||||
from typing import List, Optional, Tuple
|
|
||||||
|
|
||||||
from marshmallow import pre_load
|
|
||||||
from marshmallow.validate import Range
|
|
||||||
from marshmallow_dataclass import class_schema
|
|
||||||
|
|
||||||
from platypush.schemas.dataclasses import DataClassSchema
|
|
||||||
|
|
||||||
|
|
||||||
def percent_field(**kwargs):
|
|
||||||
"""
|
|
||||||
Field used to model percentage float fields between 0 and 1.
|
|
||||||
"""
|
|
||||||
return field(
|
|
||||||
default_factory=float,
|
|
||||||
metadata={
|
|
||||||
'validate': Range(min=0, max=1),
|
|
||||||
**kwargs,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class CpuInfoBaseSchema(DataClassSchema):
|
|
||||||
"""
|
|
||||||
Base schema for CPU info.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@pre_load
|
|
||||||
def pre_load(self, data: dict, **_) -> dict:
|
|
||||||
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]
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
class MemoryStatsBaseSchema(DataClassSchema):
|
|
||||||
"""
|
|
||||||
Base schema for memory stats.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@pre_load
|
|
||||||
def pre_load(self, data: dict, **_) -> dict:
|
|
||||||
# Normalize the percentage between 0 and 1
|
|
||||||
if data.get('percent') is not None:
|
|
||||||
data['percent'] /= 100
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
class CpuTimesBaseSchema(DataClassSchema):
|
|
||||||
"""
|
|
||||||
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].
|
|
||||||
"""
|
|
||||||
return {
|
|
||||||
key: value / 100.0
|
|
||||||
for key, value in (
|
|
||||||
data if isinstance(data, dict) else data._asdict()
|
|
||||||
).items()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class DiskBaseSchema(DataClassSchema):
|
|
||||||
"""
|
|
||||||
Base schema for disk stats.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@pre_load
|
|
||||||
def pre_load(self, data: dict, **_) -> dict:
|
|
||||||
# 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
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
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[int] = field(
|
|
||||||
metadata={
|
|
||||||
'metadata': {
|
|
||||||
'description': 'Advertised CPU frequency, in Hz',
|
|
||||||
'example': 2400000000,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
frequency_actual: Optional[int] = 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[int] = field(
|
|
||||||
metadata={
|
|
||||||
'metadata': {
|
|
||||||
'description': 'Size of the L1 instruction cache, in bytes',
|
|
||||||
'example': 65536,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
l1_data_cache_size: Optional[int] = field(
|
|
||||||
metadata={
|
|
||||||
'metadata': {
|
|
||||||
'description': 'Size of the L1 data cache, in bytes',
|
|
||||||
'example': 65536,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
l2_cache_size: Optional[int] = field(
|
|
||||||
metadata={
|
|
||||||
'metadata': {
|
|
||||||
'description': 'Size of the L2 cache, in bytes',
|
|
||||||
'example': 524288,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
l3_cache_size: Optional[int] = field(
|
|
||||||
metadata={
|
|
||||||
'metadata': {
|
|
||||||
'description': 'Size of the L2 cache, in bytes',
|
|
||||||
'example': 4194304,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
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()
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class CpuStats:
|
|
||||||
"""
|
|
||||||
CPU stats data class.
|
|
||||||
"""
|
|
||||||
|
|
||||||
ctx_switches: int
|
|
||||||
interrupts: int
|
|
||||||
soft_interrupts: int
|
|
||||||
syscalls: int
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class CpuFrequency:
|
|
||||||
"""
|
|
||||||
CPU frequency data class.
|
|
||||||
"""
|
|
||||||
|
|
||||||
current: float
|
|
||||||
min: float
|
|
||||||
max: float
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class CpuData:
|
|
||||||
"""
|
|
||||||
CPU data aggregate dataclass.
|
|
||||||
"""
|
|
||||||
|
|
||||||
info: CpuInfo
|
|
||||||
times: CpuTimes
|
|
||||||
frequency: CpuFrequency
|
|
||||||
stats: CpuStats
|
|
||||||
load_avg: Tuple[float, float, float]
|
|
||||||
percent: float = percent_field()
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
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()
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
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()
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
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: int = field(
|
|
||||||
metadata={
|
|
||||||
'metadata': {
|
|
||||||
'description': 'Number of recorded read operations',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
write_count: int = field(
|
|
||||||
metadata={
|
|
||||||
'metadata': {
|
|
||||||
'description': 'Number of recorded write operations',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
read_bytes: int = field(
|
|
||||||
metadata={
|
|
||||||
'metadata': {
|
|
||||||
'description': 'Number of read bytes',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
write_bytes: int = field(
|
|
||||||
metadata={
|
|
||||||
'metadata': {
|
|
||||||
'description': 'Number of written bytes',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
read_time: float = field(
|
|
||||||
metadata={
|
|
||||||
'metadata': {
|
|
||||||
'description': 'Time spent reading, in seconds',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
write_time: float = field(
|
|
||||||
metadata={
|
|
||||||
'metadata': {
|
|
||||||
'description': 'Time spent writing, in seconds',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
busy_time: float = field(
|
|
||||||
metadata={
|
|
||||||
'metadata': {
|
|
||||||
'description': 'Total disk busy time, in seconds',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
percent: float = percent_field()
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class SystemInfo:
|
|
||||||
"""
|
|
||||||
Aggregate system info dataclass.
|
|
||||||
"""
|
|
||||||
|
|
||||||
cpu: CpuData
|
|
||||||
memory: MemoryStats
|
|
||||||
swap: SwapStats
|
|
||||||
disks: List[Disk]
|
|
||||||
|
|
||||||
|
|
||||||
CpuFrequencySchema = class_schema(CpuFrequency, base_schema=DataClassSchema)
|
|
||||||
CpuInfoSchema = class_schema(CpuInfo, base_schema=CpuInfoBaseSchema)
|
|
||||||
CpuTimesSchema = class_schema(CpuTimes, base_schema=CpuTimesBaseSchema)
|
|
||||||
CpuStatsSchema = class_schema(CpuStats, base_schema=DataClassSchema)
|
|
||||||
DiskSchema = class_schema(Disk, base_schema=DiskBaseSchema)
|
|
||||||
MemoryStatsSchema = class_schema(MemoryStats, base_schema=MemoryStatsBaseSchema)
|
|
||||||
SwapStatsSchema = class_schema(SwapStats, base_schema=MemoryStatsBaseSchema)
|
|
||||||
SystemInfoSchema = class_schema(SystemInfo, base_schema=DataClassSchema)
|
|
39
platypush/schemas/system/__init__.py
Normal file
39
platypush/schemas/system/__init__.py
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
from ._cpu import (
|
||||||
|
Cpu,
|
||||||
|
CpuFrequency,
|
||||||
|
CpuFrequencySchema,
|
||||||
|
CpuInfo,
|
||||||
|
CpuInfoSchema,
|
||||||
|
CpuStats,
|
||||||
|
CpuStatsSchema,
|
||||||
|
CpuTimes,
|
||||||
|
CpuTimesSchema,
|
||||||
|
)
|
||||||
|
from ._disk import Disk, DiskSchema
|
||||||
|
from ._memory import MemoryStats, MemoryStatsSchema, SwapStats, SwapStatsSchema
|
||||||
|
from ._model import SystemInfo
|
||||||
|
from ._network import NetworkInterface, NetworkInterfaceSchema
|
||||||
|
from ._schemas import SystemInfoSchema
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"Cpu",
|
||||||
|
"CpuFrequency",
|
||||||
|
"CpuFrequencySchema",
|
||||||
|
"CpuInfo",
|
||||||
|
"CpuInfoSchema",
|
||||||
|
"CpuStats",
|
||||||
|
"CpuStatsSchema",
|
||||||
|
"CpuTimes",
|
||||||
|
"CpuTimesSchema",
|
||||||
|
"Disk",
|
||||||
|
"DiskSchema",
|
||||||
|
"MemoryStats",
|
||||||
|
"MemoryStatsSchema",
|
||||||
|
"SwapStats",
|
||||||
|
"SwapStatsSchema",
|
||||||
|
"NetworkInterface",
|
||||||
|
"NetworkInterfaceSchema",
|
||||||
|
"SystemInfo",
|
||||||
|
"SystemInfoSchema",
|
||||||
|
]
|
16
platypush/schemas/system/_base.py
Normal file
16
platypush/schemas/system/_base.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
from dataclasses import field
|
||||||
|
|
||||||
|
from marshmallow.validate import Range
|
||||||
|
|
||||||
|
|
||||||
|
def percent_field(**kwargs):
|
||||||
|
"""
|
||||||
|
Field used to model percentage float fields between 0 and 1.
|
||||||
|
"""
|
||||||
|
return field(
|
||||||
|
default_factory=float,
|
||||||
|
metadata={
|
||||||
|
'validate': Range(min=0, max=1),
|
||||||
|
**kwargs,
|
||||||
|
},
|
||||||
|
)
|
15
platypush/schemas/system/_cpu/__init__.py
Normal file
15
platypush/schemas/system/_cpu/__init__.py
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
from ._model import Cpu, CpuFrequency, CpuInfo, CpuStats, CpuTimes
|
||||||
|
from ._schemas import CpuFrequencySchema, CpuInfoSchema, CpuStatsSchema, CpuTimesSchema
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"Cpu",
|
||||||
|
"CpuFrequency",
|
||||||
|
"CpuFrequencySchema",
|
||||||
|
"CpuInfo",
|
||||||
|
"CpuInfoSchema",
|
||||||
|
"CpuStats",
|
||||||
|
"CpuStatsSchema",
|
||||||
|
"CpuTimes",
|
||||||
|
"CpuTimesSchema",
|
||||||
|
]
|
37
platypush/schemas/system/_cpu/_base.py
Normal file
37
platypush/schemas/system/_cpu/_base.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
from marshmallow import pre_load
|
||||||
|
|
||||||
|
from platypush.schemas.dataclasses import DataClassSchema
|
||||||
|
|
||||||
|
|
||||||
|
class CpuInfoBaseSchema(DataClassSchema):
|
||||||
|
"""
|
||||||
|
Base schema for CPU info.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@pre_load
|
||||||
|
def pre_load(self, data: dict, **_) -> dict:
|
||||||
|
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]
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class CpuTimesBaseSchema(DataClassSchema):
|
||||||
|
"""
|
||||||
|
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].
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
key: value / 100.0
|
||||||
|
for key, value in (
|
||||||
|
data if isinstance(data, dict) else data._asdict()
|
||||||
|
).items()
|
||||||
|
}
|
178
platypush/schemas/system/_cpu/_model.py
Normal file
178
platypush/schemas/system/_cpu/_model.py
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import List, Optional, Tuple
|
||||||
|
|
||||||
|
from .._base import percent_field
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
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[int] = field(
|
||||||
|
metadata={
|
||||||
|
'metadata': {
|
||||||
|
'description': 'Advertised CPU frequency, in Hz',
|
||||||
|
'example': 2400000000,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
frequency_actual: Optional[int] = 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[int] = field(
|
||||||
|
metadata={
|
||||||
|
'metadata': {
|
||||||
|
'description': 'Size of the L1 instruction cache, in bytes',
|
||||||
|
'example': 65536,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
l1_data_cache_size: Optional[int] = field(
|
||||||
|
metadata={
|
||||||
|
'metadata': {
|
||||||
|
'description': 'Size of the L1 data cache, in bytes',
|
||||||
|
'example': 65536,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
l2_cache_size: Optional[int] = field(
|
||||||
|
metadata={
|
||||||
|
'metadata': {
|
||||||
|
'description': 'Size of the L2 cache, in bytes',
|
||||||
|
'example': 524288,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
l3_cache_size: Optional[int] = field(
|
||||||
|
metadata={
|
||||||
|
'metadata': {
|
||||||
|
'description': 'Size of the L2 cache, in bytes',
|
||||||
|
'example': 4194304,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
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()
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CpuStats:
|
||||||
|
"""
|
||||||
|
CPU stats data class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
ctx_switches: int
|
||||||
|
interrupts: int
|
||||||
|
soft_interrupts: int
|
||||||
|
syscalls: int
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CpuFrequency:
|
||||||
|
"""
|
||||||
|
CPU frequency data class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
current: float
|
||||||
|
min: float
|
||||||
|
max: float
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Cpu:
|
||||||
|
"""
|
||||||
|
CPU data aggregate dataclass.
|
||||||
|
"""
|
||||||
|
|
||||||
|
info: CpuInfo
|
||||||
|
times: CpuTimes
|
||||||
|
frequency: CpuFrequency
|
||||||
|
stats: CpuStats
|
||||||
|
load_avg: Tuple[float, float, float]
|
||||||
|
percent: float = percent_field()
|
12
platypush/schemas/system/_cpu/_schemas.py
Normal file
12
platypush/schemas/system/_cpu/_schemas.py
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
from marshmallow_dataclass import class_schema
|
||||||
|
|
||||||
|
from platypush.schemas.dataclasses import DataClassSchema
|
||||||
|
|
||||||
|
from ._base import CpuInfoBaseSchema, CpuTimesBaseSchema
|
||||||
|
from ._model import CpuFrequency, CpuInfo, CpuStats, CpuTimes
|
||||||
|
|
||||||
|
|
||||||
|
CpuFrequencySchema = class_schema(CpuFrequency, base_schema=DataClassSchema)
|
||||||
|
CpuInfoSchema = class_schema(CpuInfo, base_schema=CpuInfoBaseSchema)
|
||||||
|
CpuTimesSchema = class_schema(CpuTimes, base_schema=CpuTimesBaseSchema)
|
||||||
|
CpuStatsSchema = class_schema(CpuStats, base_schema=DataClassSchema)
|
5
platypush/schemas/system/_disk/__init__.py
Normal file
5
platypush/schemas/system/_disk/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
from ._model import Disk
|
||||||
|
from ._schemas import DiskSchema
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["Disk", "DiskSchema"]
|
22
platypush/schemas/system/_disk/_base.py
Normal file
22
platypush/schemas/system/_disk/_base.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
from marshmallow import pre_load
|
||||||
|
|
||||||
|
from platypush.schemas.dataclasses import DataClassSchema
|
||||||
|
|
||||||
|
|
||||||
|
class DiskBaseSchema(DataClassSchema):
|
||||||
|
"""
|
||||||
|
Base schema for disk stats.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@pre_load
|
||||||
|
def pre_load(self, data: dict, **_) -> dict:
|
||||||
|
# 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
|
129
platypush/schemas/system/_disk/_model.py
Normal file
129
platypush/schemas/system/_disk/_model.py
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from .._base import percent_field
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
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: int = field(
|
||||||
|
metadata={
|
||||||
|
'metadata': {
|
||||||
|
'description': 'Number of recorded read operations',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
write_count: int = field(
|
||||||
|
metadata={
|
||||||
|
'metadata': {
|
||||||
|
'description': 'Number of recorded write operations',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
read_bytes: int = field(
|
||||||
|
metadata={
|
||||||
|
'metadata': {
|
||||||
|
'description': 'Number of read bytes',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
write_bytes: int = field(
|
||||||
|
metadata={
|
||||||
|
'metadata': {
|
||||||
|
'description': 'Number of written bytes',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
read_time: float = field(
|
||||||
|
metadata={
|
||||||
|
'metadata': {
|
||||||
|
'description': 'Time spent reading, in seconds',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
write_time: float = field(
|
||||||
|
metadata={
|
||||||
|
'metadata': {
|
||||||
|
'description': 'Time spent writing, in seconds',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
busy_time: float = field(
|
||||||
|
metadata={
|
||||||
|
'metadata': {
|
||||||
|
'description': 'Total disk busy time, in seconds',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
percent: float = percent_field()
|
7
platypush/schemas/system/_disk/_schemas.py
Normal file
7
platypush/schemas/system/_disk/_schemas.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
from marshmallow_dataclass import class_schema
|
||||||
|
|
||||||
|
from ._base import DiskBaseSchema
|
||||||
|
from ._model import Disk
|
||||||
|
|
||||||
|
|
||||||
|
DiskSchema = class_schema(Disk, base_schema=DiskBaseSchema)
|
5
platypush/schemas/system/_memory/__init__.py
Normal file
5
platypush/schemas/system/_memory/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
from ._model import MemoryStats, SwapStats
|
||||||
|
from ._schemas import MemoryStatsSchema, SwapStatsSchema
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["MemoryStats", "MemoryStatsSchema", "SwapStats", "SwapStatsSchema"]
|
16
platypush/schemas/system/_memory/_base.py
Normal file
16
platypush/schemas/system/_memory/_base.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
from marshmallow import pre_load
|
||||||
|
|
||||||
|
from platypush.schemas.dataclasses import DataClassSchema
|
||||||
|
|
||||||
|
|
||||||
|
class MemoryStatsBaseSchema(DataClassSchema):
|
||||||
|
"""
|
||||||
|
Base schema for memory stats.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@pre_load
|
||||||
|
def pre_load(self, data: dict, **_) -> dict:
|
||||||
|
# Normalize the percentage between 0 and 1
|
||||||
|
if data.get('percent') is not None:
|
||||||
|
data['percent'] /= 100
|
||||||
|
return data
|
117
platypush/schemas/system/_memory/_model.py
Normal file
117
platypush/schemas/system/_memory/_model.py
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
|
from .._base import percent_field
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
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()
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
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()
|
8
platypush/schemas/system/_memory/_schemas.py
Normal file
8
platypush/schemas/system/_memory/_schemas.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
from marshmallow_dataclass import class_schema
|
||||||
|
|
||||||
|
from ._base import MemoryStatsBaseSchema
|
||||||
|
from ._model import MemoryStats, SwapStats
|
||||||
|
|
||||||
|
|
||||||
|
MemoryStatsSchema = class_schema(MemoryStats, base_schema=MemoryStatsBaseSchema)
|
||||||
|
SwapStatsSchema = class_schema(SwapStats, base_schema=MemoryStatsBaseSchema)
|
20
platypush/schemas/system/_model.py
Normal file
20
platypush/schemas/system/_model.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from ._cpu import Cpu
|
||||||
|
from ._disk import Disk
|
||||||
|
from ._memory import MemoryStats, SwapStats
|
||||||
|
from ._network import NetworkInterface
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SystemInfo:
|
||||||
|
"""
|
||||||
|
Aggregate system info dataclass.
|
||||||
|
"""
|
||||||
|
|
||||||
|
cpu: Cpu
|
||||||
|
memory: MemoryStats
|
||||||
|
swap: SwapStats
|
||||||
|
disks: List[Disk]
|
||||||
|
network: List[NetworkInterface]
|
8
platypush/schemas/system/_network/__init__.py
Normal file
8
platypush/schemas/system/_network/__init__.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
from ._model import NetworkInterface
|
||||||
|
from ._schemas import NetworkInterfaceSchema
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"NetworkInterface",
|
||||||
|
"NetworkInterfaceSchema",
|
||||||
|
]
|
22
platypush/schemas/system/_network/_base.py
Normal file
22
platypush/schemas/system/_network/_base.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
from marshmallow import pre_load
|
||||||
|
|
||||||
|
from platypush.schemas.dataclasses import DataClassSchema
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkInterfaceBaseSchema(DataClassSchema):
|
||||||
|
"""
|
||||||
|
Base schema for network interface stats.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@pre_load
|
||||||
|
def pre_load(self, data: dict, **_) -> dict:
|
||||||
|
for in_attr, out_attr in {
|
||||||
|
'errin': 'errors_in',
|
||||||
|
'errout': 'errors_out',
|
||||||
|
'dropin': 'drop_in',
|
||||||
|
'dropout': 'drop_out',
|
||||||
|
}.items():
|
||||||
|
if in_attr in data:
|
||||||
|
data[out_attr] = data.pop(in_attr)
|
||||||
|
|
||||||
|
return data
|
82
platypush/schemas/system/_network/_model.py
Normal file
82
platypush/schemas/system/_network/_model.py
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
@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',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
9
platypush/schemas/system/_network/_schemas.py
Normal file
9
platypush/schemas/system/_network/_schemas.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
from marshmallow_dataclass import class_schema
|
||||||
|
|
||||||
|
from ._base import NetworkInterfaceBaseSchema
|
||||||
|
from ._model import NetworkInterface
|
||||||
|
|
||||||
|
|
||||||
|
NetworkInterfaceSchema = class_schema(
|
||||||
|
NetworkInterface, base_schema=NetworkInterfaceBaseSchema
|
||||||
|
)
|
8
platypush/schemas/system/_schemas.py
Normal file
8
platypush/schemas/system/_schemas.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
from marshmallow_dataclass import class_schema
|
||||||
|
|
||||||
|
from platypush.schemas.dataclasses import DataClassSchema
|
||||||
|
|
||||||
|
from ._model import SystemInfo
|
||||||
|
|
||||||
|
|
||||||
|
SystemInfoSchema = class_schema(SystemInfo, base_schema=DataClassSchema)
|
Loading…
Reference in a new issue