139 lines
4.4 KiB
Python
Executable File
139 lines
4.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Simple stat daemon
|
|
# Listens on a port for requests, responds with some basic information about the current state of the system.
|
|
|
|
import configparser
|
|
import os
|
|
import subprocess
|
|
import time
|
|
import argparse
|
|
import json
|
|
import http.server
|
|
import socketserver
|
|
|
|
def get_default_config():
|
|
config = configparser.ConfigParser(interpolation=None)
|
|
config['stat.system.uptime'] = {
|
|
'command': 'uptime -p | cut -d" " -f2-',
|
|
'format': '{}'
|
|
}
|
|
config['stat.cpu.temp'] = {
|
|
'command': 'sensors -j | jq \'.["k10temp-pci-00c3"]["Tctl"]["temp1_input"]\'',
|
|
'transform': 'lambda x: float(x)',
|
|
'format': '{:.1f}',
|
|
'unit': '°C',
|
|
'historical': '1'
|
|
}
|
|
config['stat.cpu.act'] = {
|
|
'command': 'top -bn1 | grep "Cpu(s)" | sed "s/.*, *\\([0-9.]*\\)%%* id.*/\\1/" | awk \'{print 100-$1}\'',
|
|
'transform': 'lambda x: float(x)',
|
|
'format': '{:.1f}',
|
|
'unit': '%',
|
|
'historical': '1'
|
|
}
|
|
config['stat.memory.pct'] = {
|
|
'command': 'free | awk \'NR==2 {printf "%.1f", $3*100/$2}\'',
|
|
'transform': 'lambda x: float(x)',
|
|
'format': '{:.1f}',
|
|
'unit': '%',
|
|
'historical': '1'
|
|
}
|
|
config['stat.memory.total'] = {
|
|
'command': 'free -m | awk \'NR==2 {print $2}\'',
|
|
'transform': 'lambda x: int(x)',
|
|
'format': '{}',
|
|
'unit': 'MB'
|
|
}
|
|
config['stat.disk.root'] = {
|
|
'command': 'df -h / | awk \'NR==2 {printf "%s/%s [%d%%]", $3, $2, $5}\'',
|
|
'format': '{}'
|
|
}
|
|
return config
|
|
|
|
def get_config():
|
|
config_home = os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config"))
|
|
config_dir = os.path.join(config_home, "stats")
|
|
if not os.path.exists(config_dir):
|
|
os.makedirs(config_dir)
|
|
config_file = os.path.join(config_dir, "config.ini")
|
|
|
|
config = configparser.ConfigParser(interpolation=None)
|
|
|
|
if os.path.exists(config_file):
|
|
config.read(config_file)
|
|
else:
|
|
config = get_default_config()
|
|
with open(config_file, 'w') as f:
|
|
config.write(f)
|
|
return config
|
|
|
|
def get_stats(config):
|
|
stats = {}
|
|
stat_sections = [s for s in config.sections() if s.startswith('stat.')]
|
|
for section in stat_sections:
|
|
# Split into category and name
|
|
_, category, name = section.split('.')
|
|
stat_config = config[section]
|
|
if category not in stats:
|
|
stats[category] = {}
|
|
|
|
try:
|
|
value = subprocess.getoutput(stat_config['command']).strip()
|
|
|
|
transform = stat_config.get('transform', None)
|
|
format = stat_config.get('format', None)
|
|
unit = stat_config.get('unit', None)
|
|
historical = stat_config.get('historical', False)
|
|
if transform:
|
|
transform_func = eval(transform)
|
|
value = transform_func(value)
|
|
if format:
|
|
value = format.format(value)
|
|
data = {
|
|
'value': value
|
|
}
|
|
if unit:
|
|
data['unit'] = unit
|
|
if historical:
|
|
data['track'] = bool(historical)
|
|
stats[category][name] = data
|
|
|
|
except Exception as e:
|
|
print(e)
|
|
#stats[category][name] = f"Error: {str(e)}"
|
|
return stats
|
|
|
|
class StatsHandler(http.server.SimpleHTTPRequestHandler):
|
|
def do_GET(self):
|
|
data = {
|
|
'timestamp': time.time(),
|
|
'stats': get_stats(get_config())
|
|
}
|
|
response = json.dumps(data).encode('utf-8')
|
|
|
|
self.send_response(200)
|
|
self.send_header('Content-Type', 'application/json')
|
|
self.send_header('Content-Length', len(response))
|
|
self.end_headers()
|
|
self.wfile.write(response)
|
|
|
|
class StatsServer(socketserver.TCPServer):
|
|
allow_reuse_address = True
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description='Stats HTTP Server')
|
|
parser.add_argument('--port', type=int, default=5747, help='Port to listen on')
|
|
parser.add_argument('--host', default='0.0.0.0', help='Host to bind to')
|
|
args = parser.parse_args()
|
|
|
|
with StatsServer((args.host, args.port), StatsHandler) as httpd:
|
|
print(f"Serving stats on http://{args.host}:{args.port}")
|
|
try:
|
|
httpd.serve_forever()
|
|
except KeyboardInterrupt:
|
|
print("\nShutting down server...")
|
|
httpd.server_close()
|
|
|
|
if __name__ == '__main__':
|
|
main()
|