MikroTik RouterOS API

Заключительная часть цикла посвящена использованию централизованного инструмента управления устройствами с помощью API RouterOS с примерами для языка Python3. 

Нет комментариевПросмотров: 1330
17апреля 2019
все статьи

АНОНС

Первая часть цикла: Скрипты RouterOS

Вторая часть цикла: MikroTik Ansible

В цикле статей «Автоматизация при эксплуатации оборудования MikroTik RouterOS» представлен обзор средств автоматизации, применяемых при эксплуатации оборудования на базе RouterOS. Решения применимы как к аппаратным решениям MikroTik, так и к облачным решениям, построенным на базе RouterOS.

Заключительная часть цикла посвящена использованию централизованного инструмента управления устройствами с помощью API RouterOS с примерами для языка Python3.

Использование API RouterOS

В рамках этой статьи будет рассматриваться взаимодействие с устройством через специальный программный интерфейс управления (API — application programming interface). По функциональности этот интерфейс аналогичен конфигурации устройства через telnet или ssh. Отличие состоит в том, что telnet и ssh относятся к интерфейсам взаимодействия устройства и человека, а API — взаимодейтствия устройства и устройства, т.е. MikroTik API как нельзя лучше подходит для целей автоматизации.

Ранее было сказано, что в качестве языка программирования при демонстрации работы с API RouterOS будет использоваться Python 3. RouterOS API Python 3 поддерживает взаимодействие с сетевыми устройствами через протоколы telnet и ssh, однако работа с этими модулями трудоёмка, например, результат выполнения команды необходимо выделять с помощью регулярного выражения из общего ответа устройства. Одним из модулей, который значительно упрощает взаимодействие с сетевыми устройствами, является модуль netmiko, однако на текущий момент в нём не реализована поддержка RouterOS. Синтаксис языка Python 3 в рамках статьи рассматриваться не будет.

Для взаимодействия с устройством необходимо включить поддержку API непосредственно на устройстве, перейдя в раздел “/ip services”. По-умолчанию API работает через порт 8728. Кроме того, RouterOS поддерживает защищённый вариант программного интерфейса API over SSL (api-ssl по-умолчанию использует порт 8729).

Разработчики RouterOS не предоставили библиотеки для работы через API для популярных языков программирования, однако описали протокол взаимодействия в официальной документации. Помимо описания протокола взаимодействия представлены примеры программ, в том числе для языка Python 3.

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

  • libapi — файл библиотеки, подготовленный для статьи. Файл содержит основные функции и классы для взаимодействия с устройством;
  • ApiRos — класс устройств под управлением RouterOS;
  • socketOpen — функция открытия сокета;
  • socketClose — функция закрытия сокета;
  • login — функция авторизации на устройстве;
  • writeSentence — функция отправки команды на устройство;
  • readResponse — функция получения результата выполнения команды с устройства.

Следует упомянуть о формате команд, т.к. он отличается от синтаксиса, используемого при конфигурации вручную. Команды вводятся в виде списка, где первый элемент — команда, в которой пробелы заменяются на символ “/”, а последующие элементы уточняют запрос:

  1. команды просмотра: фильтр запроса начинается с вопросительного знака, после чего следует текст фильтра, например “?type=ether”. Уточняющих команд может быть несколько;
  2. команды изменения конфигурации: фильтр начинается с символа равенства, после чего следует имя параметра и его значение, например “=name=ISP1”. Уточняющих команд может быть несколько.

Демонстрация работы с API

Составим программу для получения списка IP-адресов на устройствах R2 и R3. Листинг программы ip_get.py с комментариями представлен ниже.

Листинг ip_get.py

import libapi

 

devices = [

      { 'ip': '172.16.16.13',

      'login': 'user',

      'pass': 'user'},

      { 'ip': '172.16.16.12',

      'login': 'spw',

      'pass': 'spw'}]

   

for device in devices:

      print("Connect to {}:".format(device['ip']))

 

      #Создание сокета и объекта устройства

      s = libapi.socketOpen(device['ip'])

      dev_api = libapi.ApiRos(s)

 

      #Авторизация на устройстве

      if not dev_api.login(device['login'], device['pass']):

            break

 

      #Список команд

      command = ["/ip/address/print"]

     

      #Выполнение команды на устройстве

      dev_api.writeSentence(command)

     

      #Получение результата выполнения команды

      res = libapi.readResponse(dev_api)

     

      #Закрытие сокета

      libapi.socketClose(s)

 

      #Форматированный вывод результата команды

      print("    Command result:")

      print("        {:^18} {:10}".format('IP', 'Interface'))

 

      for element in res:

            print("        {:18} {:^10}".format(element[2].split('=')[2],

                  element[4].split('=')[2])) 

      print('')

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

  • в словаре devices описаны реквизиты доступа к R2 и R3;
  • в цикле обрабатывается словарь devices и выполняется подключение к каждому из устройств;
  • после подключения на устройстве выполняется команда “/ip address print”;
  • результат выполнения команды сохраняется в переменную res;
  • закрывается подключение к устройству;
  • выполняется форматированный вывод содержимого переменной res.

Результат выполнения программы представлен на рисунке 3.8:

Automation
Рисунок 3.8 — Результат выполнения программы ip_get.py

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

Пусть администратор сети решил насроить OSPF, выделив для идентификаторов Router-id адреса из сети 10.52.52.0/24. Для R2 выбран идентификатор 10.52.52.12, а для R3 10.52.52.13.

