mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-06-13 01:16:53 +02:00
Replace subprocess usage with os module for better performance and maintainability (#6298)
* avoid executing external tools by using Python's built-in os module to interact with the filesystem directly * Refactor recording cleanup script to use os module instead of subprocess * black format util.py * Ooooops * Refactor get_cpu_stats() to properly identify recording process
This commit is contained in:
parent
19890310fe
commit
b38c9e82e2
@ -3,7 +3,7 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import itertools
|
import itertools
|
||||||
import logging
|
import logging
|
||||||
import subprocess as sp
|
import os
|
||||||
import threading
|
import threading
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@ -192,12 +192,14 @@ class RecordingCleanup(threading.Thread):
|
|||||||
return
|
return
|
||||||
|
|
||||||
logger.debug(f"Oldest recording in the db: {oldest_timestamp}")
|
logger.debug(f"Oldest recording in the db: {oldest_timestamp}")
|
||||||
process = sp.run(
|
|
||||||
["find", RECORD_DIR, "-type", "f", "!", "-newermt", f"@{oldest_timestamp}"],
|
files_to_check = []
|
||||||
capture_output=True,
|
|
||||||
text=True,
|
for root, _, files in os.walk(RECORD_DIR):
|
||||||
)
|
for file in files:
|
||||||
files_to_check = process.stdout.splitlines()
|
file_path = os.path.join(root, file)
|
||||||
|
if os.path.getmtime(file_path) < oldest_timestamp:
|
||||||
|
files_to_check.append(file_path)
|
||||||
|
|
||||||
for f in files_to_check:
|
for f in files_to_check:
|
||||||
p = Path(f)
|
p = Path(f)
|
||||||
@ -216,12 +218,10 @@ class RecordingCleanup(threading.Thread):
|
|||||||
recordings: Recordings = Recordings.select()
|
recordings: Recordings = Recordings.select()
|
||||||
|
|
||||||
# get all recordings files on disk
|
# get all recordings files on disk
|
||||||
process = sp.run(
|
files_on_disk = []
|
||||||
["find", RECORD_DIR, "-type", "f"],
|
for root, _, files in os.walk(RECORD_DIR):
|
||||||
capture_output=True,
|
for file in files:
|
||||||
text=True,
|
files_on_disk.append(os.path.join(root, file))
|
||||||
)
|
|
||||||
files_on_disk = process.stdout.splitlines()
|
|
||||||
|
|
||||||
recordings_to_delete = []
|
recordings_to_delete = []
|
||||||
for recording in recordings.objects().iterator():
|
for recording in recordings.objects().iterator():
|
||||||
|
100
frigate/util.py
100
frigate/util.py
@ -9,6 +9,7 @@ import signal
|
|||||||
import traceback
|
import traceback
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import yaml
|
import yaml
|
||||||
|
import os
|
||||||
|
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
@ -740,55 +741,54 @@ def escape_special_characters(path: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def get_cgroups_version() -> str:
|
def get_cgroups_version() -> str:
|
||||||
"""Determine what version of cgroups is enabled"""
|
"""Determine what version of cgroups is enabled."""
|
||||||
|
|
||||||
stat_command = ["stat", "-fc", "%T", "/sys/fs/cgroup"]
|
cgroup_path = "/sys/fs/cgroup"
|
||||||
|
|
||||||
p = sp.run(
|
if not os.path.ismount(cgroup_path):
|
||||||
stat_command,
|
logger.debug(f"{cgroup_path} is not a mount point.")
|
||||||
encoding="ascii",
|
return "unknown"
|
||||||
capture_output=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
if p.returncode == 0:
|
try:
|
||||||
value: str = p.stdout.strip().lower()
|
with open("/proc/mounts", "r") as f:
|
||||||
|
mounts = f.readlines()
|
||||||
|
|
||||||
if value == "cgroup2fs":
|
for mount in mounts:
|
||||||
|
mount_info = mount.split()
|
||||||
|
if mount_info[1] == cgroup_path:
|
||||||
|
fs_type = mount_info[2]
|
||||||
|
if fs_type == "cgroup2fs" or fs_type == "cgroup2":
|
||||||
return "cgroup2"
|
return "cgroup2"
|
||||||
elif value == "tmpfs":
|
elif fs_type == "tmpfs":
|
||||||
return "cgroup"
|
return "cgroup"
|
||||||
else:
|
else:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"Could not determine cgroups version: unhandled filesystem {value}"
|
f"Could not determine cgroups version: unhandled filesystem {fs_type}"
|
||||||
)
|
)
|
||||||
else:
|
break
|
||||||
logger.debug(f"Could not determine cgroups version: {p.stderr}")
|
except Exception as e:
|
||||||
|
logger.debug(f"Could not determine cgroups version: {e}")
|
||||||
|
|
||||||
return "unknown"
|
return "unknown"
|
||||||
|
|
||||||
|
|
||||||
def get_docker_memlimit_bytes() -> int:
|
def get_docker_memlimit_bytes() -> int:
|
||||||
"""Get mem limit in bytes set in docker if present. Returns -1 if no limit detected"""
|
"""Get mem limit in bytes set in docker if present. Returns -1 if no limit detected."""
|
||||||
|
|
||||||
# check running a supported cgroups version
|
# check running a supported cgroups version
|
||||||
if get_cgroups_version() == "cgroup2":
|
if get_cgroups_version() == "cgroup2":
|
||||||
memlimit_command = ["cat", "/sys/fs/cgroup/memory.max"]
|
memlimit_path = "/sys/fs/cgroup/memory.max"
|
||||||
|
|
||||||
p = sp.run(
|
try:
|
||||||
memlimit_command,
|
with open(memlimit_path, "r") as f:
|
||||||
encoding="ascii",
|
value = f.read().strip()
|
||||||
capture_output=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
if p.returncode == 0:
|
|
||||||
value: str = p.stdout.strip()
|
|
||||||
|
|
||||||
if value.isnumeric():
|
if value.isnumeric():
|
||||||
return int(value)
|
return int(value)
|
||||||
elif value.lower() == "max":
|
elif value.lower() == "max":
|
||||||
return -1
|
return -1
|
||||||
else:
|
except Exception as e:
|
||||||
logger.debug(f"Unable to get docker memlimit: {p.stderr}")
|
logger.debug(f"Unable to get docker memlimit: {e}")
|
||||||
|
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
@ -796,44 +796,36 @@ def get_docker_memlimit_bytes() -> int:
|
|||||||
def get_cpu_stats() -> dict[str, dict]:
|
def get_cpu_stats() -> dict[str, dict]:
|
||||||
"""Get cpu usages for each process id"""
|
"""Get cpu usages for each process id"""
|
||||||
usages = {}
|
usages = {}
|
||||||
# -n=2 runs to ensure extraneous values are not included
|
|
||||||
top_command = ["top", "-b", "-n", "2"]
|
|
||||||
|
|
||||||
docker_memlimit = get_docker_memlimit_bytes() / 1024
|
docker_memlimit = get_docker_memlimit_bytes() / 1024
|
||||||
|
total_mem = os.sysconf("SC_PAGE_SIZE") * os.sysconf("SC_PHYS_PAGES") / 1024
|
||||||
|
|
||||||
p = sp.run(
|
for pid in os.listdir("/proc"):
|
||||||
top_command,
|
if pid.isdigit():
|
||||||
encoding="ascii",
|
|
||||||
capture_output=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
if p.returncode != 0:
|
|
||||||
logger.error(p.stderr)
|
|
||||||
return usages
|
|
||||||
else:
|
|
||||||
lines = p.stdout.split("\n")
|
|
||||||
|
|
||||||
for line in lines:
|
|
||||||
stats = list(filter(lambda a: a != "", line.strip().split(" ")))
|
|
||||||
try:
|
try:
|
||||||
|
with open(f"/proc/{pid}/stat", "r") as f:
|
||||||
|
stats = f.readline().split()
|
||||||
|
utime = int(stats[13])
|
||||||
|
stime = int(stats[14])
|
||||||
|
cpu_usage = round((utime + stime) / os.sysconf("SC_CLK_TCK"))
|
||||||
|
|
||||||
|
with open(f"/proc/{pid}/statm", "r") as f:
|
||||||
|
mem_stats = f.readline().split()
|
||||||
|
mem_res = int(mem_stats[1]) * os.sysconf("SC_PAGE_SIZE") / 1024
|
||||||
|
|
||||||
if docker_memlimit > 0:
|
if docker_memlimit > 0:
|
||||||
mem_res = int(stats[5])
|
mem_pct = round((mem_res / docker_memlimit) * 100, 1)
|
||||||
mem_pct = str(
|
|
||||||
round((float(mem_res) / float(docker_memlimit)) * 100, 1)
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
mem_pct = stats[9]
|
mem_pct = round((mem_res / total_mem) * 100, 1)
|
||||||
|
|
||||||
idx = stats[0]
|
idx = pid
|
||||||
|
if stats[1] == "(go2rtc)":
|
||||||
if stats[-1] == "go2rtc":
|
|
||||||
idx = "go2rtc"
|
idx = "go2rtc"
|
||||||
elif stats[-1] == "frigate.r+":
|
if stats[1].startswith("(frigate.r"):
|
||||||
idx = "recording"
|
idx = "recording"
|
||||||
|
|
||||||
usages[idx] = {
|
usages[idx] = {
|
||||||
"cpu": stats[8],
|
"cpu": str(round(cpu_usage, 2)),
|
||||||
"mem": mem_pct,
|
"mem": f"{mem_pct}",
|
||||||
}
|
}
|
||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
|
Loading…
Reference in New Issue
Block a user