Skip to content

Commit

Permalink
new: Add support for Atop 2.5 and streamline future version additions.
Browse files Browse the repository at this point in the history
  • Loading branch information
dfrtz authored Jan 14, 2024
1 parent 5098a3d commit b674ef8
Show file tree
Hide file tree
Showing 8 changed files with 303 additions and 47 deletions.
55 changes: 14 additions & 41 deletions pyatop/atop_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
- Always found directly after a SStat.
- Always found directly before the next Record if not the end of file.
- Contains statistics about every task/process on the system.
See https://github.com/Atoptool/atop for more information and references to the C process source code.
"""

from __future__ import annotations
Expand All @@ -31,48 +29,23 @@
from pyatop.structs import atop_1_26
from pyatop.structs import atop_2_3
from pyatop.structs import atop_2_4
from pyatop.structs import atop_2_5

Header = Union[
atop_1_26.Header,
atop_2_3.Header,
atop_2_4.Header,
]
Record = Union[
atop_1_26.Record,
atop_2_3.Record,
atop_2_4.Record,
]
SStat = Union[
atop_1_26.SStat,
atop_2_3.SStat,
atop_2_4.SStat,
]
TStat = Union[ # pylint: disable=invalid-name
atop_1_26.TStat,
atop_2_3.TStat,
atop_2_4.TStat,
_VERSIONS = [
atop_1_26,
atop_2_3,
atop_2_4,
atop_2_5,
]
Header = Union[tuple(module.Header for module in _VERSIONS)]
Record = Union[tuple(module.Record for module in _VERSIONS)]
SStat = Union[tuple(module.SStat for module in _VERSIONS)]
TStat = Union[tuple(module.TStat for module in _VERSIONS)] # pylint: disable=invalid-name
_HEADER_BY_VERSION: dict[str, type[Header]] = {module.Header.supported_version: module.Header for module in _VERSIONS}
_RECORD_BY_VERSION: dict[str, type[Record]] = {module.Header.supported_version: module.Record for module in _VERSIONS}
_SSTAT_BY_VERSION: dict[str, type[SStat]] = {module.Header.supported_version: module.SStat for module in _VERSIONS}
_TSTAT_BY_VERSION: dict[str, type[TStat]] = {module.Header.supported_version: module.TStat for module in _VERSIONS}

_HEADER_BY_VERSION: dict[str, type[Header]] = {
"1.26": atop_1_26.Header,
"2.3": atop_2_3.Header,
"2.4": atop_2_4.Header,
}
_RECORD_BY_VERSION: dict[str, type[Record]] = {
"1.26": atop_1_26.Record,
"2.3": atop_2_3.Record,
"2.4": atop_2_4.Record,
}
_SSTAT_BY_VERSION: dict[str, type[SStat]] = {
"1.26": atop_1_26.SStat,
"2.3": atop_2_3.SStat,
"2.4": atop_2_4.SStat,
}
_TSTAT_BY_VERSION: dict[str, type[TStat]] = {
"1.26": atop_1_26.TStat,
"2.3": atop_2_3.TStat,
"2.4": atop_2_4.TStat,
}
# Fallback to latest if there is no custom class provided to attempt backwards compatibility.
_DEFAULT_VERSION = list(_HEADER_BY_VERSION.keys())[-1]

Expand Down
3 changes: 2 additions & 1 deletion pyatop/structs/atop_1_26.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
Struct ordering matches the C source to help with comparisons.
See https://github.com/Atoptool/atop for more information and references to the C process source code.
See https://github.com/Atoptool/atop for more information and full details about each field.
Using schemas and structs from Atop 1.26.
"""

Expand Down Expand Up @@ -81,6 +81,7 @@ class Header(ctypes.Structure, HeaderMixin):
("ossub", ctypes.c_int),
("ifuture", ctypes.c_int * 6),
]
supported_version = "1.26"

def check_compatibility(self) -> None:
"""Verify if the loaded values are compatible with this header version.
Expand Down
5 changes: 3 additions & 2 deletions pyatop/structs/atop_2_3.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
Struct ordering matches the C source to help with comparisons.
If structs match exactly from a previous version, they are reused via aliasing.
See https://github.com/Atoptool/atop for more information and references to the C process source code.
Using schemas and structs from Atop 2.3.
See https://github.com/Atoptool/atop for more information and full details about each field.
Using schemas and structs from Atop 2.3.0.
"""

import ctypes
Expand Down Expand Up @@ -86,6 +86,7 @@ class Header(ctypes.Structure, HeaderMixin):
("ossub", ctypes.c_int),
("ifuture", ctypes.c_int * 6),
]
supported_version = "2.3"

def check_compatibility(self) -> None:
"""Verify if the loaded values are compatible with this header version.
Expand Down
6 changes: 4 additions & 2 deletions pyatop/structs/atop_2_4.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
Struct ordering matches the C source to help with comparisons.
If structs match exactly from a previous version, they are reused via aliasing.
See https://github.com/Atoptool/atop for more information and references to the C process source code.
Using schemas and structs from Atop 2.4.
See https://github.com/Atoptool/atop for more information and full details about each field.
Using schemas and structs from Atop 2.4.0.
"""