Составим программу, которая будет создавать для OSPF bridge-интерфейс и ассоциировать созданный интерфейс с выделенным IP-адресом. Листинг программы ip_add.py представлен ниже:

Листинг ip_add.py

import libapi

 

devices = [

      { 'ip': '172.16.16.13',

      'login': 'user',

      'pass': 'user'},

      { 'ip': '172.16.16.12',

      'login': 'spw',

      'pass': 'spw'}]

 

new_network = '10.52.52.{}/32'

new_interface = 'br_ospf'

 

for device in devices:

      print("Connect to {}:".format(device['ip']))

 

      #Создание сокета и объекта устройства

      s = libapi.socketOpen(device['ip'])

      dev_api = libapi.ApiRos(s)

 

      #Авторизация на устройстве

      if not dev_api.login(device['login'], device['pass']):

            break

 

      #Команда для добавление bridge-интерфейса

      command = ["/interface/bridge/add", "=name={}".format(new_interface)]

     

      #Выполнение команды на устройстве

      dev_api.writeSentence(command)

 

      #Получение результата выполнения команды

      res = libapi.readResponse(dev_api)

     

      #Команда для добавления IP-адреса на bridge-интерфейс

      command = ["/ip/address/add",

            "=address={}".format(new_network.format(device['ip'].split('.')[3])),

            "=interface={}".format(new_interface)]

           

      #Выполнение команды на устройстве

      dev_api.writeSentence(command)

     

      #Получение результата выполнения команды

      res = libapi.readResponse(dev_api)

     

      #Закрытие сокета

      libapi.socketClose(s)

Результат выполнения программы ip_add.py представлен на рисунке 3.9:

Script
Рисунок 3.9 — Результат выполнения программы ip_add.py

Несмотря на то, что в переменную res записывался результат применения команды, она не использовалась в текущей программе. Убедимся, что ip-адреса успешно добавлены, запустив программу ip_get.py:

Python
Рисунок 3.10 — Результат выполнения программы ip_get.py

По результату работы программы видно, что программа ip_add.py выполнила действия согласно поставленной задаче: создан bridge-интерфейс и он ассоциирован с IP-адресом из выделенной сети.

Составим программу, выполняющую передачу файла script_spw на маршрутизаторы R2 и R3. Для этого нет необходимости использовать API, можно успешно реализовать это с помощью стандартных средств Python, выполнив подключение к устройствам по FTP (поддержка FTP должна быть предварительно активирована на устройствах). Листинг программы file_add.py представлен ниже.

Листинг file_add.py

import ftplib

 

devices = [

      { 'ip': '172.16.16.13',

      'login': 'user',

      'pass': 'user'},

      { 'ip': '172.16.16.12',

      'login': 'spw',

      'pass': 'spw'}]

 

filename = 'script_spw'

 

for device in devices:

      print("Connect to {}:".format(device['ip']))

     

      with ftplib.FTP(device['ip'], device['login'], device['pass']) as con:

            with open(filename, "rb") as f:

                  send_file = con.storbinary("STOR " + filename, f)

                  print("    File transfer: done")

Выполним программу, после чего выполним программу, которая выводит список файлов на устройствах (по аналогии с программой ip_get.py). Результат выполнения представлен на рисунке 3.11:

MikroTik
Рисунок 3.11 — Результат выполнения программы file_add.py

На рисунке видно, что изначально на устройствах присутствуют две директории pub и skins, созданные по-умолчанию, а после успешного выполнения программы file_add.py, на устройствах появляется файл script_spw.

Составим программу, выполняющую загрузку файлов с устройств по протоколу FTP. Листинг программы file_get.py:

Листинг программы file_get.py

import ftplib

 

devices = [

      { 'ip': '172.16.16.13',

      'login': 'user',

      'pass': 'user'},

      { 'ip': '172.16.16.12',

      'login': 'spw',

      'pass': 'spw'}]

 

filename_pattern = 'backup_{}.backup'

 

for device in devices:

      print("Connect to {}:".format(device['ip']))

     

      filename = filename_pattern.format(device['ip'])

 

      with ftplib.FTP(device['ip'], device['login'], device['pass']) as con:

            with open(filename, "wb") as f:

                  con.retrbinary('RETR ' + filename, f.write)

                  print("    File transfer: done")

Перед выполнением программы file_get.py выполним backup на всех устройствах, изменив команду в программе ip_add.py. Имя backup-файла будет соответствовать следующему шаблону: “backup_*IP-адрес*.backup”. Результат выполнения программы file_get.py представлен на рисунке 3.12.

RouterOS
Рисунок 3.12 — Результат выполнения программы file_get.py

После выполнения программы file_get.py в текущей директории появились два файла backup_172.16.16.12.backup и  backup_172.16.16.13.backup, в соответствии с задачей.

ЗАКЛЮЧЕНИЕ

В цикле статей рассмотрены основные методы автоматизации при эксплуатации устройств на базе RouterOS. Представлено описание и ряд базовых примеров при работе с языком написания скриптов RouterOS, системой управления конфигураций на примере Ansible и API RouterOS.

У каждого из рассмотренных инструментов есть свои достоинства и недостатки, поэтому выявить универсальный не представляется возможности — каждый из них хорошо справляется с определённым кругом задач. Кроме того, за рамками статьи осталось описание деталей и части возможностей этих инструментов.

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

Все использованные в статье материалы (Ansible playbooks и программы для взаимодействия через API) выложены на github.

поделиться материалом:

Читайте также

комментарии — 0