diff --git a/collectors/zfs_pools.py b/collectors/zfs_pools.py new file mode 100644 index 0000000..b4f55cf --- /dev/null +++ b/collectors/zfs_pools.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python3 + +import subprocess +from typing import Dict, Any, List +import json + +def get_zfs_pools() -> List[Dict[str, Any]]: + """Get information about ZFS pools.""" + try: + # Get list of pools + pools = subprocess.check_output(['zpool', 'list', '-H', '-o', 'name,size,alloc,free,health,readonly,dedup,altroot']).decode().strip().split('\n') + + pool_info = [] + for pool in pools: + if not pool: # Skip empty lines + continue + + name, size, alloc, free, health, readonly, dedup, altroot = pool.split('\t') + + # Get detailed pool status + status = subprocess.check_output(['zpool', 'status', name]).decode() + + # Get pool properties + properties = subprocess.check_output(['zpool', 'get', 'all', name]).decode() + + pool_info.append({ + 'name': name, + 'size': size, + 'allocated': alloc, + 'free': free, + 'health': health, + 'readonly': readonly == 'on', + 'dedup': dedup, + 'altroot': altroot, + 'status': status, + 'properties': properties + }) + + return pool_info + except subprocess.SubprocessError as e: + print(f"Error getting ZFS pool information: {e}") + return [] + +def convert_size_to_bytes(size_str: str) -> int: + """Convert ZFS size string to bytes.""" + units = { + 'B': 1, + 'K': 1024, + 'M': 1024**2, + 'G': 1024**3, + 'T': 1024**4, + 'P': 1024**5 + } + + try: + number = float(size_str[:-1]) + unit = size_str[-1].upper() + return int(number * units[unit]) + except (ValueError, KeyError): + return 0 + +def collect_metrics() -> Dict[str, Any]: + """Collect ZFS pool metrics.""" + metrics = { + "entities": [] + } + + pools = get_zfs_pools() + + for pool in pools: + # Pool health status + metrics['entities'].append({ + 'sensor_id': f'zfs_pool_{pool["name"]}_health', + 'name': f'ZFS Pool {pool["name"]} Health', + 'value': pool['health'], + 'state_class': 'measurement', + 'unit_of_measurement': '', + 'device_class': 'enum', + 'icon': 'mdi:database', + 'attributes': { + 'friendly_name': f'ZFS Pool {pool["name"]} Health Status', + 'readonly': pool['readonly'], + 'dedup': pool['dedup'], + 'altroot': pool['altroot'] + } + }) + + # Pool size + size_bytes = convert_size_to_bytes(pool['size']) + metrics['entities'].append({ + 'sensor_id': f'zfs_pool_{pool["name"]}_size', + 'name': f'ZFS Pool {pool["name"]} Size', + 'value': str(round(size_bytes / (1024**3), 2)), # Convert to GB + 'state_class': 'measurement', + 'unit_of_measurement': 'GB', + 'device_class': 'data_size', + 'icon': 'mdi:database', + 'attributes': { + 'friendly_name': f'ZFS Pool {pool["name"]} Total Size' + } + }) + + # Pool allocated space + alloc_bytes = convert_size_to_bytes(pool['allocated']) + metrics['entities'].append({ + 'sensor_id': f'zfs_pool_{pool["name"]}_allocated', + 'name': f'ZFS Pool {pool["name"]} Allocated', + 'value': str(round(alloc_bytes / (1024**3), 2)), # Convert to GB + 'state_class': 'measurement', + 'unit_of_measurement': 'GB', + 'device_class': 'data_size', + 'icon': 'mdi:database', + 'attributes': { + 'friendly_name': f'ZFS Pool {pool["name"]} Allocated Space' + } + }) + + # Pool free space + free_bytes = convert_size_to_bytes(pool['free']) + metrics['entities'].append({ + 'sensor_id': f'zfs_pool_{pool["name"]}_free', + 'name': f'ZFS Pool {pool["name"]} Free', + 'value': str(round(free_bytes / (1024**3), 2)), # Convert to GB + 'state_class': 'measurement', + 'unit_of_measurement': 'GB', + 'device_class': 'data_size', + 'icon': 'mdi:database', + 'attributes': { + 'friendly_name': f'ZFS Pool {pool["name"]} Free Space' + } + }) + + # Pool usage percentage + usage_percent = (alloc_bytes / size_bytes * 100) if size_bytes > 0 else 0 + metrics['entities'].append({ + 'sensor_id': f'zfs_pool_{pool["name"]}_usage', + 'name': f'ZFS Pool {pool["name"]} Usage', + 'value': str(round(usage_percent, 1)), + 'state_class': 'measurement', + 'unit_of_measurement': '%', + 'device_class': 'power_factor', + 'icon': 'mdi:database', + 'attributes': { + 'friendly_name': f'ZFS Pool {pool["name"]} Usage Percentage' + } + }) + + return metrics + +if __name__ == "__main__": + # Example usage + metrics = collect_metrics() + print(json.dumps(metrics, indent=2)) \ No newline at end of file