Типы устройств

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

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

Типы устройств

Рассмотрим параметры, которые могут быть указаны в типе устройства:

  • Название - обозначение типа, используется в т.ч. для формирования имени устройства;

  • Флаг является источником данных - обозначает, что данный тип устройств генерирует данные о трафике, устройства с данным типом отображаются при переобработке логов;

  • Обработчик активации сервисов - динамический класс, реализующий определённый интерфейс по управлению устройством для блокировки/разблокировки сервиса, изменения его параметров;

  • Обработчик процессора протокола - динамический класс, реализующий определённый интерфейс по пред- и постобработке пакетов протокола, связанных с конкретным устройством (например, RAIDIUS или DHCP-пакетов).

  • Обработчик управления устройством - динамический класс, реализующий определённый интерфейс по дополнительному управлению устройством (например, получение uptime устройства на текущий момент, для отслеживания перезагрузки устройства).

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

Интерфейс
После корректировки параметров типов устройств для их перечитывания на серверах Access и Accounting необходимо нажать кнопку Перечитать конфигурацию на серверах, расположенную под деревом устройств. Данная кнопка позволяет оповещать сервера об изменениях только, когда конфигурация станет законченной. До тех пор сервера используют сохранённую в памяти конфигурацию.

Обработчик активации сервисов

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

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

В поставку модуля Inet входят стандартные реализации для Cisco, Redback, MPD и т.п., производящие интеграцию с соответствующими устройствами. Исходные коды находятся в Управлении динамическим кодом, в пакете ru.avantis.abilling.modules.inet.dyn. Данные классы перетираются при обновлении, поэтому для изменения логики класса необходимо расширить его или создать копию, но не изменять его напрямую.
Код
/**
 * Интерфейс обработчика активации сервисов.<br/>
 * <pre>Жизненный цикл:
 * init
 *
 *   connect
 *     serviceModify
 *     serviceModify
 *     serviceCancel
 *   disconnect
 *
 *   connect
 *     serviceCreate
 *     serviceModify
 *     connectionModify
 *     serviceModify
 *     connectionClose
 *   disconnect

 * destroy</pre>
 */
public interface ServiceActivator
{
    /**
     * Инициализация обработчика. Вызывается после создания объекта.
     * @param setup
     * @param moduleId
     * @param device
     * @param deviceType
     * @param config
     * @return
     * @throws Exception
     */
    public Object init( Setup setup, int moduleId, InetDevice device, InetDeviceType deviceType, ParameterMap config )
        throws Exception;

    /**
     * Утилизация обработчика. Вызывается перед уничтожением объекта.
     * @return
     * @throws Exception
     */
    public Object destroy()
        throws Exception;

    /**
     * Подключение к устройству для работы с ним.
     * @return
     * @throws Exception
     */
    public Object connect()
        throws Exception;

    /**
     * Отключение от устройства.
     * @return
     * @throws Exception
     */
    public Object disconnect()
        throws Exception;

    /**
     * Создание сервиса (по событию добавления или началу периода действия)
     * @param e
     * @return
     * @throws Exception
     */
    public Object serviceCreate( ServiceActivatorEvent e )
        throws Exception;

    /**
     * Изменение сервиса (подключение/отключение/изменение скорости).
     * Вызывается при изменении набора опций или изменении состояния сервиса
     * @see ServiceActivatorEvent
     * @see {@link ServiceActivatorEvent#getNewState()}
     * @param e
     * @return
     * @throws Exception
     */
    public Object serviceModify( ServiceActivatorEvent e )
        throws Exception;

    /**
     * Удаление сервиса (по событию удаления или окончания периода действия).
     * @param e
     * @return
     * @throws Exception
     */
    public Object serviceCancel( ServiceActivatorEvent e )
        throws Exception;

