Управление через telnet, ssh, tcp, shell, mikrotik-api

TerminalServiceActivator

Обработчик предназначен для выполнения команд по telnet, ssh, tcp, shell. Команды для разных событий задаются в конфигурации типа устройства.

Параметры подключения

# Хост подключения (если не указан, используется значение из поля Хост/порт)
#sa.terminal.host=
# Порт подключения (если не указан, используется значение из поля Хост/порт)
#sa.terminal.port=
# протокол подключения (telnet/ssh/tcp/mikrotik/shell/mikrotik-api)
sa.terminal.protocol=telnet
# логин (если не указан, используется значение из поля Логин)
#sa.terminal.username=
# пароль (если не указан, используется значение из поля Пароль)
#sa.terminal.password=
# Шаблон приглашения ввода команды (для telnet/ssh/tcp)
# Для ssh можно указать regexp, если в начале и конце строки будет символ '\', например, "\(#)|(~\$)\"
sa.terminal.endSequence=#
 
# Для sa.terminal.protocol=mikrotik - хост и порт, с которых открывать соединение
#sa.terminal.sourceHost=
#sa.terminal.sourcePort=
 
# Отложенное подключение. Если указано 1, то подключение происходит перед выполнением первой команды,
# иначе - 0 (по умолчанию), подключение происходит в методе connect
sa.terminal.lazyConnect=0

При подключении по ssh можно использовать RSA/DSA ключ. При этом в конфигурации необходимо указать путь к приватному ключу:

# протокол подключения
sa.terminal.protocol=ssh
# логин (если не указан, используется значение из поля Логин)
#sa.terminal.username=
# пароль (если не указан, используется значение из поля Пароль)
#sa.terminal.password=
# Путь к ключу
sa.terminal.privateKeyFile=id_rsa

Параметр sa.endSequence используется для определения окончания вывода от удаленного оборудования и приглашения ввода команд. Например, когда консоль ssh выглядит так:

amir@ts01:~$ date
Ср. нояб. 16 15:58:55 YEKT 2016
amir@ts01:~$

Мы отправили команду, получили ответ и можем отправлять новую.

Во время выполнения команд есть возможность подменить значение sa.endSequence на другое, с помощью макроса ${setEndSequence(':~$')}. Это может быть полезно, т.к. на некотором оборудовании строка приглашения во время выполнения команд может меняться. Для того, чтобы вернуть значение на значение sa.endSequence из конфига, можно вызвать макрос без агрумента: ${setEndSequence()}.

Команды для инициации подключения и отключения

При появлении задачи синхронизации, если на данный момент соединение не установлено, обработчик подключается к устройству, используя параметры подключения, затем выполняет команды, прописанные в параметре sa.terminal.connect. Далее выполняются команды синхронизации, возможно последовательно для нескольких договоров. Когда задания выполнены и новых нет в течении sa.batch.waitNext.millis, то происходит отключение от устройства, но перед этим выполняются команды, указанные в sa.terminal.disconnect. Перед самим отключением выполняется команда sa.terminal.exit (в отличие от sa.terminal.disconnect после этой команды нет ожидания ответа от устройства ).

# Команды, которые необходимо выполнить после установки соединения
sa.terminal.connect=
# команды, которые необходимо выполнить перед отключением соединения
sa.terminal.disconnect=
# команда выхода (отключения)
sa.terminal.exit=exit

Параметры работы

# Смена состояния сессии после вызова connectionModify (по умолчанию 1)
# (подробнее о состоянии на странице документации Сущности модуля Inet - Сессии)
#sa.terminal.connection.stateModify=1
 
# Пропуск дочернего аккаунтинга (дочерних/сервисны сессий ISG/SmartEdge)
# Т.е. для них onAccountingStart/onAccountingStop вызываться не будет (по умолчанию 1)
#sa.terminal.connection.skipServiceAccounting=1
 
# Устройства, сессии с которых будут обрабатываться командами onAccountingStart/onAccountingStop
#sa.terminal.connection.deviceIds=
 
# Ограничение на количество одновременных подключений (обычно максимально возможное кол-во - это ((кол-во дочерних устройств этого устройства) + 1), но не более 100)
#sa.terminal.semaphorePermits=0
 
# Фильтр по опциям (можно использовать отдельную ветку)
#sa.inetOption.root=

Выполнение команд

