forked from platypush/platypush
Support for disk
entities in the system
integration.
This commit is contained in:
parent
6b03451386
commit
44b8fd4b34
6 changed files with 454 additions and 225 deletions
|
@ -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>
|
|
@ -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",
|
||||||
|
|
|
@ -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__,
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
DiskPartitionResponse(
|
if disk in basename_parts
|
||||||
device=p.device,
|
|
||||||
mount_point=p.mountpoint,
|
|
||||||
fstype=p.fstype,
|
|
||||||
opts=p.opts,
|
|
||||||
)
|
|
||||||
for p in parts
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
@action
|
|
||||||
def disk_usage(
|
|
||||||
self, path: Optional[str] = None
|
|
||||||
) -> Union[DiskUsageResponse, DiskResponseList]:
|
|
||||||
"""
|
|
||||||
Get the usage of a mounted disk.
|
|
||||||
|
|
||||||
:param path: Path where the device is mounted (default: get stats for all mounted devices).
|
|
||||||
:return: :class:`platypush.message.response.system.DiskUsageResponse` or list of
|
|
||||||
:class:`platypush.message.response.system.DiskUsageResponse`.
|
|
||||||
"""
|
|
||||||
import psutil
|
|
||||||
|
|
||||||
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(
|
usage = {
|
||||||
|
disk: psutil.disk_usage(info['mountpoint'])._asdict()
|
||||||
|
for disk, info in parts.items()
|
||||||
|
}
|
||||||
|
|
||||||
|
return DiskSchema().load( # type: ignore
|
||||||
[
|
[
|
||||||
DiskUsageResponse(
|
{
|
||||||
path=path,
|
**info,
|
||||||
total=disk.total,
|
**io_stats[part],
|
||||||
used=disk.used,
|
**usage[part],
|
||||||
free=disk.free,
|
}
|
||||||
percent=disk.percent,
|
for part, info in parts.items()
|
||||||
)
|
],
|
||||||
for path, disk in disks.items()
|
many=True,
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def disk_io_counters(
|
def disk_info(self):
|
||||||
self, disk: Optional[str] = None, per_disk: bool = False
|
|
||||||
) -> Union[DiskIoCountersResponse, DiskResponseList]:
|
|
||||||
"""
|
"""
|
||||||
Get the I/O counter stats for the mounted disks.
|
Get information about the detected disks and partitions.
|
||||||
|
|
||||||
:param disk: Select the stats for a specific disk (e.g. 'sda1'). Default: get stats for all mounted disks.
|
:return: .. schema:: system.DiskSchema(many=True)
|
||||||
: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
|
return DiskSchema().dump(self._disk_info(), many=True)
|
||||||
|
|
||||||
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']
|
||||||
|
],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue