Получение подсчета элементов списка с помощью jq - PullRequest
0 голосов
/ 28 сентября 2018

В настоящее время я изучаю, как использовать jq с оболочкой в ​​Linux, так как я разрабатываю пользовательские проверки для Check_MK (ранее известный как Nagios), и мое приложение (qBittorrent с их WebUI API ) возвращает строки JSON.

В настоящее время я уже могу подсчитать общее количество торрентов просто с помощью простого jq length.Теперь я хотел бы посчитать количество торрентов, которые в данный момент загружаются, загружаются или находятся в режиме паузы.Меня интересует только state, поэтому, если у меня есть 6 торрентов, мой JSON может выглядеть так:

[
  {
    "state": "uploading"
  },
  {
    "state": "downloading"
  },
  {
    "state": "downloading"
  },
  {
    "state": "downloading"
  },
  {
    "state": "pauseDL"
  },
  {
    "state": "pauseUP"
  }
]

Здесь jq length возвращает 6. Что мне нужно сделать, чтобы получитьдетали, такие как 3, загружаются, 1 загружается, 2 приостановлены и 0 ошибаются?

Вот мой действительный сценарий:

#!/bin/sh
curl -s http://localhost:8080/query/torrents -o /tmp/torrents.json
count=$(jq length /tmp/torrents.json)
echo "0 qbt_Nb_torrents - $count"

Синтаксис для echo:требуется Check_MK (как объяснено здесь ).

Я прочитал несколько примеров фильтров, но все они, кажется, работают, когда мы фильтруем атрибуты верхнего уровня.Здесь мой верхний уровень в основном просто [0], ..., [5], поэтому он не работает с примерами, которые я нашел в руководстве.

Дополнительная информация

API WebUI сообщает, что существует 12 возможных состояний.Вот как я собираюсь разделить их:

downloading: queuedDL, checkingDL, downloading 
uploading: queuedUP, checkingUP, uploading 
pause: pausedUP, pausedDL 
error: error 
stalled: stalledUP, stalledDL, metaDL

В соответствии с синтаксисом CheckMK мне нужно в основном вывести что-то вроде:

0 qbt_Nb_torrents - всего 6, 3 загрузки,1 посев, 2 при паузе, 0 остановлено, 0 ошибка

Первые 0 в начале означают состояние OK для CheckMK.Если есть какие-либо заблокированные торренты, я хочу, чтобы этот статус стал 1, а если есть какой-либо торрент по ошибке, статус становится 2. Пример:

2 qbt_Nb_torrents - всего 8, 3 загрузки, 1засев, 2 на паузе, 1 остановлен, 1 ошибка

Ответы [ 3 ]

0 голосов
/ 28 сентября 2018

На всякий случай, если кому-то интересно, что именно я в итоге использовал, следуя превосходному ответу Чарльза Даффи, вот полный сценарий оболочки /usr/lib/check_mk_agent/local/qbittorrent, который позволяет Check_MK (1.5.0 raw) получить, как мне кажется, наиболее актуальную информацию омое приложение qBittorrent (веб-интерфейс qBittorrent v3.3.7), запущенное в выделенной виртуальной машине на моем сервере:

#!/bin/sh
curl -s http://localhost:8080/query/transferInfo -o /tmp/transferInfo.json
curl -s http://localhost:8080/query/torrents -o /tmp/torrents.json

if [ -e /tmp/transferInfo.json ]
then
 dwl=$(jq .dl_info_speed /tmp/transferInfo.json)
 dwl_MB=$(bc <<< "scale=2;$dwl/1048576")
 upl=$(jq .up_info_speed /tmp/transferInfo.json)
 upl_MB=$(bc <<< "scale=2;$upl/1048576")
 echo "0 qbt_Global_speed download=$dwl_MB|upload=$upl_MB Download: $dwl_MB MB/s, Upload: $upl_MB MB/s"
 rm -f /tmp/transferInfo.json
else
 echo "3 qbt_Global_speed download=0|upload=0 Can't get the information from qBittorrent WebUI API"
fi

if [ -e /tmp/torrents.json ]
then    
 jq -r '
   def filterStates($stateMap):
    if $stateMap[.] then $stateMap[.] else . end;

   {"queuedDL": "downloading",
   "checkingDL": "downloading",
   "queuedUP": "uploading",
   "checkingUP": "uploading",
   "pausedUP": "pause",
   "pausedDL": "pause",
   "stalledUP": "stalled",
   "stalledDL": "stalled",
   "metaDL": "stalled"} as $stateMap |

   # initialize an output array since we want 0 outputs for everything
   {"pause": 0,  "stalled": 0, "error": 0, "downloading": 0, "uploading": 0} as $counts |

   # count number of items which filter to each value
   reduce (.[].state | filterStates($stateMap)) as $state ($counts; .[$state]+=1) |

   # output string
   "P qbt_Nb_torrents total=\(values|add)|downloading=\(.["downloading"])|seeding=\(.["uploading"])|pause=\(.["pause"])|stalled=\(.["stalled"]);0|error=\(.["error"]);0;0 total is \(values|add), downloading is \(.["downloading"]), seeding is \(.["uploading"]), pause is \(.["pause"])"
