diff --git a/platypush/entities/system.py b/platypush/entities/system.py new file mode 100644 index 00000000..4484dec7 --- /dev/null +++ b/platypush/entities/system.py @@ -0,0 +1,36 @@ +from sqlalchemy import Column, ForeignKey, Integer, JSON, String + +from platypush.common.db import Base + +from . import Entity + + +if 'cpu_info' not in Base.metadata: + + class CpuInfo(Entity): + """ + ``CpuInfo`` ORM model. + """ + + __tablename__ = 'cpu_info' + + id = Column( + Integer, ForeignKey(Entity.id, ondelete='CASCADE'), primary_key=True + ) + + architecture = Column(String) + bits = Column(Integer) + cores = Column(Integer) + vendor = Column(String) + brand = Column(String) + frequency_advertised = Column(Integer) + frequency_actual = Column(Integer) + flags = Column(JSON) + l1_instruction_cache_size = Column(Integer) + l1_data_cache_size = Column(Integer) + l2_cache_size = Column(Integer) + l3_cache_size = Column(Integer) + + __mapper_args__ = { + 'polymorphic_identity': __tablename__, + } diff --git a/platypush/message/response/system/__init__.py b/platypush/message/response/system/__init__.py index 96c89616..26e26436 100644 --- a/platypush/message/response/system/__init__.py +++ b/platypush/message/response/system/__init__.py @@ -1,5 +1,5 @@ from datetime import datetime -from typing import Optional, List, Union +from typing import Optional, List from platypush.message.response import Response @@ -28,60 +28,25 @@ class SensorResponse(SystemResponse): pass -class CpuInfoResponse(CpuResponse): - def __init__(self, - arch: str, - bits: int, - count: int, - vendor_id: str, - brand: str, - hz_advertised: int, - hz_actual: int, - model: int, - flags: List[str], - family: Optional[int], - stepping: Optional[int], - l1_instruction_cache_size: Optional[Union[int, str]], - l1_data_cache_size: Optional[Union[int, str]], - l2_cache_size: Optional[Union[int, str]], - l3_cache_size: Optional[Union[int, str]], - *args, **kwargs): - super().__init__( - *args, output={ - 'arch': arch, - 'bits': bits, - 'count': count, - 'vendor_id': vendor_id, - 'brand': brand, - 'hz_advertised': hz_advertised, - 'hz_actual': hz_actual, - 'stepping': stepping, - 'model': model, - 'family': family, - 'flags': flags, - 'l1_instruction_cache_size': l1_instruction_cache_size, - 'l1_data_cache_size': l1_data_cache_size, - 'l2_cache_size': l2_cache_size, - 'l3_cache_size': l3_cache_size, - }, **kwargs - ) - - class CpuTimesResponse(CpuResponse): - def __init__(self, - user: float, - nice: float, - system: float, - idle: float, - iowait: float, - irq: float, - softirq: float, - steal: float, - guest: float, - guest_nice: float, - *args, **kwargs): + def __init__( + self, + user: float, + nice: float, + system: float, + idle: float, + iowait: float, + irq: float, + softirq: float, + steal: float, + guest: float, + guest_nice: float, + *args, + **kwargs + ): super().__init__( - *args, output={ + *args, + output={ 'user': user, 'nice': nice, 'system': system, @@ -92,58 +57,66 @@ class CpuTimesResponse(CpuResponse): 'steal': steal, 'guest': guest, 'guest_nice': guest_nice, - }, **kwargs + }, + **kwargs ) class CpuStatsResponse(CpuResponse): - def __init__(self, - ctx_switches: int, - interrupts: int, - soft_interrupts: int, - syscalls: int, - *args, **kwargs): + def __init__( + self, + ctx_switches: int, + interrupts: int, + soft_interrupts: int, + syscalls: int, + *args, + **kwargs + ): super().__init__( - *args, output={ + *args, + output={ 'ctx_switches': ctx_switches, 'interrupts': interrupts, 'soft_interrupts': soft_interrupts, 'syscalls': syscalls, - }, **kwargs + }, + **kwargs ) class CpuFrequencyResponse(CpuResponse): # noinspection PyShadowingBuiltins - def __init__(self, - min: int, - max: int, - current: int, - *args, **kwargs): + def __init__(self, min: int, max: int, current: int, *args, **kwargs): super().__init__( - *args, output={ + *args, + output={ 'min': min, 'max': max, 'current': current, - }, **kwargs + }, + **kwargs ) 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): + 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={ + *args, + output={ 'total': total, 'available': available, 'percent': percent, @@ -154,82 +127,102 @@ class VirtualMemoryUsageResponse(MemoryResponse): 'buffers': buffers, 'cached': cached, 'shared': shared, - }, **kwargs + }, + **kwargs ) class SwapMemoryUsageResponse(MemoryResponse): - def __init__(self, - total: int, - percent: float, - used: int, - free: int, - sin: int, - sout: int, - *args, **kwargs): + def __init__( + self, + total: int, + percent: float, + used: int, + free: int, + sin: int, + sout: int, + *args, + **kwargs + ): super().__init__( - *args, output={ + *args, + output={ 'total': total, 'percent': percent, 'used': used, 'free': free, 'sin': sin, 'sout': sout, - }, **kwargs + }, + **kwargs ) class DiskPartitionResponse(DiskResponse): - def __init__(self, - device: str, - mount_point: str, - fstype: Optional[str] = None, - opts: Optional[str] = None, - *args, **kwargs): + 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 + *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): + 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 + *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): + 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={ + *args, + output={ 'read_count': read_count, 'write_count': write_count, 'read_bytes': read_bytes, @@ -240,24 +233,29 @@ class DiskIoCountersResponse(DiskResponse): 'write_merged_count': write_merged_count, 'busy_time': busy_time, 'disk': disk, - }, **kwargs + }, + **kwargs ) 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): + 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={ + *args, + output={ 'bytes_sent': bytes_sent, 'bytes_recv': bytes_recv, 'packets_sent': packets_sent, @@ -267,25 +265,30 @@ class NetworkIoCountersResponse(NetworkResponse): 'dropin': dropin, 'dropout': dropout, 'nic': nic, - }, **kwargs + }, + **kwargs ) class NetworkConnectionResponse(NetworkResponse): # noinspection PyShadowingBuiltins - def __init__(self, - fd: int, - family: str, - type: str, - local_address: str, - local_port: int, - remote_address: str, - remote_port: int, - status: str, - pid: int, - *args, **kwargs): + def __init__( + self, + fd: int, + family: str, + type: str, + local_address: str, + local_port: int, + remote_address: str, + remote_port: int, + status: str, + pid: int, + *args, + **kwargs + ): super().__init__( - *args, output={ + *args, + output={ 'fd': fd, 'family': family, 'type': type, @@ -295,25 +298,30 @@ class NetworkConnectionResponse(NetworkResponse): 'remote_port': remote_port, 'status': status, 'pid': pid, - }, **kwargs + }, + **kwargs ) class NetworkAddressResponse(NetworkResponse): - def __init__(self, - nic: str, - ipv4_address: Optional[str] = None, - ipv4_netmask: Optional[str] = None, - ipv4_broadcast: Optional[str] = None, - ipv6_address: Optional[str] = None, - ipv6_netmask: Optional[str] = None, - ipv6_broadcast: Optional[str] = None, - mac_address: Optional[str] = None, - mac_broadcast: Optional[str] = None, - ptp: Optional[str] = None, - *args, **kwargs): + def __init__( + self, + nic: str, + ipv4_address: Optional[str] = None, + ipv4_netmask: Optional[str] = None, + ipv4_broadcast: Optional[str] = None, + ipv6_address: Optional[str] = None, + ipv6_netmask: Optional[str] = None, + ipv6_broadcast: Optional[str] = None, + mac_address: Optional[str] = None, + mac_broadcast: Optional[str] = None, + ptp: Optional[str] = None, + *args, + **kwargs + ): super().__init__( - *args, output={ + *args, + output={ 'nic': nic, 'ipv4_address': ipv4_address, 'ipv4_netmask': ipv4_netmask, @@ -324,123 +332,136 @@ class NetworkAddressResponse(NetworkResponse): 'mac_address': mac_address, 'mac_broadcast': mac_broadcast, 'ptp': ptp, - }, **kwargs + }, + **kwargs ) class NetworkInterfaceStatsResponse(NetworkResponse): - def __init__(self, - nic: str, - is_up: bool, - duplex: str, - speed: int, - mtu: int, - *args, **kwargs): + def __init__( + self, nic: str, is_up: bool, duplex: str, speed: int, mtu: int, *args, **kwargs + ): super().__init__( - *args, output={ + *args, + output={ 'nic': nic, 'is_up': is_up, 'duplex': duplex, 'speed': speed, 'mtu': mtu, - }, **kwargs + }, + **kwargs ) class SensorTemperatureResponse(SensorResponse): - def __init__(self, - name: str, - current: float, - high: Optional[float] = None, - critical: Optional[float] = None, - label: Optional[str] = None, - *args, **kwargs): + def __init__( + self, + name: str, + current: float, + high: Optional[float] = None, + critical: Optional[float] = None, + label: Optional[str] = None, + *args, + **kwargs + ): super().__init__( - *args, output={ + *args, + output={ 'name': name, 'current': current, 'high': high, 'critical': critical, 'label': label, - }, **kwargs + }, + **kwargs ) class SensorFanResponse(SensorResponse): - def __init__(self, - name: str, - current: int, - label: Optional[str] = None, - *args, **kwargs): + def __init__( + self, name: str, current: int, label: Optional[str] = None, *args, **kwargs + ): super().__init__( - *args, output={ + *args, + output={ 'name': name, 'current': current, 'label': label, - }, **kwargs + }, + **kwargs ) class SensorBatteryResponse(SensorResponse): - def __init__(self, - percent: float, - secs_left: int, - power_plugged: bool, - *args, **kwargs): + def __init__( + self, percent: float, secs_left: int, power_plugged: bool, *args, **kwargs + ): super().__init__( - *args, output={ + *args, + output={ 'percent': percent, 'secs_left': secs_left, 'power_plugged': power_plugged, - }, **kwargs + }, + **kwargs ) class ConnectUserResponse(SystemResponse): - def __init__(self, - name: str, - terminal: str, - host: str, - started: datetime, - pid: Optional[int] = None, - *args, **kwargs): + def __init__( + self, + name: str, + terminal: str, + host: str, + started: datetime, + pid: Optional[int] = None, + *args, + **kwargs + ): super().__init__( - *args, output={ + *args, + output={ 'name': name, 'terminal': terminal, 'host': host, 'started': started, 'pid': pid, - }, **kwargs + }, + **kwargs ) class ProcessResponse(SystemResponse): - def __init__(self, - pid: int, - name: str, - started: datetime, - ppid: Optional[int], - children: Optional[List[int]] = None, - exe: Optional[List[str]] = None, - status: Optional[str] = None, - username: Optional[str] = None, - terminal: Optional[str] = None, - cpu_user_time: Optional[float] = None, - cpu_system_time: Optional[float] = None, - cpu_children_user_time: Optional[float] = None, - cpu_children_system_time: Optional[float] = None, - mem_rss: Optional[int] = None, - mem_vms: Optional[int] = None, - mem_shared: Optional[int] = None, - mem_text: Optional[int] = None, - mem_data: Optional[int] = None, - mem_lib: Optional[int] = None, - mem_dirty: Optional[int] = None, - mem_percent: Optional[float] = None, - *args, **kwargs): + def __init__( + self, + pid: int, + name: str, + started: datetime, + ppid: Optional[int], + children: Optional[List[int]] = None, + exe: Optional[List[str]] = None, + status: Optional[str] = None, + username: Optional[str] = None, + terminal: Optional[str] = None, + cpu_user_time: Optional[float] = None, + cpu_system_time: Optional[float] = None, + cpu_children_user_time: Optional[float] = None, + cpu_children_system_time: Optional[float] = None, + mem_rss: Optional[int] = None, + mem_vms: Optional[int] = None, + mem_shared: Optional[int] = None, + mem_text: Optional[int] = None, + mem_data: Optional[int] = None, + mem_lib: Optional[int] = None, + mem_dirty: Optional[int] = None, + mem_percent: Optional[float] = None, + *args, + **kwargs + ): super().__init__( - *args, output={ + *args, + output={ 'pid': pid, 'name': name, 'started': started, @@ -462,7 +483,8 @@ class ProcessResponse(SystemResponse): 'mem_dirty': mem_dirty, 'mem_percent': mem_percent, 'children': children or [], - }, **kwargs + }, + **kwargs ) diff --git a/platypush/schemas/system.py b/platypush/schemas/system.py new file mode 100644 index 00000000..34668a92 --- /dev/null +++ b/platypush/schemas/system.py @@ -0,0 +1,154 @@ +from dataclasses import dataclass, field +from typing import List, Optional + +from marshmallow import pre_load +from marshmallow_dataclass import class_schema + +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 + + +@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 SystemInfo: + """ + Aggregate system info dataclass. + """ + + cpu_info: CpuInfo + + +CpuInfoSchema = class_schema(CpuInfo, base_schema=CpuInfoBaseSchema) +SystemInfoSchema = class_schema(SystemInfo, base_schema=DataClassSchema)