Возможные наборы команд для событий

Для каждого события есть три набора команд: "перед вызовом обычного набора", "обычный набор", "после вызова обычного набора". Например, sa.terminal.serv.modify, sa.terminal.serv.modify.before, sa.terminal.serv.modify.after. Наборы команд .before и .after в основном предназначены для вызова других наборов команд и изменения логики работы, в зависимости от ситуации.

# Команды, выполняемые при добавлении сервиса на договор
sa.terminal.serv.create=
# команды, выполняемые при удалении сервиса с договора
sa.terminal.serv.cancel=
# команды, вызываемые при изменении сервиса (состояние/опции)
sa.terminal.serv.modify=
# команды, вызываемые при включении сервиса (состояние стало "включено")
sa.terminal.serv.modify.enable=
# команды, вызываемые при отключение сервиса (состояние стало "отключено")
sa.terminal.serv.modify.disable=
# команды, вызываемые при включении опции ID=x (и дочерних опций по отношению к опции с ID=x) на сервисе
sa.terminal.serv.inetOption.x.enable=
# команды, вызываемые при отключении опции ID=x (и дочерних опций по отношению к опции с ID=x) на сервисе
sa.terminal.serv.inetOption.x.disable=
# команды, вызываемые при изменении соединения (состояние/опции)
sa.terminal.connection.modify=
# команды, вызываемые при включении соединения (состояние стало "включено")
sa.terminal.connection.modify.enable=
# команды, вызываемые при отключение соединения (состояние стало "отключено")
sa.terminal.connection.modify.disable=
# команды, вызываемые при включении опции ID=x (и дочерних опций по отношению к опции с ID=x) на соединении
sa.terminal.connection.inetOption.x.enable=
# команды, вызываемые при отключении опции ID=x (и дочерних опций по отношению к опции с ID=x) на соединении
sa.terminal.connection.inetOption.x.disable=
# команды, вызываемые при сбросе соединения
sa.terminal.connection.close=
# команды, вызываемые при старте соединения
sa.terminal.connection.onAccountingStart=
# команды, вызываемые при стопе соединения
sa.terminal.connection.onAccountingStop=

Также можно использовать отдельные наборы команд для разных типов сервисов. Для этого нужно использовать префикс sa.terminal.set.имя_набора_команд. и указать его типы сервисов в параметре sa.terminal.set.имя_набора_команд.servTypeIds:

# ID типов сервиса, для которых будет выполнятся набор команд mySet
sa.terminal.set.mySet.servTypeIds=1,2,3
# Команды, выполняемые при добавлении сервиса на договор
sa.terminal.set.mySet.serv.create=
# команды, выполняемые при удалении сервиса с договора
sa.terminal.set.mySet.serv.cancel=
# команды, вызываемые при изменении сервиса (состояние/опции)
sa.terminal.set.mySet.serv.modify=
...
...
...

Команды опций .serv.inetOption.x. и .connection.inetOption.x. автоматически наследуются для дочерних опций к опции с ID равным x.

Синхронизация всегда выполняется для родительского сервиса. Для работы с информацией из дочерних сервисов необходимо использовать макрос loopServ(). При изменении IP-адреса, интерфейса, VLAN или набора дочерних сервисов вызываются команды удаления сервиса (с использованием старых параметров), а затем добавления (с использованием новых).

Макросы

Макросы вызываются с помощью последовательности ${имямакроса()}. Внутри фигурных скобок может быть логическое выражение, однако в этом случае, оно должно быть оформлено как правильный java-код, например: ${(newState()==1)?'вкл':'выкл'}, ${(newState()==1&&oldState()!=1)?(connectionEnable()+optionsEnable()):''}.

