summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Pointner <equinox@spreadspace.org>2021-12-31 00:44:15 +0100
committerChristian Pointner <equinox@spreadspace.org>2021-12-31 00:44:15 +0100
commit815b15412f7e42a8981087fdd3a1fda00c33b956 (patch)
tree03d5a8b4d258f7e094e6306479da0fe01f741a08
parentsensors: init is now idempotent (diff)
prometheus/node: add textfile collector sensors (WIP)
-rw-r--r--files/common/openwrt/sensors.module_lua54
-rw-r--r--inventory/host_vars/ch-phoebe.yml13
-rw-r--r--roles/monitoring/prometheus/exporter/node/defaults/main.yml36
-rw-r--r--roles/monitoring/prometheus/exporter/node/handlers/main.yml5
-rw-r--r--roles/monitoring/prometheus/exporter/node/tasks/textfile_collector_sensors.yml32
-rw-r--r--roles/monitoring/prometheus/exporter/node/templates/textfile-collector-scripts/sensors.j251
-rw-r--r--roles/monitoring/prometheus/exporter/node/templates/textfile-collector-scripts/sensors.service.j233
-rw-r--r--roles/monitoring/prometheus/exporter/node/templates/textfile-collector-scripts/sensors.timer.j29
8 files changed, 213 insertions, 20 deletions
diff --git a/files/common/openwrt/sensors.module_lua b/files/common/openwrt/sensors.module_lua
index 6c279188..d5d869d5 100644
--- a/files/common/openwrt/sensors.module_lua
+++ b/files/common/openwrt/sensors.module_lua
@@ -12,6 +12,12 @@ local _internal_ = {}
_internal_.setup = {}
_internal_.read = {}
+-- https://github.com/luaposix/luaposix/releases/tag/v34.1
+if glob.GLOB_NOCHECK == nil then
+ _internal_.glob = function(pattern, flags) return glob.glob(pattern) end
+else
+ _internal_.glob = glob.glob
+end
-- ############# utils ###########
@@ -26,15 +32,23 @@ function _internal_.write_to_file(path, data)
return ret, err
end
-function _internal_.read_from_file(path)
+function _internal_.read_from_file(path, what)
local f, err = io.open(path, "r")
if not f then return nil, err end
- local data, err = f:read("*a")
+ local data, err = f:read(what)
f:close()
return data, err
end
+function _internal_.read_line_from_file(path)
+ return _internal_.read_from_file(path, "*l")
+end
+
+function _internal_.read_whole_file(path)
+ return _internal_.read_from_file(path, "*a")
+end
+
-- ############# i2c ###########
@@ -67,8 +81,8 @@ function _M.i2c_add_or_replace_device(bus, address, name)
return true, nil
end
- local contents, _ = _internal_.read_from_file(_M.i2c_device_path(bus, address) .. "/name")
- if contents == nil or contents:gsub("%s+", "") ~= name then
+ local contents, _ = _internal_.read_line_from_file(_M.i2c_device_path(bus, address) .. "/name")
+ if contents == nil or contents ~= name then
local ret, err = _M.i2c_delete_device(bus, address)
if not ret then return nil, err end
@@ -83,7 +97,7 @@ function _M.i2c_add_or_replace_device(bus, address, name)
end
function _M.i2c_get_bus_number_from_name(name)
- local bus_paths, glob_result = glob.glob("/sys/bus/i2c/devices/i2c-*", 0)
+ local bus_paths, glob_result = _internal_.glob("/sys/bus/i2c/devices/i2c-*", 0)
if not bus_paths then
if glob_result == glob.GLOB_NOMATCH then return {} end
if glob_result == glob.GLOB_ABORTED then return nil, "glob(): aborted" end
@@ -95,14 +109,14 @@ function _M.i2c_get_bus_number_from_name(name)
local num = string.match(bus_path, '/i2c%-(%d+)$')
if not num then return nil, "unable to parse bus number from path: " .. bus_path end
- local contents, err = _internal_.read_from_file(bus_path .. "/name")
- if contents ~= nil and contents:gsub("%s+", "") == name then return num end
+ local contents, err = _internal_.read_line_from_file(bus_path .. "/name")
+ if contents ~= nil and contents == name then return num end
end
return nil, "unable to find i2c bus with name: " .. name
end
function _M.i2c_get_mux_channels(parent, address)
- local channel_paths, glob_result = glob.glob(_M.i2c_device_path(parent, address) .. "/channel-*", 0)
+ local channel_paths, glob_result = _internal_.glob(_M.i2c_device_path(parent, address) .. "/channel-*", 0)
if not channel_paths then
if glob_result == glob.GLOB_NOMATCH then return {} end
if glob_result == glob.GLOB_ABORTED then return nil, "glob(): aborted" end
@@ -281,7 +295,7 @@ function _M.gpio_setup_pin(number)
end
function _M.gpio_read_pin(number)
- local value, err = _internal_.read_from_file(_M.gpio_pin_path(number) .. "/value")
+ local value, err = _internal_.read_line_from_file(_M.gpio_pin_path(number) .. "/value")
if not value then return nil, err end
return value:match( "^%s*(.-)%s*$" )
end
@@ -318,7 +332,7 @@ end
-- ############# iio ###########
function _M.iio_device_path_from_i2c_device(bus, address)
- local device_paths, glob_result = glob.glob(_M.i2c_device_path(bus, address) .. "/iio:device*", 0)
+ local device_paths, glob_result = _internal_.glob(_M.i2c_device_path(bus, address) .. "/iio:device*", 0)
if not device_paths then
if glob_result == glob.GLOB_NOMATCH then return nil, "couldn't find iio device handle for i2c device " .. _M.i2c_device_name(bus, address) end
if glob_result == glob.GLOB_ABORTED then return nil, "glob(): aborted" end
@@ -348,12 +362,12 @@ function _M.iio_read_bmp280(bus, address)
if not iio_device then return end
local values = {}
- local tmp, err = _internal_.read_from_file(iio_device .. '/in_pressure_input')
+ local tmp, err = _internal_.read_line_from_file(iio_device .. '/in_pressure_input')
if tmp ~= nil then
local val = base.tonumber(tmp)
if val ~= nil then values['pressure'] = val*1000 end
end
- tmp, err = _internal_.read_from_file(iio_device .. '/in_temp_input')
+ tmp, err = _internal_.read_line_from_file(iio_device .. '/in_temp_input')
if tmp ~= nil then
local val = base.tonumber(tmp)
if val ~= nil then values['temperature'] = val/1000 end
@@ -384,17 +398,17 @@ function _M.iio_read_bme280(bus, address)
if not iio_device then return end
local values = {}
- local tmp, err = _internal_.read_from_file(iio_device .. '/in_humidityrelative_input')
+ local tmp, err = _internal_.read_line_from_file(iio_device .. '/in_humidityrelative_input')
if tmp ~= nil then
local val = base.tonumber(tmp)
if val ~= nil then values['humidity'] = val/1000 end
end
- local tmp, err = _internal_.read_from_file(iio_device .. '/in_pressure_input')
+ local tmp, err = _internal_.read_line_from_file(iio_device .. '/in_pressure_input')
if tmp ~= nil then
local val = base.tonumber(tmp)
if val ~= nil then values['pressure'] = val*1000 end
end
- tmp, err = _internal_.read_from_file(iio_device .. '/in_temp_input')
+ tmp, err = _internal_.read_line_from_file(iio_device .. '/in_temp_input')
if tmp ~= nil then
local val = base.tonumber(tmp)
if val ~= nil then values['temperature'] = val/1000 end
@@ -415,13 +429,13 @@ function _M.iio_read_am2315(bus, address)
if not iio_device then return end
local values = {}
- local tmp, err = _internal_.read_from_file(iio_device .. '/in_humidityrelative_raw')
+ local tmp, err = _internal_.read_line_from_file(iio_device .. '/in_humidityrelative_raw')
if tmp ~= nil then
local val = base.tonumber(tmp)
if val ~= nil then values['humidity'] = val/10 end
end
time.nanosleep({tv_sec = 0, tv_nsec = 100000000})
- tmp, err = _internal_.read_from_file(iio_device .. '/in_temp_raw')
+ tmp, err = _internal_.read_line_from_file(iio_device .. '/in_temp_raw')
if tmp ~= nil then
local val = base.tonumber(tmp)
if val ~= nil then values['temperature'] = val/10 end
@@ -436,7 +450,7 @@ _internal_.read['am2315'] = _M.iio_read_am2315
-- ############# hwmon ###########
function _M.hwmon_device_path_from_w1_device(address)
- local device_paths, glob_result = glob.glob(_M.w1_device_path(address) .. "/hwmon/hwmon*", 0)
+ local device_paths, glob_result = _internal_.glob(_M.w1_device_path(address) .. "/hwmon/hwmon*", 0)
if not device_paths then
if glob_result == glob.GLOB_NOMATCH then return nil, "couldn't find hwmon device handle for 1-wire device " .. address end
if glob_result == glob.GLOB_ABORTED then return nil, "glob(): aborted" end
@@ -462,7 +476,7 @@ function _M.hwmon_read_w1_therm(address)
if not hwmon_device then return end
local values = {}
- local tmp, err = _internal_.read_from_file(hwmon_device .. '/temp1_input')
+ local tmp, err = _internal_.read_line_from_file(hwmon_device .. '/temp1_input')
if tmp ~= nil then
local val = base.tonumber(tmp)
if val ~= nil then values['temperature'] = val/1000 end
@@ -481,7 +495,7 @@ _internal_.read['w1-42'] = function(address) return _M.hwmon_read_w1_therm(addre
-- ############# high-level interface ###########
function _M.read_config(path)
- sensors_json, err = _internal_.read_from_file(path)
+ sensors_json, err = _internal_.read_whole_file(path)
if not sensors_json then return nil, err end
return cjson.decode(sensors_json)
diff --git a/inventory/host_vars/ch-phoebe.yml b/inventory/host_vars/ch-phoebe.yml
index 8627f34a..e155ee2c 100644
--- a/inventory/host_vars/ch-phoebe.yml
+++ b/inventory/host_vars/ch-phoebe.yml
@@ -35,6 +35,19 @@ spreadspace_apt_repo_components:
prometheus_exporter_node_textfile_collector_scripts:
- deleted-libraries
- smartmon
+ - sensors
+
+prometheus_exporter_node_textfile_collector__sensors:
+ i2c:
+ - name: "AST i2c bit bus" ## "i2c-tiny-usb"
+ devices:
+ - address: 0x18
+ type: ds2482
+ w1:
+ - name: rack-intake
+ address: 28-????????????
+ - name: ceiling
+ address: 28-????????????
prometheus_exporters_extra:
- ssl
diff --git a/roles/monitoring/prometheus/exporter/node/defaults/main.yml b/roles/monitoring/prometheus/exporter/node/defaults/main.yml
index 0bcea14f..9e8bcf8b 100644
--- a/roles/monitoring/prometheus/exporter/node/defaults/main.yml
+++ b/roles/monitoring/prometheus/exporter/node/defaults/main.yml
@@ -16,3 +16,39 @@ prometheus_exporter_node_textfile_collector_scripts:
- deleted-libraries
# - smartmon
# - chrony
+# - sensors
+
+# prometheus_exporter_node_textfile_collector__sensors:
+# i2c:
+# - number: 1
+# devices:
+# - name: blub
+# address: 0x5c
+# type: am2315
+# - address: 0x18
+# type: ds2482
+# - name: "10000900.i2c"
+# devices:
+# - address: 0x70
+# type: pca9548
+# channels:
+# - number: 0
+# devices:
+# - name: foo
+# address: 0x76
+# type: bme280
+# - number: 1
+# devices:
+# - name: bar
+# address: 0x77
+# type: bmp280
+# w1:
+# - name: temp1
+# address: 28-987654321098
+# - name: temp2
+# address: 28-012345678901
+# gpio:
+# - name: everything-is-fine-and-dandy
+# number: 0
+# - name: something-went-horribly-wrong
+# number: 1
diff --git a/roles/monitoring/prometheus/exporter/node/handlers/main.yml b/roles/monitoring/prometheus/exporter/node/handlers/main.yml
index 56056ea6..8f5cb37c 100644
--- a/roles/monitoring/prometheus/exporter/node/handlers/main.yml
+++ b/roles/monitoring/prometheus/exporter/node/handlers/main.yml
@@ -8,3 +8,8 @@
service:
name: nginx
state: reloaded
+
+- name: remove sensors state file
+ file:
+ path: /run/prometheus-node-exporter_sensors/state
+ state: absent
diff --git a/roles/monitoring/prometheus/exporter/node/tasks/textfile_collector_sensors.yml b/roles/monitoring/prometheus/exporter/node/tasks/textfile_collector_sensors.yml
new file mode 100644
index 00000000..966c6d7f
--- /dev/null
+++ b/roles/monitoring/prometheus/exporter/node/tasks/textfile_collector_sensors.yml
@@ -0,0 +1,32 @@
+---
+- name: install lua and libs needed by sensors script
+ apt:
+ name:
+ - lua5.1
+ - lua-cjson
+ - lua-posix
+ state: present
+
+- name: create lua 5.1 module path in /usr/local
+ file:
+ path: /usr/local/share/lua/5.1
+ state: directory
+
+- name: install sensors module
+ copy:
+ src: "{{ global_files_dir }}/common/openwrt/sensors.module_lua"
+ dest: /usr/local/share/lua/5.1/sensors.lua
+
+- name: create configure directory
+ file:
+ path: /etc/prometheus/exporter/node
+ state: directory
+
+- name: generate senors config
+ copy:
+ content: "{{ prometheus_exporter_node_textfile_collector__sensors | to_nice_json }}\n"
+ dest: /etc/prometheus/exporter/node/sensors.json
+ notify: remove sensors state file
+
+- name: install the sensors textfile collector script
+ include_tasks: textfile_collector_generic.yml
diff --git a/roles/monitoring/prometheus/exporter/node/templates/textfile-collector-scripts/sensors.j2 b/roles/monitoring/prometheus/exporter/node/templates/textfile-collector-scripts/sensors.j2
new file mode 100644
index 00000000..822aa3b5
--- /dev/null
+++ b/roles/monitoring/prometheus/exporter/node/templates/textfile-collector-scripts/sensors.j2
@@ -0,0 +1,51 @@
+#!/usr/bin/lua5.1
+
+local sensors = require "sensors"
+local units = {
+ temperature = "celsius",
+ humidity = "percent",
+ pressure = "pascals",
+ gpio = "status",
+}
+
+local function metric(name, mtype, labels, value)
+ print("# TYPE " .. name .. " " .. mtype)
+
+ local label_string = ""
+ if labels then
+ for label,value in pairs(labels) do
+ label_string = label_string .. label .. '="' .. value .. '",'
+ end
+ label_string = "{" .. string.sub(label_string, 1, -2) .. "}"
+ end
+ print(string.format("%s%s %s", name, label_string, value))
+end
+
+local function scrape(config, num_sensors)
+ local readings, err = sensors.read(config)
+ if not readings then return end
+
+ metric("sensors_count_total", "gauge", nil, num_sensors)
+ for name, values in pairs(readings) do
+ labels = { name = name, kind = values._kind_ }
+ for t, v in pairs(values) do
+ local unit = units[t]
+ if unit ~= nil then
+ metric("sensors_" .. t .. "_" .. unit, "gauge", labels, v)
+ end
+ end
+ end
+end
+
+function main()
+ local config, _ = sensors.read_config('/etc/prometheus/exporter/node/sensors.json')
+
+ -- TODO: only do this if sensor state file does not exist - else read num_sensors from state file
+ local num_sensors, err = sensors.setup(config)
+ if num_sensors == nil then error(err) end
+ -- TODO: write num_sensors to state file
+
+ scrape(config, num_sensors, units)
+end
+
+main()
diff --git a/roles/monitoring/prometheus/exporter/node/templates/textfile-collector-scripts/sensors.service.j2 b/roles/monitoring/prometheus/exporter/node/templates/textfile-collector-scripts/sensors.service.j2
new file mode 100644
index 00000000..7a438317
--- /dev/null
+++ b/roles/monitoring/prometheus/exporter/node/templates/textfile-collector-scripts/sensors.service.j2
@@ -0,0 +1,33 @@
+[Unit]
+Description=Promethues node exporter textfile collector sensors
+
+[Service]
+Type=oneshot
+Environment=TMPDIR=/var/lib/prometheus-node-exporter/textfile-collector
+Environment=LC_NUMERIC=C
+ExecStart=bash -c "/usr/local/share/prometheus-node-exporter/sensors | sponge /var/lib/prometheus-node-exporter/textfile-collector/sensors.prom"
+TimeoutStartSec=30s
+
+# systemd hardening-options
+AmbientCapabilities=
+CapabilityBoundingSet=
+LockPersonality=true
+MemoryDenyWriteExecute=true
+NoNewPrivileges=true
+PrivateTmp=true
+ProtectControlGroups=true
+ProtectHome=true
+ProtectKernelModules=true
+ProtectKernelTunables=true
+ProtectSystem=strict
+ReadWritePaths=/var/lib/prometheus-node-exporter/textfile-collector
+RuntimeDirectory=prometheus-node-exporter_sensors
+RuntimeDirectoryPreserve=yes
+RemoveIPC=true
+RestrictNamespaces=true
+RestrictRealtime=true
+RestrictAddressFamilies=AF_UNIX
+SystemCallArchitectures=native
+
+[Install]
+WantedBy=multi-user.target
diff --git a/roles/monitoring/prometheus/exporter/node/templates/textfile-collector-scripts/sensors.timer.j2 b/roles/monitoring/prometheus/exporter/node/templates/textfile-collector-scripts/sensors.timer.j2
new file mode 100644
index 00000000..164b4681
--- /dev/null
+++ b/roles/monitoring/prometheus/exporter/node/templates/textfile-collector-scripts/sensors.timer.j2
@@ -0,0 +1,9 @@
+[Unit]
+Description=Promethues node exporter textfile collector sensors
+
+[Timer]
+OnBootSec=50s
+OnUnitActiveSec=5min
+
+[Install]
+WantedBy=timers.target