Support for `disk` entities in the `system` integration.

This commit is contained in:
Fabio Manganiello 2023-04-20 16:26:51 +02:00
parent 6b03451386
commit 44b8fd4b34
Signed by: blacklight
GPG Key ID: D90FBA7F76362774
6 changed files with 454 additions and 225 deletions

View File

@ -0,0 +1,225 @@
<template>
<div class="entity disk-container">
<div class="head" @click.stop="isCollapsed = !isCollapsed">
<div class="col-1 icon">
<EntityIcon
:entity="value"
:loading="loading"
:error="error" />
</div>
<div class="col-8 label">
<div class="name" v-text="value.name" />
</div>
<div class="col-2 value" v-text="Math.round(value.percent * 100, 1) + '%'" />
<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.mountpoint?.length">
<div class="col-s-12 col-m-6 label">
<div class="name">Mountpoint</div>
</div>
<div class="value">
<div class="name" v-text="value.mountpoint" />
</div>
</div>
<div class="child" v-if="value.fstype?.length">
<div class="col-s-12 col-m-6 label">
<div class="name">Filesystem</div>
</div>
<div class="value">
<div class="name" v-text="value.fstype" />
</div>
</div>
<div class="child" v-if="value.opts?.length">
<div class="col-s-12 col-m-6 label">
<div class="name">Mount options</div>
</div>
<div class="value">
<div class="name" v-text="value.opts" />
</div>
</div>
<div class="child" v-if="value.total != null">
<div class="col-s-12 col-m-6 label">
<div class="name">Total space</div>
</div>
<div class="value">
<div class="name" v-text="convertSize(value.total)" />
</div>
</div>
<div class="child" v-if="value.used != null">
<div class="col-s-12 col-m-6 label">
<div class="name">Used space</div>
</div>
<div class="value">
<div class="name" v-text="convertSize(value.used)" />
</div>
</div>
<div class="child" v-if="value.free != null">
<div class="col-s-12 col-m-6 label">
<div class="name">Available space</div>
</div>
<div class="value">
<div class="name" v-text="convertSize(value.free)" />
</div>
</div>
<div class="child" v-if="value.read_count != null">
<div class="col-s-12 col-m-6 label">
<div class="name">Number of reads</div>
</div>
<div class="value">
<div class="name" v-text="value.read_count" />
</div>
</div>
<div class="child" v-if="value.write_count != null">
<div class="col-s-12 col-m-6 label">
<div class="name">Number of writes</div>
</div>
<div class="value">
<div class="name" v-text="value.write_count" />
</div>
</div>
<div class="child" v-if="value.read_bytes != null">
<div class="col-s-12 col-m-6 label">
<div class="name">Bytes read</div>
</div>
<div class="value">
<div class="name" v-text="convertSize(value.read_bytes)" />
</div>
</div>
<div class="child" v-if="value.write_bytes != null">
<div class="col-s-12 col-m-6 label">
<div class="name">Bytes written</div>
</div>
<div class="value">
<div class="name" v-text="convertSize(value.write_bytes)" />
</div>
</div>
<div class="child" v-if="value.read_time != null">
<div class="col-s-12 col-m-6 label">
<div class="name">Read time</div>
</div>
<div class="value">
<div class="name" v-text="convertTime(value.read_time)" />
</div>
</div>
<div class="child" v-if="value.write_time != null">
<div class="col-s-12 col-m-6 label">
<div class="name">Write time</div>
</div>
<div class="value">
<div class="name" v-text="convertTime(value.write_time)" />
</div>
</div>
<div class="child" v-if="value.busy_time != null">
<div class="col-s-12 col-m-6 label">
<div class="name">Busy time</div>
</div>
<div class="value">
<div class="name" v-text="convertTime(value.busy_time)" />
</div>
</div>
</div>
</div>
</template>
<script>
import EntityMixin from "./EntityMixin"
import EntityIcon from "./EntityIcon"
export default {
name: 'Disk',
components: {EntityIcon},
mixins: [EntityMixin],
data() {
return {
isCollapsed: true,
}
},
}
</script>
<style lang="scss" scoped>
@import "common";
.entity {
.head {
padding: 0.25em;
.value {
text-align: right;
font-weight: bold;
}
.icon {
margin-right: 1em;
}
}
}
.collapse-toggler {
display: flex;
align-items: center;
flex: 1;
min-height: 3em;
cursor: pointer;
@include from($tablet) {
@include until($desktop) {
margin-left: 3.25em;
}
}
&: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>

View File

@ -63,6 +63,14 @@
} }
}, },
"disk": {
"name": "System",
"name_plural": "System",
"icon": {
"class": "fas fa-hard-drive"
}
},
"current_sensor": { "current_sensor": {
"name": "Sensor", "name": "Sensor",
"name_plural": "Sensors", "name_plural": "Sensors",

View File

@ -142,3 +142,36 @@ if 'swap_stats' not in Base.metadata:
__mapper_args__ = { __mapper_args__ = {
'polymorphic_identity': __tablename__, 'polymorphic_identity': __tablename__,
} }
if 'disk' not in Base.metadata:
class Disk(Entity):
"""
``Disk`` ORM model.
"""
__tablename__ = 'disk'
id = Column(
Integer, ForeignKey(Entity.id, ondelete='CASCADE'), primary_key=True
)
mountpoint = Column(String)
fstype = Column(String)
opts = Column(String)
total = Column(Integer)
used = Column(Integer)
free = Column(Integer)
percent = Column(Float)
read_count = Column(Integer)
write_count = Column(Integer)
read_bytes = Column(Integer)
write_bytes = Column(Integer)
read_time = Column(Float)
write_time = Column(Float)
busy_time = Column(Float)
__mapper_args__ = {
'polymorphic_identity': __tablename__,
}

View File

@ -28,86 +28,6 @@ class SensorResponse(SystemResponse):
pass pass
class DiskPartitionResponse(DiskResponse):
def __init__(
self,
device: str,
mount_point: str,
fstype: Optional[str] = None,
opts: Optional[str] = None,
*args,
**kwargs
):
super().__init__(
*args,
output={
'device': device,
'mount_point': mount_point,
'fstype': fstype,
'opts': opts,
},
**kwargs
)
class DiskUsageResponse(DiskResponse):
def __init__(
self,
path: str,
total: int,
used: int,
free: int,
percent: float,
*args,
**kwargs
):
super().__init__(
*args,
output={
'path': path,
'total': total,
'used': used,
'free': free,
'percent': percent,
},
**kwargs
)
class DiskIoCountersResponse(DiskResponse):
def __init__(
self,
read_count: int,
write_count: int,
read_bytes: int,
write_bytes: int,
read_time: int,
write_time: int,
read_merged_count: int,
write_merged_count: int,
busy_time: int,
disk: Optional[str] = None,
*args,
**kwargs
):
super().__init__(
*args,
output={
'read_count': read_count,
'write_count': write_count,
'read_bytes': read_bytes,
'write_bytes': write_bytes,
'read_time': read_time,
'write_time': write_time,
'read_merged_count': read_merged_count,
'write_merged_count': write_merged_count,
'busy_time': busy_time,
'disk': disk,
},
**kwargs
)
class NetworkIoCountersResponse(NetworkResponse): class NetworkIoCountersResponse(NetworkResponse):
def __init__( def __init__(
self, self,
@ -363,11 +283,6 @@ class SystemResponseList(SystemResponse):
super().__init__(output=[r.output for r in responses], *args, **kwargs) super().__init__(output=[r.output for r in responses], *args, **kwargs)
class DiskResponseList(DiskResponse, SystemResponseList):
def __init__(self, responses: List[DiskResponse], *args, **kwargs):
super().__init__(responses=responses, *args, **kwargs)
class NetworkResponseList(NetworkResponse, SystemResponseList): class NetworkResponseList(NetworkResponse, SystemResponseList):
def __init__(self, responses: List[NetworkResponse], *args, **kwargs): def __init__(self, responses: List[NetworkResponse], *args, **kwargs):
super().__init__(responses=responses, *args, **kwargs) super().__init__(responses=responses, *args, **kwargs)

View File

@ -1,9 +1,12 @@
import os
import socket import socket
from datetime import datetime from datetime import datetime
from typing import Tuple, Union, List, Optional, Dict from typing import Tuple, Union, List, Optional, Dict
from typing_extensions import override from typing_extensions import override
import psutil
from platypush.entities import Entity from platypush.entities import Entity
from platypush.entities.devices import Device from platypush.entities.devices import Device
from platypush.entities.managers import EntityManager from platypush.entities.managers import EntityManager
@ -13,14 +16,11 @@ from platypush.entities.system import (
CpuInfo as CpuInfoModel, CpuInfo as CpuInfoModel,
CpuStats as CpuStatsModel, CpuStats as CpuStatsModel,
CpuTimes as CpuTimesModel, CpuTimes as CpuTimesModel,
Disk as DiskModel,
MemoryStats as MemoryStatsModel, MemoryStats as MemoryStatsModel,
SwapStats as SwapStatsModel, SwapStats as SwapStatsModel,
) )
from platypush.message.response.system import ( from platypush.message.response.system import (
DiskResponseList,
DiskPartitionResponse,
DiskUsageResponse,
DiskIoCountersResponse,
NetworkIoCountersResponse, NetworkIoCountersResponse,
NetworkResponseList, NetworkResponseList,
NetworkConnectionResponse, NetworkConnectionResponse,
@ -46,6 +46,8 @@ from platypush.schemas.system import (
CpuStatsSchema, CpuStatsSchema,
CpuTimes, CpuTimes,
CpuTimesSchema, CpuTimesSchema,
Disk,
DiskSchema,
MemoryStats, MemoryStats,
MemoryStatsSchema, MemoryStatsSchema,
SwapStats, SwapStats,
@ -95,16 +97,12 @@ class SystemPlugin(SensorPlugin, EntityManager):
@classmethod @classmethod
def _cpu_times_avg(cls, percent=True) -> CpuTimes: def _cpu_times_avg(cls, percent=True) -> CpuTimes:
import psutil
method = psutil.cpu_times_percent if percent else psutil.cpu_times method = psutil.cpu_times_percent if percent else psutil.cpu_times
times = method(percpu=False) times = method(percpu=False)
return cls._load_cpu_times(times, many=False) # type: ignore return cls._load_cpu_times(times, many=False) # type: ignore
@classmethod @classmethod
def _cpu_times_per_cpu(cls, percent=True) -> List[CpuTimes]: def _cpu_times_per_cpu(cls, percent=True) -> List[CpuTimes]:
import psutil
method = psutil.cpu_times_percent if percent else psutil.cpu_times method = psutil.cpu_times_percent if percent else psutil.cpu_times
times = method(percpu=True) times = method(percpu=True)
return cls._load_cpu_times(times, many=True) # type: ignore return cls._load_cpu_times(times, many=True) # type: ignore
@ -143,8 +141,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
function be called with at least 0.1 seconds between calls. function be called with at least 0.1 seconds between calls.
:return: float if ``per_cpu=False``, ``list[float]`` otherwise. :return: float if ``per_cpu=False``, ``list[float]`` otherwise.
""" """
import psutil
percent = psutil.cpu_percent(percpu=per_cpu, interval=interval) percent = psutil.cpu_percent(percpu=per_cpu, interval=interval)
if per_cpu: if per_cpu:
@ -152,8 +148,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
return percent return percent
def _cpu_stats(self) -> CpuStats: def _cpu_stats(self) -> CpuStats:
import psutil
stats = psutil.cpu_stats() stats = psutil.cpu_stats()
return CpuStatsSchema().load(stats._asdict()) # type: ignore return CpuStatsSchema().load(stats._asdict()) # type: ignore
@ -167,14 +161,10 @@ class SystemPlugin(SensorPlugin, EntityManager):
return CpuStatsSchema().dump(self._cpu_stats()) # type: ignore return CpuStatsSchema().dump(self._cpu_stats()) # type: ignore
def _cpu_frequency_avg(self) -> CpuFrequency: def _cpu_frequency_avg(self) -> CpuFrequency:
import psutil
freq = psutil.cpu_freq(percpu=False) freq = psutil.cpu_freq(percpu=False)
return CpuFrequencySchema().load(freq._asdict()) # type: ignore return CpuFrequencySchema().load(freq._asdict()) # type: ignore
def _cpu_frequency_per_cpu(self) -> List[CpuFrequency]: def _cpu_frequency_per_cpu(self) -> List[CpuFrequency]:
import psutil
freq = psutil.cpu_freq(percpu=True) freq = psutil.cpu_freq(percpu=True)
return CpuFrequencySchema().load(freq._asdict(), many=True) # type: ignore return CpuFrequencySchema().load(freq._asdict(), many=True) # type: ignore
@ -203,13 +193,9 @@ class SystemPlugin(SensorPlugin, EntityManager):
""" """
Get the average load as a vector that represents the load within the last 1, 5 and 15 minutes. Get the average load as a vector that represents the load within the last 1, 5 and 15 minutes.
""" """
import psutil
return psutil.getloadavg() return psutil.getloadavg()
def _mem_virtual(self) -> MemoryStats: def _mem_virtual(self) -> MemoryStats:
import psutil
return MemoryStatsSchema().load( return MemoryStatsSchema().load(
psutil.virtual_memory()._asdict() psutil.virtual_memory()._asdict()
) # type: ignore ) # type: ignore
@ -224,8 +210,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
return MemoryStatsSchema().dump(self._mem_virtual()) # type: ignore return MemoryStatsSchema().dump(self._mem_virtual()) # type: ignore
def _mem_swap(self) -> SwapStats: def _mem_swap(self) -> SwapStats:
import psutil
return SwapStatsSchema().load(psutil.swap_memory()._asdict()) # type: ignore return SwapStatsSchema().load(psutil.swap_memory()._asdict()) # type: ignore
@action @action
@ -237,111 +221,41 @@ class SystemPlugin(SensorPlugin, EntityManager):
""" """
return SwapStatsSchema().dump(self._mem_swap()) # type: ignore return SwapStatsSchema().dump(self._mem_swap()) # type: ignore
@action def _disk_info(self) -> List[Disk]:
def disk_partitions(self) -> DiskResponseList: parts = {part.device: part._asdict() for part in psutil.disk_partitions()}
""" basename_parts = {os.path.basename(part): part for part in parts}
Get the list of partitions mounted on the system.
:return: list of :class:`platypush.message.response.system.DiskPartitionResponse`
"""
import psutil
parts = psutil.disk_partitions() io_stats = {
return DiskResponseList( basename_parts[disk]: stats._asdict()
for disk, stats in psutil.disk_io_counters(perdisk=True).items()
if disk in basename_parts
}
usage = {
disk: psutil.disk_usage(info['mountpoint'])._asdict()
for disk, info in parts.items()
}
return DiskSchema().load( # type: ignore
[ [
DiskPartitionResponse( {
device=p.device, **info,
mount_point=p.mountpoint, **io_stats[part],
fstype=p.fstype, **usage[part],
opts=p.opts, }
) for part, info in parts.items()
for p in parts ],
] many=True,
) )
@action @action
def disk_usage( def disk_info(self):
self, path: Optional[str] = None
) -> Union[DiskUsageResponse, DiskResponseList]:
""" """
Get the usage of a mounted disk. Get information about the detected disks and partitions.
:param path: Path where the device is mounted (default: get stats for all mounted devices). :return: .. schema:: system.DiskSchema(many=True)
:return: :class:`platypush.message.response.system.DiskUsageResponse` or list of
:class:`platypush.message.response.system.DiskUsageResponse`.
""" """
import psutil return DiskSchema().dump(self._disk_info(), many=True)
if path:
usage = psutil.disk_usage(path)
return DiskUsageResponse(
path=path,
total=usage.total,
used=usage.used,
free=usage.free,
percent=usage.percent,
)
else:
disks = {
p.mountpoint: psutil.disk_usage(p.mountpoint)
for p in psutil.disk_partitions()
}
return DiskResponseList(
[
DiskUsageResponse(
path=path,
total=disk.total,
used=disk.used,
free=disk.free,
percent=disk.percent,
)
for path, disk in disks.items()
]
)
@action
def disk_io_counters(
self, disk: Optional[str] = None, per_disk: bool = False
) -> Union[DiskIoCountersResponse, DiskResponseList]:
"""
Get the I/O counter stats for the mounted disks.
:param disk: Select the stats for a specific disk (e.g. 'sda1'). Default: get stats for all mounted disks.
:param per_disk: Return the stats per disk (default: False).
:return: :class:`platypush.message.response.system.DiskIoCountersResponse` or list of
:class:`platypush.message.response.system.DiskIoCountersResponse`.
"""
import psutil
def _expand_response(_disk, _stats):
return DiskIoCountersResponse(
read_count=_stats.read_count,
write_count=_stats.write_count,
read_bytes=_stats.read_bytes,
write_bytes=_stats.write_bytes,
read_time=_stats.read_time,
write_time=_stats.write_time,
read_merged_count=_stats.read_merged_count,
write_merged_count=_stats.write_merged_count,
busy_time=_stats.busy_time,
disk=_disk,
)
if disk:
per_disk = True
io = psutil.disk_io_counters(perdisk=per_disk)
if disk:
stats = [d for name, d in io.items() if name == disk]
assert stats, 'No such disk: {}'.format(disk)
return _expand_response(disk, stats[0])
if not per_disk:
return _expand_response(None, io)
return DiskResponseList(
[_expand_response(disk, stats) for disk, stats in io.items()]
)
@action @action
def net_io_counters( def net_io_counters(
@ -355,7 +269,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
:return: :class:`platypush.message.response.system.NetIoCountersResponse` or list of :return: :class:`platypush.message.response.system.NetIoCountersResponse` or list of
:class:`platypush.message.response.system.NetIoCountersResponse`. :class:`platypush.message.response.system.NetIoCountersResponse`.
""" """
import psutil
def _expand_response(_nic, _stats): def _expand_response(_nic, _stats):
return NetworkIoCountersResponse( return NetworkIoCountersResponse(
@ -414,8 +327,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
:return: List of :class:`platypush.message.response.system.NetworkConnectionResponse`. :return: List of :class:`platypush.message.response.system.NetworkConnectionResponse`.
""" """
import psutil
conns = psutil.net_connections(kind=type) conns = psutil.net_connections(kind=type)
return NetworkResponseList( return NetworkResponseList(
@ -446,8 +357,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
:return: :class:`platypush.message.response.system.NetworkAddressResponse` or list of :return: :class:`platypush.message.response.system.NetworkAddressResponse` or list of
:class:`platypush.message.response.system.NetworkAddressResponse`. :class:`platypush.message.response.system.NetworkAddressResponse`.
""" """
import psutil
addrs = psutil.net_if_addrs() addrs = psutil.net_if_addrs()
def _expand_addresses(_nic, _addrs): def _expand_addresses(_nic, _addrs):
@ -504,8 +413,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
:return: :class:`platypush.message.response.system.NetworkInterfaceStatsResponse` or list of :return: :class:`platypush.message.response.system.NetworkInterfaceStatsResponse` or list of
:class:`platypush.message.response.system.NetworkInterfaceStatsResponse`. :class:`platypush.message.response.system.NetworkInterfaceStatsResponse`.
""" """
import psutil
stats = psutil.net_if_stats() stats = psutil.net_if_stats()
def _expand_stats(_nic, _stats): def _expand_stats(_nic, _stats):
@ -540,8 +447,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
:param sensor: Select the sensor name. :param sensor: Select the sensor name.
:param fahrenheit: Return the temperature in Fahrenheit (default: Celsius). :param fahrenheit: Return the temperature in Fahrenheit (default: Celsius).
""" """
import psutil
stats = psutil.sensors_temperatures(fahrenheit=fahrenheit) stats = psutil.sensors_temperatures(fahrenheit=fahrenheit)
if sensor: if sensor:
@ -596,8 +501,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
:param sensor: Select the sensor name. :param sensor: Select the sensor name.
:return: List of :class:`platypush.message.response.system.SensorFanResponse`. :return: List of :class:`platypush.message.response.system.SensorFanResponse`.
""" """
import psutil
stats = psutil.sensors_fans() stats = psutil.sensors_fans()
def _expand_stats(name, _stats): def _expand_stats(name, _stats):
@ -627,8 +530,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
Get stats from the battery sensor. Get stats from the battery sensor.
:return: List of :class:`platypush.message.response.system.SensorFanResponse`. :return: List of :class:`platypush.message.response.system.SensorFanResponse`.
""" """
import psutil
stats = psutil.sensors_battery() stats = psutil.sensors_battery()
return SensorBatteryResponse( return SensorBatteryResponse(
@ -643,8 +544,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
Get the list of connected users. Get the list of connected users.
:return: List of :class:`platypush.message.response.system.ConnectUserResponse`. :return: List of :class:`platypush.message.response.system.ConnectUserResponse`.
""" """
import psutil
users = psutil.users() users = psutil.users()
return ConnectedUserResponseList( return ConnectedUserResponseList(
@ -668,8 +567,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
:param filter: Filter the list by name. :param filter: Filter the list by name.
:return: List of :class:`platypush.message.response.system.ProcessResponse`. :return: List of :class:`platypush.message.response.system.ProcessResponse`.
""" """
import psutil
processes = [psutil.Process(pid) for pid in psutil.pids()] processes = [psutil.Process(pid) for pid in psutil.pids()]
p_list = [] p_list = []
@ -720,8 +617,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
@staticmethod @staticmethod
def _get_process(pid: int): def _get_process(pid: int):
import psutil
return psutil.Process(pid) return psutil.Process(pid)
@action @action
@ -730,8 +625,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
:param pid: Process PID. :param pid: Process PID.
:return: ``True`` if the process exists, ``False`` otherwise. :return: ``True`` if the process exists, ``False`` otherwise.
""" """
import psutil
return psutil.pid_exists(pid) return psutil.pid_exists(pid)
@action @action
@ -794,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(),
} }
) )
@ -870,6 +764,14 @@ class SystemPlugin(SensorPlugin, EntityManager):
name='Swap', name='Swap',
**entities['swap'], **entities['swap'],
), ),
*[
DiskModel(
id=f'system:disk:{disk["device"]}',
name=disk.pop('device'),
**disk,
)
for disk in entities['disks']
],
] ]

View File

@ -68,6 +68,25 @@ class CpuTimesBaseSchema(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
@dataclass @dataclass
class CpuInfo: class CpuInfo:
""" """
@ -356,6 +375,131 @@ class SwapStats:
percent: float = percent_field() 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 @dataclass
class SystemInfo: class SystemInfo:
""" """
@ -365,12 +509,14 @@ class SystemInfo:
cpu: CpuData cpu: CpuData
memory: MemoryStats memory: MemoryStats
swap: SwapStats swap: SwapStats
disks: List[Disk]
CpuFrequencySchema = class_schema(CpuFrequency, base_schema=DataClassSchema) CpuFrequencySchema = class_schema(CpuFrequency, base_schema=DataClassSchema)
CpuInfoSchema = class_schema(CpuInfo, base_schema=CpuInfoBaseSchema) CpuInfoSchema = class_schema(CpuInfo, base_schema=CpuInfoBaseSchema)
CpuTimesSchema = class_schema(CpuTimes, base_schema=CpuTimesBaseSchema) CpuTimesSchema = class_schema(CpuTimes, base_schema=CpuTimesBaseSchema)
CpuStatsSchema = class_schema(CpuStats, base_schema=DataClassSchema) CpuStatsSchema = class_schema(CpuStats, base_schema=DataClassSchema)
DiskSchema = class_schema(Disk, base_schema=DiskBaseSchema)
MemoryStatsSchema = class_schema(MemoryStats, base_schema=MemoryStatsBaseSchema) MemoryStatsSchema = class_schema(MemoryStats, base_schema=MemoryStatsBaseSchema)
SwapStatsSchema = class_schema(SwapStats, base_schema=MemoryStatsBaseSchema) SwapStatsSchema = class_schema(SwapStats, base_schema=MemoryStatsBaseSchema)
SystemInfoSchema = class_schema(SystemInfo, base_schema=DataClassSchema) SystemInfoSchema = class_schema(SystemInfo, base_schema=DataClassSchema)