    /**
     * Изменение соединения.
     * Вызывается при изменении набора опции на соединении или при изменении состояния.<br/>
     * Обычно, при {@link ServiceActivatorEvent#getNewState()} == {@link InetServ#STATE_DISABLE} из этого метода происходит вызов метода {@link #connectionClose(ServiceActivatorEvent)}
     * @param e
     * @return
     * @throws Exception
     */
    public Object connectionModify( ServiceActivatorEvent e )
        throws Exception;

    /**
     * Закрытие (принудительное) соединения.<br/>
     * Обычно вызывается при {@link AccessCodes#TOO_MANY_SESSIONS_ERROR} или из метода {@link #connectionModify(ServiceActivatorEvent)}
     * @param e
     * @return
     * @throws Exception
     */
    public Object connectionClose( ServiceActivatorEvent e )
        throws Exception;

    /**
     * Обработка старта соединения.
     * @param event
     * @return
     * @throws Exception
     */
    public Object onAccountingStart( ServiceActivatorEvent event )
        throws Exception;

    /**
     * Обработка стопа соединения.
     * @param event
     * @return
     * @throws Exception
     */
    public Object onAccountingStop( ServiceActivatorEvent event )
        throws Exception;
}

Описание интерфейса ru.avantis.abilling.modules.inet.access.sa.ServiceActivator доступно в документации по API.

Параметры работы обработчика активации сервисов (прописываются в конфиге типа устройства или конфиге устройства):

Код
# кол-во задач выполняющихся одним блоком
#sa.batch.size=20
# время паузы между блоком задач
#sa.batch.pause.millis=0
# время ожидания завершения асинхронных задач
sa.batch.wait.millis=5000
# время ожидания новой задачи, до того как отключиться от устройства (вызвать disconnect у ServiceActivator)
sa.batch.waitNext.millis=5000

# пауза при ошибке синхронизации
sa.error.pause.millis=60000
# кол-во ошибок, возникающих подряд, для отправки оповещения (alarm)
sa.error.alarm.count=20
# кол-во ошибок, возникающих подряд, для отправки задачи в конец очереди
sa.error.redelivery.count=100
# таймаут задачи, которая не может выполниться из-за ошибок синхронизации
sa.error.redelivery.timeout=86400

sa.device.sync.allPath=.

Параметры пересинхронизации устройства (например, по определению перезагрузки устройства по uptime, который опрашивает обработчик управления устройством):

Код
# при полной синхронизации устройства - синхронизировать все связанные устройства или только данное
#sa.device.sync.allPath=1
# при полной синхронизации устройства нужно ли вызывать onAccountingStart для активных сессий
#sa.device.sync.onAccountingStart=0
# нужно ли при полной синхронизации устройства вызывать сначала serviceCancel и только потом serviceCreate
#sa.device.sync.cancelBeforeCreate=1
# вызов всегда connectionModify вместо connectionClose
#sa.connection.modifyInsteadClose=0
# вызов всегда connectionClose вместо connectionModify
#sa.connection.closeInsteadModify=0

Обработчик процессора протокола

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

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

В поставку модуля Inet входят стандартные обработчики для Cisco, Redback, MPD и т.п., позволяющие расширить интеграцию и функционал простым изменением конфигурации. Исходные коды находятся в Управлении динамическим кодом, в пакете ru.avantis.abilling.modules.inet.dyn. Данные классы перетираются при обновлении, поэтому для изменения логики класса необходимо расширить его или создать копию, но не изменять его напрямую.

Обработчик процессора протокола состоит из двух интерфейсов, которые он расширяет:

Код
/**
 * Обработчик RADIUS-запросов
 */
public interface RadiusProtocolHandler
{
    /**
     * Предобработка RADIUS-запроса Access-Request
     * @param request
     * @param response
     * @param connectionSet
     * @throws Exception
     */
    public void preprocessAccessRequest( RadiusPacket request, RadiusPacket response, ConnectionSet connectionSet )
        throws Exception;

