시놀로지 health 조회 SSH 통합
This commit is contained in:
1 parent
61ad4af4ae
commit
64510e8fe5
2 files changed
+30
-38
No files matched your search
@@ -159,7 +159,7 @@ cd /var/www/control
|
|||||||
|
|
||||||
`RASPBERRY` 카드는 `SSD I/O`, `TEMP`, `FAN`, `THROT`, `CPU`, `IRQ`, `CTX`, `FORK`, `LOCAL`, `UPLINK`, `ETH1`, `5G`, `2.4G`, `SOFTIRQ`, `TCP`, `UDP`, `VM`, `RUNQ`를 표시합니다. 네트워크와 SSD I/O 점멸은 100ms 샘플에서 직전 카운터보다 증가했는지를 보고, CPU/IRQ/CTX/FORK/SOFTIRQ/TCP/UDP/VM/RUNQ 카드는 `/proc/stat`, `/proc/net/snmp`, `/proc/vmstat`의 누적 카운터 증가량으로 점멸합니다. CPU 온도/SSD 온도와 팬 RPM은 500ms 간격으로 표시합니다.
|
`RASPBERRY` 카드는 `SSD I/O`, `TEMP`, `FAN`, `THROT`, `CPU`, `IRQ`, `CTX`, `FORK`, `LOCAL`, `UPLINK`, `ETH1`, `5G`, `2.4G`, `SOFTIRQ`, `TCP`, `UDP`, `VM`, `RUNQ`를 표시합니다. 네트워크와 SSD I/O 점멸은 100ms 샘플에서 직전 카운터보다 증가했는지를 보고, CPU/IRQ/CTX/FORK/SOFTIRQ/TCP/UDP/VM/RUNQ 카드는 `/proc/stat`, `/proc/net/snmp`, `/proc/vmstat`의 누적 카운터 증가량으로 점멸합니다. CPU 온도/SSD 온도와 팬 RPM은 500ms 간격으로 표시합니다.
|
||||||
|
|
||||||
`SYNOLOGY` 카드는 `PING`, `TEMP`, `FAN`, `GUARD`, `CPU`, `IRQ`, `CTX`, `FORK`, `SOFTIRQ`, `TCP`, `UDP`, `VM`, `LOCAL`, `UPLINK`, `RUNQ`, `SATA1 I/O`, `SATA2 I/O`, `SATA4 I/O`를 표시합니다. 시놀로지는 SSH를 유지한 채 100ms마다 네트워크, 디스크 sector I/O, CPU/IRQ/CTX/FORK/SOFTIRQ/TCP/UDP/VM/RUNQ 카운터를 스트림으로 가져옵니다. SATA SMART/온도 직접 조회는 응답 지연 위험 때문에 대시보드에서 실행하지 않고, SATA I/O 카드에는 `조회 중지`를 표시합니다. CPU 온도, 팬 RPM, fan guard 상태는 500ms 간격으로 별도 조회합니다.
|
`SYNOLOGY` 카드는 `PING`, `TEMP`, `FAN`, `GUARD`, `CPU`, `IRQ`, `CTX`, `FORK`, `SOFTIRQ`, `TCP`, `UDP`, `VM`, `LOCAL`, `UPLINK`, `RUNQ`, `SATA1 I/O`, `SATA2 I/O`, `SATA4 I/O`를 표시합니다. 시놀로지는 SSH를 유지한 채 100ms마다 네트워크, 디스크 sector I/O, CPU/IRQ/CTX/FORK/SOFTIRQ/TCP/UDP/VM/RUNQ 카운터를 스트림으로 가져옵니다. CPU 온도, 팬 RPM, fan guard 상태도 같은 SSH 스트림 안에서 약 5초마다 가져오며 별도 SSH 연결은 만들지 않습니다. SATA SMART/온도 직접 조회는 응답 지연 위험 때문에 대시보드에서 실행하지 않고, SATA I/O 카드에는 `조회 중지`를 표시합니다.
|
||||||
|
|
||||||
온도 카드는 80도 이상부터 빨간 표시등을 500ms 간격으로 점멸하고, 팬 카드는 RPM 0 또는 7000 초과를 빨강, 4500 초과를 주황으로 표시합니다. 시놀로지 `PING`은 장기 실행 `ping -i 0.5` 출력 파싱으로 읽고, 15ms 이하 초록, 30ms 이하 주황, 50ms 이하 빨강, 50ms 초과 빨강 500ms 점멸입니다. `RPI THROT`은 정상 초록, 부팅 후 이력 주황, 현재 언더볼트 빨강, 현재 스로틀 빨강 500ms 점멸, 현재 언더볼트와 스로틀 동시 발생 빨강 100ms 점멸로 표시합니다. `SYNO GUARD`는 `synology-fan-guard.service`가 active이고 DSM 자동제어면 초록 `AUTO`, 고온으로 PWM 최대 강제 중이면 주황 `FULL`, 서비스 실패나 팬 RPM 0이면 빨강 `FAIL`로 표시합니다.
|
온도 카드는 80도 이상부터 빨간 표시등을 500ms 간격으로 점멸하고, 팬 카드는 RPM 0 또는 7000 초과를 빨강, 4500 초과를 주황으로 표시합니다. 시놀로지 `PING`은 장기 실행 `ping -i 0.5` 출력 파싱으로 읽고, 15ms 이하 초록, 30ms 이하 주황, 50ms 이하 빨강, 50ms 초과 빨강 500ms 점멸입니다. `RPI THROT`은 정상 초록, 부팅 후 이력 주황, 현재 언더볼트 빨강, 현재 스로틀 빨강 500ms 점멸, 현재 언더볼트와 스로틀 동시 발생 빨강 100ms 점멸로 표시합니다. `SYNO GUARD`는 `synology-fan-guard.service`가 active이고 DSM 자동제어면 초록 `AUTO`, 고온으로 PWM 최대 강제 중이면 주황 `FULL`, 서비스 실패나 팬 RPM 0이면 빨강 `FAIL`로 표시합니다.
|
||||||
|
|
||||||
|
|||||||
+29
-37
@@ -45,6 +45,7 @@ SYNOLOGY_SSH = [
|
|||||||
"seo@chaegeon.com",
|
"seo@chaegeon.com",
|
||||||
]
|
]
|
||||||
SYNOLOGY_STATUS_SCRIPT = r"""
|
SYNOLOGY_STATUS_SCRIPT = r"""
|
||||||
|
health_tick=49
|
||||||
while :; do
|
while :; do
|
||||||
wan="$(awk '$2=="00000000"{print $1; exit}' /proc/net/route)"
|
wan="$(awk '$2=="00000000"{print $1; exit}' /proc/net/route)"
|
||||||
[ -n "$wan" ] || wan=ovs_eth0
|
[ -n "$wan" ] || wan=ovs_eth0
|
||||||
@@ -90,22 +91,25 @@ while :; do
|
|||||||
}
|
}
|
||||||
' /proc/net/snmp
|
' /proc/net/snmp
|
||||||
awk '/^(pgfault|pgmajfault|pgpgin|pgpgout) / {key=toupper($1); printf "%s=%s\n", key, $2}' /proc/vmstat
|
awk '/^(pgfault|pgmajfault|pgpgin|pgpgout) / {key=toupper($1); printf "%s=%s\n", key, $2}' /proc/vmstat
|
||||||
|
if [ "$health_tick" -ge 49 ]; then
|
||||||
|
cpu="$(cat /sys/class/hwmon/hwmon0/temp1_input 2>/dev/null)"
|
||||||
|
fan="$(cat /sys/class/hwmon/hwmon1/fan2_input 2>/dev/null)"
|
||||||
|
pwm="$(cat /sys/class/hwmon/hwmon1/pwm2 2>/dev/null)"
|
||||||
|
enable="$(cat /sys/class/hwmon/hwmon1/pwm2_enable 2>/dev/null)"
|
||||||
|
guard="$(systemctl is-active synology-fan-guard.service 2>/dev/null || printf unknown)"
|
||||||
|
case "$cpu" in ''|*[!0-9]*) cpu=0 ;; esac
|
||||||
|
case "$fan" in ''|*[!0-9]*) fan=0 ;; esac
|
||||||
|
case "$pwm" in ''|*[!0-9]*) pwm=0 ;; esac
|
||||||
|
case "$enable" in ''|*[!0-9]*) enable=-1 ;; esac
|
||||||
|
awk -v cpu="$cpu" -v fan="$fan" -v pwm="$pwm" -v enable="$enable" -v guard="$guard" 'BEGIN {printf "SYNO_CPU=%.1f\nSYNO_FAN=%d\nSYNO_GUARD=%s\nSYNO_PWM=%d\nSYNO_PWM_ENABLE=%d\n", cpu / 1000, fan, guard, pwm, enable}'
|
||||||
|
health_tick=0
|
||||||
|
else
|
||||||
|
health_tick=$((health_tick + 1))
|
||||||
|
fi
|
||||||
echo END=1
|
echo END=1
|
||||||
sleep 0.1
|
sleep 0.1
|
||||||
done
|
done
|
||||||
"""
|
"""
|
||||||
SYNOLOGY_HEALTH_SCRIPT = r"""
|
|
||||||
cpu="$(cat /sys/class/hwmon/hwmon0/temp1_input 2>/dev/null)"
|
|
||||||
fan="$(cat /sys/class/hwmon/hwmon1/fan2_input 2>/dev/null)"
|
|
||||||
pwm="$(cat /sys/class/hwmon/hwmon1/pwm2 2>/dev/null)"
|
|
||||||
enable="$(cat /sys/class/hwmon/hwmon1/pwm2_enable 2>/dev/null)"
|
|
||||||
guard="$(systemctl is-active synology-fan-guard.service 2>/dev/null || printf unknown)"
|
|
||||||
case "$cpu" in ''|*[!0-9]*) cpu=0 ;; esac
|
|
||||||
case "$fan" in ''|*[!0-9]*) fan=0 ;; esac
|
|
||||||
case "$pwm" in ''|*[!0-9]*) pwm=0 ;; esac
|
|
||||||
case "$enable" in ''|*[!0-9]*) enable=-1 ;; esac
|
|
||||||
awk -v cpu="$cpu" -v fan="$fan" -v pwm="$pwm" -v enable="$enable" -v guard="$guard" 'BEGIN {printf "SYNO_CPU=%.1f\nSYNO_FAN=%d\nSYNO_GUARD=%s\nSYNO_PWM=%d\nSYNO_PWM_ENABLE=%d\n", cpu / 1000, fan, guard, pwm, enable}'
|
|
||||||
"""
|
|
||||||
SAMPLE_INTERVAL_MS = 100
|
SAMPLE_INTERVAL_MS = 100
|
||||||
DASHBOARD_INTERVAL_MS = 500
|
DASHBOARD_INTERVAL_MS = 500
|
||||||
FAN_INTERVAL_MS = 500
|
FAN_INTERVAL_MS = 500
|
||||||
@@ -114,13 +118,12 @@ TEMP_GRAPH_SECONDS = 60
|
|||||||
WAN_GRAPH_POINT_DENSITY = 2
|
WAN_GRAPH_POINT_DENSITY = 2
|
||||||
WAN_GRAPH_POINTS = WAN_GRAPH_SECONDS * 1000 // SAMPLE_INTERVAL_MS * WAN_GRAPH_POINT_DENSITY
|
WAN_GRAPH_POINTS = WAN_GRAPH_SECONDS * 1000 // SAMPLE_INTERVAL_MS * WAN_GRAPH_POINT_DENSITY
|
||||||
TEMP_GRAPH_POINTS = TEMP_GRAPH_SECONDS * 1000 // DASHBOARD_INTERVAL_MS
|
TEMP_GRAPH_POINTS = TEMP_GRAPH_SECONDS * 1000 // DASHBOARD_INTERVAL_MS
|
||||||
SYNOLOGY_RESTART_INTERVAL_MS = 1000
|
SYNOLOGY_RESTART_INTERVAL_MS = 3000
|
||||||
TEMP_INTERVAL_MS = 500
|
TEMP_INTERVAL_MS = 500
|
||||||
DISK_TEMP_INTERVAL_MS = TEMP_INTERVAL_MS
|
DISK_TEMP_INTERVAL_MS = TEMP_INTERVAL_MS
|
||||||
DISK_TEMP_CACHE_FILE = "/run/control-rpi-disk-temp"
|
DISK_TEMP_CACHE_FILE = "/run/control-rpi-disk-temp"
|
||||||
DISK_TEMP_CACHE_MAX_AGE_SECONDS = 30
|
DISK_TEMP_CACHE_MAX_AGE_SECONDS = 30
|
||||||
RPI_THROTTLE_INTERVAL_MS = SAMPLE_INTERVAL_MS
|
RPI_THROTTLE_INTERVAL_MS = SAMPLE_INTERVAL_MS
|
||||||
SYNOLOGY_HEALTH_INTERVAL_MS = FAN_INTERVAL_MS
|
|
||||||
|
|
||||||
|
|
||||||
def byte_rate_unit(values):
|
def byte_rate_unit(values):
|
||||||
@@ -692,6 +695,7 @@ class TouchPanel(QMainWindow):
|
|||||||
self.synology_values = {}
|
self.synology_values = {}
|
||||||
self.synology_cpu_temp = 0.0
|
self.synology_cpu_temp = 0.0
|
||||||
self.synology_fan_rpm = 0
|
self.synology_fan_rpm = 0
|
||||||
|
self.synology_guard_values = {}
|
||||||
self.last_io = None
|
self.last_io = None
|
||||||
self.last_sample = None
|
self.last_sample = None
|
||||||
self.last_graph = None
|
self.last_graph = None
|
||||||
@@ -739,12 +743,6 @@ class TouchPanel(QMainWindow):
|
|||||||
self.synology_timer.timeout.connect(self.update_synology_status)
|
self.synology_timer.timeout.connect(self.update_synology_status)
|
||||||
self.synology_timer.start(SYNOLOGY_RESTART_INTERVAL_MS)
|
self.synology_timer.start(SYNOLOGY_RESTART_INTERVAL_MS)
|
||||||
|
|
||||||
self.synology_health_proc = QProcess(self)
|
|
||||||
self.synology_health_proc.finished.connect(self.synology_health_finished)
|
|
||||||
self.synology_health_timer = QTimer(self)
|
|
||||||
self.synology_health_timer.timeout.connect(self.update_synology_health)
|
|
||||||
self.synology_health_timer.start(SYNOLOGY_HEALTH_INTERVAL_MS)
|
|
||||||
|
|
||||||
self.synology_ping_proc = QProcess(self)
|
self.synology_ping_proc = QProcess(self)
|
||||||
self.synology_ping_buffer = ""
|
self.synology_ping_buffer = ""
|
||||||
self.synology_ping_proc.readyReadStandardOutput.connect(self.synology_ping_ready)
|
self.synology_ping_proc.readyReadStandardOutput.connect(self.synology_ping_ready)
|
||||||
@@ -768,7 +766,6 @@ class TouchPanel(QMainWindow):
|
|||||||
self.ping_pc()
|
self.ping_pc()
|
||||||
self.update_disk_temp()
|
self.update_disk_temp()
|
||||||
self.update_synology_status()
|
self.update_synology_status()
|
||||||
self.update_synology_health()
|
|
||||||
self.ping_synology()
|
self.ping_synology()
|
||||||
self.update_rpi_throttle()
|
self.update_rpi_throttle()
|
||||||
|
|
||||||
@@ -1105,30 +1102,24 @@ class TouchPanel(QMainWindow):
|
|||||||
self.synology_values = {}
|
self.synology_values = {}
|
||||||
self.synology_proc.start(SYNOLOGY_SSH[0], SYNOLOGY_SSH[1:] + [SYNOLOGY_STATUS_SCRIPT])
|
self.synology_proc.start(SYNOLOGY_SSH[0], SYNOLOGY_SSH[1:] + [SYNOLOGY_STATUS_SCRIPT])
|
||||||
|
|
||||||
def update_synology_health(self):
|
def apply_synology_health(self, values):
|
||||||
if self.synology_health_proc.state() != QProcess.NotRunning:
|
health_keys = ["SYNO_CPU", "SYNO_FAN", "SYNO_GUARD", "SYNO_PWM", "SYNO_PWM_ENABLE"]
|
||||||
|
if not any(key in values for key in health_keys):
|
||||||
return
|
return
|
||||||
self.synology_health_proc.start(SYNOLOGY_SSH[0], SYNOLOGY_SSH[1:] + [SYNOLOGY_HEALTH_SCRIPT])
|
if "SYNO_CPU" in values:
|
||||||
|
|
||||||
def synology_health_finished(self, exit_code, exit_status):
|
|
||||||
del exit_status
|
|
||||||
if exit_code != 0:
|
|
||||||
return
|
|
||||||
output = bytes(self.synology_health_proc.readAllStandardOutput()).decode("utf-8", "ignore")
|
|
||||||
values = {}
|
|
||||||
for line in output.splitlines():
|
|
||||||
if "=" not in line:
|
|
||||||
continue
|
|
||||||
key, value = line.split("=", 1)
|
|
||||||
values[key.strip()] = value.strip()
|
|
||||||
try:
|
try:
|
||||||
self.synology_cpu_temp = float(values.get("SYNO_CPU", "0") or 0)
|
self.synology_cpu_temp = float(values.get("SYNO_CPU", "0") or 0)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.synology_cpu_temp = 0.0
|
self.synology_cpu_temp = 0.0
|
||||||
|
if "SYNO_FAN" in values:
|
||||||
|
try:
|
||||||
self.synology_fan_rpm = int(values.get("SYNO_FAN", "0") or 0)
|
self.synology_fan_rpm = int(values.get("SYNO_FAN", "0") or 0)
|
||||||
|
except ValueError:
|
||||||
|
self.synology_fan_rpm = 0
|
||||||
|
self.synology_guard_values.update({key: values[key] for key in health_keys if key in values})
|
||||||
self.render_synology_temp()
|
self.render_synology_temp()
|
||||||
self.render_synology_fan()
|
self.render_synology_fan()
|
||||||
self.render_synology_guard(values)
|
self.render_synology_guard(self.synology_guard_values)
|
||||||
|
|
||||||
def synology_status_ready(self):
|
def synology_status_ready(self):
|
||||||
output = bytes(self.synology_proc.readAllStandardOutput()).decode("utf-8", "ignore")
|
output = bytes(self.synology_proc.readAllStandardOutput()).decode("utf-8", "ignore")
|
||||||
@@ -1172,6 +1163,7 @@ class TouchPanel(QMainWindow):
|
|||||||
if not values:
|
if not values:
|
||||||
self.set_synology_offline()
|
self.set_synology_offline()
|
||||||
return
|
return
|
||||||
|
self.apply_synology_health(values)
|
||||||
for disk in ["SATA1", "SATA2", "SATA4"]:
|
for disk in ["SATA1", "SATA2", "SATA4"]:
|
||||||
self.set_synology_io_tile(
|
self.set_synology_io_tile(
|
||||||
f"synology_{disk.lower()}",
|
f"synology_{disk.lower()}",
|
||||||
|
|||||||
Reference in new issue
Block a user