Add .deb agent package with systemd timer and interactive setup
This commit is contained in:
@@ -5,3 +5,4 @@ __pycache__/
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
|
*.deb
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ info-user/
|
|||||||
│ ├── agent.py
|
│ ├── agent.py
|
||||||
│ ├── config.json
|
│ ├── config.json
|
||||||
│ └── install.sh
|
│ └── install.sh
|
||||||
|
├── agent-deb/ # Исходники .deb пакета (dpkg-deb --build)
|
||||||
├── data/ # SQLite БД (создаётся при запуске)
|
├── data/ # SQLite БД (создаётся при запуске)
|
||||||
├── docker-compose.yml
|
├── docker-compose.yml
|
||||||
├── nginx.conf
|
├── nginx.conf
|
||||||
@@ -70,6 +71,23 @@ http://localhost
|
|||||||
|
|
||||||
5. **Установить агент** на клиентский ПК (Linux):
|
5. **Установить агент** на клиентский ПК (Linux):
|
||||||
|
|
||||||
|
### Debian/Ubuntu/Astra Linux (рекомендуется)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Установить .deb пакет
|
||||||
|
sudo dpkg -i info-user-agent_1.0.0_all.deb
|
||||||
|
|
||||||
|
# Настроить сервер и токен (интерактивно)
|
||||||
|
sudo info-user-agent-setup
|
||||||
|
|
||||||
|
# Или вручную:
|
||||||
|
sudo nano /opt/info-user-agent/config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Либо через репозиторий (добавив его в `/etc/apt/sources.list.d/`).
|
||||||
|
|
||||||
|
### Вручную (любой Linux)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd agent
|
cd agent
|
||||||
sudo bash install.sh
|
sudo bash install.sh
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
Package: info-user-agent
|
||||||
|
Version: 1.0.0
|
||||||
|
Section: admin
|
||||||
|
Priority: optional
|
||||||
|
Architecture: all
|
||||||
|
Depends: python3, python3-psutil, python3-requests
|
||||||
|
Maintainer: Hidosi <hidosi@hidosi.ru>
|
||||||
|
Description: InfoUser Monitor - агент мониторинга ПК
|
||||||
|
Агент собирает метрики компьютера (CPU, RAM, диски, процессы, пользователь, IP)
|
||||||
|
и отправляет их раз в час на сервер InfoUser Monitor.
|
||||||
|
.
|
||||||
|
После установки запустите:
|
||||||
|
sudo info-user-agent-setup
|
||||||
|
для настройки адреса сервера и токена.
|
||||||
Executable
+20
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
systemctl daemon-reload 2>/dev/null || true
|
||||||
|
systemctl enable info-user-agent.timer 2>/dev/null || true
|
||||||
|
systemctl start info-user-agent.timer 2>/dev/null || true
|
||||||
|
|
||||||
|
cat <<EOF
|
||||||
|
|
||||||
|
========================================
|
||||||
|
InfoUser Agent установлен!
|
||||||
|
|
||||||
|
Настройте сервер и токен:
|
||||||
|
sudo info-user-agent-setup
|
||||||
|
|
||||||
|
Статус таймера:
|
||||||
|
systemctl status info-user-agent.timer
|
||||||
|
========================================
|
||||||
|
|
||||||
|
EOF
|
||||||
Executable
+6
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
systemctl daemon-reload 2>/dev/null || true
|
||||||
|
|
||||||
|
exit 0
|
||||||
Executable
+7
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
systemctl stop info-user-agent.timer 2>/dev/null || true
|
||||||
|
systemctl disable info-user-agent.timer 2>/dev/null || true
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=InfoUser Agent
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/bin/python3 /opt/info-user-agent/agent.py
|
||||||
|
User=root
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=InfoUser Agent Timer
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnCalendar=hourly
|
||||||
|
Persistent=true
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
Executable
+146
@@ -0,0 +1,146 @@
|
|||||||
|
import psutil
|
||||||
|
import socket
|
||||||
|
import platform
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
import getpass
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format="%(asctime)s [%(levelname)s] %(message)s",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def load_config():
|
||||||
|
cfg_path = Path(__file__).with_suffix("").parent / "config.json"
|
||||||
|
if cfg_path.exists():
|
||||||
|
with open(cfg_path, "r", encoding="utf-8") as f:
|
||||||
|
return json.load(f)
|
||||||
|
return {
|
||||||
|
"server_url": "http://127.0.0.1",
|
||||||
|
"agent_token": "agent-secret-token",
|
||||||
|
"interval_minutes": 60,
|
||||||
|
"send_public_ip": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_local_ip():
|
||||||
|
try:
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
s.settimeout(1)
|
||||||
|
s.connect(("8.8.8.8", 80))
|
||||||
|
ip = s.getsockname()[0]
|
||||||
|
s.close()
|
||||||
|
return ip
|
||||||
|
except Exception:
|
||||||
|
return "127.0.0.1"
|
||||||
|
|
||||||
|
|
||||||
|
def get_public_ip():
|
||||||
|
try:
|
||||||
|
r = requests.get("https://api.ipify.org", timeout=5)
|
||||||
|
return r.text.strip()
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_uptime():
|
||||||
|
return int(time.time() - psutil.boot_time())
|
||||||
|
|
||||||
|
|
||||||
|
def get_disk_info():
|
||||||
|
disks = []
|
||||||
|
for part in psutil.disk_partitions(all=False):
|
||||||
|
try:
|
||||||
|
usage = psutil.disk_usage(part.mountpoint)
|
||||||
|
disks.append({
|
||||||
|
"device": part.device,
|
||||||
|
"mountpoint": part.mountpoint,
|
||||||
|
"total_gb": round(usage.total / (2**30), 1),
|
||||||
|
"used_gb": round(usage.used / (2**30), 1),
|
||||||
|
"free_gb": round(usage.free / (2**30), 1),
|
||||||
|
"percent": usage.percent,
|
||||||
|
})
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
return disks
|
||||||
|
|
||||||
|
|
||||||
|
def get_cpu_temp():
|
||||||
|
try:
|
||||||
|
temps = psutil.sensors_temperatures()
|
||||||
|
if temps:
|
||||||
|
for name, entries in temps.items():
|
||||||
|
for entry in entries:
|
||||||
|
if entry.current:
|
||||||
|
return round(entry.current, 1)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_load_avg():
|
||||||
|
try:
|
||||||
|
return ", ".join(str(x) for x in os.getloadavg())
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_top_processes(limit=10):
|
||||||
|
procs = []
|
||||||
|
for p in psutil.process_iter(["pid", "name", "cpu_percent", "memory_percent"]):
|
||||||
|
try:
|
||||||
|
procs.append({
|
||||||
|
"pid": p.info["pid"],
|
||||||
|
"name": p.info["name"],
|
||||||
|
"cpu_percent": round(p.info["cpu_percent"] or 0, 1),
|
||||||
|
"ram_percent": round((p.info["memory_percent"] or 0), 1),
|
||||||
|
})
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
procs.sort(key=lambda x: x["cpu_percent"], reverse=True)
|
||||||
|
return procs[:limit]
|
||||||
|
|
||||||
|
|
||||||
|
def collect():
|
||||||
|
cfg = load_config()
|
||||||
|
payload = {
|
||||||
|
"hostname": socket.gethostname(),
|
||||||
|
"username": getpass.getuser(),
|
||||||
|
"os_info": f"{platform.system()} {platform.release()}",
|
||||||
|
"local_ip": get_local_ip(),
|
||||||
|
"public_ip": get_public_ip() if cfg.get("send_public_ip") else None,
|
||||||
|
"uptime_sec": get_uptime(),
|
||||||
|
"cpu_percent": psutil.cpu_percent(interval=1),
|
||||||
|
"ram_percent": psutil.virtual_memory().percent,
|
||||||
|
"ram_total_gb": round(psutil.virtual_memory().total / (2**30), 1),
|
||||||
|
"disk_info": get_disk_info(),
|
||||||
|
"cpu_temp": get_cpu_temp(),
|
||||||
|
"load_avg": get_load_avg(),
|
||||||
|
"processes": get_top_processes(10),
|
||||||
|
}
|
||||||
|
return payload, cfg
|
||||||
|
|
||||||
|
|
||||||
|
def send(payload, cfg):
|
||||||
|
url = cfg["server_url"].rstrip("/") + "/api/heartbeat"
|
||||||
|
headers = {"Authorization": f"Bearer {cfg['agent_token']}", "Content-Type": "application/json"}
|
||||||
|
try:
|
||||||
|
resp = requests.post(url, json=payload, headers=headers, timeout=15)
|
||||||
|
resp.raise_for_status()
|
||||||
|
logging.info("Heartbeat sent: %s", resp.status_code)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error("Failed to send heartbeat: %s", e)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
payload, cfg = collect()
|
||||||
|
send(payload, cfg)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Executable
+32
@@ -0,0 +1,32 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# InfoUser Agent - интерактивная настройка
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
CONFIG_FILE="/opt/info-user-agent/config.json"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Настройка InfoUser Agent ==="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
read -p "URL сервера (например http://192.168.255.180:3000): " SERVER_URL
|
||||||
|
read -p "Токен агента: " AGENT_TOKEN
|
||||||
|
|
||||||
|
cat > "$CONFIG_FILE" <<EOF
|
||||||
|
{
|
||||||
|
"server_url": "${SERVER_URL}",
|
||||||
|
"agent_token": "${AGENT_TOKEN}",
|
||||||
|
"interval_minutes": 60,
|
||||||
|
"send_public_ip": false
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Конфиг сохранён в $CONFIG_FILE"
|
||||||
|
echo "Проверяю отправку heartbeat..."
|
||||||
|
|
||||||
|
/usr/bin/python3 /opt/info-user-agent/agent.py
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Таймер запущен (каждый час):"
|
||||||
|
systemctl status info-user-agent.timer --no-pager -l
|
||||||
Reference in New Issue
Block a user