' /tmp/torrents.json

 rm -f /tmp/torrents.json
else
 echo "3 qbt_Nb_torrents total=0|downloading=0|seeding=0|pause=0|stalled=0;0|error=0;0;0 Can't get the information from qBittorrent WebUI API"
fi

Вот вывод с 1 остановленным торрентом:

0 qbt_Global_speed download=0|upload=0 Download: 0 MB/s, Upload: 0 MB/s
P qbt_Nb_torrents total=1|downloading=0|seeding=0|pause=0|stalled=1;0|error=0;0;0 total is 1, downloading is 0, seeding is 0, pause is 0

errorLevel то, что я думал, что мне нужно (см. ответ Чарльза) не требуется из-за того, как работает Check_MK;он обрабатывает пороговые значения через метрический параметр , когда указаны предупреждения и критические значения.

Вот как это выглядит в Check_MK:

Check_MK view

Обратите внимание, как Check_MK автоматически добавляет торможенные и ошибочные торренты.Это связано с указанием предупреждающих и / или критических пороговых значений.Метрики в целом (с пороговыми значениями или без них) важны для того, чтобы иметь подробные соответствующие диаграммы.

0 голосов
/ 28 сентября 2018

Вот немного более модульная версия решения @ CharlesDuffy.Возможно, основной интерес представляет общий фильтр «мешок слов»:

# bag of words
def bow(init; s): reduce s as $word (init; .[$word] += 1) ;

Обратите внимание также на функцию инициализации:

# initialize an output object since we minimally want 0s
def init:
  {} | {pause,stalled,error,downloading,uploading} | map_values(0);

С этими дополнительными абстракциями программа «main»становится просто двумя строками кода.

  def filterStates($stateMap):
    if $stateMap[.] then $stateMap[.] else . end ;

  def errorLevel:
    if .error > 0 then 2
    elif .stalled > 0 then 1
    else 0
    end ;

  def stateMap:
    {"queuedDL": "downloading", 
     "checkingDL": "downloading",
     "queuedUP": "uploading", 
     "checkingUP": "uploading",
     "pausedUP": "pause", 
     "pausedDL": "pause",
     "stalledUP": "stalled", 
     "stalledDL": "stalled", 
     "metaDL": "stalled"} ;

"Main"

  # count number of items which map to each value
  bow(init; .[].state | filterStates(stateMap))
  # format an output string
  | "\(errorLevel) qbt_Nb_torrents - \(values | add) total, \(.downloading) downloading, \(.uploading) seeding, \(.pause) on pause, \(.stalled) stalled, \(.error) error"
0 голосов
/ 28 сентября 2018

Для тех, у кого есть связанные вопросы, но они не разделяют особые требования ОП: см. Историю изменений!Есть несколько других соответствующих предложений, включая использование group_by в предыдущих итерациях этого ответа.


Если вам нужны записи для всех значений, даже тех, которые имеютни одного случая, вы можете рассмотреть:

jq -r '
  def filterStates($stateMap):
    if $stateMap[.] then $stateMap[.] else . end;

  def errorLevel:
    if (.["error"] > 0) then 2 else
      if (.["stalled"] > 0) then 1 else
        0
      end
    end;

  {"queuedDL": "downloading", 
   "checkingDL": "downloading",
   "queuedUP": "uploading", 
   "checkingUP": "uploading",
   "pausedUP": "pause", 
   "pausedDL": "pause",
   "stalledUP": "stalled", 
   "stalledDL": "stalled", 
   "metaDL": "stalled"} as $stateMap |

  # initialize an output array since we want 0 outputs for everything
  {"pause": 0,  "stalled": 0, "error": 0, "downloading": 0, "uploading": 0} as $counts |

  # count number of items which filter to each value
  reduce (.[].state | filterStates($stateMap)) as $state ($counts; .[$state]+=1) |

  # actually format an output string
  "\(. | errorLevel) qbt_Nb_torrents - \(values | add) total, \(.["downloading"]) downloading, \(.["uploading"]) seeding, \(.["pause"]) on pause, \(.["stalled"]) stalled, \(.["error"]) error"
' /tmp/torrents.json
...