forked from platypush/platypush
Added memory stats entities.
This commit is contained in:
parent
0073239a40
commit
4ebfbf3851
7 changed files with 426 additions and 95 deletions
|
@ -0,0 +1,182 @@
|
|||
<template>
|
||||
<div class="entity memory-stats-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.total != null">
|
||||
<div class="col-s-12 col-m-6 label">
|
||||
<div class="name">Total</div>
|
||||
</div>
|
||||
<div class="value">
|
||||
<div class="name" v-text="convertSize(value.total)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="child" v-if="value.available != null">
|
||||
<div class="col-s-12 col-m-6 label">
|
||||
<div class="name">Available</div>
|
||||
</div>
|
||||
<div class="value">
|
||||
<div class="name" v-text="convertSize(value.available)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="child" v-if="value.used != null">
|
||||
<div class="col-s-12 col-m-6 label">
|
||||
<div class="name">Used</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">Free</div>
|
||||
</div>
|
||||
<div class="value">
|
||||
<div class="name" v-text="convertSize(value.free)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="child" v-if="value.active != null">
|
||||
<div class="col-s-12 col-m-6 label">
|
||||
<div class="name">Active</div>
|
||||
</div>
|
||||
<div class="value">
|
||||
<div class="name" v-text="convertSize(value.active)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="child" v-if="value.inactive != null">
|
||||
<div class="col-s-12 col-m-6 label">
|
||||
<div class="name">Inactive</div>
|
||||
</div>
|
||||
<div class="value">
|
||||
<div class="name" v-text="convertSize(value.inactive)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="child" v-if="value.buffers != null">
|
||||
<div class="col-s-12 col-m-6 label">
|
||||
<div class="name">Buffers</div>
|
||||
</div>
|
||||
<div class="value">
|
||||
<div class="name" v-text="convertSize(value.buffers)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="child" v-if="value.cached != null">
|
||||
<div class="col-s-12 col-m-6 label">
|
||||
<div class="name">Cached</div>
|
||||
</div>
|
||||
<div class="value">
|
||||
<div class="name" v-text="convertSize(value.cached)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="child" v-if="value.shared != null">
|
||||
<div class="col-s-12 col-m-6 label">
|
||||
<div class="name">Shared</div>
|
||||
</div>
|
||||
<div class="value">
|
||||
<div class="name" v-text="convertSize(value.shared)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import EntityMixin from "./EntityMixin"
|
||||
import EntityIcon from "./EntityIcon"
|
||||
|
||||
export default {
|
||||
name: 'MemoryStats',
|
||||
components: {EntityIcon},
|
||||
mixins: [EntityMixin],
|
||||
|
||||
data() {
|
||||
return {
|
||||
isCollapsed: true,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "common";
|
||||
|
||||
.entity {
|
||||
.head {
|
||||
padding: 0.25em;
|
||||
|
||||
.icon {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
.value {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.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>
|
|
@ -0,0 +1 @@
|
|||
MemoryStats.vue
|
|
@ -47,6 +47,22 @@
|
|||
}
|
||||
},
|
||||
|
||||
"memory_stats": {
|
||||
"name": "System",
|
||||
"name_plural": "System",
|
||||
"icon": {
|
||||
"class": "fas fa-memory"
|
||||
}
|
||||
},
|
||||
|
||||
"swap_stats": {
|
||||
"name": "System",
|
||||
"name_plural": "System",
|
||||
"icon": {
|
||||
"class": "fas fa-memory"
|
||||
}
|
||||
},
|
||||
|
||||
"current_sensor": {
|
||||
"name": "Sensor",
|
||||
"name_plural": "Sensors",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from sqlalchemy import Column, ForeignKey, Integer, JSON, String
|
||||
from sqlalchemy import Column, Float, ForeignKey, Integer, JSON, String
|
||||
|
||||
from platypush.common.db import Base
|
||||
|
||||
|
@ -88,3 +88,55 @@ if 'cpu_stats' not in Base.metadata:
|
|||
__mapper_args__ = {
|
||||
'polymorphic_identity': __tablename__,
|
||||
}
|
||||
|
||||
|
||||
if 'memory_stats' not in Base.metadata:
|
||||
|
||||
class MemoryStats(Entity):
|
||||
"""
|
||||
``MemoryStats`` ORM model.
|
||||
"""
|
||||
|
||||
__tablename__ = 'memory_stats'
|
||||
|
||||
id = Column(
|
||||
Integer, ForeignKey(Entity.id, ondelete='CASCADE'), primary_key=True
|
||||
)
|
||||
|
||||
total = Column(Integer)
|
||||
available = Column(Integer)
|
||||
used = Column(Integer)
|
||||
free = Column(Integer)
|
||||
active = Column(Integer)
|
||||
inactive = Column(Integer)
|
||||
buffers = Column(Integer)
|
||||
cached = Column(Integer)
|
||||
shared = Column(Integer)
|
||||
percent = Column(Float)
|
||||
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': __tablename__,
|
||||
}
|
||||
|
||||
|
||||
if 'swap_stats' not in Base.metadata:
|
||||
|
||||
class SwapStats(Entity):
|
||||
"""
|
||||
``SwapStats`` ORM model.
|
||||
"""
|
||||
|
||||
__tablename__ = 'swap_stats'
|
||||
|
||||
id = Column(
|
||||
Integer, ForeignKey(Entity.id, ondelete='CASCADE'), primary_key=True
|
||||
)
|
||||
|
||||
total = Column(Integer)
|
||||
used = Column(Integer)
|
||||
free = Column(Integer)
|
||||
percent = Column(Float)
|
||||
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': __tablename__,
|
||||
}
|
||||
|
|
|
@ -28,66 +28,6 @@ class SensorResponse(SystemResponse):
|
|||
pass
|
||||
|
||||
|
||||
class VirtualMemoryUsageResponse(MemoryResponse):
|
||||
def __init__(
|
||||
self,
|
||||
total: int,
|
||||
available: int,
|
||||
percent: float,
|
||||
used: int,
|
||||
free: int,
|
||||
active: int,
|
||||
inactive: int,
|
||||
buffers: int,
|
||||
cached: int,
|
||||
shared: int,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
super().__init__(
|
||||
*args,
|
||||
output={
|
||||
'total': total,
|
||||
'available': available,
|
||||
'percent': percent,
|
||||
'used': used,
|
||||
'free': free,
|
||||
'active': active,
|
||||
'inactive': inactive,
|
||||
'buffers': buffers,
|
||||
'cached': cached,
|
||||
'shared': shared,
|
||||
},
|
||||
**kwargs
|
||||
)
|
||||
|
||||
|
||||
class SwapMemoryUsageResponse(MemoryResponse):
|
||||
def __init__(
|
||||
self,
|
||||
total: int,
|
||||
percent: float,
|
||||
used: int,
|
||||
free: int,
|
||||
sin: int,
|
||||
sout: int,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
super().__init__(
|
||||
*args,
|
||||
output={
|
||||
'total': total,
|
||||
'percent': percent,
|
||||
'used': used,
|
||||
'free': free,
|
||||
'sin': sin,
|
||||
'sout': sout,
|
||||
},
|
||||
**kwargs
|
||||
)
|
||||
|
||||
|
||||
class DiskPartitionResponse(DiskResponse):
|
||||
def __init__(
|
||||
self,
|
||||
|
|
|
@ -13,10 +13,10 @@ from platypush.entities.system import (
|
|||
CpuInfo as CpuInfoModel,
|
||||
CpuStats as CpuStatsModel,
|
||||
CpuTimes as CpuTimesModel,
|
||||
MemoryStats as MemoryStatsModel,
|
||||
SwapStats as SwapStatsModel,
|
||||
)
|
||||
from platypush.message.response.system import (
|
||||
VirtualMemoryUsageResponse,
|
||||
SwapMemoryUsageResponse,
|
||||
DiskResponseList,
|
||||
DiskPartitionResponse,
|
||||
DiskUsageResponse,
|
||||
|
@ -46,6 +46,10 @@ from platypush.schemas.system import (
|
|||
CpuStatsSchema,
|
||||
CpuTimes,
|
||||
CpuTimesSchema,
|
||||
MemoryStats,
|
||||
MemoryStatsSchema,
|
||||
SwapStats,
|
||||
SwapStatsSchema,
|
||||
SystemInfoSchema,
|
||||
)
|
||||
|
||||
|
@ -203,45 +207,35 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
|
||||
return psutil.getloadavg()
|
||||
|
||||
@action
|
||||
def mem_virtual(self) -> VirtualMemoryUsageResponse:
|
||||
"""
|
||||
Get the current virtual memory usage stats.
|
||||
:return: list of :class:`platypush.message.response.system.VirtualMemoryUsageResponse`
|
||||
"""
|
||||
def _mem_virtual(self) -> MemoryStats:
|
||||
import psutil
|
||||
|
||||
mem = psutil.virtual_memory()
|
||||
return VirtualMemoryUsageResponse(
|
||||
total=mem.total,
|
||||
available=mem.available,
|
||||
percent=mem.percent,
|
||||
used=mem.used,
|
||||
free=mem.free,
|
||||
active=mem.active,
|
||||
inactive=mem.inactive,
|
||||
buffers=mem.buffers,
|
||||
cached=mem.cached,
|
||||
shared=mem.shared,
|
||||
)
|
||||
return MemoryStatsSchema().load(
|
||||
psutil.virtual_memory()._asdict()
|
||||
) # type: ignore
|
||||
|
||||
@action
|
||||
def mem_swap(self) -> SwapMemoryUsageResponse:
|
||||
def mem_virtual(self) -> dict:
|
||||
"""
|
||||
Get the current virtual memory usage stats.
|
||||
:return: list of :class:`platypush.message.response.system.SwapMemoryUsageResponse`
|
||||
|
||||
:return: .. schema:: system.MemoryStatsSchema
|
||||
"""
|
||||
return MemoryStatsSchema().dump(self._mem_virtual()) # type: ignore
|
||||
|
||||
def _mem_swap(self) -> SwapStats:
|
||||
import psutil
|
||||
|
||||
mem = psutil.swap_memory()
|
||||
return SwapMemoryUsageResponse(
|
||||
total=mem.total,
|
||||
percent=mem.percent,
|
||||
used=mem.used,
|
||||
free=mem.free,
|
||||
sin=mem.sin,
|
||||
sout=mem.sout,
|
||||
)
|
||||
return SwapStatsSchema().load(psutil.swap_memory()._asdict()) # type: ignore
|
||||
|
||||
@action
|
||||
def mem_swap(self) -> dict:
|
||||
"""
|
||||
Get the current swap memory usage stats.
|
||||
|
||||
:return: .. schema:: system.SwapStatsSchema
|
||||
"""
|
||||
return SwapStatsSchema().dump(self._mem_swap()) # type: ignore
|
||||
|
||||
@action
|
||||
def disk_partitions(self) -> DiskResponseList:
|
||||
|
@ -793,11 +787,13 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
'cpu': {
|
||||
'frequency': self._cpu_frequency_avg(),
|
||||
'info': self._cpu_info,
|
||||
'load_avg': self.load_avg().output,
|
||||
'load_avg': self.load_avg().output, # type: ignore
|
||||
'stats': self._cpu_stats(),
|
||||
'times': self._cpu_times_avg(),
|
||||
'percent': self.cpu_percent().output / 100.0, # type: ignore
|
||||
},
|
||||
'memory': self._mem_virtual(),
|
||||
'swap': self._mem_swap(),
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -867,7 +863,17 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
value=cpu['percent'],
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
MemoryStatsModel(
|
||||
id='system:memory',
|
||||
name='Memory',
|
||||
**entities['memory'],
|
||||
),
|
||||
SwapStatsModel(
|
||||
id='system:swap',
|
||||
name='Swap',
|
||||
**entities['swap'],
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -9,6 +9,9 @@ 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={
|
||||
|
@ -33,6 +36,19 @@ class CpuInfoBaseSchema(DataClassSchema):
|
|||
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.
|
||||
|
@ -226,6 +242,120 @@ class CpuData:
|
|||
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 SystemInfo:
|
||||
"""
|
||||
|
@ -233,10 +363,14 @@ class SystemInfo:
|
|||
"""
|
||||
|
||||
cpu: CpuData
|
||||
memory: MemoryStats
|
||||
swap: SwapStats
|
||||
|
||||
|
||||
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)
|
||||
MemoryStatsSchema = class_schema(MemoryStats, base_schema=MemoryStatsBaseSchema)
|
||||
SwapStatsSchema = class_schema(SwapStats, base_schema=MemoryStatsBaseSchema)
|
||||
SystemInfoSchema = class_schema(SystemInfo, base_schema=DataClassSchema)
|
||||
|
|
Loading…
Reference in a new issue