Skip to content

Применение Victoriametrics для мониторинга по SNMP

Заметка написана специально для коммунити VictoriaMetrics_ru1 в телеграм.

Выбор tsdb

Развитие облачных сервисов, хайп вокруг kubernetes привели к тому, что с 2017 года популярность стали набирать базы данных временных рядов (time series databases). В таких базах хранят метрики (metrics) приложений, а специально разработанные языки запросов для этих баз позволяют проводить анализ данных, строить графики, информировать об отклонениях от заданных пороговых значений и т.д. Prometheus становится решением по умолчанию для мониторинга kubernetes и все больше разработчиков начинают дописывать свои приложения, чтобы те могли по протоколу http отдавать свои метрики в в формате prometheus:

myapp_status{server="prod1"} 1

Не всегда приложение или какое-то устройство может отдать метрики в нужном формате. Начинается разработка специализированных приложений для интеграции с prometheus, получивших общее название exporter. Список exporter'ов ничем не ограничен. Надо только согласовать с community номер порта, на котором ваш exporter будет принимать запросы. Есть и более простой способ передать метрики без написания exporter'а. Разработчики node_exporter предусмотрели для этого textfile collector. Теперь достаточно скопировать файл с готовыми метриками в определенный каталог.

С заданным интервалом (scrape interval) Prometheus опрашивает приложения, exporter'ы и записывает метрики с базу. При всех достоинствах этого решения, конечно, были ограничения:

  • ограниченное время хранения метрик;
  • отсутствие горизонтального масштабирования;
  • и другие ограничения пулл модели.

Первое ограничение обходится применением удаленных хранилищ, т.н. remote storage. Такой базой данных временных рядов с возможностью длительного хранения данных является VictoriaMetrics (далее VM). Ее разработка началась в 2018 году, а в мае 2019 года появляется open-source версия на github. VM начинают выбирать за быстроту, производительность, простоту установки и возможности горизонтального масштабирования в кластерной версии. Экономия на ресурсах, поддержка языка запросов PromQl и наличие новых востребованных функций в MetricsQl приводит к росту ее популярности. Вокруг VM начинает развиваться экосистема, разрабатываются дополнительные приложения, позволяющие исключить prometheus, как компонент, полностью.

telegraf vs vmagent+snmp_exporter

Протокол snmp все еще активно используется для мониторинга параметров работы телекоммуникационного оборудования, например:

  • счетчики объемов переданных данных через интерфейс в байтах, пакетах;
  • счетчики ошибок и потерь на интерфейсах;
  • состояние сенсоров температуры, питания, вентиляторов;
  • счетчики утизизации ресуров, таких как память, cpu;
  • состояние сессий протоколов маршрутизации;
  • различные счетчики политик, acl.

Ниже будет сравнение telegraf c плагином snmp (T) и vmagent + snmp_exporter (VS). Я работал с обоими, telegraf не использую более года. Не будет сравнения с другим ПО: zabbix, cacti, librenms.

vmagent периодически опрашивает через snmp_exporter устройства по протоколу snmp и записывает полученные метрики в базу VictoriaMetrics. После 0.20.0 заявлены изменения и snmp_exporter возможно пеередет github.com/prometheus-community/snmp

service discovery

Удобно, когда хосты для мониторинга можно добавить на лету и нет необходимости править конфигурацию, релоадить сервис.

T: Нет service discovery. Необходимо после изменения списка агентов делать reload telegraf'у.

VS: Есть service discovery и file_sd в частности. Для каждого модуля можно создавать файл со списком хостов средствами автоматизации.

Статус опрашиваемых устройств

Если устройство недоступно, то это надо видеть как-то и не только по отсутствию метрик в базе.

T: Статус опрашиваемых устройств определить нельзя. Можно посмотреть логи об ошибках.

VS: Есть ручка curl http://vmagent-host:8429/targets , метрики и официальный dashboard для них с кол-вом targets up/down.

Буфер для опрошенных метрик

При отсуствии связи между агентом и базой метрики будут кешироваться на хосте агента.

T: metric_buffer_limit в настройках telegraf определяет размер кэша для хранения метрик при опросе устройств При недоступности базы метрики хранятся в кэше (оперативной памяти) и при восстановлении связи записываются. При перезапуске telegraf'а, например из-за изменения адреса базы, данные будут потеряны.

VS: remoteWrite.tmpDataPath у vmagent определяет каталог для хранения данных при недоступности базы. Данные дописываются при восстановлении доступа к базе, каталог очищается, а метрика vmagent_remotewrite_pending_data_bytes будет с какого-то числа уменьшаться до нуля.

snmp mib'ы

Обычно на хост, с которого осуществляется опрос, требуется установить snmp, все mib'ы с зависимостями. В случае snmp_exporter'а, чей конфиг с oid'ами генерится на отдельном хосте, mib'ы нужны только на рабочем месте или виртуальной машине администратора, работаюшего с генератором.

T: На хосте, который будет мониторинть сетевые устройства устанавливается snmp, mib'ы, telegraf. В конфигурации можно использовать oid без мибов в случае inputs.snmp.field. Необходимы mib'ы на хосте, т.к плагин выполняет snmptranslate при старте и делает проверку oid для таблиц inputs.snmp.table что это действительно таблица.

VS: На хост устанавливается snmp_exporter c конфигурационным файлом snmp.yml, vmagent. Устанавливать snmp, mib'ы не нужно.

Отдельно добавлю про генератор, т.к. он является частью подготовительных работ по настройке. Создать конфигурацию без миба не получится, потому что все индексы - это лейблы.

интервал опроса

interval или scrape interval. Считается хорошей практикой хранить метрики в базе с одним интервалом. Интервал 1 минута хорош для алертинга, а интервал в 5 минут является достаточным для хранения исторических данных и подходит для опроса устройств с медленным control-plane, которые не могут на минуту отдать свои таблицы, либо устройства расположены на каналами с высоким RTT. Во втором случае следует определиться с масштабированием и выносом агента поближе к опрашиваемым устройствам. Необходимо понимать, что устройства отвечают по snmp медленно и необходим компромис между количеством опрашиваемых данных и интервалом опроса.

T: Интервал опроса указывается в каждом конфигурационном файле, либо глобально. Имеет смысл делать множество файлов, т.к. если плагин не успеет завершить работу в указанный интервал, то данные обо всех устройствах будут потеряны. Когда начинается какая-то проблема, то потеря данных мониторинга делает бессмысленным мониторинг.

VS: Рекомендуется один интервал для всех метрик. 1 минута - интервал опроса всех устройств по snmp. Недоступность устройства по snmp или длительное время опроса, превышающее интервал опроса данных не влияет на получения результатов опроса от других устройств.

конфигурационный файл

T: Казалось бы, что настраивать все плагины в одном файле удобно, но из-за ограничений плагина при таймауте интервала их нужно много. Для каждого вендора или модели оборудования или/и региона опроса создается отдельный конфигурационный файл для snmp плагина в /etc/telegraf/telegraf.d/.

