у меня небольшой опыт в линуксе и я не разработчик ПО, но есть опыт работы с разработчиками которые очень строго стараются следовать общепринятым традициям написания кода, т.н. "правилам хорошего тона"
хочу поделится методом (приемами) написания скриптов на bash для получения ключей zabbix клиента, заодно в качестве примера приведу реальный скрипт для получения параметров мониторинга UPS
абсолютно ничего особенного в этом нет, это все давно всем известно, но начинающим будет полезно
итак, есть у нас например несколько ключей, которые выглядят примерно так
UserParameter=uHDD.temp[*],sudo /etc/zabbix/disksmart.sh $1 | grep "194 Temp"| sed 's/\s\+/,/g' | cut -f10 -d,
UserParameter=uHDD.bb[*],sudo /etc/zabbix/disksmart.sh $1 | grep "5 Re"| sed 's/\s\+/,/g' | cut -f11 -d,
UserParameter=uHDD.ph[*],sudo /etc/zabbix/disksmart.sh $1 | grep "9 Pow"| sed 's/\s\+/,/g' | cut -f11 -d,
UserParameter=uHDD.wlc[*],sudo /etc/zabbix/disksmart.sh $1 | grep "177 We"| sed 's/\s\+/,/g' | cut -f10 -d,
UserParameter=uHDD.tlw[*],sudo /etc/zabbix/disksmart.sh $1 | grep "241 Tot"| sed 's/\s\+/,/g' | cut -f10 -d,
UserParameter=uHDD.ps[*],sudo /etc/zabbix/disksmart.sh $1 | grep "197 Curr"| sed 's/\s\+/,/g' | cut -f10 -d,
в принципе нормально, главное - все рабтает :)
не совсем красиво, для каждого ключа нужна своя строка
можно сделать, что бы все обработки выполнялись внутри скрипта, а запрос ключа для всех параметров выполнялся одной строкой:
UserParameter=ups.status[*],sudo /etc/zabbix/upsmon.sh $1
это на примере UPS, так выглядит универсальная строка для всех запрашиваемых параметров UPS
в случае с дисками можно например привести все к виду:
UserParameter=smarthdd[*],sudo /etc/zabbix/disksmart_new.sh $1 $2
где первый параметр - диск, второй - запрашиваемый параметр диска
но вернемся к UPS, мониторинг настроен с помощью NUT
я написал немного другой скрипт, кроме параметров UPS добавил туда еще контроль наличия подключения по usb и наличие запущенного демона
ну и конечно, т.к. параметров у нас достаточно, а периоды опроса не очень большие (питание дело серьезное) чтоб для получения каждого ключа не запускать исполняемый фай, применим прием с кэшем (кто-то может сказать, что я этот прием применяю где надо и где не надо, и возможно будет прав)
#!/bin/bash HOST="localhost" PORT="80" stub_status=server-status function check_daemon() { ps ax | grep -c "[b]lazer_usb" } function check_conn() { tail -n1 /var/log/syslog | grep ups | grep -c "unavailable\|stale" } CACHE_TTL="55" CACHE_FILE="/tmp/zabbix.ups.cache" EXEC_TIMEOUT="2" NOW_TIME=`date '+%s'` if [ -s "${CACHE_FILE}" ]; then CACHE_TIME=`stat -c"%Y" "${CACHE_FILE}"` else CACHE_TIME=0 fi DELTA_TIME=$((${NOW_TIME} - ${CACHE_TIME})) if [ ${DELTA_TIME} -lt ${EXEC_TIMEOUT} ]; then sleep $((${EXEC_TIMEOUT} - ${DELTA_TIME})) elif [ ${DELTA_TIME} -gt ${CACHE_TTL} ]; then DATACACHE=`upsc myups 2>&1 | grep -v '^Init SSL'` echo "" >> "${CACHE_FILE}" # !!! echo "${DATACACHE}" > "${CACHE_FILE}" # !!! chmod 640 "${CACHE_FILE}" fi function charge() { cat "${CACHE_FILE}" 2>/dev/null| grep 'charge:' | awk '{print $NF}' } function status() { cat "${CACHE_FILE}" 2>/dev/null| grep 'ups.status:' | awk '{print $2}' } function input_v() { cat "${CACHE_FILE}" 2>/dev/null| grep 'input.voltage:' | awk '{print $2}' } function output_v() { cat "${CACHE_FILE}" 2>/dev/null| grep 'output.voltage:' | awk '{print $2}' } function load() { cat "${CACHE_FILE}" 2>/dev/null| grep 'load:' | awk '{print $2}' } function bat_v() { cat "${CACHE_FILE}" 2>/dev/null| grep 'battery.voltage:' | awk '{print $2}' } function temp() { cat "${CACHE_FILE}" 2>/dev/null| grep 'temperature:' | awk '{print $2}' } case "$1" in check_daemon) check_daemon ;; check_conn) check_conn ;; charge) charge ;; status) status ;; input_v) input_v ;; output_v) output_v ;; load) load ;; bat_v) bat_v ;; temp) temp ;; *) echo $"Usage $0 {check_daemon|check_conn|charge|status|input_v|output_v|load|bat_v|temp}" exit esac
*) echo $"Usage $0 {check_daemon|check_conn|charge|status|input_v|output_v|load|bat_v|temp}"
обратите внимание на эту строку, если будет передана в качестве параметра строка с неизвестным скрипту параметром, он выдаст пояснение о том что он ожидает увидеть, обработка исключений важный момент в работе кода
ps ax | grep -c "[b]lazer_usb"
квадратные скобки на первой букве позволяют исключить из списка найденных процессов сам процесс поиска
tail -n1 /var/log/syslog | grep ups | grep -c "unavailable\|stale"
ключ "-с" в grep выводит количество найденных строк, появился не так давно, рекомендуют использовать вместо "wc -l"
не так давно переписал скрипт для мониторинга дисков, теперь все параметры всех дисков прописаны одной строкой:
UserParameter=smarthdd[*],sudo /etc/zabbix/disksmart_new.sh $1 $2
ну и сам скрипт, по коду думаю все понятно - какие параметры беруться, и таки да кроме параметров состояния, беру серийник, модель, наработку
потому что удобно все видеть сразу в одном месте, когда возникают вопросы, а кэширование позволяет не беспокоится об излишней нагрузке, да и периоды опроса этих ключей большие
#!/bin/bash if [ -z "$1" ]; then echo $"Usage $0 sda|sdb|sdx" exit 1 fi if [ -z "$2" ]; then echo $"Usage $0 $1 {temp|ph|wlc|tbw|ps|bb|model|sn|health}" exit 1 fi ##### PARAMETERS ##### nHDD="$1" SMART_PARAM="$2" CACHE_TTL="55" CACHE_FILE="/tmp/smartctl.`echo ${nHDD} | md5sum | cut -d" " -f1`.cache" EXEC_TIMEOUT="5" NOW_TIME=`date '+%s'` ##### RUN ##### if [ -s "${CACHE_FILE}" ]; then DATACACHE=$(cat "${CACHE_FILE}") LAST_FILE=`find /tmp/smartctl* -type f -printf '%TY-%Tm-%Td %TT %p\n' | sort -r | head -n1 | sed 's/\s\+/,/g' | cut -f3 -d,` CACHE_TIME=`stat -c"%Y" "${LAST_FILE}"` else CACHE_TIME=0 fi DELTA_TIME=$((${NOW_TIME} - ${CACHE_TIME})) if [ ${DELTA_TIME} -lt ${EXEC_TIMEOUT} ]; then sleep $((${EXEC_TIMEOUT} - ${DELTA_TIME})) elif [ ${DELTA_TIME} -gt ${CACHE_TTL} ]; then DATACACHE=`smartctl -a /dev/"${nHDD}" 2>&1` echo "" >> "${CACHE_FILE}" # !!! echo "${DATACACHE}" > "${CACHE_FILE}" # !!! chmod 640 "${CACHE_FILE}" fi function temp () { cat "${CACHE_FILE}" 2>/dev/nul | grep "194 Temp"| sed 's/\s\+/,/g' | cut -f10 -d, } function ph () { cat "${CACHE_FILE}" 2>/dev/null| grep "9 Pow"| sed 's/\s\+/,/g' | cut -f11 -d, } function wlc () { cat "${CACHE_FILE}" 2>/dev/null| grep "177 We"| sed 's/\s\+/,/g' | cut -f10 -d, } function tbw () { cat "${CACHE_FILE}" 2>/dev/null| grep "241 Tot"| sed 's/\s\+/,/g' | cut -f10 -d, } function ps () { cat "${CACHE_FILE}" 2>/dev/null| grep "197 Curr"| sed 's/\s\+/,/g' | cut -f10 -d, } function bb () { cat "${CACHE_FILE}" 2>/dev/null| grep "5 Real"| sed 's/\s\+/,/g' | cut -f11 -d, } function model () { cat "${CACHE_FILE}" 2>/dev/null| grep "Device Model"| sed 's/\s\+/,/g' | cut -f3-4 -d, } function sn () { cat "${CACHE_FILE}" 2>/dev/null| grep "Serial Number"| sed 's/\s\+/,/g' | cut -f3 -d, } function health () { smartctl -H /dev/${nHDD} |grep "test"| cut -f2 -d: |tr -d " " } case ${SMART_PARAM} in temp) temp ;; ph) ph ;; wlc) wlc ;; tbw) tbw ;; ps) ps ;; bb) bb ;; model) model ;; sn) sn ;; health) health ;; *) echo $"Usage $0 ${nHDD} {temp|ph|wlc|tbw|ps|bb|model|sn|health}" exit esac exit 0
так же думаю переписать скрипт обнаружения для LLD на lsblk
в свежих линуксах для команды lsblk появился ключ -J
-J, --json use JSON output format
получается практически готовый шаблон для LLD
и значительно выросло количество доступных столбцов для параметра "-o", в т.ч. выдает и модель, и серийный номер (его раньше не было), т.е. можно обойтись без использования smartctl (не во всех случаях конечно) и поулучить параметры, которых в smartctl нет, точку монтирования устройства например, иногда очень надо увидеть в параметрах устройства куда оно смонтировано
т.е. в задумках переписать LLD и более его привязать к параметрам состояния тома, а не только устройства
хотя наверно в 4.2 какие-то новшества есть на этот счет
Интересная информация про lsblk и json формат. Это очень удобно, не знал об этом.
grep -c "unavailable\|stale"
в данной строке стоит обратить внимание на фильтр - "unavailable\|stale"
т.е. используется "логическое или", в список выборки попадут строки включающие "unavailable" или "stale", но что бы символ "|" воспринимался как оператор сравнения, а не просто символ, перед ним надо поставить знак "\"
Спасибо Вам за оригинальные методы! Сам в скриптах не очень силён, поэтому для меня это будет полезным :)