Add meteocat exporter #103

Manually merged
rarias merged 2 commits from monitor-outside-temp into master 2025-05-29 13:07:27 +02:00
6 changed files with 110 additions and 0 deletions
Showing only changes of commit aae6585f66 - Show all commits

View File

@@ -3,6 +3,7 @@
{
imports = [
../module/slurm-exporter.nix
../module/meteocat-exporter.nix
./gpfs-probe.nix
./nix-daemon-exporter.nix
];
@@ -110,6 +111,7 @@
"127.0.0.1:9341" # Slurm exporter
"127.0.0.1:9966" # GPFS custom exporter
"127.0.0.1:9999" # Nix-daemon custom exporter
"127.0.0.1:9929" # Meteocat custom exporter
"127.0.0.1:${toString config.services.prometheus.exporters.blackbox.port}"
];
}];

View File

@@ -0,0 +1,17 @@
{ config, lib, pkgs, ... }:
with lib;
{
systemd.services."prometheus-meteocat-exporter" = {
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
Restart = mkDefault "always";
PrivateTmp = mkDefault true;
WorkingDirectory = mkDefault "/tmp";
DynamicUser = mkDefault true;
ExecStart = "${pkgs.meteocat-exporter}/bin/meteocat-exporter";
};
};
}

View File

@@ -0,0 +1,25 @@
{ python3Packages, lib }:
python3Packages.buildPythonApplication rec {
pname = "meteocat-exporter";
version = "1.0";
src = ./.;
doCheck = false;
build-system = with python3Packages; [
setuptools
];
dependencies = with python3Packages; [
beautifulsoup4
lxml
prometheus-client
];
meta = with lib; {
description = "MeteoCat Prometheus Exporter";
platforms = platforms.linux;
};
}

View File

@@ -0,0 +1,54 @@
#!/usr/bin/env python3
import time
from prometheus_client import start_http_server, Gauge
from bs4 import BeautifulSoup
from urllib import request
# Configuration -------------------------------------------
meteo_station = "X8" # Barcelona - Zona Universitària
listening_port = 9929
update_period = 60 * 5 # Each 5 min
# ---------------------------------------------------------
metric_tmin = Gauge('meteocat_temp_min', 'Min temperature')
metric_tmax = Gauge('meteocat_temp_max', 'Max temperature')
metric_tavg = Gauge('meteocat_temp_avg', 'Average temperature')
metric_srad = Gauge('meteocat_solar_radiation', 'Solar radiation')
def update(st):
url = 'https://www.meteo.cat/observacions/xema/dades?codi=' + st
response = request.urlopen(url)
data = response.read()
soup = BeautifulSoup(data, 'lxml')
table = soup.find("table", {"class" : "tblperiode"})
rows = table.find_all('tr')
row = rows[-1] # Take the last row
rarias marked this conversation as resolved Outdated

Seems that meteocat does not update the data in realtime (right now at 17:20 the last row is from 14:30). Also, the periods are of 30 minutes, so we will have duplicates.

Can we manually set the time of the gauge and check for duplicates?

Seems that meteocat does not update the data in realtime (right now at 17:20 the last row is from 14:30). Also, the periods are of 30 minutes, so we will have duplicates. Can we manually set the time of the gauge and check for duplicates?
  1. Meteocat uses UTC time, so you need to add 2 hours to the website times to convert to Spain time. It is mostly realtime.

  2. Meteocat samples the temp over 30 minutes, then computes the min/max/avg during ~5 minutes and then outputs the values on the table, so they have at most 40 min of delay as we retry every 5 minutes.

  3. There are no duplicates per-se. We update the gauge as soon as we discover a new value. But prometheus will query the metrics every minute, so it will continue to read the same values for 30 min at least. This is fine as they have a very effective mechanism to store duplicates that only takes around 1 bit of information (see https://www.vldb.org/pvldb/vol8/p1816-teller.pdf section 4.1.2).

1. Meteocat uses UTC time, so you need to add 2 hours to the website times to convert to Spain time. It is mostly realtime. 2. Meteocat samples the temp over 30 minutes, then computes the min/max/avg during ~5 minutes and then outputs the values on the table, so they have at most 40 min of delay as we retry every 5 minutes. 3. There are no duplicates per-se. We update the gauge as soon as we discover a new value. But prometheus will query the metrics every minute, so it will continue to read the same values for 30 min at least. This is fine as they have a very effective mechanism to store duplicates that only takes around 1 bit of information (see https://www.vldb.org/pvldb/vol8/p1816-teller.pdf section 4.1.2).
row_data = []
header = row.find('th')
header_text = header.text.strip()
row_data.append(header_text)
for col in row.find_all('td'):
row_data.append(col.text)
try:
# Sometimes it will return '(s/d)' and fail to parse
metric_tavg.set(float(row_data[1]))
metric_tmax.set(float(row_data[2]))
metric_tmin.set(float(row_data[3]))
metric_srad.set(float(row_data[10]))
#print("ok: temp_avg={}".format(float(row_data[1])))
except:
print("cannot parse row: {}".format(row))
metric_tavg.set(float("nan"))
metric_tmax.set(float("nan"))
metric_tmin.set(float("nan"))
metric_srad.set(float("nan"))
if __name__ == '__main__':
start_http_server(port=listening_port, addr="localhost")
while True:
try:
update(meteo_station)
except:
print("update failed")
time.sleep(update_period)

View File

@@ -0,0 +1,11 @@
#!/usr/bin/env python
from setuptools import setup, find_packages
setup(name='meteocat-exporter',
version='1.0',
# Modules to import from other scripts:
packages=find_packages(),
# Executables
scripts=["meteocat-exporter"],
)

View File

@@ -54,4 +54,5 @@ final: prev:
});
prometheus-slurm-exporter = prev.callPackage ./slurm-exporter.nix { };
meteocat-exporter = prev.callPackage ./meteocat-exporter/default.nix { };
}