VS: У snmp_exporter один конфигурационный файл, в котором для каждого вендора или модели оборудования описывается модуль. Этот конфигурационный файл может быть универсальным для всех устройств на сети и копироваться на все хосты с exporter'ами. В vmagent'е для каждого модуля свой job или иная реализация. Для каждого хоста в регионах будут свои таргеты из service discovery.

кардинальность

При использовании influx об кардинальности вообще не задумываются до тех пор, пока база как-то работает. Это простота использования обманчива и расплачиваться приходится медленной работой базы. В случае БД prometheus уже тот факт что в базу нельзя писать текстовые значения, заставляет смотреть что вообще пишется.

T: Добавление тегов влияет на производительность, но рекомендуется для индексации и поиска. Запишет все полученные значения (value) текст, числа, неотфильтрованный мусор в базу influxdb как одну метрику (measurement) с множеством значений и индексов (tag). Есть мибы, например от polycom, где числа описаны как строки. Такое значение можно преобразовать через conversion = "int". Лишние индексы в oid можно ограничить через oid_index_length в inputs.snmp.table.field. Можно настроить фильтрацию через fieldpass, tagexclude, inputs.snmp.tagdrop в конфигурации плагина.

VS: Метрика с другим набором лейблов - это другой временной ряд. В victoria-metrics и prometheus можно записать только числовые значения, а текст только в индексы/лейблы. Одной метрике будет соотвествовать одно значение с набором лейблов. snmp_exporter умеет преобразовывать string через overrides. Ниже будет пример в конфигураторе с коммутатором extreme, где cpu usage в oid описана как string и по дефолту запишется в label. В конфигурации vmagent'а необходимо настроить labeldrop в metric_relabel_configs. Есть drop_source_indexes в lookup и ignore overrides в snmp_exporter.

GUI для запросов

Для новичков низкий порог входа в язык запросов influx может показаться удобным. Здесь медвежью услугу оказала grafana.

T: В grafana есть конструктор запросов для influxdb, где в качестве условий указываются теги.

VS: Необходимо знать функции promql для составления запросов в базу, указывать в запросе специфичные для метрики лейблы.

rate

Для построения исторических данных в виде графиков загрузки интерфейса используются метрики с 64-битными счетчиками, которые растут, либо сбрасываются в 0. Чтобы определить скорость изменения, необходимо получить из базы несколько точек за указанный интервал.

T: Функции derivative() или non_negative_derivative() испольуются в grafana для отображения графиков загрузки интерфейсов. Интервал для использования в функциях может совпадать с интервалом опроса устройств.

VS: Функции rate() и increase() для счетчиков (counters) 32 и 64 битных. В базе prometheus для rate() необходимо 2.3 точки на интервале. Т.е. при опросе раз в минуту можно задать rate(metric[5m]), но не rate(metric[1m]). В victoria-metrics такого ограничения нет.

telegraf

Ниже будет текст про предыдущий опыт эксплуатации telegraf'а с influxdb. В influxdb можно писать текст, а это уже совсем не про хранение метрик. Еще я пробовал плагин сlickhouse и писал туда дескрипшены интерфейсов, а счетчики iftable писал в VM. Рекомендую пропустить этот абзац для экономии времени.

Информацию по протоколу snmp можно получать разную: счетчики интерфейсов, статус sla, описание интерфейсов, состав оборудования из entity-mib, счетчики политик qos. Все это можно опрашивать с разными интервалами, например, данные по sla нужны раз в минуту, счетчики интерфейсов и политик раз в пять минут, инвентаризацию раз в сутки. Разные интервалы подразумевают разные конфигурационные файлы для плагина snmp, т.к. интервал указывается для плагина, а не для job'а, как в prometheus/vmagent. Опрашивать все с одним интервалом не получится, т.к. нет необходимости хранить одни и те же данные, например, строковые значения инвертацизации, описаний интерфейсов, ip-адреса, arp'ы и т.п. Еще опрос такого количества таблиц занимает приличное время и в минутный интервал просто не успеть. Это легко проверить, запустив в тестовом режиме telegraf.

$ telegraf -config test.cfg -test
Отсюда появляется ограничение, что интервал опроса для конкретного конфигурационного файла не может быть меньше, чем время опроса всех агентов в т.ч. с учетом таймаутов из-за недоступности. Если все агенты не будут опрошены до наступления следующего интервала опроса, то данные, полученные от всех агентов будут потеряны с фиксацией события в логах "input "inputs.snmp" did not complete within its interval". Т.е. приходится как-то оптимизировать запрашиваемую информацию, опрашивать какие-то таблицы менее часто, но с бОльшим таймаутом по времени. Ниже будут примеры, но это больше для информации, истории. Теперь пользуюсь vmagent'ом и snmp_exporter'ом для сбора каунтеров и конфигурация для него немного отличается в сторону упрощения и уменьшения количества лейблов. Telegraf оставил для записи строк в clickhouse. Дальше в примерах будут некоторые штуки, которые пришли с опытом:

  • Если какой-то field будет использоваться как tag, то имя в конфиге будет отличаться. Это связано с использованием ранних версий influxdb, где с наименованием в самой базе случалась каная-то каша. Так появлялись ifName и ifName_1 и я уже не помню что из этого что. Опять же в запросах меньше путаницы будет, когда ifName - это field, а Name - это tag. Позже стал использовать fieldpass , в котором не указывал все field, используемые в качестве tag.
  • Вместо имени oid'а используется сам oid, а его имя для удобства указано в комментарии выше. Старая версия telegraf'а при старте запускала в shell'е snmptranslate и чем больше было конфигурационных файлов, тем выше была загрузка cpu. Рестарт был очень неудобен и позже алгоритм немного поменяли на однократный запуск snmptranslate. Все таблицы все равно проверяются в мибах и опросить таблицу совсем без миба не получится.

Пример для cisco ip sla, где не опрашивается вся таблица rttMonLatestRttOperTable (закоменнирован oid), а только два значения из нее. Тэг для замены индекса берется из другой таблицы rttMonCtrlAdmintable. На оборудовании cisco настроен следующий sla:

ip sla 1
 icmp-echo 10.4.3.1 source-ip 10.4.3.2
 tos 96
 threshold 200
 owner IPSName-vlanid@AddrA-AddrB
ip sla schedule 1 life forever start-time now
Конфигурация плагина с фильтрацией по тегу, чтобы не записывать все тестовые, временные пробы:
[[inputs.snmp]]
 interval = "1m"
 agents = [ "cisco" ]
 version = 2
 community = "public"