    /**
     * Постобработка RADIUS-запроса Access-Request
     * @param request
     * @param response
     * @param connectionSet
     * @throws Exception
     */
    public void postprocessAccessRequest( RadiusPacket request, RadiusPacket response, ConnectionSet connectionSet )
        throws Exception;

    /**
     * Предобработка RADIUS-запроса Accounting-Request
     * @param request
     * @param response
     * @param connectionSet
     * @throws Exception
     */
    public void preprocessAccountingRequest( RadiusPacket request, RadiusPacket response, ConnectionSet connectionSet )
        throws Exception;

    /**
     * Постобработка RADIUS-запроса Accounting-Request
     * @param request
     * @param response
     * @param connectionSet
     * @throws Exception
     */
    public void postprocessAccountingRequest( RadiusPacket request, RadiusPacket response, ConnectionSet connectionSet )
        throws Exception;
}
/**
 * Обработчик DHCP-запросов
 */
public interface DhcpProtocolHandler
{
    /**
     * Предобработка DHCP-запроса
     * @param request
     * @param response
     * @throws Exception
     */
    public void preprocessDhcpRequest( DhcpPacket request, DhcpPacket response )
        throws Exception;

    /**
     * Постобработка DHCP-запроса
     * @param request
     * @param response
     * @throws Exception
     */
    public void postprocessDhcpRequest( DhcpPacket request, DhcpPacket response )
        throws Exception;
}
/**
 * Обработчик процессора протокола.
 * @see RadiusProtocolHandler
 * @author amir
 *
 */
public interface ProtocolHandler
    extends RadiusProtocolHandler, DhcpProtocolHandler
{
    public void init( Setup setup, int moduleId, InetDevice inetDevice, InetDeviceType inetDeviceType, ParameterMap config )
        throws Exception;
}

Например, такой обработчик будет подменять значение атрибута User-Name на значение идентификатора устройства + значение атрибута Nas-Port-Id, а значение пароля на "Password":

Код
import ru.avantis.abilling.kernel.network.radius.RadiusDictionary;
import ru.avantis.abilling.kernel.network.radius.RadiusPacket;
import ru.avantis.abilling.modules.inet.access.sa.ProtocolHandler;
import ru.avantis.abilling.modules.inet.access.sa.ProtocolHandlerAdapter;
import ru.avantis.abilling.modules.inet.api.common.bean.InetDevice;
import ru.avantis.abilling.modules.inet.api.common.bean.InetDeviceType;
import ru.avantis.abilling.server.util.Setup;
import ru.avantis.common.ParameterMap;
import ru.avantis.common.sql.ConnectionSet;

public class SomeProtocolHandler
    extends ProtocolHandlerAdapter
    implements ProtocolHandler
{
    private String deviceIdentifier;

    @Override
    public void init( Setup setup, int moduleId, InetDevice inetDevice, InetDeviceType inetDeviceType, ParameterMap deviceConfig )
        throws Exception
    {
        this.deviceIdentifier = inetDevice.getIdentifier();
    }

    @Override
    public void preprocessAccessRequest( RadiusPacket request, RadiusPacket response, ConnectionSet connectionSet )
        throws Exception
    {
        String nasPortId = request.getStringAttribute( -1, RadiusDictionary.NAS_Port_Id, null );

        request.setStringAttribute( -1, RadiusDictionary.User_Name, this.deviceIdentifier + ":" + nasPortId );
        request.setStringAttribute( -1, RadiusDictionary.User_Password, "Password" );
    }
}

Обработчик управления устройством

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

  • Uptime и обнаружение перезагрузки устройства;

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

Uptime и обнаружение перезагрузки устройства

На текущий момент главной функцией обработчика является получение текущего uptime устройства. Uptime необходим для определения перезагрузки устройства, чтобы при наступлении такого события Access мог синхронизировать заново все сервисы на этом устройстве.

