Skip to content

Commit a4c5727

Browse files
committed
Initial setup of the project
1 parent 6075859 commit a4c5727

134 files changed

Lines changed: 13622 additions & 3118 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,27 @@
1-
node_modules
2-
# Keep environment variables out of version control
3-
.env
4-
.env.local
5-
.env.development
6-
.env.test
7-
8-
# Ignore the .env file that will be created for you
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
6+
# next.js
7+
/.next/
8+
/out/
9+
10+
# production
11+
/build
12+
13+
# debug
14+
npm-debug.log*
15+
yarn-debug.log*
16+
yarn-error.log*
17+
.pnpm-debug.log*
18+
19+
# env files
20+
.env*
21+
22+
# vercel
23+
.vercel
24+
25+
# typescript
26+
*.tsbuildinfo
27+
next-env.d.ts

README.md

Whitespace-only changes.

agent/agent.py

Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Simple Task Scheduler Agent
4+
A lightweight agent that receives commands from the scheduler and executes scripts locally.
5+
"""
6+
7+
import os
8+
import sys
9+
import json
10+
import time
11+
import platform
12+
import subprocess
13+
import tempfile
14+
import threading
15+
from datetime import datetime
16+
from flask import Flask, request, jsonify
17+
import psutil
18+
19+
app = Flask(__name__)
20+
21+
class TaskAgent:
22+
def __init__(self):
23+
self.hostname = platform.node()
24+
self.system_info = self._get_system_info()
25+
self.running_tasks = {}
26+
27+
def _get_system_info(self):
28+
"""Get system information"""
29+
try:
30+
cpu_info = platform.processor() or "Unknown"
31+
memory = psutil.virtual_memory()
32+
33+
return {
34+
"hostname": self.hostname,
35+
"os": f"{platform.system()} {platform.release()}",
36+
"arch": platform.machine(),
37+
"cpu": cpu_info,
38+
"ram": f"{memory.total // (1024**3)}GB",
39+
"python_version": platform.python_version()
40+
}
41+
except Exception as e:
42+
print(f"Error getting system info: {e}")
43+
return {
44+
"hostname": self.hostname,
45+
"os": f"{platform.system()} {platform.release()}",
46+
"arch": platform.machine(),
47+
"cpu": "Unknown",
48+
"ram": "Unknown",
49+
"python_version": platform.python_version()
50+
}
51+
52+
def execute_script(self, script_name, script_content, parameters, execution_id):
53+
"""Execute a script with given parameters"""
54+
start_time = time.time()
55+
56+
try:
57+
# Determine script type and create temporary file
58+
script_extension = self._get_script_extension(script_name)
59+
60+
with tempfile.NamedTemporaryFile(mode='w', suffix=script_extension, delete=False) as temp_file:
61+
temp_file.write(script_content)
62+
temp_file_path = temp_file.name
63+
64+
try:
65+
# Make script executable if it's a shell script
66+
if script_extension in ['.sh', '.bash']:
67+
os.chmod(temp_file_path, 0o755)
68+
69+
# Prepare command based on script type
70+
if script_extension == '.py':
71+
cmd = [sys.executable, temp_file_path]
72+
elif script_extension in ['.sh', '.bash']:
73+
cmd = ['/bin/bash', temp_file_path]
74+
elif script_extension == '.js':
75+
cmd = ['node', temp_file_path]
76+
else:
77+
# Try to execute directly
78+
cmd = [temp_file_path]
79+
80+
# Add parameters as environment variables
81+
env = os.environ.copy()
82+
for key, value in parameters.items():
83+
env[f'PARAM_{key.upper()}'] = str(value)
84+
85+
# Execute the script
86+
result = subprocess.run(
87+
cmd,
88+
capture_output=True,
89+
text=True,
90+
timeout=300, # 5 minutes timeout
91+
env=env
92+
)
93+
94+
duration = int((time.time() - start_time) * 1000) # milliseconds
95+
96+
if result.returncode == 0:
97+
return {
98+
"execution_id": execution_id,
99+
"status": "success",
100+
"output": result.stdout,
101+
"duration": duration
102+
}
103+
else:
104+
return {
105+
"execution_id": execution_id,
106+
"status": "error",
107+
"output": result.stdout,
108+
"error": result.stderr,
109+
"duration": duration
110+
}
111+
112+
finally:
113+
# Clean up temporary file
114+
try:
115+
os.unlink(temp_file_path)
116+
except:
117+
pass
118+
119+
except subprocess.TimeoutExpired:
120+
duration = int((time.time() - start_time) * 1000)
121+
return {
122+
"execution_id": execution_id,
123+
"status": "error",
124+
"error": "Script execution timed out (5 minutes)",
125+
"duration": duration
126+
}
127+
except Exception as e:
128+
duration = int((time.time() - start_time) * 1000)
129+
return {
130+
"execution_id": execution_id,
131+
"status": "error",
132+
"error": str(e),
133+
"duration": duration
134+
}
135+
136+
def _get_script_extension(self, script_name):
137+
"""Get appropriate file extension based on script name"""
138+
if script_name.endswith('.py'):
139+
return '.py'
140+
elif script_name.endswith('.sh') or script_name.endswith('.bash'):
141+
return '.sh'
142+
elif script_name.endswith('.js'):
143+
return '.js'
144+
else:
145+
# Default to shell script
146+
return '.sh'
147+
148+
# Initialize agent
149+
agent = TaskAgent()
150+
151+
@app.route('/ping', methods=['GET'])
152+
def ping():
153+
"""Health check endpoint"""
154+
return jsonify({"status": "ok", "timestamp": datetime.now().isoformat()})
155+
156+
@app.route('/info', methods=['GET'])
157+
def get_info():
158+
"""Get agent system information"""
159+
info = agent.system_info.copy()
160+
info["status"] = "online"
161+
info["ip"] = request.environ.get('SERVER_NAME', 'localhost')
162+
return jsonify(info)
163+
164+
@app.route('/execute', methods=['POST'])
165+
def execute_script():
166+
"""Execute a script"""
167+
try:
168+
data = request.get_json()
169+
170+
if not data:
171+
return jsonify({"error": "No JSON data provided"}), 400
172+
173+
required_fields = ['script_name', 'execution_id']
174+
for field in required_fields:
175+
if field not in data:
176+
return jsonify({"error": f"Missing required field: {field}"}), 400
177+
178+
script_name = data['script_name']
179+
script_content = data.get('script_content', '')
180+
parameters = data.get('parameters', {})
181+
execution_id = data['execution_id']
182+
183+
# If no script content provided, try to load from local scripts directory
184+
if not script_content:
185+
script_path = os.path.join('scripts', script_name)
186+
if os.path.exists(script_path):
187+
with open(script_path, 'r') as f:
188+
script_content = f.read()
189+
else:
190+
return jsonify({
191+
"execution_id": execution_id,
192+
"status": "error",
193+
"error": f"Script '{script_name}' not found and no content provided"
194+
}), 404
195+
196+
# Execute script
197+
result = agent.execute_script(script_name, script_content, parameters, execution_id)
198+
199+
return jsonify(result)
200+
201+
except Exception as e:
202+
return jsonify({
203+
"execution_id": data.get('execution_id', 'unknown'),
204+
"status": "error",
205+
"error": str(e)
206+
}), 500
207+
208+
@app.route('/scripts', methods=['GET'])
209+
def list_scripts():
210+
"""List available local scripts"""
211+
scripts_dir = 'scripts'
212+
scripts = []
213+
214+
if os.path.exists(scripts_dir):
215+
for filename in os.listdir(scripts_dir):
216+
if os.path.isfile(os.path.join(scripts_dir, filename)):
217+
scripts.append(filename)
218+
219+
return jsonify(scripts)
220+
221+
@app.route('/status', methods=['GET'])
222+
def get_status():
223+
"""Get agent status and running tasks"""
224+
try:
225+
cpu_percent = psutil.cpu_percent(interval=1)
226+
memory = psutil.virtual_memory()
227+
disk = psutil.disk_usage('/')
228+
229+
return jsonify({
230+
"status": "online",
231+
"hostname": agent.hostname,
232+
"uptime": time.time() - psutil.boot_time(),
233+
"cpu_usage": cpu_percent,
234+
"memory_usage": memory.percent,
235+
"disk_usage": disk.percent,
236+
"running_tasks": len(agent.running_tasks),
237+
"timestamp": datetime.now().isoformat()
238+
})
239+
except Exception as e:
240+
return jsonify({
241+
"status": "online",
242+
"error": str(e),
243+
"timestamp": datetime.now().isoformat()
244+
})
245+
246+
if __name__ == '__main__':
247+
# Create scripts directory if it doesn't exist
248+
os.makedirs('scripts', exist_ok=True)
249+
250+
# Create some example scripts
251+
example_scripts = {
252+
'hello.py': '''#!/usr/bin/env python3
253+
import os
254+
import sys
255+
from datetime import datetime
256+
257+
print(f"Hello from Python agent!")
258+
print(f"Hostname: {os.environ.get('HOSTNAME', 'unknown')}")
259+
print(f"Timestamp: {datetime.now()}")
260+
261+
# Access parameters via environment variables
262+
name = os.environ.get('PARAM_NAME', 'World')
263+
print(f"Hello, {name}!")
264+
265+
# Example of using parameters
266+
if 'PARAM_COUNT' in os.environ:
267+
count = int(os.environ.get('PARAM_COUNT', '1'))
268+
for i in range(count):
269+
print(f"Message {i+1}: Hello from iteration {i+1}")
270+
''',
271+
272+
'system_info.sh': '''#!/bin/bash
273+
echo "=== System Information ==="
274+
echo "Hostname: $(hostname)"
275+
echo "Date: $(date)"
276+
echo "Uptime: $(uptime)"
277+
echo "Disk Usage:"
278+
df -h
279+
echo ""
280+
echo "Memory Usage:"
281+
free -h
282+
echo ""
283+
echo "Parameters received:"
284+
env | grep "^PARAM_" | sort
285+
''',
286+
287+
'backup_example.py': '''#!/usr/bin/env python3
288+
import os
289+
import shutil
290+
import time
291+
from datetime import datetime
292+
293+
def backup_directory():
294+
source = os.environ.get('PARAM_SOURCE', '/tmp/test')
295+
destination = os.environ.get('PARAM_DESTINATION', '/tmp/backup')
296+
297+
print(f"Starting backup from {source} to {destination}")
298+
print(f"Timestamp: {datetime.now()}")
299+
300+
try:
301+
# Create destination if it doesn't exist
302+
os.makedirs(destination, exist_ok=True)
303+
304+
# Simulate backup process
305+
if os.path.exists(source):
306+
backup_name = f"backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
307+
backup_path = os.path.join(destination, backup_name)
308+
309+
print(f"Creating backup: {backup_path}")
310+
# In a real scenario, you'd copy files here
311+
# shutil.copytree(source, backup_path)
312+
313+
# Simulate work
314+
time.sleep(2)
315+
316+
print("Backup completed successfully!")
317+
print(f"Backup size: simulated 1.2GB")
318+
print(f"Files processed: simulated 1,234 files")
319+
else:
320+
print(f"Warning: Source directory {source} does not exist")
321+
322+
except Exception as e:
323+
print(f"Error during backup: {e}")
324+
exit(1)
325+
326+
if __name__ == "__main__":
327+
backup_directory()
328+
'''
329+
}
330+
331+
# Write example scripts
332+
for script_name, content in example_scripts.items():
333+
script_path = os.path.join('scripts', script_name)
334+
if not os.path.exists(script_path):
335+
with open(script_path, 'w') as f:
336+
f.write(content)
337+
# Make shell scripts executable
338+
if script_name.endswith('.sh'):
339+
os.chmod(script_path, 0o755)
340+
341+
print("Task Scheduler Agent starting...")
342+
print(f"Hostname: {agent.hostname}")
343+
print(f"System: {agent.system_info['os']}")
344+
print("Available endpoints:")
345+
print(" GET /ping - Health check")
346+
print(" GET /info - System information")
347+
print(" GET /status - Agent status")
348+
print(" GET /scripts - List available scripts")
349+
print(" POST /execute - Execute script")
350+
print("")
351+
print("Example scripts created in ./scripts/")
352+
print("Starting server on http://0.0.0.0:5000")
353+
354+
app.run(host='0.0.0.0', port=5000, debug=True)

0 commit comments

Comments
 (0)