#CISCO-RTTMON-MIB::rttMonCtrlAdminOwner.5 = STRING: "IPSName-vlanid@AddrA-AddrB"

  [[inputs.snmp.table]]
    name = "rttsla"
    #oid = "CISCO-RTTMON-MIB::rttMonLatestRttOperTable"

    [[inputs.snmp.table.field]]
      name = "Descr"
      #oid = "CISCO-RTTMON-MIB::rttMonCtrlAdminOwner"
      oid = "1.3.6.1.4.1.9.9.42.1.2.1.1.2"
      is_tag = true

    [[inputs.snmp.table.field]]
      name = "icmpResults"
      #oid = "CISCO-RTTMON-MIB::rttMonLatestRttOperSense"
      oid = "1.3.6.1.4.1.9.9.42.1.2.10.1.2"

    [[inputs.snmp.table.field]]
      name = "rttAvg"
      #oid = "CISCO-RTTMON-MIB::rttMonLatestRttOperCompletionTime"
      oid = "1.3.6.1.4.1.9.9.42.1.2.10.1.1"

 [inputs.snmp.tagpass]
   Descr = [ "*@*" ]
Более сложным оказался опрос аналогичных проберов на коммутаторах huawei s5720ei, где в качестве одного из индексов используется растущий счетчик для истории проверок. Чтобы его удалить, понадобилось ограничить oid. Для удобства, используются такие же имена метрик:
[[inputs.snmp]]
 interval = "1m"
 agents = [ "huawei" ]
 version = 2
 community = "public"

# for  huawei s5720-ei
#NQA-MIB::nqaAdminCtrlTag."1"."1" = STRING: IPSName-vlanid@AddrA-AddrB
#.1.3.6.1.4.1.2011.5.25.111.2.1.1.3.1.49.1.49 = STRING: IPSName-vlanid@AddrA-AddrB
#index is 1.49.1.49. oid_index_length = 4
#NQA-MIB::nqaResultsCompletions."1"."1".186177.1 = INTEGER: success(1)
#s5720-ei has a history index

  [[inputs.snmp.table]]
    name = "rttsla"
    #oid = "NQA-MIB::nqaAdminCtrlTable"

    [[inputs.snmp.table.field]]
      name = "Descr"
      #oid = "NQA-MIB::nqaAdminCtrlTag"
      oid = "1.3.6.1.4.1.2011.5.25.111.2.1.1.3"
      is_tag = true

    [[inputs.snmp.table.field]]
      name = "icmpResults"
      #NQA-MIB::nqaResultsCompletions
      oid = "1.3.6.1.4.1.2011.5.25.111.4.1.1.3"
      oid_index_length = 4

    [[inputs.snmp.table.field]]
      name = "rttAvg"
      #NQA-MIB::nqaResultsRttAvg
      oid = "1.3.6.1.4.1.2011.5.25.111.4.1.1.26"
      oid_index_length = 4

 [inputs.snmp.tagpass]
   Descr = [ "*@*" ]
Вот закончились небольшие таблицы, которые можно опросить за короткое время с интервалом опроса одна минута. Далее конфигурация для маршрутизатора cisco, где есть ряд ограничений, чтобы случайно в базу influxdb не записать лишнего и не было такого, когда одно устройство пишет больше полей в базу, чем другое. Эта конфигурация не запишет в базу значения mtu, ifalias и прочее. Только то, что определено в fieldpass. Метрики с тегами логических интерфейсов будут удалены. Сейчас я удаляю по лейблу ifType, а не по имени интерфейса. Вопреки примерам, не следует опрашивать таблицы ifTable и ifTableX целиком и записывать в influxdb все подряд: дескрипшены, таймстампы и прочую ненужную информацию. Необходимо сосредоточится на скорости выполения запроса, хотя тут тоже много лишнего для маршрутизатора. Жаль было расставаться с функционалом, который позволил использовать именно такую замену индексов в таблице cbqos, потому что этот миб самый невменяемый, а chain lookup в snmp_exporter'е сделали относительно недавно.
[[inputs.snmp]]
 interval = "5m"
 agents = [ "cisco_router" ]
 version = 2
 community = "public"
 fieldpass = [ "cbQos*", "bgpPeerState", "bgpPeerFsmEstablishedTransitions", "Duplex" , "Speed" , "ifAdminStatus" , "ifOperStatus" , "ifInDiscards" , "ifInErrors" , "ifOutDiscards" , "ifOutErrors" , "ifHCInOctets" , "ifHCInUcastPkts" , "ifHCInMulticastPkts" , "ifHCInBroadcastPkts" , "ifHCOutOctets" , "ifHCOutUcastPkts" , "ifHCOutMulticastPkts" , "ifHCOutBroadcastPkts" ]
 tagexclude = [ "ifIndex" ]

 [[inputs.snmp.table]]
  name = "interface32"
  #oid = "IF-MIB::ifTable"

   [[inputs.snmp.table.field]]
    name = "Name"
    #IF-MIB::ifName
    oid = "1.3.6.1.2.1.31.1.1.1.1"
    is_tag = true

   [[inputs.snmp.table.field]]
    name = "Speed"
    #IF-MIB::ifHighSpeed
    oid = "1.3.6.1.2.1.31.1.1.1.15"

   [[inputs.snmp.table.field]]
    name = "Duplex"
    #EtherLike-MIB::dot3StatsDuplexStatus
    oid = "1.3.6.1.2.1.10.7.2.1.19"

   [[inputs.snmp.table.field]]
    name = "ifAdminStatus"
    #IF-MIB::ifAdminStatus
    oid = "1.3.6.1.2.1.2.2.1.7"

   [[inputs.snmp.table.field]]
    name = "ifOperStatus"
    #IF-MIB::ifOperStatus
    oid = "1.3.6.1.2.1.2.2.1.8"

   [[inputs.snmp.table.field]]
    name = "ifInDiscards"
    #IF-MIB::ifInDiscards
    oid = "1.3.6.1.2.1.2.2.1.13"

   [[inputs.snmp.table.field]]
    name = "ifInErrors"
    #IF-MIB::ifInErrors
    oid = "1.3.6.1.2.1.2.2.1.14"

   [[inputs.snmp.table.field]]
    name = "ifOutDiscards"
    #IF-MIB::ifOutDiscards
    oid = "1.3.6.1.2.1.2.2.1.19"

   [[inputs.snmp.table.field]]
    name = "ifOutErrors"
    #IF-MIB::ifOutErrors
    oid = "1.3.6.1.2.1.2.2.1.20"

 [[inputs.snmp.table]]
  name = "interface64"
  #oid = "IF-MIB::ifXTable"

   [[inputs.snmp.table.field]]
    name = "Name"
    #IF-MIB::ifName
    oid = "1.3.6.1.2.1.31.1.1.1.1"
    is_tag = true

   [[inputs.snmp.table.field]]
     name = "Speed"
     #IF-MIB::ifHighSpeed
     oid = "1.3.6.1.2.1.31.1.1.1.15"

   [[inputs.snmp.table.field]]
     name = "ifHCInOctets"
     #IF-MIB::ifHCInOctets
     oid = "1.3.6.1.2.1.31.1.1.1.6"

   [[inputs.snmp.table.field]]
     name = "ifHCInUcastPkts"
     #IF-MIB::ifHCInUcastPkts
     oid = "1.3.6.1.2.1.31.1.1.1.7"

   [[inputs.snmp.table.field]]
     name = "ifHCInMulticastPkts"
     #IF-MIB::ifHCInMulticastPkts
     oid = "1.3.6.1.2.1.31.1.1.1.8"

   [[inputs.snmp.table.field]]
     name = "ifHCInBroadcastPkts"
     #IF-MIB::ifHCInBroadcastPkts
     oid = "1.3.6.1.2.1.31.1.1.1.9"

   [[inputs.snmp.table.field]]
     name = "ifHCOutOctets"
     #IF-MIB::ifHCOutOctets
     oid = "1.3.6.1.2.1.31.1.1.1.10"

   [[inputs.snmp.table.field]]
     name = "ifHCOutUcastPkts"
     #IF-MIB::ifHCOutUcastPkts
     oid = "1.3.6.1.2.1.31.1.1.1.11"

   [[inputs.snmp.table.field]]
     name = "ifHCOutMulticastPkts"
     #IF-MIB::ifHCOutMulticastPkts
     oid = "1.3.6.1.2.1.31.1.1.1.12"

   [[inputs.snmp.table.field]]
     name = "ifHCOutBroadcastPkts"
     #IF-MIB::ifHCOutBroadcastPkts
     oid = "1.3.6.1.2.1.31.1.1.1.13"

  [[inputs.snmp.table]]
    name = "bgpPeer"
    #oid = "1.3.6.1.2.1.15.3"

    [[inputs.snmp.table.field]]
      name = "bgpPeerIdentifier"
      #BGP4-MIB::bgpPeerIdentifier"
      oid = "1.3.6.1.2.1.15.3.1.1"
      is_tag = true

    [[inputs.snmp.table.field]]
      name = "bgpPeerState"
      #BGP4-MIB::bgpPeerState"
      oid = "1.3.6.1.2.1.15.3.1.2"

    [[inputs.snmp.table.field]]
      name = "bgpPeerFsmEstablishedTransitions"
      #BGP4-MIB::bgpPeerFsmEstablishedTransitions
      oid = "1.3.6.1.2.1.15.3.1.15"

 [[inputs.snmp.table]]
   name ="cbQos"
   oid = "CISCO-CLASS-BASED-QOS-MIB::cbQosCMStatsTable"

   [[inputs.snmp.table.field]]
     name = "ConfigIndex"
     #oid = "CISCO-CLASS-BASED-QOS-MIB::cbQosConfigIndex"
     oid = "1.3.6.1.4.1.9.9.166.1.5.1.1.2"
     is_tag = true

   [[inputs.snmp.table.field]]
     name = "ParentObjectsIndex"
     #oid = "CISCO-CLASS-BASED-QOS-MIB::cbQosParentObjectsIndex"
     oid = "1.3.6.1.4.1.9.9.166.1.5.1.1.4"
     is_tag = true

 [inputs.snmp.tagdrop]
   Name = [ "SONET*", "Vo*", "Nu0", "E1*", "Lo*", "Vl*", "CH*", "*:*", "St*", "VL*" ]
