adds stats daemon
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user