Skip to content

Commit ed985a0

Browse files
committed
[core] local farm : Start creating local farm & submitter
1 parent a6c345f commit ed985a0

File tree

5 files changed

+1437
-0
lines changed

5 files changed

+1437
-0
lines changed
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
#!/usr/bin/env python
2+
3+
import os
4+
import shutil
5+
import sys
6+
import time
7+
import signal
8+
import argparse
9+
from pathlib import Path
10+
import subprocess
11+
from collections import defaultdict
12+
13+
from localFarm import LocalFarmEngine
14+
15+
16+
class FarmLauncher:
17+
def __init__(self, root=None):
18+
self.root = Path(root or Path.home() / ".local_farm")
19+
self.pidFile = self.root / "farm.pid"
20+
self.logFile = self.root / "backend.log"
21+
22+
def clean(self):
23+
"""Clean farm backend files"""
24+
if self.logFile.exists():
25+
self.logFile.unlink()
26+
if (self.root / "jobs").exists():
27+
shutil.rmtree(str((self.root / "jobs")))
28+
29+
def start(self):
30+
"""Start the farm backend"""
31+
if self.is_running():
32+
print("Farm backend is already running")
33+
return
34+
self.clean()
35+
36+
print("Starting farm backend...")
37+
# Get path to backend script
38+
backendScript = Path(__file__).parent / "localFarmBackend.py"
39+
# Start backend as daemon
40+
with open(self.logFile, 'a') as log:
41+
subprocess.Popen(
42+
[sys.executable, str(backendScript), str(self.root)],
43+
stdout=log,
44+
stderr=log,
45+
# stderr=subprocess.PIPE,
46+
start_new_session=True
47+
)
48+
49+
# Wait for it to start
50+
for _ in range(10):
51+
time.sleep(0.5)
52+
if self.is_running():
53+
print(f"Farm backend started (PID: {self.getFarmPid()})")
54+
print(f"Logs: {self.logFile}")
55+
return
56+
57+
print("Failed to start farm backend")
58+
sys.exit(1)
59+
60+
def stop(self):
61+
"""Stop the farm backend"""
62+
if not self.is_running():
63+
print("Farm backend is not running")
64+
return
65+
66+
pid = self.getFarmPid()
67+
print(f"Stopping farm backend (PID: {pid})...")
68+
69+
try:
70+
os.kill(pid, signal.SIGTERM)
71+
72+
# Wait for it to stop
73+
for _ in range(10):
74+
time.sleep(0.5)
75+
if not self.is_running():
76+
print("Farm backend stopped")
77+
return
78+
79+
# Force kill if still running
80+
print("Force killing farm backend...")
81+
os.kill(pid, signal.SIGKILL)
82+
83+
except ProcessLookupError:
84+
print("Backend process not found")
85+
self.pidFile.unlink(missing_ok=True)
86+
87+
def restart(self):
88+
"""Restart the farm backend"""
89+
self.stop()
90+
time.sleep(1)
91+
self.start()
92+
93+
def status(self, allInfos=False):
94+
"""Show status of the farm backend"""
95+
if self.is_running():
96+
pid = self.getFarmPid()
97+
print(f"Farm backend is running (PID: {pid})")
98+
99+
# Try to get job list
100+
try:
101+
client = LocalFarmEngine(root=self.root)
102+
response = client.list_jobs()
103+
jobs = response.get('jobs', {})
104+
print(f"Active jobs: {len(jobs)}")
105+
for jid, job in jobs.items():
106+
taskByStatus = defaultdict(set)
107+
for task in job['tasks']:
108+
status = task.get("status", "UNKNOWN")
109+
taskByStatus[status].add(task.get("tid"))
110+
print(f" - {jid}: {job['status']} ({len(job['tasks'])} tasks) -> {dict(taskByStatus)}")
111+
if allInfos:
112+
for task in job['tasks']:
113+
print(f" * Task {task['tid']}: {task}")
114+
print("")
115+
except Exception as e:
116+
print(f"Could not get job list: {e}")
117+
else:
118+
print("Farm backend is not running")
119+
120+
def is_running(self):
121+
"""Check if backend is running"""
122+
pid = self.getFarmPid()
123+
print(f"Check if {pid} is running")
124+
if pid is None:
125+
return False
126+
try:
127+
os.kill(pid, 0)
128+
return True
129+
except ProcessLookupError:
130+
return False
131+
132+
def getFarmPid(self):
133+
"""Get PID of running backend"""
134+
if not self.pidFile.exists():
135+
return None
136+
try:
137+
return int(self.pidFile.read_text())
138+
except:
139+
return None
140+
141+
142+
def main(root, command):
143+
launcher = FarmLauncher(root=root)
144+
if command == 'start':
145+
return launcher.start()
146+
elif command == 'stop':
147+
return launcher.stop()
148+
elif command == 'restart':
149+
return launcher.restart()
150+
elif command == 'status':
151+
return launcher.status()
152+
elif command == 'fullInfos':
153+
return launcher.status(allInfos=True)
154+
155+
156+
if __name__ == "__main__":
157+
parser = argparse.ArgumentParser(description='Local Farm Launcher')
158+
parser.add_argument('root', help='Farm directory path')
159+
parser.add_argument('command', choices=['start', 'stop', 'restart', 'status', 'fullInfos'], help='Command to execute')
160+
args = parser.parse_args()
161+
main(args.root, args.command)

0 commit comments

Comments
 (0)