Раз в 4 часа неспеша c getnext, а не getbulk собрать текст из ifalias. Пробовал писать подобное в clickhouse, чтобы вывести описание интерфейсов в графане.
[[inputs.snmp]]
 interval = "4h"
 agents = [ "device" ]
 version = 2
 max_repetitions = 1
 retries = 1
 community = "public"
 fieldpass = [ "Alias", "Name" ]

 [[inputs.snmp.table]]
  name = "ifalias"

   [[inputs.snmp.table.field]]
    name = "Ifindex"
    #IF-MIB::ifIndex
    oid = "1.3.6.1.2.1.2.2.1.1"
    is_tag = true

   [[inputs.snmp.table.field]]
    name = "Name"
    #IF-MIB::ifName
    oid = "1.3.6.1.2.1.31.1.1.1.1"

   [[inputs.snmp.table.field]]
    name = "Alias"
    #IF-MIB::ifAlias
    oid = "1.3.6.1.2.1.31.1.1.1.18"
Этот конфигурационный файл для сбора информации о модели, серийниках, когда устройство поддерживает entity-mib. Juniper не поддерживает. Тут обратить внимание на index_as_tag, чтобы не потерять информацию о устройствах в стеке, точках доступа, зарегистрированных на контроллере. Т.к. в строке вендора может быть все что угодно (dlink,d-link и тп), то для каждого вендора решил использовать тег MfgName. Дополнительная проверка с ModelName в inputs.snmp.tagpass для фильтрации избыточных данных о портах.
[[inputs.snmp]]
 interval = "24h"
 agents = [ "dlink" ]
 version = 2
 timeout = "2m"
 community = "public"
 fieldpass = [ "entPhysicalDescr" , "entPhysicalName" , "entPhysicalFirmwareRev" , "entPhysicalHardwareRev" , "entPhysicalSerialNum" , "entPhysicalSoftwareRev" ]

 [[inputs.snmp.field]]
  name = "location"
  #SNMPv2-MIB::sysLocation.0
  oid = "1.3.6.1.2.1.1.6.0"
  is_tag = true

 [[inputs.snmp.field]]
  name = "contact"
  #SNMPv2-MIB::sysContact.0
  oid = "1.3.6.1.2.1.1.4.0"
  is_tag = true

 [[inputs.snmp.table]]
  name = "inventory_entity"
  inherit_tags = [ "location", "contact" ]
  #oid = "ENTITY-MIB::entPhysicalTable"
  index_as_tag = true # stack, access points
   [[inputs.snmp.table.field]]
    name = "ModelName"
    #ENTITY-MIB::entPhysicalModelName
    oid = "1.3.6.1.2.1.47.1.1.1.1.13"
    is_tag = true

   [[inputs.snmp.table.field]]
    name = "Class"
    #ENTITY-MIB::entPhysicalClass
    oid = "1.3.6.1.2.1.47.1.1.1.1.5"
    is_tag = true

   [[inputs.snmp.table.field]]
    name = "entPhysicalName"
    #ENTITY-MIB::entPhysicalName
    oid = "1.3.6.1.2.1.47.1.1.1.1.7"

   [[inputs.snmp.table.field]]
    name = "entPhysicalDescr"
    #ENTITY-MIB::entPhysicalDescr
    oid = "1.3.6.1.2.1.47.1.1.1.1.2"

   [[inputs.snmp.table.field]]
    name = "entPhysicalHardwareRev"
    #ENTITY-MIB::entPhysicalHardwareRev
    oid = "1.3.6.1.2.1.47.1.1.1.1.8"

   [[inputs.snmp.table.field]]
    name = "entPhysicalFirmwareRev"
    #ENTITY-MIB::entPhysicalFirmwareRev
    oid = "1.3.6.1.2.1.47.1.1.1.1.9"

   [[inputs.snmp.table.field]]
    name = "entPhysicalSoftwareRev"
    #ENTITY-MIB::entPhysicalSoftwareRev
    oid = "1.3.6.1.2.1.47.1.1.1.1.10"

   [[inputs.snmp.table.field]]
    name = "entPhysicalSerialNum"
    #ENTITY-MIB::entPhysicalSerialNum
    oid = "1.3.6.1.2.1.47.1.1.1.1.11"

 [inputs.snmp.tags]
  MfgName = "dlink"
  obj = "object_id"

 [inputs.snmp.tagpass]
  ModelName = [ "*" ]