Доступны следующие макросы:

  • ip() - IP-адрес сессии или IP-адрес сервиса или начало диапазона IP-адресов сервиса;

  • ipList() - список IP-адресов диапазона сервиса, через запятую с пробелом, например: 192.168.1.1, 192.168.1.2, 192.168.1.3, 192.168.1.4, 192.168.1.5, ... ;

  • ipList(',') - список IP-адресов диапазона сервиса, через запятую, например: 192.168.1.1,192.168.1.2,192.168.1.3,192.168.1.4,192.168.1.5,... ;

  • net() - подсеть сервиса в виде строки, например, 192.168.1.1/24;

  • mask() - маска подсети сервиса, например, 24;

  • netmask() - маска подсети сервиса, например, 255.255.255.0;

  • netmaskWildcard() - wildcard подсети, например, 0.0.0.255;

  • ipNetHostMin() - первый адрес подсети, например, 192.168.1.1;

  • ipNetHostMax() - последний адрес подсети, например, 192.168.1.254;

  • ipNetBroadcast() - 192.168.1.255;

  • ipFrom() - первый адрес диапазона сервиса;

  • ipTo() - последний адрес диапазона сервиса;

  • contractId() - ID договора

  • contractTitle() - название договора

  • servId() - ID сервиса договора

  • servTitle() - название сервиса договора

  • servDeviceId() - ID устройства, к которому привязан сервис договора

  • vlan() - VLAN сервиса

  • svlan() - SVLAN устройства данного обработчика;

  • svlan(id_устройства) - SVLAN устройства с указанным ID;

  • svlan(servDeviceId()) - SVLAN устройства сервиса;

  • iface() - интерфейс сервиса

  • ifaceTitle() - название интерфейса сервиса

  • ifaceTitleBeforeColon() - название интерфейса до символа ':'

  • ifaceTitleAfterColon() - название интерфейса после символа ':'

  • mac() - MAC-адрес, в виде 0a:1b:2c:3d:4e:5f

  • macBytes() - MAC-адрес, в виде 0a1b2c3d4e5f

  • macDoted() - MAC-адрес, в виде 0a1b.2c3d.4e5f

  • identifier() - идентификатор сервиса

  • servParam(имя_параметра) - параметр сервиса

  • servParam(имя_параметра, значение_по_умолчанию) - параметр сервиса

  • option() - возвращает активную опцию (используя фильтр sa.inetOption.root, если он установлен);

  • option(id_опции) - возвращает активную опцию, которая будет потомком к указанной id_опции (т.е. таким образом можно отфильтровать до опции из определенной подгруппы);

  • param(опция, имя_параметра, значение_по_умолчанию) - параметр конфигурации опции (или одного из предка опции, т.к. параметры наследуются) или значение_по_умолчанию, если опция==null или параметр в опции не указан;

  • param(имя_параметра, значение_по_умолчанию) - параметр конфигурации устройства (или одного из предка устройства, т.к. параметры наследуются) или значение_по_умолчанию, если параметр в устройстве не указан;

  • ipGate() - шлюз из IP-ресурса

  • ipDns() - DNS из IP-ресурса

  • ipSubnetMask() - маска подсети из IP-ресурса

  • ipParam(имя_параметра) - параметр из конфигурации IP-ресурса

  • deviceId() - ID устройства данного обработчика;

  • deviceIP() - IP-адрес устройства (из поля Хост/порт) данного обработчика;

  • deviceIP(id_устройства) - IP-адрес устройства (из поля Хост/порт) с указанным ID;

  • deviceIdentifier() - идентификатор устройства данного обработчика;

  • deviceIdentifier(id_устройства) - идентификатор устройства с указанным ID;

  • deviceIdentifier(servDeviceId()) - идентификатор устройства сервиса;

  • deviceAttr(id_атрибута) - значение атрибута устройства данного обработчика;

  • deviceAttr(id_устройства, id_атрибута) - значение атрибута устройства с указанным ID;

  • deviceAttr(id_устройства, id_атрибута, значение_по_умолчанию) - значение атрибута устройства с указанным ID, если значения нет - возвращается значение по умолчанию;

  • connectionAgentDeviceId() - ID агентского устройства, к которому привязано соединение;

  • connectionDeviceId() - ID устройства, к которому привязано соединение;

  • translit(строка) - транслитерированное значение агрумента;

  • format(шаблон, арг1, ...) - форматирование, используя java.text.MessageFormat.format(String, Object...);

  • now() - текущее время, например, ${format('{0,date,yyyy}', now())} ;

  • nowString() - текущее время, в формате dd.MM.yyyy HH:mm:ss ;

  • setEndSequence(строка) - установка последовательности для ожидания приглашения ввода новой команды;

  • newState() - новое (требуемое) состояние сервиса/сессии;

  • oldState() - старое (текущее) состояние сервиса/сессии;

  • skipIfEmpty(val) - пропускает выполнение команды, если val - null или пустая строка;

  • skipIf(val) - пропускает выполнение команды, если val - true;

  • mikrotikLastIds() - результаты вызова предыдущей команды (для Mikrotik, все параметры id, через запятую)

  • loopServ(имя_параметра_шаблона_команды, использовать_в_цикле_родительский_сервис, выполять_как_отдельную_команду_или_построить_строку) - способ выполнения команд с использованием дочерних сервисов; выполняется цикл для всех дочерних сервисов, а также для родительского, если второй агрумент равен true; шаблоном команды является значение параметра, имя которого прописано в первом агрументе; выполняются как отдельные команды (true) или возвращается конкатенированная строка с указанным разделителем

    m_ipLoop=${net()}
    sa.commandSet.staticIP.serv.create.1=/queue/simple/remove\n=numbers=${contractId()}:${servId()}
    sa.commandSet.staticIP.serv.create.2=/queue/simple/add\n=name=${contractId()}:${servId()}\n=target=127.0.0.1/32,${loopServ('m_ipLoop', false, ',')}

