diff options
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 |