From bfe5db669d136920ebb0e05244ce914639353672 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Sun, 16 Jun 2024 10:49:43 +0200 Subject: [PATCH 01/54] fix: change power_cycles to power_cycle --- README.md | 3 +++ smartmon.sh | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1e970d4..42a0cff 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,9 @@ https://www.smartmontools.org/wiki/Download#Installfromthesourcetarball - save it under `/usr/local/bin/smartmon.sh` +- make sure `/var/lib/node_exporter/textfile_collector/` exists + - `mkdir -p /var/lib/node_exporter/textfile_collector/` + To enable the text_collector on your system add the following as cronjob. It will execute the script every five minutes and save the result to the `text_collector` directory. diff --git a/smartmon.sh b/smartmon.sh index 51e6752..878e801 100755 --- a/smartmon.sh +++ b/smartmon.sh @@ -9,6 +9,7 @@ # Formatting done via shfmt -i 2 # https://github.com/mvdan/sh +set -x parse_smartctl_attributes_awk="$( cat <<'SMARTCTLAWK' @@ -116,7 +117,7 @@ parse_smartctl_nvme_attributes() { Accumulated_start-stop_cycles) power_cycle="$(echo ${attr_value} | awk '{ printf "%e\n", $1 }')" ;; Elements_in_grown_defect_list) grown_defects="$(echo ${attr_value} | awk '{ printf "%e\n", $1 }')" ;; Unsafe_Shutdowns) unsafe_shutdowns="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; - Power_Cycles) power_cycles="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; + Power_Cycle) power_cycle="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; Power_On_Hours) power_on="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; Host_Read_Commands) host_read_commands="$(echo "${attr_value}" | tr -dc "0-9" | awk '{printf "%d\n", $1 }')" ;; Host_Write_Commands) host_write_commands="$(echo "${attr_value}" | tr -dc "0-9" | awk '{printf "%d\n", $1 }')" ;; @@ -133,7 +134,7 @@ parse_smartctl_nvme_attributes() { [ ! -z "$power_on" ] && echo "power_on_hours_raw_value{${labels},smart_id=\"9\"} ${power_on}" [ ! -z "$temp_cel" ] && echo "temperature_celsius_raw_value{${labels},smart_id=\"194\"} ${temp_cel}" [ ! -z "$lbas_read" ] && echo "total_lbas_read_raw_value{${labels},smart_id=\"242\"} ${lbas_read}" - [ ! -z "$power_cycles" ] && echo "power_cycles_count_raw_value{${labels},smart_id=\"255\"} ${power_cycles}" + [ ! -z "$power_cycle" ] && echo "power_cycle_count_raw_value{${labels},smart_id=\"255\"} ${power_cycle}" [ ! -z "$unsafe_shutdowns" ] && echo "unsafe_shutdowns_count_raw_value{${labels},smart_id=\"255\"} ${unsafe_shutdowns}" [ ! -z "$grown_defects" ] && echo "grown_defects_count_raw_value{${labels},smart_id=\"12\"} ${grown_defects}" [ ! -z "$percentage_used" ] && echo "percentage_used_raw_value{${labels},smart_id=\"255\"} ${percentage_used}" @@ -234,7 +235,7 @@ for device in ${device_list}; do nvme) /usr/sbin/smartctl -A -d "${type}" "${disk}" | parse_smartctl_nvme_attributes "${disk}" "${type}" ;; megaraid*) /usr/sbin/smartctl -A -d "${type}" "${disk}" | parse_smartctl_scsi_attributes "${disk}" "${type}" ;; *) - echo "disk type is not sat, scsi or megaraid but ${type}" + echo "disk type is not sat, scsi, nvme or megaraid but ${type}" exit ;; esac From 2f6836f4ccc885cd9496a52eb6ac8e0674b3e7b3 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Sun, 16 Jun 2024 11:24:03 +0200 Subject: [PATCH 02/54] chore: Update readme --- README.md | 37 ++++++++++++++++++++++++++----------- smartmon.sh | 3 +++ 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 42a0cff..3470179 100644 --- a/README.md +++ b/README.md @@ -16,24 +16,17 @@ https://github.com/prometheus/node_exporter/tree/master/text_collector_examples - Prometheus - node_exporter - text_collector enabled for node_exporter -- Grafana = 6.2 -- smartmontools = 7 +- Grafana >= 10 +- smartmontools >= 7 ## Set up To enable text_collector set the following flag for `node_exporter`: - `--collector.textfile.directory` run command with `/var/lib/node_exporter/textfile_collector` -To get an up to date version of smartmontools it could be necessary to compile it: -https://www.smartmontools.org/wiki/Download#Installfromthesourcetarball - -- check by executing `smartctl --version` - -- make smartmon.sh executable -- save it under `/usr/local/bin/smartmon.sh` +Install [smartmontools](https://www.smartmontools.org/) -- make sure `/var/lib/node_exporter/textfile_collector/` exists - - `mkdir -p /var/lib/node_exporter/textfile_collector/` +For UBUNTU: `sudo apt-get install smartmontools` To enable the text_collector on your system add the following as cronjob. It will execute the script every five minutes and save the result to the `text_collector` directory. @@ -49,3 +42,25 @@ Add the desired attributes to `smartmon_attrs` array in `smartmon.sh`. You get a list of your disks privided attributes by executing: `sudo smartctl -i -H /dev/` `sudo smartctl -A /dev/` + +## Running Locally +If you want to test the exporter locally. For example on a laptop you can move +the exporter to the following directory and run it. +```sh +sudo cp smartmon.sh /var/lib/node_exporter/textfile_collector/smart_metrics.prom + +/usr/bin/prometheus-node-exporter --collector.textfile.directory /var/lib/node_exporter/textfile_collector/ +``` + +## Troubleshooting +To get an up to date version of smartmontools it could be necessary to compile it: +https://www.smartmontools.org/wiki/Download#Installfromthesourcetarball + +- check by executing `smartctl --version` + +- make smartmon.sh executable + +- save it under `/usr/local/bin/smartmon.sh` + +- make sure `/var/lib/node_exporter/textfile_collector/` exists + - `mkdir -p /var/lib/node_exporter/textfile_collector/` diff --git a/smartmon.sh b/smartmon.sh index 878e801..4e04f17 100755 --- a/smartmon.sh +++ b/smartmon.sh @@ -9,6 +9,9 @@ # Formatting done via shfmt -i 2 # https://github.com/mvdan/sh + +# Source: +# https://github.com/prometheus-community/node-exporter-textfile-collector-scripts/blob/master/smartmon.sh set -x parse_smartctl_attributes_awk="$( From 0dd2db24ed184477ee421d45ccf15ec5d3341b0d Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Sun, 16 Jun 2024 11:37:00 +0200 Subject: [PATCH 03/54] fix: remove debug code --- smartmon.sh | 8 -------- 1 file changed, 8 deletions(-) diff --git a/smartmon.sh b/smartmon.sh index 4e04f17..7e1097b 100755 --- a/smartmon.sh +++ b/smartmon.sh @@ -3,16 +3,8 @@ # by Samuel B. (c) 2012 # source at: http://devel.dob.sk/collectd-scripts/ -# TODO: This probably needs to be a little more complex. The raw numbers can have more -# data in them than you'd think. -# http://arstechnica.com/civis/viewtopic.php?p=22062211 - -# Formatting done via shfmt -i 2 -# https://github.com/mvdan/sh - # Source: # https://github.com/prometheus-community/node-exporter-textfile-collector-scripts/blob/master/smartmon.sh -set -x parse_smartctl_attributes_awk="$( cat <<'SMARTCTLAWK' From 1db3e78b775979a54608d393c80050aa3d41e84d Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Sun, 16 Jun 2024 14:55:47 +0200 Subject: [PATCH 04/54] fix: use cycles instead of cycle --- README.md | 4 +++- smartmon.sh | 29 +++++++++++++++-------------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 3470179..a2bbe71 100644 --- a/README.md +++ b/README.md @@ -47,8 +47,10 @@ You get a list of your disks privided attributes by executing: If you want to test the exporter locally. For example on a laptop you can move the exporter to the following directory and run it. ```sh -sudo cp smartmon.sh /var/lib/node_exporter/textfile_collector/smart_metrics.prom +# execute collector +sudo sh -c 'smartmon.sh > /var/lib/node_exporter/textfile_collector/smart_metrics.prom' +# let node-exporter run /usr/bin/prometheus-node-exporter --collector.textfile.directory /var/lib/node_exporter/textfile_collector/ ``` diff --git a/smartmon.sh b/smartmon.sh index 7e1097b..4821f8c 100755 --- a/smartmon.sh +++ b/smartmon.sh @@ -5,6 +5,9 @@ # Source: # https://github.com/prometheus-community/node-exporter-textfile-collector-scripts/blob/master/smartmon.sh +# + +export LC_ALL=C parse_smartctl_attributes_awk="$( cat <<'SMARTCTLAWK' @@ -27,26 +30,23 @@ end_to_end_error erase_fail_count g_sense_error_rate hardware_ecc_recovered -host_reads_mib host_reads_32mib -host_writes_mib +host_reads_mib host_writes_32mib +host_writes_mib load_cycle_count media_wearout_indicator -multi_zone_error_rate -wear_leveling_count nand_writes_1gib offline_uncorrectable -percent_lifetime_remain -power_cycle_count -power_off_retract_count +power_cycles_count power_on_hours +program_fail_cnt_total program_fail_count raw_read_error_rate reallocated_event_count reallocated_sector_ct -reallocate_nand_blk_cnt reported_uncorrect +runtime_bad_block sata_downshift_count seek_error_rate spin_retry_count @@ -57,9 +57,10 @@ temperature_celsius temperature_internal total_lbas_read total_lbas_written -total_host_sector_write udma_crc_error_count unsafe_shutdown_count +unused_rsvd_blk_cnt_tot +wear_leveling_count workld_host_reads_perc workld_media_wear_indic workload_minutes @@ -89,14 +90,14 @@ parse_smartctl_scsi_attributes() { number_of_hours_powered_up_) power_on="$(echo "${attr_value}" | awk '{ printf "%e\n", $1 }')" ;; Current_Drive_Temperature) temp_cel="$(echo ${attr_value} | cut -f1 -d' ' | awk '{ printf "%e\n", $1 }')" ;; Blocks_read_from_cache_and_sent_to_initiator_) lbas_read="$(echo ${attr_value} | awk '{ printf "%e\n", $1 }')" ;; - Accumulated_start-stop_cycles) power_cycle="$(echo ${attr_value} | awk '{ printf "%e\n", $1 }')" ;; + Accumulated_start-stop_cycles) power_cycles="$(echo ${attr_value} | awk '{ printf "%e\n", $1 }')" ;; Elements_in_grown_defect_list) grown_defects="$(echo ${attr_value} | awk '{ printf "%e\n", $1 }')" ;; esac done [ ! -z "$power_on" ] && echo "power_on_hours_raw_value{${labels},smart_id=\"9\"} ${power_on}" [ ! -z "$temp_cel" ] && echo "temperature_celsius_raw_value{${labels},smart_id=\"194\"} ${temp_cel}" [ ! -z "$lbas_read" ] && echo "total_lbas_read_raw_value{${labels},smart_id=\"242\"} ${lbas_read}" - [ ! -z "$power_cycle" ] && echo "power_cycle_count_raw_value{${labels},smart_id=\"12\"} ${power_cycle}" + [ ! -z "$power_cycles" ] && echo "power_cycles_count_raw_value{${labels},smart_id=\"12\"} ${power_cycles}" [ ! -z "$grown_defects" ] && echo "grown_defects_count_raw_value{${labels},smart_id=\"12\"} ${grown_defects}" } @@ -109,10 +110,10 @@ parse_smartctl_nvme_attributes() { attr_value="$(echo "${line}" | tr '=' ':' | cut -f2 -d: | sed 's/^ \+//g')" case "${attr_type}" in Blocks_read_from_cache_and_sent_to_initiator_) lbas_read="$(echo ${attr_value} | awk '{ printf "%e\n", $1 }')" ;; - Accumulated_start-stop_cycles) power_cycle="$(echo ${attr_value} | awk '{ printf "%e\n", $1 }')" ;; + Accumulated_start-stop_cycles) power_cycles="$(echo ${attr_value} | awk '{ printf "%e\n", $1 }')" ;; Elements_in_grown_defect_list) grown_defects="$(echo ${attr_value} | awk '{ printf "%e\n", $1 }')" ;; Unsafe_Shutdowns) unsafe_shutdowns="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; - Power_Cycle) power_cycle="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; + Power_Cycle) power_cycles="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; Power_On_Hours) power_on="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; Host_Read_Commands) host_read_commands="$(echo "${attr_value}" | tr -dc "0-9" | awk '{printf "%d\n", $1 }')" ;; Host_Write_Commands) host_write_commands="$(echo "${attr_value}" | tr -dc "0-9" | awk '{printf "%d\n", $1 }')" ;; @@ -129,7 +130,7 @@ parse_smartctl_nvme_attributes() { [ ! -z "$power_on" ] && echo "power_on_hours_raw_value{${labels},smart_id=\"9\"} ${power_on}" [ ! -z "$temp_cel" ] && echo "temperature_celsius_raw_value{${labels},smart_id=\"194\"} ${temp_cel}" [ ! -z "$lbas_read" ] && echo "total_lbas_read_raw_value{${labels},smart_id=\"242\"} ${lbas_read}" - [ ! -z "$power_cycle" ] && echo "power_cycle_count_raw_value{${labels},smart_id=\"255\"} ${power_cycle}" + [ ! -z "$power_cycles" ] && echo "power_cycles_count_raw_value{${labels},smart_id=\"255\"} ${power_cycles}" [ ! -z "$unsafe_shutdowns" ] && echo "unsafe_shutdowns_count_raw_value{${labels},smart_id=\"255\"} ${unsafe_shutdowns}" [ ! -z "$grown_defects" ] && echo "grown_defects_count_raw_value{${labels},smart_id=\"12\"} ${grown_defects}" [ ! -z "$percentage_used" ] && echo "percentage_used_raw_value{${labels},smart_id=\"255\"} ${percentage_used}" From 15da858918515bed6163570944496edd52bbddb1 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Sat, 20 Jul 2024 19:48:20 +0200 Subject: [PATCH 05/54] feat: use json output --- .gitmodules | 9 + README.md | 6 + json.out | 72 ++++++ parse.out | 60 +++++ src/smartmon-json.sh | 242 ++++++++++++++++++++ smartmon.sh => src/smartmon.sh | 132 ++++++----- test/bats | 1 + test/test_helper/bats-assert | 1 + test/test_helper/bats-support | 1 + test/test_smartmon-json.bats | 390 +++++++++++++++++++++++++++++++++ test/test_smartmon.bak-bats | 199 +++++++++++++++++ 11 files changed, 1055 insertions(+), 58 deletions(-) create mode 100644 .gitmodules create mode 100644 json.out create mode 100644 parse.out create mode 100755 src/smartmon-json.sh rename smartmon.sh => src/smartmon.sh (61%) create mode 160000 test/bats create mode 160000 test/test_helper/bats-assert create mode 160000 test/test_helper/bats-support create mode 100644 test/test_smartmon-json.bats create mode 100644 test/test_smartmon.bak-bats diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..b7efcb4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "test/bats"] + path = test/bats + url = https://github.com/bats-core/bats-core.git +[submodule "test/test_helper/bats-support"] + path = test/test_helper/bats-support + url = https://github.com/bats-core/bats-support.git +[submodule "test/test_helper/bats-assert"] + path = test/test_helper/bats-assert + url = https://github.com/bats-core/bats-assert.git diff --git a/README.md b/README.md index a2bbe71..1a538f8 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ https://github.com/prometheus/node_exporter/tree/master/text_collector_examples - text_collector enabled for node_exporter - Grafana >= 10 - smartmontools >= 7 +- jq ## Set up To enable text_collector set the following flag for `node_exporter`: @@ -66,3 +67,8 @@ https://www.smartmontools.org/wiki/Download#Installfromthesourcetarball - make sure `/var/lib/node_exporter/textfile_collector/` exists - `mkdir -p /var/lib/node_exporter/textfile_collector/` + + +## Tests +To do tests install bats (bats-core): +[bats-tutorial](https://bats-core.readthedocs.io/en/stable/tutorial.html) diff --git a/json.out b/json.out new file mode 100644 index 0000000..e34b9c0 --- /dev/null +++ b/json.out @@ -0,0 +1,72 @@ +# HELP smartmon_smartctl_version SMART metric smartctl_version +# TYPE smartmon_smartctl_version gauge +smartmon_smartctl_version{version="7.4"} 1 +# HELP smartmon_available_spare SMART metric available_spare +# TYPE smartmon_available_spare gauge +smartmon_available_spare{disk="/dev/nvme0",type="nvme"} 100 +# HELP smartmon_available_spare_threshold SMART metric available_spare_threshold +# TYPE smartmon_available_spare_threshold gauge +smartmon_available_spare_threshold{disk="/dev/nvme0",type="nvme"} 10 +# HELP smartmon_controller_busy_time SMART metric controller_busy_time +# TYPE smartmon_controller_busy_time gauge +smartmon_controller_busy_time{disk="/dev/nvme0",type="nvme"} 83 +# HELP smartmon_critical_comp_time SMART metric critical_comp_time +# TYPE smartmon_critical_comp_time gauge +smartmon_critical_comp_time{disk="/dev/nvme0",type="nvme"} 0 +# HELP smartmon_critical_warning SMART metric critical_warning +# TYPE smartmon_critical_warning gauge +smartmon_critical_warning{disk="/dev/nvme0",type="nvme"} 0 +# HELP smartmon_data_units_read SMART metric data_units_read +# TYPE smartmon_data_units_read gauge +smartmon_data_units_read{disk="/dev/nvme0",type="nvme"} 2453999 +# HELP smartmon_data_units_written SMART metric data_units_written +# TYPE smartmon_data_units_written gauge +smartmon_data_units_written{disk="/dev/nvme0",type="nvme"} 4088338 +# HELP smartmon_device_active SMART metric device_active +# TYPE smartmon_device_active gauge +smartmon_device_active{disk="/dev/nvme0",type="nvme"} 1 +# HELP smartmon_device_info SMART metric device_info +# TYPE smartmon_device_info gauge +smartmon_device_info{disk="/dev/nvme0",type="nvme",vendor="",product="",revision="",lun_id="",model_family="",device_model="",serial_number="23048U472208",firmware_version="731100WD"} 1 +# HELP smartmon_device_smart_available SMART metric device_smart_available +# TYPE smartmon_device_smart_available gauge +smartmon_device_smart_available{disk="/dev/nvme0",type="nvme"} 0 +# HELP smartmon_device_smart_enabled SMART metric device_smart_enabled +# TYPE smartmon_device_smart_enabled gauge +smartmon_device_smart_enabled{disk="/dev/nvme0",type="nvme"} 0 +# HELP smartmon_device_smart_healthy SMART metric device_smart_healthy +# TYPE smartmon_device_smart_healthy gauge +smartmon_device_smart_healthy{disk="/dev/nvme0",type="nvme"} true +# HELP smartmon_host_reads SMART metric host_reads +# TYPE smartmon_host_reads gauge +smartmon_host_reads{disk="/dev/nvme0",type="nvme"} 24206161 +# HELP smartmon_host_writes SMART metric host_writes +# TYPE smartmon_host_writes gauge +smartmon_host_writes{disk="/dev/nvme0",type="nvme"} 53251926 +# HELP smartmon_media_errors SMART metric media_errors +# TYPE smartmon_media_errors gauge +smartmon_media_errors{disk="/dev/nvme0",type="nvme"} 0 +# HELP smartmon_num_err_log_entries SMART metric num_err_log_entries +# TYPE smartmon_num_err_log_entries gauge +smartmon_num_err_log_entries{disk="/dev/nvme0",type="nvme"} 0 +# HELP smartmon_percentage_used SMART metric percentage_used +# TYPE smartmon_percentage_used gauge +smartmon_percentage_used{disk="/dev/nvme0",type="nvme"} 0 +# HELP smartmon_power_cycles SMART metric power_cycles +# TYPE smartmon_power_cycles gauge +smartmon_power_cycles{disk="/dev/nvme0",type="nvme"} 1108 +# HELP smartmon_power_on_hours SMART metric power_on_hours +# TYPE smartmon_power_on_hours gauge +smartmon_power_on_hours{disk="/dev/nvme0",type="nvme"} 74 +# HELP smartmon_smartctl_run SMART metric smartctl_run +# TYPE smartmon_smartctl_run gauge +smartmon_smartctl_run{disk="/dev/nvme0",type="nvme"} 1721322621 +# HELP smartmon_temperature SMART metric temperature +# TYPE smartmon_temperature gauge +smartmon_temperature{disk="/dev/nvme0",type="nvme"} 30 +# HELP smartmon_unsafe_shutdowns SMART metric unsafe_shutdowns +# TYPE smartmon_unsafe_shutdowns gauge +smartmon_unsafe_shutdowns{disk="/dev/nvme0",type="nvme"} 53 +# HELP smartmon_warning_temp_time SMART metric warning_temp_time +# TYPE smartmon_warning_temp_time gauge +smartmon_warning_temp_time{disk="/dev/nvme0",type="nvme"} 0 diff --git a/parse.out b/parse.out new file mode 100644 index 0000000..264d6e8 --- /dev/null +++ b/parse.out @@ -0,0 +1,60 @@ +# HELP smartmon_smartctl_version SMART metric smartctl_version +# TYPE smartmon_smartctl_version gauge +smartmon_smartctl_version{version="7.4"} 1 +# HELP smartmon_available_spare_raw_value SMART metric available_spare_raw_value +# TYPE smartmon_available_spare_raw_value gauge +smartmon_available_spare_raw_value{disk="/dev/nvme0",type="nvme",smart_id="255"} 100 +# HELP smartmon_available_spare_threshold SMART metric available_spare_threshold +# TYPE smartmon_available_spare_threshold gauge +smartmon_available_spare_threshold{disk="/dev/nvme0",type="nvme",smart_id="255"} 10 +# HELP smartmon_controller_busy_time_raw_value SMART metric controller_busy_time_raw_value +# TYPE smartmon_controller_busy_time_raw_value gauge +smartmon_controller_busy_time_raw_value{disk="/dev/nvme0",type="nvme",smart_id="255"} 82 +# HELP smartmon_data_units_written_raw_value SMART metric data_units_written_raw_value +# TYPE smartmon_data_units_written_raw_value gauge +smartmon_data_units_written_raw_value{disk="/dev/nvme0",type="nvme",smart_id="8"} 4042358 +# HELP smartmon_device_active SMART metric device_active +# TYPE smartmon_device_active gauge +smartmon_device_active{disk="/dev/nvme0",type="nvme"} 1 +# HELP smartmon_device_info SMART metric device_info +# TYPE smartmon_device_info gauge +smartmon_device_info{disk="/dev/nvme0",type="nvme",vendor="",product="",revision="",lun_id="",model_family="",device_model="",serial_number="23048U472208",firmware_version="731100WD"} 1 +# HELP smartmon_device_smart_available SMART metric device_smart_available +# TYPE smartmon_device_smart_available gauge +smartmon_device_smart_available{disk="/dev/nvme0",type="nvme"} 0 +# HELP smartmon_device_smart_enabled SMART metric device_smart_enabled +# TYPE smartmon_device_smart_enabled gauge +smartmon_device_smart_enabled{disk="/dev/nvme0",type="nvme"} 0 +# HELP smartmon_device_smart_healthy SMART metric device_smart_healthy +# TYPE smartmon_device_smart_healthy gauge +smartmon_device_smart_healthy{disk="/dev/nvme0",type="nvme"} 1 +# HELP smartmon_error_information_log_entries_raw_value SMART metric error_information_log_entries_raw_value +# TYPE smartmon_error_information_log_entries_raw_value gauge +smartmon_error_information_log_entries_raw_value{disk="/dev/nvme0",type="nvme",smart_id="255"} 0 +# HELP smartmon_host_read_commands_raw_value SMART metric host_read_commands_raw_value +# TYPE smartmon_host_read_commands_raw_value gauge +smartmon_host_read_commands_raw_value{disk="/dev/nvme0",type="nvme",smart_id="255"} 23953180 +# HELP smartmon_host_write_commands_raw_value SMART metric host_write_commands_raw_value +# TYPE smartmon_host_write_commands_raw_value gauge +smartmon_host_write_commands_raw_value{disk="/dev/nvme0",type="nvme",smart_id="255"} 52595138 +# HELP smartmon_media_and_data_integrity_errors_count_raw_value SMART metric media_and_data_integrity_errors_count_raw_value +# TYPE smartmon_media_and_data_integrity_errors_count_raw_value gauge +smartmon_media_and_data_integrity_errors_count_raw_value{disk="/dev/nvme0",type="nvme",smart_id="255"} 0 +# HELP smartmon_percentage_used_raw_value SMART metric percentage_used_raw_value +# TYPE smartmon_percentage_used_raw_value gauge +smartmon_percentage_used_raw_value{disk="/dev/nvme0",type="nvme",smart_id="255"} 0 +# HELP smartmon_power_cycles_count_raw_value SMART metric power_cycles_count_raw_value +# TYPE smartmon_power_cycles_count_raw_value gauge +smartmon_power_cycles_count_raw_value{disk="/dev/nvme0",type="nvme",smart_id="255"} 1 +# HELP smartmon_power_on_hours_raw_value SMART metric power_on_hours_raw_value +# TYPE smartmon_power_on_hours_raw_value gauge +smartmon_power_on_hours_raw_value{disk="/dev/nvme0",type="nvme",smart_id="9"} 73 +# HELP smartmon_smartctl_run SMART metric smartctl_run +# TYPE smartmon_smartctl_run gauge +smartmon_smartctl_run{disk="/dev/nvme0",type="nvme"} 1720264085 +# HELP smartmon_temperature_celsius_raw_value SMART metric temperature_celsius_raw_value +# TYPE smartmon_temperature_celsius_raw_value gauge +smartmon_temperature_celsius_raw_value{disk="/dev/nvme0",type="nvme",smart_id="194"} 32 +# HELP smartmon_unsafe_shutdowns_count_raw_value SMART metric unsafe_shutdowns_count_raw_value +# TYPE smartmon_unsafe_shutdowns_count_raw_value gauge +smartmon_unsafe_shutdowns_count_raw_value{disk="/dev/nvme0",type="nvme",smart_id="255"} 53 diff --git a/src/smartmon-json.sh b/src/smartmon-json.sh new file mode 100755 index 0000000..1b7e8f9 --- /dev/null +++ b/src/smartmon-json.sh @@ -0,0 +1,242 @@ +#!/bin/bash +# shellcheck disable=SC2155 + +# This script is designed to collect SMART data from various types of +# disks (ATA, NVMe, SCSI) and format it for Prometheus monitoring. + +# Ensure jq is installed. This function checks if the 'jq' command is available. +ensure_jq_installed() { + if ! command -v jq &>/dev/null; then + echo "jq could not be found. Please install it." + exit 1 + fi +} + +# Parse and extract SMART information from the provided JSON. +# The function extracts various fields and prints them in a formatted manner. +# Arguments: +# $1 - Disk name +# $2 - Disk type +# $3 - JSON string containing SMART information +parse_smartctl_info_json() { + local disk="$1" + local disk_type="$2" + local json="$3" + local labels="disk=\"${disk}\",type=\"${disk_type}\"" + + # Extract various SMART attributes using jq + local model_family=$(echo "$json" | jq -r '.model_family // empty') + local device_model=$(echo "$json" | jq -r '.device_model // empty') + local serial_number=$(echo "$json" | jq -r '.serial_number // empty') + local fw_version=$(echo "$json" | jq -r '.firmware_version // empty') + local vendor=$(echo "$json" | jq -r '.vendor // empty') + local product=$(echo "$json" | jq -r '.product // empty') + local revision=$(echo "$json" | jq -r '.revision // empty') + local lun_id=$(echo "$json" | jq -r '.lun_id // empty') + local smart_available=$(echo "$json" | jq -r '.smart_support.is_available // 0') + local smart_enabled=$(echo "$json" | jq -r '.smart_support.is_enabled // 0') + local smart_healthy=$(echo "$json" | jq -r '.smart_status.passed // 0') + + # Print the extracted information in the Prometheus format + echo "device_info{${labels},vendor=\"${vendor}\",product=\"${product}\",revision=\"${revision}\",lun_id=\"${lun_id}\",model_family=\"${model_family}\",device_model=\"${device_model}\",serial_number=\"${serial_number}\",firmware_version=\"${fw_version}\"} 1" + echo "device_smart_available{${labels}} ${smart_available}" + echo "device_smart_enabled{${labels}} ${smart_enabled}" + echo "device_smart_healthy{${labels}} ${smart_healthy}" +} + +# Parse and extract ATA SMART attributes from the provided JSON. +# The function extracts various fields and prints them in a formatted manner. +# Arguments: +# $1 - Disk name +# $2 - Disk type +# $3 - JSON string containing SMART attributes +parse_smartctl_attributes_json() { + local disk="$1" + local disk_type="$2" + local json="$3" + local labels="disk=\"${disk}\",type=\"${disk_type}\"" + + # return early if no input + if [ "$json" == {} ]; then + return 0 + fi + + # Extract and format SMART attributes using jq + echo "$json" | jq -r ' + .ata_smart_attributes.table[] | + select(.id and .name and (.value | type != "array") and (.worst | type != "array") and (.thresh | type != "array") and (.raw.value | type != "array")) | + [ + .id, + (.name | gsub("-"; "_")), + .value, + .worst, + .thresh, + .raw.value + ] | @tsv + ' | while IFS=$'\t' read -r id name value worst thresh raw; do + echo "${name}_value{${labels},smart_id=\"${id}\"} ${value}" + echo "${name}_worst{${labels},smart_id=\"${id}\"} ${worst}" + echo "${name}_threshold{${labels},smart_id=\"${id}\"} ${thresh}" + echo "${name}_raw_value{${labels},smart_id=\"${id}\"} ${raw}" + done +} + +# Parse and extract NVMe SMART attributes from the provided JSON. +# The function extracts various fields and prints them in a formatted manner. +# Arguments: +# $1 - Disk name +# $2 - Disk type +# $3 - JSON string containing NVMe SMART attributes +parse_smartctl_nvme_attributes_json() { + local disk="$1" + local disk_type="$2" + local json="$3" + local labels="disk=\"${disk}\",type=\"${disk_type}\"" + + # return early if no input + if [ "$json" == {} ]; then + return 0 + fi + + # Extract and format NVMe SMART attributes using jq + echo "$json" | jq -r ' + .nvme_smart_health_information_log | + to_entries[] | + select(.key and (.value | type != "array")) | + [ + (.key | gsub("-"; "_")), + .value + ] | @tsv + ' | while IFS=$'\t' read -r key value; do + echo "${key}{${labels}} ${value}" + done +} + +# Parse and extract SCSI SMART attributes from the provided JSON. +# The function extracts various fields and prints them in a formatted manner. +# Arguments: +# $1 - Disk name +# $2 - Disk type +# $3 - JSON string containing SCSI SMART attributes +parse_smartctl_scsi_attributes_json() { + local disk="$1" + local disk_type="$2" + local json="$3" + local labels="disk=\"${disk}\",type=\"${disk_type}\"" + + # Extract and format SCSI SMART attributes using jq + echo "$json" | jq -r ' + [ + {key: "power_on_hours", value: .scsi_grown_defects_count}, + {key: "Current_Drive_Temperature", value: .temperature.current}, + {key: "Accumulated_start-stop_cycles", value: .accumulated_start_stop_cycles}, + {key: "Unsafe_Shutdowns", value: .unsafe_shutdowns}, + {key: "Power_Cycles", value: .power_cycles}, + {key: "Power_On_Hours", value: .power_on_hours}, + {key: "Host_Read_Commands", value: .host_read_commands}, + {key: "Host_Write_Commands", value: .host_write_commands}, + {key: "Controller_Busy_Time", value: .controller_busy_time}, + {key: "Error_Information_Log_Entries", value: .error_information_log_entries}, + {key: "Temperature", value: .temperature}, + {key: "Percentage_Used", value: .percentage_used}, + {key: "Available_Spare", value: .available_spare}, + {key: "Available_Spare_Threshold", value: .available_spare_threshold}, + {key: "Media_and_Data_Integrity_Errors", value: .media_and_data_integrity_errors} + ] | + map(select(.value != null and (.value | type != "array"))) | + .[] | + "\(.key){${labels}} \(.value)" + ' | while IFS=$'\t' read -r key value; do + echo "${key}{${labels}} ${value}" + done +} + +# AWK script to format the output in Prometheus format. +# This script processes the sorted output to add HELP and TYPE comments +# for each unique metric. +# v = $1 is used to store last metric and avoid duplicates in the next run +# This is used to track the last seen metric name to avoid printing duplicate help and type lines. +format_output_awk="$( + cat <<'OUTPUTAWK' +BEGIN { v = "" } +(v != $1 && $0 != $1 ){ + print "# HELP smartmon_" $1 " SMART metric " $1; + print "# TYPE smartmon_" $1 " gauge"; + v = $1 +} +($0 != $1 ) {print "smartmon_" $0} +OUTPUTAWK +)" + +# Format the output using awk. +# This function sorts the input and processes it using the awk script defined above. +format_output() { + sort | + awk -F'{' "${format_output_awk}" +} + +# Main function to orchestrate the script. +main() { + ensure_jq_installed + + # Get the smartctl version + local smartctl_version + smartctl_version=$(/usr/sbin/smartctl -V | head -n1 | awk '$1 == "smartctl" {print $2}') + + # Output the smartctl version in Prometheus format + echo "smartctl_version{version=\"${smartctl_version}\"} 1" | format_output + + # Exit if smartctl version is less than 6 + if [[ "$(expr "${smartctl_version}" : '\([0-9]*\)\..*')" -lt 6 ]]; then + exit + fi + + # Get the list of devices + local device_list + device_list=$(/usr/sbin/smartctl --scan-open | awk '/^\/dev/{print $1 "|" $3}') + + # Iterate over each device and gather SMART information + for device in ${device_list}; do + local disk + local type + disk=$(echo "${device}" | cut -f1 -d'|') + type=$(echo "${device}" | cut -f2 -d'|') + local active=1 + + # Record the time of smartctl run + echo "smartctl_run{disk=\"${disk}\",type=\"${type}\"}" "$(TZ=UTC date '+%s')" + # Check if the device is active + /usr/sbin/smartctl -n standby -d "${type}" "${disk}" > /dev/null || active=0 + echo "device_active{disk=\"${disk}\",type=\"${type}\"}" "${active}" + + # Skip inactive devices + test ${active} -eq 0 && continue + + # Get and parse SMART information + local info_json + info_json=$(/usr/sbin/smartctl -i -H -j -d "${type}" "${disk}") + parse_smartctl_info_json "${disk}" "${type}" "${info_json}" + + # Get and parse SMART attributes + local attributes_json + attributes_json=$(/usr/sbin/smartctl -A -j -d "${type}" "${disk}") + case ${type} in + sat|sat+megaraid*) + parse_smartctl_attributes_json "${disk}" "${type}" "${attributes_json}" + ;; + scsi|megaraid*) + parse_smartctl_scsi_attributes_json "${disk}" "${type}" "${attributes_json}" + ;; + nvme) + parse_smartctl_nvme_attributes_json "${disk}" "${type}" "${attributes_json}" + ;; + *) + echo "disk type is not supported: ${type}" + exit 1 + ;; + esac + done | format_output +} + +# Run the main function +main diff --git a/smartmon.sh b/src/smartmon.sh similarity index 61% rename from smartmon.sh rename to src/smartmon.sh index 4821f8c..415cb6e 100755 --- a/smartmon.sh +++ b/src/smartmon.sh @@ -1,14 +1,25 @@ -#!/bin/bash +#!/usr/bin/env bash + +# This script collects metrics from disks using smartctl. It will parse the +# output of scmartctl and collect metrics by a given filter. +# # Script informed by the collectd monitoring script for smartmontools (using smartctl) # by Samuel B. (c) 2012 # source at: http://devel.dob.sk/collectd-scripts/ - # Source: # https://github.com/prometheus-community/node-exporter-textfile-collector-scripts/blob/master/smartmon.sh # +# New attributes needs to be added to different areas. They are marked with a +# comment "Add new ..." + +set -o errexit # abort on nonzero exitstatus +# set -o nounset # abort on unbound variable +set -o pipefail # don't hide errors within pipes export LC_ALL=C +# Check if first field is a number and the second consists of alphanumeric +# characters, underscores and/or hyphens. Gsub replaces hyphens with underscores. parse_smartctl_attributes_awk="$( cat <<'SMARTCTLAWK' $1 ~ /^ *[0-9]+$/ && $2 ~ /^[a-zA-Z0-9_-]+$/ { @@ -21,11 +32,14 @@ $1 ~ /^ *[0-9]+$/ && $2 ~ /^[a-zA-Z0-9_-]+$/ { SMARTCTLAWK )" +# Add new attributes here. This variable is used as a filter. +# TODO: validate where it is actually used. SATA only? smartmon_attrs="$( cat <<'SMARTMONATTRS' airflow_temperature_cel command_timeout current_pending_sector +data_units_written end_to_end_error erase_fail_count g_sense_error_rate @@ -66,81 +80,77 @@ workld_media_wear_indic workload_minutes SMARTMONATTRS )" -smartmon_attrs="$(echo ${smartmon_attrs} | xargs | tr ' ' '|')" +smartmon_attrs="$(echo "${smartmon_attrs}" | xargs | tr ' ' '|')" +# Remove leading spaces and formatting available information for output that +# is usable for Prometheus. Grep filters only matching lines that are stored +# in "smartmon_attrs". +# Example output: +# spin_up_time_worst{disk="sda",type="ssd",smart_id="2"} 100 +# +# Input variables: +# - disk = path of a specific disk +# - type = type of the specific disk parse_smartctl_attributes() { local disk="$1" local disk_type="$2" local labels="disk=\"${disk}\",type=\"${disk_type}\"" - local vars="$(echo "${smartmon_attrs}" | xargs | tr ' ' '|')" + sed 's/^ \+//g' | awk -v labels="${labels}" "${parse_smartctl_attributes_awk}" 2>/dev/null | - tr A-Z a-z | + tr '[:upper:]' '[:lower:]' | grep -E "(${smartmon_attrs})" } -parse_smartctl_scsi_attributes() { - local disk="$1" - local disk_type="$2" - local labels="disk=\"${disk}\",type=\"${disk_type}\"" - while read line; do - attr_type="$(echo "${line}" | tr '=' ':' | cut -f1 -d: | sed 's/^ \+//g' | tr ' ' '_')" - attr_value="$(echo "${line}" | tr '=' ':' | cut -f2 -d: | sed 's/^ \+//g')" - case "${attr_type}" in - number_of_hours_powered_up_) power_on="$(echo "${attr_value}" | awk '{ printf "%e\n", $1 }')" ;; - Current_Drive_Temperature) temp_cel="$(echo ${attr_value} | cut -f1 -d' ' | awk '{ printf "%e\n", $1 }')" ;; - Blocks_read_from_cache_and_sent_to_initiator_) lbas_read="$(echo ${attr_value} | awk '{ printf "%e\n", $1 }')" ;; - Accumulated_start-stop_cycles) power_cycles="$(echo ${attr_value} | awk '{ printf "%e\n", $1 }')" ;; - Elements_in_grown_defect_list) grown_defects="$(echo ${attr_value} | awk '{ printf "%e\n", $1 }')" ;; - esac - done - [ ! -z "$power_on" ] && echo "power_on_hours_raw_value{${labels},smart_id=\"9\"} ${power_on}" - [ ! -z "$temp_cel" ] && echo "temperature_celsius_raw_value{${labels},smart_id=\"194\"} ${temp_cel}" - [ ! -z "$lbas_read" ] && echo "total_lbas_read_raw_value{${labels},smart_id=\"242\"} ${lbas_read}" - [ ! -z "$power_cycles" ] && echo "power_cycles_count_raw_value{${labels},smart_id=\"12\"} ${power_cycles}" - [ ! -z "$grown_defects" ] && echo "grown_defects_count_raw_value{${labels},smart_id=\"12\"} ${grown_defects}" -} - +# Expected input: +# Piped output from smartctl as input stream +# $1 = disk path (example: /dev/sda) +# $2 = disk type (example: sat) parse_smartctl_nvme_attributes() { local disk="$1" local disk_type="$2" local labels="disk=\"${disk}\",type=\"${disk_type}\"" - while read line; do + # Add new NVME attributes here. + while read -r line; do attr_type="$(echo "${line}" | tr '=' ':' | cut -f1 -d: | sed 's/^ \+//g' | tr ' ' '_')" attr_value="$(echo "${line}" | tr '=' ':' | cut -f2 -d: | sed 's/^ \+//g')" + echo "${attr_type}" case "${attr_type}" in - Blocks_read_from_cache_and_sent_to_initiator_) lbas_read="$(echo ${attr_value} | awk '{ printf "%e\n", $1 }')" ;; - Accumulated_start-stop_cycles) power_cycles="$(echo ${attr_value} | awk '{ printf "%e\n", $1 }')" ;; - Elements_in_grown_defect_list) grown_defects="$(echo ${attr_value} | awk '{ printf "%e\n", $1 }')" ;; - Unsafe_Shutdowns) unsafe_shutdowns="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; - Power_Cycle) power_cycles="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; - Power_On_Hours) power_on="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; - Host_Read_Commands) host_read_commands="$(echo "${attr_value}" | tr -dc "0-9" | awk '{printf "%d\n", $1 }')" ;; - Host_Write_Commands) host_write_commands="$(echo "${attr_value}" | tr -dc "0-9" | awk '{printf "%d\n", $1 }')" ;; + Accumulated_start-stop_cycles) power_cycles="$(echo "${attr_value}" | awk '{ printf "%e\n", $1 }')" ;; + Available_Spare) available_spare="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; + Available_Spare_Threshold) available_spare_threshold="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; + Blocks_read_from_cache_and_sent_to_initiator_) lbas_read="$(echo "${attr_value}" | awk '{ printf "%e\n", $1 }')" ;; Controller_Busy_Time) controller_busy_time="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; + Data_Units_Written) data_units_written="$(echo "${attr_value}" | sed 's/,//g' | awk '{ printf "%u\n", $1 }')" ;; + Elements_in_grown_defect_list) grown_defects="$(echo "${attr_value}" | awk '{ printf "%e\n", $1 }')" ;; Error_Information_Log_Entries) error_info_log_entry="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; - Temperature) echo ${attr_value}; temp_cel="$(echo ${attr_value} | awk '{ printf "%d\n", $1 }')" ;; - Percentage_Used) percentage_used="$(echo ${attr_value} | awk '{ printf "%d\n", $1 }')" ;; - Available_Spare) available_spare="$(echo ${attr_value} | awk '{ printf "%d\n", $1 }')" ;; - Available_Spare_Threshold) available_spare_threshold="$(echo ${attr_value} | awk '{ printf "%d\n", $1 }')" ;; - Media_and_Data_Integrity_Errors) media_and_data_integrity_errors="$(echo ${attr_value} | awk '{ printf "%d\n", $1 }')" ;; + Host_Read_Commands) host_read_commands="$(echo "${attr_value}" | tr -dc "0-9" | awk '{printf "%d\n", $1 }')" ;; + Host_Write_Commands) host_write_commands="$(echo "${attr_value}" | tr -dc "0-9" | awk '{printf "%d\n", $1 }')" ;; + Media_and_Data_Integrity_Errors) media_and_data_integrity_errors="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; + Percentage_Used) percentage_used="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; + Power_Cycles) power_cycles="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; + Power_On_Hours) power_on="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; + Temperature) echo "${attr_value}"; temp_cel="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; + Unsafe_Shutdowns) unsafe_shutdowns="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; esac done - [ ! -z "$power_on" ] && echo "power_on_hours_raw_value{${labels},smart_id=\"9\"} ${power_on}" - [ ! -z "$temp_cel" ] && echo "temperature_celsius_raw_value{${labels},smart_id=\"194\"} ${temp_cel}" - [ ! -z "$lbas_read" ] && echo "total_lbas_read_raw_value{${labels},smart_id=\"242\"} ${lbas_read}" - [ ! -z "$power_cycles" ] && echo "power_cycles_count_raw_value{${labels},smart_id=\"255\"} ${power_cycles}" - [ ! -z "$unsafe_shutdowns" ] && echo "unsafe_shutdowns_count_raw_value{${labels},smart_id=\"255\"} ${unsafe_shutdowns}" - [ ! -z "$grown_defects" ] && echo "grown_defects_count_raw_value{${labels},smart_id=\"12\"} ${grown_defects}" - [ ! -z "$percentage_used" ] && echo "percentage_used_raw_value{${labels},smart_id=\"255\"} ${percentage_used}" - [ ! -z "$error_info_log_entry" ] && echo "error_information_log_entries_raw_value{${labels},smart_id=\"255\"} ${error_info_log_entry}" - [ ! -z "$host_read_commands" ] && echo "host_read_commands_raw_value{${labels},smart_id=\"255\"} ${host_read_commands}" - [ ! -z "$host_write_commands" ] && echo "host_write_commands_raw_value{${labels},smart_id=\"255\"} ${host_write_commands}" - [ ! -z "$controller_busy_time" ] && echo "controller_busy_time_raw_value{${labels},smart_id=\"255\"} ${controller_busy_time}" - [ ! -z "$available_spare" ] && echo "available_spare_raw_value{${labels},smart_id=\"255\"} ${available_spare}" - [ ! -z "$available_spare_threshold" ] && echo "available_spare_threshold{${labels},smart_id=\"255\"} ${available_spare_threshold}" - [ ! -z "$media_and_data_integrity_errors" ] && echo "media_and_data_integrity_errors_count_raw_value{${labels},smart_id=\"255\"} ${media_and_data_integrity_errors}" + # Add new NVME attributes here. + [ -n "$available_spare" ] && echo "available_spare_raw_value{${labels},smart_id=\"255\"} ${available_spare}" + [ -n "$available_spare_threshold" ] && echo "available_spare_threshold{${labels},smart_id=\"255\"} ${available_spare_threshold}" + [ -n "$controller_busy_time" ] && echo "controller_busy_time_raw_value{${labels},smart_id=\"255\"} ${controller_busy_time}" + [ -n "$data_units_written" ] && echo "data_units_written_raw_value{${labels},smart_id=\"8\"} ${data_units_written}" + [ -n "$lbas_read" ] && echo "total_lbas_read_raw_value{${labels},smart_id=\"242\"} ${lbas_read}" + [ -n "$error_info_log_entry" ] && echo "error_information_log_entries_raw_value{${labels},smart_id=\"255\"} ${error_info_log_entry}" + [ -n "$grown_defects" ] && echo "grown_defects_count_raw_value{${labels},smart_id=\"12\"} ${grown_defects}" + [ -n "$host_read_commands" ] && echo "host_read_commands_raw_value{${labels},smart_id=\"255\"} ${host_read_commands}" + [ -n "$host_write_commands" ] && echo "host_write_commands_raw_value{${labels},smart_id=\"255\"} ${host_write_commands}" + [ -n "$media_and_data_integrity_errors" ] && echo "media_and_data_integrity_errors_count_raw_value{${labels},smart_id=\"255\"} ${media_and_data_integrity_errors}" + [ -n "$percentage_used" ] && echo "percentage_used_raw_value{${labels},smart_id=\"255\"} ${percentage_used}" + [ -n "$power_cycles" ] && echo "power_cycles_count_raw_value{${labels},smart_id=\"255\"} ${power_cycles}" + [ -n "$power_on" ] && echo "power_on_hours_raw_value{${labels},smart_id=\"9\"} ${power_on}" + [ -n "$temp_cel" ] && echo "temperature_celsius_raw_value{${labels},smart_id=\"194\"} ${temp_cel}" + [ -n "$unsafe_shutdowns" ] && echo "unsafe_shutdowns_count_raw_value{${labels},smart_id=\"255\"} ${unsafe_shutdowns}" } @@ -148,7 +158,7 @@ parse_smartctl_info() { local -i smart_available=0 smart_enabled=0 smart_healthy=0 local disk="$1" disk_type="$2" local model_family='' device_model='' serial_number='' fw_version='' vendor='' product='' revision='' lun_id='' - while read line; do + while read -r line; do info_type="$(echo "${line}" | cut -f1 -d: | tr ' ' '_')" info_value="$(echo "${line}" | cut -f2- -d: | sed 's/^ \+//g' | sed 's/"/\\"/')" case "${info_type}" in @@ -201,6 +211,7 @@ format_output() { awk -F'{' "${output_format_awk}" } +main() { smartctl_version="$(/usr/sbin/smartctl -V | head -n1 | awk '$1 == "smartctl" {print $2}')" echo "smartctl_version{version=\"${smartctl_version}\"} 1" | format_output @@ -212,8 +223,8 @@ fi device_list="$(/usr/sbin/smartctl --scan-open | awk '/^\/dev/{print $1 "|" $3}')" for device in ${device_list}; do - disk="$(echo ${device} | cut -f1 -d'|')" - type="$(echo ${device} | cut -f2 -d'|')" + disk="$(echo "${device}" | cut -f1 -d'|')" + type="$(echo "${device}" | cut -f2 -d'|')" active=1 echo "smartctl_run{disk=\"${disk}\",type=\"${type}\"}" "$(TZ=UTC date '+%s')" # Check if the device is in a low-power mode @@ -224,7 +235,9 @@ for device in ${device_list}; do # Get the SMART information and health /usr/sbin/smartctl -i -H -d "${type}" "${disk}" | parse_smartctl_info "${disk}" "${type}" # Get the SMART attributes + echo "${type}" case ${type} in + # Actual command where the metrics are parsed from sat) /usr/sbin/smartctl -A -d "${type}" "${disk}" | parse_smartctl_attributes "${disk}" "${type}" ;; sat+megaraid*) /usr/sbin/smartctl -A -d "${type}" "${disk}" | parse_smartctl_attributes "${disk}" "${type}" ;; scsi) /usr/sbin/smartctl -A -d "${type}" "${disk}" | parse_smartctl_scsi_attributes "${disk}" "${type}" ;; @@ -236,3 +249,6 @@ for device in ${device_list}; do ;; esac done | format_output +} + +main diff --git a/test/bats b/test/bats new file mode 160000 index 0000000..190c7c9 --- /dev/null +++ b/test/bats @@ -0,0 +1 @@ +Subproject commit 190c7c9e793d93eed3ab873f5b6b6c790df44045 diff --git a/test/test_helper/bats-assert b/test/test_helper/bats-assert new file mode 160000 index 0000000..e2d855b --- /dev/null +++ b/test/test_helper/bats-assert @@ -0,0 +1 @@ +Subproject commit e2d855bc78619ee15b0c702b5c30fb074101159f diff --git a/test/test_helper/bats-support b/test/test_helper/bats-support new file mode 160000 index 0000000..9bf10e8 --- /dev/null +++ b/test/test_helper/bats-support @@ -0,0 +1 @@ +Subproject commit 9bf10e876dd6b624fe44423f0b35e064225f7556 diff --git a/test/test_smartmon-json.bats b/test/test_smartmon-json.bats new file mode 100644 index 0000000..b973317 --- /dev/null +++ b/test/test_smartmon-json.bats @@ -0,0 +1,390 @@ +#!/usr/bin/env bats + +setup() { + load 'test_helper/bats-assert/load' + load 'test_helper/bats-support/load' + load '../src/smartmon-json.sh' + # make executables in src/ visible to PATH + PATH="$( pwd )/src/smartmon-json.sh:$PATH" +} + +### Begin Helper Functions +# Helper for parse_smartctl_nvme_attributes_json +run_parse_smartctl_nvme_attributes_json() { + local disk="$1" + local disk_type="$2" + local test_data="$3" + # echo "$test_data" | parse_smartctl_nvme_attributes_json "$disk" "$disk_type" + parse_smartctl_nvme_attributes_json "$disk" "$disk_type" "$test_data" +} + +# Helper for parse_smartctl_attributes_json +run_parse_smartctl_attributes_json() { + local disk="$1" + local disk_type="$2" + local test_data="$3" + # echo "$test_data" | parse_smartctl_attributes_json "$disk" "$disk_type" + parse_smartctl_attributes_json "$disk" "$disk_type" "$test_data" +} +### End Helper Functions + +#test_info_json=$( jq -n \ +#' +#{ +# "json_format_version": [ +# 1, +# 0 +# ], +# "smartctl": { +# "version": [ +# 7, +# 4 +# ], +# "pre_release": false, +# "svn_revision": "5530", +# "platform_info": "x86_64-linux-1.2.3-4-MANJARO", +# "build_info": "(local build)", +# "argv": [ +# "smartctl", +# "-i", +# "-j", +# "-H", +# "/dev/nvme0" +# ], +# "exit_status": 0 +# }, +# "local_time": { +# "time_t": 1234567890, +# "asctime": "Thu Jul 1 22:22:22 2024 CEST" +# }, +# "device": { +# "name": "/dev/nvme0", +# "info_name": "/dev/nvme0", +# "type": "nvme", +# "protocol": "NVMe" +# }, +# "model_name": "WD_BLACK SN770 1TB", +# "serial_number": "12345U123456", +# "firmware_version": "112200WD", +# "nvme_pci_vendor": { +# "id": 5559, +# "subsystem_id": 5559 +# }, +# "nvme_ieee_oui_identifier": 6980, +# "nvme_total_capacity": 1000204886016, +# "nvme_unallocated_capacity": 0, +# "nvme_controller_id": 0, +# "nvme_version": { +# "string": "1.4", +# "value": 66560 +# }, +# "nvme_number_of_namespaces": 1, +# "nvme_namespaces": [ +# { +# "id": 1, +# "size": { +# "blocks": 1953525168, +# "bytes": 1000204886016 +# }, +# "capacity": { +# "blocks": 1953525168, +# "bytes": 1000204886016 +# }, +# "utilization": { +# "blocks": 1953525168, +# "bytes": 1000204886016 +# }, +# "formatted_lba_size": 512, +# "eui64": { +# "oui": 6980, +# "ext_id": 319051012292 +# } +# } +# ], +# "user_capacity": { +# "blocks": 1953525168, +# "bytes": 1000204886016 +# }, +# "logical_block_size": 512, +# "smart_support": { +# "available": true, +# "enabled": true +# }, +# "smart_status": { +# "passed": true, +# "nvme": { +# "value": 0 +# } +# } +#} +#' ) +# +# +#echo "$test_info_json" +# +# +#test_values_json=$( jq -n \ +#' +#{ +# "json_format_version": [ +# 1, +# 0 +# ], +# "smartctl": { +# "version": [ +# 7, +# 4 +# ], +# "pre_release": false, +# "svn_revision": "5530", +# "platform_info": "x86_64-linux-1.2.3-4-MANJARO", +# "build_info": "(local build)", +# "argv": [ +# "smartctl", +# "-A", +# "-j", +# "-H", +# "/dev/nvme0" +# ], +# "exit_status": 0 +# }, +# "local_time": { +# "time_t": 1234567890, +# "asctime": "Thu Jul 1 22:22:22 2024 CEST" +# }, +# "device": { +# "name": "/dev/nvme0", +# "info_name": "/dev/nvme0", +# "type": "nvme", +# "protocol": "NVMe" +# }, +# "smart_status": { +# "passed": true, +# "nvme": { +# "value": 0 +# } +# }, +# "nvme_smart_health_information_log": { +# "critical_warning": 0, +# "temperature": 34, +# "available_spare": 100, +# "available_spare_threshold": 10, +# "percentage_used": 0, +# "data_units_read": 2454092, +# "data_units_written": 4090197, +# "host_reads": 24207664, +# "host_writes": 53302692, +# "controller_busy_time": 83, +# "power_cycles": 1109, +# "power_on_hours": 74, +# "unsafe_shutdowns": 53, +# "media_errors": 0, +# "num_err_log_entries": 0, +# "warning_temp_time": 0, +# "critical_comp_time": 0, +# "temperature_sensors": [ +# 42, +# 34 +# ] +# }, +# "temperature": { +# "current": 34 +# }, +# "power_cycle_count": 1109, +# "power_on_time": { +# "hours": 74 +# } +#} +#' ) +#echo "$test_values_json" + +# Example test_data for: "parse_smartctl_nvme_attributes_json" +# +#sudo smartctl -A -d nvme /dev/nvme0 +#smartctl 7.4 2023-08-01 r5530 [x86_64-linux-6.6.32-1-MANJARO] (local build) +#Copyright (C) 2002-23, Bruce Allen, Christian Franke, www.smartmontools.org +# +#=== START OF SMART DATA SECTION === +#SMART/Health Information (NVMe Log 0x02) +#Critical Warning: 0x00 +#Temperature: 28 Celsius +#Available Spare: 100% +#Available Spare Threshold: 10% +#Percentage Used: 0% +#Data Units Read: 2,389,400 [1.22 TB] +#Data Units Written: 3,958,438 [2.02 TB] +#Host Read Commands: 23,444,268 +#Host Write Commands: 51,391,963 +#Controller Busy Time: 79 +#Power Cycles: 1,012 +#Power On Hours: 70 +#Unsafe Shutdowns: 52 +#Media and Data Integrity Errors: 0 +#Error Information Log Entries: 0 +#Warning Comp. Temperature Time: 0 +#Critical Comp. Temperature Time: 0 +#Temperature Sensor 1: 36 Celsius +#Temperature Sensor 2: 28 Celsius + +############################################################################### +# sudo smartctl -A /dev/sda +#smartctl 7.1 2019-12-30 r5022 [x86_64-linux-5.4.0-182-generic] (local build) +#Copyright (C) 2002-19, Bruce Allen, Christian Franke, www.smartmontools.org +# +#=== START OF READ SMART DATA SECTION === +#SMART Attributes Data Structure revision number: 1 +#Vendor Specific SMART Attributes with Thresholds: +#ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE +# 1 Raw_Read_Error_Rate 0x002f 100 100 050 Pre-fail Always - 0 +# 5 Reallocate_NAND_Blk_Cnt 0x0032 100 100 010 Old_age Always - 0 +# 9 Power_On_Hours 0x0032 100 100 050 Old_age Always - 30104 +# 12 Power_Cycle_Count 0x0032 100 100 050 Old_age Always - 57 +#171 Program_Fail_Count 0x0032 100 100 050 Old_age Always - 0 +#172 Erase_Fail_Count 0x0032 100 100 050 Old_age Always - 0 +#173 Ave_Block-Erase_Count 0x0032 100 100 050 Old_age Always - 183 +#174 Unexpect_Power_Loss_Ct 0x0032 100 100 050 Old_age Always - 31 +#180 Unused_Reserve_NAND_Blk 0x0032 100 100 050 Old_age Always - 100 +#183 SATA_Interfac_Downshift 0x0032 100 100 050 Old_age Always - 120 +#184 Error_Correction_Count 0x0032 100 100 050 Old_age Always - 8 +#187 Reported_Uncorrect 0x0032 100 100 050 Old_age Always - 0 +#194 Temperature_Celsius 0x0022 069 049 050 Old_age Always In_the_past 31 (Min/Max 25/51) +#196 Reallocated_Event_Count 0x0032 100 100 050 Old_age Always - 0 +#197 Current_Pending_Sector 0x0032 100 100 050 Old_age Always - 0 +#198 Offline_Uncorrectable 0x0030 100 100 050 Old_age Offline - 0 +#199 UDMA_CRC_Error_Count 0x0032 100 100 050 Old_age Always - 0 +#202 Percent_Lifetime_Remain 0x0030 088 088 001 Old_age Offline - 88 +#206 Write_Error_Rate 0x002e 100 100 050 Old_age Always - 0 +#210 Success_RAIN_Recov_Cnt 0x0032 100 100 050 Old_age Always - 0 +#246 Total_LBAs_Written 0x0032 100 100 050 Old_age Always - 8866549306 +#247 Host_Program_Page_Count 0x0032 100 100 050 Old_age Always - 277079665 +#248 FTL_Program_Page_Count 0x0032 100 100 050 Old_age Always - 318468096 + + +### Begin Tests + +@test "parse_smartctl_nvme_attributes_json with temperature test_data" { + local disk="nvme0" + local disk_type="nvme" + local test_data='{"nvme_smart_health_information_log": {"temperature": 28}}' + + run run_parse_smartctl_nvme_attributes_json "$disk" "$disk_type" "$test_data" + assert_output 'temperature{disk="nvme0",type="nvme"} 28' +} + +@test "parse_smartctl_nvme_attributes_json with empty test_data" { + local disk="nvme0" + local disk_type="nvme" + local test_data='{}' + + run run_parse_smartctl_nvme_attributes_json "$disk" "$disk_type" "$test_data" + assert_output "" +} + +@test "parse_smartctl_nvme_attributes_json data_units_written" { + local disk="nvme0" + local disk_type="nvme" + local test_data='{"nvme_smart_health_information_log": {"data_units_written": 3958438}}' + + run run_parse_smartctl_nvme_attributes_json "$disk" "$disk_type" "$test_data" + assert_output - <<-EOF +data_units_written{disk="nvme0",type="nvme"} 3958438 +EOF +} + +@test "parse_smartctl_sata_attributes_json power_on_hours" { + local disk="sda" + local disk_type="sat" + local test_data + test_data=$(jq -n \ +' +{ + "json_format_version": [ + 1, + 0 + ], + "ata_smart_attributes": { + "table": [ + { + "id": 9, + "name": "Power_On_Hours", + "value": 100, + "worst": 100, + "thresh": 50, + "when_failed": "", + "flags": { + "value": 50, + "string": "-O--CK ", + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true + }, + "raw": { + "value": 30839, + "string": "30839" + } + } + ] + } +} +' +) + + run run_parse_smartctl_attributes_json "$disk" "$disk_type" "$test_data" + assert_output - <<-EOF + Power_On_Hours_value{disk="sda",type="sat",smart_id="9"} 100 + Power_On_Hours_worst{disk="sda",type="sat",smart_id="9"} 100 + Power_On_Hours_threshold{disk="sda",type="sat",smart_id="9"} 50 + Power_On_Hours_raw_value{disk="sda",type="sat",smart_id="9"} 30839 +EOF +} + +@test "parse_smartctl_sata_attributes_json invalid data" { + local disk="sda" + local disk_type="sat" + local test_data='{}' + + run run_parse_smartctl_attributes_json "$disk" "$disk_type" "$test_data" + assert_output "" +} + +@test "parse_smartctl_sata_attributes_json replace hyphens" { + local disk="sda" + local disk_type="sat" + local test_data + test_data=$(jq -n \ +' +{ + "json_format_version": [ + 1, + 0 + ], + "ata_smart_attributes": { + "table": [ + { + "id": 9, + "name": "Power-On-Hours", + "value": 100, + "worst": 100, + "thresh": 50, + "raw": { + "value": 30839, + "string": "30839" + } + } + ] + } +} +' +) + + run run_parse_smartctl_attributes_json "$disk" "$disk_type" "$test_data" + assert_output - <<-EOF + Power_On_Hours_value{disk="sda",type="sat",smart_id="9"} 100 + Power_On_Hours_worst{disk="sda",type="sat",smart_id="9"} 100 + Power_On_Hours_threshold{disk="sda",type="sat",smart_id="9"} 50 + Power_On_Hours_raw_value{disk="sda",type="sat",smart_id="9"} 30839 +EOF +} diff --git a/test/test_smartmon.bak-bats b/test/test_smartmon.bak-bats new file mode 100644 index 0000000..f7bbde6 --- /dev/null +++ b/test/test_smartmon.bak-bats @@ -0,0 +1,199 @@ +#!/usr/bin/env bats + +setup() { + load 'test_helper/bats-assert/load' + load 'test_helper/bats-support/load' + load '../src/smartmon.sh' + # make executables in src/ visible to PATH + PATH="$( pwd )/src/smartmon.sh:$PATH" +} + +### Begin Helper Functions +# Helper for parse_smartctl_nvme_attributes +run_parse_smartctl_nvme_attributes() { + local disk="$1" + local disk_type="$2" + local test_data="$3" + echo "$test_data" | parse_smartctl_nvme_attributes "$disk" "$disk_type" +} + +# Helper for parse_smartctl_attributes +run_parse_smartctl_attributes() { + local disk="$1" + local disk_type="$2" + local test_data="$3" + local smartmon_attrs="$4" + echo "$test_data" | parse_smartctl_attributes "$disk" "$disk_type" +} +### End Helper Functions + +# Example test_data for: "parse_smartctl_nvme_attributes" +# +#sudo smartctl -A -d nvme /dev/nvme0 +#smartctl 7.4 2023-08-01 r5530 [x86_64-linux-6.6.32-1-MANJARO] (local build) +#Copyright (C) 2002-23, Bruce Allen, Christian Franke, www.smartmontools.org +# +#=== START OF SMART DATA SECTION === +#SMART/Health Information (NVMe Log 0x02) +#Critical Warning: 0x00 +#Temperature: 28 Celsius +#Available Spare: 100% +#Available Spare Threshold: 10% +#Percentage Used: 0% +#Data Units Read: 2,389,400 [1.22 TB] +#Data Units Written: 3,958,438 [2.02 TB] +#Host Read Commands: 23,444,268 +#Host Write Commands: 51,391,963 +#Controller Busy Time: 79 +#Power Cycles: 1,012 +#Power On Hours: 70 +#Unsafe Shutdowns: 52 +#Media and Data Integrity Errors: 0 +#Error Information Log Entries: 0 +#Warning Comp. Temperature Time: 0 +#Critical Comp. Temperature Time: 0 +#Temperature Sensor 1: 36 Celsius +#Temperature Sensor 2: 28 Celsius + +############################################################################### +# sudo smartctl -A /dev/sda +#smartctl 7.1 2019-12-30 r5022 [x86_64-linux-5.4.0-182-generic] (local build) +#Copyright (C) 2002-19, Bruce Allen, Christian Franke, www.smartmontools.org +# +#=== START OF READ SMART DATA SECTION === +#SMART Attributes Data Structure revision number: 1 +#Vendor Specific SMART Attributes with Thresholds: +#ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE +# 1 Raw_Read_Error_Rate 0x002f 100 100 050 Pre-fail Always - 0 +# 5 Reallocate_NAND_Blk_Cnt 0x0032 100 100 010 Old_age Always - 0 +# 9 Power_On_Hours 0x0032 100 100 050 Old_age Always - 30104 +# 12 Power_Cycle_Count 0x0032 100 100 050 Old_age Always - 57 +#171 Program_Fail_Count 0x0032 100 100 050 Old_age Always - 0 +#172 Erase_Fail_Count 0x0032 100 100 050 Old_age Always - 0 +#173 Ave_Block-Erase_Count 0x0032 100 100 050 Old_age Always - 183 +#174 Unexpect_Power_Loss_Ct 0x0032 100 100 050 Old_age Always - 31 +#180 Unused_Reserve_NAND_Blk 0x0032 100 100 050 Old_age Always - 100 +#183 SATA_Interfac_Downshift 0x0032 100 100 050 Old_age Always - 120 +#184 Error_Correction_Count 0x0032 100 100 050 Old_age Always - 8 +#187 Reported_Uncorrect 0x0032 100 100 050 Old_age Always - 0 +#194 Temperature_Celsius 0x0022 069 049 050 Old_age Always In_the_past 31 (Min/Max 25/51) +#196 Reallocated_Event_Count 0x0032 100 100 050 Old_age Always - 0 +#197 Current_Pending_Sector 0x0032 100 100 050 Old_age Always - 0 +#198 Offline_Uncorrectable 0x0030 100 100 050 Old_age Offline - 0 +#199 UDMA_CRC_Error_Count 0x0032 100 100 050 Old_age Always - 0 +#202 Percent_Lifetime_Remain 0x0030 088 088 001 Old_age Offline - 88 +#206 Write_Error_Rate 0x002e 100 100 050 Old_age Always - 0 +#210 Success_RAIN_Recov_Cnt 0x0032 100 100 050 Old_age Always - 0 +#246 Total_LBAs_Written 0x0032 100 100 050 Old_age Always - 8866549306 +#247 Host_Program_Page_Count 0x0032 100 100 050 Old_age Always - 277079665 +#248 FTL_Program_Page_Count 0x0032 100 100 050 Old_age Always - 318468096 + + +### Begin Tests +@test "parse_smartctl_nvme_attributes with temperature test_data" { + local disk="nvme0" + local disk_type="nvme" + local test_data="Temperature: 28 Celsius" + + run run_parse_smartctl_nvme_attributes "$disk" "$disk_type" "$test_data" + # Note: Content of heredoc needs tabs as indentation to work. [%s/" "/\t/g] + assert_output - <<-EOF + Temperature + 28 Celsius + temperature_celsius_raw_value{disk="nvme0",type="nvme",smart_id="194"} 28 +EOF +} + +@test "parse_smartctl_nvme_attributes with empty test_data" { + local disk="nvme0" + local disk_type="nvme" + local test_data="" + + run run_parse_smartctl_nvme_attributes "$disk" "$disk_type" "$test_data" + # Note: Content of heredoc needs tabs as indentation to work. [%s/" "/\t/g] + assert_output "" +} + +@test "parse_smartctl_nvme_attributes data_units_written" { + local disk="nvme0" + local disk_type="nvme" + local test_data="Data Units Written: 3,958,438 [2.02 TB]" + + run run_parse_smartctl_nvme_attributes "$disk" "$disk_type" "$test_data" + # Note: Content of heredoc needs tabs as indentation to work. [%s/" "/\t/g] + assert_output - <<-EOF + Data_Units_Written + data_units_written_raw_value{disk="nvme0",type="nvme",smart_id="8"} 3958438 +EOF +} + +@test "parse_smartctl_sata_attributes power_on_hours" { + local disk="sda" + local disk_type="sat" + local test_data="9 Power_On_Hours 0x0032 100 100 050 Old_age Always - 30104" + local smartmon_attrs="power_on_hours" + + run run_parse_smartctl_attributes "$disk" "$disk_type" "$test_data" + # Note: Content of heredoc needs tabs as indentation to work. [%s/" "/\t/g] + assert_output - <<-EOF + power_on_hours_value{disk="sda",type="sat",smart_id="9"} 100 + power_on_hours_worst{disk="sda",type="sat",smart_id="9"} 100 + power_on_hours_threshold{disk="sda",type="sat",smart_id="9"} 50 + power_on_hours_raw_value{disk="sda",type="sat",smart_id="9"} 3.010400e+04 +EOF +} + +@test "parse_smartctl_sata_attributes invalid data" { + local disk="sda" + local disk_type="sat" + local test_data="FOOBAR" + + run run_parse_smartctl_attributes "$disk" "$disk_type" "$test_data" + # No output expected + assert_output "" +} + +@test "parse_smartctl_sata_attributes replace hyphens" { + local disk="sda" + local disk_type="sat" + local test_data="9 Power-On-Hours" + local smartmon_attrs="power_on_hours" + + run run_parse_smartctl_attributes "$disk" "$disk_type" "$test_data" "$smartmon_attrs" + assert_output - <<-EOF + power_on_hours_value{disk="sda",type="sat",smart_id="9"} 0 + power_on_hours_worst{disk="sda",type="sat",smart_id="9"} 0 + power_on_hours_threshold{disk="sda",type="sat",smart_id="9"} 0 + power_on_hours_raw_value{disk="sda",type="sat",smart_id="9"} 0.000000e+00 +EOF +} + +@test "parse_smartctl_sata_attributes multiple inputs" { + local disk="sda" + local disk_type="sat" + local smartmon_attrs="power_on_hours|power_cycle_count" + local test_data="9 Power_On_Hours 0x0032 100 100 050 Old_age Always - 30104\n 12 Power_Cycle_Count 0x0032 100 100 050 Old_age Always - 57\n" + + run run_parse_smartctl_attributes "$disk" "$disk_type" "$test_data" "$smartmon_attrs" + assert_output - <<-EOF + power_on_hours_value{disk="sda",type="sat",smart_id="9"} 100 + power_on_hours_worst{disk="sda",type="sat",smart_id="9"} 100 + power_on_hours_threshold{disk="sda",type="sat",smart_id="9"} 50 + power_on_hours_raw_value{disk="sda",type="sat",smart_id="9"} 3.010400e+04 +EOF +} + +@test "parse_smartctl_sata_attributes mixed inputs valid and invalid" { + local disk="sda" + local disk_type="sat" + local smartmon_attrs="power_on_hours|power_cycle_count" + local test_data="9 Power_On_Hours 0x0032 100 100 050 Old_age Always - 30104\n THISISINVALID \n 12 Power_Cycle_Count 0x0032 100 100 050 Old_age Always - 57\n" + + run run_parse_smartctl_attributes "$disk" "$disk_type" "$test_data" "$smartmon_attrs" + assert_output - <<-EOF + power_on_hours_value{disk="sda",type="sat",smart_id="9"} 100 + power_on_hours_worst{disk="sda",type="sat",smart_id="9"} 100 + power_on_hours_threshold{disk="sda",type="sat",smart_id="9"} 50 + power_on_hours_raw_value{disk="sda",type="sat",smart_id="9"} 3.010400e+04 +EOF +} From 906272d3cd1bc1a6d257bcb62102bcc3ba87dd17 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Tue, 23 Jul 2024 20:32:40 +0200 Subject: [PATCH 06/54] wip:tests --- README.md | 13 +- json.out | 72 --------- parse.out | 60 ------- src/smartmon-json.sh | 101 +++++++++--- test/test_smartmon-json.bats | 295 +++++------------------------------ 5 files changed, 137 insertions(+), 404 deletions(-) delete mode 100644 json.out delete mode 100644 parse.out diff --git a/README.md b/README.md index 1a538f8..840d463 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,14 @@ https://grafana.com/dashboards/10530 https://grafana.com/dashboards/10531 ## Purpose -This text_collector is a customized version of the S.M.A.R.T. `text_collector` example from `node_exporter` github repo: +This text_collector is a customized version of the S.M.A.R.T. `text_collector` +example from `node_exporter` github repo: https://github.com/prometheus/node_exporter/tree/master/text_collector_examples +This bash script uses `smartctl` to get S.M.A.R.T. values. It is designed to +work with SATA and NVME disks. It should also work with SCSI disks but is not +tested. + ## Requirements - Prometheus - node_exporter @@ -36,6 +41,7 @@ Example for UBUNTU `crontab -e`: `*/5 * * * * /usr/local/bin/smartmon.sh > /var/lib/node_exporter/textfile_collector/smart_metrics.prom` +# TODO: adapt to new script ## How to add specific S.M.A.R.T. attributes If you are missing some attributes you can extend the text_collector. Add the desired attributes to `smartmon_attrs` array in `smartmon.sh`. @@ -70,5 +76,8 @@ https://www.smartmontools.org/wiki/Download#Installfromthesourcetarball ## Tests -To do tests install bats (bats-core): +# TODO: tests install bats (bats-core): [bats-tutorial](https://bats-core.readthedocs.io/en/stable/tutorial.html) + +## TODO +- Proper implementation and tets for SCSI type disks. Please provide input if interested. diff --git a/json.out b/json.out deleted file mode 100644 index e34b9c0..0000000 --- a/json.out +++ /dev/null @@ -1,72 +0,0 @@ -# HELP smartmon_smartctl_version SMART metric smartctl_version -# TYPE smartmon_smartctl_version gauge -smartmon_smartctl_version{version="7.4"} 1 -# HELP smartmon_available_spare SMART metric available_spare -# TYPE smartmon_available_spare gauge -smartmon_available_spare{disk="/dev/nvme0",type="nvme"} 100 -# HELP smartmon_available_spare_threshold SMART metric available_spare_threshold -# TYPE smartmon_available_spare_threshold gauge -smartmon_available_spare_threshold{disk="/dev/nvme0",type="nvme"} 10 -# HELP smartmon_controller_busy_time SMART metric controller_busy_time -# TYPE smartmon_controller_busy_time gauge -smartmon_controller_busy_time{disk="/dev/nvme0",type="nvme"} 83 -# HELP smartmon_critical_comp_time SMART metric critical_comp_time -# TYPE smartmon_critical_comp_time gauge -smartmon_critical_comp_time{disk="/dev/nvme0",type="nvme"} 0 -# HELP smartmon_critical_warning SMART metric critical_warning -# TYPE smartmon_critical_warning gauge -smartmon_critical_warning{disk="/dev/nvme0",type="nvme"} 0 -# HELP smartmon_data_units_read SMART metric data_units_read -# TYPE smartmon_data_units_read gauge -smartmon_data_units_read{disk="/dev/nvme0",type="nvme"} 2453999 -# HELP smartmon_data_units_written SMART metric data_units_written -# TYPE smartmon_data_units_written gauge -smartmon_data_units_written{disk="/dev/nvme0",type="nvme"} 4088338 -# HELP smartmon_device_active SMART metric device_active -# TYPE smartmon_device_active gauge -smartmon_device_active{disk="/dev/nvme0",type="nvme"} 1 -# HELP smartmon_device_info SMART metric device_info -# TYPE smartmon_device_info gauge -smartmon_device_info{disk="/dev/nvme0",type="nvme",vendor="",product="",revision="",lun_id="",model_family="",device_model="",serial_number="23048U472208",firmware_version="731100WD"} 1 -# HELP smartmon_device_smart_available SMART metric device_smart_available -# TYPE smartmon_device_smart_available gauge -smartmon_device_smart_available{disk="/dev/nvme0",type="nvme"} 0 -# HELP smartmon_device_smart_enabled SMART metric device_smart_enabled -# TYPE smartmon_device_smart_enabled gauge -smartmon_device_smart_enabled{disk="/dev/nvme0",type="nvme"} 0 -# HELP smartmon_device_smart_healthy SMART metric device_smart_healthy -# TYPE smartmon_device_smart_healthy gauge -smartmon_device_smart_healthy{disk="/dev/nvme0",type="nvme"} true -# HELP smartmon_host_reads SMART metric host_reads -# TYPE smartmon_host_reads gauge -smartmon_host_reads{disk="/dev/nvme0",type="nvme"} 24206161 -# HELP smartmon_host_writes SMART metric host_writes -# TYPE smartmon_host_writes gauge -smartmon_host_writes{disk="/dev/nvme0",type="nvme"} 53251926 -# HELP smartmon_media_errors SMART metric media_errors -# TYPE smartmon_media_errors gauge -smartmon_media_errors{disk="/dev/nvme0",type="nvme"} 0 -# HELP smartmon_num_err_log_entries SMART metric num_err_log_entries -# TYPE smartmon_num_err_log_entries gauge -smartmon_num_err_log_entries{disk="/dev/nvme0",type="nvme"} 0 -# HELP smartmon_percentage_used SMART metric percentage_used -# TYPE smartmon_percentage_used gauge -smartmon_percentage_used{disk="/dev/nvme0",type="nvme"} 0 -# HELP smartmon_power_cycles SMART metric power_cycles -# TYPE smartmon_power_cycles gauge -smartmon_power_cycles{disk="/dev/nvme0",type="nvme"} 1108 -# HELP smartmon_power_on_hours SMART metric power_on_hours -# TYPE smartmon_power_on_hours gauge -smartmon_power_on_hours{disk="/dev/nvme0",type="nvme"} 74 -# HELP smartmon_smartctl_run SMART metric smartctl_run -# TYPE smartmon_smartctl_run gauge -smartmon_smartctl_run{disk="/dev/nvme0",type="nvme"} 1721322621 -# HELP smartmon_temperature SMART metric temperature -# TYPE smartmon_temperature gauge -smartmon_temperature{disk="/dev/nvme0",type="nvme"} 30 -# HELP smartmon_unsafe_shutdowns SMART metric unsafe_shutdowns -# TYPE smartmon_unsafe_shutdowns gauge -smartmon_unsafe_shutdowns{disk="/dev/nvme0",type="nvme"} 53 -# HELP smartmon_warning_temp_time SMART metric warning_temp_time -# TYPE smartmon_warning_temp_time gauge -smartmon_warning_temp_time{disk="/dev/nvme0",type="nvme"} 0 diff --git a/parse.out b/parse.out deleted file mode 100644 index 264d6e8..0000000 --- a/parse.out +++ /dev/null @@ -1,60 +0,0 @@ -# HELP smartmon_smartctl_version SMART metric smartctl_version -# TYPE smartmon_smartctl_version gauge -smartmon_smartctl_version{version="7.4"} 1 -# HELP smartmon_available_spare_raw_value SMART metric available_spare_raw_value -# TYPE smartmon_available_spare_raw_value gauge -smartmon_available_spare_raw_value{disk="/dev/nvme0",type="nvme",smart_id="255"} 100 -# HELP smartmon_available_spare_threshold SMART metric available_spare_threshold -# TYPE smartmon_available_spare_threshold gauge -smartmon_available_spare_threshold{disk="/dev/nvme0",type="nvme",smart_id="255"} 10 -# HELP smartmon_controller_busy_time_raw_value SMART metric controller_busy_time_raw_value -# TYPE smartmon_controller_busy_time_raw_value gauge -smartmon_controller_busy_time_raw_value{disk="/dev/nvme0",type="nvme",smart_id="255"} 82 -# HELP smartmon_data_units_written_raw_value SMART metric data_units_written_raw_value -# TYPE smartmon_data_units_written_raw_value gauge -smartmon_data_units_written_raw_value{disk="/dev/nvme0",type="nvme",smart_id="8"} 4042358 -# HELP smartmon_device_active SMART metric device_active -# TYPE smartmon_device_active gauge -smartmon_device_active{disk="/dev/nvme0",type="nvme"} 1 -# HELP smartmon_device_info SMART metric device_info -# TYPE smartmon_device_info gauge -smartmon_device_info{disk="/dev/nvme0",type="nvme",vendor="",product="",revision="",lun_id="",model_family="",device_model="",serial_number="23048U472208",firmware_version="731100WD"} 1 -# HELP smartmon_device_smart_available SMART metric device_smart_available -# TYPE smartmon_device_smart_available gauge -smartmon_device_smart_available{disk="/dev/nvme0",type="nvme"} 0 -# HELP smartmon_device_smart_enabled SMART metric device_smart_enabled -# TYPE smartmon_device_smart_enabled gauge -smartmon_device_smart_enabled{disk="/dev/nvme0",type="nvme"} 0 -# HELP smartmon_device_smart_healthy SMART metric device_smart_healthy -# TYPE smartmon_device_smart_healthy gauge -smartmon_device_smart_healthy{disk="/dev/nvme0",type="nvme"} 1 -# HELP smartmon_error_information_log_entries_raw_value SMART metric error_information_log_entries_raw_value -# TYPE smartmon_error_information_log_entries_raw_value gauge -smartmon_error_information_log_entries_raw_value{disk="/dev/nvme0",type="nvme",smart_id="255"} 0 -# HELP smartmon_host_read_commands_raw_value SMART metric host_read_commands_raw_value -# TYPE smartmon_host_read_commands_raw_value gauge -smartmon_host_read_commands_raw_value{disk="/dev/nvme0",type="nvme",smart_id="255"} 23953180 -# HELP smartmon_host_write_commands_raw_value SMART metric host_write_commands_raw_value -# TYPE smartmon_host_write_commands_raw_value gauge -smartmon_host_write_commands_raw_value{disk="/dev/nvme0",type="nvme",smart_id="255"} 52595138 -# HELP smartmon_media_and_data_integrity_errors_count_raw_value SMART metric media_and_data_integrity_errors_count_raw_value -# TYPE smartmon_media_and_data_integrity_errors_count_raw_value gauge -smartmon_media_and_data_integrity_errors_count_raw_value{disk="/dev/nvme0",type="nvme",smart_id="255"} 0 -# HELP smartmon_percentage_used_raw_value SMART metric percentage_used_raw_value -# TYPE smartmon_percentage_used_raw_value gauge -smartmon_percentage_used_raw_value{disk="/dev/nvme0",type="nvme",smart_id="255"} 0 -# HELP smartmon_power_cycles_count_raw_value SMART metric power_cycles_count_raw_value -# TYPE smartmon_power_cycles_count_raw_value gauge -smartmon_power_cycles_count_raw_value{disk="/dev/nvme0",type="nvme",smart_id="255"} 1 -# HELP smartmon_power_on_hours_raw_value SMART metric power_on_hours_raw_value -# TYPE smartmon_power_on_hours_raw_value gauge -smartmon_power_on_hours_raw_value{disk="/dev/nvme0",type="nvme",smart_id="9"} 73 -# HELP smartmon_smartctl_run SMART metric smartctl_run -# TYPE smartmon_smartctl_run gauge -smartmon_smartctl_run{disk="/dev/nvme0",type="nvme"} 1720264085 -# HELP smartmon_temperature_celsius_raw_value SMART metric temperature_celsius_raw_value -# TYPE smartmon_temperature_celsius_raw_value gauge -smartmon_temperature_celsius_raw_value{disk="/dev/nvme0",type="nvme",smart_id="194"} 32 -# HELP smartmon_unsafe_shutdowns_count_raw_value SMART metric unsafe_shutdowns_count_raw_value -# TYPE smartmon_unsafe_shutdowns_count_raw_value gauge -smartmon_unsafe_shutdowns_count_raw_value{disk="/dev/nvme0",type="nvme",smart_id="255"} 53 diff --git a/src/smartmon-json.sh b/src/smartmon-json.sh index 1b7e8f9..a80864d 100755 --- a/src/smartmon-json.sh +++ b/src/smartmon-json.sh @@ -1,13 +1,22 @@ -#!/bin/bash -# shellcheck disable=SC2155 +#!/usr/bin/env bash # This script is designed to collect SMART data from various types of # disks (ATA, NVMe, SCSI) and format it for Prometheus monitoring. +# Be aware that SCSI type is not tested! +# +# By design this script works with a filter list of values. Opposed to parsing +# all elements from the JSON output of smartctl this seems more robust as +# different vendors might use inconsistent value naming. + + +# Global Variables +device_infos="" # Ensure jq is installed. This function checks if the 'jq' command is available. ensure_jq_installed() { if ! command -v jq &>/dev/null; then - echo "jq could not be found. Please install it." + echo "jq could not be found. Please install it:" + echo "https://jqlang.github.io/jq/download/" exit 1 fi } @@ -18,32 +27,88 @@ ensure_jq_installed() { # $1 - Disk name # $2 - Disk type # $3 - JSON string containing SMART information -parse_smartctl_info_json() { +parse_smartctl_info_json_old() { local disk="$1" local disk_type="$2" local json="$3" local labels="disk=\"${disk}\",type=\"${disk_type}\"" # Extract various SMART attributes using jq - local model_family=$(echo "$json" | jq -r '.model_family // empty') - local device_model=$(echo "$json" | jq -r '.device_model // empty') - local serial_number=$(echo "$json" | jq -r '.serial_number // empty') - local fw_version=$(echo "$json" | jq -r '.firmware_version // empty') - local vendor=$(echo "$json" | jq -r '.vendor // empty') - local product=$(echo "$json" | jq -r '.product // empty') - local revision=$(echo "$json" | jq -r '.revision // empty') - local lun_id=$(echo "$json" | jq -r '.lun_id // empty') - local smart_available=$(echo "$json" | jq -r '.smart_support.is_available // 0') - local smart_enabled=$(echo "$json" | jq -r '.smart_support.is_enabled // 0') - local smart_healthy=$(echo "$json" | jq -r '.smart_status.passed // 0') - - # Print the extracted information in the Prometheus format - echo "device_info{${labels},vendor=\"${vendor}\",product=\"${product}\",revision=\"${revision}\",lun_id=\"${lun_id}\",model_family=\"${model_family}\",device_model=\"${device_model}\",serial_number=\"${serial_number}\",firmware_version=\"${fw_version}\"} 1" + local model_family device_model serial_number fw_version vendor product \ + revision lun_id smart_available smart_enabled smart_healthy + model_family=$(echo "$json" | jq -r '.model_family // empty') + device_model=$(echo "$json" | jq -r '.device_model // empty') + serial_number=$(echo "$json" | jq -r '.serial_number // empty') + fw_version=$(echo "$json" | jq -r '.firmware_version // empty') + vendor=$(echo "$json" | jq -r '.vendor // empty') + product=$(echo "$json" | jq -r '.product // empty') + revision=$(echo "$json" | jq -r '.revision // empty') + lun_id=$(echo "$json" | jq -r '.lun_id // empty') + smart_available=$(echo "$json" | jq -r '.smart_support.is_available // 0') + smart_enabled=$(echo "$json" | jq -r '.smart_support.is_enabled // 0') + smart_healthy=$(echo "$json" | jq -r '.smart_status.passed // 0') + + # Print the extracted information in Prometheus format + # Note: use TABS for indentation + device_infos=$(cat <<-EOF +device_info{${labels},\ +vendor="${vendor}",\ +product="${product}",\ +revision="${revision}",\ +lun_id="${lun_id}",\ +model_family="${model_family}",\ +device_model="${device_model}",\ +serial_number="${serial_number}",\ +firmware_version="${fw_version}"} 1 +EOF +) + echo "$device_infos" echo "device_smart_available{${labels}} ${smart_available}" echo "device_smart_enabled{${labels}} ${smart_enabled}" echo "device_smart_healthy{${labels}} ${smart_healthy}" } +parse_smartctl_info_json() { + local disk="$1" + local disk_type="$2" + local json="$3" + local labels="disk=\"${disk}\",type=\"${disk_type}\"" + + declare -a keys_general=("model_family" "device_model" "serial_number" "firmware_version" "vendor" "product" "revision" "lun_id") + declare -a keys_binary=("smart_support.is_available" "smart_support.is_enabled" "smart_status.passed") + + + # Print the extracted information in Prometheus format + # Note: use TABS for indentation + device_infos=$(cat <<-EOF +device_info{${labels}, +EOF +) + +for key in "${keys_general[@]}"; do + device_infos+="$key="\"$(jq -r --arg key "$key" '.[$key] // empty' <<< "$json")\"\, +done + +# Remove the last comma +device_infos_jsonized=${device_infos%,} + + echo "$device_infos_jsonized""}" "1" +# smart_available=$(echo "$json" | jq -r '.smart_support.is_available // 0') +# smart_enabled=$(echo "$json" | jq -r '.smart_support.is_enabled // 0') +# smart_healthy=$(echo "$json" | jq -r '.smart_status.passed // 0') +# echo "device_smart_available{${labels}} ${smart_available}" +# echo "device_smart_enabled{${labels}} ${smart_enabled}" +# echo "device_smart_healthy{${labels}} ${smart_healthy}" +for key in "${keys_binary[@]}"; do + value="$(jq -r ."$key // 0" <<< "$json")" + echo "$(echo "$key" | tr '.' '_'){${labels}} ${value}" +done + +# echo "device_smart_available{${labels}} ${smart_available}" +# echo "device_smart_enabled{${labels}} ${smart_enabled}" +# echo "device_smart_healthy{${labels}} ${smart_healthy}" +} + # Parse and extract ATA SMART attributes from the provided JSON. # The function extracts various fields and prints them in a formatted manner. # Arguments: diff --git a/test/test_smartmon-json.bats b/test/test_smartmon-json.bats index b973317..7c06081 100644 --- a/test/test_smartmon-json.bats +++ b/test/test_smartmon-json.bats @@ -8,7 +8,7 @@ setup() { PATH="$( pwd )/src/smartmon-json.sh:$PATH" } -### Begin Helper Functions +### Begin Helper Functions ### # Helper for parse_smartctl_nvme_attributes_json run_parse_smartctl_nvme_attributes_json() { local disk="$1" @@ -26,242 +26,9 @@ run_parse_smartctl_attributes_json() { # echo "$test_data" | parse_smartctl_attributes_json "$disk" "$disk_type" parse_smartctl_attributes_json "$disk" "$disk_type" "$test_data" } -### End Helper Functions - -#test_info_json=$( jq -n \ -#' -#{ -# "json_format_version": [ -# 1, -# 0 -# ], -# "smartctl": { -# "version": [ -# 7, -# 4 -# ], -# "pre_release": false, -# "svn_revision": "5530", -# "platform_info": "x86_64-linux-1.2.3-4-MANJARO", -# "build_info": "(local build)", -# "argv": [ -# "smartctl", -# "-i", -# "-j", -# "-H", -# "/dev/nvme0" -# ], -# "exit_status": 0 -# }, -# "local_time": { -# "time_t": 1234567890, -# "asctime": "Thu Jul 1 22:22:22 2024 CEST" -# }, -# "device": { -# "name": "/dev/nvme0", -# "info_name": "/dev/nvme0", -# "type": "nvme", -# "protocol": "NVMe" -# }, -# "model_name": "WD_BLACK SN770 1TB", -# "serial_number": "12345U123456", -# "firmware_version": "112200WD", -# "nvme_pci_vendor": { -# "id": 5559, -# "subsystem_id": 5559 -# }, -# "nvme_ieee_oui_identifier": 6980, -# "nvme_total_capacity": 1000204886016, -# "nvme_unallocated_capacity": 0, -# "nvme_controller_id": 0, -# "nvme_version": { -# "string": "1.4", -# "value": 66560 -# }, -# "nvme_number_of_namespaces": 1, -# "nvme_namespaces": [ -# { -# "id": 1, -# "size": { -# "blocks": 1953525168, -# "bytes": 1000204886016 -# }, -# "capacity": { -# "blocks": 1953525168, -# "bytes": 1000204886016 -# }, -# "utilization": { -# "blocks": 1953525168, -# "bytes": 1000204886016 -# }, -# "formatted_lba_size": 512, -# "eui64": { -# "oui": 6980, -# "ext_id": 319051012292 -# } -# } -# ], -# "user_capacity": { -# "blocks": 1953525168, -# "bytes": 1000204886016 -# }, -# "logical_block_size": 512, -# "smart_support": { -# "available": true, -# "enabled": true -# }, -# "smart_status": { -# "passed": true, -# "nvme": { -# "value": 0 -# } -# } -#} -#' ) -# -# -#echo "$test_info_json" -# -# -#test_values_json=$( jq -n \ -#' -#{ -# "json_format_version": [ -# 1, -# 0 -# ], -# "smartctl": { -# "version": [ -# 7, -# 4 -# ], -# "pre_release": false, -# "svn_revision": "5530", -# "platform_info": "x86_64-linux-1.2.3-4-MANJARO", -# "build_info": "(local build)", -# "argv": [ -# "smartctl", -# "-A", -# "-j", -# "-H", -# "/dev/nvme0" -# ], -# "exit_status": 0 -# }, -# "local_time": { -# "time_t": 1234567890, -# "asctime": "Thu Jul 1 22:22:22 2024 CEST" -# }, -# "device": { -# "name": "/dev/nvme0", -# "info_name": "/dev/nvme0", -# "type": "nvme", -# "protocol": "NVMe" -# }, -# "smart_status": { -# "passed": true, -# "nvme": { -# "value": 0 -# } -# }, -# "nvme_smart_health_information_log": { -# "critical_warning": 0, -# "temperature": 34, -# "available_spare": 100, -# "available_spare_threshold": 10, -# "percentage_used": 0, -# "data_units_read": 2454092, -# "data_units_written": 4090197, -# "host_reads": 24207664, -# "host_writes": 53302692, -# "controller_busy_time": 83, -# "power_cycles": 1109, -# "power_on_hours": 74, -# "unsafe_shutdowns": 53, -# "media_errors": 0, -# "num_err_log_entries": 0, -# "warning_temp_time": 0, -# "critical_comp_time": 0, -# "temperature_sensors": [ -# 42, -# 34 -# ] -# }, -# "temperature": { -# "current": 34 -# }, -# "power_cycle_count": 1109, -# "power_on_time": { -# "hours": 74 -# } -#} -#' ) -#echo "$test_values_json" - -# Example test_data for: "parse_smartctl_nvme_attributes_json" -# -#sudo smartctl -A -d nvme /dev/nvme0 -#smartctl 7.4 2023-08-01 r5530 [x86_64-linux-6.6.32-1-MANJARO] (local build) -#Copyright (C) 2002-23, Bruce Allen, Christian Franke, www.smartmontools.org -# -#=== START OF SMART DATA SECTION === -#SMART/Health Information (NVMe Log 0x02) -#Critical Warning: 0x00 -#Temperature: 28 Celsius -#Available Spare: 100% -#Available Spare Threshold: 10% -#Percentage Used: 0% -#Data Units Read: 2,389,400 [1.22 TB] -#Data Units Written: 3,958,438 [2.02 TB] -#Host Read Commands: 23,444,268 -#Host Write Commands: 51,391,963 -#Controller Busy Time: 79 -#Power Cycles: 1,012 -#Power On Hours: 70 -#Unsafe Shutdowns: 52 -#Media and Data Integrity Errors: 0 -#Error Information Log Entries: 0 -#Warning Comp. Temperature Time: 0 -#Critical Comp. Temperature Time: 0 -#Temperature Sensor 1: 36 Celsius -#Temperature Sensor 2: 28 Celsius - -############################################################################### -# sudo smartctl -A /dev/sda -#smartctl 7.1 2019-12-30 r5022 [x86_64-linux-5.4.0-182-generic] (local build) -#Copyright (C) 2002-19, Bruce Allen, Christian Franke, www.smartmontools.org -# -#=== START OF READ SMART DATA SECTION === -#SMART Attributes Data Structure revision number: 1 -#Vendor Specific SMART Attributes with Thresholds: -#ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE -# 1 Raw_Read_Error_Rate 0x002f 100 100 050 Pre-fail Always - 0 -# 5 Reallocate_NAND_Blk_Cnt 0x0032 100 100 010 Old_age Always - 0 -# 9 Power_On_Hours 0x0032 100 100 050 Old_age Always - 30104 -# 12 Power_Cycle_Count 0x0032 100 100 050 Old_age Always - 57 -#171 Program_Fail_Count 0x0032 100 100 050 Old_age Always - 0 -#172 Erase_Fail_Count 0x0032 100 100 050 Old_age Always - 0 -#173 Ave_Block-Erase_Count 0x0032 100 100 050 Old_age Always - 183 -#174 Unexpect_Power_Loss_Ct 0x0032 100 100 050 Old_age Always - 31 -#180 Unused_Reserve_NAND_Blk 0x0032 100 100 050 Old_age Always - 100 -#183 SATA_Interfac_Downshift 0x0032 100 100 050 Old_age Always - 120 -#184 Error_Correction_Count 0x0032 100 100 050 Old_age Always - 8 -#187 Reported_Uncorrect 0x0032 100 100 050 Old_age Always - 0 -#194 Temperature_Celsius 0x0022 069 049 050 Old_age Always In_the_past 31 (Min/Max 25/51) -#196 Reallocated_Event_Count 0x0032 100 100 050 Old_age Always - 0 -#197 Current_Pending_Sector 0x0032 100 100 050 Old_age Always - 0 -#198 Offline_Uncorrectable 0x0030 100 100 050 Old_age Offline - 0 -#199 UDMA_CRC_Error_Count 0x0032 100 100 050 Old_age Always - 0 -#202 Percent_Lifetime_Remain 0x0030 088 088 001 Old_age Offline - 88 -#206 Write_Error_Rate 0x002e 100 100 050 Old_age Always - 0 -#210 Success_RAIN_Recov_Cnt 0x0032 100 100 050 Old_age Always - 0 -#246 Total_LBAs_Written 0x0032 100 100 050 Old_age Always - 8866549306 -#247 Host_Program_Page_Count 0x0032 100 100 050 Old_age Always - 277079665 -#248 FTL_Program_Page_Count 0x0032 100 100 050 Old_age Always - 318468096 - - -### Begin Tests +### End Helper Functions ### +### Begin Tests ### @test "parse_smartctl_nvme_attributes_json with temperature test_data" { local disk="nvme0" local disk_type="nvme" @@ -294,10 +61,8 @@ EOF @test "parse_smartctl_sata_attributes_json power_on_hours" { local disk="sda" local disk_type="sat" - local test_data - test_data=$(jq -n \ -' -{ + local test_json + local test_json='{ "json_format_version": [ 1, 0 @@ -328,11 +93,9 @@ EOF } ] } -} -' -) +}' - run run_parse_smartctl_attributes_json "$disk" "$disk_type" "$test_data" + run run_parse_smartctl_attributes_json "$disk" "$disk_type" "$test_json" assert_output - <<-EOF Power_On_Hours_value{disk="sda",type="sat",smart_id="9"} 100 Power_On_Hours_worst{disk="sda",type="sat",smart_id="9"} 100 @@ -353,10 +116,8 @@ EOF @test "parse_smartctl_sata_attributes_json replace hyphens" { local disk="sda" local disk_type="sat" - local test_data - test_data=$(jq -n \ -' -{ + local test_json + local test_json='{ "json_format_version": [ 1, 0 @@ -376,11 +137,9 @@ EOF } ] } -} -' -) +}' - run run_parse_smartctl_attributes_json "$disk" "$disk_type" "$test_data" + run run_parse_smartctl_attributes_json "$disk" "$disk_type" "$test_json" assert_output - <<-EOF Power_On_Hours_value{disk="sda",type="sat",smart_id="9"} 100 Power_On_Hours_worst{disk="sda",type="sat",smart_id="9"} 100 @@ -388,3 +147,35 @@ EOF Power_On_Hours_raw_value{disk="sda",type="sat",smart_id="9"} 30839 EOF } + +@test "parse_smartctl_info_json correctly parses SMART info JSON" { + local disk="sda" + local disk_type="sat" + local json='{ + "vendor": "NorthernDigital", + "model_family": "JBOD-Star", + "product": "ND-01", + "revision": "10.01", + "lun_id": "0", + "device_model": "ND-01Model", + "serial_number": "123456789", + "firmware_version": "80.00A80", + "smart_support": { + "is_available": 1, + "is_enabled": 1 + }, + "smart_status": { + "passed": 1 + } + }' + + local expected_output="device_info{disk=\"${disk}\",type=\"${disk_type}\",model_family=\"JBOD-Star\",device_model=\"ND-01Model\",serial_number=\"123456789\",firmware_version=\"80.00A80\",vendor=\"NorthernDigital\",product=\"ND-01\",revision=\"10.01\",lun_id=\"0\"} 1 +smart_support_is_available{disk=\"${disk}\",type=\"${disk_type}\"} 1 +smart_support_is_enabled{disk=\"${disk}\",type=\"${disk_type}\"} 1 +smart_status_passed{disk=\"${disk}\",type=\"${disk_type}\"} 1" + + local output + output=$(parse_smartctl_info_json "${disk}" "${disk_type}" "${json}") + + assert_equal "${output}" "${expected_output}" +} From 388199aea7bc5de8f23946095f4568477533071a Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Sat, 27 Jul 2024 19:13:08 +0200 Subject: [PATCH 07/54] wip:compact main --- src/smartmon-json.sh | 69 ++++++++------------------------------------ 1 file changed, 12 insertions(+), 57 deletions(-) diff --git a/src/smartmon-json.sh b/src/smartmon-json.sh index a80864d..243f7e1 100755 --- a/src/smartmon-json.sh +++ b/src/smartmon-json.sh @@ -27,47 +27,6 @@ ensure_jq_installed() { # $1 - Disk name # $2 - Disk type # $3 - JSON string containing SMART information -parse_smartctl_info_json_old() { - local disk="$1" - local disk_type="$2" - local json="$3" - local labels="disk=\"${disk}\",type=\"${disk_type}\"" - - # Extract various SMART attributes using jq - local model_family device_model serial_number fw_version vendor product \ - revision lun_id smart_available smart_enabled smart_healthy - model_family=$(echo "$json" | jq -r '.model_family // empty') - device_model=$(echo "$json" | jq -r '.device_model // empty') - serial_number=$(echo "$json" | jq -r '.serial_number // empty') - fw_version=$(echo "$json" | jq -r '.firmware_version // empty') - vendor=$(echo "$json" | jq -r '.vendor // empty') - product=$(echo "$json" | jq -r '.product // empty') - revision=$(echo "$json" | jq -r '.revision // empty') - lun_id=$(echo "$json" | jq -r '.lun_id // empty') - smart_available=$(echo "$json" | jq -r '.smart_support.is_available // 0') - smart_enabled=$(echo "$json" | jq -r '.smart_support.is_enabled // 0') - smart_healthy=$(echo "$json" | jq -r '.smart_status.passed // 0') - - # Print the extracted information in Prometheus format - # Note: use TABS for indentation - device_infos=$(cat <<-EOF -device_info{${labels},\ -vendor="${vendor}",\ -product="${product}",\ -revision="${revision}",\ -lun_id="${lun_id}",\ -model_family="${model_family}",\ -device_model="${device_model}",\ -serial_number="${serial_number}",\ -firmware_version="${fw_version}"} 1 -EOF -) - echo "$device_infos" - echo "device_smart_available{${labels}} ${smart_available}" - echo "device_smart_enabled{${labels}} ${smart_enabled}" - echo "device_smart_healthy{${labels}} ${smart_healthy}" -} - parse_smartctl_info_json() { local disk="$1" local disk_type="$2" @@ -77,7 +36,6 @@ parse_smartctl_info_json() { declare -a keys_general=("model_family" "device_model" "serial_number" "firmware_version" "vendor" "product" "revision" "lun_id") declare -a keys_binary=("smart_support.is_available" "smart_support.is_enabled" "smart_status.passed") - # Print the extracted information in Prometheus format # Note: use TABS for indentation device_infos=$(cat <<-EOF @@ -93,20 +51,10 @@ done device_infos_jsonized=${device_infos%,} echo "$device_infos_jsonized""}" "1" -# smart_available=$(echo "$json" | jq -r '.smart_support.is_available // 0') -# smart_enabled=$(echo "$json" | jq -r '.smart_support.is_enabled // 0') -# smart_healthy=$(echo "$json" | jq -r '.smart_status.passed // 0') -# echo "device_smart_available{${labels}} ${smart_available}" -# echo "device_smart_enabled{${labels}} ${smart_enabled}" -# echo "device_smart_healthy{${labels}} ${smart_healthy}" for key in "${keys_binary[@]}"; do value="$(jq -r ."$key // 0" <<< "$json")" echo "$(echo "$key" | tr '.' '_'){${labels}} ${value}" done - -# echo "device_smart_available{${labels}} ${smart_available}" -# echo "device_smart_enabled{${labels}} ${smart_enabled}" -# echo "device_smart_healthy{${labels}} ${smart_healthy}" } # Parse and extract ATA SMART attributes from the provided JSON. @@ -240,10 +188,9 @@ format_output() { awk -F'{' "${format_output_awk}" } -# Main function to orchestrate the script. -main() { - ensure_jq_installed - +# Get the smartctl version and output it in Prometheus format. +# Exits if version is too old. +output_smartctl_version() { # Get the smartctl version local smartctl_version smartctl_version=$(/usr/sbin/smartctl -V | head -n1 | awk '$1 == "smartctl" {print $2}') @@ -253,8 +200,16 @@ main() { # Exit if smartctl version is less than 6 if [[ "$(expr "${smartctl_version}" : '\([0-9]*\)\..*')" -lt 6 ]]; then - exit + echo "Error: smartctl version is less than 6." >&2 + exit 1 fi +} + +# Main function to orchestrate the script. +main() { + ensure_jq_installed + + output_smartctl_version # Get the list of devices local device_list From b4baef771ec9ad3db1d0f0f2a4dd0c70f238c123 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Sun, 28 Jul 2024 18:51:02 +0200 Subject: [PATCH 08/54] wip:compact main more --- src/smartmon-json.sh | 112 +++++++++++++++++++++++++++---------------- 1 file changed, 71 insertions(+), 41 deletions(-) diff --git a/src/smartmon-json.sh b/src/smartmon-json.sh index 243f7e1..cb20ec3 100755 --- a/src/smartmon-json.sh +++ b/src/smartmon-json.sh @@ -9,9 +9,6 @@ # different vendors might use inconsistent value naming. -# Global Variables -device_infos="" - # Ensure jq is installed. This function checks if the 'jq' command is available. ensure_jq_installed() { if ! command -v jq &>/dev/null; then @@ -28,6 +25,7 @@ ensure_jq_installed() { # $2 - Disk type # $3 - JSON string containing SMART information parse_smartctl_info_json() { + local device_infos="" local disk="$1" local disk_type="$2" local json="$3" @@ -205,6 +203,72 @@ output_smartctl_version() { fi } +# Check if a storage device is active. +# Arguments: +# $1 - Disk name +# $2 - Disk type +# Returns 0 if the device is active, and a non-zero value otherwise. +is_device_active() { + local disk=$1 + local type=$2 + /usr/sbin/smartctl -n standby -d "${type}" "${disk}" > /dev/null +} + +# This function processes a single storage device. +# Arguments: +# $1 - Disk name +# $2 - Disk type +# Performed operations: +# - Record the time of the smartctl run +# - Check if the device is active +# - Skip inactive devices +# - Get and parses SMART information +# - Get and parses SMART attributes +# Returns nothing +process_device() { + local disk=$1 + local type=$2 + local active=1 + + # Record the time of smartctl run + echo "smartctl_run{disk=\"${disk}\",type=\"${type}\"}" "$(TZ=UTC date '+%s')" + + # Check if the device is active + if is_device_active "${disk}" "${type}"; then + active=1 + else + active=0 + fi + echo "device_active{disk=\"${disk}\",type=\"${type}\"}" "${active}" + + # Skip inactive devices + test ${active} -eq 0 && return + + # Get and parse SMART information + local info_json + info_json=$(/usr/sbin/smartctl -i -H -j -d "${type}" "${disk}") + parse_smartctl_info_json "${disk}" "${type}" "${info_json}" + + # Get and parse SMART attributes + local attributes_json + attributes_json=$(/usr/sbin/smartctl -A -j -d "${type}" "${disk}") + case ${type} in + sat|sat+megaraid*) + parse_smartctl_attributes_json "${disk}" "${type}" "${attributes_json}" + ;; + scsi|megaraid*) + parse_smartctl_scsi_attributes_json "${disk}" "${type}" "${attributes_json}" + ;; + nvme) + parse_smartctl_nvme_attributes_json "${disk}" "${type}" "${attributes_json}" + ;; + *) + echo "disk type is not supported: ${type}" + exit 1 + ;; + esac +} + # Main function to orchestrate the script. main() { ensure_jq_installed @@ -217,44 +281,10 @@ main() { # Iterate over each device and gather SMART information for device in ${device_list}; do - local disk - local type - disk=$(echo "${device}" | cut -f1 -d'|') - type=$(echo "${device}" | cut -f2 -d'|') - local active=1 - - # Record the time of smartctl run - echo "smartctl_run{disk=\"${disk}\",type=\"${type}\"}" "$(TZ=UTC date '+%s')" - # Check if the device is active - /usr/sbin/smartctl -n standby -d "${type}" "${disk}" > /dev/null || active=0 - echo "device_active{disk=\"${disk}\",type=\"${type}\"}" "${active}" - - # Skip inactive devices - test ${active} -eq 0 && continue - - # Get and parse SMART information - local info_json - info_json=$(/usr/sbin/smartctl -i -H -j -d "${type}" "${disk}") - parse_smartctl_info_json "${disk}" "${type}" "${info_json}" - - # Get and parse SMART attributes - local attributes_json - attributes_json=$(/usr/sbin/smartctl -A -j -d "${type}" "${disk}") - case ${type} in - sat|sat+megaraid*) - parse_smartctl_attributes_json "${disk}" "${type}" "${attributes_json}" - ;; - scsi|megaraid*) - parse_smartctl_scsi_attributes_json "${disk}" "${type}" "${attributes_json}" - ;; - nvme) - parse_smartctl_nvme_attributes_json "${disk}" "${type}" "${attributes_json}" - ;; - *) - echo "disk type is not supported: ${type}" - exit 1 - ;; - esac + local disk; disk=$(echo "${device}" | cut -f1 -d'|') + local type; type=$(echo "${device}" | cut -f2 -d'|') + + process_device "${disk}" "${type}" done | format_output } From a36e66ca9a4ec27625a8972b450cacfda0305efd Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Fri, 9 Aug 2024 20:21:58 +0200 Subject: [PATCH 09/54] chore: fix sat metrics --- README.md | 2 +- src/smartmon-json.sh | 126 +++++++++-------- src/smartmon.sh | 254 ----------------------------------- test/test_smartmon-json.bats | 105 +++++++++++++-- 4 files changed, 169 insertions(+), 318 deletions(-) delete mode 100755 src/smartmon.sh diff --git a/README.md b/README.md index 840d463..e07d8cb 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ For UBUNTU: `sudo apt-get install smartmontools` To enable the text_collector on your system add the following as cronjob. It will execute the script every five minutes and save the result to the `text_collector` directory. -Example for UBUNTU `crontab -e`: +Example for UBUNTU `sudo crontab -e`: `*/5 * * * * /usr/local/bin/smartmon.sh > /var/lib/node_exporter/textfile_collector/smart_metrics.prom` diff --git a/src/smartmon-json.sh b/src/smartmon-json.sh index cb20ec3..a56e5e4 100755 --- a/src/smartmon-json.sh +++ b/src/smartmon-json.sh @@ -1,5 +1,12 @@ #!/usr/bin/env bash +# TODO: remove json postfix +# TODO: warning breaking changes +# TODO: test tolowercase +# - Renaming +# - JSON +# - Drop SCSI + # This script is designed to collect SMART data from various types of # disks (ATA, NVMe, SCSI) and format it for Prometheus monitoring. # Be aware that SCSI type is not tested! @@ -9,7 +16,7 @@ # different vendors might use inconsistent value naming. -# Ensure jq is installed. This function checks if the 'jq' command is available. +# Check if the 'jq' command is available. ensure_jq_installed() { if ! command -v jq &>/dev/null; then echo "jq could not be found. Please install it:" @@ -31,7 +38,7 @@ parse_smartctl_info_json() { local json="$3" local labels="disk=\"${disk}\",type=\"${disk_type}\"" - declare -a keys_general=("model_family" "device_model" "serial_number" "firmware_version" "vendor" "product" "revision" "lun_id") + declare -a keys_general=("model_family" "model_name" "device_model" "serial_number" "firmware_version" "vendor" "product" "revision" "lun_id") declare -a keys_binary=("smart_support.is_available" "smart_support.is_enabled" "smart_status.passed") # Print the extracted information in Prometheus format @@ -48,9 +55,16 @@ done # Remove the last comma device_infos_jsonized=${device_infos%,} - echo "$device_infos_jsonized""}" "1" +echo "$device_infos_jsonized""}" "1" + for key in "${keys_binary[@]}"; do value="$(jq -r ."$key // 0" <<< "$json")" + # Convert boolean to numeric + if [ "$value" == "true" ]; then + value=1 + elif [ "$value" == "false" ]; then + value=0 + fi echo "$(echo "$key" | tr '.' '_'){${labels}} ${value}" done } @@ -75,21 +89,63 @@ parse_smartctl_attributes_json() { # Extract and format SMART attributes using jq echo "$json" | jq -r ' .ata_smart_attributes.table[] | - select(.id and .name and (.value | type != "array") and (.worst | type != "array") and (.thresh | type != "array") and (.raw.value | type != "array")) | + select( + .id and + .name and + (.value | type != "array") and + (.worst | type != "array") and + (.thresh | type != "array") and + (.raw.value | type != "array") + ) | [ .id, (.name | gsub("-"; "_")), .value, .worst, .thresh, - .raw.value + (.raw.string | capture("(?^[0-9]+)") | .num) ] | @tsv ' | while IFS=$'\t' read -r id name value worst thresh raw; do - echo "${name}_value{${labels},smart_id=\"${id}\"} ${value}" - echo "${name}_worst{${labels},smart_id=\"${id}\"} ${worst}" - echo "${name}_threshold{${labels},smart_id=\"${id}\"} ${thresh}" - echo "${name}_raw_value{${labels},smart_id=\"${id}\"} ${raw}" + + local metric_name + + metric_name="${name}_value" + printf "%s{%s,smart_id=\"%s\"} %s\n" \ + "$(echo "$metric_name" | awk '{print tolower($0)}')" \ + "$labels" \ + "$id" \ + "$value" + + metric_name="${name}_worst" + printf "%s{%s,smart_id=\"%s\"} %s\n" \ + "$(echo "$metric_name" | awk '{print tolower($0)}')" \ + "$labels" \ + "$id" \ + "$worst" + + metric_name="${name}_threshold" + printf "%s{%s,smart_id=\"%s\"} %s\n" \ + "$(echo "$metric_name" | awk '{print tolower($0)}')" \ + "$labels" \ + "$id" \ + "$thresh" + + metric_name="${name}_raw_value" + printf "%s{%s,smart_id=\"%s\"} %s\n" \ + "$(echo "$metric_name" | awk '{print tolower($0)}')" \ + "$labels" \ + "$id" \ + "$raw" done + + # Extract and format temperature + local temperature + temperature=$(echo "$json" | jq -r '.temperature.current // empty') + + if [[ -n "$temperature" ]]; then + printf "temperature_current{%s} %s\n" "$labels" "$temperature" + fi + } # Parse and extract NVMe SMART attributes from the provided JSON. @@ -119,46 +175,8 @@ parse_smartctl_nvme_attributes_json() { .value ] | @tsv ' | while IFS=$'\t' read -r key value; do - echo "${key}{${labels}} ${value}" - done -} - -# Parse and extract SCSI SMART attributes from the provided JSON. -# The function extracts various fields and prints them in a formatted manner. -# Arguments: -# $1 - Disk name -# $2 - Disk type -# $3 - JSON string containing SCSI SMART attributes -parse_smartctl_scsi_attributes_json() { - local disk="$1" - local disk_type="$2" - local json="$3" - local labels="disk=\"${disk}\",type=\"${disk_type}\"" - - # Extract and format SCSI SMART attributes using jq - echo "$json" | jq -r ' - [ - {key: "power_on_hours", value: .scsi_grown_defects_count}, - {key: "Current_Drive_Temperature", value: .temperature.current}, - {key: "Accumulated_start-stop_cycles", value: .accumulated_start_stop_cycles}, - {key: "Unsafe_Shutdowns", value: .unsafe_shutdowns}, - {key: "Power_Cycles", value: .power_cycles}, - {key: "Power_On_Hours", value: .power_on_hours}, - {key: "Host_Read_Commands", value: .host_read_commands}, - {key: "Host_Write_Commands", value: .host_write_commands}, - {key: "Controller_Busy_Time", value: .controller_busy_time}, - {key: "Error_Information_Log_Entries", value: .error_information_log_entries}, - {key: "Temperature", value: .temperature}, - {key: "Percentage_Used", value: .percentage_used}, - {key: "Available_Spare", value: .available_spare}, - {key: "Available_Spare_Threshold", value: .available_spare_threshold}, - {key: "Media_and_Data_Integrity_Errors", value: .media_and_data_integrity_errors} - ] | - map(select(.value != null and (.value | type != "array"))) | - .[] | - "\(.key){${labels}} \(.value)" - ' | while IFS=$'\t' read -r key value; do - echo "${key}{${labels}} ${value}" + local metric_name="${key}" + echo "$(echo "${metric_name}" | awk '{print tolower($0)}'){${labels}} ${value}" done } @@ -231,7 +249,8 @@ process_device() { local active=1 # Record the time of smartctl run - echo "smartctl_run{disk=\"${disk}\",type=\"${type}\"}" "$(TZ=UTC date '+%s')" + local metric_name="smartctl_run" + echo "$(echo "${metric_name}" | awk '{print tolower($0)}'){disk=\"${disk}\",type=\"${type}\"}" "$(TZ=UTC date '+%s')" # Check if the device is active if is_device_active "${disk}" "${type}"; then @@ -239,7 +258,9 @@ process_device() { else active=0 fi - echo "device_active{disk=\"${disk}\",type=\"${type}\"}" "${active}" + metric_name="device_active" + # make metric names lowercase + echo "$(echo "${metric_name}" | awk '{print tolower($0)}'){disk=\"${disk}\",type=\"${type}\"}" "${active}" # Skip inactive devices test ${active} -eq 0 && return @@ -256,9 +277,6 @@ process_device() { sat|sat+megaraid*) parse_smartctl_attributes_json "${disk}" "${type}" "${attributes_json}" ;; - scsi|megaraid*) - parse_smartctl_scsi_attributes_json "${disk}" "${type}" "${attributes_json}" - ;; nvme) parse_smartctl_nvme_attributes_json "${disk}" "${type}" "${attributes_json}" ;; diff --git a/src/smartmon.sh b/src/smartmon.sh deleted file mode 100755 index 415cb6e..0000000 --- a/src/smartmon.sh +++ /dev/null @@ -1,254 +0,0 @@ -#!/usr/bin/env bash - -# This script collects metrics from disks using smartctl. It will parse the -# output of scmartctl and collect metrics by a given filter. -# -# Script informed by the collectd monitoring script for smartmontools (using smartctl) -# by Samuel B. (c) 2012 -# source at: http://devel.dob.sk/collectd-scripts/ -# Source: -# https://github.com/prometheus-community/node-exporter-textfile-collector-scripts/blob/master/smartmon.sh -# -# New attributes needs to be added to different areas. They are marked with a -# comment "Add new ..." - -set -o errexit # abort on nonzero exitstatus -# set -o nounset # abort on unbound variable -set -o pipefail # don't hide errors within pipes - -export LC_ALL=C - -# Check if first field is a number and the second consists of alphanumeric -# characters, underscores and/or hyphens. Gsub replaces hyphens with underscores. -parse_smartctl_attributes_awk="$( - cat <<'SMARTCTLAWK' -$1 ~ /^ *[0-9]+$/ && $2 ~ /^[a-zA-Z0-9_-]+$/ { - gsub(/-/, "_"); - printf "%s_value{%s,smart_id=\"%s\"} %d\n", $2, labels, $1, $4 - printf "%s_worst{%s,smart_id=\"%s\"} %d\n", $2, labels, $1, $5 - printf "%s_threshold{%s,smart_id=\"%s\"} %d\n", $2, labels, $1, $6 - printf "%s_raw_value{%s,smart_id=\"%s\"} %e\n", $2, labels, $1, $10 -} -SMARTCTLAWK -)" - -# Add new attributes here. This variable is used as a filter. -# TODO: validate where it is actually used. SATA only? -smartmon_attrs="$( - cat <<'SMARTMONATTRS' -airflow_temperature_cel -command_timeout -current_pending_sector -data_units_written -end_to_end_error -erase_fail_count -g_sense_error_rate -hardware_ecc_recovered -host_reads_32mib -host_reads_mib -host_writes_32mib -host_writes_mib -load_cycle_count -media_wearout_indicator -nand_writes_1gib -offline_uncorrectable -power_cycles_count -power_on_hours -program_fail_cnt_total -program_fail_count -raw_read_error_rate -reallocated_event_count -reallocated_sector_ct -reported_uncorrect -runtime_bad_block -sata_downshift_count -seek_error_rate -spin_retry_count -spin_up_time -start_stop_count -temperature_case -temperature_celsius -temperature_internal -total_lbas_read -total_lbas_written -udma_crc_error_count -unsafe_shutdown_count -unused_rsvd_blk_cnt_tot -wear_leveling_count -workld_host_reads_perc -workld_media_wear_indic -workload_minutes -SMARTMONATTRS -)" -smartmon_attrs="$(echo "${smartmon_attrs}" | xargs | tr ' ' '|')" - -# Remove leading spaces and formatting available information for output that -# is usable for Prometheus. Grep filters only matching lines that are stored -# in "smartmon_attrs". -# Example output: -# spin_up_time_worst{disk="sda",type="ssd",smart_id="2"} 100 -# -# Input variables: -# - disk = path of a specific disk -# - type = type of the specific disk -parse_smartctl_attributes() { - local disk="$1" - local disk_type="$2" - local labels="disk=\"${disk}\",type=\"${disk_type}\"" - - sed 's/^ \+//g' | - awk -v labels="${labels}" "${parse_smartctl_attributes_awk}" 2>/dev/null | - tr '[:upper:]' '[:lower:]' | - grep -E "(${smartmon_attrs})" -} - -# Expected input: -# Piped output from smartctl as input stream -# $1 = disk path (example: /dev/sda) -# $2 = disk type (example: sat) -parse_smartctl_nvme_attributes() { - local disk="$1" - local disk_type="$2" - local labels="disk=\"${disk}\",type=\"${disk_type}\"" - # Add new NVME attributes here. - while read -r line; do - attr_type="$(echo "${line}" | tr '=' ':' | cut -f1 -d: | sed 's/^ \+//g' | tr ' ' '_')" - attr_value="$(echo "${line}" | tr '=' ':' | cut -f2 -d: | sed 's/^ \+//g')" - echo "${attr_type}" - case "${attr_type}" in - Accumulated_start-stop_cycles) power_cycles="$(echo "${attr_value}" | awk '{ printf "%e\n", $1 }')" ;; - Available_Spare) available_spare="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; - Available_Spare_Threshold) available_spare_threshold="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; - Blocks_read_from_cache_and_sent_to_initiator_) lbas_read="$(echo "${attr_value}" | awk '{ printf "%e\n", $1 }')" ;; - Controller_Busy_Time) controller_busy_time="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; - Data_Units_Written) data_units_written="$(echo "${attr_value}" | sed 's/,//g' | awk '{ printf "%u\n", $1 }')" ;; - Elements_in_grown_defect_list) grown_defects="$(echo "${attr_value}" | awk '{ printf "%e\n", $1 }')" ;; - Error_Information_Log_Entries) error_info_log_entry="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; - Host_Read_Commands) host_read_commands="$(echo "${attr_value}" | tr -dc "0-9" | awk '{printf "%d\n", $1 }')" ;; - Host_Write_Commands) host_write_commands="$(echo "${attr_value}" | tr -dc "0-9" | awk '{printf "%d\n", $1 }')" ;; - Media_and_Data_Integrity_Errors) media_and_data_integrity_errors="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; - Percentage_Used) percentage_used="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; - Power_Cycles) power_cycles="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; - Power_On_Hours) power_on="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; - Temperature) echo "${attr_value}"; temp_cel="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; - Unsafe_Shutdowns) unsafe_shutdowns="$(echo "${attr_value}" | awk '{ printf "%d\n", $1 }')" ;; - esac - done - - # Add new NVME attributes here. - [ -n "$available_spare" ] && echo "available_spare_raw_value{${labels},smart_id=\"255\"} ${available_spare}" - [ -n "$available_spare_threshold" ] && echo "available_spare_threshold{${labels},smart_id=\"255\"} ${available_spare_threshold}" - [ -n "$controller_busy_time" ] && echo "controller_busy_time_raw_value{${labels},smart_id=\"255\"} ${controller_busy_time}" - [ -n "$data_units_written" ] && echo "data_units_written_raw_value{${labels},smart_id=\"8\"} ${data_units_written}" - [ -n "$lbas_read" ] && echo "total_lbas_read_raw_value{${labels},smart_id=\"242\"} ${lbas_read}" - [ -n "$error_info_log_entry" ] && echo "error_information_log_entries_raw_value{${labels},smart_id=\"255\"} ${error_info_log_entry}" - [ -n "$grown_defects" ] && echo "grown_defects_count_raw_value{${labels},smart_id=\"12\"} ${grown_defects}" - [ -n "$host_read_commands" ] && echo "host_read_commands_raw_value{${labels},smart_id=\"255\"} ${host_read_commands}" - [ -n "$host_write_commands" ] && echo "host_write_commands_raw_value{${labels},smart_id=\"255\"} ${host_write_commands}" - [ -n "$media_and_data_integrity_errors" ] && echo "media_and_data_integrity_errors_count_raw_value{${labels},smart_id=\"255\"} ${media_and_data_integrity_errors}" - [ -n "$percentage_used" ] && echo "percentage_used_raw_value{${labels},smart_id=\"255\"} ${percentage_used}" - [ -n "$power_cycles" ] && echo "power_cycles_count_raw_value{${labels},smart_id=\"255\"} ${power_cycles}" - [ -n "$power_on" ] && echo "power_on_hours_raw_value{${labels},smart_id=\"9\"} ${power_on}" - [ -n "$temp_cel" ] && echo "temperature_celsius_raw_value{${labels},smart_id=\"194\"} ${temp_cel}" - [ -n "$unsafe_shutdowns" ] && echo "unsafe_shutdowns_count_raw_value{${labels},smart_id=\"255\"} ${unsafe_shutdowns}" -} - - -parse_smartctl_info() { - local -i smart_available=0 smart_enabled=0 smart_healthy=0 - local disk="$1" disk_type="$2" - local model_family='' device_model='' serial_number='' fw_version='' vendor='' product='' revision='' lun_id='' - while read -r line; do - info_type="$(echo "${line}" | cut -f1 -d: | tr ' ' '_')" - info_value="$(echo "${line}" | cut -f2- -d: | sed 's/^ \+//g' | sed 's/"/\\"/')" - case "${info_type}" in - Model_Family) model_family="${info_value}" ;; - Device_Model) device_model="${info_value}" ;; - Serial_Number) serial_number="${info_value}" ;; - Firmware_Version) fw_version="${info_value}" ;; - Vendor) vendor="${info_value}" ;; - Product) product="${info_value}" ;; - Revision) revision="${info_value}" ;; - Logical_Unit_id) lun_id="${info_value}" ;; - esac - if [[ "${info_type}" == 'SMART_support_is' ]]; then - case "${info_value:0:7}" in - Enabled) smart_enabled=1 ;; - Availab) smart_available=1 ;; - Unavail) smart_available=0 ;; - esac - fi - if [[ "${info_type}" == 'SMART_overall-health_self-assessment_test_result' ]]; then - case "${info_value:0:6}" in - PASSED) smart_healthy=1 ;; - esac - elif [[ "${info_type}" == 'SMART_Health_Status' ]]; then - case "${info_value:0:2}" in - OK) smart_healthy=1 ;; - esac - fi - done - echo "device_info{disk=\"${disk}\",type=\"${disk_type}\",vendor=\"${vendor}\",product=\"${product}\",revision=\"${revision}\",lun_id=\"${lun_id}\",model_family=\"${model_family}\",device_model=\"${device_model}\",serial_number=\"${serial_number}\",firmware_version=\"${fw_version}\"} 1" - echo "device_smart_available{disk=\"${disk}\",type=\"${disk_type}\"} ${smart_available}" - echo "device_smart_enabled{disk=\"${disk}\",type=\"${disk_type}\"} ${smart_enabled}" - echo "device_smart_healthy{disk=\"${disk}\",type=\"${disk_type}\"} ${smart_healthy}" -} - -output_format_awk="$( - cat <<'OUTPUTAWK' -BEGIN { v = "" } -(v != $1 && $0 != $1 ){ - print "# HELP smartmon_" $1 " SMART metric " $1; - print "# TYPE smartmon_" $1 " gauge"; - v = $1 -} -($0 != $1 ) {print "smartmon_" $0} -OUTPUTAWK -)" - -format_output() { - sort | - awk -F'{' "${output_format_awk}" -} - -main() { -smartctl_version="$(/usr/sbin/smartctl -V | head -n1 | awk '$1 == "smartctl" {print $2}')" - -echo "smartctl_version{version=\"${smartctl_version}\"} 1" | format_output - -if [[ "$(expr "${smartctl_version}" : '\([0-9]*\)\..*')" -lt 6 ]]; then - exit -fi - -device_list="$(/usr/sbin/smartctl --scan-open | awk '/^\/dev/{print $1 "|" $3}')" - -for device in ${device_list}; do - disk="$(echo "${device}" | cut -f1 -d'|')" - type="$(echo "${device}" | cut -f2 -d'|')" - active=1 - echo "smartctl_run{disk=\"${disk}\",type=\"${type}\"}" "$(TZ=UTC date '+%s')" - # Check if the device is in a low-power mode - /usr/sbin/smartctl -n standby -d "${type}" "${disk}" > /dev/null || active=0 - echo "device_active{disk=\"${disk}\",type=\"${type}\"}" "${active}" - # Skip further metrics to prevent the disk from spinning up - test ${active} -eq 0 && continue - # Get the SMART information and health - /usr/sbin/smartctl -i -H -d "${type}" "${disk}" | parse_smartctl_info "${disk}" "${type}" - # Get the SMART attributes - echo "${type}" - case ${type} in - # Actual command where the metrics are parsed from - sat) /usr/sbin/smartctl -A -d "${type}" "${disk}" | parse_smartctl_attributes "${disk}" "${type}" ;; - sat+megaraid*) /usr/sbin/smartctl -A -d "${type}" "${disk}" | parse_smartctl_attributes "${disk}" "${type}" ;; - scsi) /usr/sbin/smartctl -A -d "${type}" "${disk}" | parse_smartctl_scsi_attributes "${disk}" "${type}" ;; - nvme) /usr/sbin/smartctl -A -d "${type}" "${disk}" | parse_smartctl_nvme_attributes "${disk}" "${type}" ;; - megaraid*) /usr/sbin/smartctl -A -d "${type}" "${disk}" | parse_smartctl_scsi_attributes "${disk}" "${type}" ;; - *) - echo "disk type is not sat, scsi, nvme or megaraid but ${type}" - exit - ;; - esac -done | format_output -} - -main diff --git a/test/test_smartmon-json.bats b/test/test_smartmon-json.bats index 7c06081..1f2d9f8 100644 --- a/test/test_smartmon-json.bats +++ b/test/test_smartmon-json.bats @@ -97,10 +97,10 @@ EOF run run_parse_smartctl_attributes_json "$disk" "$disk_type" "$test_json" assert_output - <<-EOF - Power_On_Hours_value{disk="sda",type="sat",smart_id="9"} 100 - Power_On_Hours_worst{disk="sda",type="sat",smart_id="9"} 100 - Power_On_Hours_threshold{disk="sda",type="sat",smart_id="9"} 50 - Power_On_Hours_raw_value{disk="sda",type="sat",smart_id="9"} 30839 + power_on_hours_value{disk="sda",type="sat",smart_id="9"} 100 + power_on_hours_worst{disk="sda",type="sat",smart_id="9"} 100 + power_on_hours_threshold{disk="sda",type="sat",smart_id="9"} 50 + power_on_hours_raw_value{disk="sda",type="sat",smart_id="9"} 30839 EOF } @@ -141,10 +141,10 @@ EOF run run_parse_smartctl_attributes_json "$disk" "$disk_type" "$test_json" assert_output - <<-EOF - Power_On_Hours_value{disk="sda",type="sat",smart_id="9"} 100 - Power_On_Hours_worst{disk="sda",type="sat",smart_id="9"} 100 - Power_On_Hours_threshold{disk="sda",type="sat",smart_id="9"} 50 - Power_On_Hours_raw_value{disk="sda",type="sat",smart_id="9"} 30839 + power_on_hours_value{disk="sda",type="sat",smart_id="9"} 100 + power_on_hours_worst{disk="sda",type="sat",smart_id="9"} 100 + power_on_hours_threshold{disk="sda",type="sat",smart_id="9"} 50 + power_on_hours_raw_value{disk="sda",type="sat",smart_id="9"} 30839 EOF } @@ -154,6 +154,7 @@ EOF local json='{ "vendor": "NorthernDigital", "model_family": "JBOD-Star", + "model_name": "Star-Light", "product": "ND-01", "revision": "10.01", "lun_id": "0", @@ -169,7 +170,7 @@ EOF } }' - local expected_output="device_info{disk=\"${disk}\",type=\"${disk_type}\",model_family=\"JBOD-Star\",device_model=\"ND-01Model\",serial_number=\"123456789\",firmware_version=\"80.00A80\",vendor=\"NorthernDigital\",product=\"ND-01\",revision=\"10.01\",lun_id=\"0\"} 1 + local expected_output="device_info{disk=\"${disk}\",type=\"${disk_type}\",model_family=\"JBOD-Star\",model_name=\"Star-Light\",device_model=\"ND-01Model\",serial_number=\"123456789\",firmware_version=\"80.00A80\",vendor=\"NorthernDigital\",product=\"ND-01\",revision=\"10.01\",lun_id=\"0\"} 1 smart_support_is_available{disk=\"${disk}\",type=\"${disk_type}\"} 1 smart_support_is_enabled{disk=\"${disk}\",type=\"${disk_type}\"} 1 smart_status_passed{disk=\"${disk}\",type=\"${disk_type}\"} 1" @@ -179,3 +180,89 @@ smart_status_passed{disk=\"${disk}\",type=\"${disk_type}\"} 1" assert_equal "${output}" "${expected_output}" } + + +@test "parse_smartctl_sata_attributes_json handle string temperature value" { + local disk="sda" + local disk_type="sat" + local test_json + local test_json='{ + "json_format_version": [ + 1, + 0 + ], + "ata_smart_attributes": { + "table": [ + { + "id": 194, + "name": "Temperature_Celsius", + "value": 69, + "worst": 40, + "thresh": 50, + "when_failed": "past", + "flags": { + "value": 34, + "string": "-O---K ", + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": false, + "auto_keep": true + }, + "raw": { + "value": 257699676191, + "string": "31 (Min/Max 25/60)" + } + } + ] + } +}' + + run run_parse_smartctl_attributes_json "$disk" "$disk_type" "$test_json" + assert_output - <<-EOF + temperature_celsius_value{disk="sda",type="sat",smart_id="194"} 69 + temperature_celsius_worst{disk="sda",type="sat",smart_id="194"} 40 + temperature_celsius_threshold{disk="sda",type="sat",smart_id="194"} 50 + temperature_celsius_raw_value{disk="sda",type="sat",smart_id="194"} 31 +EOF +} + +@test "parse_smartctl_sata_attributes_json parse temperature value" { + local disk="sda" + local disk_type="sat" + local test_json + local test_json='{ + "json_format_version": [ + 1, + 0 + ], + "ata_smart_attributes": { + "table": [ + { + "id": 194, + "name": "Temperature_Celsius", + "value": 69, + "worst": 40, + "thresh": 50, + "raw": { + "value": 257699676191, + "string": "31 (Min/Max 25/60)" + } + } + ] + }, + "temperature": { + "current": 31 + } +}' + + run run_parse_smartctl_attributes_json "$disk" "$disk_type" "$test_json" + assert_output - <<-EOF + temperature_celsius_value{disk="sda",type="sat",smart_id="194"} 69 + temperature_celsius_worst{disk="sda",type="sat",smart_id="194"} 40 + temperature_celsius_threshold{disk="sda",type="sat",smart_id="194"} 50 + temperature_celsius_raw_value{disk="sda",type="sat",smart_id="194"} 31 + temperature_current{disk="sda",type="sat"} 31 +EOF +} From 11f2d7ff9116a0b3444ff7c35170e8686af16fb3 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Fri, 9 Aug 2024 20:22:39 +0200 Subject: [PATCH 10/54] chore: remove test bak file --- test/test_smartmon.bak-bats | 199 ------------------------------------ 1 file changed, 199 deletions(-) delete mode 100644 test/test_smartmon.bak-bats diff --git a/test/test_smartmon.bak-bats b/test/test_smartmon.bak-bats deleted file mode 100644 index f7bbde6..0000000 --- a/test/test_smartmon.bak-bats +++ /dev/null @@ -1,199 +0,0 @@ -#!/usr/bin/env bats - -setup() { - load 'test_helper/bats-assert/load' - load 'test_helper/bats-support/load' - load '../src/smartmon.sh' - # make executables in src/ visible to PATH - PATH="$( pwd )/src/smartmon.sh:$PATH" -} - -### Begin Helper Functions -# Helper for parse_smartctl_nvme_attributes -run_parse_smartctl_nvme_attributes() { - local disk="$1" - local disk_type="$2" - local test_data="$3" - echo "$test_data" | parse_smartctl_nvme_attributes "$disk" "$disk_type" -} - -# Helper for parse_smartctl_attributes -run_parse_smartctl_attributes() { - local disk="$1" - local disk_type="$2" - local test_data="$3" - local smartmon_attrs="$4" - echo "$test_data" | parse_smartctl_attributes "$disk" "$disk_type" -} -### End Helper Functions - -# Example test_data for: "parse_smartctl_nvme_attributes" -# -#sudo smartctl -A -d nvme /dev/nvme0 -#smartctl 7.4 2023-08-01 r5530 [x86_64-linux-6.6.32-1-MANJARO] (local build) -#Copyright (C) 2002-23, Bruce Allen, Christian Franke, www.smartmontools.org -# -#=== START OF SMART DATA SECTION === -#SMART/Health Information (NVMe Log 0x02) -#Critical Warning: 0x00 -#Temperature: 28 Celsius -#Available Spare: 100% -#Available Spare Threshold: 10% -#Percentage Used: 0% -#Data Units Read: 2,389,400 [1.22 TB] -#Data Units Written: 3,958,438 [2.02 TB] -#Host Read Commands: 23,444,268 -#Host Write Commands: 51,391,963 -#Controller Busy Time: 79 -#Power Cycles: 1,012 -#Power On Hours: 70 -#Unsafe Shutdowns: 52 -#Media and Data Integrity Errors: 0 -#Error Information Log Entries: 0 -#Warning Comp. Temperature Time: 0 -#Critical Comp. Temperature Time: 0 -#Temperature Sensor 1: 36 Celsius -#Temperature Sensor 2: 28 Celsius - -############################################################################### -# sudo smartctl -A /dev/sda -#smartctl 7.1 2019-12-30 r5022 [x86_64-linux-5.4.0-182-generic] (local build) -#Copyright (C) 2002-19, Bruce Allen, Christian Franke, www.smartmontools.org -# -#=== START OF READ SMART DATA SECTION === -#SMART Attributes Data Structure revision number: 1 -#Vendor Specific SMART Attributes with Thresholds: -#ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE -# 1 Raw_Read_Error_Rate 0x002f 100 100 050 Pre-fail Always - 0 -# 5 Reallocate_NAND_Blk_Cnt 0x0032 100 100 010 Old_age Always - 0 -# 9 Power_On_Hours 0x0032 100 100 050 Old_age Always - 30104 -# 12 Power_Cycle_Count 0x0032 100 100 050 Old_age Always - 57 -#171 Program_Fail_Count 0x0032 100 100 050 Old_age Always - 0 -#172 Erase_Fail_Count 0x0032 100 100 050 Old_age Always - 0 -#173 Ave_Block-Erase_Count 0x0032 100 100 050 Old_age Always - 183 -#174 Unexpect_Power_Loss_Ct 0x0032 100 100 050 Old_age Always - 31 -#180 Unused_Reserve_NAND_Blk 0x0032 100 100 050 Old_age Always - 100 -#183 SATA_Interfac_Downshift 0x0032 100 100 050 Old_age Always - 120 -#184 Error_Correction_Count 0x0032 100 100 050 Old_age Always - 8 -#187 Reported_Uncorrect 0x0032 100 100 050 Old_age Always - 0 -#194 Temperature_Celsius 0x0022 069 049 050 Old_age Always In_the_past 31 (Min/Max 25/51) -#196 Reallocated_Event_Count 0x0032 100 100 050 Old_age Always - 0 -#197 Current_Pending_Sector 0x0032 100 100 050 Old_age Always - 0 -#198 Offline_Uncorrectable 0x0030 100 100 050 Old_age Offline - 0 -#199 UDMA_CRC_Error_Count 0x0032 100 100 050 Old_age Always - 0 -#202 Percent_Lifetime_Remain 0x0030 088 088 001 Old_age Offline - 88 -#206 Write_Error_Rate 0x002e 100 100 050 Old_age Always - 0 -#210 Success_RAIN_Recov_Cnt 0x0032 100 100 050 Old_age Always - 0 -#246 Total_LBAs_Written 0x0032 100 100 050 Old_age Always - 8866549306 -#247 Host_Program_Page_Count 0x0032 100 100 050 Old_age Always - 277079665 -#248 FTL_Program_Page_Count 0x0032 100 100 050 Old_age Always - 318468096 - - -### Begin Tests -@test "parse_smartctl_nvme_attributes with temperature test_data" { - local disk="nvme0" - local disk_type="nvme" - local test_data="Temperature: 28 Celsius" - - run run_parse_smartctl_nvme_attributes "$disk" "$disk_type" "$test_data" - # Note: Content of heredoc needs tabs as indentation to work. [%s/" "/\t/g] - assert_output - <<-EOF - Temperature - 28 Celsius - temperature_celsius_raw_value{disk="nvme0",type="nvme",smart_id="194"} 28 -EOF -} - -@test "parse_smartctl_nvme_attributes with empty test_data" { - local disk="nvme0" - local disk_type="nvme" - local test_data="" - - run run_parse_smartctl_nvme_attributes "$disk" "$disk_type" "$test_data" - # Note: Content of heredoc needs tabs as indentation to work. [%s/" "/\t/g] - assert_output "" -} - -@test "parse_smartctl_nvme_attributes data_units_written" { - local disk="nvme0" - local disk_type="nvme" - local test_data="Data Units Written: 3,958,438 [2.02 TB]" - - run run_parse_smartctl_nvme_attributes "$disk" "$disk_type" "$test_data" - # Note: Content of heredoc needs tabs as indentation to work. [%s/" "/\t/g] - assert_output - <<-EOF - Data_Units_Written - data_units_written_raw_value{disk="nvme0",type="nvme",smart_id="8"} 3958438 -EOF -} - -@test "parse_smartctl_sata_attributes power_on_hours" { - local disk="sda" - local disk_type="sat" - local test_data="9 Power_On_Hours 0x0032 100 100 050 Old_age Always - 30104" - local smartmon_attrs="power_on_hours" - - run run_parse_smartctl_attributes "$disk" "$disk_type" "$test_data" - # Note: Content of heredoc needs tabs as indentation to work. [%s/" "/\t/g] - assert_output - <<-EOF - power_on_hours_value{disk="sda",type="sat",smart_id="9"} 100 - power_on_hours_worst{disk="sda",type="sat",smart_id="9"} 100 - power_on_hours_threshold{disk="sda",type="sat",smart_id="9"} 50 - power_on_hours_raw_value{disk="sda",type="sat",smart_id="9"} 3.010400e+04 -EOF -} - -@test "parse_smartctl_sata_attributes invalid data" { - local disk="sda" - local disk_type="sat" - local test_data="FOOBAR" - - run run_parse_smartctl_attributes "$disk" "$disk_type" "$test_data" - # No output expected - assert_output "" -} - -@test "parse_smartctl_sata_attributes replace hyphens" { - local disk="sda" - local disk_type="sat" - local test_data="9 Power-On-Hours" - local smartmon_attrs="power_on_hours" - - run run_parse_smartctl_attributes "$disk" "$disk_type" "$test_data" "$smartmon_attrs" - assert_output - <<-EOF - power_on_hours_value{disk="sda",type="sat",smart_id="9"} 0 - power_on_hours_worst{disk="sda",type="sat",smart_id="9"} 0 - power_on_hours_threshold{disk="sda",type="sat",smart_id="9"} 0 - power_on_hours_raw_value{disk="sda",type="sat",smart_id="9"} 0.000000e+00 -EOF -} - -@test "parse_smartctl_sata_attributes multiple inputs" { - local disk="sda" - local disk_type="sat" - local smartmon_attrs="power_on_hours|power_cycle_count" - local test_data="9 Power_On_Hours 0x0032 100 100 050 Old_age Always - 30104\n 12 Power_Cycle_Count 0x0032 100 100 050 Old_age Always - 57\n" - - run run_parse_smartctl_attributes "$disk" "$disk_type" "$test_data" "$smartmon_attrs" - assert_output - <<-EOF - power_on_hours_value{disk="sda",type="sat",smart_id="9"} 100 - power_on_hours_worst{disk="sda",type="sat",smart_id="9"} 100 - power_on_hours_threshold{disk="sda",type="sat",smart_id="9"} 50 - power_on_hours_raw_value{disk="sda",type="sat",smart_id="9"} 3.010400e+04 -EOF -} - -@test "parse_smartctl_sata_attributes mixed inputs valid and invalid" { - local disk="sda" - local disk_type="sat" - local smartmon_attrs="power_on_hours|power_cycle_count" - local test_data="9 Power_On_Hours 0x0032 100 100 050 Old_age Always - 30104\n THISISINVALID \n 12 Power_Cycle_Count 0x0032 100 100 050 Old_age Always - 57\n" - - run run_parse_smartctl_attributes "$disk" "$disk_type" "$test_data" "$smartmon_attrs" - assert_output - <<-EOF - power_on_hours_value{disk="sda",type="sat",smart_id="9"} 100 - power_on_hours_worst{disk="sda",type="sat",smart_id="9"} 100 - power_on_hours_threshold{disk="sda",type="sat",smart_id="9"} 50 - power_on_hours_raw_value{disk="sda",type="sat",smart_id="9"} 3.010400e+04 -EOF -} From 4b8dc0f67328a189ed51db272b71afeb68ce0f72 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Tue, 13 Aug 2024 22:35:36 +0200 Subject: [PATCH 11/54] feat: add written_bytes --- src/smartmon-json.sh | 73 ++++++++++++++++++++++++++---------- test/test_smartmon-json.bats | 5 ++- 2 files changed, 56 insertions(+), 22 deletions(-) diff --git a/src/smartmon-json.sh b/src/smartmon-json.sh index a56e5e4..e6fbd3d 100755 --- a/src/smartmon-json.sh +++ b/src/smartmon-json.sh @@ -48,25 +48,25 @@ device_info{${labels}, EOF ) -for key in "${keys_general[@]}"; do - device_infos+="$key="\"$(jq -r --arg key "$key" '.[$key] // empty' <<< "$json")\"\, -done - -# Remove the last comma -device_infos_jsonized=${device_infos%,} - -echo "$device_infos_jsonized""}" "1" - -for key in "${keys_binary[@]}"; do - value="$(jq -r ."$key // 0" <<< "$json")" - # Convert boolean to numeric - if [ "$value" == "true" ]; then - value=1 - elif [ "$value" == "false" ]; then - value=0 - fi - echo "$(echo "$key" | tr '.' '_'){${labels}} ${value}" -done + for key in "${keys_general[@]}"; do + device_infos+="$key="\"$(jq -r --arg key "$key" '.[$key] // empty' <<< "$json")\"\, + done + + # Remove the last comma + device_infos_jsonized=${device_infos%,} + + echo "$device_infos_jsonized""}" "1" + + for key in "${keys_binary[@]}"; do + value="$(jq -r ."$key // 0" <<< "$json")" + # Convert boolean to numeric + if [ "$value" == "true" ]; then + value=1 + elif [ "$value" == "false" ]; then + value=0 + fi + echo "$(echo "$key" | tr '.' '_'){${labels}} ${value}" + done } # Parse and extract ATA SMART attributes from the provided JSON. @@ -154,6 +154,31 @@ parse_smartctl_attributes_json() { # $1 - Disk name # $2 - Disk type # $3 - JSON string containing NVMe SMART attributes +#parse_smartctl_nvme_attributes_json() { +# local disk="$1" +# local disk_type="$2" +# local json="$3" +# local labels="disk=\"${disk}\",type=\"${disk_type}\"" +# +# # return early if no input +# if [ "$json" == {} ]; then +# return 0 +# fi +# +# # Extract and format NVMe SMART attributes using jq +# echo "$json" | jq -r ' +# .nvme_smart_health_information_log | +# to_entries[] | +# select(.key and (.value | type != "array")) | +# [ +# (.key | gsub("-"; "_")), +# .value +# ] | @tsv +# ' | while IFS=$'\t' read -r key value; do +# local metric_name="${key}" +# echo "$(echo "${metric_name}" | awk '{print tolower($0)}'){${labels}} ${value}" +# done +#} parse_smartctl_nvme_attributes_json() { local disk="$1" local disk_type="$2" @@ -176,6 +201,13 @@ parse_smartctl_nvme_attributes_json() { ] | @tsv ' | while IFS=$'\t' read -r key value; do local metric_name="${key}" + # Data Units Written: Represents the number of 512-byte data units written. + # It is reported as thousends 1 = 1000 units of 512 bytes) + # Block-sizes different from 512 will be converted to 512 + if [ "${metric_name}" == "data_units_written" ]; then + local written_bytes=$((value * 512 *1000)) + echo "written_bytes{${labels}} ${written_bytes}" + fi echo "$(echo "${metric_name}" | awk '{print tolower($0)}'){${labels}} ${value}" done } @@ -267,7 +299,7 @@ process_device() { # Get and parse SMART information local info_json - info_json=$(/usr/sbin/smartctl -i -H -j -d "${type}" "${disk}") + info_json=$(/usr/sbin/smartctl -i -j -d "${type}" "${disk}") parse_smartctl_info_json "${disk}" "${type}" "${info_json}" # Get and parse SMART attributes @@ -275,6 +307,7 @@ process_device() { attributes_json=$(/usr/sbin/smartctl -A -j -d "${type}" "${disk}") case ${type} in sat|sat+megaraid*) + # TODO: continue integrating block size if available from json info and multiply with lbs_written if avail parse_smartctl_attributes_json "${disk}" "${type}" "${attributes_json}" ;; nvme) diff --git a/test/test_smartmon-json.bats b/test/test_smartmon-json.bats index 1f2d9f8..28e3543 100644 --- a/test/test_smartmon-json.bats +++ b/test/test_smartmon-json.bats @@ -50,11 +50,12 @@ run_parse_smartctl_attributes_json() { @test "parse_smartctl_nvme_attributes_json data_units_written" { local disk="nvme0" local disk_type="nvme" - local test_data='{"nvme_smart_health_information_log": {"data_units_written": 3958438}}' + local test_data='{"nvme_smart_health_information_log": {"data_units_written": 4211733}}' run run_parse_smartctl_nvme_attributes_json "$disk" "$disk_type" "$test_data" assert_output - <<-EOF -data_units_written{disk="nvme0",type="nvme"} 3958438 +written_bytes{disk="nvme0",type="nvme"} 2156407296000 +data_units_written{disk="nvme0",type="nvme"} 4211733 EOF } From abbe3a5190302f257c5810d20574251ccf1eb60f Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Sun, 18 Aug 2024 11:16:33 +0200 Subject: [PATCH 12/54] get sector_size from smartctl info --- src/smartmon-json.sh | 31 +++++++++++++--- test/test_smartmon-json.bats | 69 ++++++++++++++++++++++++++++++++---- 2 files changed, 89 insertions(+), 11 deletions(-) diff --git a/src/smartmon-json.sh b/src/smartmon-json.sh index e6fbd3d..a852400 100755 --- a/src/smartmon-json.sh +++ b/src/smartmon-json.sh @@ -15,6 +15,14 @@ # all elements from the JSON output of smartctl this seems more robust as # different vendors might use inconsistent value naming. +# Check if this script is run as root +# Do not exit if test is run by bats +check_root() { + if [ "$EUID" -ne 0 ] && [ -z "$BATS_RUN_SKIP" ]; then + echo "This script must be run as root" + exit 1 + fi +} # Check if the 'jq' command is available. ensure_jq_installed() { @@ -75,10 +83,12 @@ EOF # $1 - Disk name # $2 - Disk type # $3 - JSON string containing SMART attributes +# $4 - Sector size for calculating written_bytes parse_smartctl_attributes_json() { local disk="$1" local disk_type="$2" - local json="$3" + local sector_size="$3" + local json="$4" local labels="disk=\"${disk}\",type=\"${disk_type}\"" # return early if no input @@ -136,6 +146,12 @@ parse_smartctl_attributes_json() { "$labels" \ "$id" \ "$raw" + + # Calculate written_bytes for SATA devices + if [[ "$name" =~ ^Total_LBAs_Written$ ]]; then + local written_bytes=$((raw * sector_size)) + printf "written_bytes{%s} %s\n" "$labels" "$written_bytes" + fi done # Extract and format temperature @@ -145,7 +161,6 @@ parse_smartctl_attributes_json() { if [[ -n "$temperature" ]]; then printf "temperature_current{%s} %s\n" "$labels" "$temperature" fi - } # Parse and extract NVMe SMART attributes from the provided JSON. @@ -307,8 +322,15 @@ process_device() { attributes_json=$(/usr/sbin/smartctl -A -j -d "${type}" "${disk}") case ${type} in sat|sat+megaraid*) - # TODO: continue integrating block size if available from json info and multiply with lbs_written if avail - parse_smartctl_attributes_json "${disk}" "${type}" "${attributes_json}" + # Extract sector size from info_json for SATA devices + local sector_size=512 + local extracted_sector_size + extracted_sector_size=$(echo "$info_json" | jq -r '.logical_block_size') + if [ "$extracted_sector_size" != "null" ]; then + sector_size="$extracted_sector_size" + fi + + parse_smartctl_attributes_json "${disk}" "${type}" "${sector_size}" "${attributes_json}" ;; nvme) parse_smartctl_nvme_attributes_json "${disk}" "${type}" "${attributes_json}" @@ -322,6 +344,7 @@ process_device() { # Main function to orchestrate the script. main() { + check_root ensure_jq_installed output_smartctl_version diff --git a/test/test_smartmon-json.bats b/test/test_smartmon-json.bats index 28e3543..2823844 100644 --- a/test/test_smartmon-json.bats +++ b/test/test_smartmon-json.bats @@ -1,6 +1,7 @@ #!/usr/bin/env bats setup() { + export BATS_RUN_SKIP=true # used to detect that script is tested not run load 'test_helper/bats-assert/load' load 'test_helper/bats-support/load' load '../src/smartmon-json.sh' @@ -22,9 +23,10 @@ run_parse_smartctl_nvme_attributes_json() { run_parse_smartctl_attributes_json() { local disk="$1" local disk_type="$2" - local test_data="$3" + local sector_size="$3" + local test_data="$4" # echo "$test_data" | parse_smartctl_attributes_json "$disk" "$disk_type" - parse_smartctl_attributes_json "$disk" "$disk_type" "$test_data" + parse_smartctl_attributes_json "$disk" "$disk_type" "$sector_size" "$test_data" } ### End Helper Functions ### @@ -59,9 +61,58 @@ data_units_written{disk="nvme0",type="nvme"} 4211733 EOF } +@test "parse_smartctl_sata_attributes_json sat written_bytes" { + local disk="sda" + local disk_type="sat" + local sector_size="512" + local test_json + local test_json='{ + "json_format_version": [ + 1, + 0 + ], + "ata_smart_attributes": { + "table": [ + { + "id": 246, + "name": "Total_LBAs_Written", + "value": 100, + "worst": 100, + "thresh": 50, + "when_failed": "", + "flags": { + "value": 50, + "string": "-O--CK ", + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true + }, + "raw": { + "value": 9525606174, + "string": "9525606174" + } + } + ] + } +}' + + run run_parse_smartctl_attributes_json "$disk" "$disk_type" "$sector_size" "$test_json" + assert_output - <<-EOF + total_lbas_written_value{disk="sda",type="sat",smart_id="246"} 100 + total_lbas_written_worst{disk="sda",type="sat",smart_id="246"} 100 + total_lbas_written_threshold{disk="sda",type="sat",smart_id="246"} 50 + total_lbas_written_raw_value{disk="sda",type="sat",smart_id="246"} 9525606174 + written_bytes{disk="sda",type="sat"} 4877110361088 +EOF +} + @test "parse_smartctl_sata_attributes_json power_on_hours" { local disk="sda" local disk_type="sat" + local sector_size="512" local test_json local test_json='{ "json_format_version": [ @@ -96,7 +147,7 @@ EOF } }' - run run_parse_smartctl_attributes_json "$disk" "$disk_type" "$test_json" + run run_parse_smartctl_attributes_json "$disk" "$disk_type" "$sector_size" "$test_json" assert_output - <<-EOF power_on_hours_value{disk="sda",type="sat",smart_id="9"} 100 power_on_hours_worst{disk="sda",type="sat",smart_id="9"} 100 @@ -108,15 +159,17 @@ EOF @test "parse_smartctl_sata_attributes_json invalid data" { local disk="sda" local disk_type="sat" + local sector_size="512" local test_data='{}' - run run_parse_smartctl_attributes_json "$disk" "$disk_type" "$test_data" + run run_parse_smartctl_attributes_json "$disk" "$disk_type" "$sector_size" "$test_data" assert_output "" } @test "parse_smartctl_sata_attributes_json replace hyphens" { local disk="sda" local disk_type="sat" + local sector_size="512" local test_json local test_json='{ "json_format_version": [ @@ -140,7 +193,7 @@ EOF } }' - run run_parse_smartctl_attributes_json "$disk" "$disk_type" "$test_json" + run run_parse_smartctl_attributes_json "$disk" "$disk_type" "$sector_size" "$test_json" assert_output - <<-EOF power_on_hours_value{disk="sda",type="sat",smart_id="9"} 100 power_on_hours_worst{disk="sda",type="sat",smart_id="9"} 100 @@ -186,6 +239,7 @@ smart_status_passed{disk=\"${disk}\",type=\"${disk_type}\"} 1" @test "parse_smartctl_sata_attributes_json handle string temperature value" { local disk="sda" local disk_type="sat" + local sector_size="512" local test_json local test_json='{ "json_format_version": [ @@ -220,7 +274,7 @@ smart_status_passed{disk=\"${disk}\",type=\"${disk_type}\"} 1" } }' - run run_parse_smartctl_attributes_json "$disk" "$disk_type" "$test_json" + run run_parse_smartctl_attributes_json "$disk" "$disk_type" "$sector_size" "$test_json" assert_output - <<-EOF temperature_celsius_value{disk="sda",type="sat",smart_id="194"} 69 temperature_celsius_worst{disk="sda",type="sat",smart_id="194"} 40 @@ -232,6 +286,7 @@ EOF @test "parse_smartctl_sata_attributes_json parse temperature value" { local disk="sda" local disk_type="sat" + local sector_size="512" local test_json local test_json='{ "json_format_version": [ @@ -258,7 +313,7 @@ EOF } }' - run run_parse_smartctl_attributes_json "$disk" "$disk_type" "$test_json" + run run_parse_smartctl_attributes_json "$disk" "$disk_type" "$sector_size" "$test_json" assert_output - <<-EOF temperature_celsius_value{disk="sda",type="sat",smart_id="194"} 69 temperature_celsius_worst{disk="sda",type="sat",smart_id="194"} 40 From 58d76a3b1d87a0e4070fdc2875d166a96bfe5dd3 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Wed, 21 Aug 2024 21:04:48 +0200 Subject: [PATCH 13/54] add coverage to readme --- README.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e07d8cb..8594963 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +![coverage](https://img.shields.io/badge/Coverage-50%25-brightgreen) + # S.M.A.R.T.-disk-monitoring-for-Prometheus text_collector Prometheus `node_exporter` `text_collector` for S.M.A.R.T disk values @@ -79,5 +81,18 @@ https://www.smartmontools.org/wiki/Download#Installfromthesourcetarball # TODO: tests install bats (bats-core): [bats-tutorial](https://bats-core.readthedocs.io/en/stable/tutorial.html) +## Coverage +´´´sh +run_coverage.sh +``` + +### Manual Coverage +kcov --bash-dont-parse-binary-dir \ + --include-path=. \ + /var/tmp/coverage \ + bats -t test/test_smartmon-json.bats + ## TODO -- Proper implementation and tets for SCSI type disks. Please provide input if interested. +- Write docs +- docker +- systemd instead of cron From 9100d74485e5030ce630c349f577a46786627590 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Wed, 21 Aug 2024 22:13:21 +0200 Subject: [PATCH 14/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 45 +++++++++++++++++ README.md | 2 +- coverage.sh | 49 +++++++++++++++++++ src/{smartmon-json.sh => smartmon.sh} | 0 ..._smartmon-json.bats => test_smartmon.bats} | 4 +- 5 files changed, 97 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/github-workflow.yaml create mode 100755 coverage.sh rename src/{smartmon-json.sh => smartmon.sh} (100%) rename test/{test_smartmon-json.bats => test_smartmon.bats} (99%) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml new file mode 100644 index 0000000..1b20b5e --- /dev/null +++ b/.github/workflows/github-workflow.yaml @@ -0,0 +1,45 @@ +name: Coverage Report + +on: + push: + branches: + - main + - dev + +jobs: + coverage: + runs-on: ubuntu-22.04 + + services: + kcov: + image: kcov/kcov:v42 + options: --entrypoint sh + volumes: + - ${{ github.workspace }}:/workspace + + steps: + - name: Checkout code + uses: actions/checkout@v3.6.0 + + - name: Run coverage script inside kcov service container + run: | + docker exec ${{ job.services.kcov.id }} sh -c "cd /workspace && ./coverage.sh" + + - name: Extract coverage from index.js + id: coverage + run: | + # Extract the coverage percentage directly from the index.js file + coverage=$(grep -oP '(?<=covered"\s*:\s*")\d+\.\d+' coverage/test_smartmon-json.coverage/index.js) + + # Output the coverage percentage + echo "::set-output name=coverage::$coverage" + + - name: Update README.md with coverage badge + if: github.ref_name == 'main' || github.ref_name == 'dev' + run: | + sed -i 's|\[!\[Coverage\](https://img.shields.io/badge/Coverage-[0-9]*%25-brightgreen)\]|[!\[Coverage\](https://img.shields.io/badge/Coverage-${{ steps.coverage.outputs.coverage }}%25-brightgreen)]|' README.md + git config --global user.name 'github-actions[bot]' + git config --global user.email 'github-actions[bot]@users.noreply.github.com' + git add README.md + git commit -m "Update coverage badge" + git push origin ${{ github.ref_name }} diff --git a/README.md b/README.md index 8594963..185d3e9 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ run_coverage.sh kcov --bash-dont-parse-binary-dir \ --include-path=. \ /var/tmp/coverage \ - bats -t test/test_smartmon-json.bats + bats -t test/test_smartmon.bats ## TODO - Write docs diff --git a/coverage.sh b/coverage.sh new file mode 100755 index 0000000..6935cc8 --- /dev/null +++ b/coverage.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash + +SRC_DIR="./src" +TEST_DIR="./test" +COVERAGE_DIR="./coverage" + +# Function to create the coverage directory +create_coverage_dir() { + mkdir -p "$COVERAGE_DIR" +} + +# Function to run kcov with bats tests +run_kcov() { + local test_file; test_file="$1" + local src_file; src_file="$2" + local coverage_file; coverage_file="$COVERAGE_DIR/$(basename "$test_file" .bats).coverage" + + if ! kcov --bash-dont-parse-binary-dir --include-path="$SRC_DIR" "$coverage_file" bats -t "$test_file"; then + printf "Error: kcov failed for %s\n" "$test_file" >&2 + return 1 + fi +} + +# Main function +main() { + create_coverage_dir + + local test_file; test_file="$TEST_DIR/test_smartmon.bats" + local src_file; src_file="$SRC_DIR/smartmon.sh" + + if [[ ! -f "$test_file" ]]; then + printf "Error: Test file %s does not exist\n" "$test_file" >&2 + return 1 + fi + + if [[ ! -f "$src_file" ]]; then + printf "Error: Source file %s does not exist\n" "$src_file" >&2 + return 1 + fi + + if ! run_kcov "$test_file" "$src_file"; then + printf "Error: Failed to run kcov for %s\n" "$test_file" >&2 + return 1 + fi + + printf "Coverage report generated in %s\n" "$COVERAGE_DIR" +} + +main "$@" diff --git a/src/smartmon-json.sh b/src/smartmon.sh similarity index 100% rename from src/smartmon-json.sh rename to src/smartmon.sh diff --git a/test/test_smartmon-json.bats b/test/test_smartmon.bats similarity index 99% rename from test/test_smartmon-json.bats rename to test/test_smartmon.bats index 2823844..04f452b 100644 --- a/test/test_smartmon-json.bats +++ b/test/test_smartmon.bats @@ -4,9 +4,9 @@ setup() { export BATS_RUN_SKIP=true # used to detect that script is tested not run load 'test_helper/bats-assert/load' load 'test_helper/bats-support/load' - load '../src/smartmon-json.sh' + load '../src/smartmon.sh' # make executables in src/ visible to PATH - PATH="$( pwd )/src/smartmon-json.sh:$PATH" + PATH="$( pwd )/src/smartmon.sh:$PATH" } ### Begin Helper Functions ### From 72ab699d9580f73717a3b3a0bc86b2fb9a8e3c97 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Wed, 21 Aug 2024 22:16:11 +0200 Subject: [PATCH 15/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index 1b20b5e..37538e9 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -8,6 +8,7 @@ on: jobs: coverage: + runs-on: ubuntu-22.04 services: From b822b40cb2a5fdf90c73cf0d746168bb74ce5776 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Wed, 21 Aug 2024 22:18:22 +0200 Subject: [PATCH 16/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 36 +++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index 37538e9..9da370f 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -26,21 +26,21 @@ jobs: run: | docker exec ${{ job.services.kcov.id }} sh -c "cd /workspace && ./coverage.sh" - - name: Extract coverage from index.js - id: coverage - run: | - # Extract the coverage percentage directly from the index.js file - coverage=$(grep -oP '(?<=covered"\s*:\s*")\d+\.\d+' coverage/test_smartmon-json.coverage/index.js) - - # Output the coverage percentage - echo "::set-output name=coverage::$coverage" - - - name: Update README.md with coverage badge - if: github.ref_name == 'main' || github.ref_name == 'dev' - run: | - sed -i 's|\[!\[Coverage\](https://img.shields.io/badge/Coverage-[0-9]*%25-brightgreen)\]|[!\[Coverage\](https://img.shields.io/badge/Coverage-${{ steps.coverage.outputs.coverage }}%25-brightgreen)]|' README.md - git config --global user.name 'github-actions[bot]' - git config --global user.email 'github-actions[bot]@users.noreply.github.com' - git add README.md - git commit -m "Update coverage badge" - git push origin ${{ github.ref_name }} + - name: Extract coverage from index.js + id: coverage + run: | + # Extract the coverage percentage directly from the index.js file + coverage=$(grep -oP '(?<=covered"\s*:\s*")\d+\.\d+' coverage/test_smartmon-json.coverage/index.js) + + # Output the coverage percentage + echo "::set-output name=coverage::$coverage" + + - name: Update README.md with coverage badge + if: github.ref_name == 'main' || github.ref_name == 'dev' + run: | + sed -i 's|\[!\[Coverage\](https://img.shields.io/badge/Coverage-[0-9]*%25-brightgreen)\]|[!\[Coverage\](https://img.shields.io/badge/Coverage-${{ steps.coverage.outputs.coverage }}%25-brightgreen)]|' README.md + git config --global user.name 'github-actions[bot]' + git config --global user.email 'github-actions[bot]@users.noreply.github.com' + git add README.md + git commit -m "Update coverage badge" + git push origin ${{ github.ref_name }} From 9abd107add122e0dad662ac734fda721b1c1b70e Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Wed, 21 Aug 2024 22:21:59 +0200 Subject: [PATCH 17/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index 9da370f..3c1937d 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -1,3 +1,4 @@ +--- name: Coverage Report on: From aa7fe4acd77206678b73a9c43dfeffb777bd17ba Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Wed, 21 Aug 2024 22:23:34 +0200 Subject: [PATCH 18/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index 3c1937d..af7aadd 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -1,5 +1,5 @@ --- -name: Coverage Report +name: Coverage on: push: From 96bff8115399e744ba7fade6f1c6f2305ab58265 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Wed, 21 Aug 2024 22:26:34 +0200 Subject: [PATCH 19/54] add workflow for coverage --- coverage.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverage.sh b/coverage.sh index 6935cc8..5fb46b4 100755 --- a/coverage.sh +++ b/coverage.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh SRC_DIR="./src" TEST_DIR="./test" From b0179c694cc1bb5dc4b3f6e37bc58527d4930428 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Wed, 21 Aug 2024 22:35:46 +0200 Subject: [PATCH 20/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index af7aadd..548352d 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -23,6 +23,10 @@ jobs: - name: Checkout code uses: actions/checkout@v3.6.0 + - name: DEBUG + run: | + docker exec ${{ job.services.kcov.id }} sh -c "echo test" + - name: Run coverage script inside kcov service container run: | docker exec ${{ job.services.kcov.id }} sh -c "cd /workspace && ./coverage.sh" From 6e6871e0cb3ae51171ca7a303120892e0092ef85 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Wed, 21 Aug 2024 22:44:14 +0200 Subject: [PATCH 21/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 32 +++++++++----------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index 548352d..ec269fe 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -9,43 +9,33 @@ on: jobs: coverage: - runs-on: ubuntu-22.04 - - services: - kcov: - image: kcov/kcov:v42 - options: --entrypoint sh - volumes: - - ${{ github.workspace }}:/workspace + container: + image: kcov/kcov:v42 + options: --privileged + # volumes: + # - ${{ github.workspace }}:/workspace steps: - name: Checkout code uses: actions/checkout@v3.6.0 - - name: DEBUG + - name: Run coverage script run: | - docker exec ${{ job.services.kcov.id }} sh -c "echo test" + chmod +x ./coverage.sh + ./coverage.sh - - name: Run coverage script inside kcov service container - run: | - docker exec ${{ job.services.kcov.id }} sh -c "cd /workspace && ./coverage.sh" - - - name: Extract coverage from index.js + - name: Generate coverage percentage id: coverage run: | - # Extract the coverage percentage directly from the index.js file - coverage=$(grep -oP '(?<=covered"\s*:\s*")\d+\.\d+' coverage/test_smartmon-json.coverage/index.js) - - # Output the coverage percentage + coverage=$(grep -o 'Overall coverage rate.*' | grep -o '[0-9]*\.[0-9]*') echo "::set-output name=coverage::$coverage" - name: Update README.md with coverage badge - if: github.ref_name == 'main' || github.ref_name == 'dev' run: | sed -i 's|\[!\[Coverage\](https://img.shields.io/badge/Coverage-[0-9]*%25-brightgreen)\]|[!\[Coverage\](https://img.shields.io/badge/Coverage-${{ steps.coverage.outputs.coverage }}%25-brightgreen)]|' README.md git config --global user.name 'github-actions[bot]' git config --global user.email 'github-actions[bot]@users.noreply.github.com' git add README.md git commit -m "Update coverage badge" - git push origin ${{ github.ref_name }} + git push origin main From 0302c2f5fe643c5b0dd4bc878bbba078d5db7809 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Wed, 21 Aug 2024 22:45:48 +0200 Subject: [PATCH 22/54] add workflow for coverage --- coverage.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverage.sh b/coverage.sh index 5fb46b4..6935cc8 100755 --- a/coverage.sh +++ b/coverage.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env sh +#!/usr/bin/env bash SRC_DIR="./src" TEST_DIR="./test" From a4795331951f82db940f59c69080f6c39a34e7d2 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Wed, 21 Aug 2024 22:51:06 +0200 Subject: [PATCH 23/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index ec269fe..89e6338 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -20,6 +20,13 @@ jobs: - name: Checkout code uses: actions/checkout@v3.6.0 + - name: Install specific version of bats + run: | + curl -L https://github.com/bats-core/bats-core/archive/v1.4.1.tar.gz | tar -xz + cd bats-core-1.4.1 + ./install.sh /usr/local + rm -rf bats-core-1.4.1 + - name: Run coverage script run: | chmod +x ./coverage.sh From 0b3042a6a5bbdc2609eb79be96ea6632875c1248 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Wed, 21 Aug 2024 22:53:19 +0200 Subject: [PATCH 24/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index 89e6338..21eab72 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -20,6 +20,10 @@ jobs: - name: Checkout code uses: actions/checkout@v3.6.0 + - name: Install curl + run: | + apt-get update && apt-get install -y curl + - name: Install specific version of bats run: | curl -L https://github.com/bats-core/bats-core/archive/v1.4.1.tar.gz | tar -xz From a5a3d2d441c0850739abbcca105a1b1db022603d Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 09:46:10 +0200 Subject: [PATCH 25/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index 21eab72..abc1b5d 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -17,8 +17,14 @@ jobs: # - ${{ github.workspace }}:/workspace steps: - - name: Checkout code + # - name: Checkout code + # uses: actions/checkout@v3.6.0 + + - name: Checkout code with submodules uses: actions/checkout@v3.6.0 + with: + submodules: true # Fetch all submodules + fetch-depth: 0 # Necessary to fetch all branches/tags, including submodules - name: Install curl run: | From 8ccf0ed5e3cb9cdeac010534add18b5af8d6e735 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 09:50:15 +0200 Subject: [PATCH 26/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index abc1b5d..6a2d338 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -17,14 +17,8 @@ jobs: # - ${{ github.workspace }}:/workspace steps: - # - name: Checkout code - # uses: actions/checkout@v3.6.0 - - - name: Checkout code with submodules + - name: Checkout code uses: actions/checkout@v3.6.0 - with: - submodules: true # Fetch all submodules - fetch-depth: 0 # Necessary to fetch all branches/tags, including submodules - name: Install curl run: | @@ -37,6 +31,10 @@ jobs: ./install.sh /usr/local rm -rf bats-core-1.4.1 + - name: Install bats-assert + run: | + git clone https://github.com/bats-core/bats-assert.git /usr/local/lib/bats-assert + - name: Run coverage script run: | chmod +x ./coverage.sh From d8c73394c0ab3bf8c8e72c6d2b7e6ba58472ce6e Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 09:52:36 +0200 Subject: [PATCH 27/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index 6a2d338..bf0cd01 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -20,9 +20,9 @@ jobs: - name: Checkout code uses: actions/checkout@v3.6.0 - - name: Install curl + - name: Install necessary tools (Git and Curl) run: | - apt-get update && apt-get install -y curl + apt-get update && apt-get install -y git curl - name: Install specific version of bats run: | From b513c673ff2cb34487296db0c5b36852c0a5fed6 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 09:55:56 +0200 Subject: [PATCH 28/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index bf0cd01..0d742a8 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -31,8 +31,9 @@ jobs: ./install.sh /usr/local rm -rf bats-core-1.4.1 - - name: Install bats-assert + - name: Install bats-assert and bats-support run: | + git clone https://github.com/bats-core/bats-support.git /usr/local/lib/bats-support git clone https://github.com/bats-core/bats-assert.git /usr/local/lib/bats-assert - name: Run coverage script From 909bb2c1b9898af1afd0a81f9d139e07c0834ea4 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 09:57:35 +0200 Subject: [PATCH 29/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index 0d742a8..8ef434b 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -36,10 +36,11 @@ jobs: git clone https://github.com/bats-core/bats-support.git /usr/local/lib/bats-support git clone https://github.com/bats-core/bats-assert.git /usr/local/lib/bats-assert - - name: Run coverage script + - name: Run coverage script with Bash run: | + export BATS_LIB_PATH=/usr/local/lib chmod +x ./coverage.sh - ./coverage.sh + bash ./coverage.sh - name: Generate coverage percentage id: coverage From a6b1f20b140565f197679353dc9ad362f677c062 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 10:02:03 +0200 Subject: [PATCH 30/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index 8ef434b..4e611ee 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -24,21 +24,15 @@ jobs: run: | apt-get update && apt-get install -y git curl - - name: Install specific version of bats - run: | - curl -L https://github.com/bats-core/bats-core/archive/v1.4.1.tar.gz | tar -xz - cd bats-core-1.4.1 - ./install.sh /usr/local - rm -rf bats-core-1.4.1 - - - name: Install bats-assert and bats-support - run: | - git clone https://github.com/bats-core/bats-support.git /usr/local/lib/bats-support - git clone https://github.com/bats-core/bats-assert.git /usr/local/lib/bats-assert + - name: Checkout code with submodules + uses: actions/checkout@v3 + with: + submodules: true # Fetch all submodules + fetch-depth: 0 # Fetch full history, required for submodules + submodule-token: ${{ secrets.GITHUB_TOKEN }} # Ensure access to private submodules if needed - name: Run coverage script with Bash run: | - export BATS_LIB_PATH=/usr/local/lib chmod +x ./coverage.sh bash ./coverage.sh From bf9304a156df679c230b1216a7ffb9ef1dd79d9f Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 15:41:10 +0200 Subject: [PATCH 31/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index 4e611ee..df20253 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -1,6 +1,8 @@ +# yamllint disable rule:line-length --- name: Coverage +# yamllint disable-line rule:truthy on: push: branches: @@ -24,6 +26,13 @@ jobs: run: | apt-get update && apt-get install -y git curl + - name: Install specific version of bats + run: | + curl -L https://github.com/bats-core/bats-core/archive/v1.4.1.tar.gz | tar -xz + cd bats-core-1.4.1 + ./install.sh /usr/local + rm -rf bats-core-1.4.1 + - name: Checkout code with submodules uses: actions/checkout@v3 with: From a884d62f427c54ff4a9b306b1fa3bf30c3bac3dd Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 15:42:21 +0200 Subject: [PATCH 32/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index df20253..cc5792a 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -24,7 +24,7 @@ jobs: - name: Install necessary tools (Git and Curl) run: | - apt-get update && apt-get install -y git curl + apt-get update && apt-get install -y curl git jq - name: Install specific version of bats run: | From db5ddad6b030e1f10a3e7a17933a06673193c259 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 15:48:29 +0200 Subject: [PATCH 33/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index cc5792a..d115f12 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -24,7 +24,7 @@ jobs: - name: Install necessary tools (Git and Curl) run: | - apt-get update && apt-get install -y curl git jq + apt-get update && apt-get install -y curl git jq smartctl - name: Install specific version of bats run: | From edf06300ad2984201b78fade2c35a7d916f3665d Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 15:50:41 +0200 Subject: [PATCH 34/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index d115f12..bc5b023 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -24,7 +24,7 @@ jobs: - name: Install necessary tools (Git and Curl) run: | - apt-get update && apt-get install -y curl git jq smartctl + apt-get update && apt-get install -y curl git jq smartmontools - name: Install specific version of bats run: | From 203cd25816d62589007eaec3177eb578e6ae7e48 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 15:53:03 +0200 Subject: [PATCH 35/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index bc5b023..0b337ec 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -47,6 +47,7 @@ jobs: - name: Generate coverage percentage id: coverage + shell: bash run: | coverage=$(grep -o 'Overall coverage rate.*' | grep -o '[0-9]*\.[0-9]*') echo "::set-output name=coverage::$coverage" From 22825c06bb9a6d576155f5c7cb6338fa5dc29002 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 15:57:13 +0200 Subject: [PATCH 36/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index 0b337ec..3c6e62f 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -45,11 +45,12 @@ jobs: chmod +x ./coverage.sh bash ./coverage.sh - - name: Generate coverage percentage + - name: Extract coverage percentage id: coverage shell: bash run: | - coverage=$(grep -o 'Overall coverage rate.*' | grep -o '[0-9]*\.[0-9]*') + grep -o 'Overall coverage rate.*' coverage/index.html | grep -o '[0-9]*\.[0-9]*' > coverage.txt + coverage=$(cat coverage.txt) echo "::set-output name=coverage::$coverage" - name: Update README.md with coverage badge From 9d0faec7c6ff93000fbd28bf30401350ba14511a Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 16:02:10 +0200 Subject: [PATCH 37/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index 3c6e62f..8effe38 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -45,12 +45,12 @@ jobs: chmod +x ./coverage.sh bash ./coverage.sh - - name: Extract coverage percentage + - name: Extract coverage percentage from index.js id: coverage shell: bash run: | - grep -o 'Overall coverage rate.*' coverage/index.html | grep -o '[0-9]*\.[0-9]*' > coverage.txt - coverage=$(cat coverage.txt) + coverage=$(jq -r '.files[0].covered' ./coverage/test_smartmon.coverage/index.js) + echo "Extracted coverage percentage: $coverage" echo "::set-output name=coverage::$coverage" - name: Update README.md with coverage badge From 82e14626e4cb45b7379e706048a41542a8ea703a Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 16:04:26 +0200 Subject: [PATCH 38/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index 8effe38..8d56619 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -45,11 +45,12 @@ jobs: chmod +x ./coverage.sh bash ./coverage.sh + # Info: sed is used as the js file is no strict json format - name: Extract coverage percentage from index.js id: coverage shell: bash run: | - coverage=$(jq -r '.files[0].covered' ./coverage/test_smartmon.coverage/index.js) + coverage=$(sed -n '/var data = /,/};/p' ./coverage/test_smartmon.coverage/index.js | sed '1s/.*= //' | sed '$s/};/}/' | jq -r '.files[0].covered') echo "Extracted coverage percentage: $coverage" echo "::set-output name=coverage::$coverage" From 1dd652deebfbd105f7cf67db98cb184637826b98 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 16:06:45 +0200 Subject: [PATCH 39/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index 8d56619..5fba51a 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -45,14 +45,13 @@ jobs: chmod +x ./coverage.sh bash ./coverage.sh - # Info: sed is used as the js file is no strict json format - name: Extract coverage percentage from index.js id: coverage shell: bash run: | - coverage=$(sed -n '/var data = /,/};/p' ./coverage/test_smartmon.coverage/index.js | sed '1s/.*= //' | sed '$s/};/}/' | jq -r '.files[0].covered') - echo "Extracted coverage percentage: $coverage" - echo "::set-output name=coverage::$coverage" + coverage=$(grep -oP '(?<=covered":")[^"]+' ./coverage/test_smartmon.coverage/index.js | head -n 1) + echo "Extracted coverage percentage: $coverage" + echo "::set-output name=coverage::$coverage" - name: Update README.md with coverage badge run: | From f51e3e12410fc78df65460ceaf915cb9807a7bd5 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 16:08:05 +0200 Subject: [PATCH 40/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index 5fba51a..23f8e65 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -50,8 +50,8 @@ jobs: shell: bash run: | coverage=$(grep -oP '(?<=covered":")[^"]+' ./coverage/test_smartmon.coverage/index.js | head -n 1) - echo "Extracted coverage percentage: $coverage" - echo "::set-output name=coverage::$coverage" + echo "Extracted coverage percentage: $coverage" + echo "::set-output name=coverage::$coverage" - name: Update README.md with coverage badge run: | From 7beae41639950667a37a8ac6f18c36e4801c2e44 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 16:10:07 +0200 Subject: [PATCH 41/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index 23f8e65..c516b0c 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -53,6 +53,10 @@ jobs: echo "Extracted coverage percentage: $coverage" echo "::set-output name=coverage::$coverage" + - name: Configure Git Safe Directory + run: | + git config --global --add safe.directory /__w/${{ github.repository }}/__w/${{ github.repository }} + - name: Update README.md with coverage badge run: | sed -i 's|\[!\[Coverage\](https://img.shields.io/badge/Coverage-[0-9]*%25-brightgreen)\]|[!\[Coverage\](https://img.shields.io/badge/Coverage-${{ steps.coverage.outputs.coverage }}%25-brightgreen)]|' README.md From 4c5d412bb5f28564b622d40e2e0fb83c09550828 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 18:51:40 +0200 Subject: [PATCH 42/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index c516b0c..3be75a1 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -53,9 +53,12 @@ jobs: echo "Extracted coverage percentage: $coverage" echo "::set-output name=coverage::$coverage" + - name: Print Current Working Directory + run: pwd + - name: Configure Git Safe Directory run: | - git config --global --add safe.directory /__w/${{ github.repository }}/__w/${{ github.repository }} + git config --global --add safe.directory "$(pwd)" - name: Update README.md with coverage badge run: | From 43cb18870ffe1ef67b640fd5c6893cf15439529d Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 19:04:18 +0200 Subject: [PATCH 43/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index 3be75a1..c7b2cde 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -65,6 +65,6 @@ jobs: sed -i 's|\[!\[Coverage\](https://img.shields.io/badge/Coverage-[0-9]*%25-brightgreen)\]|[!\[Coverage\](https://img.shields.io/badge/Coverage-${{ steps.coverage.outputs.coverage }}%25-brightgreen)]|' README.md git config --global user.name 'github-actions[bot]' git config --global user.email 'github-actions[bot]@users.noreply.github.com' - git add README.md + git add README.md # Ensure README.md is staged git commit -m "Update coverage badge" git push origin main From 0ef13ce13f2e772d77a783f1425cbdc4bfaa7514 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 19:12:49 +0200 Subject: [PATCH 44/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index c7b2cde..e437256 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -67,4 +67,4 @@ jobs: git config --global user.email 'github-actions[bot]@users.noreply.github.com' git add README.md # Ensure README.md is staged git commit -m "Update coverage badge" - git push origin main + git push origin ${GITHUB_REF#refs/heads/} From a0f3aba7aebcc1321844f4d120f808ee6f684e23 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 19:17:50 +0200 Subject: [PATCH 45/54] add workflow for coverage --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 185d3e9..51f6427 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![coverage](https://img.shields.io/badge/Coverage-50%25-brightgreen) +![Coverage](https://img.shields.io/badge/Coverage-50%25-brightgreen) # S.M.A.R.T.-disk-monitoring-for-Prometheus text_collector From fcb3846a9daf36631007551161a6858a61e81e52 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 19:57:44 +0200 Subject: [PATCH 46/54] add workflow for coverage --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0bc3c90..6f6b68a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.swp *.swo *.tmp +coverage/ From 4ce987e412c1f97556e88d50963bb3ca18171926 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 19:58:36 +0200 Subject: [PATCH 47/54] add workflow for coverage --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6f6b68a..8cb9a36 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.swo *.tmp coverage/ +grafana* From 78c951f8ca9572b8e4efdd5a72475ab487b6514e Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 20:34:54 +0200 Subject: [PATCH 48/54] add workflow for coverage --- .github/workflows/github-workflow.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github-workflow.yaml b/.github/workflows/github-workflow.yaml index e437256..5b3eaef 100644 --- a/.github/workflows/github-workflow.yaml +++ b/.github/workflows/github-workflow.yaml @@ -62,7 +62,7 @@ jobs: - name: Update README.md with coverage badge run: | - sed -i 's|\[!\[Coverage\](https://img.shields.io/badge/Coverage-[0-9]*%25-brightgreen)\]|[!\[Coverage\](https://img.shields.io/badge/Coverage-${{ steps.coverage.outputs.coverage }}%25-brightgreen)]|' README.md + sed -i 's|https:\/\/img\.shields\.io\/badge\/Coverage-[0-9]*\(\.[0-9]*\)\?%25-brightgreen|https:\/\/img\.shields\.io\/badge\/Coverage-65.2%25-brightgreen|g' README.md git config --global user.name 'github-actions[bot]' git config --global user.email 'github-actions[bot]@users.noreply.github.com' git add README.md # Ensure README.md is staged From f7e8ffa08ed027e2a75230a56be62ca54f96612a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 22 Aug 2024 18:35:21 +0000 Subject: [PATCH 49/54] Update coverage badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 51f6427..ba2d0ae 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![Coverage](https://img.shields.io/badge/Coverage-50%25-brightgreen) +![Coverage](https://img.shields.io/badge/Coverage-65.2%25-brightgreen) # S.M.A.R.T.-disk-monitoring-for-Prometheus text_collector From e8f216d3bc9f60344d0111bcb76c010326887586 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 21:58:04 +0200 Subject: [PATCH 50/54] add workflow for coverage --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 51f6427..aea6861 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,19 @@ +![Tag](https://img.shields.io/github/v/tag/micha37-martins/S.M.A.R.T-disk-monitoring-for-Prometheus) + ![Coverage](https://img.shields.io/badge/Coverage-50%25-brightgreen) +![Language](https://img.shields.io/github/languages/top/micha37-martins/S.M.A.R.T-disk-monitoring-for-Prometheus) + +![Downloads](https://img.shields.io/github/downloads/micha37-martins/S.M.A.R.T-disk-monitoring-for-Prometheus/total) + +![License](https://img.shields.io/github/license/micha37-martins/S.M.A.R.T-disk-monitoring-for-Prometheus) + +![Forks](https://img.shields.io/github/forks/micha37-martins/S.M.A.R.T-disk-monitoring-for-Prometheus) + +![Stars](https://img.shields.io/github/stars/micha37-martins/S.M.A.R.T-disk-monitoring-for-Prometheus) + +![Last Commit](https://img.shields.io/github/last-commit/micha37-martins/S.M.A.R.T-disk-monitoring-for-Prometheus) + # S.M.A.R.T.-disk-monitoring-for-Prometheus text_collector Prometheus `node_exporter` `text_collector` for S.M.A.R.T disk values From 966179ebc9b6abab4551e4bab2726f553d9ffdd5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 22 Aug 2024 20:01:18 +0000 Subject: [PATCH 51/54] Update coverage badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aea6861..5d74bc8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![Tag](https://img.shields.io/github/v/tag/micha37-martins/S.M.A.R.T-disk-monitoring-for-Prometheus) -![Coverage](https://img.shields.io/badge/Coverage-50%25-brightgreen) +![Coverage](https://img.shields.io/badge/Coverage-65.2%25-brightgreen) ![Language](https://img.shields.io/github/languages/top/micha37-martins/S.M.A.R.T-disk-monitoring-for-Prometheus) From 8a0867c62a55dde41a0c54f73fa23fc636701539 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Thu, 22 Aug 2024 22:03:34 +0200 Subject: [PATCH 52/54] add workflow for coverage --- README.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/README.md b/README.md index aea6861..5f40b12 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,7 @@ ![Tag](https://img.shields.io/github/v/tag/micha37-martins/S.M.A.R.T-disk-monitoring-for-Prometheus) - ![Coverage](https://img.shields.io/badge/Coverage-50%25-brightgreen) - ![Language](https://img.shields.io/github/languages/top/micha37-martins/S.M.A.R.T-disk-monitoring-for-Prometheus) - -![Downloads](https://img.shields.io/github/downloads/micha37-martins/S.M.A.R.T-disk-monitoring-for-Prometheus/total) - ![License](https://img.shields.io/github/license/micha37-martins/S.M.A.R.T-disk-monitoring-for-Prometheus) - -![Forks](https://img.shields.io/github/forks/micha37-martins/S.M.A.R.T-disk-monitoring-for-Prometheus) - -![Stars](https://img.shields.io/github/stars/micha37-martins/S.M.A.R.T-disk-monitoring-for-Prometheus) - ![Last Commit](https://img.shields.io/github/last-commit/micha37-martins/S.M.A.R.T-disk-monitoring-for-Prometheus) # S.M.A.R.T.-disk-monitoring-for-Prometheus text_collector From 2aa72076f3ffac9eeaa403f4496168a893da49d0 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Fri, 30 Aug 2024 21:01:02 +0200 Subject: [PATCH 53/54] Add install script --- README.md | 151 +++++++++++++++++++++++++++++++++--------------- install.sh | 56 ++++++++++++++++++ src/smartmon.sh | 39 +------------ uninstall.sh | 31 ++++++++++ 4 files changed, 194 insertions(+), 83 deletions(-) create mode 100755 install.sh create mode 100755 uninstall.sh diff --git a/README.md b/README.md index 4469c50..a50a7e9 100644 --- a/README.md +++ b/README.md @@ -4,57 +4,100 @@ ![License](https://img.shields.io/github/license/micha37-martins/S.M.A.R.T-disk-monitoring-for-Prometheus) ![Last Commit](https://img.shields.io/github/last-commit/micha37-martins/S.M.A.R.T-disk-monitoring-for-Prometheus) -# S.M.A.R.T.-disk-monitoring-for-Prometheus text_collector +# S.M.A.R.T. disk monitoring for Prometheus +_SMART Exporter for Prometheus node-exporter_ + +This script is a specialized tool designed to collect SMART data from various +types of disks (ATA, NVMe) and format it for Prometheus monitoring. To collect +SMART values it uses `smartctl`. + +_Inspired by the great examples of the Prometheus community:_ +[textfile-collector-scripts](https://github.com/prometheus-community/node-exporter-textfile-collector-scripts) +___ +It has been specifically developed to work seamlessly with the +[SMART Disk Monitoring for Prometheus Dashboard](https://grafana.com/grafana/dashboards/10530-s-m-a-r-t-disk-monitoring-for-prometheus-dashboard/) on Grafana. + +> +>[https://grafana.com/dashboards/10530](https://grafana.com/dashboards/10530) +>![screenshot1](media/grafana_dashboard_1.png) + +If you're interested in alternative solutions, you might want to check out the [smartctl_exporter](https://github.com/prometheus-community/smartctl_exporter) project from the Prometheus community. + +___ +>**Warning:** This script has been rewritten from version 0.1.0 and has breaking changes. Please be aware of the following: +> +>- A lot of renaming has been done +>- The script now uses the JSON output of `smartctl` +>- SCSI support has been dropped + +## Prerequisites +Mandatory: +- Bash +- `jq` (https://jqlang.github.io/jq/download/) +- Root privileges (optional, but required to access SMART data for all disks) + +Optional: +- node_exporter + - text_collector enabled for node_exporter +- Grafana >= 10 +- smartmontools >= 7 -Prometheus `node_exporter` `text_collector` for S.M.A.R.T disk values +## Usage +1. Clone the repository: -Following dashboards are designed for this exporter: +git clone https://github.com/your-username/smart-disk-exporter.git -https://grafana.com/dashboards/10530 +2. Make the script executable: -https://grafana.com/dashboards/10531 +chmod +x smartmon.sh -## Purpose -This text_collector is a customized version of the S.M.A.R.T. `text_collector` -example from `node_exporter` github repo: -https://github.com/prometheus/node_exporter/tree/master/text_collector_examples +3. Run the script (use `sudo`): -This bash script uses `smartctl` to get S.M.A.R.T. values. It is designed to -work with SATA and NVME disks. It should also work with SCSI disks but is not -tested. +./smartmon.sh -## Requirements -- Prometheus -- node_exporter - - text_collector enabled for node_exporter -- Grafana >= 10 -- smartmontools >= 7 -- jq +The script will output the SMART data for all detected disks in Prometheus format. + +## (WIP)Install / Uninstall +For convenience this repo contains helper scripts for installing and uninstalling. +Make them executable and run the desired script: + +>Note that the script should be executed with root privileges as the path are +>usually not accessible for normal users. + +Like this: +```sh +chmod +x install.sh +``` +```sh +sudo ./install.sh +``` -## Set up -To enable text_collector set the following flag for `node_exporter`: -- `--collector.textfile.directory` -run command with `/var/lib/node_exporter/textfile_collector` +## Set up Prometheus Node Exporter +To enable `text_collector` set the following flag to `node_exporter`: +- `--collector.textfile.directory /var/lib/node_exporter/textfile_collector` + +example: +```sh +/usr/bin/prometheus-node-exporter --collector.textfile.directory /var/lib/node_exporter/textfile_collector/ +``` Install [smartmontools](https://www.smartmontools.org/) -For UBUNTU: `sudo apt-get install smartmontools` +UBUNTU: `sudo apt-get install smartmontools` -To enable the text_collector on your system add the following as cronjob. -It will execute the script every five minutes and save the result to the `text_collector` directory. +To enable the text_collector on your system add the following as cronjob or create +a Systemd timer unit like shown in the install.sh script. -Example for UBUNTU `sudo crontab -e`: +The Cronjob will execute the script every five minutes and save the result to +the `text_collector` directory. -`*/5 * * * * /usr/local/bin/smartmon.sh > /var/lib/node_exporter/textfile_collector/smart_metrics.prom` +Example for UBUNTU: -# TODO: adapt to new script -## How to add specific S.M.A.R.T. attributes -If you are missing some attributes you can extend the text_collector. -Add the desired attributes to `smartmon_attrs` array in `smartmon.sh`. +```sh +sudo crontab -e -You get a list of your disks privided attributes by executing: -`sudo smartctl -i -H /dev/` -`sudo smartctl -A /dev/` +*/5 * * * * /usr/local/bin/smartmon.sh > /var/lib/node_exporter/textfile_collector/smart_metrics.prom +``` ## Running Locally If you want to test the exporter locally. For example on a laptop you can move @@ -81,22 +124,40 @@ https://www.smartmontools.org/wiki/Download#Installfromthesourcetarball - `mkdir -p /var/lib/node_exporter/textfile_collector/` -## Tests -# TODO: tests install bats (bats-core): -[bats-tutorial](https://bats-core.readthedocs.io/en/stable/tutorial.html) +## Development and Testing +### Adding New Metrics +Feel free to adapt this script to your needs. The metrics provided are a subset +of all available, so feel free to add more. If you'd like to add a new metric, +here's a general guide: -## Coverage -´´´sh -run_coverage.sh +1. Identify the new metric you want to add. This can be done by checking the +output of `smartctl -A -j /dev/diskX`, where `X` is the device's name. Look for +the relevant attribute in the JSON output. +2. Modify the appropriate parsing function (`parse_smartctl_attributes_json` for +ATA devices, `parse_smartctl_nvme_attributes_json` for NVMe devices) to add a new `if` statement in the `while` loop to check for the new metric's attribute name. +3. Calculate the new metric's value based on the attribute's value and any relevant conversion factors. +4. Print the new metric in Prometheus format using `printf`. + +### Test using bats (bats-core) +How to install and use is best described here: [bats-tutorial](https://bats-core.readthedocs.io/en/stable/tutorial.html) + +Run tests with: +```sh +bats test ``` -### Manual Coverage +### Generate Coverage +```sh +./run_coverage.sh +``` +OR +```sh kcov --bash-dont-parse-binary-dir \ --include-path=. \ /var/tmp/coverage \ bats -t test/test_smartmon.bats +``` ## TODO -- Write docs -- docker -- systemd instead of cron +- create container +- Test install.sh script diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..a2d466f --- /dev/null +++ b/install.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash + +# Set the installation path with a default value +SMARTMON_PATH="${SMARTMON_PATH:-/usr/local/bin}" + +# Download the smartmon.sh script from the repository or a release asset +wget -O "${SMARTMON_PATH}/smartmon.sh" https://raw.githubusercontent.com/micha37-martins/smart-disk-exporter/main/smartmon.sh + +# Set the appropriate permissions on the smartmon.sh script +chmod +x "${SMARTMON_PATH}/smartmon.sh" + +# Create the systemd service unit file +cat < /etc/systemd/system/smartmon.service +[Unit] +Description=SMART Disk Exporter for Prometheus +After=network.target + +[Service] +Type=simple +ExecStart=/usr/bin/env bash ${SMARTMON_PATH}/smartmon.sh > /var/lib/node_exporter/textfile_collector/smart_metrics.prom +WorkingDirectory=/var/lib/node_exporter/textfile_collector/ + +[Install] +WantedBy=multi-user.target +EOF + +# Reload the systemd daemon to load the new service unit file +systemctl daemon-reload + +# Enable the service to start automatically at boot +systemctl enable smartmon.service + +# Start the service immediately +systemctl start smartmon.service + +# Create the systemd timer unit file +cat < /etc/systemd/system/smartmon.timer +[Unit] +Description=Run SMART Disk Exporter for Prometheus every 10 minutes + +[Timer] +OnBootSec=1min +OnUnitActiveSec=10min + +[Install] +WantedBy=multi-user.target +EOF + +# Reload the systemd daemon to load the new timer unit file +systemctl daemon-reload + +# Enable the timer to start automatically at boot +systemctl enable smartmon.timer + +# Start the timer immediately +systemctl start smartmon.timer diff --git a/src/smartmon.sh b/src/smartmon.sh index a852400..b6539cf 100755 --- a/src/smartmon.sh +++ b/src/smartmon.sh @@ -1,19 +1,7 @@ #!/usr/bin/env bash -# TODO: remove json postfix -# TODO: warning breaking changes -# TODO: test tolowercase -# - Renaming -# - JSON -# - Drop SCSI - # This script is designed to collect SMART data from various types of -# disks (ATA, NVMe, SCSI) and format it for Prometheus monitoring. -# Be aware that SCSI type is not tested! -# -# By design this script works with a filter list of values. Opposed to parsing -# all elements from the JSON output of smartctl this seems more robust as -# different vendors might use inconsistent value naming. +# disks (ATA, NVMe) and format it for Prometheus monitoring. # Check if this script is run as root # Do not exit if test is run by bats @@ -169,31 +157,6 @@ parse_smartctl_attributes_json() { # $1 - Disk name # $2 - Disk type # $3 - JSON string containing NVMe SMART attributes -#parse_smartctl_nvme_attributes_json() { -# local disk="$1" -# local disk_type="$2" -# local json="$3" -# local labels="disk=\"${disk}\",type=\"${disk_type}\"" -# -# # return early if no input -# if [ "$json" == {} ]; then -# return 0 -# fi -# -# # Extract and format NVMe SMART attributes using jq -# echo "$json" | jq -r ' -# .nvme_smart_health_information_log | -# to_entries[] | -# select(.key and (.value | type != "array")) | -# [ -# (.key | gsub("-"; "_")), -# .value -# ] | @tsv -# ' | while IFS=$'\t' read -r key value; do -# local metric_name="${key}" -# echo "$(echo "${metric_name}" | awk '{print tolower($0)}'){${labels}} ${value}" -# done -#} parse_smartctl_nvme_attributes_json() { local disk="$1" local disk_type="$2" diff --git a/uninstall.sh b/uninstall.sh new file mode 100755 index 0000000..eaf7eff --- /dev/null +++ b/uninstall.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +# Set the installation path with a default value +SMARTMON_PATH="${SMARTMON_PATH:-/usr/local/bin}" + +# Stop the timer +systemctl stop smartmon.timer + +# Disable the timer +systemctl disable smartmon.timer + +# Remove the timer unit file +rm /etc/systemd/system/smartmon.timer + +# Stop the service +systemctl stop smartmon.service + +# Disable the service +systemctl disable smartmon.service + +# Remove the service unit file +rm /etc/systemd/system/smartmon.service + +# Reload the systemd daemon +systemctl daemon-reload + +# Remove the smartmon.sh script +rm "${SMARTMON_PATH}/smartmon.sh" + +# Print a message indicating that the uninstallation is complete +echo "Uninstallation complete." From 402ca1e0074d8f847567ab5b967a75edaeeec637 Mon Sep 17 00:00:00 2001 From: micha37-martins Date: Sat, 31 Aug 2024 20:12:56 +0200 Subject: [PATCH 54/54] Add Grafana dashboard --- .gitignore | 1 - grafana/smart-dashboard.json | 3789 +++++++++++++++++++++++++++++++++ media/grafana_dashboard_1.png | Bin 0 -> 277609 bytes 3 files changed, 3789 insertions(+), 1 deletion(-) create mode 100644 grafana/smart-dashboard.json create mode 100644 media/grafana_dashboard_1.png diff --git a/.gitignore b/.gitignore index 8cb9a36..6f6b68a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,3 @@ *.swo *.tmp coverage/ -grafana* diff --git a/grafana/smart-dashboard.json b/grafana/smart-dashboard.json new file mode 100644 index 0000000..ce73349 --- /dev/null +++ b/grafana/smart-dashboard.json @@ -0,0 +1,3789 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Smartmon Texfile node_exporter Dashboard", + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": 10530, + "graphTooltip": 0, + "id": 60, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 50, + "panels": [], + "title": "Disk Stats", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "index": 0, + "text": "0" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "#299c46", + "value": null + }, + { + "color": "#E02F44", + "value": 1 + }, + { + "color": "#d44a3a" + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 0, + "y": 1 + }, + "id": 4, + "interval": "", + "maxDataPoints": 100, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "/^Value$/", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "expr": "sum(smartmon_device_smart_healthy{instance=~\"$instance\"})-sum(smartmon_device_smart_healthy{instance=~\"$instance\"})", + "format": "table", + "interval": "", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A" + } + ], + "title": "Unhealthy Disks", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "index": 0, + "text": "0" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "#299c46", + "value": null + }, + { + "color": "rgba(237, 129, 40, 0.89)", + "value": 1 + }, + { + "color": "#d44a3a", + "value": 10 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 3, + "y": 1 + }, + "id": 26, + "links": [ + { + "title": "Errors", + "url": "/d/HIwbBGIZk/s-m-a-r-t-disk-monitoring-for-prometheus-errors?$__url_time_range&$__all_variables" + } + ], + "maxDataPoints": 100, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "sum( smartmon_reallocate_nand_blk_cnt_raw_value ) +\nsum( smartmon_reallocated_sector_ct_raw_value ) +\nsum( smartmon_reallocated_event_count_raw_value ) +\nsum( smartmon_raw_read_error_rate_raw_value ) +\nsum( smartmon_current_pending_sector_raw_value ) +\nsum( smartmon_offline_uncorrectable_raw_value ) +\nsum( smartmon_udma_crc_error_count_raw_value ) +\nsum( smartmon_seek_error_rate_raw_value ) +\nsum( smartmon_spin_retry_count_raw_value ) +\nsum( smartmon_multi_zone_error_rate_raw_value )", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "__auto", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "Errors All Devices", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Devices that are active", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 6, + "y": 1 + }, + "id": 10, + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "expr": "sum(smartmon_device_active)", + "format": "time_series", + "intervalFactor": 1, + "refId": "A" + } + ], + "title": "Active", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 1, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "celsius" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 15, + "x": 9, + "y": 1 + }, + "id": 1, + "interval": "", + "options": { + "legend": { + "calcs": [ + "mean", + "lastNotNull", + "max", + "min" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "10.4.1", + "repeatDirection": "h", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "expr": "avg(smartmon_temperature{ instance=~\"$instance\", disk=~\"$disk\" }) by (instance, disk) OR avg(smartmon_temperature_celsius_raw_value{ instance=~\"$instance\", disk=~\"$disk\" }) by (instance, disk)", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 4, + "legendFormat": "{{instance}}{{disk}}", + "range": true, + "refId": "A" + } + ], + "title": "Temperature_Graph", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 1, + "mappings": [], + "max": "80", + "min": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 35 + }, + { + "color": "red", + "value": 50 + } + ] + }, + "unit": "celsius" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 9, + "x": 0, + "y": 4 + }, + "id": 14, + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "sizing": "auto" + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": false, + "expr": "smartmon_temperature{ instance=~\"$instance\", disk=~\"$disk\" } OR smartmon_temperature_celsius_raw_value{ instance=~\"$instance\", disk=~\"$disk\" }", + "format": "time_series", + "instant": true, + "intervalFactor": 1, + "legendFormat": "{{ disk }}", + "range": false, + "refId": "A" + } + ], + "title": "Temperture_Gauge", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "gridPos": { + "h": 2, + "w": 24, + "x": 0, + "y": 10 + }, + "id": 49, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "

