ScottzillaSystems commited on
Commit
07846a9
·
verified ·
1 Parent(s): 5c15f3b

Upload healer_service.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. healer_service.py +164 -0
healer_service.py ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Autonomous Self-Healing Service — Background daemon
4
+ Runs healer.py as a persistent service with logging and alerting.
5
+
6
+ Usage:
7
+ python healer_service.py start # Start daemon
8
+ python healer_service.py stop # Stop daemon
9
+ python healer_service.py status # Check status
10
+ python healer_service.py logs # View logs
11
+ """
12
+
13
+ import os
14
+ import sys
15
+ import time
16
+ import json
17
+ import signal
18
+ import subprocess
19
+ import argparse
20
+ from datetime import datetime
21
+
22
+ PID_FILE = "/tmp/healer_daemon.pid"
23
+ LOG_FILE = "/tmp/healer_daemon.log"
24
+ REPORT_FILE = "/tmp/healer_last_report.json"
25
+
26
+
27
+ def start_daemon():
28
+ """Start the healer as a background process."""
29
+ if os.path.exists(PID_FILE):
30
+ with open(PID_FILE) as f:
31
+ old_pid = f.read().strip()
32
+ if os.path.exists(f"/proc/{old_pid}"):
33
+ print(f"[Service] Healer already running (PID {old_pid})")
34
+ return
35
+ else:
36
+ os.remove(PID_FILE)
37
+
38
+ # Fork to background
39
+ pid = os.fork()
40
+ if pid > 0:
41
+ print(f"[Service] Healer daemon started (PID {pid})")
42
+ with open(PID_FILE, 'w') as f:
43
+ f.write(str(pid))
44
+ return
45
+
46
+ # Child process
47
+ os.setsid()
48
+ sys.stdout = open(LOG_FILE, 'a')
49
+ sys.stderr = open(LOG_FILE, 'a')
50
+
51
+ print(f"\n{'='*60}")
52
+ print(f"[Service] Daemon started at {datetime.utcnow().isoformat()}")
53
+ print(f"{'='*60}\n")
54
+
55
+ # Import and run healer
56
+ from healer import SpaceHealer, HEALER_CONFIG
57
+
58
+ healer = SpaceHealer()
59
+ spaces = healer.discover_spaces()
60
+
61
+ print(f"[Service] Monitoring {len(spaces)} spaces")
62
+ print(f"[Service] Poll interval: {HEALER_CONFIG['poll_interval_seconds']}s")
63
+
64
+ def handle_signal(signum, frame):
65
+ print(f"[Service] Received signal {signum}, shutting down...")
66
+ sys.exit(0)
67
+
68
+ signal.signal(signal.SIGTERM, handle_signal)
69
+ signal.signal(signal.SIGINT, handle_signal)
70
+
71
+ while True:
72
+ try:
73
+ healer.run_cycle(spaces)
74
+ healer.print_report()
75
+
76
+ # Save report for external access
77
+ report = healer.generate_report()
78
+ with open(REPORT_FILE, 'w') as f:
79
+ json.dump(report, f, indent=2)
80
+
81
+ except Exception as e:
82
+ print(f"[Service] ❌ Cycle error: {e}")
83
+
84
+ time.sleep(HEALER_CONFIG["poll_interval_seconds"])
85
+
86
+
87
+ def stop_daemon():
88
+ """Stop the healer daemon."""
89
+ if not os.path.exists(PID_FILE):
90
+ print("[Service] Healer not running")
91
+ return
92
+
93
+ with open(PID_FILE) as f:
94
+ pid = f.read().strip()
95
+
96
+ try:
97
+ os.kill(int(pid), signal.SIGTERM)
98
+ os.remove(PID_FILE)
99
+ print(f"[Service] Healer stopped (PID {pid})")
100
+ except ProcessLookupError:
101
+ os.remove(PID_FILE)
102
+ print("[Service] Healer was not running (stale PID file)")
103
+ except Exception as e:
104
+ print(f"[Service] Error stopping: {e}")
105
+
106
+
107
+ def check_status():
108
+ """Check if daemon is running."""
109
+ if not os.path.exists(PID_FILE):
110
+ print("[Service] Healer: STOPPED")
111
+ return
112
+
113
+ with open(PID_FILE) as f:
114
+ pid = f.read().strip()
115
+
116
+ if os.path.exists(f"/proc/{pid}"):
117
+ print(f"[Service] Healer: RUNNING (PID {pid})")
118
+ if os.path.exists(REPORT_FILE):
119
+ with open(REPORT_FILE) as f:
120
+ report = json.load(f)
121
+ print(f" Last check: {report.get('generated_at', 'unknown')}")
122
+ print(f" Spaces monitored: {len(report.get('spaces', []))}")
123
+ print(f" Actions today: {report.get('actions_today', 0)}")
124
+ print(f" Est. daily cost: ${report.get('total_estimated_daily_cost', 0):.2f}")
125
+ else:
126
+ print(f"[Service] Healer: STOPPED (stale PID {pid})")
127
+ os.remove(PID_FILE)
128
+
129
+
130
+ def view_logs(lines: int = 50):
131
+ """View recent daemon logs."""
132
+ if not os.path.exists(LOG_FILE):
133
+ print("[Service] No logs found")
134
+ return
135
+
136
+ with open(LOG_FILE) as f:
137
+ all_lines = f.readlines()
138
+
139
+ for line in all_lines[-lines:]:
140
+ print(line.rstrip())
141
+
142
+
143
+ def main():
144
+ parser = argparse.ArgumentParser(description="Healer Service Manager")
145
+ parser.add_argument("command", choices=["start", "stop", "status", "logs", "restart"])
146
+ parser.add_argument("--lines", type=int, default=50, help="Lines of log to show")
147
+ args = parser.parse_args()
148
+
149
+ if args.command == "start":
150
+ start_daemon()
151
+ elif args.command == "stop":
152
+ stop_daemon()
153
+ elif args.command == "restart":
154
+ stop_daemon()
155
+ time.sleep(1)
156
+ start_daemon()
157
+ elif args.command == "status":
158
+ check_status()
159
+ elif args.command == "logs":
160
+ view_logs(args.lines)
161
+
162
+
163
+ if __name__ == "__main__":
164
+ main()