Информация в полученных строках бывает кривая, с пробелами в конце, символами, чем грешат sfp и wic модули cisco. Поэтому в telegraf.d дополнительный конфигурационный файл proc_trim.conf. К сожалению, процессоры в телеграфе никак нельзя тестировать.
[[processors.strings]]
 namepass = ["inventory_entity"]
 [[processors.strings.trim]]
  tag = "ModelName"
 [[processors.strings.trim]]
  field = "entPhysicalDescr"
 [[processors.strings.trim]]
  field = "entPhysicalName"
 [[processors.strings.trim]]
  field = "entPhysicalFirmwareRev"
 [[processors.strings.trim]]
  field = "entPhysicalHardwareRev"
 [[processors.strings.trim]]
  field = "entPhysicalSerialNum"
 [[processors.strings.trim]]
  field = "entPhysicalSoftwareRev"
 [[processors.strings.lowercase]]
  tag = "contact"
 [[processors.strings.lowercase]]
  tag = "location"
Вроде бы можно было и сделать обработку, но нет. Запускаешь тест и получаешь:
inventory_entity,Class=9,MfgName=cisco,ModelName=WIC-2T\ \ \ \ \ \ \ \ \ \ \ \ ������
Чем больше устройств опрашивается, тем больше LimitNOFILE требуется указать в /lib/systemd/system/telegraf.service. Необходимо увеличивать metric_buffer_limit в настройках агента в /etc/telegraf/telegraf.conf, когда в логах появляются сообщения о дропах. Размер этого кэша влияет на время хранения данных в момент недоступности базы. Беда, когда нужен мониторинг не метрик, которые в каждый интервал возвращают значение, а событий, когда временной ряд метрики прерывается. Тут показателен мониторинг видеокодеков polycom, которые по snmp отдают данные только в момент сеанса видеоконференцсвязи. Странным образом все перечисленные oid'ы ниже - это строки. Тестовый запуск без преобразования строки в число приводит к тому, что field отсутствует, если строка пустая. Но преобразование делает из пустой строки 0i и все значения пишутся в базу, временной ряд метрики не прерывается. С генератором для snmp_exporter'а так не получится.
[[inputs.snmp]]
  interval = "1m"
  agents = [ "polycom" ]
  version = 2
  community = "public"

  name = "polycom"
  [[inputs.snmp.field]]
   name = "PercentPacketLoss"
   oid = "1.3.6.1.4.1.2684.1.1.21.0"
   conversion = "int"

  [[inputs.snmp.field]]
   name = "Jitter"
   oid = "1.3.6.1.4.1.2684.1.1.22.0"
   conversion = "int"

  [[inputs.snmp.field]]
   name = "Latency"
   oid = "1.3.6.1.4.1.2684.1.1.23.0"
   conversion = "int"

Генератор конфигурации snmp_exporter

В версии 0.20 пока еще нужен генератор для формирования snmp.yml (конфигурационный файл snmp_exporter) из yaml файла generator.yml Из исходиков не получилось собрать в этот раз, поэтому вариант с докером https://hub.docker.com/r/prom/snmp-generator. В примере ниже podman в ubuntu 20.10 Старый генератор искал мибы в домашнем каталоге .snmp/mibs, поэтому архив со всеми необходимыми мибами там. Сам файл generator.yml в .snmp/

cd .snmp/
docker run -it -v "${PWD}:/opt/" prom/snmp-generator generate
Если завершится без ошибок, то сгенериться конфигурационный файл snmp.yml
$ ls
generator.yml  mibs  snmp.yml

generator.yml

Для простоты все устройства, описываемые одним модулем имеют одну и ту же версию snmp и коммутити public. В противном случае необходимо писать множество модулей и далее в конфигурации vmagent'а для каждого такого модуля делать отдельный job

Для примера простой модуль для коммутатора в generator.yml:

modules:

  devicename:
    retries: 1
    walk:
      - 1.3.6.1.2.1.1.3         #sysUpTime
      - 1.3.6.1.2.1.2.2.1.8     #IF-MIB::ifOperStatus
      - 1.3.6.1.2.1.2.2.1.14    #IF-MIB::ifInErrors
      - 1.3.6.1.2.1.2.2.1.19    #IF-MIB::ifOutDiscards
      - 1.3.6.1.2.1.31.1.1.1.6  #IF-MIB::ifHCInOctets
      - 1.3.6.1.2.1.31.1.1.1.7  #IF-MIB::ifHCInUcastPkts
      - 1.3.6.1.2.1.31.1.1.1.10 #IF-MIB::ifHCOutOctets
      - 1.3.6.1.2.1.31.1.1.1.11 #IF-MIB::ifHCOutUcastPkts
      - 1.3.6.1.2.1.31.1.1.1.15 #IF-MIB::ifHighSpeed

    lookups:
      - source_indexes: [ifIndex]
        lookup: 1.3.6.1.2.1.31.1.1.1.1  #ifName
        drop_source_indexes: true
      - source_indexes: [ifIndex]
        lookup: 1.3.6.1.2.1.2.2.1.3     #ifType

Используется только то, что нужно и что можно (строки нельзя, но ссылка внизу). Не требуется собирать вообще все и тем более обе таблицы iftable и ifxtable целиком. Таблица с метриками:

что зачем
sysUpTime для определения времени работы оборудования
ifOperStatus статус интерфейса up/down
ifInErrors общий счетчик входящих ошибок
ifOutDiscards дискарды на портах из-за speed mismatch, qos, и тп
ifHCInOctets 64-битный счетчик входящих байт
ifHCInUcastPkts 64-битный счетчик входящих юникастовых пакетов
ifHCOutOctets 64-битный счетчик исходящих байт
ifHCOutUcastPkts 64-битный счетчик исходящих юникастовых пакетов
ifHighSpeed скорость интерфейса для расчета % утилизации

Таблица с label, которые дополнительно будут у каждой метрики предыдущей таблицы:

что зачем
ifName имя интерфейса из ifxtable вместо ifIndex, который удален drop_source_indexes: true
ifType тип интерфейса для фильтрации метрик в vmagent'е, чтобы исключить сбор данных по логическим интерфейсам

Дропать индекс интерфейсов для некоторых маршрутизаторах cisco нельзя. Например, при опросе 7206VXR с модулями может возникнуть ошибка.

