new awesome build
This commit is contained in:
140
APP_PROFILER/services/process.py
Normal file
140
APP_PROFILER/services/process.py
Normal file
@@ -0,0 +1,140 @@
|
||||
import os
|
||||
import subprocess
|
||||
import logging
|
||||
import time
|
||||
import psutil
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def get_os_type():
|
||||
"""
|
||||
Simple check to distinguish Alpine from others.
|
||||
"""
|
||||
if os.path.exists("/etc/alpine-release"):
|
||||
return "alpine"
|
||||
return "debian" # default fallback to systemctl
|
||||
|
||||
def control_service(action: str):
|
||||
"""
|
||||
Action: start, stop, restart
|
||||
"""
|
||||
if action not in ["start", "stop", "restart"]:
|
||||
raise ValueError("Invalid action")
|
||||
|
||||
os_type = get_os_type()
|
||||
|
||||
cmd = []
|
||||
if os_type == "alpine":
|
||||
cmd = ["rc-service", "openvpn", action]
|
||||
else:
|
||||
cmd = ["systemctl", action, "openvpn"]
|
||||
|
||||
try:
|
||||
# Capture output to return it or log it
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
||||
return {
|
||||
"status": "success",
|
||||
"message": f"Service {action} executed successfully",
|
||||
"stdout": result.stdout
|
||||
}
|
||||
except subprocess.CalledProcessError as e:
|
||||
logger.error(f"Service control failed: {e.stderr}")
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"Failed to {action} service",
|
||||
"stderr": e.stderr
|
||||
}
|
||||
except FileNotFoundError:
|
||||
# Happens if rc-service or systemctl is missing (e.g. dev env)
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"Command not found found for OS type {os_type}"
|
||||
}
|
||||
|
||||
def get_process_stats():
|
||||
"""
|
||||
Returns dict with pid, cpu_percent, memory_mb, uptime.
|
||||
Uses psutil for robust telemetry.
|
||||
"""
|
||||
pid = None
|
||||
process = None
|
||||
|
||||
# Find the process
|
||||
try:
|
||||
# Iterate over all running processes
|
||||
for proc in psutil.process_iter(['pid', 'name']):
|
||||
try:
|
||||
if proc.info['name'] == 'openvpn':
|
||||
pid = proc.info['pid']
|
||||
process = proc
|
||||
break
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to find process via psutil: {e}")
|
||||
|
||||
if not pid or not process:
|
||||
return {
|
||||
"status": "stopped",
|
||||
"pid": None,
|
||||
"cpu_percent": 0.0,
|
||||
"memory_mb": 0.0,
|
||||
"uptime": None
|
||||
}
|
||||
|
||||
# Get Stats
|
||||
try:
|
||||
# CPU Percent
|
||||
# Increasing interval to 1.0s to ensure we capture enough ticks for accurate reading
|
||||
# especially on systems with low clock resolution (e.g. 100Hz = 10ms ticks)
|
||||
cpu = process.cpu_percent(interval=1.0)
|
||||
|
||||
# Memory (RSS)
|
||||
mem_info = process.memory_info()
|
||||
rss_mb = round(mem_info.rss / 1024 / 1024, 2)
|
||||
|
||||
# Uptime
|
||||
create_time = process.create_time()
|
||||
uptime_seconds = time.time() - create_time
|
||||
uptime_str = format_seconds(uptime_seconds)
|
||||
|
||||
return {
|
||||
"status": "running",
|
||||
"pid": pid,
|
||||
"cpu_percent": cpu,
|
||||
"memory_mb": rss_mb,
|
||||
"uptime": uptime_str
|
||||
}
|
||||
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
||||
# Process might have died between discovery and stats
|
||||
return {
|
||||
"status": "stopped",
|
||||
"pid": None,
|
||||
"cpu_percent": 0.0,
|
||||
"memory_mb": 0.0,
|
||||
"uptime": None
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get process stats: {e}")
|
||||
return {
|
||||
"status": "running",
|
||||
"pid": pid,
|
||||
"cpu_percent": 0.0,
|
||||
"memory_mb": 0.0,
|
||||
"uptime": None
|
||||
}
|
||||
|
||||
def format_seconds(seconds: float) -> str:
|
||||
seconds = int(seconds)
|
||||
days, seconds = divmod(seconds, 86400)
|
||||
hours, seconds = divmod(seconds, 3600)
|
||||
minutes, seconds = divmod(seconds, 60)
|
||||
|
||||
parts = []
|
||||
if days > 0: parts.append(f"{days}d")
|
||||
if hours > 0: parts.append(f"{hours}h")
|
||||
if minutes > 0: parts.append(f"{minutes}m")
|
||||
parts.append(f"{seconds}s")
|
||||
|
||||
return " ".join(parts)
|
||||
Reference in New Issue
Block a user