Вызовы других наборов команд через макросы

Для каждого события есть три набора команд: "перед вызовом обычного набора", "обычный набор", "после вызова обычного набора". Например, sa.terminal.serv.modify, sa.terminal.serv.modify.before, sa.terminal.serv.modify.after. Наборы команд .before и .after предназначены в основном для вызова других наборов команд и изменения логики работы, в зависимости от ситуации. Например, стандартная предопределенная конфигурация выглядит так:

# После выполнения команд создания сервиса вызываем команды включения сервиса (serv.enable=) и опций (inetOption.x.enable=), если сервис должен быть включен
# или команды выключения сервиса (serv.disable=), если сервис должен быть отключен
sa.terminal.serv.create.after=${(newState()==1)?(serviceEnable()+optionsEnable()):serviceDisable()}
# Перед выполнением команд изменения сервиса (serv.modify=), если произошло включение сервиса - вызываем команды включения сервиса (serv.modify.enable=) и опций (inetOption.x.enable=)
sa.terminal.serv.modify.before=${(newState()==1&&oldState()!=1)?(serviceEnable()+optionsEnable()):''}
# после (serv.modify=), если произошло отключение сервиса - сначала отключаем опции (inetOption.x.disable=), затем - сервис (serv.modify.disable=);
# иначе, если переключения состояния нет - вызываем переключение опций (inetOption.x.disable= и (inetOption.x.enable=))
sa.terminal.serv.modify.after=${(newState()!=1)?(optionsDisable()+serviceDisable()):''};${(newState()==1&&oldState()==1)?(optionsSwitch()):''}
# Перед удалением сервиса вызываем команды отключения опций и отключения сервиса
sa.terminal.serv.cancel.before=${optionsDisable()+serviceDisable()}
# Перед выполнением команд изменения соединения, если произошло включение сервиса - вызываем команды включения сервиса и опций
sa.terminal.connection.modify.before=${(newState()==1&&oldState()!=1)?(connectionEnable()+optionsEnable()):''}
# после, если произошло отключение соединения - сначала отключаем опции, затем - соединение; иначе, если переключения состояния нет - вызываем переключение опций для соединения
sa.terminal.connection.modify.after=${(newState()!=1)?(optionsDisable()+connectionDisable()):''};${(newState()==1&&oldState()==1)?(optionsSwitch()):''}
 
# Перед выполнением команд старта соединения (sa.terminal.connection.onAccountingStart), если соединение должно быть отключено - выполняем команды
# отключения (sa.terminal.connection.modify.disable), иначе - выполняем команды включения (sa.terminal.connection.modify.enable)
sa.terminal.connection.onAccountingStart.before=${(newState()!=1)?(connectionDisable()):(connectionEnable())}
# После выполнения команд старта соединения, если соединени включено - вызываем команды включения опций соединения
sa.terminal.connection.onAccountingStart.after=${(newState()==1)?(optionsEnable()):''}
 
# Перед выполнением команд стопа соединения (sa.terminal.connection.onAccountingStop), если соединение было включено - вызываем команды отключения опций
sa.terminal.connection.onAccountingStop.before=${(oldState()==1)?(optionsDisable()):''}