У snmp_exporter'а есть функционал overrides, который можеть дропать метрики. Это необходимо для удаления высококардинальных метрик, например, или мусора.

  n3k:
    retries: 1
    walk:
      - 1.3.6.1.2.1.1.3             # sysUpTime
      - 1.3.6.1.2.1.2.2.1.7         # IF-MIB::ifAdminStatus
      - 1.3.6.1.2.1.2.2.1.8         # IF-MIB::ifOperStatus
      - 1.3.6.1.2.1.2.2.1.9         # IF-MIB::ifLastChange
      - 1.3.6.1.2.1.2.2.1.14        # IF-MIB::ifInErrors
      - 1.3.6.1.2.1.2.2.1.19        # IF-MIB::ifOutDiscards
      - 1.3.6.1.2.1.31.1.1.1.6      # IF-MIB::ifHCInOctets
      - 1.3.6.1.2.1.31.1.1.1.7      # IF-MIB::ifHCInUcastPkts
      - 1.3.6.1.2.1.31.1.1.1.10     # IF-MIB::ifHCOutOctets
      - 1.3.6.1.2.1.31.1.1.1.11     # IF-MIB::ifHCOutUcastPkts
      - 1.3.6.1.2.1.31.1.1.1.15     # IF-MIB::ifHighSpeed
      - 1.3.6.1.2.1.10.7.10         # EtherLike-MIB::dot3PauseTable
      - 1.3.6.1.2.1.15.3.1.2        # BGP4-MIB::bgpPeerState
      - 1.3.6.1.2.1.15.3.1.15       # BGP4-MIB::bgpPeerFsmEstablishedTransitions
      - 1.3.6.1.4.1.9.9.91.1.1.1.1.4    # CISCO-ENTITY-SENSOR-MIB::entSensorValue
      - 1.3.6.1.4.1.9.9.91.1.1.1.1.5    # CISCO-ENTITY-SENSOR-MIB::entSensorStatus
      - 1.3.6.1.4.1.9.9.109.1.1.1.1.7       # CISCO-PROCESS-MIB::cpmCPUTotal1minRev
      - 1.3.6.1.4.1.9.9.109.1.1.1.1.12      # CISCO-PROCESS-MIB::cpmCPUMemoryUsed
      - 1.3.6.1.4.1.9.9.109.1.1.1.1.13      # CISCO-PROCESS-MIB::cpmCPUMemoryFree
      - 1.3.6.1.4.1.9.9.117.1.1.2       # CISCO-ENTITY-FRU-CONTROL-MIB::cefcFRUPowerStatusTable
      - 1.3.6.1.4.1.9.9.117.1.2.1       # CISCO-ENTITY-FRU-CONTROL-MIB::cefcModuleTable
      - 1.3.6.1.4.1.9.9.117.1.4.1       # CISCO-ENTITY-FRU-CONTROL-MIB::cefcFanTrayStatusTable
      - 1.3.6.1.4.1.9.9.42.1.2.10.1.2   # CISCO-RTTMON-MIB::rttMonLatestRttOperSense

    lookups:
      - source_indexes: [ifIndex]
        lookup: 1.3.6.1.2.1.31.1.1.1.1  # ifName
        drop_source_indexes: true
      - source_indexes: [dot3StatsIndex]
        lookup: 1.3.6.1.2.1.31.1.1.1.1  # ifName
        drop_source_indexes: true
      - source_indexes: [ifIndex]
        lookup: 1.3.6.1.2.1.2.2.1.3     # ifType for eth=6
      - source_indexes: [entPhysicalIndex]
        drop_source_indexes: true
        lookup: 1.3.6.1.2.1.47.1.1.1.1.2    # entPhysicalDescr

    overrides:
      entSensorStatus:
        ignore: true

Override DisplayStrings

Бывает что oid имеет тип string, но в него пишутся необходимые для мониторинга значения. Помимо прочего такие oid'ы необходимо отслеживать и игнорировать, либо переделывать. Если оставить как есть, то строки станут label'ми, что плохо скажется на производительности, т.к. увеличивается кардинальность метрик. Решение описано - Numbers from DisplayStrings with the snmp_exporter Ниже пример с коммутаторами extreme, где память и cpu - это string. https://gtacknowledge.extremenetworks.com/articles/Q_A/Is-there-a-way-to-display-the-OID-1-3-6-1-4-1-1916-1-32-1-4-1-9-for-CPU-utilization-to-an-integer-instead-of-a-string EXTREME-SOFTWARE-MONITOR-MIB

  exos:
    retries: 1
    walk:
      - 1.3.6.1.2.1.1.3             # sysUpTime
      - 1.3.6.1.2.1.2.2.1.7         # IF-MIB::ifAdminStatus
      - 1.3.6.1.2.1.2.2.1.8         # IF-MIB::ifOperStatus
      - 1.3.6.1.2.1.2.2.1.9         # IF-MIB::ifLastChange
      - 1.3.6.1.2.1.2.2.1.14        # IF-MIB::ifInErrors
      - 1.3.6.1.2.1.2.2.1.19        # IF-MIB::ifOutDiscards
      - 1.3.6.1.4.1.1916.1.4.14.1.1   # EXTREME-PORT-MIB::extremePortCongDropPkts
      - 1.3.6.1.2.1.31.1.1.1.6      # IF-MIB::ifHCInOctets
      - 1.3.6.1.2.1.31.1.1.1.7      # IF-MIB::ifHCInUcastPkts
      - 1.3.6.1.2.1.31.1.1.1.10     # IF-MIB::ifHCOutOctets
      - 1.3.6.1.2.1.31.1.1.1.11     # IF-MIB::ifHCOutUcastPkts
      - 1.3.6.1.2.1.31.1.1.1.15     # IF-MIB::ifHighSpeed
      - 1.3.6.1.2.1.15.3.1.2        # BGP4-MIB::bgpPeerState
      - 1.3.6.1.2.1.15.3.1.15       # BGP4-MIB::bgpPeerFsmEstablishedTransitions
      - 1.3.6.1.4.1.1916.1.32.1.4.1.8   # EXTREME-SOFTWARE-MONITOR-MIB::extremeCpuMonitorSystemUtilization1min
      - 1.3.6.1.4.1.1916.1.32.2.2       # EXTREME-SOFTWARE-MONITOR-MIB::extremeMemoryMonitorSystemTable
      - 1.3.6.1.4.1.1916.1.1.1.27.1.2       # EXTREME-SYSTEM-MIB::extremePowerSupplyStatus
      - 1.3.6.1.4.1.1916.1.1.1.7        # EXTREME-SYSTEM-MIB::extremeOverTemperatureAlarm
      - 1.3.6.1.4.1.1916.1.1.1.8        # EXTREME-SYSTEM-MIB::extremeCurrentTemperature
      - 1.3.6.1.4.1.1916.1.1.1.9        # EXTREME-SYSTEM-MIB::extremeFanStatusTable

    lookups:
      - source_indexes: [ifIndex]
        lookup: 1.3.6.1.2.1.31.1.1.1.1  # ifName for ifmib and fcfemib
        drop_source_indexes: true
      - source_indexes: [ifIndex]
        lookup: 1.3.6.1.2.1.2.2.1.3     # ifType for eth=6
      - source_indexes: [entPhysicalIndex]
        drop_source_indexes: true
        lookup: 1.3.6.1.2.1.47.1.1.1.1.2    # entPhysicalDescr
    overrides:
      extremeCpuMonitorSystemUtilization1min:
        regex_extracts:
          '':
            - regex: '(.*)'
              value: '$1'
      extremeMemoryMonitorSystemTotal:
        regex_extracts:
          '':
            - regex: '(.*)'
              value: '$1'
      extremeMemoryMonitorSystemFree:
        regex_extracts:
          '':
            - regex: '(.*)'
              value: '$1'
      extremeMemoryMonitorSystemUsage:
        regex_extracts:
          '':
            - regex: '(.*)'
              value: '$1'
      extremeMemoryMonitorUserUsage:
        regex_extracts:
          '':
            - regex: '(.*)'
              value: '$1'

