Перейти к публикации
iT4iT.CLUB

Kitsum

Пользователи
  • Публикации

    424
  • Зарегистрирован

  • Посещение

  • Дней в лидерах

    234

Сообщения, опубликованные пользователем Kitsum


  1. Update 02.12.2015

    • Статья переработана и перепроверена
    • Актуально для Optiboot 6.2 и Arduino IDE 1.6.6 или выше.

    Рано или поздно приходится столкнуться с необходимостью замены bootloader (загрузчика) микроконтроллера. В моём случае остро потребовался функционал загрузчика Optiboot, из-за реализованного в нем сторожевого таймера (watchdog). Плюс ко всему, он весит в четыре раза меньше стандартного и освобождает дополнительно 1.5kb памяти для скетча!

    Сразу хочу отметить, что Optiboot залит по умолчанию на Arduino UNO и Mega 2560.

    У меня в наличии имелись: Arduino Nano и Arduino Pro Mini на чипах ATmega328P-AU, и просто пачка "голых" ATmega328P-PU. Есть несколько простых способов перезаписать загрузчик которыми я воспользовался (это далеко не все возможные варианты).

    Использование программатора USBasp

    Как по мне, это самый простой способ не требующий никакой магии. Сам программатор можно приобрести у братьев Китайцев за $2. Он представляет из себя плату с ATmega8 или ATmega128, USB на входе и 10Pin ICSP интерфейс на выходе. От стандартного 6Pin интерфейса он отличается лишь тремя дополнительными пинами GND и одним не использующимся NC. Чтобы упростить себе жизнь, советую сразу брать переходник с USBasp 10pin to 6pin за $0.5

    Выглядит это следующим образом

    USBasp_1.jpg

    USBasp_2.jpg

    Сама распиновка программатора и разъемов (позаимствована у братьев наших)

    USBasp_PinoutVisual.jpg

    Если на плате Arduino распаян 6pin разъем ICSP, то подключение до безобразия простое. Совмещаем одноименные контакты программатора с платой Arduino. Если готового разъема нет, то используем соответствующие ноги на плате Arduino или на самом микроконтроллере.

    Необходимые ноги:

    1. GND
    2. VCC
    3. MOSI
    4. MISO
    5. RST

    Распиновка некоторых плат Arduino и микроконтроллеров находится в соседней теме

    Скрытый текст

    USBasp_3.jpgUSBasp_4.jpg

    Пример подключения USBasp к Arduino Pro Mini и к ATmega328P-PU. В случае с последним, конденсаторы можно выкинуть из схемы.

    Скрытый текст

    USBasp_pro_mini.png

    USBasp_atmega328p-pu.png

    Скрытый текст

    USBasp_6.jpgUSBasp_5.jpg

    Теперь самое интересное

    1. Переходим на GitHub проекта Optiboot (ссылка имеется в начале поста) и скачиваем архив со всем необходимым.
    2. Переходим в каталог где хранятся созданные вами скетчи (в Windows это "\Мои документы\Arduino\") и создаем в нем ветку каталогов \hardware\optiboot\avr\
    3. В скачанном архиве находим каталог optiboot и переносим все его содержимое в каталог созданный в предыдущем пункте.
    4. Среди всех перенесенных файлов находим boards.txt удаляем его. Он является устаревшим и более нас не интересует!
    5. Находим файл boards-1.6.txt и переименовываем в boards.txt, в итоге должно получится как на изображении ниже
      Скрытый текст

      Arduino_optiboot_1.png

       

    6. Перезапускаем срезу Arduino IDE
    7. Проверяем список плат во вкладке "Инструменты" и наслаждаемся результатом
    Скрытый текст

    optiboot_1.png

    optiboot_2.png

    optiboot_3.png

     

    При хранении всех необходимых файлов вне среды разработки Arduino IDE последующее обновление самой среды не позволит Вам потерять настройки и файлы загрузчика Optiboot в отличие от прямого вмешательства в оригинальный boards.txt

    Ленивые могут забрать готовый архив со всеми изменениями и распаковать его содержимое в папку со скетчами. В данном случае все достаточно только перезапустить среду разработки. Сам архив прикреплен к посту.

    Выбираем программатор USBasp

    optiboot_4.png

    И жмем "Записать загрузчик".

    ВАЖНО: Если желаете залить скетч через USBasp, то необходимо нажимать "Записать\Вгрузить" при зажатой клавиши SHIFT. Только в этом случае будет выбран программатор для заливки, а соответствующая подсказка изменится за "Загрузить через программатор".

    Arduino_optiboot_4.png

    В ходе данной операции могут сыпаться ошибки указывающие на проблемы с USBasp.

    Цитата

    avrdude: warning: cannot set sck period. please check for usbasp firmware update.

    Попросту игнорируем их!

    Использование Arduino в качестве программатора

    Нам понадобится вторая плата Arduino. Прошиваем её скетчем ArduinoISP, идущим по умолчанию со средой разработки. После этого Arduino становится полноценным программатором.

    // pin name:    not-mega:         mega(1280 and 2560)
    // slave reset: 10:               53
    // MOSI:        11:               51
    // MISO:        12:               50
    // SCK:         13:               52

    В скетче имеется подсказка с способом подключения.

    Скрытый текст

    Arduino_isp_1.png

    Скрытый текст

    Arduino_isp_2.jpg

    Изменяем в Arduino IDE программатор на  Arduino as ISP, выбираем необходимую плату, к которой подключен наш вновь испеченный программатор и "прожигаем" загрузчик.

    Arduino_isp_3.png

    Если необходимо записать какой либо скетч, то делаем это также при зажатой клавиши SHIFT (как и в первом варианте).

    PS: По умолчанию в среде разработки имеются файлы загрузчика optiboot и скачивать ничего не нужно. Достаточно выбрать плату Arduino UNO и перезаписать загрузчик. Но в этом случае Ваше детище станет определяться именно как UNO, даже если на самом деле это NANO, Pro Mini или еще что-то.

    hardware.zip

    • Like 1

  2. Данный пост является шпаргалкой из собранных в сети различных материалов связанных с распиновкой (pinout) распространенных микроконтроллеров и плат на их основе.

    Микроконтроллеры

    ATmega328p-pu

    Скрытый текст

    Atmega328P-PU.thumb.png.1701da0396de316c

    ATmega328p-au

    Скрытый текст

    Atmega328P-AU.thumb.png.db7299316d0d61c1

    ATmega2560

    Скрытый текст

    ATMEGA2560U.thumb.png.348d460cf61b1c2181

    ATmega32u4-au

    Скрытый текст

    atmega32u4-au.thumb.png.079db8ccff4b3877

    Attiny

    Скрытый текст

    attiny.png.1aab35cee53c1501804720212f722

    Платы

    Arduino UNO

    Скрытый текст

    arduino_uno.thumb.jpg.ff036c92479856679c

    Arduino NANO

    Скрытый текст

    arduono-nano.thumb.png.60210eb9259fe6eaf

    Arduino PRO-MINI

    Скрытый текст

    pro_mini.thumb.png.f5ddc4081b0ab088412f3

    Arduino MICRO

    Скрытый текст

    arduino_micro.thumb.png.20eb4270b8971d50

    Arduino MEGA

    Скрытый текст

    arduino_mega.thumb.png.b3efe3fb39fb4f531

    Arduino LEONARDO

    Скрытый текст

    arduino_leonardo.thumb.png.16302e7fe7b72 

    Arduino Yun

    Скрытый текст

    Arduino-Yun.png.3f9ce62fcdeb0db2ecd9dea5

    Arduino Esplora

    Скрытый текст

    arduino-esplora.thumb.png.e719a3cccd0e06

    Arduino Fio

    Скрытый текст

    arduino-fio.thumb.png.f97c06db6113012eb0

     

    • Like 2

  3. Это первая часть и соответственно первая версия метеостанции. Постепенно будем её улучшать обвешивая всяческими лампочками, свистульками и т.п. Текущий вариант протестирован и стабильно работает с июля 2015.

    Использованы:

    1. Копия Arduino NANO на базе микроконтроллера ATmega328p
    2. Ethernet shield на базе чипа ENC28J60
    3. Датчик влажности AM2302, известный в народе как DHT22
    4. Барометр/Термометр GY-68 на базе сенсора фирмы BOSH - BMP180

    Все заказывалось в Китае. Думаю не имеет смысла кидать ссылки т.к они могут быть не актуальны через некоторое время после публикации.

    Также Вас может заинтересовать вариант метеостанции на ESP8266

    ВАЖНО!

    Я использовал в скетче сторожевой таймер (Watchdog timer). Для этого необходимо перепрошить загрузчик Arduino NANO (ATmega328p) со стандартного на Оptiboot (как это сделать описано тут). Если залить выложенный ниже скетч в неподготовленный контроллер, получите "кирпич".

    Если Вы читаете эту часть уже после того как залили скетч, то необходимо перезапустить микроконтроллер по питанию (и только по питанию) и у Вас будет 8 сек. на перепрошивку его другой программой. Иначе "селяви", что в переводе с древнегреческого - НЕ ПОВЕЗЛО! Процедуру можно повторять сколько угодно раз, пока не восстановите беднягу.

    Вступительная часть окончена, переходим к делу

    Станция разделена на две части

    Первая находится дома, состоит из микроконтроллера и сетевого модуля, её задача производить все необходимые вычисления и держать связь с проектом "Народный мониторинг".

    Скрытый текст

    2015-07-26 11.05.04.jpg

    Вторая часть состоит из двух датчиков описанных выше и размещенных на улице в продуваемом пластиковом корпусе, защищенным от прямых попаданий воды и солнечных лучей.

    Скрытый текст

    2015-07-26 11.05.16.jpg

    На самом деле есть стандарты для размещения погодных станций и ни одно из изделий используемых в быту (пусть даже самое дорогое и крутое) никогда не покажет Вам точных данных. Это связано с местом её установки. Погодная станция должна стоять в чистом поле на высоте пары метров от земли и т.д и т.п. Вы это легко найдете в сети. Ну а нас интересуют приблизительные значения.

    Схема этого безобразия, за исключением Ethernet модуля (это просо "бутерброд"), выглядит так.

    arduino_weather_station1.png

    На схеме использовал датчик BMP085 т.к другого я не нашел в Fritzing. На практике стоит BMP180 и подключен по 5V а не по 3.3V Т.к изучение китайской платы показало, что на борту имеется преобразователь.

    Скрытый текст

    2015-09-26 22.54.46.jpg

    Датчик DHT22 имеет обвязку в виде SMD резистора 0805 на 10kOm между двумя ногами - питание и 1-Wire. Но братья Китайцы могут продать датчик уже в обвязке по значительно завышенной ценой.

    Скрытый текст

    2015-07-05 20.10.13.jpg

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

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

    Если следовать ГОСТу, то сенсоры должны быть размещены в тени в течение всего дня.

    Скрытый текст

    2015-07-05 20.09.56.jpg

    Скрытый текст

    2015-07-05 20.10.05.jpg

    Переходим к самому вкусному

    Необходимо зарегистрироваться на http://narodmon.ru/ и добавить Ваше устройство. В качестве идентификатора, чтобы не запутать себя, Вы можете использовать MAC адрес Ethernet модуля. В данном случае мы будем передавать данные по TCP на 8283 порт сервера. Телеграмма будет состоять из:

    1. Идентификатора устройства (MAC адрес)
    2. Названия устройства
    3. GPS координаты для позиционирования на карте проекта (т.к станция стационарна, координаты указаны в скетче без использования GPS модуля)
    4. Показания со всех датчиков

    Программная часть

    Понадобятся библиотеки:

    1. BMP180 (использует библиотеку BMP085) bmp085.zip
    2. DHT22 https://github.com/adafruit/DHT-sensor-library
    3. UIPEthernet https://github.com/ntruchsess/arduino_uip
    4. 1-Wire http://playground.arduino.cc/Learning/OneWire

    PayPal.png

    Скетч

    Скрытый текст
    
    #include <avr/wdt.h>
    #include <Wire.h>
    #include <BMP085.h>
    #include "DHT.h"
    #include <UIPEthernet.h>
    
    #define DHTTYPE DHT22
    #define DHTPIN 5
    
    DHT dht(DHTPIN, DHTTYPE);
    
    byte mac[] = {0x54, 0x34, 0x31, 0x31, 0x31, 0x31};
    char server[] = "narodmon.ru";
    int port = 8283;
    
    IPAddress ip(192,168,0,201);
    EthernetClient client;
    
    BMP085 dps = BMP085();
    long Temperature = 0, Pressure = 0;
    
    bool interval = true;
    
    void setup() {
      // Инициализация сторожевого таймера (Watchdog timer)
      wdt_disable();
      delay(8000);
      wdt_enable(WDTO_8S);
      // Инициализация консоли
      Serial.begin(9600);
      // Инициализация датчика DHT
      dht.begin();
      // Стартуем сеть, если не дождались данных с DHCP сервера то
      // присваеваем себе адрес самостоятельно
      if (Ethernet.begin(mac) == 0) Ethernet.begin(mac, ip);
      // Инициализация 1-Wire
      Wire.begin();
      delay(200);
      
      // Инициализация BMP180 с корректировкой высоты
      // dps.init(MODE_STANDARD, 3200, true);
      
      // Инициализация BMP180
      dps.init();
    
      // Отправляем первые данные сразу после включения устройства
      send_info();
    }
    // dewPoint function NOAA
    // reference (1) : http://wahiduddin.net/calc/density_algorithms.htm
    // reference (2) : http://www.colorado.edu/geography/weather_station/Geog_site/about.htm
    double dewPoint(double celsius, double humidity)
    {
      // (1) Saturation Vapor Pressure = ESGG(T)
      double RATIO = 373.15 / (273.15 + celsius);
      double RHS = -7.90298 * (RATIO - 1);
      RHS += 5.02808 * log10(RATIO);
      RHS += -1.3816e-7 * (pow(10, (11.344 * (1 - 1/RATIO ))) - 1) ;
      RHS += 8.1328e-3 * (pow(10, (-3.49149 * (RATIO - 1))) - 1) ;
      RHS += log10(1013.246);
      // factor -3 is to adjust units - Vapor Pressure SVP * humidity
      double VP = pow(10, RHS - 3) * humidity;
      // (2) DEWPOINT = F(Vapor Pressure)
      double T = log(VP/0.61078);   // temp var
      return (241.88 * T) / (17.558 - T);
    }
    // delta max = 0.6544 wrt dewPoint()
    // 6.9 x faster than dewPoint()
    // reference: http://en.wikipedia.org/wiki/Dew_point
    double dewPointFast(double celsius, double humidity)
    {
      double a = 17.271;
      double b = 237.7;
      double temp = (a * celsius) / (b + celsius) + log(humidity*0.01);
      double Td = (b * temp) / (a - temp);
      return Td;
    }
    
    void send_info() {
      float H, dP, dPt;
      bool fail = true;
    
      while(fail) {
        // Пытаемся считать данные с датчика влажности DHT до тех пор, пока не получим 
        // результат. В 90% случаев все работает нормально, но нам нужны 100%
        if((H = dht.readHumidity()) >= 0) {
          // Получение влажности и температуры с датчика BMP180
          dps.getPressure(&Pressure);
          dps.getTemperature(&Temperature);
          // Подсчитываем точку росы, если температура на улице выше 0 градусов Цельсия
          // и ожидаем результат выше 0, в противном случае выводим 0. Это необходимо
          // чтобы не вводить в заблуждения в зимее время года.
          dP = Temperature>0?((dPt=dewPoint(Temperature*0.1, H))<0?0:dPt):0;
          // Подключаемся к серверу "Народный мониторинг"
          if(client.connect(server, port)) {
            // Начинаем передачу данных
            // адрес_устройства_в_проекте, имя_устройства, GPS широта, GPS долгота
            client.print(F("#fe-31-31-0e-5a-3b#Arduino Nano#71.344699#27.200014\n"));
            // температура
            client.print(F("#T0#"));
            client.print(Temperature*0.1);        
            client.print(F("#Температура\n"));
            // Давление
            client.print("#P1#");
            client.print(Pressure/133.3);
            client.print(F("#Давление\n"));
            // Влажность
            client.print("#H1#");
            client.print(H);
            client.print(F("#Влажность\n"));
            // Точка росы
            client.print("#T1#");
            client.print(dP);
            client.print(F("#Точка росы\n"));
            // Отправляем конец телеграммы
            client.print("##");
    
            // Даем время отработать Ethernet модулю и разрываем соединение
            delay(200);
            client.stop();
            // Останавливаем цикл, если передача завершена
            fail = !fail;
            break;
          }
          delay(250);
        }
        delay(250);
      }
    }
    
    void loop()
    {
      // Каждые 3 секунды сбрасываем сторожевой таймер микроконтроллера
      // Каждые 6 минут отправляем данные на "Народный мониторинг"
      if(!(millis()/1000%30)) wdt_reset();
      if(!(millis()/1000%360)) {
        if(interval) {
          send_info();
          interval = false;
        }
      }
      else interval = true; 
    }

     

    Данные о температуре снимаются с датчика BOSH, они более точны.

    Точка росы рассчитывается исходя из текущих показаний температуры и влажности. В коде имеются две функции взятые с просторов интернета:

    1. dewPoint
    2. dewPointFast

    Использовать можно любую. Также имеется проверка показаний точки росы. Вычисления не производятся при минусовой температуре и значение обнуляется при показаниях ниже нуля. Т.к это уже не точка росы, а точка образования инея. Если я не прав, то прошу меня поправить.

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

    dP = Temperature>0?((dPt=dewPoint(Temperature*0.1, H))<0?0:dPt):0;

    на

    dP = dewPoint(Temperature*0.1, H);

    На сайте проекта имеются ссылки на приложения под разные платформы. Я использую Android и для примера взял два разных виджета. Выглядит это следующим образом.

    Скрытый текст

    2015-09-27 11.25.39.png

    В дальнейшем планирую:

    1. Увеличить разнообразие датчиков
    2. Объединить обе части метеостанции и вынести все на улицу
    3. Уйти от ENC28J60 в пользу W5100 или W5500
    4. Использовать POE для питания всей кухни

    PS: пока все доволен. Посмотрим как конструкция переживет зиму и сделаем дополнительные выводы.

    bmp085.zip

    • Like 2

  4. mysql_slow_query.thumb.jpg.f08359cf3dcde

    На самом деле тривиальная задача, но задумался я о ней только после того, как товарищи из соседнего отдела начали бегать сломя голову вокруг нагруженного сервера с базой. Что-то работало не так...

    Через некоторое время выяснилось, что на КПД системы влияют медленные запросы, и они крутятся не один год. В один прекрасный день система, многократно окупающая сервер, на котором стоит, стала захлебываться.

     

    А ведь проблему можно было решить еще до её проявления, не прибегая к мозговому штурму.

    Начинаем действовать

    Беглое погружение в просторы интернета приводят нас на официальный сайт MySQL где черным по "Русски" написано что в my.cnf имеется соответствующий раздел позволяющий настроить логирование для медленных запросов.

    http://dev.mysql.com/doc/refman/5.7/en/slow-query-log.html

    # Here you can see queries with especially long duration
    log_slow_queries        = /var/log/mysql/mysql-slow.log
    long_query_time = 2

    Тут все ясно с одного взгляда. Превысил установленный лимит времени на выполнение, улетел в лог.

    Остается дело за малым - отследить изменения в файле и поднять тревогу

    В Zabbix имеется набор функций для отслеживания log файлов, но я не вижу смысла читать его построчно. Думаю, что достаточно просто смотреть на контрольную сумму. Для этих целей в Zabbix предусмотрена функция vfs.file.cksum, которой необходимо передать путь до файла.

    В нашем случае ключ выглядит следующим образом

    vfs.file.cksum[/var/log/mysql/mysql-slow.log]

    Останется только создать триггер проверяющий, отличаются ли два последних значения друг от друга и поднимающий тревогу, если они различаются.

    {<template-name>:vfs.file.cksum[/var/log/mysql/mysql-slow.log].diff()}=1

    Значение <template-name> замените на имя Вашего шаблона, если вы решили добавить отслеживание медленных запросов mysql к уже имеющемуся у Вас шаблону.

    На выходе получаем

    sql_slow_query2.thumb.png.c0aa424a991092

    Замечание

    Если перезапустить сервер баз данных, то поднимется тревога т.к в лог свалится запись о перезапуске сервера. Но этого можно избежать если дополнительно контролировать время прошедшее с момента запуска mysqld.

    PS: конечно я понимаю, что лепить это в отдельный шаблон смешно, но вдруг у кого ни-ть возникнут трудности =)

    MySQL-Slow.zip

    • Like 1

  5. Необычные проблемы требуют необычных решений.

    Tunnel_bouwen_cartoon.thumb.jpg.190c8d38

    Начнем с прелюдии

    На определенном удалении друг от друга имеются две локальные домашние сети. Во главе одной стоит маршрутизатор на базе стационарного компьютера с RouterOS на борту, а сердцем второй является Cisco 800-ой серии (далее просто Кошка). В каждой из сетей имеются ресурсы представляющие интерес друг для друга. И вот настал прекрасный момент,  была приобретена статика, и начался процесс поднятия GRE тоннеля. Все завелось с первого раза, пакетики побежали между сетями. Данное событие было отмечено, как подобает Русскому представителю IT сообщества и все разошлись заниматься своими делами.

    Но счастье было не долгим. Через пару дней, из-за не преодолимых обстоятельств, произошло отключение питания на стороне "Кошки" и MikroTik начал скучать и ждать своего боевого товарища. После проведения аварийно-восстановительных работ "Кошка" радостно заурчала, но пакетики не побежали. 

    Вскрытие показало, что провайдер не поддерживает адекватную работу с DHCP семейства кошачьих и в панике выдавал первый свободный IP из пула, со всеми вытекающими последствиями. Несколько недель велись переговоры с провайдером, и раз эта статья была написана, значит, провайдер пока не добился положительного результата.

    Решено разбираться самостоятельно и попробовать поднимать тоннель, основываясь только на одном статическом адресе. Ведь в цирке медведи ездят на одноколесных велосипедах? А чем мы хуже медведей?!

    Реализация

    Кошка будет гулять по всему пулу адресов провайдера, следовательно, приспосабливаться к этим загулам придется MiktoTik-у. Средствами RouterOS решить эту проблему сходу не получилось и решено вводить в бой тяжелую артиллерию, это сверх мощный и производительный сервер Raspberry Pi :$ А Вы, что подумали?) Эта игрушка с 512mb RAM, 800мГц CPU, Lunux-ом на борту и питанием от USB разъема самого MikroTik-а очень сильно расширяет функционал маршрутизатора, не повышая расходы на электроэнергию и общую стоимость проекта. 

    Если быть серьезным, то MikroTik API + Raspberry Pi (далее просто Малина) позволяют реализовать практически любую Вашу фантазию.

    Фото моего роутера

    DSC_0382.thumb.jpg.9a01371273d962d6518e2DSC_0387.thumb.jpg.257a52ba00f7f089191e5

    DSC_0385.thumb.jpg.6c1943f1d67cb1b3d1fb1

    DSC_0381.thumb.jpg.74f0b91cfae38c892ab21

    Поведение будет следующим:

    1. На MikroTik-е настраивается проброс 80 порта до Малины.
    2. Cisco каждые N минут будет обращаться по протоколу http на Raspberry Pi и методом GET передавать секретный ключ.
    3. На борту Малинки крутится Apache + PHP. Последний проверяет корректность ключа, реализуя идентификацию кошки по схеме "Свой\Чужой".
    4. Если идентификация пройдена успешно, то по средствам API маршрутизатора происходит перенастройка GRE тоннеля.

    Хочу отметить, что таким способом можно производить конфигурацию всего маршрутизатора, а не только GRE.

    Проброс портов на MikroTik-е

    /ip firewall nat add action=netmap chain=dstnat disabled=no dst-port=80 in-interface=ether1 protocol=tcp to-addresses=192.168.1.100 to-ports=80
    1. in-interface - имя интерфейса смотрящего в интернет
    2. to-addresses - внутренний адрес малинки

    Не забудьте активировать сервис API у маршрутизатора.

    /ip service enable 5 

    Настройка Cisco

    cisco-test#configure terminal
    cisco-test(config)#kron policy-list gre
    cisco-test(config-kron-policy)#cli more http://64.64.64.64/?key=a7656fafe94dae72b1e1487670148412
    cisco-test(config-kron-policy)#exit
    cisco-test(config)#kron occurrence gre in 015 recurring
    cisco-test(config-kron-occurrence)#policy-list gre
    cisco-test(config-kron-occurrence)#^Z
    cisco-test#write
    
    cisco-test#more ?
      /ascii    Display binary files in ascii
      /binary   Force display to hex/text format
      /ebcdic   Display binary files in ebcdic
      archive:  File to display
      cns:      File to display
      flash:    File to display
      ftp:      File to display
      http:     File to display
      https:    File to display
      null:     File to display
      nvram:    File to display
      rcp:      File to display
      scp:      File to display
      system:   File to display
      tar:      File to display
      tftp:     File to display
      xmodem:   File to display
      ymodem:   File to display
    

    В данном случае настраивается задание для планировщика KRON на выполнение команды more каждые 15 минут. Сама команда просто выводит на экран содержимое из различных источников. Естественно необходимо изменить IP адрес на актуальный для Вас.

    Значение key необходимо изменить на Ваш ключ, указанный в PHP скрипте ниже и закодированный в md5!

    Еще один важный момент. В GET запросе, символ вопроса "?" обозначает, что начинается передача параметров. В Cisco это спец символ показывающий справку по командам и чтобы его ввести, необходимо перед его написанием нажать Ctrl + V

    PHP Script для Raspberry Pi

    Нам понадобится PHP class для работы с RouterOS API https://github.com/BenMenking/routeros-api

    <?PHP
    /**
     * @copyright: iT4iT.CLUB (c) 2015
     * @author: https://it4it.club
     */
    
    # Данные для подключения к API RouterOS
    $host = '192.168.0.1';
    $login = 'login';
    $pass = 'password';
    
    # Уникальный ключ для проверки "Свой\Чужой". Удаленный маршрутизатор обязан передавать его в md5
    $key = 'secret key';
    
    # Интерфейс, который необходимо переконфигурировать
    $interfaceName = 'gre-tunnel1';
    
    if(isset($_GET['key']) and $_GET['key'] == md5($key)) {
        require('./routeros_api.class.php');
        
        $API = new RouterosAPI();    
        if($API->connect($host, $login, $pass)) {
            $API->write('/interface/gre/print');
            foreach($API->read() as $id => $param) {
                if($param['name'] == $interfaceName and $param["remote-address"] != $_SERVER['REMOTE_ADDR']) {
                    $API->comm("/interface/gre/set", array(
                        "numbers" => $id, 
                        "remote-address" => $_SERVER['REMOTE_ADDR'],
                    ));
                    break;
                }
            }        
            $API->disconnect();
        }
    }
    ?>

    В данном скрипте при совпадении ключей происходит поиск интерфейса gre-tunnel1 в ветке /interface/gre и в случае его обнаружения следует изменение параметра remote-address на IP клиента передавшего верный ключ в формате md5.

    Более подробно почитать об API можно на официальном сайте http://wiki.mikrotik.com/wiki/Manual:API

    PS: Данный вариант управления маршрутизатором довольно забавен и очень гибок, но будьте очень осторожны, "друзья" Эдварда не дремлют! Советую также использовать https, фильтр по IP (провайдера) в коде скрипта и в фаерволе MikroTik-а.

    routeros_cisco_rpi.zip

    • Like 1

  6. Управление микроконтроллером Arduino через Ethernet получает все большую популярность. Я сам активно использую микроконтроллеры в сети для сбора различной информации на удаленных объектах. И в один прекрасный день натолкнулся на мысль, что изменять настройки моих поделок можно только перезаписывая микропрограмму, что не совсем удобно. Намного приятнее производить конфигурацию и менять поведение контроллера через Web интерфейс. Но последовала мысль, что любой может набрать в браузере адрес моей железки и оказаться там, где его не ждут, со всеми вытекающими последствиями! Нужно сделать хотя бы картонную дверь.

    DSC_0328.thumb.jpg.88049c3a98a4fe772b689DSC_0331.thumb.jpg.7136e9efea18a8646db13

    Идея есть, приступаем к реализации. 

    Т.к работа с моими игрушками происходит через Web браузер, то логично воспользоваться стандартными средствами авторизации заложенными в большинство браузеров. Забегая вперед скажу, что у меня все заработало в браузерах: chrome, firefox и opera. Через ишака не завелось, но он живет своей жизнью и меня не интересует (даже разбираться не стал). В остальных не проверял, но думаю, что все будет работать.

    Механизм выглядит следующим образом

    При запросе страницы, браузер (например Opera) отправляет множество различной информации на Web сервер. 

    GET / HTTP/1.1
    User-Agent: Opera/9.80 (Windows NT 6.1; U; ru) Presto/2.10.229 Version/11.60
    Host: 10.10.10.2
    Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
    Accept-Language: ru-RU,ru;q=0.9,en;q=0.8
    Accept-Encoding: gzip, deflate
    Connection: keep-alive

    В свою очередь мы ожидаем во всем этом шлаке увидеть заголовок Authorization: Basic <encoded-key> и естественно его не видим. Делаем правильный вывод - клиент не авторизован. Отправляем пользователю заголовок который заставит его браузер отобразить форму авторизации.

    HTTP/1.0 401 Unauthorized
    WWW-Authenticate: Basic realm="Arduino - iT4iT.CLUB"

    arduino_web_auth.thumb.png.af90cb0fe91e3

    После заполнения пользователем соответствующих форм и отправки данных на сервер, мы получаем тот же шлак, но в нем присутствует интересующий нас заголовок.

    GET / HTTP/1.1
    User-Agent: Opera/9.80 (Windows NT 6.1; U; ru) Presto/2.10.229 Version/11.60
    Host: 10.10.10.2
    Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
    Accept-Language: ru-RU,ru;q=0.9,en;q=0.8
    Accept-Encoding: gzip, deflate
    Authorization: Basic bG9naW46cGFzc3dvcmQ=
    Connection: keep-alive

    При дальнейшем изучении вопроса, выясняется, что уникальный ключ bG9naW46cGFzc3dvcmQ= является ничем иным как строкой закодированной в Base64. И при её преобразовании в читаемый вид получаем login:password. Это и есть наши данные разделенные спец. символом ":".

    Для работы с Base64, для Arduino имеется сторонняя библиотека с одноименным названием и забрать ей можно тут: https://github.com/adamvr/arduino-base64

    У меня в наличие имеются Ethernet модули на чипах W5100 и ENC28J60 для Arduino Uno и Nano соответственно. Они имеют серьезные отличия и требуют для работы различные библиотеки. К примеру W5100 , в отличие от ENC28J60, имеет аппаратную поддержку TCP/IP: TCP, UDP, IPv4, ICMP, ARP, IGMP, MAC. Следовательно в ENC28J60 вся эта кухня должна быть реализована программно, что отразится на свободной памяти микроконтроллера.

    W5100

    DSC_0332.thumb.jpg.3760124f669f5a65753db

    Для работы требуются библиотеки Ethernet.h и SPI.h. Поставляется совместно со средой разработки Arduino. Скетч для микроконтроллера будет выглядеть следующим образом:

    /*
      sign in (authentication) to the Web server Arduino
      (с) http://it4it.club
    */
    #include <SPI.h>
    #include <Ethernet.h> // Used for Ethernet
    #include <Base64.h>
    /*
      Настройки сети
    */
    byte mac[] = { 0x54, 0x34, 0x41, 0x30, 0x30, 0x32 };
    IPAddress ip(10, 10, 10, 11);                        
    
    EthernetServer server(80);
    /*
      Переменные для авторизации через web
    */
    String readString;                // Буфер для данных от пользователя Web сервера
    String auth_hash;                 // Тут храним полноценную строку авторизации, которую ожидаем от браузера в таком виде "Authorization: Basic bG9naW46cGFzc3dvcmQ="
    char login[] = "login";           // Логин по дефолту. В дальнейшем необходимо измененные данные авторизации в Web морде хранить в EPROM
    char password[] = "password";     // Пароль по умолчанию. -/-
    /*
      SETUP
    */
    void setup() {
      Serial.begin(9600);
      // Подготавливаем строку авторизации
      auth_hash = auth_update(login, password);
      // Поднимаем сеть  
      Ethernet.begin(mac, ip);
      server.begin();
      Serial.println(Ethernet.localIP());
    }
    /*
      LOOP
    */
    void loop() {
      EthernetClient client = server.available();
      if (client) {  
        boolean currentLineIsBlank = true;
        while (client.connected()) {
          if (client.available()) {
            char c = client.read();
            readString += c;
            if (c == '\n' && currentLineIsBlank) {
              if (readString.lastIndexOf(auth_hash)>-1) {
                if (readString.lastIndexOf("GET /favicon.ico")>-1) {
                  client.println(F("HTTP/1.0 404 Not Found"));
                }
                else {
                  client.println(F("HTTP/1.0 200 OK"));
                  client.println(F("Content-Type: text/html"));
                  client.println(F("Pragma: no-cache\r\nRefresh: 30\r\n"));
                  client.println(F("<html><head><meta charset=UTF-8\"><title>Arduino - iT4iT.CLUB</title>"));
                  client.println(F("<style>body,table,tr,td{font-style:normal;font-family:verdana;font-size:11px;}body{background-color:#FFEBD5;}</style></head><body>"));
                  client.print(F("Arduino on <a href=\"https://it4it.club\">iT4iT.CLUB</a>"));
                  client.println(F("</body></html>"));
                }
              }
              else {
                client.println(F("HTTP/1.0 401 Unauthorized"));
                client.println(F("WWW-Authenticate: Basic realm=\"Arduino - iT4iT.CLUB\""));
              }
              break;
            }
    
            if (c == '\n') {
              currentLineIsBlank = true;
            }
            else if (c != '\r') {
              currentLineIsBlank = false;
            }
          } 
        }
        delay(30);
    
        readString = "";
        client.stop();
      }
    }
    /*
      Функция подготавливает строку авторизации с хэшем учетных данных, для сравнения её с данными в HEADER браузера которые мы получим
    */
    String auth_update(char login[], char password[]) {
      // Строка для будущего хэша
      String hash = strcat(strcat(login, ":"),password);
      int buf_size = hash.length()+1;
      char buf_char[buf_size];
      // Переводим строку в char для дальнейшей обработки base64
      hash.toCharArray(buf_char, buf_size);
      char encoded[base64_enc_len(buf_size-1)];
      // Получаем хэш для авторизации
      base64_encode(encoded, buf_char, buf_size-1);
      // Формируем полную строку поиска "Authorization: Basic <BASE64-HASH>" чтобы не могли подсунуть хэш в GET или POST
      return "Authorization: Basic " + String(encoded);
    }

     

    ENC28J60

    Для данного чипа требуется сторонняя библиотека. Я выбрал UIPEthernet.h, забрать можно тут: https://github.com/ntruchsess/arduino_uip 

    Для работы подойдет предыдущий скетч, необходимо лишь изменить подключаемые библиотеки:

    /*
      sign in (authentication) to the Web server Arduino
      (с) http://it4it.club
    */
    #include <UIPEthernet.h> // Used for Ethernet
    #include <Base64.h>

    На этом пока все, всем удачи с экспериментами!

    PS: Не забывайте изменить IP в скетче.

    • Like 2

  7. Думаю, учитывая тот факт, что данная запись позиционируется как шпаргалка, начинать с заурядных вступлений с описанием, что такое SNMP бессмысленно. Основное можно прочитать на wikipedia

    В моем случае SNMP протокол используется для мониторинга различного оборудования Ubuntu Server'ом. 

    В Debian установка выглядит весьма тривиально

    sudo apt-get install snmp

    Если необходим еще и демон, то

    sudo apt-get install snmp snmpd

    В пакет snmp также входит всем известная утилита snmpwalk

    файлы конфигурации (из названия понятно за что отвечает каждый из них)

    1. /etc/snmp/snmp.conf
    2. /etc/snmp/snmpd.conf
    3. /etc/snmp/snmptrapd.conf

    По умолчанию, после установки, snmpwalk может работать только с цифровыми именами, что может вызвать неудобство для восприятия человека.

    snmpwalk -v 2c -c public 127.0.0.1 iso.3.6.1.2.1.1.3.0

    Если заглянуть в snmp.conf, то там красуется печальный, но вполне обоснованный заголовок

    # As the snmp packages come without MIB files due to license reasons, loading
    # of MIBs is disabled by default. If you added the MIBs you can reenable
    # loading them by commenting out the following line.

    Что нам требуется, то и загружаем самостоятельно в каталог /usr/share/mibs, с последующим занесением имени базы в данный файл.

    Можно установить дополнительный пакет который, по нашему требованию, будет закачивать различные MIB'ы.

     sudo apt-get install snmp-mibs-downloader

    Для обновления списка баз используем

     sudo /usr/bin/download-mibs

    Выглядит это следующим образом

    Downloading documents and extracting MIB files.
    This will take some minutes.
    
    In case this process fails, it can always be repeated later by executing
    /usr/bin/download-mibs again.
    
    RFC1155-SMI: 119 lines.
    RFC1213-MIB: 2613 lines.
    NOTE: SMUX: ignored.
    SMUX-MIB: 158 lines.
    CLNS-MIB: 1294 lines.
    RFC1381-MIB: 1007 lines.
    RFC1382-MIB: 2627 lines.
    RFC1414-MIB: 131 lines.
    SNMPv2-PARTY-MIB: 1410 lines.
    SNMPv2-M2M-MIB: 807 lines.
    MIOX25-MIB: 708 lines.
    PPP-LCP-MIB: 764 lines.
    PPP-SEC-MIB: 289 lines.
    PPP-IP-NCP-MIB: 203 lines.
    PPP-BRIDGE-NCP-MIB: 429 lines.
    FDDI-SMT73-MIB: 2126 lines.
    TOKEN-RING-RMON-MIB: 2302 lines.
    SOURCE-ROUTING-MIB: 450 lines.
    DECNET-PHIV-MIB: 3030 lines.
    DSA-MIB: 642 lines.
    DPI20-MIB: 47 lines.
    IBM-6611-APPN-MIB: 5112 lines.
    DNS-SERVER-MIB: 1078 lines.
    DNS-RESOLVER-MIB: 1196 lines.
    UPS-MIB: 1899 lines.
    CHARACTER-MIB: 646 lines.
    RS-232-MIB: 788 lines.
    PARALLEL-MIB: 286 lines.
    SNA-NAU-MIB: 2765 lines.
    SIP-MIB: 1099 lines.
    Modem-MIB: 1340 lines.
    ...
    

     

    После данной процедуры каталог /usr/share/mibs будет под завязку полон.

    В файле конфигурации можно указать какие MIB'ы использовать просто перечислив их

     
    mibs :SNMPv2-MIB:IP-MIB:IF-MIB:UPS-MIB

    или для использования всех, указать

    mibs :ALL
    

     

    Теперь можно использовать иерархическое пространство в запросах. 

    snmpwalk -v 2c -c public 127.0.0.1 SNMPv2-MIB::sysUpTime.0

    На мой взгляд, в каталоге /usr/share/mibs стоит держать только те MIB'ы которые необходимы для работы. Можно создавать собственные базы или использовать те, что предоставляет изготовитель используемого Вами оборудования.

    Что касается демона snmpd, то по умолчанию он обслуживает только кольцевой ip со стандартным community public. Раскомментируем следующую строку для доступа через любой сетевой интерфейс или укажем явно необходимый адрес.

    agentAddress udp:161,udp6:[::1]:161

    Для изменения/добавления community смотрим в ветку ACCESS CONTROL.

    Формат записи

    [[rocommunity[6]]|[rwcommunity[6]] [community name] [source [OID | -V VIEW [CONTEXT]]]
    1. rocommunity - доступ на чтение
    2. rwcommunity - полный доступ
    rocommunity secret1 192.168.1.10
    rwcommunity secret2 192.168.1.10
    rocommunity secret3 192.168.1.11 .1.3.6.1.2.1.1.3.0

    Все изменения применяются только после перезапуска демона

    service snmpd restart

    Подробное описание можно найти тут http://www.net-snmp.org/docs/man/snmpd.conf.html

    PS: об остальных возможностях поговорим позже.

    • Like 1

  8. Push уведомления - очень удобное, даже жизненно необходимое, дополнение к серверу. В любом месте на планете мы получаем оперативную информацию с поля боя.

    Для реализации задуманного есть очень удобный ресурс pushbullet.com с простым API.

    Забегая немного вперед, сразу покажу как это хозяйство будет выглядеть.

    Android

    s1.thumb.png.067c83fe1d55a363ce62574de66 s2.thumb.png.2105f437a4c1afe59434ffd2308

    Windows

    s4.thumb.png.64fbb3de4e1978d082ae74974ef

    s3.thumb.png.15b0c0f712b43798f63df297ec3

    На момент написания статьи, сервис поддерживает следующие платформы для получения push уведомлений:

    1. Android
    2. iOS
    3. Windows
    4. Chrome
    5. Firefox
    6. Opera
    7. Safari

    Думаю, что в скором времени и Mac добавится к списку.

    И так, к делу

    Все работы я проводил на Linux Ubuntu Server 4.8 и Zabbix 2.4, но описанное подойдет для любой ОС семейства UNIX, а под Windows лишь с небольшими модификациями.

    Первым делом нам необходимо перейти на сайт pushbullet (ссылка в начале поста) и пройти регистрацию или авторизоваться под одной из предоставленных систем: google, facebook ... В общем есть из чего выбирать.

    Привязать необходимую платформу для получения уведомлений можно в разделе Devices -> Add device. Для установки Вас перенаправят на ресурс соответствующий вашему выбору. Устанавливаем, авторизуемся и возвращаемся на сайт в раздел Settings ->  Account. Ищем графу Access Token и, если у Вас хорошая память, запоминаем или переписываем секретный ключ.

    Самое тяжелое позади, переходим к изучению API

    Необходимый багаж знаний мы можем получить в соответствующем разделе сайта docs.pushbullet.com

    Раздел Pushes гласит, что мы можем отправлять запросы методом POST/GET и использовать следующие типы запросов с сопутствующими параметрами:

    На момент написания статьи, ссылка по которой реализована работа с API выглядит так - https://api.pushbullet.com/v2/pushes

    В документации приведен пример для отправки тестового push уведомления методом POST с использованием Curl в формате Json.

    curl --header 'Authorization: Bearer <your_access_token_here>' \
    -X POST https://api.pushbullet.com/v2/pushes \
    --header 'Content-Type: application/json' \
    --data-binary '{"type": "note", "title": "Note Title", "body": "Note Body"}'

    Для теста замените <your_access_token_here> на свой секретный ключ.

    Это довольно удобно, но у меня возникли проблемы в реализации связки Zabbix -> Bash -> Curl+Json, при которой наблюдались перебои в работе. Поэтому я выкинул из связки Json.

    Устремляемся на сервер

    Идем в каталог со скриптами оповещений Zabbix (в Linux это /usr/local/share/zabbix/alertscripts) и создаем там файл с любым именем, но для удобства понимания назовем его pushbullet.

    Это будет простенький Bash скрипт со следующим содержимым:

    #!/bin/bash
    
    API_URL="https://api.pushbullet.com/v2/pushes"
    API_KEY="$1"
    SUBJECT="$2"
    MESSAGE="$3"
    
    curl $API_URL -u $API_KEY: -d type=note -d title="$SUBJECT" -d body="$MESSAGE" -X POST
    

    Переходим в Zabbix

    В разделе Администрирование -> Способы оповещения и добавляем новый способ со следующими параметрами:

    1. Имя - pushbullet
    2. Тип - Скрипт
    3. Имя скрипта - pushbullet

    Идем в раздел Настройка -> Действия -> Источник событий: Триггеры и создаем новое действие с именем Report problems to Pushbullet.

    Активируйте и отредактируйте Сообщение по умолчанию и Сообщение о восстановлении, это поможет получать как список самих проблем так и сообщения при их устранении. Далее в разделе Операции указываем Тип операции: отправлять сообщение и выбираем отправлять только через pushbullet.

    Можно отредактировать через стандартный шаблон оповещений, но отдельный шаблон позволяет создать конкретное сообщение, что очень удобно т.к например на телефоне не большой дисплей и плавать во всем шлаке данных о проблеме будет не удобно. Что выбрать решать только Вам.

    Теперь самое вкусно. Переходим в раздел Администрирование -> Пользователи и выбираем нашу учетную запись. Во вкладке Оповещения добавляем новое оповещение со следующими параметрами:

    1. Типpushbullet
    2. Отправлять на - указываем <your_access_token_here>

    Остальные параметры по желанию, но по личному опыту советую изменить активность на Ваше рабочее время. Я уже получал посреди ночи пулеметную очередь уведомлений с последующими одиночными выстрелами.

    Таким образом настраиваются уведомления для всех необходимых пользователей.

    Все готово к работе.

    PS: По желанию Вы можете модифицировать скрипт и профили пользователей для получения уведомлений на конкретные устройства или распределить между устройствами оповещения в зависимости от текущего времени дня (на рабочий телефон в рабочее время, на домашний компьютер/телефон в свободное). Выбор остается за Вами.

    • Like 1

  9. Необходимо ограничить доступ к TFTP серверу на который сохраняются различные конфигурационные файлы. В моем случае на сервер ходят только маршрутизаторы со статическими ip адресами. Следовательно доступ органическим существам необходимо строго-настрого запретить.

    В Linux имеются два замечательных файла, названия которых говорят сами за себя:

    1. /etc/hosts.allow
    2. /etc/hosts.deny

    Синтаксис файлов:

    <служба или ALL>: <IP, имя хоста или подсеть>

    Я использую демона tftpd для организации TFTP сервера.

    Идем в /etc/hosts.allow и добавляем в конец файла секцию описывающую каким хостам разрешено работать с демоном:

    # ---------------------------------------------------------------------------
    # TFTP
    # ---------------------------------------------------------------------------
    
    # router 1
    in.tftpd: 10.10.10.1
    # router 2
    in.tftpd: 10.10.10.2
    # router 3
    in.tftpd: 10.10.10.3
    # router 4
    in.tftpd: 10.10.10.4

    Теперь идем в /etc/hosts.deny и добавляем секцию в которой красуется короткая и лаконичная запись "ты кто такой давай до свидания":

    # ---------------------------------------------------------------------------
    # TFTP
    # ---------------------------------------------------------------------------
    
    in.tftpd: ALL

    Естественно Вы можете указать любую службу: smtp, http, mysqld, sshd ...

    Spawn

    Позволяет выполнить код при наступление определенного события соответствующего правилам описанным выше.

    Добавим логирование событий чтобы знать о всех неудачных попытках подключения к TFTP серверу. Модифицируем /etc/hosts.deny

    # ---------------------------------------------------------------------------
    # TFTP
    # ---------------------------------------------------------------------------
    
    in.tftpd: ALL : spawn /bin/echo $(/bin/date +"%%d-%%m-%%y %%T") отказано в доступе %h>>/var/log/tftp.access.log

    Можно использовать следующие макросы:

    1. %a  Адрес клиента
    2. %A  Адрес сервера
    3. %c  Информация о клиенте: user@host, user@address, имя хоста или просто адрес
    4. %d  Имя демона/процесса
    5. %h  Имя хоста клиента или адрес, если имя хоста недоступно
    6. %H  Имя хоста сервера или адрес, если имя хоста недоступно
    7. %n  Имя хоста клиента (либо 'unknown' или 'paranoid')
    8. %N  Имя хоста сервера (либо 'unknown' или 'paranoid')
    9. %p  Идентификатор процесса (PID)
    10. %s  Информация о сервере: daemon@host, daemon@address или просто имя демона
    11. %u  Имя пользователя клиента (или 'unknown')
    12. %%  Одиночный символ '%'

    Теперь мы будем знать о всех неудачных попытках подключения к TFTP серверу.

    Полученный в итоге файл можно скормить Zabbix'у и сигнализировать при его изменении администратору сервера!

    • Like 1

  10. Еще один интересный вариант, это воспользоваться планировщиком задач.
    Любопытно, что CRON у CISCO называется KRON! Ну, Бог с ним, лишь бы работал.

    Делаем копию текущей конфигурации раз в день, в час ночи:

    cisco-test#configure terminal
    cisco-test(config)#kron policy-list put_config_tftp
    cisco-test(config-kron-policy)#cli show startup-config | redirect tftp://10.10.10.10/cisco-test-running-config
    cisco-test(config-kron-policy)#exit
    cisco-test(config)#kron occurrence daily at 1:00 recurring
    cisco-test(config-kron-occurrence)#policy-list put_config_tftp
    cisco-test(config-kron-occurrence)#^Z
    cisco-test#write

     

    • Like 1

  11. Для данной процедуры использую автоматическое архивирование которое происходит при записи изменений в NVRAM.
    Архивы хранятся на удаленном TFTP сервере.

    Настройка маршрутизатора:

    Router>enable
    Router#configure terminal
    Enter configuration commands, one per line.  End with CNTL/Z.
    Router(config)#archive
    Router(config-archive)#path tftp://10.10.10.10/OBJ-NAME/$H
    Router(config-archive)#write-memory
    Router(config-archive)#^Z
    Router#write
    1. OBJ-NAME - папка с именем агрегата\помещения\т.п (должна уже быть на TFTP сервере) для удобства поиска т.к это все потом будет съедать другая программа
    2. $H - имя устройства (устройство знает этот макрос как и $T - время создания архива)
    3. write-memory - создание архива происходит при изменении (записи в nvram) конфигурации

    Можно сделать автоматическое создание архивов раз в день или как душе угодно

    Router(config-archive)#time-period 1440

    Просмотр созданных архивов

    Router#show archive

    Сравнение архивов

    Router#show archive config differences tftp://10.10.10.10/router-1  tftp://10.10.10.10/router-2

    Восстановление конфигурации из множества источников

    Router#configure replace ?
      archive:    URL of config file that will replace running-config
      cns:        URL of config file that will replace running-config
      flash:      URL of config file that will replace running-config
      ftp:        URL of config file that will replace running-config
      http:       URL of config file that will replace running-config
      https:      URL of config file that will replace running-config
      null:       URL of config file that will replace running-config
      nvram:      URL of config file that will replace running-config
      rcp:        URL of config file that will replace running-config
      scp:        URL of config file that will replace running-config
      system:     URL of config file that will replace running-config
      tar:        URL of config file that will replace running-config
      tftp:       URL of config file that will replace running-config
      usbflash0:  URL of config file that will replace running-config
      xmodem:     URL of config file that will replace running-config
      ymodem:     URL of config file that will replace running-config

    TFTP

    Router#configure replace tftp://10.10.10.10/router-1

    USB

    Router#configure replace usbflash0:router-1

     

    • Like 1

  12. Мониторинг ИБП через USB с помощью пакета apcupsd.

    Все манипуляции производились на операционной системе Linux Ubuntu 13.10 и Zabbix 2.2

    Установка проста до невозможности:

    sudo apt-get install apcupsd

    После установки погружаемся в конфиг:

    nano /etc/apcupsd/apcupsd.conf

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

    # Имя ИБП, может быть любым
    UPSNAME ups-name
    # Используемый кабель
    UPSCABLE usb
    # Тип подключения.
    UPSTYPE usb
    # Закомментируем строку указанную по умолчанию
    #DEVICE /dev/ttyS0
    # Указываем как часто сохранять информацию в "статус-файл", по умолчанию 0 и говорит о том, что файл создан не будет
    STATTIME 30
    # Куда сохранять информацию о статусе ИБП, оставляем по умолчанию, но путь до файла запомнили
    STATFILE /var/log/apcupsd.status

    Далее необходимо организовать сбор и передачу данных на zabbix. Захламлять конфиг демона десятками UserParameter нет совершенно никакого желания. Душа просит эстетики и мы начинаем конструировать велосипед.
    Нам понадобится скрипт который будет посредником между zabbix агентом и apcupsd. Для подобных фокусов у меня имеется каталог "/usr/local/etc/scripts/".
    Создаем там файл "/usr/local/etc/scripts/apcupsd.status.sh" права 750 владелец root группа zabbix

    Сам скрипт. Сильно впечатлительным и отцам программирования придется пролить слезу:

    #!/bin/bash
    if [[ -z "$1" || -z "$2" ]]; then
      exit 1
    fi
    if [ -s "$1" ]; then
      if [[ "$2" =~ DATE|VERSION|CABLE|DRIVER|UPSMODE|STARTTIME|MODEL|LASTXFER|STATFLAG|FIRMWARE|'END APC' ]]; then
        QUERY=`cat "$1" | grep -iw "$2" | cut -d':' -f 2,3,4,5 | head -n1`
      else
        QUERY=`cat "$1" | grep -iw "$2" | cut -d':' -f 2 | awk '{print \$1}'`
      fi
      if [ "$2" = "STATUS" ]; then
        if [[ "${QUERY}" = *ONLINE* ]]; then
          echo "1"
        else
          echo "0"
        fi
      else
        echo "${QUERY}"
      fi
    else
      exit 1
    fi

    Обращаю внимание, что в файле "/var/log/apcupsd.status" используется разделитесь ":", в то время как этот же символ может фигурировать и в самих данных, что приведет к потери части строки.
    В довесок к этому, некоторые данные отдаются с описанием их единиц измерения, а нам необходимо передать в zabbix эти значения как INT или FLOAT т.к если получать их как STRING, то мы не сможем ими манипулировать в дальнейшем. Например использовать в собственных расчетах и формулах. 
    В общем, для всех этих фильтраций мы используем cut, head и awk. Как обрабатывать тот или иной параметр задано в условии формирующем переменную QUERY.
    Параметр STATUS отдаёт нам значение имеющее тип STRING и не подходящее нам для использования в графиках или других расчетах. Из тех вариантов, что мы получаем, нас интересует только определение типа его работы (от сети или от аккумулятора), для этого мы преобразуем для zabbix STRING в INT и отдаём "1" (работа от сети) или "0" (работа от батареи).

    ВАЖНО: Я использовал не все параметры указанные в спецификации apcupsd. Для более подробной информации и добавлению нужных Вам данных стоит почитать тут http://www.apcupsd.org/manual/manual.html

    Велосипед готов.
    Идем в конфиг агента "/usr/local/etc/zabbix_agentd.conf" и добавляем в конце файла строку которая будет отвечать за сбор параметров у скрипта:

    UserParameter=ups[*],/usr/local/etc/scripts/apcupsd.status.sh "/var/log/apcupsd.status" "$1"

    Тут все просто:

    1. ups[*] - массив в котором будут храниться данные от ИБП и по совместительству являться ключами в zabbix
    2. /usr/local/etc/scripts/apcupsd.status.sh - путь до скрипта
    3. "/var/log/apcupsd.status" - путь до "статус-файла" apcupsd
    4. "$1" - запрашиваемый параметр

    Перезапускаем zabbix агент и идем добавлять шаблон для всей этой кухни.
    Сам шаблон описывать сильно не буду, а просто добавлю его к посту.

    GR1.thumb.png.9cbaece977970a40507d1dafb9GR2.thumb.png.63cd8fd6f23ea4b8442caa80a8GR3.thumb.png.ea4ce63577b3f895176efbd398GR4.thumb.png.c97392a71a83843649d48bf32fGR5.thumb.png.d5c1c6da1f3f99cc729c73f4bc

    PS: На данный момент я отказался от подключения через USB и использую только SNMP описанный в посте выше. Но данный вариант отлично подходит для ИБП без сетевого интерфейса. Можно легко представить ситуацию, что ИБП отвечает за работу автоматизированной станции где нибудь в малообитаемой местности и для доступа к нему используется Raspberry Pi с установленным Zabbix Agent'ом. Хм... дешево и сердито, учитывая стоимость "APC Network Management Card".

    template_APCUPSD.xml

    apcupsd.status.zip

    • Like 1

  13. Речь пойдет о мониторинге источников бесперебойного питания, в моём случае SMART-UPS 5000 RM, по протоколу SNMP. Версия Zabbix 2.4. Использовать Ethernet для этих целей очень выгодно т.к это избавляет от лишнего звена в виде компьютера подключенного по COM или USB, но ИБП должен быть укомплектован сетевой платой Apc Network Management Card (NMC).

    У меня все прекрасно заработало на ИБП: SMART-UPS 1000\1500\5000\8000. Уверен, что все получится и с другими моделями.

    Скрытый текст

    DSC_0001.thumb.jpg.c086a4a76249c92ff3194DSC_0003.thumb.jpg.19217de27e9a1fcade4a8

    Доступное мне оборудование использует NMC: AP9617, AP9619, AP9631.

    Скрытый текст

    DSC_0324.thumb.jpg.b95ce597245a900bb95c3

    DSC_0325.thumb.jpg.509b5f354e8ad4fdfe876

    DSC_0326.thumb.jpg.7300b52ebad1aa06c6084 

    И так, к делу.
    Первоначально необходимо произвести сброс настроек NMC.
    На лицевой стороне NMC есть еле заметное отверстие с красующейся надписью Reset. Ковырять там, разогнутой скрепкой, можно на работающем устройстве.

    Я использовал два вида кабелей

    1. COM to COM - ID 940-1524D
    2. USB to COM - ID 940-0272A

    Для подключения можно использовать как специализированный софт, так и обычный терминал. Я использовал PuTTY, что и Вам советую. Со скоростью поэкспериментируйте (у меня диалог с железкой завязался на 2400).

    После подключения жмем Enter и появляется запрос на авторизацию. По умолчанию логин и пароль: apc
    Настройка реализована в виде диалога с вариантами ответа как в дешевой квест-рпг игре.

    Пример начального квеста:

    Скрытый текст
    
    User Name : apc
    Password  : ***
    
    
    American Power Conversion               Network Management Card AOS      v2.1.0
    (c) Copyright 2002 All Rights Reserved  Smart-UPS & Matrix-UPS APP       v2.1.0
    -------------------------------------------------------------------------------
    Name      : Unknown                                   Date : 01/02/2011
    Contact   : Unknown                                   Time : 01:07:35
    Location  : Unknown                                   User : Administrator
    Up Time   : 101 Days 15 Hours 12 Minutes              Stat : P+ N- A+
    
    Smart-UPS 5000 RM named UPS_IDEN : On Line
    
    ------- Control Console -------------------------------------------------------
    
         1- Device Manager
         2- Network
         3- System
         4- Logout
    
         <ESC>- Main Menu, <ENTER>- Refresh, <CTRL-L>- Event Log
    > 2
    
    ------- Network ---------------------------------------------------------------
    
         1- TCP/IP
         2- DNS
         3- Ping Utility
         4- FTP Server
         5- Telnet/SSH
         6- Web/SSL/TLS
         7- WAP
         8- SNMP
         9- Email
        10- Syslog
    
         <ESC>- Back, <ENTER>- Refresh, <CTRL-L>- Event Log
    > 1
    
    ------- TCP/IP ----------------------------------------------------------------
    
            Network not started. Invalid BOOTP response.
            -----------------------------------------------------------------------
            MAC Address : 00 00 00 00 00 00
    
            * Manually reboot to restart network. System->Tools->Reboot.
    
         1- Boot Mode  : DHCP & BOOTP
         2- Advanced...
    
         <ESC>- Back, <ENTER>- Refresh, <CTRL-L>- Event Log
    > 1
    
    ------- Boot Mode -------------------------------------------------------------
    
         1- DHCP & BOOTP
         2- DHCP only
         3- BOOTP only
         4- Manual
    
         <ESC>- Back, <ENTER>- Refresh
    > 4
    
    ------- TCP/IP ----------------------------------------------------------------
    
            Network not started. Invalid BOOTP response.
            -----------------------------------------------------------------------
            MAC Address : 00 00 00 00 00 00
    
            * Changes will take effect on Logout.
    
         1- System IP      : 0.0.0.0
         2- Subnet Mask    : 0.0.0.0
         3- Default Gateway: 0.0.0.0
         4- Boot Mode      : Manual
         5- Advanced...
    
         <ESC>- Back, <ENTER>- Refresh, <CTRL-L>- Event Log
    > 1
    System IP : 10.10.10.244
    
    ------- TCP/IP ----------------------------------------------------------------
    
            Network not started. Invalid BOOTP response.
            -----------------------------------------------------------------------
            MAC Address : 00 00 00 00 00 00
    
            * Changes will take effect on Logout.
    
         1- System IP      : 10.10.10.244
         2- Subnet Mask    : 0.0.0.0
         3- Default Gateway: 0.0.0.0
         4- Boot Mode      : Manual
         5- Advanced...
    
         <ESC>- Back, <ENTER>- Refresh, <CTRL-L>- Event Log
    > 2
    Subnet Mask : 255.255.255.0
    
    ------- TCP/IP ----------------------------------------------------------------
    
            Network not started. Invalid BOOTP response.
            -----------------------------------------------------------------------
            MAC Address : 00 00 00 00 00 00
    
            * Changes will take effect on Logout.
    
         1- System IP      : 10.10.10.244
         2- Subnet Mask    : 255.255.255.0
         3- Default Gateway: 0.0.0.0
         4- Boot Mode      : Manual
         5- Advanced...
    
         <ESC>- Back, <ENTER>- Refresh, <CTRL-L>- Event Log
    > 3
    Default Gateway : 10.10.10.1
    
    ------- TCP/IP ----------------------------------------------------------------
    
            Network not started. Invalid BOOTP response.
            -----------------------------------------------------------------------
            MAC Address : 00 00 00 00 00 00
    
            * Changes will take effect on Logout.
    
         1- System IP      : 10.10.10.244
         2- Subnet Mask    : 255.255.255.0
         3- Default Gateway: 10.10.10.1
         4- Boot Mode      : Manual
         5- Advanced...
    
         <ESC>- Back, <ENTER>- Refresh, <CTRL-L>- Event Log
    > 5
    
    ------- Advanced... -----------------------------------------------------------
    
            Link is down.
    
         1- Host Name     : APC
         2- Domain Name   : somedomain.com
         3- Port Speed    : Auto-negotiation
         4- Accept Changes:
    
         <ESC>- Back, <ENTER>- Refresh, <CTRL-L>- Event Log
    > 1
    Host Name : UPS-NAME
    
    ------- Advanced... -----------------------------------------------------------
    
            Link is down.
    
         1- Host Name     : UPS-NAME
         2- Domain Name   : somedomain.com
         3- Port Speed    : Auto-negotiation
         4- Accept Changes: Pending
    
         <ESC>- Cancel Changes, <ENTER>- Refresh, <CTRL-L>- Event Log
    > 2
    Domain Name : domain.name
    
    ------- Advanced... -----------------------------------------------------------
    
            Link is down.
    
         1- Host Name     : UPS-NAME
         2- Domain Name   : domain.name
         3- Port Speed    : Auto-negotiation
         4- Accept Changes: Pending
    
         <ESC>- Cancel Changes, <ENTER>- Refresh, <CTRL-L>- Event Log
    > 4
    
    ------- Advanced... -----------------------------------------------------------
    
            Link is down.
    
         1- Host Name     : UPS-NAME
         2- Domain Name   : domain.name
         3- Port Speed    : Auto-negotiation
         4- Accept Changes: Success
    
         <ESC>- Back, <ENTER>- Refresh, <CTRL-L>- Event Log
    >
    
    # Мжем ESC пока не попадем в главное меню
    
    ------- Control Console -------------------------------------------------------
    
         1- Device Manager
         2- Network
         3- System
         4- Logout
    
         <ESC>- Main Menu, <ENTER>- Refresh, <CTRL-L>- Event Log
    > 4
    
    You are now in passthru mode.

     

    Теперь продолжить настройку ИБП можно через Web интерфейс. Там все еще проще, описывать нет смысла, главное настройте SNMP COMMUNITY.

    Переходим к Zabbix.

    Для начала необходимо создать макрос {$SNMP_COMMUNITY} со значением SNMP COMMUNITY которое выставлено в настройках ИБП.

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

    Теперь можно импортировать сам шаблон, он как всегда, во вложении к данному посту.

    Что имеется на данные момент:

    Элемента данных

    1. Версия прошивки микропроцессора
    2. Верхний порог (V) для перехода на работу от батареи
    3. Время, прошедшее с момента как ИБП перешел на работу от батареи
    4. Входное максимальное зарегистрированное напряжение
    5. Входное минимальное зарегистрированное напряжение
    6. Входное напряжение
    7. Выходная потребляемая нагрузка в амперах
    8. Выходное напряжение
    9. Индикатор замены батареи
    10. Модель устройства
    11. Напряжение батареии
    12. Нижний порог (V) для перехода на работу от батареи
    13. Оставшееся время автономной работы
    14. Причина последнего перехода на работу от батареи
    15. Серийный номер микропроцессора
    16. Статус ONLINE\OFFLINE
    17. Статус батареи
    18. Текущая нагрузка
    19. Текущее состояние ИБП
    20. Температура батареи
    21. Температура внутри ИБП
    22. Уровень заряда батареи
    23. Частота входной линии
    24. Частота выходной линии

    Триггеры

    1. ИБП: Работает от батареи
    2. ИБП: батарея в аварийном состоянии
    3. ИБП: батарея разряжена
    4. ИБП: выключен или с ним нет связи по Ethernet
    5. ИБП: заряд батареи менее 25%
    6. ИБП: заряд батареи менее 50%
    7. ИБП: заряд батареи менее 75%
    8. ИБП: зафиксирован высокий скачек напряжения на входной линии
    9. ИБП: зафиксировано сильное падение напряжения на входной линии
    10. ИБП: нагрузка превысила 80%
    11. ИБП: нагрузка превысила 85%
    12. ИБП: нагрузка превысила 90%
    13. ИБП: нагрузка превысила 100%
    14. ИБП: необходимо заменить батарею
    15. ИБП: температура батареи превысила 40 градусов С

    Графики

    1. Входное и выходное напряжение
    2. Нагрузка (A, %)
    3. Оставшееся время автономной работы
    4. Состояние батареи
    5. Частота входной и выходной линии (Гц)

    Ниже добавлю несколько изображений с графиками, но для полноты данных они взяты уже не с 5000 серии, а с 1500.

    Скрытый текст

    ups1.thumb.png.cbb4af309fda1e644fdb677efups2.thumb.png.981c404786557834676204858ups3.thumb.png.739569600bbda8d225bff2547ups4.thumb.png.f5ddd50f730b37036a6b16bf3ups5.thumb.png.087b509855a82014aa0eb323f

    PS:  Хочу добавить, что некоторые данные могут не поступать, например температура батареи. Это, как я понял, зависит от комплектации конкретного ИБП.

     

    • Like 1

  14. Делюсь шаблоном для мониторинга состояния маршрутизаторов CISCO, zabbix 2.4

    Работа с устройством реализована по протоколу SNMP и тут есть ряд подводных камней, но самый основной это изменения стандарта от одной версии прошивки к другой.
    Если понадобится изменить OID значения, то поискать их можно тут http://tools.cisco.com/Support/SNMP/do/BrowseOID.do но часть старый параметров в базе отсутствует. Тут уж или через поисковик или "Селяви, что в перевод с древнегреческого - не повезло!".

    Простая конфигурация устройства:

    cisco-test#configure terminal
    Enter configuration commands, one per line.  End with CNTL/Z.
    cisco-test(config)#snmp-server community public RO
    cisco-test(config)#

    Но при этом любой может подключиться к устройству и читать с него данные, если вы хотите чтобы плохие люди могли еще и отдавать команды то RO необходимо заменить на RW

    Если Вы желаете ограничить доступ к SNMP серверу устройства по IP, то необходимо использовать access-list:

    cisco-test#configure terminal
    Enter configuration commands, one per line.  End with CNTL/Z.
    cisco-test(config)#ip access-list standard SNMP_ACCESS_RO
    cisco-test(config-std-nacl)#permit 192.168.1.2
    cisco-test(config)#snmp-server community public RO SNMP_ACCESS_RO
    cisco-test(config)#^Z
    cisco-test#

    Сommunity можно обозвать как угодно, хоть BLABLABLA место public. IP естественно заменить на адрес Вашего сервера. RO - только чтение, RW - полный доступ

    Мы будем только читать, поэтому RO. В теории конечно можно отлавливать определенные события на устройстве и предпринимать ответные действия автоматически, только в таких случаях RW. Но об этом мы поговорим в другой раз.

    Теперь необходимо добавить макрос в zabbix Администрирование -> Общие -> Макросы

    {$SNMP_COMMUNITY} = имя community указанное в конфигурации CISCO, например "public"

    Сам шаблон можно забрать в конце поста.

    Немного о шаблоне.

    NET.thumb.png.a1cfa8d3d37de4d07ac2089268

    На данным момент собираются данные:

    1. Аптайм
    2. Модель маршрутизатора
    3. Загрузка центрального процессора за: 5 секунд, 1 минуту, 5 минут
    4. Статус (online\offline)
    5. Память: ОЗУ, ПЗУ
    6. Список доступных интерфейсов
    7. Статусы сетевых интерфейсов
    8. Количество byte RX\TX на каждом интерфейсе
    9. Количество error byte RX\TX на каждом интерфейсе

    По этим данным строятся соответствующие графики (ОЗУ, ПЗУ и т.п не выведены, можете добавить самостоятельно)

    Скрытый текст

    ztrc1.thumb.png.ee808f723737512ea2fc49e7

    ztrc2.thumb.png.fd3191b85d8a1bc4741c229f

    Пока маловато триггеров, но по мере обновления шаблона, их количество увеличится:

    1. Маршрутизатор выключен или с ним нет связи по Ethernet
    2. Маршрутизатор был пере запущен менее 5 минут назад
    3. Нагрузка на ЦП выше 80%

     PS: следите за обновлениями!

    zbx_templates_router_cisco.xml

    • Like 1

  15. Описанный ниже велосипед является продолжением одной из моих статей на другом, интересном мне, ресурсе. Продолжение и все последующие обновления будут происходить только на it4it.club

    Для начала, хочу процитировать самого себя и тем самым дать пояснения: почему, зачем и для кого это нужно.

    Цитата

    Доброе время суток.

    Прежде чем вдаваться в описание программы, укажу на ситуацию которая привела к созданию очередного велосипеда.
    Есть группа в которую входит Ваш покорный слуга + два сотрудника. В наше присутствие на рабочем месте, события в Zabbix (2.2) отслеживаются постоянно, а если быть точным то 8 часов в день, 5 дней в неделю. Исключения составляют праздничные дни и отпуска совпавшие с изменениями графика работы у коллег.
    В наше отсутствие мониторингом инфраструктуры занимается (по крайней мере должен заниматься) дежурный персонал. В этом и кроется проблема!
    Как выясняется, люди работающие в режиме день - ночь - 48 часов дома, не проявляют особого интереса к мониторингу состояния триггеров со всеми вытекающими из этого последствиями. Ну да Бог с ними, воспитательные работы - задача административного персонала.

    Наша задача более явно оповещать об изменениях в активных триггерах на машинах дежурного персонала под управлением ОС Windows.
    Под этим я подразумеваю:
    1. Оповещать о переходе активного триггера в состояние TRUE
        - Звуковым сигналом
        - Всплывающей подсказкой в трее
    2. Возможность видеть проблемные триггеры в GUI Windows

    Велосипед заключается в том, что мы будем использовать PHP для реализации GUI под Windows. Да, да, да ... можете плевать в мою сторону и я полностью согласен, что C++ намного эффективнее, но мои познаний в C++ еще не достаточно, для перехода на новую ступень эволюции.

    ...

    Скрытый текст

    P3.thumb.png.e1315a4c50d0c1d70dcf3765924

    Данная тема получила большую популярность у коллег и не раз выручила Вашего покорного слугу, не дав разгореться стулу на котором он сидит.
    Но имеющегося функционала стало не хватать, требовалось не только слышать и иногда видеть, а еще получать удаленный доступ к оборудованию и лицезреть всю картину в целом.

    Задача поставлена, идем исполнять, но с начало забегу немного вперед и продемонстрирую текущий функционал.

    Общее окно программы переработано и теперь отображает список всех групп из Zabbix с их хостами. Список активных триггеров переехал в нижнюю часть окна и отображает дополнительную информацию об узлах с которыми не все гладко. Сам список можно увеличивать, если все совсем плохо, или уменьшать если выражение "Хьюстон у нас проблемы" Вы слышите только в фильмах. Также появились цветные маркеры важности триггера. По двойному клику по активному триггеру мы перемещаемся в соответствующую группу где находится проблемный узел.

    Скрытый текст

    zti1.thumb.jpg.ff0c49f828126174700bc02ba

    Скрытый текст

    zti4.thumb.jpg.ca907061b917487d97f148b81 

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

    Скрытый текст

    zti3.thumb.jpg.6776f05f6810f4e06bcf670c8

    Хосты в группах стали кликабельны, правда пока только ПКМ, и имеют всплывающее меню позволяющее выполнять банальные действия:
    Подключение к узлу:

    1. Telnet
    2. SSH
    3. RDP
    4. VNC

    Проверка соединения:

    1. Ping
    2. Traceroute

    Естественно его можно расширить, добавив всяких вкусностей по мере возрастания аппетита IT отдела.

    Скрытый текст

    zti6.thumb.jpg.4f1cfbad5c1de63aba0c558e9

    Скрытый текст

    zti5.thumb.jpg.d4e3e2d144d813b9959ba4dfe 

    Программа по прежнему имеет серверную и клиентскую часть, что позволило организовать кэширование запросов и осуществлять оповещения в момент определения проблемы сервером, а не по таймеру обновления, как это организовано в dashboard zabbix. Раньше узнали о проблеме, раньше приступили к устранению. Естественно и устаревшие триггеры убираются также оперативно.
    Также это позволило избавить программу от информации о доступе к серверу, что мне кажется не приемлемым для оповещалки. Чем меньше дырок, тем крепче сон.

    Приступим к реализации.

    Ну и естественно, Ваш покорный слуга еще не научился писать на C++ ничего более сложного чем "Hello World", поэтому клиент и сервер будет написан на PHP. За исключением того момента, что клиентская часть (при помощи магии Гарри Поттера) станет бинарным файлом. Бог с ним, серверная часть не доступна обывателю, а это самое важное!!!

    Для начала, нужно придумать безопасный и понятный алгоритм не нагружающий сервер т.к клиентов может быть 100и или 1000и, всякое бывает. Все не безопасные команды обязаны выполняться на сервере без участия клиента. Дальнейшая работа клиента: обработка данных, выполнение действий с триггерами и т.п должно выполняться только у клиента, не затрагивая сервер.

    1. При обращении клиента к серверу по http происходит проверка его ip на наличие в списках разрешенных. В противном случае показываем фигу.
    2. Проверяем, наличие актуального кеша, и отдаём его.
    3. Если кеша нет, генерируем новый.
    4. Если запрос прилетел от zabbix: принудительно считаем кеш устаревшим, генерируем новый и после этого рассылаем всем клиентам оповещения по UDP протоколу. Это позволит свести расход ресурсов базы данных к нулю т.к все клиенты ринутся забирать новые данные, а все уже в кеше.

    Супер, все дешево и сердито.

    Серверная часть представляет из себя файл index.php где-то на Вашем web сервере. Актуально для Zabbix 2.4 (скорее всего будет актуальна и для последующих версий, будем проверять по мере возрастания аппетита).

    Скрытый текст
    
    <?PHP
    # Храним файл кэша вне web сервера!!!, желательно во временной директории вашей UNIX системы.
    $cacheFile = '/tmp/zabbix_trigger.json';
    # Время жизни кэша в секундах, по его истечении и только по запросу клиента кэш файл перезаписывается новыми данными (если не было сигнала от zabbix).
    $cacheInterval = 60;
    # Список разрешенных IP адресов, они же являются и списком для оповещения.
    $client = array(
        # Администраторы
        '10.10.10.100',
        '10.10.10.101',
        '10.10.10.102',
        # Дежурные
        '10.10.10.200',
        # Начальство
        '10.10.10.210',
    );
    # Настройки доступа к MySQL
    $settings = array(
        'db'    => 'zabbix', 
        'login' => 'zabbix',
        'pass'  => 'zabbix'
    );
    # Определяем кто запустил скрипт, zabbix или хост из вне
    $alarm = (substr(PHP_SAPI, 0, 3) == 'cli') ? true : false;
    
    # Не работаем с не зарегистрированными клиентами
    if(!$alarm) {
        if(!in_array($_SERVER['REMOTE_ADDR'], $client)) exit();
    }
    # Работа с БД
    class db extends PDO
    {
        # Начальные данные для подключения к серверу
        private $settings = array(
            'host'  => 'localhost',
            'db'    => '',
            'login' => 'root',
            'pass'  => '',
        );
        #
        function __construct($settings = array())
        {
            # Изменяем конфигурацию
            if(is_array($settings) and 0 != count($settings)) {
                foreach($settings as $key => $val) {
                    if(isset($this->settings[$key])) $this->settings[$key] = $val;
                }
            }
            # Подключаемся к базе данных
            try {
                parent::__construct('mysql:host='.$this->settings['host'].';dbname='.$this->settings['db'].';charset=utf8', $this->settings['login'], $this->settings['pass']);
            }
            catch(PDOException $error) {
                echo $error->getMessage();
                exit();
            }
        }
    }
    
    if(!file_exists($cacheFile) or $cacheInterval < (time()-filemtime($cacheFile)) or $alarm) {
        $message = array(
            'trigger' => array(),
            'group' => array(),
        );
        
        $db = new db($settings);
        $query = $db->query("
        SELECT
            t.triggerid,
            h.hostid,
            h.host,
            g.groupid,
            t.priority,
            t.description,
            t.lastchange,
            t.error,
            it.ip
        FROM
            triggers t,
            hosts h,
            items i,
            functions f,
            interface it,
            groups g,
            hosts_groups hg
        WHERE
            t.value=1 AND
            t.status=0 AND
            f.triggerid=t.triggerid AND
            i.itemid=f.itemid AND
            i.status=0 AND
            h.hostid=i.hostid AND
            h.status=0 AND
            h.hostid = it.hostid AND
            h.hostid = hg.hostid AND hg.groupid = g.groupid
        ORDER BY
            t.lastchange DESC
        ");
        if($query->rowCount()) {
            foreach($query->fetchAll(PDO::FETCH_ASSOC) as $var => $val) $message['trigger'][$val['triggerid']] = $val;
        }
        $query = $db->query("
        SELECT 
            h.hostid,
            h.host,
            h.name showname,
            g.name,
            g.groupid,
            i.ip
        FROM 
            hosts h,
            groups g,
            hosts_groups hg,
            interface i
        WHERE 
            h.status=0 and h.flags=0
            and
            h.hostid = hg.hostid and hg.groupid = g.groupid and h.hostid = i.hostid
        ORDER BY g.name ASC, h.name ASC
        ");
        if($query->rowCount()) {
            foreach($query->fetchAll(PDO::FETCH_ASSOC) as $var => $val) {
                $message['host'][$val['groupid']][$val['hostid']] = $val;
                if(!isset($message['group'][$val['groupid']])) $message['group'][$val['groupid']] = $val['name'];
            }
        }
        $query = $db->query("SELECT `sysmapid`, `name` FROM `sysmaps` ORDER BY sysmapid ASC");
        if($query->rowCount()) {
            foreach($query->fetchAll(PDO::FETCH_ASSOC) as $var => $val) {
                $message['map'][$val['sysmapid']] = $val['name'];
            }
        }
        file_put_contents($cacheFile, json_encode($message), LOCK_EX);
        if(!$alarm) chmod($cacheFile, 0666);
    }
    if(!$alarm) echo file_get_contents($cacheFile);
    else {
        # Отсылаем администраторам предупреждение о событии
        $socket = socket_create(AF_INET, SOCK_DGRAM, GetProtoByName('udp'));
        foreach($client as $ip) {
            socket_connect($socket, $ip, 5055);
            socket_write($socket, 'trigger');
        }
        socket_close($socket);
    }
    ?>

     

    Для дополнительной безопасности рекомендую создать отдельную учетную запись в базе данных с правами только на чтение и только к базе zabbix. Это актуально в наши дни.

    Переходи в zabbix

    Первым делом переходим в Настройка -> Действия -> Источники события: Триггеры и создать новое действие. Назовем случайным образом, например Report problems to ZabbixTrigger.

    В условиях выставляем

    Значение триггера = ПРОБЛЕМА		
    Значение триггера = OK

    Это все необходимо, чтобы проблемы в GUI появлялись и исчезали точно именно тогда, когда это происходит в Zabbix (не путать с dashboard).

    Вкладка "Операции" должна выглядеть следующим образом. Естественно что путь до php скрипта должен быть вашим.

    Скрытый текст

    zti7.thumb.jpg.266d1ac1c77d32a38bfeb2202

    Вуаля! Если Вы все сделали правильно (без кардинальных изменений), то я рад приветствовать нового ездока моего велосипеда. Поедем тандемом!

    Сам клиент прикреплен в конце поста.

    Для тех, кто читает титры в кинотеатре

    При запуске будет создан файл config.ini в корне программы. При необходимости, измените его настройки, благо их не много.

    [server]
    url="http://zabbix/zabbix-gui"
    update="60"
    
    [client]
    showalarm="20"

    Параметр url отвечает за http путь до скрипта на сервере, update за интервал (в секундах) обновления данных с сервера, а showalarm вроде как должен влиять на время отображения всплывающей подсказки в трее.

    Теперь немного структуры.

    1. В каталоге sounds находится звук оповещения о новом триггере, формат ogg
    2. Каталог icons содержит маркеры триггеров. trigger00 - для нормального состояния, а все последующие соответствуют начальным настройкам zabbix во вкладке Администрирование -> Общие раздел Важность триггеров. Можете изменить на свои цвета, программа все подхватит при перезагрузки.
    3. Ну и на закуску каталог ext, это как говорится для меломанов, содержит фри программы для обработки внешних действий клиента. Вы можете заменить их на свои при условии, что стандартные имена параметров запуска и их последовательность совпадают.

    PS: На этом все, спасибо тем кого это заинтересовало. Предлагайте свои идеи или способы доработки велосипеда. А если найдется программист на C++ так милости просим. В любом случае, функционал будет в дальнейшем расширяться.

    zabbixTrigger.zip

    • Like 2

  16. Начнем с прелюдии.
    За благополучный доступ во всемирную паутину в моем жилище отвечает само сборный конструктор с операционной системой RouterOS v6.22 на борту.
    И настал тот кризисный день, когда понадобился доступ к устройству из вне (а может и за него), а денег на белый IP тратить не хочется т.к. цель должна сначала оправдать средства.
    А на данный момент самый лучший вариант - использовать DDNS.
    Для реализации задуманного решено использовать ресурс myddns.ru (и это не реклама, а первый попавшийся под руку).

    И так к делу
    Сервис myddns.ru предлагает несколько способов общения с ним. Но меня заинтересовал метод использования уникального ключа при обращения к сервису по http
    Выглядит это так http://myddns/key/куча_цифр_или_ваш_уникальный_ключ
    Но есть ограничения - если в течении трех дней не поступают запросы на обновления IP адреса, то происходит делегирование записи в DNS на сервере. И это может показаться банально смешной вещью, но мой провайдер выдает адреса на 3 часа, при этом если я не ухожу в офлайн, то буду получать один и тоже адрес из пула до скончания веков (ну или +/- день от века).
    Следовательно к моменту как произойдет смена IP адреса устройства, я уже потеряю запись в DNS на сервисе myddns и вся эта затея потеряет смысл. ЭТО НУЖНО УЧЕСТЬ! 

    Нам понадобится всего две вещи:
    1. создать скрипт в котором будет реализована вся логика
    2. прописать его в планировщик задач RouterOS

    Пункт 1 (Скрипт)
    Суть его очень проста. При инициализации провести проверку соответствия текущего IP адреса интерфейса смотрящего во внешний и страшный мир и IP соответствующего нашему субдомену на myddns. Плюс к этому производить дополнительный запрос к сервису через каждый N-ый запуск скрипта.
    Решено запускать скрипт каждую минуту для проверки условия соответствия IP адресов. И каждый 60-ый запуск скрипта должен вызывать принудительный запрос к DDNS сервису (и не важно, изменился мой адрес или нет), это поможет дополнительно поддерживать стабильность услуги.

    :local ddnshost "kitsum.myddns.ru";
    :local ddnsid "2356273494558114944";
    :local wan "ether1";
    :local tmpFile "myddns.info";
    :local interval 60;
    :local ddnsip [:resolve $ddnshost];
    :local localip [/ip addres get [/ip address find interface=$wan ] address];
    :local localip [:pick $localip 0 [:find $localip "/"]];
    :global ddnsinterval;
    :if ($localip != $ddnsip) do={
        /tool fetch url="http://myddns.ru/key/$ddnsid" dst-path="$tmpFile"
        delay 2;
        :local fileid [/file find name="$tmpFile"];
        :local fileContent [/file get $fileid contents];
        /file remove $fileid;
        :if ($fileContent = "update") do={
            :log info "DDNS: mismatch DNS records, correction is made. $ddnshost ($ddnsip) changed to $localip";
        }
    } else={
        :if ($interval <= $ddnsinterval) do={
            :log info "DDNS: launched additional check"
            :set ddnsinterval 1;
            /tool fetch url="http://myddns.ru/key/$ddnsid" dst-path="$tmpFile"
            delay 2;
            :local fileid [/file find name="$tmpFile"];
            :local fileContent [/file get $fileid contents];
            :log info "DDNS: server response '$fileContent'";
            /file remove $fileid;
        } else={ :set ddnsinterval ([$ddnsinterval] + 1); }
    }

    Параметры:
    1. ddnshost - соответствует Вашему субдомену myddns.ru
    2. ddnsid - Ваш уникальный ключ прописанный на сервисе ddns
    3. wan - содержит имя интерфейса Вашего MikroTik который смотрит в интернет
    4. tmpFile - имя временного файла который будет создаваться в ходе работы скрипта и содержать ответ сервера:

    noparametrs - не хватает параметров.
    nologinpass - не верный логин или пароль.
    nodomens - не верный домен.Такого домена не существует или не принадлежит Вам.
    update - прошло обновление домена.IP поменялся.
    noupdate - нечего обновлять.IP не поменялся.

    5. interval - как часто проводить принудительный запрос (измеряется в интервалах запуска помноженный на частоту запуска скрипта)
    Все остальные значения не предназначены для редактирования.

    Планировщик задач MikroTik
    Консольный вариант расписывать не буду т.к используют winbox.
    Проваливаемся в System -> Scheduler и создаем новую задачу с произвольным именем в которую прописываем запуск скрипта с интервалом в 1 минуту и запуск самой задачи при старте OS. Вы можете указать иной интервал, но для меня важно не терять связь с устройством (учитывая, что если IP изменится во время работы, то связь и так будет потеряна на время до 15-20 минут), пока обновятся записи DNS на сервисе.

    Команда запуска скрипта выглядит так

    /system script run myddns

    Где myddns - имя исполняемого скрипта

    Привожу скин с настройками для наглядностиMikroTik.thumb.png.825b542b272057d35c1e3

     

    • Like 1

  17. Первым делом необходимо получить список записей в access-list с их порядковыми номерами.

    cisco-test#show access-lists
    Standard IP access list SNMP_ACCESS_RO
        10 permit 10.10.10.100
    Extended IP access list 101
        10 permit ip 192.168.0.0 0.0.0.255 host 10.10.10.100
        20 permit ip 192.168.0.0 0.0.0.255 host 10.10.10.101
        30 permit ip 192.168.0.0 0.0.0.255 host 10.10.10.102

    В данном случае в ACL 101 имеются три записи с порядковыми номерами 10, 20, 30. Эти уникальные идентификаторы и позволят нам совершить акт вандализма.

    cisco-test#configure t
    Enter configuration commands, one per line.  End with CNTL/Z.
    cisco-test(config)#ip access-list e 101
    cisco-test(config-ext-nacl)#no 30

    В конфигурационном терминале мы выбрали для редактирования ACL 101 и указали какую запись необходимо удалить ссылаясь на её порядковый номер (в данном случае 30).

    • Like 1
×
×
  • Создать...