import ctypes
Expand Down Expand Up @@ -54,6 +54,8 @@
class Header(atop_2_3.Header):
"""Top level struct to describe information about the system running ATOP and the log file itself."""

supported_version = "2.4"

def check_compatibility(self) -> None:
"""Verify if the loaded values are compatible with this header version."""
sizes = [
Expand Down
185 changes: 185 additions & 0 deletions pyatop/structs/atop_2_5.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
"""Structs and definitions used serialize/deserialize Atop statistics directly from log files.
Structs are declared in a way that will help provide as close to a 1 to 1 match as possible for debuggability
and maintenance. The _fields_ of every struct match their original name, however the struct names have been updated
to match python CamelCase standards. Each struct includes the following to help identify the original source:
C Name: utsname
C Location: sys/utsname.h
Struct ordering matches the C source to help with comparisons.
If structs match exactly from a previous version, they are reused via aliasing.
See https://github.com/Atoptool/atop for more information and full details about each field.
Using schemas and structs from Atop 2.5.0.
"""

import ctypes

from pyatop.structs import atop_1_26
from pyatop.structs import atop_2_3
from pyatop.structs import atop_2_4

# Disable the following pylint warnings to allow the variables and classes to match the style from the C.
# This helps with maintainability and cross-referencing.
# pylint: disable=invalid-name,too-few-public-methods

# Definitions from atop.h
ACCTACTIVE = 0x00000001
IOSTAT = 0x00000004
NETATOP = 0x00000010
NETATOPD = 0x00000020
DOCKSTAT = 0x00000040
GPUSTAT = 0x00000080

# Definitions from photoproc.h
PNAMLEN = 15
CMDLEN = 255

# Definitions from photosyst.h
MAXCPU = 2048
MAXDSK = 1024
MAXLVM = 2048
MAXMDD = 256
MAXINTF = 128
MAXCONTAINER = 128
MAXNFSMOUNT = 64
MAXIBPORT = 32
MAXGPU = 32
MAXGPUBUS = 12
MAXGPUTYPE = 12
MAXDKNAM = 32
MAXIBNAME = 12


class Header(atop_2_3.Header):
"""Top level struct to describe information about the system running ATOP and the log file itself."""

supported_version = "2.5"

def check_compatibility(self) -> None:
"""Verify if the loaded values are compatible with this header version."""
sizes = [
(self.rawheadlen, ctypes.sizeof(Header)),
(self.rawreclen, ctypes.sizeof(Record)),
(self.sstatlen, ctypes.sizeof(SStat)),
(self.tstatlen, ctypes.sizeof(TStat)),
]
if any(size[0] != size[1] for size in sizes):
raise ValueError(f"File has incompatible Atop format. Struct length evaluations (found, expected): {sizes}")


Record = atop_2_3.Record


MemStat = atop_2_3.MemStat


FreqCnt = atop_1_26.FreqCnt


PerCPU = atop_2_3.PerCPU


CPUStat = atop_2_3.CPUStat


PerDSK = atop_2_3.PerDSK


DSKStat = atop_2_3.DSKStat


PerIntf = atop_2_3.PerIntf


IntfStat = atop_2_3.IntfStat


PerNFSMount = atop_2_3.PerNFSMount


Server = atop_2_3.Server


Client = atop_2_3.Client


NFSMounts = atop_2_3.NFSMounts


NFSStat = atop_2_3.NFSStat


PSI = atop_2_4.PSI


Pressure = atop_2_4.Pressure


PerContainer = atop_2_3.PerContainer


ContStat = atop_2_3.ContStat


WWWStat = atop_1_26.WWWStat


PerGPU = atop_2_4.PerGPU


GPUStat = atop_2_4.GPUStat


PerIFB = atop_2_4.PerIFB


IFBStat = atop_2_4.IFBStat


IPv4Stats = atop_1_26.IPv4Stats


ICMPv4Stats = atop_1_26.ICMPv4Stats


UDPv4Stats = atop_1_26.UDPv4Stats


TCPStats = atop_1_26.TCPStats


IPv6Stats = atop_1_26.IPv6Stats


ICMPv6Stats = atop_1_26.ICMPv6Stats


UDPv6Stats = atop_1_26.UDPv6Stats


NETStat = atop_1_26.NETStat


SStat = atop_2_4.SStat


GEN = atop_2_3.GEN


CPU = atop_1_26.CPU


DSK = atop_1_26.DSK


MEM = atop_2_3.MEM


NET = atop_2_3.NET


GPU = atop_2_4.GPU


TStat = atop_2_4.TStat
8 changes: 7 additions & 1 deletion pyatop/structs/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,13 @@


class HeaderMixin:
"""Shared logic for top level struct describing information contained in the log file."""
"""Shared logic for top level struct describing information contained in the log file.
Attributes:
supported_version: The version of Atop that this header is compatible with as <major.<minor>.
"""

supported_version = None

@property
def semantic_version(self) -> str:
Expand Down
Binary file added test/files/atop_2_5.log.gz
Binary file not shown.
Loading

0 comments on commit b674ef8

Please sign in to comment.