Chain lookup indexes

Есть возможность заменить несколько индексов в oid'е на один. Например, было и стало

# TYPE jnxOperatingTemp gauge
jnxOperatingTemp{jnxOperatingContentsIndex="1",jnxOperatingL1Index="0",jnxOperatingL2Index="0",jnxOperatingL3Index="0"} 0
jnxOperatingTemp{jnxOperatingContentsIndex="12",jnxOperatingL1Index="1",jnxOperatingL2Index="0",jnxOperatingL3Index="0"} 36
jnxOperatingTemp{jnxOperatingContentsIndex="2",jnxOperatingL1Index="1",jnxOperatingL2Index="0",jnxOperatingL3Index="0"} 29
jnxOperatingTemp{jnxOperatingContentsIndex="2",jnxOperatingL1Index="2",jnxOperatingL2Index="0",jnxOperatingL3Index="0"} 32
jnxOperatingTemp{jnxOperatingContentsIndex="4",jnxOperatingL1Index="1",jnxOperatingL2Index="1",jnxOperatingL3Index="0"} 0
jnxOperatingTemp{jnxOperatingContentsIndex="4",jnxOperatingL1Index="1",jnxOperatingL2Index="2",jnxOperatingL3Index="0"} 0
jnxOperatingTemp{jnxOperatingContentsIndex="4",jnxOperatingL1Index="2",jnxOperatingL2Index="1",jnxOperatingL3Index="0"} 0
jnxOperatingTemp{jnxOperatingContentsIndex="4",jnxOperatingL1Index="2",jnxOperatingL2Index="2",jnxOperatingL3Index="0"} 0
jnxOperatingTemp{jnxOperatingContentsIndex="4",jnxOperatingL1Index="3",jnxOperatingL2Index="1",jnxOperatingL3Index="0"} 0
jnxOperatingTemp{jnxOperatingContentsIndex="4",jnxOperatingL1Index="3",jnxOperatingL2Index="2",jnxOperatingL3Index="0"} 0
jnxOperatingTemp{jnxOperatingContentsIndex="7",jnxOperatingL1Index="1",jnxOperatingL2Index="0",jnxOperatingL3Index="0"} 0
jnxOperatingTemp{jnxOperatingContentsIndex="8",jnxOperatingL1Index="1",jnxOperatingL2Index="1",jnxOperatingL3Index="0"} 0
jnxOperatingTemp{jnxOperatingContentsIndex="8",jnxOperatingL1Index="1",jnxOperatingL2Index="2",jnxOperatingL3Index="0"} 0
jnxOperatingTemp{jnxOperatingContentsIndex="9",jnxOperatingL1Index="1",jnxOperatingL2Index="0",jnxOperatingL3Index="0"} 41


# TYPE jnxOperatingTemp gauge
jnxOperatingTemp{jnxOperatingDescr="CB 0"} 36
jnxOperatingTemp{jnxOperatingDescr="FPC: MPC @ 0/*/*"} 0
jnxOperatingTemp{jnxOperatingDescr="Fan Tray 0 Fan 0"} 0
jnxOperatingTemp{jnxOperatingDescr="Fan Tray 0 Fan 1"} 0
jnxOperatingTemp{jnxOperatingDescr="Fan Tray 1 Fan 0"} 0
jnxOperatingTemp{jnxOperatingDescr="Fan Tray 1 Fan 1"} 0
jnxOperatingTemp{jnxOperatingDescr="Fan Tray 2 Fan 0"} 0
jnxOperatingTemp{jnxOperatingDescr="Fan Tray 2 Fan 1"} 0
jnxOperatingTemp{jnxOperatingDescr="PEM 0"} 29
jnxOperatingTemp{jnxOperatingDescr="PEM 1"} 32
jnxOperatingTemp{jnxOperatingDescr="PIC: 4XQSFP28 PIC @ 0/0/*"} 0
jnxOperatingTemp{jnxOperatingDescr="PIC: 8XSFPP PIC @ 0/1/*"} 0
jnxOperatingTemp{jnxOperatingDescr="Routing Engine"} 41
jnxOperatingTemp{jnxOperatingDescr="midplane"} 0
Это достигается следующей конфигурацией
  mx:
    retries: 1
    walk:
      - 1.3.6.1.2.1.1.3             # sysUpTime
      - 1.3.6.1.2.1.2.2.1.14        # IF-MIB::ifInErrors
      - 1.3.6.1.2.1.31.1.1.1.6      # IF-MIB::ifHCInOctets
      - 1.3.6.1.2.1.31.1.1.1.10     # IF-MIB::ifHCOutOctets
      - 1.3.6.1.2.1.15.3.1.2        # BGP4-MIB::bgpPeerState
      - 1.3.6.1.2.1.15.3.1.15       # BGP4-MIB::bgpPeerFsmEstablishedTransitions
      - 1.3.6.1.4.1.2636.3.5.2.1.4  # JUNIPER-FIREWALL-MIB::jnxFWCounterPacketCount
      - 1.3.6.1.4.1.2636.3.5.2.1.5  # JUNIPER-FIREWALL-MIB::jnxFWCounterByteCount
      - 1.3.6.1.4.1.2636.3.1.13     # https://kb.juniper.net/InfoCenter/index?page=content&id=KB17526
                                    # https://kb.juniper.net/InfoCenter/index?page=content&id=KB15467
    lookups:
      - source_indexes: [ifIndex]
        lookup: 1.3.6.1.2.1.31.1.1.1.1  # ifName
        drop_source_indexes: true
      - source_indexes: [ifIndex]
        lookup: 1.3.6.1.2.1.2.2.1.3     # ifType
      - source_indexes: [jnxOperatingContentsIndex,jnxOperatingL1Index,jnxOperatingL2Index,jnxOperatingL3Index]
        lookup: 1.3.6.1.4.1.2636.3.1.13.1.5     # jnxOperatingDescr
        drop_source_indexes: true

HEX in string