\n

__________ Wear and Tear __________

", + "mode": "html" + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "refId": "A" + } + ], + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "yellow after 2 years\nred after 4 years", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "max": 50000, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 17520 + }, + { + "color": "red", + "value": 35040 + } + ] + }, + "unit": "h" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 12 + }, + "id": 3, + "interval": "", + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "sizing": "auto" + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "expr": "smartmon_power_on_hours{ disk=~\"$disk\", instance=~\"$instance\" } OR smartmon_power_on_hours_raw_value{ disk=~\"$disk\", instance=~\"$instance\" }", + "format": "time_series", + "instant": false, + "intervalFactor": 2, + "legendFormat": " {{disk}}", + "refId": "A" + } + ], + "title": "Power_On_Hours", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Number of times disk was turned on", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 10000, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "yellow", + "value": 2000 + }, + { + "color": "red", + "value": 5000 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 12 + }, + "id": 32, + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "sizing": "auto" + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "expr": "smartmon_power_cycles{ instance=~\"$instance\", disk=~\"$disk\"} OR smartmon_power_cycle_count_raw_value { instance=~\"$instance\", disk=~\"$disk\"}", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{disk}}", + "range": true, + "refId": "A" + } + ], + "title": "Power_Cycle_Count", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "gridPos": { + "h": 2, + "w": 24, + "x": 0, + "y": 17 + }, + "id": 48, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "

\n

__________ SSD Specific Values __________

", + "mode": "html" + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "refId": "A" + } + ], + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Estimated remaining lifetime.\nBased on amount of written data.\n(Very vague one flash chip can be witten 10 000 times)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "index": 0, + "value": null + }, + { + "color": "#EAB839", + "index": 1, + "value": 33 + }, + { + "color": "green", + "index": 2, + "value": 67 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 19 + }, + "id": 44, + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "sizing": "auto" + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "expr": "smartmon_percent_lifetime_remain_raw_value{ instance=~\"$instance\", disk=~\"$disk\"}", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{disk}}", + "range": true, + "refId": "A" + } + ], + "title": "Remaining_Lifetime", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "smartmon_written_bytes" + }, + "properties": [ + { + "id": "unit", + "value": "decbytes" + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#6ED0E0", + "value": 50000000000000 + }, + { + "color": "#EAB839", + "value": 100000000000000 + }, + { + "color": "#EF843C", + "value": 500000000000000 + }, + { + "color": "red", + "value": 750000000000000 + } + ] + } + }, + { + "id": "max", + "value": 1000000000000000 + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 19 + }, + "id": 47, + "maxDataPoints": 100, + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "sizing": "auto" + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": false, + "expr": "smartmon_written_bytes{ instance=~\"$instance\", disk=~\"$disk\"}", + "format": "time_series", + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{disk}}", + "range": true, + "refId": "A" + } + ], + "title": "Written Data (\"TBW\")", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "gridPos": { + "h": 2, + "w": 24, + "x": 0, + "y": 24 + }, + "id": 43, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "

\n

__________ HDD Specific Values __________

", + "mode": "html" + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "refId": "A" + } + ], + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Average time to spin up disk. (in ms)\nHigh values indicate problems with the motor.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 24, + "x": 0, + "y": 26 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": false + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "expr": "smartmon_spin_up_time_raw_value", + "format": "time_series", + "instant": false, + "intervalFactor": 1, + "legendFormat": "{{disk}}", + "refId": "A" + } + ], + "title": "Spin_Up_Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Number of unexpected power outages (no correct shutdown) \nThis kind of shutdown lowers the lifetime of a disk way more than a clean shutdown", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "index": 0, + "value": null + }, + { + "color": "#EAB839", + "index": 1, + "value": 10 + }, + { + "color": "red", + "index": 2, + "value": 40 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 8, + "x": 0, + "y": 30 + }, + "id": 34, + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "sizing": "auto" + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "expr": "smartmon_power_off_retract_count_raw_value{ instance=~\"$instance\", disk=~\"$disk\"}", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{disk}}", + "refId": "A" + } + ], + "title": "Power_Off_Retract_Count", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Counts start/stop events.\nHigh values indicate high wear.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 0, + "mappings": [], + "max": 2000, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "index": 0, + "value": null + }, + { + "color": "#EAB839", + "index": 1, + "value": 750 + }, + { + "color": "red", + "index": 2, + "value": 1500 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 8, + "x": 8, + "y": 30 + }, + "id": 24, + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "sizing": "auto" + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "expr": "smartmon_start_stop_count_raw_value { instance=~\"$instance\", disk=~\"$disk\" }", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{disk}}", + "refId": "A" + } + ], + "title": "Start_Stop_Count", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Number times the head was placed in the parking area.\nMostly mobile disks.\n300000 times is intended.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 300000, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "index": 0, + "value": null + }, + { + "color": "#EAB839", + "index": 1, + "value": 50000 + }, + { + "color": "red", + "index": 2, + "value": 150000 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 8, + "x": 16, + "y": 30 + }, + "id": 36, + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "sizing": "auto" + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "expr": "smartmon_load_cycle_count_raw_value{ instance=~\"$instance\", disk=~\"$disk\"}", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{disk}}", + "refId": "A" + } + ], + "title": "Load_Cycle_Count", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "filterable": true, + "inspect": false + }, + "decimals": 2, + "displayName": "", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 34 + }, + "id": 12, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "expr": "smartmon_device_info", + "format": "table", + "instant": true, + "interval": "", + "intervalFactor": 1, + "refId": "A" + } + ], + "title": "Device_Info", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "disk", + "firmware_version", + "instance", + "model_family", + "model_name", + "serial_number", + "type" + ] + } + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "includeByName": {}, + "indexByName": { + "Value": 7, + "disk": 1, + "firmware_version": 5, + "instance": 0, + "model_family": 4, + "model_name": 3, + "serial_number": 6, + "type": 2 + }, + "renameByName": {} + } + }, + { + "id": "sortBy", + "options": { + "fields": {}, + "sort": [ + { + "field": "instance" + } + ] + } + } + ], + "type": "table" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 41 + }, + "id": 64, + "panels": [], + "title": "General Disk Errors", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "auto" + }, + "filterable": false, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "smart_id" + }, + "properties": [ + { + "id": "custom.width", + "value": 50 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "erorr_type" + }, + "properties": [ + { + "id": "custom.width", + "value": 405 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "instance" + }, + "properties": [ + { + "id": "custom.width", + "value": 200 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "disk" + }, + "properties": [ + { + "id": "custom.width", + "value": 159 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Value" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "gradient", + "type": "color-background" + } + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + }, + { + "color": "red", + "value": 10 + } + ] + } + }, + { + "id": "custom.width", + "value": 60 + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 42 + }, + "id": 57, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "frameIndex": 0, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": false, + "expr": "smartmon_current_pending_sector_raw_value", + "format": "table", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "current_pending_sector", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "instance": false, + "job": true, + "type": true + }, + "includeByName": {}, + "indexByName": { + "Time": 0, + "Value": 7, + "__name__": 3, + "disk": 2, + "instance": 1, + "job": 4, + "smart_id": 5, + "type": 6 + }, + "renameByName": { + "__name__": "erorr_type" + } + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Value": { + "aggregations": [], + "operation": "groupby" + }, + "disk": { + "aggregations": [], + "operation": "groupby" + }, + "erorr_type": { + "aggregations": [], + "operation": "groupby" + }, + "instance": { + "aggregations": [], + "operation": "groupby" + }, + "smart_id": { + "aggregations": [], + "operation": "groupby" + } + } + } + }, + { + "id": "sortBy", + "options": { + "fields": {}, + "sort": [ + { + "desc": true, + "field": "Value" + } + ] + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "auto" + }, + "filterable": false, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "smart_id" + }, + "properties": [ + { + "id": "custom.width", + "value": 50 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "erorr_type" + }, + "properties": [ + { + "id": "custom.width", + "value": 405 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "instance" + }, + "properties": [ + { + "id": "custom.width", + "value": 200 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "disk" + }, + "properties": [ + { + "id": "custom.width", + "value": 159 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Value" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "gradient", + "type": "color-background" + } + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + }, + { + "color": "red", + "value": 10 + } + ] + } + }, + { + "id": "custom.width", + "value": 60 + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 42 + }, + "id": 58, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "frameIndex": 0, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": false, + "expr": "smartmon_offline_uncorrectable_raw_value", + "format": "table", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "offline_uncorrectable", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "instance": false, + "job": true, + "type": true + }, + "includeByName": {}, + "indexByName": { + "Time": 0, + "Value": 7, + "__name__": 3, + "disk": 2, + "instance": 1, + "job": 4, + "smart_id": 5, + "type": 6 + }, + "renameByName": { + "__name__": "erorr_type" + } + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Value": { + "aggregations": [], + "operation": "groupby" + }, + "disk": { + "aggregations": [], + "operation": "groupby" + }, + "erorr_type": { + "aggregations": [], + "operation": "groupby" + }, + "instance": { + "aggregations": [], + "operation": "groupby" + }, + "smart_id": { + "aggregations": [], + "operation": "groupby" + } + } + } + }, + { + "id": "sortBy", + "options": { + "fields": {}, + "sort": [ + { + "desc": true, + "field": "Value" + } + ] + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "auto" + }, + "filterable": false, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "smart_id" + }, + "properties": [ + { + "id": "custom.width", + "value": 50 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "erorr_type" + }, + "properties": [ + { + "id": "custom.width", + "value": 405 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "instance" + }, + "properties": [ + { + "id": "custom.width", + "value": 200 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "disk" + }, + "properties": [ + { + "id": "custom.width", + "value": 159 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Value" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "gradient", + "type": "color-background" + } + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + }, + { + "color": "red", + "value": 10 + } + ] + } + }, + { + "id": "custom.width", + "value": 60 + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 47 + }, + "id": 55, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "frameIndex": 0, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": false, + "expr": "smartmon_reallocated_event_count_raw_value", + "format": "table", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "reallocated_event_count", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "instance": false, + "job": true, + "type": true + }, + "includeByName": {}, + "indexByName": { + "Time": 0, + "Value": 7, + "__name__": 3, + "disk": 2, + "instance": 1, + "job": 4, + "smart_id": 5, + "type": 6 + }, + "renameByName": { + "__name__": "erorr_type" + } + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Value": { + "aggregations": [], + "operation": "groupby" + }, + "disk": { + "aggregations": [], + "operation": "groupby" + }, + "erorr_type": { + "aggregations": [], + "operation": "groupby" + }, + "instance": { + "aggregations": [], + "operation": "groupby" + }, + "smart_id": { + "aggregations": [], + "operation": "groupby" + } + } + } + }, + { + "id": "sortBy", + "options": { + "fields": {}, + "sort": [ + { + "desc": true, + "field": "Value" + } + ] + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "auto" + }, + "filterable": false, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "smart_id" + }, + "properties": [ + { + "id": "custom.width", + "value": 50 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "erorr_type" + }, + "properties": [ + { + "id": "custom.width", + "value": 405 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "instance" + }, + "properties": [ + { + "id": "custom.width", + "value": 200 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "disk" + }, + "properties": [ + { + "id": "custom.width", + "value": 159 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Value" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "gradient", + "type": "color-background" + } + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + }, + { + "color": "red", + "value": 10 + } + ] + } + }, + { + "id": "custom.width", + "value": 60 + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 47 + }, + "id": 56, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "frameIndex": 0, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Value" + } + ] + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": false, + "expr": "smartmon_reallocated_sector_ct_raw_value", + "format": "table", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "reallocated_sector_count", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "instance": false, + "job": true, + "type": true + }, + "includeByName": {}, + "indexByName": { + "Time": 0, + "Value": 7, + "__name__": 3, + "disk": 2, + "instance": 1, + "job": 4, + "smart_id": 5, + "type": 6 + }, + "renameByName": { + "__name__": "erorr_type" + } + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Value": { + "aggregations": [], + "operation": "groupby" + }, + "disk": { + "aggregations": [], + "operation": "groupby" + }, + "erorr_type": { + "aggregations": [], + "operation": "groupby" + }, + "instance": { + "aggregations": [], + "operation": "groupby" + }, + "smart_id": { + "aggregations": [], + "operation": "groupby" + } + } + } + }, + { + "id": "sortBy", + "options": { + "fields": {}, + "sort": [ + { + "desc": true, + "field": "Value" + } + ] + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "auto" + }, + "filterable": false, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "smart_id" + }, + "properties": [ + { + "id": "custom.width", + "value": 50 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "erorr_type" + }, + "properties": [ + { + "id": "custom.width", + "value": 405 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "instance" + }, + "properties": [ + { + "id": "custom.width", + "value": 200 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "disk" + }, + "properties": [ + { + "id": "custom.width", + "value": 159 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Value" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "gradient", + "type": "color-background" + } + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + }, + { + "color": "red", + "value": 10 + } + ] + } + }, + { + "id": "custom.width", + "value": 60 + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 52 + }, + "id": 53, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "frameIndex": 0, + "showHeader": true, + "sortBy": [ + { + "desc": false, + "displayName": "Value" + } + ] + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": false, + "expr": "smartmon_reallocate_nand_blk_cnt_raw_value", + "format": "table", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "reallocate_nand_blk_count", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "instance": false, + "job": true, + "type": true + }, + "includeByName": {}, + "indexByName": { + "Time": 0, + "Value": 7, + "__name__": 3, + "disk": 2, + "instance": 1, + "job": 4, + "smart_id": 5, + "type": 6 + }, + "renameByName": { + "__name__": "erorr_type" + } + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Value": { + "aggregations": [], + "operation": "groupby" + }, + "disk": { + "aggregations": [], + "operation": "groupby" + }, + "erorr_type": { + "aggregations": [], + "operation": "groupby" + }, + "instance": { + "aggregations": [], + "operation": "groupby" + }, + "smart_id": { + "aggregations": [], + "operation": "groupby" + } + } + } + }, + { + "id": "sortBy", + "options": { + "fields": {}, + "sort": [ + { + "desc": true, + "field": "Value" + } + ] + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "auto" + }, + "filterable": false, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "smart_id" + }, + "properties": [ + { + "id": "custom.width", + "value": 50 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "erorr_type" + }, + "properties": [ + { + "id": "custom.width", + "value": 405 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "instance" + }, + "properties": [ + { + "id": "custom.width", + "value": 200 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "disk" + }, + "properties": [ + { + "id": "custom.width", + "value": 159 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Value" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "gradient", + "type": "color-background" + } + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + }, + { + "color": "red", + "value": 10 + } + ] + } + }, + { + "id": "custom.width", + "value": 60 + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 52 + }, + "id": 54, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "frameIndex": 0, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": false, + "expr": "smartmon_raw_read_error_rate_raw_value", + "format": "table", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "raw_read_error_rate", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "instance": false, + "job": true, + "type": true + }, + "includeByName": {}, + "indexByName": { + "Time": 0, + "Value": 7, + "__name__": 3, + "disk": 2, + "instance": 1, + "job": 4, + "smart_id": 5, + "type": 6 + }, + "renameByName": { + "__name__": "erorr_type" + } + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Value": { + "aggregations": [], + "operation": "groupby" + }, + "disk": { + "aggregations": [], + "operation": "groupby" + }, + "erorr_type": { + "aggregations": [], + "operation": "groupby" + }, + "instance": { + "aggregations": [], + "operation": "groupby" + }, + "smart_id": { + "aggregations": [], + "operation": "groupby" + } + } + } + }, + { + "id": "sortBy", + "options": { + "fields": {}, + "sort": [ + { + "desc": true, + "field": "Value" + } + ] + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "auto" + }, + "filterable": false, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "smart_id" + }, + "properties": [ + { + "id": "custom.width", + "value": 50 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "erorr_type" + }, + "properties": [ + { + "id": "custom.width", + "value": 405 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "instance" + }, + "properties": [ + { + "id": "custom.width", + "value": 200 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "disk" + }, + "properties": [ + { + "id": "custom.width", + "value": 159 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Value" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "gradient", + "type": "color-background" + } + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + }, + { + "color": "red", + "value": 10 + } + ] + } + }, + { + "id": "custom.width", + "value": 60 + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 57 + }, + "id": 59, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "frameIndex": 0, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": false, + "expr": "smartmon_udma_crc_error_count_raw_value", + "format": "table", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "udma_crc_error_count", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "instance": false, + "job": true, + "type": true + }, + "includeByName": {}, + "indexByName": { + "Time": 0, + "Value": 7, + "__name__": 3, + "disk": 2, + "instance": 1, + "job": 4, + "smart_id": 5, + "type": 6 + }, + "renameByName": { + "__name__": "erorr_type" + } + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Value": { + "aggregations": [], + "operation": "groupby" + }, + "disk": { + "aggregations": [], + "operation": "groupby" + }, + "erorr_type": { + "aggregations": [], + "operation": "groupby" + }, + "instance": { + "aggregations": [], + "operation": "groupby" + }, + "smart_id": { + "aggregations": [], + "operation": "groupby" + } + } + } + }, + { + "id": "sortBy", + "options": { + "fields": {}, + "sort": [ + { + "desc": true, + "field": "Value" + } + ] + } + } + ], + "type": "table" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 62 + }, + "id": 63, + "panels": [], + "title": "HDD Sepecific Disk Errors", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "auto" + }, + "filterable": false, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "smart_id" + }, + "properties": [ + { + "id": "custom.width", + "value": 50 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "erorr_type" + }, + "properties": [ + { + "id": "custom.width", + "value": 405 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "instance" + }, + "properties": [ + { + "id": "custom.width", + "value": 200 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "disk" + }, + "properties": [ + { + "id": "custom.width", + "value": 159 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Value" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "gradient", + "type": "color-background" + } + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + }, + { + "color": "red", + "value": 10 + } + ] + } + }, + { + "id": "custom.width", + "value": 60 + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 63 + }, + "id": 62, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "frameIndex": 0, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": false, + "expr": "smartmon_multi_zone_error_rate_raw_value", + "format": "table", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "multi_zone_error_rate", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "instance": false, + "job": true, + "type": true + }, + "includeByName": {}, + "indexByName": { + "Time": 0, + "Value": 7, + "__name__": 3, + "disk": 2, + "instance": 1, + "job": 4, + "smart_id": 5, + "type": 6 + }, + "renameByName": { + "__name__": "erorr_type" + } + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Value": { + "aggregations": [], + "operation": "groupby" + }, + "disk": { + "aggregations": [], + "operation": "groupby" + }, + "erorr_type": { + "aggregations": [], + "operation": "groupby" + }, + "instance": { + "aggregations": [], + "operation": "groupby" + }, + "smart_id": { + "aggregations": [], + "operation": "groupby" + } + } + } + }, + { + "id": "sortBy", + "options": { + "fields": {}, + "sort": [ + { + "desc": true, + "field": "Value" + } + ] + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "auto" + }, + "filterable": false, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "smart_id" + }, + "properties": [ + { + "id": "custom.width", + "value": 50 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "erorr_type" + }, + "properties": [ + { + "id": "custom.width", + "value": 405 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "instance" + }, + "properties": [ + { + "id": "custom.width", + "value": 200 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "disk" + }, + "properties": [ + { + "id": "custom.width", + "value": 159 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Value" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "gradient", + "type": "color-background" + } + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + }, + { + "color": "red", + "value": 10 + } + ] + } + }, + { + "id": "custom.width", + "value": 60 + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 63 + }, + "id": 60, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "frameIndex": 0, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": false, + "expr": "smartmon_seek_error_rate_raw_value", + "format": "table", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "seek_error_rate", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "instance": false, + "job": true, + "type": true + }, + "includeByName": {}, + "indexByName": { + "Time": 0, + "Value": 7, + "__name__": 3, + "disk": 2, + "instance": 1, + "job": 4, + "smart_id": 5, + "type": 6 + }, + "renameByName": { + "__name__": "erorr_type" + } + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Value": { + "aggregations": [], + "operation": "groupby" + }, + "disk": { + "aggregations": [], + "operation": "groupby" + }, + "erorr_type": { + "aggregations": [], + "operation": "groupby" + }, + "instance": { + "aggregations": [], + "operation": "groupby" + }, + "smart_id": { + "aggregations": [], + "operation": "groupby" + } + } + } + }, + { + "id": "sortBy", + "options": { + "fields": {}, + "sort": [ + { + "desc": true, + "field": "Value" + } + ] + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "auto" + }, + "filterable": false, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "smart_id" + }, + "properties": [ + { + "id": "custom.width", + "value": 50 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "erorr_type" + }, + "properties": [ + { + "id": "custom.width", + "value": 405 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "instance" + }, + "properties": [ + { + "id": "custom.width", + "value": 200 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "disk" + }, + "properties": [ + { + "id": "custom.width", + "value": 159 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Value" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "gradient", + "type": "color-background" + } + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + }, + { + "color": "red", + "value": 10 + } + ] + } + }, + { + "id": "custom.width", + "value": 60 + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 68 + }, + "id": 61, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "frameIndex": 0, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "10.4.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": false, + "expr": "smartmon_spin_retry_count_raw_value", + "format": "table", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "spin_retry_count", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "instance": false, + "job": true, + "type": true + }, + "includeByName": {}, + "indexByName": { + "Time": 0, + "Value": 7, + "__name__": 3, + "disk": 2, + "instance": 1, + "job": 4, + "smart_id": 5, + "type": 6 + }, + "renameByName": { + "__name__": "erorr_type" + } + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Value": { + "aggregations": [], + "operation": "groupby" + }, + "disk": { + "aggregations": [], + "operation": "groupby" + }, + "erorr_type": { + "aggregations": [], + "operation": "groupby" + }, + "instance": { + "aggregations": [], + "operation": "groupby" + }, + "smart_id": { + "aggregations": [], + "operation": "groupby" + } + } + } + }, + { + "id": "sortBy", + "options": { + "fields": {}, + "sort": [ + { + "desc": true, + "field": "Value" + } + ] + } + } + ], + "type": "table" + } + ], + "refresh": "", + "schemaVersion": 39, + "tags": [ + "prometheus", + "node_exporter", + "smartmon" + ], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "definition": "label_values(smartmon_device_info,instance)", + "hide": 0, + "includeAll": true, + "multi": false, + "name": "instance", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(smartmon_device_info,instance)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "definition": "label_values({instance=~\"$instance\"},disk)", + "hide": 0, + "includeAll": true, + "multi": false, + "name": "disk", + "options": [], + "query": { + "qryType": 1, + "query": "label_values({instance=~\"$instance\"},disk)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-24h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "browser", + "title": "S.M.A.R.T disk monitoring for Prometheus DashboardNEW", + "uid": "PkPI32xGWz", + "version": 28, + "weekStart": "" +} diff --git a/media/grafana_dashboard_1.png b/media/grafana_dashboard_1.png new file mode 100644 index 0000000000000000000000000000000000000000..88b4eb19b60e0115608d8cba2680fbc56755cb4e GIT binary patch literal 277609 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