В конфигурации типа устройства (или в самом устройстве) можно задать параметры выполнения команд:

# Пауза между выполнением команды после ошибки
#manage.error.pause=5
# Пауза между получением uptime
#manage.uptime.pause=120
# Пауза после ошибки, возникшей при получении uptime
#manage.uptime.error.pause=120

С модулем Inet поставляется динамический класс ru.avantis.abilling.modules.inet.dyn.device.snmp.SnmpDeviceManager, у него реализован метод uptime, который по SNMP у устройства получает его текущий uptime в секундах.

# Хост для отправки SNMP-запросов (по умолчанию хост, заданный в параметрах устройства Хост/порт)
#snmp.host=
# Порт для отправки SNMP-запросов (по умолчанию 161)
#snmp.port=
# Версия SNMP (по умолчанию 1)
#snmp.version=
# Сommunity (по умолчанию из параметра устройства Сommunity/Secret)
#snmp.community=public
# SNMP OID, из которого извлекается значение uptime (1.3.6.1.2.1.1.3.0)
#snmp.uptimeOid=1.3.6.1.2.1.1.3.0

После привязки обработчика управления устройством с реализованным методом uptime, все устройства с периодичностью manage.uptime.pause будут опрашиваться на время работы после перезагрузки. Для того, чтобы при определении очередной перезагрузки устройства Access выполнил синхронизацию сервисов, в конфигурации типа устройства (или в устройстве) необходимо прописать:

# Синхронизировать ли сервисы при обнаружении перезагрузки, 0 - не синхронизировать (по умолчанию), 1 - синхронизировать
# (для обнаружения перезагрузки в типе устройства должен быть установлен обработчик управления устройством)
#sa.device.sync.onReboot=1
# Вызывать ли при синхронизации для каждого сервиса, 0 - только serviceCreate или 1 (по умолчанию) -
# сначала serviceCancel, а затем serviceCreate
#sa.device.sync.cancelBeforeCreate=1

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

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

В контекстном меню дерева устройств доступен пункт выполнить команду. Данный пункт вызывает метод у Обработчика управления устройством (DeviceManager), указанном в типе устройства. Таким методом, в том числе, является uptime() из ru.avantis.abilling.modules.inet.dyn.device.snmp.SnmpDeviceManager, хотя нужен он не для ручного вызова из контекстного меню.

@Override
public Object uptime()
    throws Exception
{
    return snmpClient.get( uptimeOid, -1, Long.class );
}

Команды-методы можно именовать с помощью аннотации ru.avantis.oss.systems.inventory.resource.server.DeviceManagerMethod, в этом случае они будут сразу доступны в контекстном меню:

import ru.avantis.oss.systems.inventory.resource.server.DeviceManagerMethod;

@DeviceManagerMethod(title = "Перезагрузить")
public Object reboot()
    throws Exception
{
    return return snmpClient.set( new AsnObjectId( "1.2.3.4.5.6.7.8.9" ).getOid(), -1, Long.class );
}

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

@DeviceManagerMethod(title = "Статус")
public Object status()
    throws Exception
{
    return "browse:http://google.ru";
}

Или окно с подключением по telnet:

@DeviceManagerMethod(title = "Telnet")
public Object telnet()
    throws Exception
{
    return "telnet:google.ru 80";
}

Как аргумент в методе можно указать ru.avantis.abilling.modules.inet.access.manage.event.InetDeviceManageEvent, чтобы узнать, например, userId пользователя, выполняющего команду:

import ru.avantis.abilling.modules.inet.access.manage.event.InetDeviceManageEvent;

@DeviceManagerMethod(title = "Telnet")
public Object telnet2( InetDeviceManageEvent e )
    throws Exception
{
    logger.info( e.getUserId() );
    return "telnet:google.ru 80";
}

Пример:

package ru.provider.abilling.modules.inet.dyn.device;

