Skip to content

Base Classes

This section documents the base classes that provide core functionality for all storage devices.

Device Base Classes

sts.base

Base device classes.

This module provides base classes for all device types: - Device: Base class for all devices - NetworkDevice: Network-capable devices - StorageDevice: Storage devices

Device dataclass

Base class for all devices.

This class provides common functionality for all device types: - Device identification - Path handling - Basic operations - udev synchronization

Parameters:

Name Type Description Default
path Path | str | None

Device path (optional, e.g. '/dev/sda', '/sys/class/net/eth0')

None

Raises:

Type Description
DeviceNotFoundError

If device does not exist

DeviceError

If device cannot be accessed

Example
device = Device('/dev/sda')
Source code in sts_libs/src/sts/base.py
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
@dataclass
class Device:
    """Base class for all devices.

    This class provides common functionality for all device types:
    - Device identification
    - Path handling
    - Basic operations
    - udev synchronization

    Args:
        path: Device path (optional, e.g. '/dev/sda', '/sys/class/net/eth0')

    Raises:
        DeviceNotFoundError: If device does not exist
        DeviceError: If device cannot be accessed

    Example:
        ```python
        device = Device('/dev/sda')
        ```
    """

    path: Path | str | None = None

    # Standard Linux device paths
    DEV_PATH: ClassVar[Path] = Path('/dev')  # Device nodes (e.g. /dev/sda)
    SYS_PATH: ClassVar[Path] = Path('/sys')  # Sysfs device info (e.g. /sys/block/sda)

    def __post_init__(self) -> None:
        """Initialize device.

        Validates device exists and can be accessed.

        Raises:
            DeviceNotFoundError: If device does not exist
            DeviceError: If device cannot be accessed
        """
        # Convert path to Path if provided
        if self.path:
            self.path = Path(self.path)
            if not self.path.exists():
                raise DeviceNotFoundError(f'Device not found: {self.path}')

    def wait_udev(self, timeout: int = UDEV_SETTLE_TIMEOUT) -> bool:
        """Wait for udev to settle.

        This method waits for udev to finish processing device events.
        This is useful after device creation or modification to ensure
        all device nodes and symlinks are created.

        Args:
            timeout: Maximum time to wait in seconds

        Returns:
            True if udev settled, False if timeout reached

        Example:
            ```python
            device = Device('/dev/sda')
            device.wait_udev()
            True
            ```
        """
        # First try udevadm settle - this is faster but may fail if udev is busy
        result = run('udevadm settle')
        if result.succeeded:
            return True

        # If settle failed, poll for device existence
        # This is slower but more reliable, especially for slow devices
        if not self.path:
            return False

        start_time = time.time()
        while time.time() - start_time < timeout:
            if Path(self.path).exists():
                return True
            time.sleep(0.1)  # Small sleep to avoid busy waiting

        logging.warning(f'Timeout waiting for udev to settle on {self.path}')
        return False

    def __str__(self) -> str:
        """Return string representation of device."""
        return f'{self.__class__.__name__}({self.path or "unknown"})'

__post_init__()

Initialize device.

Validates device exists and can be accessed.

Raises:

Type Description
DeviceNotFoundError

If device does not exist

DeviceError

If device cannot be accessed

Source code in sts_libs/src/sts/base.py
56
57
58
59
60
61
62
63
64
65
66
67
68
69
def __post_init__(self) -> None:
    """Initialize device.

    Validates device exists and can be accessed.

    Raises:
        DeviceNotFoundError: If device does not exist
        DeviceError: If device cannot be accessed
    """
    # Convert path to Path if provided
    if self.path:
        self.path = Path(self.path)
        if not self.path.exists():
            raise DeviceNotFoundError(f'Device not found: {self.path}')

__str__()

Return string representation of device.

Source code in sts_libs/src/sts/base.py
110
111
112
def __str__(self) -> str:
    """Return string representation of device."""
    return f'{self.__class__.__name__}({self.path or "unknown"})'

wait_udev(timeout=UDEV_SETTLE_TIMEOUT)

Wait for udev to settle.

This method waits for udev to finish processing device events. This is useful after device creation or modification to ensure all device nodes and symlinks are created.

Parameters:

Name Type Description Default
timeout int

Maximum time to wait in seconds

UDEV_SETTLE_TIMEOUT

Returns:

Type Description
bool

True if udev settled, False if timeout reached

Example
device = Device('/dev/sda')
device.wait_udev()
True
Source code in sts_libs/src/sts/base.py
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
def wait_udev(self, timeout: int = UDEV_SETTLE_TIMEOUT) -> bool:
    """Wait for udev to settle.

    This method waits for udev to finish processing device events.
    This is useful after device creation or modification to ensure
    all device nodes and symlinks are created.

    Args:
        timeout: Maximum time to wait in seconds

    Returns:
        True if udev settled, False if timeout reached

    Example:
        ```python
        device = Device('/dev/sda')
        device.wait_udev()
        True
        ```
    """
    # First try udevadm settle - this is faster but may fail if udev is busy
    result = run('udevadm settle')
    if result.succeeded:
        return True

    # If settle failed, poll for device existence
    # This is slower but more reliable, especially for slow devices
    if not self.path:
        return False

    start_time = time.time()
    while time.time() - start_time < timeout:
        if Path(self.path).exists():
            return True
        time.sleep(0.1)  # Small sleep to avoid busy waiting

    logging.warning(f'Timeout waiting for udev to settle on {self.path}')
    return False