Бывает необходимость поменять тип, в противном случае данные в label отображаются в hex. Описано тут Why is my SNMP string showing as hexadecimal?. Ниже пример для мониторинга гипервизора esxi

  esxi:
    retries: 1
    walk:
      - 1.3.6.1.2.1.25.1.1              # hrSystemUptime
      - 1.3.6.1.2.1.25.2.3.1.5          # hrStorageSize
      - 1.3.6.1.2.1.25.2.3.1.6          # hrStorageUsed
      - 1.3.6.1.2.1.25.3.3              # hrProcessorTable
      - 1.3.6.1.2.1.25.5.1.1.2          # hrSWRunPerfMem
      - 1.3.6.1.2.1.25.4.2.1.7          # hrSWRunStatus

    lookups:
      - source_indexes: [hrStorageIndex]
        lookup: 1.3.6.1.2.1.25.2.3.1.2    # hrStorageType
        drop_source_indexes: false
      - source_indexes: [hrSWRunIndex]
        lookup: 1.3.6.1.2.1.25.4.2.1.2    # hrSWRunName
        drop_source_indexes: false
    overrides:
      hrSWRunName:
        type: DisplayString
Мониторинг RAM в метриках на примере ниже.
hrStorageSize{hrStorageType="1.3.6.1.2.1.25.2.1.2"}
hrStorageUsed{hrStorageType="1.3.6.1.2.1.25.2.1.2"}

проверка работы экспортера

snmp.yml копируется на хост с exporter'ом, reload. Можно самостоятельно делать запросы для тестирования новых модулей без риска записать лишнее в базу.

curl localhost:9116/snmp?module=mx_v2\&target=mx_host

vmagent

snmp_exporter'у нельзя указать несколько параметров. Есть два способа описать job'ы в конфигурации. Первый, где все ограничивается одним job'ом:

- job_name: snmp
  honor_timestamps: true
  metrics_path: /snmp
  scheme: http
  http_sd_configs:
  - url: xxxx
    follow_redirects: false
    refresh_interval: 3m
  relabel_configs:
...
  - source_labels: [__meta_netbox_module]
    separator: ;
    regex: (.*)
    target_label: __param_module
    replacement: $1
    action: replace
...
Второй, где у каждого job'а есть еще специфичные таргеты, relabel настройки:
  - job_name: 'n3k'
    metrics_path: /snmp
    params:
      module: [n3k]
    file_sd_configs:
        - files:
            - 'targets/n3k.yml'
    metric_relabel_configs:
      - source_labels: [ifType]
        regex: '^[^6]$|\d{2,}'
        action: drop
      - source_labels: [ifName]
        regex: 'mgmt0'
        action: drop
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: "fdqn.local:9116"

  - job_name: 'exos'
    metrics_path: /snmp
    params:
      module: [exos]
    file_sd_configs:
        - files:
            - 'targets/exos.yml'
    metric_relabel_configs:
      - source_labels: [ifType]
        regex: '^[^6]$|\d{2,}'
        action: drop
      - source_labels: [ifName]
        regex: 'Management'
        action: drop
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: "fdqn.local:9116"

  - job_name: 'mds9k'
    metrics_path: /snmp
    params:
      module: [mds9k]
    file_sd_configs:
        - files:
            - 'targets/mds9k.yml'
    metric_relabel_configs:
      - source_labels: [ifType]
        regex: '^6$'
        action: drop
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: "fdqn.local:9116"

  - job_name: 'mx'
    metrics_path: /snmp
    params:
      module: [mx]
    file_sd_configs:
        - files:
            - 'targets/mx.yml'
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: "fdqn.local:9116"
Таргеты (хосты) можно описать с лейблами и без. Вот с дополнительными лейблами:
---
- labels:
    rack: 8
    region: 1
  targets:
    - s0002
    - s0003
Без лейблов просто список таргетов:
---
- targets:
    - s0002
    - s0003
У vmagent'а есть rewrite функционал, где можно своими руками описать дескрипшены, добавить еще один label hw или desc, а не собирать их с оборудования. Пример файла, который указан как remoteWrite_relabelConfig:
---
- source_labels: [instance,ifName]
  regex: "s0001;Ethernet1/47"
  target_label: "hw"
  replacement: "s0003"
- source_labels: [instance,ifName]
  regex: "s0002;Ethernet1/48"
  target_label: "hw"
  replacement: "s0004"
- source_labels: [instance,ifName]
  regex: "s0007;1:48"
  target_label: "hw"
  replacement: "m9"
У vmagent'а есть url http://fdqn.local:8429/targets, где видно наглядно за какое время и с каким успехом удалось опросить snmp_exporter по каждому хосту.

Язык запросов

MetricQL и PromQL

Expand VictoriaMetrics WITH templates to canonical PromQL

Все описанные метрики получены vmagent'ом с snmp_exporter'а, записаны в базу VictoriaMetrics.

Для вывода всех метрик, полученных по job'у job_name выполнить:

max({job="job_name"}) by (__name__)

Ниже будут выражения (expr) для vmalert с кратким описанием

ifmib

flapping физического интерфейса, который в up, но его состояние изменилось до этого

(changes(ifLastChange{ifType="6"}[15m]) > 0) and (ifOperStatus == 1) and ON(instance) (sysUpTime > 150000)
Перезагружался коммутатор. Иначе есть шанс что какой-то ifLastChange больше, а каунтер sysUpTime обнулился. Если еще были labels, как у меня, например, rack, то by (instance,rack)
(max(ifLastChange{ifType="6"} < 20000) by (instance)) and ON(instance) (sysUpTime < 150000)
Нет линка на административно включенном интерфейсе
(ifAdminStatus == 1) and (ifOperStatus == 2)
Включился flowcontrol. Полезно для портов, куда подключены стораджи
rate(dot3HCInPauseFrames[5m]) > 5
Входящие ошибки
rate(ifInErrors[15m]) > 5
Дискарды
rate(ifOutDiscards[15m]) > 100
Утилизация пропускной полосы в зависимости от указанной bw или физической скорости интерфейса. 62500 = 1e6 / 8 / 2, где 2 - rate 50%. В примере ниже 72% потому что круглое число
rate(ifHCOutOctets{ifType="6"}[10m]) > ((ifHighSpeed{ifType="6"})*90000)

bgp

flapping сессии

 changes(bgpPeerFsmEstablishedTransitions[15m]) > 0
Статус не established до конкретного нейбора
bgpPeerState{bgpPeerRemoteAddr="9.8.7.6",instance="r0001"} < 6

специфичные для extreme метрики

Питание

extremePowerSupplyStatus !=2
Блок вентиляторов
extremeFanOperational > 1
Перегрев
extremeOverTemperatureAlarm < 2
cpu
extremeCpuMonitorSystemUtilization1min > 15

состояние ИБП APC

Статус. 2 - ОК. 3 - на батареях. 12 - напряжение выше 250В на входе.

upsBasicOutputStatus{instance="apc_ups"} != 2

Enum из миба для понимания о чем речь

  1: unknown
  2: onLine
  3: onBattery
  4: onSmartBoost
  5: timedSleeping
  6: softwareBypass
  7: "off"
  8: rebooting
  9: switchedBypass
  10: hardwareFailureBypass
  11: sleepingUntilPowerReturn
  12: onSmartTrim