Макросы serviceEnable(), serviceDisable(), connectionEnable(), connectionDisable(), connectionClose(), optionsEnable(), optionsDisable() вызывают соответствующие наборы команд. optionsSwitch() - вызывает наборы команд на отключение для старых опций и включение для новых. newState() и oldState() возвращают текущее и новое состояние сервиса или соединения, значения -1 - удален (для сервиса), 0 - отключен, 1 - включен.

Управление c помощью Mikrotik API

Оборудование Mikrotik предоставляет протокол для управления: https://wiki.mikrotik.com/wiki/Manual:API

Основное отличие от обычного консольного синтаксиса CLI состоит в том, что в командах заменяются пробелы на "/"( "/interface vlan print" превращается в "/interface/vlan/print"), параметры передаются по другому и нет возможности вызывать вложенные команды (однако есть запоминание id, которые вернул предыдущий вызов команды, и есть возможность использовать их в следующей команде).

Новый режим

Для управления с помощью Mikrotik API нужно указать:

sa.terminal.protocol=mikrotik-api

Для управления с помощью Mikrotik API-SSL нужно указать:

sa.terminal.protocol=mikrotik-api-ssl

В обоих случаях используется библиотека mikrotik-java. Она поддерживает как api так и api-ssl. Синтаксис несколько отличается от того, который описан в протоколе Mikrotik, примеры можно посмотреть здесь: https://github.com/GideonLeGrange/mikrotik-java.

Пример конфигурации:

# Порт подключения (если не указан, используется значение из поля Хост/порт)
sa.terminal.port=8728
# протокол подключения (telnet/ssh/tcp/mikrotik-api/shell)
sa.terminal.protocol=mikrotik-api
 
sa.port=8728
# команда выхода (отключения)
sa.terminal.exit=
# команды, вызываемые при включении сервиса (состояние стало "включено")
sa.terminal.serv.modify.enable.1=/interface/vlan/print where name=${ifaceTitle()}
sa.terminal.serv.modify.enable.2=/interface/vlan/enable .id=${mikrotikLastIds()}
# команды, вызываемые при отключение сервиса (состояние стало "отключено")
sa.terminal.serv.modify.disable.1=/interface/vlan/print where name=${ifaceTitle()}
sa.terminal.serv.modify.disable.2=/interface/vlan/disable .id=${mikrotikLastIds()}
 
# команды, вызываемые при включении опции ID=x (и дочерних опций по отношению к опции с ID=x) на сервисе
sa.terminal.serv.inetOption.1.enable.1=/queue/simple/print where target=${ifaceTitle()}
sa.terminal.serv.inetOption.1.enable.2=/queue/simple/set .id=${mikrotikLastIds()} max-limit= ${param(option() , 'mikrotik_rate','20M/20M')}

Еще пример:

# Команды, вызываемые при включении опции ID=x (и дочерних опций по отношению к ID=x) на соединении
sa.terminal.connection.inetOption.1.enable.1=/ip/firewall/address-list/print .proplist=.id where address=${ip()} and comment=bgb${servId()}
sa.terminal.connection.inetOption.1.enable.2=${skipIfEmpty(mikrotikLastIds())}/ip/firewall/address-list/remove .id=${mikrotikLastIds()}
sa.terminal.connection.inetOption.1.enable.3=/ip/firewall/address-list/add address=${ip()} list=${param(option(1),'list','512k_ip')} comment=bgb${servId()}
# команды, вызываемые при отключении опции ID=x (и дочерних опций по отношению к ID=x) на соединении
sa.terminal.connection.inetOption.1.disable.1=/ip/firewall/address-list/print .proplist=.id where address=${ip()} and comment=bgb${servId()}
sa.terminal.connection.inetOption.1.disable.2=${skipIfEmpty(mikrotikLastIds())}/ip/firewall/address-list/remove .id=${mikrotikLastIds()}

Старый режим

Так же есть вот такой режим работы:

sa.terminal.protocol=mikortik

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

Здесь команды задаются немного другим образом:

#Команды включения сервиса на устройстве
sa.terminal.serv.modify.enable.1=/interface/vlan/print\n?name=${ifaceTitle()}
sa.terminal.serv.modify.enable.2=/interface/vlan/enable\n.id=${mikrotikLastIds()}
#Выключение интерфейса
sa.terminal.serv.modify.disable.1=/interface/vlan/print\n?name=${ifaceTitle()}
sa.terminal.serv.modify.disable.2=/interface/vlan/disable\n.id=${mikrotikLastIds()}