import ru.avantis.abilling.server.util.Setup;
import ru.avantis.common.ParameterMap;
import ru.avantis.oss.systems.inventory.resource.common.DeviceManagerMethodType;
import ru.avantis.oss.systems.inventory.resource.common.bean.Device;
import ru.avantis.oss.systems.inventory.resource.common.bean.DeviceType;
import ru.avantis.oss.systems.inventory.resource.server.DeviceManagerMethod;

public class SnmpDeviceManager
    extends ru.avantis.abilling.modules.inet.dyn.device.snmp.SnmpDeviceManager
{
    protected ParameterMap deviceConfig;

    @Override
    public Object init( Setup setup, int moduleId, Device<?, ?> device, DeviceType deviceType, ParameterMap deviceConfig )
    {
        super.init( setup, moduleId, device, deviceType, deviceConfig );

        this.deviceConfig = deviceConfig;

        return null;
    }

    @DeviceManagerMethod(title = "Монитор", types = { DeviceManagerMethodType.DEVICE, DeviceManagerMethodType.ACCOUNT })
    public Object monitor()
    {
        return "browse:" + deviceConfig.get( "monitor.url", "http://zabbix.intranet.provider.ru/latest.php?hostid=$monitorHostId" )
                    .replaceAll( "\\$monitorHostId", deviceConfig.get( "monitor.hostId", "" ) );
    }

    @DeviceManagerMethod(title = "Telnet")
    public Object telnet()
    {
        return "telnet:" + deviceConfig.get( "telnet.host", this.host ) + " " + deviceConfig.getInt( "telnet.port", 23 );
    }
}

Без SNMP/uptime:

package ru.provider.abilling.modules.inet.dyn.device;

import java.util.List;

import ru.avantis.abilling.server.util.Setup;
import ru.avantis.common.ParameterMap;
import ru.avantis.oss.systems.inventory.resource.common.DeviceManagerMethodType;
import ru.avantis.oss.systems.inventory.resource.common.bean.Device;
import ru.avantis.oss.systems.inventory.resource.common.bean.DeviceType;
import ru.avantis.oss.systems.inventory.resource.server.DeviceManagerAdapter;
import ru.avantis.oss.systems.inventory.resource.server.DeviceManagerMethod;

public class DeviceManager
    extends DeviceManagerAdapter
    implements ru.avantis.oss.systems.inventory.resource.server.DeviceManager
{
    protected ParameterMap deviceConfig;

    protected String host;

    @Override
    public Object init( Setup setup, int moduleId, Device<?, ?> device, DeviceType deviceType, ParameterMap deviceConfig )
    throws Exception
    {
        super.init( setup, moduleId, device, deviceType, deviceConfig );

        this.deviceConfig = deviceConfig;

        final List<String[]> hosts = device.getHostsAsString();
        final String[] host = (hosts != null && hosts.size() > 0) ? hosts.get( 0 ) : null;

        this.host = deviceConfig.get( "snmp.host", host != null ? host[0] : device.getHost() );

        return null;
    }

    @DeviceManagerMethod(title = "Монитор", types = { DeviceManagerMethodType.DEVICE, DeviceManagerMethodType.ACCOUNT })
    public Object monitor()
    {
        return "browse:" + deviceConfig.get( "monitor.url", "http://zabbix.intranet.provider.ru/latest.php?hostid=$monitorHostId" )
                    .replaceAll( "\\$monitorHostId", deviceConfig.get( "monitor.hostId", "" ) );
    }

    @DeviceManagerMethod(title = "Telnet")
    public Object telnet()
    {
        return "telnet:" + deviceConfig.get( "telnet.host", this.host ) + " " + deviceConfig.getInt( "telnet.port", 23 );
    }
}

DeviceManagerMethodType.ACCOUNT в types означает, что пункт будет доступен на вкладке сервисов договора, в контестном меню сервиса, если сервис привязан к устройству данного типа.