NetworkDevice dataclass

Bases: Device

Network-capable device.

This class provides functionality for network-capable devices: - IP address management - Port management - Network operations

Parameters:

Name Type Description Default
path Path | str | None

Device path (optional, e.g. '/sys/class/net/eth0')

None
ip str | None

IP address (optional)

None
port int | None

Port number (optional)

None
Example
device = NetworkDevice('/sys/class/net/eth0', '192.168.1.1', 80)
Source code in sts_libs/src/sts/base.py
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
@dataclass
class NetworkDevice(Device):
    """Network-capable device.

    This class provides functionality for network-capable devices:
    - IP address management
    - Port management
    - Network operations

    Args:
        path: Device path (optional, e.g. '/sys/class/net/eth0')
        ip: IP address (optional)
        port: Port number (optional)

    Example:
        ```python
        device = NetworkDevice('/sys/class/net/eth0', '192.168.1.1', 80)
        ```
    """

    ip: str | None = None
    port: int | None = field(default=None)

    # Network devices are managed through sysfs
    NET_PATH: ClassVar[Path] = Path('/sys/class/net')

StorageDevice dataclass

Bases: Device

Storage device.

This class provides functionality for storage devices: - Size information - Model information - Storage operations

Parameters:

Name Type Description Default
path Path | str | None

Device path (optional, e.g. '/dev/sda')

None
size int | None

Device size in bytes (optional)

None
model str | None

Device model (optional)

None
Example
device = StorageDevice('/dev/sda', 1000000000000, 'Samsung SSD 970 EVO')
Source code in sts_libs/src/sts/base.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
@dataclass
class StorageDevice(Device):
    """Storage device.

    This class provides functionality for storage devices:
    - Size information
    - Model information
    - Storage operations

    Args:
        path: Device path (optional, e.g. '/dev/sda')
        size: Device size in bytes (optional)
        model: Device model (optional)

    Example:
        ```python
        device = StorageDevice('/dev/sda', 1000000000000, 'Samsung SSD 970 EVO')
        ```
    """

    validate_on_init: bool = field(init=False, default=True)
    size: int | None = None
    model: str | None = None

    # Block devices are managed through sysfs block subsystem
    BLOCK_PATH: ClassVar[Path] = Path('/sys/block')

    def __post_init__(self) -> None:
        """Initialize storage device.

        Validates size is positive if provided.

        Raises:
            DeviceNotFoundError: If device does not exist
            DeviceError: If device cannot be accessed
            ValueError: If size is invalid
        """
        # Basic size validation - negative sizes are impossible
        if self.size is not None and self.size < 0:
            msg = f'Invalid size: {self.size}'
            raise ValueError(msg)

        if self.validate_on_init:
            self.validate_device_exists()

    def validate_device_exists(self) -> None:
        """Validate that the device exists on the system."""
        if self.path and not Path(self.path).exists():
            raise DeviceNotFoundError(f'Device not found: {self.path}')

    @property
    def size_human(self) -> str:
        """Get human-readable size.

        Returns:
            Size string (e.g. '1.0 TB') or 'Unknown' if size is not available

        Example:
            ```python
            device = StorageDevice('/dev/sda', 1000000000000)
            device.size_human
            '1.0 TB'
            ```
        """
        if self.size is None:
            return 'Unknown'

        # Convert bytes to human readable format using binary prefixes
        # (1024-based: KiB, MiB, etc. but displayed as KB, MB for simplicity)
        size = float(self.size)
        for unit in ('B', 'KB', 'MB', 'GB', 'TB', 'PB'):
            if size < BYTES_PER_UNIT:
                return f'{size:.1f} {unit}'
            size /= BYTES_PER_UNIT
        return f'{size:.1f} EB'

size_human: str property

Get human-readable size.

Returns:

Type Description
str

Size string (e.g. '1.0 TB') or 'Unknown' if size is not available

Example
device = StorageDevice('/dev/sda', 1000000000000)
device.size_human
'1.0 TB'

__post_init__()

Initialize storage device.

Validates size is positive if provided.

Raises:

Type Description
DeviceNotFoundError

If device does not exist

DeviceError

If device cannot be accessed

ValueError

If size is invalid

Source code in sts_libs/src/sts/base.py
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
def __post_init__(self) -> None:
    """Initialize storage device.

    Validates size is positive if provided.

    Raises:
        DeviceNotFoundError: If device does not exist
        DeviceError: If device cannot be accessed
        ValueError: If size is invalid
    """
    # Basic size validation - negative sizes are impossible
    if self.size is not None and self.size < 0:
        msg = f'Invalid size: {self.size}'
        raise ValueError(msg)

    if self.validate_on_init:
        self.validate_device_exists()

validate_device_exists()

Validate that the device exists on the system.

Source code in sts_libs/src/sts/base.py
187
188
189
190
def validate_device_exists(self) -> None:
    """Validate that the device exists on the system."""
    if self.path and not Path(self.path).exists():
        raise DeviceNotFoundError(f'Device not found: {self.path}')