#!/bin/bash ####################################################################################################################### # Этот скрипт снимает телеметрию с NAS и отправляет ее по MQTT. # Какие данные используются: # уровень рейда # cat /sys/block/$mdx/md/level # количество дисков задействованых в рейде # cat /sys/block/$mdx/md/raid_disks # состояние рейда # cat /sys/block/$mdx/md/sync_action # состояние сбойных дисков # cat /sys/block/$mdx/md/degraded # информация о файловой системе # df /dev/md0 # df -h /dev/md0 # общий объем # занято # свободно # точка монтирования # серийные номера дисков # lsblk -o name,serial # номер слота каждого диска # cat /sys/block/$mdx/md/$d/slot # состояние каждого диска # cat /sys/block/$mdx/md/$d/state # количество ошибок по каждому диску # cat /sys/block/$mdx/md/$d/errors # производитель каждого диска # cat /sys/block/$mdx/md/$d/block/device/vendor # модель каждого диска # cat /sys/block/$mdx/md/$d/block/device/model # информация о блочном устройстве # cat /sys/block/$mdx/md/$d/block/uevent # /dev/sdx # мажор # минор # паспортный объем диска # hdparm -I /dev/$sdx # # Скрипт все данные упаковывает в json сериализированную строку и отправляет всю целиком на один (!) топик MQTT # # Ничего изменять в этом файле не надо (если конечно вы не отдаете отчет своим действиям) # Параметры подключения и прочие конфигурации прописываются в конфигурационном файле. # Если конфигурационного файла нет, то при запуске будет создан шаблон конфигурационного файла # # Автор: SvC # Версия: 2.2.3.12 от 16.01.2021 # ####################################################################################################################### IFS_old=$IFS IFS="" # я не знаю что это за магический костыль, но благодаря ему перестали съедаться переносы строк true_script_name='nas_telemetry_pub' script_name=`basename $0` settings_path=`dirname $0` settings_name=$script_name.conf settings_fullname=$settings_path/$settings_name script_fullname=$settings_path/$script_name # проверка на наличие файла конфигурации if [ -e $settings_fullname ]; then settings_out=`/bin/cat $settings_fullname` check_update=`echo $settings_out | grep -P 'check_update' | cut -d '#' -f 1 | cut -d '=' -f 2 | awk '{$1=$1;print}'` script_token=`echo $settings_out | grep -P 'script_token' | cut -d '#' -f 1 | cut -d '=' -f 2 | awk '{$1=$1;print}'` mdx=`echo $settings_out | grep -P 'mdx=' | cut -d '#' -f 1 | cut -d '=' -f 2 | awk '{$1=$1;print}'` ip=`echo $settings_out | grep -P 'ip=' | cut -d '#' -f 1 | cut -d '=' -f 2 | awk '{$1=$1;print}'` port=`echo $settings_out | grep -P 'port=' | cut -d '#' -f 1 | cut -d '=' -f 2 | awk '{$1=$1;print}'` user=`echo $settings_out | grep -P 'user=' | cut -d '#' -f 1 | cut -d '=' -f 2 | awk '{$1=$1;print}'` pass=`echo $settings_out | grep -P 'pass=' | cut -d '#' -f 1 | cut -d '=' -f 2 | awk '{$1=$1;print}'` name=`echo $settings_out | grep -P 'name=' | cut -d '#' -f 1 | cut -d '=' -f 2 | awk '{$1=$1;print}'` topik=`echo $settings_out | grep -P 'topik=' | cut -d '#' -f 1 | cut -d '=' -f 2 | awk '{$1=$1;print}'` # проверка обновлений if [ "$check_update" == "true" ]; then current_ver=`cat $script_fullname | grep -P '# Версия:' | head -1 | awk '{print $3}'` update_url="ss.svcserv.ru/update.php?script_name=$true_script_name¤t_ver=$current_ver&script_token=$script_token" update_ver=`curl -sL $update_url` if ((`echo $update_ver | cut -d '.' -f 4` > `echo $current_ver | cut -d '.' -f 4`)); then echo -e "\e[01;33mДоступно обновление ($current_ver -> $update_ver)\e[0m" echo 'Чтобы скачать напишите:' echo '' if [ "$script_name" == "$true_script_name" ]; then echo -e " rm $true_script_name && wget http://ss.svcserv.ru/$true_script_name && chmod ugo+x $true_script_name\e[0m" else echo -e " rm $script_name && wget http://ss.svcserv.ru/$true_script_name && chmod ugo+x $true_script_name && mv $true_script_name $script_name\e[0m" fi echo '' fi fi ####################################################################################################################### # тело скрипта # обработка общего состояния рейда payload[0]=`/bin/cat /sys/block/$mdx/md/level` keys[0]='level' types[0]='s' payload[1]=`/bin/cat /sys/block/$mdx/md/raid_disks` keys[1]='dev_raid' types[1]='i' payload[2]=`/bin/cat /sys/block/$mdx/md/sync_action` keys[2]='state' types[2]='s' payload[3]=`/bin/cat /sys/block/$mdx/md/degraded` keys[3]='dev_failed' types[3]='i' payload[11]=0 keys[11]='dev_working' types[11]='i' payload[12]=0 keys[12]='dev_spare' types[12]='i' # обработка свободного места рейда в байтах df_out=`/bin/df /dev/$mdx | grep -P '/dev/'$mdx` payload[4]=`echo $df_out | awk '{print $2}'` keys[4]='size_total' types[4]='i' payload[5]=`echo $df_out | awk '{print $3}'` keys[5]='size_used' types[5]='i' payload[6]=`echo $df_out | awk '{print $4}'` keys[6]='size_free' types[6]='i' payload[7]=`echo $df_out | awk '{print $6}'` keys[7]='mount_point' types[7]='s' # обработка свободного места рейда в человеческом виде df_out=`/bin/df -h /dev/$mdx | grep -P '/dev/'$mdx` payload[8]=`echo $df_out | awk '{print $2}'` keys[8]='size_total_human' types[8]='s' payload[9]=`echo $df_out | awk '{print $3}'` keys[9]='size_used_human' types[9]='s' payload[10]=`echo $df_out | awk '{print $4}'` keys[10]='size_free_human' types[10]='s' # таймстемп для того чтоб строка менялась, нигде не используется, можно убрать payload[13]=`date +%Y.%m.%d_%H:%M:%S` keys[13]='timestamp' types[13]='s' # обработка состояния дисков рейда disks=`ls -1 /sys/block/$mdx/md | grep rd` IFS=" " i=0 lsblk_out=`lsblk -o name,serial` for d in ${disks[@]}; do disks_slot[$i]=`/bin/cat /sys/block/$mdx/md/$d/slot` disks_states[$i]=`/bin/cat /sys/block/$mdx/md/$d/state` disks_errors[$i]=`/bin/cat /sys/block/$mdx/md/$d/errors` disks_vendors[$i]=`/bin/cat /sys/block/$mdx/md/$d/block/device/vendor | awk '{$1=$1;print}'` disks_models[$i]=`/bin/cat /sys/block/$mdx/md/$d/block/device/model | awk '{$1=$1;print}'` IFS="" uevent_out=`/bin/cat /sys/block/$mdx/md/$d/block/uevent` disks_devs[$i]=`echo $uevent_out | grep DEVNAME | cut -d '=' -f 2` disks_majors[$i]=`echo $uevent_out | grep MAJOR | cut -d '=' -f 2` disks_minors[$i]=`echo $uevent_out | grep MINOR | cut -d '=' -f 2` disks_serial_numbers[$i]=`echo $lsblk_out | grep ${disks_devs[$i]} | awk '{print $2}'` # проверка существования директории if ! [ -d /var/lib/nas_telemetry_pub/ ]; then mkdir /var/lib/nas_telemetry_pub/ fi # проверка существования файла if ! [ -f /var/lib/nas_telemetry_pub/${disks_majors[$i]}.${disks_minors[$i]} ]; then disks_capacitys[$i]=`/sbin/hdparm -I /dev/${disks_devs[$i]} | grep '1000\*1000:' | cut -d ':' -f 2 | cut -d '(' -f 2 | cut -d ')' -f 1` echo ${disks_capacitys[$i]} > /var/lib/nas_telemetry_pub/${disks_majors[$i]}.${disks_minors[$i]} else disks_capacitys[$i]=`cat /var/lib/nas_telemetry_pub/${disks_majors[$i]}.${disks_minors[$i]}` fi # ручной подсчет количества дисков if [ "${disks_states[$i]}" == "in_sync" ]; then payload[11]=$((${payload[11]}+1)) fi if [ "${disks_states[$i]}" == "spare" ]; then payload[12]=$((${payload[12]}+1)) fi if [ "${disks_states[$i]}" == "faulty" ]; then payload[3]=$((${payload[3]}+1)) fi i=$(($i+1)) done disks_numbers=$i ####################################################################################################################### # отправка данных # формирование json сериализированной строки json= p=$((${#payload[@]}-1)) for ((i=0; i < ${#payload[@]}; i++)) do if [[ ${types[$i]} == 'i' ]]; then json=${json}'"'${keys[$i]}'":'${payload[$i]} else if [[ ${types[$i]} == 's' ]]; then json=${json}'"'${keys[$i]}'":"'${payload[$i]}'"' fi fi if [[ $i != $p ]]; then json=${json}',' fi done json_dev= n=$((${disks_numbers}-1)) for ((i=0; i < $disks_numbers; i++)) do #json_dev=$json_dev'{"number":'${numbers[$i]}',"raid_dev":'${raid_devs[$i]}',"state":"'${states[$i]}'","dev":"'${devs[$i]}'","model":"'${models[$i]}'","serial_number":"'${serial_numbers[$i]}'","capacity":"'${capacitys[$i]}'","temperature":'${temperatures[$i]}'}' json_dev=$json_dev'{"disk_slot":'${disks_slot[$i]}',"disk_state":"'${disks_states[$i]}'","disk_error":'${disks_errors[$i]}',"disk_vendor":"'${disks_vendors[$i]}'","disk_model":"'${disks_models[$i]}'","disk_dev":"/dev/'${disks_devs[$i]}'","disk_serial_number":"'${disks_serial_numbers[$i]}'","disk_major":'${disks_majors[$i]}',"disk_minor":'${disks_minors[$i]}',"disk_capacity":"'${disks_capacitys[$i]}'"}' if [[ $i != $n ]]; then json_dev=${json_dev}',' fi done json='{'$json',"devs":['$json_dev']}' echo '' echo 'Отправка на '$user'@'$ip'/'$topik echo '' echo $json echo '' # отправка через MQTT mosquitto_pub -h $ip -p $port -u $user -P $pass -i $name -t $topik -m "$json" # конец тела скрипта ####################################################################################################################### # если конфигурационного файла нет else script_token=`< /dev/urandom tr -dc A-Za-z0-9 | head -c32` echo "" echo "Файл $settings_name отсутствует" echo "Cоздан шаблон файла, заполните его и запустите команду заново" echo "# это файл настроек для скрипта $settings_name # скрипт и файл настроек должны иметь одинаковое имя, если вы переименовываете скрипт, то файл настроек должен быть так же переименован check_update=true # проверять ли обновления скрипта script_token=$script_token # токен позволяющий однозначно идентифицировать скрипт mdx=md0 # имя raid ip=127.0.0.1 # адрес mqtt сервера port=1883 # порт mqtt сервера user=mqtt_user # логин для подключения к mqtt серверу pass=mqtt_pass # пароль для подключения к mqtt серверу name=script_by_svc # имя клиента которое будет передано mqtt серверу topik=mqtt_topic # топик в который будет записана json строка, в качестве раделителя используется / " > $settings_fullname fi