configure collectd
This commit is contained in:
43
collectd/Dockerfile
Normal file
43
collectd/Dockerfile
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
FROM debian:bookworm
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
ENV TIMEZONE=Europe/Amsterdam
|
||||||
|
|
||||||
|
RUN apt update --fix-missing
|
||||||
|
RUN apt dist-upgrade -y --no-install-recommends
|
||||||
|
|
||||||
|
RUN echo $TIMEZONE > /etc/timezone
|
||||||
|
RUN dpkg-reconfigure -f noninteractive tzdata
|
||||||
|
|
||||||
|
RUN apt install -y libsensors5 liblzo2-2 collectd btrfs-progs libatasmart4 speedtest-cli
|
||||||
|
|
||||||
|
RUN apt install -y smartmontools
|
||||||
|
|
||||||
|
RUN apt install -y wget git
|
||||||
|
|
||||||
|
ENV HDDTEMP_VERSION=0.3.1
|
||||||
|
RUN wget https://github.com/slowpeek/hddtemp/archive/refs/tags/${HDDTEMP_VERSION}.tar.gz \
|
||||||
|
&& tar xvf ${HDDTEMP_VERSION}.tar.gz && mv hddtemp-${HDDTEMP_VERSION}/hddtemp-lt /usr/sbin/hddtemp
|
||||||
|
|
||||||
|
RUN apt -y install make g++ python3 python3-dev python3-pybind11 cmake
|
||||||
|
|
||||||
|
COPY PMT /pmt
|
||||||
|
#RUN git clone https://git.astron.nl:/RD/pmt
|
||||||
|
RUN cmake -Spmt -Bpmt/build -DPMT_BUILD_RAPL=1 -DPMT_BUILD_BINARY=1 -DPMT_BUILD_PYTHON=1 -DCMAKE_INSTALL_PREFIX=/opt/pmt
|
||||||
|
RUN make -Cpmt/build -j install
|
||||||
|
ENV LD_LIBRARY_PATH="/opt/pmt/lib"
|
||||||
|
ENV PYTHONPATH="/opt/pmt/lib/python3.11/site-packages"
|
||||||
|
ENV PATH="$PATH:/opt/pmt/bin"
|
||||||
|
RUN apt -y remove make g++ python3-dev python3-pybind11 cmake
|
||||||
|
RUN apt autoremove -y
|
||||||
|
|
||||||
|
RUN apt -y install sudo
|
||||||
|
RUN adduser collectd
|
||||||
|
RUN usermod -aG sudo collectd
|
||||||
|
RUN echo 'collectd ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/collectd
|
||||||
|
|
||||||
|
|
||||||
|
RUN apt install -y --no-install-recommends python3-pip
|
||||||
|
RUN pip install --break-system-packages requests
|
||||||
|
|
||||||
|
CMD ["/usr/sbin/collectd", "-f"]
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
FROM debian:bookworm
|
|
||||||
|
|
||||||
ENV LC_ALL=C
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
|
||||||
ENV TIMEZONE=Europe/Amsterdam
|
|
||||||
|
|
||||||
RUN apt update
|
|
||||||
RUN apt dist-upgrade -y --no-install-recommends
|
|
||||||
|
|
||||||
RUN echo $TIMEZONE > /etc/timezone
|
|
||||||
RUN dpkg-reconfigure -f noninteractive tzdata
|
|
||||||
|
|
||||||
RUN apt install -y libsensors5 liblzo2-2 collectd sudo btrfs-progs libatasmart4 speedtest-cli
|
|
||||||
|
|
||||||
RUN apt install -y smartmontools
|
|
||||||
|
|
||||||
RUN apt install -y wget git
|
|
||||||
|
|
||||||
ENV HDDTEMP_VERSION=0.2.4
|
|
||||||
RUN wget https://github.com/slowpeek/hddtemp/archive/refs/tags/${HDDTEMP_VERSION}.tar.gz \
|
|
||||||
&& tar xvf ${HDDTEMP_VERSION}.tar.gz && mv hddtemp-${HDDTEMP_VERSION}/hddtemp-lt /usr/sbin/hddtemp
|
|
||||||
|
|
||||||
RUN apt install -y gcc python3-dev make
|
|
||||||
RUN git clone https://github.com/RRZE-HPC/likwid.git
|
|
||||||
RUN cd likwid && make -j && make install
|
|
||||||
RUN git clone https://github.com/RRZE-HPC/pylikwid.git
|
|
||||||
RUN cd pylikwid && python3 setup.py build_ext && python3 setup.py install
|
|
||||||
RUN apt remove -y gcc python-dev make
|
|
||||||
RUN apt autoremove -y
|
|
||||||
|
|
||||||
RUN useradd collectd
|
|
||||||
RUN usermod -aG sudo collectd
|
|
||||||
RUN echo 'collectd ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers.d/collectd
|
|
||||||
|
|
||||||
CMD /usr/sbin/collectd -f
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
FROM debian:buster
|
|
||||||
|
|
||||||
ENV LC_ALL=C
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
|
||||||
ENV TIMEZONE=Europe/Amsterdam
|
|
||||||
|
|
||||||
RUN apt update
|
|
||||||
RUN apt dist-upgrade -y --no-install-recommends
|
|
||||||
|
|
||||||
RUN echo $TIMEZONE > /etc/timezone
|
|
||||||
RUN dpkg-reconfigure -f noninteractive tzdata
|
|
||||||
|
|
||||||
RUN apt install -y software-properties-common gpgv dirmngr psmisc wget curl python3-pip git gawk zip gperf unzip lbzip2 inetutils-ping inetutils-telnet rsync
|
|
||||||
|
|
||||||
RUN pip3 install argparse
|
|
||||||
|
|
||||||
RUN apt install -y libsensors5 liblzo2-2 hddtemp collectd sudo btrfs-progs libatasmart4 speedtest-cli
|
|
||||||
|
|
||||||
RUN useradd collectd
|
|
||||||
RUN usermod -aG sudo collectd
|
|
||||||
RUN echo 'collectd ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers.d/collectd
|
|
||||||
|
|
||||||
RUN apt install -y gcc python-dev make
|
|
||||||
RUN git clone https://github.com/RRZE-HPC/likwid.git
|
|
||||||
RUN cd likwid && make -j && make install
|
|
||||||
RUN git clone https://github.com/RRZE-HPC/pylikwid.git
|
|
||||||
RUN cd pylikwid && python setup.py build_ext && python setup.py install
|
|
||||||
|
|
||||||
ENV LD_LIBRARY_PATH /usr/local/lib
|
|
||||||
|
|
||||||
CMD /usr/sbin/collectd -f
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
|||||||
LoadPlugin exec
|
LoadPlugin exec
|
||||||
|
|
||||||
<Plugin "exec">
|
<Plugin "exec">
|
||||||
Exec collectd "/host/usr/local/bin/btrfs-data"
|
Exec collectd "/host/usr/local/bin/btrfs-data"
|
||||||
</Plugin>
|
</Plugin>
|
||||||
|
|
||||||
|
|||||||
5
collectd/etc/collectd.conf.d/cpufreq-data.conf
Normal file
5
collectd/etc/collectd.conf.d/cpufreq-data.conf
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
LoadPlugin exec
|
||||||
|
|
||||||
|
<Plugin "exec">
|
||||||
|
Exec collectd "/host/usr/local/bin/cpufreq-data"
|
||||||
|
</Plugin>
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
<Plugin "df">
|
<Plugin df>
|
||||||
MountPoint "/mnt/due"
|
MountPoint "/media/docker"
|
||||||
MountPoint "/mnt/quattro"
|
FSType "ext4"
|
||||||
MountPoint "/mnt/mezzo/var/lib/docker"
|
IgnoreSelected false
|
||||||
</Plugin>
|
</Plugin>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
LoadPlugin exec
|
LoadPlugin exec
|
||||||
|
|
||||||
<Plugin "exec">
|
<Plugin "exec">
|
||||||
Exec collectd "/host/usr/local/bin/du-data"
|
Exec collectd "/host/usr/local/bin/du-data"
|
||||||
</Plugin>
|
</Plugin>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
LoadPlugin exec
|
LoadPlugin exec
|
||||||
|
|
||||||
<Plugin "exec">
|
<Plugin "exec">
|
||||||
Exec nobody "/host/usr/local/bin/speedtest-data"
|
Exec nobody "/host/usr/local/bin/speedtest-data"
|
||||||
</Plugin>
|
</Plugin>
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
#!/usr/bin/python -u
|
#!/usr/bin/python3
|
||||||
|
|
||||||
#
|
#
|
||||||
# Imports
|
# Imports
|
||||||
#
|
#
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import commands
|
import subprocess
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
|
|
||||||
|
#sys.exit(1)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Misc
|
# Misc
|
||||||
#
|
#
|
||||||
#sys.tracebacklimit = 0
|
# sys.tracebacklimit = 0
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -28,26 +30,27 @@ size_snapshot_exclusive = 0
|
|||||||
# Methods
|
# Methods
|
||||||
#
|
#
|
||||||
def get_subvol_list(path):
|
def get_subvol_list(path):
|
||||||
command = 'sudo btrfs subvolume list -t %s' % (path)
|
command = "sudo btrfs subvolume list -t %s" % (path)
|
||||||
status, output = commands.getstatusoutput(command)
|
status, output = subprocess.getstatusoutput(command)
|
||||||
|
|
||||||
if status is not 0:
|
if status != 0:
|
||||||
raise Exception(command)
|
raise Exception(command)
|
||||||
|
|
||||||
# Every line contains the following values: subvol_id, gen, toplevel, path
|
# Every line contains the following values: subvol_id, gen, toplevel, path
|
||||||
return output.splitlines()[2:]
|
return output.splitlines()[2:]
|
||||||
|
|
||||||
def get_filesystem_size(path):
|
|
||||||
command = 'sudo btrfs filesystem show --raw %s' % (path)
|
|
||||||
status, output = commands.getstatusoutput(command)
|
|
||||||
|
|
||||||
if status is not 0 or True:
|
def get_filesystem_size(path):
|
||||||
|
command = "sudo btrfs filesystem show --raw %s" % (path)
|
||||||
|
status, output = subprocess.getstatusoutput(command)
|
||||||
|
|
||||||
|
if status != 0:
|
||||||
# This command fails when running inside Docker container
|
# This command fails when running inside Docker container
|
||||||
# return maximum size of any filesystem instead
|
# return maximum size of any filesystem instead
|
||||||
command = 'sudo btrfs filesystem show --raw'
|
command = "sudo btrfs filesystem show --raw"
|
||||||
status, output = commands.getstatusoutput(command)
|
status, output = subprocess.getstatusoutput(command)
|
||||||
lines = output.splitlines()
|
lines = output.splitlines()
|
||||||
lines = filter(lambda x: 'devid' in x, lines)
|
lines = [x for x in lines if "devid" in x]
|
||||||
sizes = [int(line.split()[3]) for line in lines]
|
sizes = [int(line.split()[3]) for line in lines]
|
||||||
return max(sizes)
|
return max(sizes)
|
||||||
|
|
||||||
@@ -57,11 +60,12 @@ def get_filesystem_size(path):
|
|||||||
# Element 3 and 5 respectively contain total and used sizes
|
# Element 3 and 5 respectively contain total and used sizes
|
||||||
return int(line.split()[3])
|
return int(line.split()[3])
|
||||||
|
|
||||||
|
|
||||||
def get_id_root(name, path):
|
def get_id_root(name, path):
|
||||||
lines = get_subvol_list(path)
|
lines = get_subvol_list(path)
|
||||||
|
|
||||||
# Filter lines where toplevel == 5
|
# Filter lines where toplevel == 5
|
||||||
subvol_ids = filter(lambda x: int(x.split()[2]) == 5, lines)
|
subvol_ids = [x for x in lines if int(x.split()[2]) == 5]
|
||||||
|
|
||||||
# Try to retrieve the subvol_id for the root subvolume (if any)
|
# Try to retrieve the subvol_id for the root subvolume (if any)
|
||||||
if len(subvol_ids) == 1:
|
if len(subvol_ids) == 1:
|
||||||
@@ -70,18 +74,18 @@ def get_id_root(name, path):
|
|||||||
else:
|
else:
|
||||||
# The path contains a btrfs filesystem with multiple subvolumes for data
|
# The path contains a btrfs filesystem with multiple subvolumes for data
|
||||||
try:
|
try:
|
||||||
return int(filter(lambda x: x.split()[3] == name, subvol_ids)[0].split()[0])
|
return int(list(filter(lambda x: x.split()[3] == name, subvol_ids))[0].split()[0])
|
||||||
except IndexError:
|
except IndexError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# Volume not found, root is probably the btrfs default (5)
|
# Volume not found, root is probably the btrfs default (5)
|
||||||
return 5
|
return 5
|
||||||
|
|
||||||
|
|
||||||
def get_id_subvolumes(path, subvol_id):
|
def get_id_subvolumes(path, subvol_id):
|
||||||
lines = get_subvol_list(path)
|
lines = get_subvol_list(path)
|
||||||
lines = filter(lambda x: int(x.split()[2]) == subvol_id, lines)
|
lines = [x for x in lines if int(x.split()[2]) == subvol_id]
|
||||||
return list(map(lambda x: int(x.split()[0]), lines))
|
return list([int(x.split()[0]) for x in lines])
|
||||||
|
|
||||||
|
|
||||||
def get_disk_usage(name, path):
|
def get_disk_usage(name, path):
|
||||||
@@ -90,10 +94,10 @@ def get_disk_usage(name, path):
|
|||||||
size_filesystem = get_filesystem_size(path)
|
size_filesystem = get_filesystem_size(path)
|
||||||
|
|
||||||
# Get disk usage from quota
|
# Get disk usage from quota
|
||||||
command = 'sudo btrfs qgroup show --raw %s' % (path)
|
command = "sudo btrfs qgroup show --raw %s" % (path)
|
||||||
status, output = commands.getstatusoutput(command)
|
status, output = subprocess.getstatusoutput(command)
|
||||||
|
|
||||||
if status is not 0:
|
if status != 0:
|
||||||
raise Exception(command)
|
raise Exception(command)
|
||||||
|
|
||||||
lines = output.splitlines()[2:]
|
lines = output.splitlines()[2:]
|
||||||
@@ -125,11 +129,11 @@ def get_disk_usage(name, path):
|
|||||||
size_total = 0
|
size_total = 0
|
||||||
size_exclusive = 0
|
size_exclusive = 0
|
||||||
try:
|
try:
|
||||||
subvol_id = int(split[0].split('/')[1])
|
subvol_id = int(split[0].split("/")[1])
|
||||||
size_total = float(split[1])
|
size_total = float(split[1])
|
||||||
size_exclusive = float(split[2])
|
size_exclusive = float(split[2])
|
||||||
except IndexError:
|
except IndexError:
|
||||||
# ignore 'WARNING: Quota disabled'
|
# ignore "WARNING: Quota disabled"
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# size_exclusive is incorrect when snapshot is
|
# size_exclusive is incorrect when snapshot is
|
||||||
@@ -145,51 +149,83 @@ def get_disk_usage(name, path):
|
|||||||
size_snapshot_total += size_total
|
size_snapshot_total += size_total
|
||||||
size_snapshot_exclusive += size_exclusive
|
size_snapshot_exclusive += size_exclusive
|
||||||
|
|
||||||
|
|
||||||
def rescan_quota(path):
|
def rescan_quota(path):
|
||||||
command = 'sudo btrfs quota rescan %s' % (path)
|
command = "sudo btrfs quota rescan %s" % (path)
|
||||||
status, output = commands.getstatusoutput(command)
|
status, output = subprocess.getstatusoutput(command)
|
||||||
if status is not 0:
|
if status != 0:
|
||||||
Exception(command)
|
Exception(command)
|
||||||
|
|
||||||
|
|
||||||
def print_human_readable(name):
|
def print_human_readable(name):
|
||||||
global size_data_total
|
global size_data_total
|
||||||
global size_data_exclusive
|
global size_data_exclusive
|
||||||
global size_snapshot_exclusive
|
global size_snapshot_exclusive
|
||||||
size_data_total = size_data_total / (1024*1e6)
|
size_data_total = size_data_total / (1024 * 1e6)
|
||||||
size_data_exclusive = size_data_exclusive / (1024*1e6)
|
size_data_exclusive = size_data_exclusive / (1024 * 1e6)
|
||||||
size_snapshot_exclusive = size_snapshot_exclusive / (1024*1e6)
|
size_snapshot_exclusive = size_snapshot_exclusive / (1024 * 1e6)
|
||||||
print '%10s: %6.1f Gb, %6.1f Gb, %6.1f Gb' % (name, size_data_total, size_data_exclusive, size_snapshot_exclusive)
|
print(
|
||||||
|
"%10s: %6.1f Gb, %6.1f Gb, %6.1f Gb"
|
||||||
|
% (name, size_data_total, size_data_exclusive, size_snapshot_exclusive)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def print_rrd(name):
|
def print_rrd(name):
|
||||||
timestamp = int(time.time())
|
timestamp = int(time.time())
|
||||||
print('PUTVAL {}/exec-btrfs_{}/gauge-data_total {}:{:.1f}'.format(hostname, name, timestamp, size_data_total))
|
print(
|
||||||
print('PUTVAL {}/exec-btrfs_{}/gauge-data_exclusive {}:{:.1f}'.format(hostname, name, timestamp, size_data_exclusive))
|
(
|
||||||
print('PUTVAL {}/exec-btrfs_{}/gauge-snapshot_total {}:{:.1f}'.format(hostname, name, timestamp, size_snapshot_total))
|
"PUTVAL {}/exec-btrfs_{}/gauge-data_total {}:{:.1f}".format(
|
||||||
print('PUTVAL {}/exec-btrfs_{}/gauge-snapshot_exclusive {}:{:.1f}'.format(hostname, name, timestamp, size_snapshot_exclusive))
|
hostname, name, timestamp, size_data_total
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
(
|
||||||
|
"PUTVAL {}/exec-btrfs_{}/gauge-data_exclusive {}:{:.1f}".format(
|
||||||
|
hostname, name, timestamp, size_data_exclusive
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
(
|
||||||
|
"PUTVAL {}/exec-btrfs_{}/gauge-snapshot_total {}:{:.1f}".format(
|
||||||
|
hostname, name, timestamp, size_snapshot_total
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
(
|
||||||
|
"PUTVAL {}/exec-btrfs_{}/gauge-snapshot_exclusive {}:{:.1f}".format(
|
||||||
|
hostname, name, timestamp, size_snapshot_exclusive
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Volumes to scan
|
# Volumes to scan
|
||||||
#
|
#
|
||||||
hostname = 'server'
|
hostname = "shuttle"
|
||||||
interval = 10
|
interval = 10
|
||||||
volumes = [
|
volumes = list()
|
||||||
['mezzo-scratch', '/mnt/mezzo/scratch'],
|
|
||||||
['mezzo-sync', '/mnt/mezzo/sync'],
|
|
||||||
['helium-personal', '/mnt/yotta/helium/personal'],
|
|
||||||
['helium-shared', '/mnt/yotta/helium/shared'],
|
|
||||||
['neon', '/mnt/yotta/neon'],
|
|
||||||
['krypton', '/mnt/yotta/krypton'],
|
|
||||||
['xenon-borg', '/mnt/yotta/xenon/borg'],
|
|
||||||
['xenon-rsnapshot', '/mnt/yotta/xenon/rsnapshot']
|
|
||||||
]
|
|
||||||
|
|
||||||
|
# SSD
|
||||||
|
volumes.append(["scratch", "/host/root/mnt/mezzo/scratch"])
|
||||||
|
volumes.append(["sync", "/host/root/mnt/mezzo/sync"])
|
||||||
|
|
||||||
|
# HDD
|
||||||
|
volumes.append(["personal", "/host/root/mnt/yotta/helium/personal"])
|
||||||
|
volumes.append(["shared", "/host/root/mnt/yotta/helium/shared"])
|
||||||
|
volumes.append(["neon", "/host/root/mnt/yotta/neon"])
|
||||||
|
volumes.append(["krypton", "/host/root/mnt/yotta/krypton"])
|
||||||
|
volumes.append(["borg", "/host/root/mnt/yotta/xenon/borg"])
|
||||||
|
volumes.append(["rsnapshot", "/host/root/mnt/yotta/xenon/rsnapshot"])
|
||||||
|
|
||||||
#
|
#
|
||||||
# Command line arguments
|
# Command line arguments
|
||||||
#
|
#
|
||||||
parser = argparse.ArgumentParser(description='Get BTRFS disk usage')
|
parser = argparse.ArgumentParser(description="Get BTRFS disk usage")
|
||||||
parser.add_argument('-s', action='store_true', help='print in human readable format')
|
parser.add_argument("-s", action="store_true", help="print in human readable format")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
human_readable = args.s
|
human_readable = args.s
|
||||||
|
|
||||||
@@ -198,15 +234,17 @@ human_readable = args.s
|
|||||||
# Main
|
# Main
|
||||||
#
|
#
|
||||||
if human_readable:
|
if human_readable:
|
||||||
for name, path in volumes:
|
for (name, path) in volumes:
|
||||||
get_disk_usage(name, path)
|
get_disk_usage(name, path)
|
||||||
print_human_readable(name)
|
print_human_readable(name)
|
||||||
else:
|
else:
|
||||||
# RRD mode
|
# RRD mode
|
||||||
while True:
|
while True:
|
||||||
for name, path in volumes:
|
for (name, path) in volumes:
|
||||||
get_disk_usage(name, path)
|
get_disk_usage(name, path)
|
||||||
|
|
||||||
print_rrd(name)
|
print_rrd(name)
|
||||||
|
|
||||||
|
sys.stdout.flush()
|
||||||
time.sleep(interval)
|
time.sleep(interval)
|
||||||
rescan_quota(path)
|
# rescan_quota(path)
|
||||||
|
|||||||
58
collectd/usr/local/bin/cpufreq-data
Executable file
58
collectd/usr/local/bin/cpufreq-data
Executable file
@@ -0,0 +1,58 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
hostname = "shuttle"
|
||||||
|
measurement_interval = 5
|
||||||
|
|
||||||
|
|
||||||
|
def get_cpu_frequencies():
|
||||||
|
frequencies = []
|
||||||
|
try:
|
||||||
|
cpu_dirs = [
|
||||||
|
d
|
||||||
|
for d in os.listdir("/sys/devices/system/cpu/")
|
||||||
|
if d.startswith("cpu") and d[3:].isdigit()
|
||||||
|
]
|
||||||
|
for cpu_dir in cpu_dirs:
|
||||||
|
with open(
|
||||||
|
f"/sys/devices/system/cpu/{cpu_dir}/cpufreq/scaling_cur_freq", "r"
|
||||||
|
) as f:
|
||||||
|
frequency = int(f.read().strip()) / 1000 # Convert Hz to MHz
|
||||||
|
frequencies.append((int(cpu_dir[3:]), frequency))
|
||||||
|
except Exception as e:
|
||||||
|
print("Error:", e)
|
||||||
|
return frequencies
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Query CPU frequencies.")
|
||||||
|
parser.add_argument(
|
||||||
|
"-s",
|
||||||
|
"--human-readable",
|
||||||
|
action="store_true",
|
||||||
|
help="Print frequencies in human-readable format",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.human_readable:
|
||||||
|
frequencies = get_cpu_frequencies()
|
||||||
|
for cpu, frequency in frequencies:
|
||||||
|
print(f"CPU{cpu} Frequency: {frequency:.2f} MHz")
|
||||||
|
else:
|
||||||
|
while True:
|
||||||
|
frequencies = get_cpu_frequencies()
|
||||||
|
timestamp = int(time.time())
|
||||||
|
for cpu, frequency in frequencies:
|
||||||
|
print(
|
||||||
|
f"PUTVAL {hostname}/cpu-frequency/gauge-cpu{cpu} {timestamp}:{frequency:.0f}"
|
||||||
|
)
|
||||||
|
sys.stdout.flush()
|
||||||
|
time.sleep(measurement_interval)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python3
|
||||||
|
|
||||||
#
|
#
|
||||||
# Imports
|
# Imports
|
||||||
#
|
#
|
||||||
import os
|
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import commands
|
import subprocess
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
|
|
||||||
@@ -14,13 +13,12 @@ import argparse
|
|||||||
# Methods
|
# Methods
|
||||||
#
|
#
|
||||||
def get_disk_usage(path, human_readable):
|
def get_disk_usage(path, human_readable):
|
||||||
'''disk usage in human readable format (e.g. '2,1GB')'''
|
"""disk usage in human readable format (e.g. '2,1GB')"""
|
||||||
arguments = '-sh' if human_readable else '-s'
|
arguments = "-sh" if human_readable else "-s"
|
||||||
path = os.path.realpath(path)
|
command = "du %s %s" % (arguments, path)
|
||||||
command = 'sudo du %s %s' % (arguments, path)
|
status, output = subprocess.getstatusoutput(command)
|
||||||
status, output = commands.getstatusoutput(command)
|
|
||||||
|
|
||||||
if status is not 0:
|
if status != 0:
|
||||||
raise Exception(command)
|
raise Exception(command)
|
||||||
|
|
||||||
disk_usage = output.split()[0]
|
disk_usage = output.split()[0]
|
||||||
@@ -29,13 +27,13 @@ def get_disk_usage(path, human_readable):
|
|||||||
disk_usage = int(disk_usage) * 1024
|
disk_usage = int(disk_usage) * 1024
|
||||||
return disk_usage
|
return disk_usage
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Directories to scan
|
# Directories to scan
|
||||||
#
|
#
|
||||||
hostname = 'server'
|
hostname = 'shuttle'
|
||||||
interval = 10
|
interval = 10
|
||||||
directories = [
|
directories = [
|
||||||
#['bram', '/media/data/Personal/Bram'],
|
|
||||||
['rik', '/media/data/Personal/Rik'],
|
['rik', '/media/data/Personal/Rik'],
|
||||||
['books', '/media/data/Shared/Books'],
|
['books', '/media/data/Shared/Books'],
|
||||||
['games', '/media/data/Shared/Games'],
|
['games', '/media/data/Shared/Games'],
|
||||||
@@ -45,15 +43,14 @@ directories = [
|
|||||||
['music', '/media/data/Shared/Music'],
|
['music', '/media/data/Shared/Music'],
|
||||||
['photographs', '/media/data/Shared/Photographs'],
|
['photographs', '/media/data/Shared/Photographs'],
|
||||||
['pictures', '/media/data/Shared/Pictures'],
|
['pictures', '/media/data/Shared/Pictures'],
|
||||||
['raw', '/media/data/Shared/Raw'],
|
|
||||||
['software', '/media/data/Shared/Software']
|
['software', '/media/data/Shared/Software']
|
||||||
]
|
]
|
||||||
|
|
||||||
#
|
#
|
||||||
# Command line arguments
|
# Command line arguments
|
||||||
#
|
#
|
||||||
parser = argparse.ArgumentParser(description='Get BTRFS disk usage')
|
parser = argparse.ArgumentParser(description="Get DU disk usage")
|
||||||
parser.add_argument('-s', action='store_true', help='print in human readable format')
|
parser.add_argument("-s", action="store_true", help="print in human readable format")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
human_readable = args.s
|
human_readable = args.s
|
||||||
|
|
||||||
@@ -61,10 +58,10 @@ human_readable = args.s
|
|||||||
#
|
#
|
||||||
# Main
|
# Main
|
||||||
#
|
#
|
||||||
if (human_readable):
|
if human_readable:
|
||||||
for (name, path) in directories:
|
for (name, path) in directories:
|
||||||
disk_usage = get_disk_usage(path, human_readable)
|
disk_usage = get_disk_usage(path, human_readable)
|
||||||
print('%s: %s' % (name, disk_usage))
|
print(("%s: %s" % (name, disk_usage)))
|
||||||
else:
|
else:
|
||||||
# RRD mode
|
# RRD mode
|
||||||
while True:
|
while True:
|
||||||
@@ -72,6 +69,12 @@ else:
|
|||||||
disk_usage = get_disk_usage(path, human_readable)
|
disk_usage = get_disk_usage(path, human_readable)
|
||||||
timestamp = int(time.time())
|
timestamp = int(time.time())
|
||||||
size = float(disk_usage)
|
size = float(disk_usage)
|
||||||
print('PUTVAL {}/exec-du_{}/gauge-size {}:{:.1f}'.format(hostname, name, timestamp, size))
|
print(
|
||||||
|
(
|
||||||
|
"PUTVAL {}/exec-du_{}/gauge-size {}:{:.1f}".format(
|
||||||
|
hostname, name, timestamp, size
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
time.sleep(interval)
|
time.sleep(interval)
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import sys
|
||||||
import os
|
import os
|
||||||
import requests
|
import requests
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
import datetime
|
import datetime
|
||||||
import sys
|
|
||||||
import random
|
import random
|
||||||
|
|
||||||
collection = 'funds'
|
collection = 'funds'
|
||||||
@@ -55,7 +56,7 @@ def morningstar_ticker(funds):
|
|||||||
def put_value(fund, value, timestamp = 'N'):
|
def put_value(fund, value, timestamp = 'N'):
|
||||||
print('PUTVAL {}/exec-fund-{}/gauge-ticker interval={} {}:{}'.format(collection, fund, interval, timestamp, value))
|
print('PUTVAL {}/exec-fund-{}/gauge-ticker interval={} {}:{}'.format(collection, fund, interval, timestamp, value))
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
if timestamp is 'N':
|
if timestamp == 'N':
|
||||||
timestamp = int(time.time())
|
timestamp = int(time.time())
|
||||||
# log.write('{},{},{}\n'.format(fund, timestamp, int(value)))
|
# log.write('{},{},{}\n'.format(fund, timestamp, int(value)))
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python3
|
||||||
|
|
||||||
#
|
#
|
||||||
# Imports
|
# Imports
|
||||||
@@ -6,61 +6,58 @@
|
|||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import argparse
|
import argparse
|
||||||
import pylikwid
|
import pmt
|
||||||
|
|
||||||
#
|
#
|
||||||
# Configuration
|
# Configuration
|
||||||
#
|
#
|
||||||
hostname = 'server'
|
hostname = "shuttle"
|
||||||
cpuid = 0
|
measurement_duration = 5
|
||||||
pinfo = pylikwid.getpowerinfo()
|
measurement_interval = 15
|
||||||
domainid = pinfo.get('domains').get('PKG').get('ID')
|
pm = pmt.create("rapl")
|
||||||
measurement_duration = 10
|
|
||||||
measurement_interval = 60
|
|
||||||
dinfo = pinfo.get('domains')
|
|
||||||
domain_names = dinfo.keys()
|
|
||||||
domain_ids = [domain['ID'] for domain in dinfo.values()]
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Command line arguments
|
# Command line arguments
|
||||||
#
|
#
|
||||||
parser = argparse.ArgumentParser(description='Get CPU power consumption')
|
parser = argparse.ArgumentParser(description="Get CPU power consumption")
|
||||||
parser.add_argument('-s', action='store_true', help='print in human readable format')
|
parser.add_argument("-s", action="store_true", help="print in human readable format")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
human_readable = args.s
|
human_readable = args.s
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Methods
|
# Methods
|
||||||
#
|
#
|
||||||
def get_power():
|
def get_power():
|
||||||
#print dict(zip(domain_names, domain_ids))
|
|
||||||
start = list()
|
|
||||||
end = list()
|
|
||||||
power = list()
|
|
||||||
for domain_id in domain_ids:
|
|
||||||
e_start = pylikwid.startpower(cpuid, domain_id)
|
|
||||||
start.append(e_start)
|
|
||||||
time.sleep(measurement_duration)
|
time.sleep(measurement_duration)
|
||||||
for domain_id in domain_ids:
|
measurements = dict()
|
||||||
e_stop = pylikwid.stoppower(cpuid, domain_id)
|
state = pm.read()
|
||||||
end.append(e_stop)
|
for i in range(state.nr_measurements()):
|
||||||
for events in zip(start, end, domain_ids):
|
name = state.name(i)
|
||||||
power.append(pylikwid.getpower(events[0], events[1], events[2]))
|
watts = state.watts(i)
|
||||||
|
measurements[name] = watts
|
||||||
|
return measurements
|
||||||
|
|
||||||
return dict(zip(domain_names, power))
|
|
||||||
|
|
||||||
def print_rrd(measurements):
|
def print_rrd(measurements):
|
||||||
timestamp = int(time.time())
|
timestamp = int(time.time())
|
||||||
for measurement in measurements.items():
|
for measurement in list(measurements.items()):
|
||||||
name = measurement[0].lower()
|
name = measurement[0].lower()
|
||||||
power = measurement[1]
|
power = measurement[1]
|
||||||
print('PUTVAL {}/exec-power/gauge-{} {}:{:.1f}'.format(hostname, name, timestamp, power))
|
print(
|
||||||
|
(
|
||||||
|
"PUTVAL {}/exec-power/gauge-{} {}:{:.1f}".format(
|
||||||
|
hostname, name, timestamp, power
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Main
|
# Main
|
||||||
#
|
#
|
||||||
if (human_readable):
|
if human_readable:
|
||||||
print get_power()
|
print(get_power())
|
||||||
else:
|
else:
|
||||||
while True:
|
while True:
|
||||||
power = get_power()
|
power = get_power()
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
SPEEDTEST=/sbin/speedtest-cli
|
|
||||||
COLLECTION=server
|
SPEEDTEST=/usr/bin/speedtest-cli
|
||||||
INTERVAL=1800
|
COLLECTION=shuttle
|
||||||
|
INTERVAL=900
|
||||||
|
|
||||||
while :; do
|
while :; do
|
||||||
SECONDS=0
|
SECONDS=0
|
||||||
RESULT=($($SPEEDTEST | grep Mbit | cut -d' ' -f 2))
|
RESULT=($($SPEEDTEST | grep Mbit | cut -d' ' -f 2))
|
||||||
echo "PUTVAL $COLLECTION/exec-speedtest/gauge-download interval=$INTERVAL N:${RESULT[0]}"
|
TIMESTAMP=$(date +%s)
|
||||||
echo "PUTVAL $COLLECTION/exec-speedtest/gauge-upload interval=$INTERVAL N:${RESULT[1]}"
|
#echo "PUTVAL $COLLECTION/exec-speedtest/gauge-download interval=$INTERVAL N:${RESULT[0]}"
|
||||||
|
#echo "PUTVAL $COLLECTION/exec-speedtest/gauge-upload interval=$INTERVAL N:${RESULT[1]}"
|
||||||
|
echo "PUTVAL $COLLECTION/exec-speedtest/gauge-download ${TIMESTAMP}:${RESULT[0]}"
|
||||||
|
echo "PUTVAL $COLLECTION/exec-speedtest/gauge-upload ${TIMESTAMP}:${RESULT[1]}"
|
||||||
sleep $((INTERVAL-$SECONDS))
|
sleep $((INTERVAL-$SECONDS))
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -1,23 +1,22 @@
|
|||||||
#!/usr/bin/python -u
|
#!/usr/bin/python3
|
||||||
|
|
||||||
#
|
#
|
||||||
# Imports
|
# Imports
|
||||||
#
|
#
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import commands
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import subprocess
|
||||||
|
|
||||||
#
|
#
|
||||||
# Methods
|
# Methods
|
||||||
#
|
#
|
||||||
def get_temperature(disks):
|
def get_temperature(disks):
|
||||||
command = "sudo smartctl -a /dev/%s | grep Temperature_Celsius | awk '{print $10}'" % disk
|
command = "sudo smartctl -a /dev/%s | grep Temperature_Celsius | awk '{print $10}'" % disk
|
||||||
status, output = commands.getstatusoutput(command)
|
result = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return int(output)
|
return int(result.stdout)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -25,9 +24,9 @@ def get_temperature(disks):
|
|||||||
#
|
#
|
||||||
# Settings
|
# Settings
|
||||||
#
|
#
|
||||||
hostname = 'server'
|
hostname = 'shuttle'
|
||||||
interval = 10
|
interval = 10
|
||||||
disks = ['sdd', 'sde', 'sdf']
|
disks = ['sda', 'sdb', 'sdc', 'sdd']
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -40,4 +39,3 @@ while True:
|
|||||||
if temperature:
|
if temperature:
|
||||||
print('PUTVAL {}/exec-temperature/gauge-{}_total {}:{}'.format(hostname, disk, timestamp, temperature))
|
print('PUTVAL {}/exec-temperature/gauge-{}_total {}:{}'.format(hostname, disk, timestamp, temperature))
|
||||||
time.sleep(interval)
|
time.sleep(interval)
|
||||||
|
|
||||||
|
|||||||
17
docker-compose.collectd.yaml
Normal file
17
docker-compose.collectd.yaml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
services:
|
||||||
|
collectd:
|
||||||
|
build:
|
||||||
|
context: /home/user/src
|
||||||
|
dockerfile: /opt/collectd/Dockerfile
|
||||||
|
container_name: collectd
|
||||||
|
image: collectd:bookworm
|
||||||
|
privileged: true
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- /opt/collectd/etc:/etc/collectd
|
||||||
|
- /opt/collectd/var:/var/lib/collectd
|
||||||
|
- /opt/collectd/usr:/host/usr
|
||||||
|
- /:/host/root:ro
|
||||||
|
- /media/data:/media/data:ro
|
||||||
|
- /var/lib/docker:/media/docker:ro
|
||||||
|
- /dev/mapper:/dev/mapper
|
||||||
@@ -6,6 +6,7 @@ include:
|
|||||||
- docker-compose.authentik.yaml
|
- docker-compose.authentik.yaml
|
||||||
|
|
||||||
# Other
|
# Other
|
||||||
|
- docker-compose.collectd.yaml
|
||||||
- docker-compose.homarr.yaml
|
- docker-compose.homarr.yaml
|
||||||
- docker-compose.homeassistant.yaml
|
- docker-compose.homeassistant.yaml
|
||||||
- docker-compose.grafana.yaml
|
- docker-compose.grafana.yaml
|
||||||
|
|||||||
Reference in New Issue
Block a user