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

Материал из BiTel WiKi

Перейти к: навигация, поиск

Содержание

Описание

Класс - обработчик управления устройством по SNMP (DeviceManager) взамен стандартного.

  • Умеет выдавать uptime, как и стандартный SnmpDeviceManager.
  • Умеет синхронизировать интерфейсы устройства и их snmp-индексы (нужны для учёта flow)
Полезен для схем, использующих интерфейсы, например Vlan per user + Cisco IP subscriber interface + ISG

Как пользоваться

  • Скачиваем библиотеку snmp4j (я использовал snmp4j-2.2.3.jar).
  • Копируем snmp4j-2.2.3.jar (или другую версию) в /usr/local/BGBillingServer/lib/app
  • Рестартуем сервер биллинга
  • Делаем ./update в BGInetAccess и BGInetAccounting и рестартуем их
  • Копируем себе в динамический код классы:
ru.dsi.bgbilling.modules.inet.dyn.device.snmp.IfaceSnmpDeviceManager
ru.dsi.bgbilling.modules.inet.dyn.device.snmp.SnmpDeviceManager
ru.dsi.bgbilling.modules.inet.dyn.device.snmp.SnmpClient
  • Компилируем динамический код
  • Указываем в типе устройства модуля Inet "Обработчик управления устройством: ru.dsi.bgbilling.modules.inet.dyn.device.snmp.IfaceSnmpDeviceManager"
  • Настраиваем конфигурацию устройства/типа устройства/родительского устройства (по усмотрению) - см раздел "Конфигурация"
  • Перечитываем конфигурацию устройств
  • Заходим в устройство с нашим обработчиком, кликаем правой кнопкой, выбираем "выполнить команду", пишем ifsync, смотрим результат

Файл:ifsync-manager.png

Файл:ifsync-command.png

Файл:ifsync-result.png

Файл:ifsync-ifaces.png

Принцип работы

Обработчик поддерживает команды:

  • uptime - аналогично стандартному обработчику, возвращает uptime устройства для определения стандартными средствами модуля Inet его перезагрузки. Можно отключить, тогда будет возвращать null (см конфигурацию). Отклчение может пригодиться, если устройство расформировано, чтобы обработчик не слал алармы.
  • ifsync - синхронизация интерфейсов и их ifIndex на роутере и в биллинге:
    • 1. Получаем по SNMP список соответствий: ifIndex->ifName интерфейсов на устройстве
    • 2. Для каждого полученного интерфейса проверяем, есть ли в биллинге на устройсве интерфейс с таким именем?
      • 2.1. Если нет, и он проходит через фильтр snmp.ifNameRegexpFilter - создаём с правильным ifIndex и категорией ip = snmp.ipCategory
      • 2.2. Если есть, то совпадает ли ifIndex?
        • 2.2.1. Если да, то ничего не делаем, всё ок.
        • 2.2.2. Если нет, то меняем ifIndex
    • 3. Оставшиеся необработанными интерфейсы в BG: если их нет на устройстве, то помечаем как отключенный и устанавливаем ifIndex=0

Настройка

Параметры конфигурации устройства/типа устройства:

  • для SnmpDeviceManager
#версия протокола (1 или 2 (для 2с))
snmp.version=1
#ip-адрес или hostname устройства. По-умолчанию берётся из поля "Хост" устройства в модуле Inet
snmp.host=
#порт SNMP устройства
snmp.port=161
#SNMP community. По-умолчанию - secret устройства в Inet
snmp.community=
#проверять ли uptime? Если 0, то метод uptime возвращает null
snmp.checkUptime=1
#OID для опроса uptime устройства
snmp.uptimeOid=1.3.6.1.2.1.1.3.0
#включает работу менеджера на этом устройстве
snmp.enable=1
  • для IfaceSnmpDeviceManager (в дополнение к настройкам из SnmpDeviceManager):
#oid для получения списка интерфейсов устройства
snmp.ifNameOid=1.3.6.1.2.1.31.1.1.1.1
#regexp для фильтрации интерфейсов. Например, если нам в биллинге нужны только интерфейсы, 
#начинающиеся с Gi или Fa, то указываем snmp.ifNameRegexpFilter=^(Gi|Fa).*
snmp.ifNameRegexpFilter=.*
#id категории ресурсов, которая будет указана для создаваемых интерфейсов
snmp.ipCategory=0
#если 1, то меняем индексы не с текущего момента, а со времени старта устройства, если это произошло сегодня.
#Предполагаем, что ifIndex могут меняться только при рестарте устройства.
snmp.substractUptime=1

Код

  • SnmpClient
package ru.dsi.bgbilling.modules.inet.dyn.device.snmp;
 
import org.snmp4j.CommunityTarget;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.TransportMapping;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.*;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import ru.bitel.bgbilling.common.BGException;
import org.apache.log4j.Logger;
 
import java.io.IOException;
import java.net.InetAddress;
 
public class SnmpClient {
 
    private CommunityTarget target;
    private Snmp snmp;
    private boolean destroyed = false;
    private static final Logger logger = Logger.getLogger(SnmpClient.class);
 
    public SnmpClient(Address address, String community, int snmpVersion) throws BGException {
        try{
            target = new CommunityTarget();
            target.setCommunity(new OctetString(community));
            target.setAddress(address);
            switch (snmpVersion){
                case 1:
                    target.setVersion(SnmpConstants.version1);
                    break;
                case 2:
                    target.setVersion(SnmpConstants.version2c);
                    break;
                default:
                    target.setVersion(SnmpConstants.version1);
            }
 
            TransportMapping transport = new DefaultUdpTransportMapping();
            this.snmp = new Snmp(transport);
            transport.listen();
        }
        catch (IOException ex)
        {
            throw new BGException(ex);
        }
    }
 
    public SnmpClient(String address, String community, int snmpVersion) throws BGException{
        this(new UdpAddress(address), community, snmpVersion);
    }
 
    public SnmpClient(InetAddress ip, int port, String community, int snmpVersion) throws BGException{
        this(new UdpAddress(ip, port), community, snmpVersion);
    }
    public <V> void walk(String oid, Class<V> clazz, Walker<V> walker) throws BGException {
        walk(new OID(oid), clazz, walker);
    }
 
    /**
     * @param oid target oid
     * @param clazz variable type
     * @param walker callback class
     * @param <V> variable type
     * @throws BGException
     */
    public <V> void walk(OID oid, Class<V> clazz, Walker<V> walker) throws BGException {
        try{
 
            PDU requestPDU = new PDU();
            requestPDU.add(new VariableBinding(oid));
            requestPDU.setType(PDU.GETNEXT);
 
            boolean finished = false;
 
            while (!finished){
                VariableBinding vb = null;
 
                ResponseEvent re = snmp.send(requestPDU, target);
                PDU responsePDU = re.getResponse();
 
                if (responsePDU != null){
                    vb = responsePDU.get(0);
                }
 
                if (responsePDU == null){
                    finished = true;
                }else if (responsePDU.getErrorStatus() != 0){
                    throw new BGException(responsePDU.getErrorStatusText());
                }else if (vb.getOid() == null){
                    finished = true;
                }else if (vb.getOid().size() < oid.size()){
                    finished = true;
                }else if (oid.leftMostCompare(oid.size(),vb.getOid()) != 0){
                    finished = true;
                }else if (Null.isExceptionSyntax(vb.getVariable().getSyntax())){
                    finished = true;
                }else if (vb.getOid().compareTo(oid) <= 0){
                    finished = true;
                }else{
                    walker.walk(vb.getOid(), this.castVariable(vb.getVariable(), clazz));
                    // Set up the variable binding for the next entry.
                    requestPDU.setRequestID(new Integer32(0));
                    requestPDU.set(0, vb);
                }
            }
 
        }catch (IOException ex){
            throw new BGException(ex);
        }
    }
 
    public <V> V get(OID oid, Class<V> clazz) throws BGException {
        try {
            PDU requestPDU = new PDU();
            requestPDU.add(new VariableBinding(oid));
            requestPDU.setType(PDU.GET);
 
            ResponseEvent re = snmp.send(requestPDU, target);
            if(re!=null){
                PDU responsePDU = re.getResponse();
 
                if (responsePDU != null){
                    VariableBinding vb = responsePDU.get(0);
                    return castVariable(vb.getVariable(), clazz);
                }
            }
 
        }catch (IOException ex){
            throw new BGException(ex);
        }
        return null;
    }
 
    public void destroy() throws IOException {
        this.destroyed = true;
 
        this.snmp.close();
        this.snmp = null;
    }
 
    protected void finalize()
            throws Throwable
    {
        if (!this.destroyed)
        {
            logger.warn("SnmpClient was not destroyed before finalize!");
            destroy();
        }
 
        super.finalize();
    }
 
    /**
     *
     * @param var variable
     * @param clazz type to cast to
     * @return value
     */
    private <V> V castVariable(Variable var, Class<V> clazz){
        //Если V - это Variable или его потомки, то отдаём в чистом виде
        if(Variable.class.isAssignableFrom(clazz)){
            return clazz.cast(var);
        }
        if(var instanceof UnsignedInteger32 || var instanceof Counter64){
            return clazz.cast(var.toLong());
        }
        if(var instanceof Integer32){
            return clazz.cast(var.toInt());
        }
        if(var instanceof IpAddress){
            if(clazz == InetAddress.class){
                return clazz.cast(((IpAddress) var).getInetAddress());
            }
            if(clazz == String.class){
                return clazz.cast(var.toString());
            }
            return clazz.cast(((IpAddress) var).toByteArray());
        }
        if(var instanceof Null){
            return null;
        }
        if(var instanceof OctetString){
            if(clazz == String.class){
                return clazz.cast(var.toString());
            }
            return clazz.cast(((OctetString) var).toByteArray());
        }
        if(var instanceof OID){
            if(clazz == String.class){
                return clazz.cast(var.toString());
            }
            return clazz.cast(((OID) var).toIntArray());
        }
        if(var instanceof VariantVariable){
            return clazz.cast(((VariantVariable) var).getVariable());
        }
 
        return clazz.cast(var.toString());
    }
 
}
  • SnmpDeviceManager
package ru.dsi.bgbilling.modules.inet.dyn.device.snmp;
 
import org.apache.log4j.Logger;
import org.snmp4j.smi.OID;
import ru.bitel.bgbilling.common.BGException;
import ru.bitel.bgbilling.server.util.Setup;
import ru.bitel.common.ParameterMap;
import ru.bitel.oss.systems.inventory.resource.common.bean.Device;
import ru.bitel.oss.systems.inventory.resource.common.bean.DeviceType;
import ru.bitel.oss.systems.inventory.resource.server.DeviceManager;
import ru.bitel.oss.systems.inventory.resource.server.DeviceManagerAdapter;
 
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;
 
/**
 * Базовый класс для работы с устройствами по SNMP
 * <pre>
 * команды:
 *      uptime - возвращает uptime устройства. Используется модулем Inet для определения перезагрузки.
 *
 * параметры:
 *      snmp.version=1
 *          - версия протокола (1 или 2 (для 2с))
 *      snmp.host=
 *          - ip-адрес или hostname устройства. По-умолчанию берётся из поля "Хост" устройства в модуле Inet
 *      snmp.port=161
 *          - порт SNMP устройства
 *      snmp.community=
 *          - SNMP community. По-умолчанию - secret устройства в Inet
 *      snmp.checkUptime=1
 *          - проверять ли uptime? Если 0, то метод uptime возвращает null
 *      snmp.uptimeOid=1.3.6.1.2.1.1.3.0
 *          - OID для опроса uptime устройства
 *      snmp.enable=1
 *          - включает работу менеджера на этом устройстве
 *
 * </pre>
 */
public class SnmpDeviceManager extends DeviceManagerAdapter
        implements DeviceManager {
    protected static final Logger logger = Logger.getLogger( SnmpDeviceManager.class );
 
 
    String snmpHost;
 
    private int snmpVersion;
    private int snmpPort;
    private String snmpCommunity;
 
    /**
     * выдавать ли uptime устройства
     * полезно для заведомо отключенных устройств, чтобы не слать бесконечно алармы о недоступности
     */
    private boolean checkUptime;
    protected SnmpClient snmpClient;
    protected boolean enabled;//Работаем?
 
    private OID uptimeOid;
 
    @Override
    public Object init( Setup setup, int moduleId, Device<?, ?> device, DeviceType deviceType, ParameterMap deviceConfig )
    {
        logger.info( "INIT" );
 
        final List<String[]> hosts = device.getHostsAsString();
        final String[] host = (hosts != null && hosts.size() > 0) ? hosts.get( 0 ) : null;
 
        this.snmpVersion = deviceConfig.getInt( "snmp.version", 1 );
        this.snmpHost = deviceConfig.get("snmp.host", host != null ? host[0] : device.getHost());
        this.snmpPort = deviceConfig.getInt("snmp.port", 161);
        this.snmpCommunity = deviceConfig.get("snmp.community", device.getSecret());
        this.checkUptime = deviceConfig.getBoolean("snmp.checkUptime", true);
        this.uptimeOid = new OID(deviceConfig.get( "snmp.uptimeOid", "1.3.6.1.2.1.1.3.0" ));
        this.snmpClient = null;
        this.enabled = deviceConfig.getBoolean("snmp.enable", true);
 
        return null;
    }
 
    @Override
    public Object destroy()
            throws Exception
    {
        if( this.snmpClient != null){
            this.snmpClient.destroy();
            this.snmpClient = null;
        }
        return null;
    }
 
    @Override
    public Object connect()
            throws Exception
    {
        if(!this.enabled){return null;}
        try {
            this.snmpClient = new SnmpClient(
                    InetAddress.getByName(this.snmpHost),
                    this.snmpPort,
                    this.snmpCommunity,
                    this.snmpVersion);
        } catch (BGException e) {
            logger.error(e.getMessage(), e);
        } catch (UnknownHostException e) {
            logger.error(e.getMessage(), e);
        }
 
        return null;
    }
 
    @Override
    public Object disconnect()
            throws Exception
    {
        if(snmpClient!=null){
            this.snmpClient.destroy();
        }
        return null;
    }
 
    @Override
    public Integer uptime()
            throws Exception
    {
        if(this.checkUptime && this.enabled){
            return snmpClient.get( this.uptimeOid, Integer.class );
        }else{
            return null;
        }
    }
 
}
  • IfaceSnmpDeviceManager
package ru.dsi.bgbilling.modules.inet.dyn.device.snmp;
 
import bitel.billing.common.TimeUtils;
import org.snmp4j.smi.OID;
import ru.bitel.bgbilling.kernel.container.managed.ServerContext;
import ru.bitel.bgbilling.server.util.Setup;
import ru.bitel.common.ParameterMap;
import ru.bitel.common.worker.ThreadContext;
import ru.bitel.oss.systems.inventory.resource.common.DeviceInterfaceService;
import ru.bitel.oss.systems.inventory.resource.common.bean.Device;
import ru.bitel.oss.systems.inventory.resource.common.bean.DeviceInterface;
import ru.bitel.oss.systems.inventory.resource.common.bean.DeviceInterfaceIndex;
import ru.bitel.oss.systems.inventory.resource.common.bean.DeviceType;
import ru.bitel.oss.systems.inventory.resource.server.DeviceManager;
 
import java.util.*;
 
/**
 * @author cromeshnic@gmail.com
 *
 * Класс для работы с интерфейсами их индексами по SNMP
 * <pre>
 * команды:
 *      ifsync - синхронизация интерфейсов и их ifIndex на роутере и в биллинге:
 *          1. Получаем по SNMP список соответствий: ifIndex->ifName интерфейсов на устройстве
 *          2. Для каждого полученного интерфейса проверяем,
 *              есть ли в биллинге на устройсве интерфейс с таким именем?
 *          2.1. Если нет, и он проходит через фильтр snmp.ifNameRegexpFilter - создаём с правильным ifIndex и категорией ip = snmp.ipCategory
 *          2.2. Если есть, то совпадает ли ifIndex?
 *          2.2.1. Если да, то ничего не делаем, всё ок.
 *          2.2.2. Если нет, то меняем ifIndex
 *          3. Оставшиеся необработанными интерфейсы в BG: если их нет на устройстве, то
 *              помечаем как отключенный и устанавливаем ifIndex=0
 * параметры:
 *      snmp.ifNameOid=1.3.6.1.2.1.31.1.1.1.1
 *          - oid для получения списка интерфейсов устройства
 *      snmp.ifNameRegexpFilter=.*
 *          - regexp для фильтрации интерфейсов. Например, если нам в биллинге нужны только интерфейсы,
 *              начинающиеся с Gi или Fa, то указываем snmp.ifNameRegexpFilter=^(Gi|Fa).*
 *      snmp.ipCategory=0
 *          - id категории ресурсов, которая будет указана для создаваемых интерфейсов
 *      snmp.substractUptime=1
 *          - если 1, то меняем индексы не с текущего момента, а со времени старта устройства, если это произошло сегодня.
 *              Предполагаем, что ifIndex могут меняться только при рестарте устройства.
 * </pre>
 * @see SnmpDeviceManager
 */
public class IfaceSnmpDeviceManager extends SnmpDeviceManager implements DeviceManager {
    //TODO - резервирование/закрытие датой расформированных интерфейсов : http://forum.bitel.ru/viewtopic.php?p=71924#p71924
 
    private OID ifNameOid;
    /**
     * Фильтр имён интерфейсов
     * Если имя не соответствует регэкспу, то не заводим его в биллинге
     */
    private String ifNameRegexpFilter;
    private int ipCategory;
    private boolean subtractUptimeForIfIndex;
    protected int mid;
    protected int deviceId;
 
    @Override
    public Object init(Setup setup, int moduleId, Device<?, ?> device, DeviceType deviceType, ParameterMap deviceConfig) {
        super.init(setup, moduleId, device, deviceType, deviceConfig);
        this.mid = moduleId;
        this.deviceId = device.getId();
        this.ifNameOid = new OID(deviceConfig.get( "snmp.ifNameOid", "1.3.6.1.2.1.31.1.1.1.1"));
        this.ifNameRegexpFilter = deviceConfig.get("snmp.ifNameRegexpFilter", ".*");
        //Категория IP, выставляемая новым интерфейсам
        this.ipCategory = deviceConfig.getInt("snmp.ipCategory", 0);
        //Вычитать ли из текущего времени uptime устройства при проставлении даты/времени для ifIndex?
        //(не дальше, чем за границу суток)
        this.subtractUptimeForIfIndex = deviceConfig.getBoolean("snmp.substractUptime", true);
 
        return null;
    }
 
    public String ifsync()
            throws Exception
    {
        if(!this.enabled){
            return "SNMP manager disabled on device";
        }
        StringBuilder result = new StringBuilder();
        ServerContext ctx = ThreadContext.get();
        DeviceInterfaceService devicePortService = ctx.getService(DeviceInterfaceService.class, this.mid);
        //Интерфейсы устройства в биллинге
        Map<String, DeviceInterface> ifaces = new HashMap<String, DeviceInterface>();
        int maxPortNumber = 0;
        for (DeviceInterface deviceInterface : devicePortService.devicePortList(this.deviceId, false)) {
            //Не дублируются ли интерфейсы по именам?
            if(ifaces.get(deviceInterface.getTitle())!=null){
                logger.error("duplicate iface names on device " + this.deviceId + ": " + deviceInterface.getTitle() +
                        " (ports:" + deviceInterface.getPort() + "," + ifaces.get(deviceInterface.getTitle()).getPort() + ")");
                result.append("! duplicate bg ifaces: ")
                        .append(deviceInterface.getTitle())
                        .append(" (ports:")
                        .append(deviceInterface.getPort())
                        .append(",")
                        .append(ifaces.get(deviceInterface.getTitle()).getPort())
                        .append(")\n");
            }
            ifaces.put(deviceInterface.getTitle(), deviceInterface);
            //Определяем максимальный номер порта
            if(deviceInterface.getPort()>maxPortNumber){
                maxPortNumber=deviceInterface.getPort();
            }
        }
 
        //Интерфейсы, полученные с устройства
        final Map<String, Integer> ifaceTitleToIfIndexMap = new HashMap<String, Integer>();
        this.snmpClient.walk(this.ifNameOid, String.class, new Walker<String>() {
            @Override
            public void walk(OID oid, String value) {
                //ifName -> ifIndex
                ifaceTitleToIfIndexMap.put(value, oid.last());
            }
        });
        Calendar now = Calendar.getInstance();
 
        //Получаем uptime (в TimeTicks - 0.01 секунды)
        Integer uptime = this.uptime();
        Date start;
        Calendar cal;
        if(uptime==null || !this.subtractUptimeForIfIndex){//нет данных оп uptime, либо мы их не должны использовать
            start = (Date)now.getTime().clone();
        }else{
            cal = (Calendar)now.clone();
            cal.add(Calendar.SECOND, uptime/-100);
            if(TimeUtils.daysDelta(now, cal)!=0){//Девайс был рестартован не сегодня
                start = (Date)now.getTime().clone(); //Меняем индексы с текущего момента
                logger.info("device "+deviceId+" started "+TimeUtils.formatDate(cal));
            }else{
                start = (Date)cal.getTime().clone();
            }
        }
        cal = Calendar.getInstance();
        cal.setTime(start);
        cal.add(Calendar.SECOND, -1);
        Date nowMinusSecond = cal.getTime();
 
        DeviceInterface iface;
        DeviceInterfaceIndex deviceInterfaceIndex;
        Integer ifaceIndex;
        List<DeviceInterfaceIndex> indexList;
        //1. Перебираем все полученные интерфейсы
        for (Map.Entry<String, Integer> nameToIndex : ifaceTitleToIfIndexMap.entrySet()) {
            //1.1 Ищем интерфейс в биллинге по его имени
            iface = ifaces.get(nameToIndex.getKey());
            if(iface == null){//1.1.1 Не нашли - заводим в
                // Удовлетворяет ли интерфейс фильтру? Если нет - не заводим его
                if(!nameToIndex.getKey().matches(this.ifNameRegexpFilter)){
                    logger.info("skip snmp iface "+nameToIndex.getKey()+" on deviceId="+this.deviceId+" due to regexp "+this.ifNameRegexpFilter);
                    continue;
                }
                iface = new DeviceInterface();
                iface.setPort(++maxPortNumber);//increment port number (port number != ifindex here !!!)
                iface.setTitle(nameToIndex.getKey());
                iface.setComment("");
                iface.setStatus(1);//1 - Доступен, 0 - Зарезервирован
                iface.setDeviceId(this.deviceId);
                iface.setIpCategoryId(this.ipCategory);
                deviceInterfaceIndex = new DeviceInterfaceIndex();
                deviceInterfaceIndex.setId(0);//id<=0 => new
                deviceInterfaceIndex.setPort(maxPortNumber);//вроде бы не обязательно, но пусть будет
                deviceInterfaceIndex.setDeviceId(this.deviceId);//вроде бы не обязательно, но пусть будет
                deviceInterfaceIndex.setIndex(nameToIndex.getValue());
                deviceInterfaceIndex.setTimeFrom(now.getTime());
                deviceInterfaceIndex.setTimeTo(null);
                iface.setIndexList(Collections.singletonList(deviceInterfaceIndex));
                devicePortService.devicePortUpdate(iface);
                logger.info("adding new iface "+iface.getTitle()+" with ifIndex="+nameToIndex.getValue()+" from "+ TimeUtils.formatFullDate(now.getTime()));
                result.append("+ ")
                    .append(iface.getTitle())
                    .append(" (")
                    .append(nameToIndex.getValue())
                    .append(") from ")
                    .append(TimeUtils.formatFullDate(now.getTime()))
                    .append("\n");
            }else{//1.1.2 Нашли такой интерфейс, проверяем, изменился ли у него ifindex
                //Если интерфейс при этом не удовлетворяет фильтру, то пишем об этом в лог
                if(!nameToIndex.getKey().matches(this.ifNameRegexpFilter)){
                    logger.warn("existing bg iface "+nameToIndex.getKey()+" on deviceId="+this.deviceId+" doesn't match regexp "+this.ifNameRegexpFilter);
                    result.append("? ")
                            .append(iface.getTitle())
                            .append(" - нестандартный интерфейс в BG\n");
                }
                indexList = iface.getIndexList();
                deviceInterfaceIndex = this.getCurrentIndex(now.getTime(), indexList);
                if(deviceInterfaceIndex!=null){
                    ifaceIndex = deviceInterfaceIndex.getIndex();
                }else{//По-умолчанию, индекс интерфейса - это его порт
                    ifaceIndex = iface.getPort();
                }
                if(!ifaceIndex.equals(nameToIndex.getValue())){//Индекс поменялся! Нужно обновить
                    //Закрываем датой предыдущий ifIndex, если есть
                    if(deviceInterfaceIndex!=null){
                        deviceInterfaceIndex.setTimeTo(nowMinusSecond);
                    }
                    if(indexList==null){
                        indexList = new ArrayList<DeviceInterfaceIndex>();
                        iface.setIndexList(indexList);
                    }
                    deviceInterfaceIndex = new DeviceInterfaceIndex();
                    deviceInterfaceIndex.setId(0);//id<=0 => new
                    deviceInterfaceIndex.setPort(iface.getPort());//вроде бы не обязательно, но пусть будет
                    deviceInterfaceIndex.setDeviceId(iface.getDeviceId());//вроде бы не обязательно, но пусть будет
                    deviceInterfaceIndex.setIndex(nameToIndex.getValue());
                    deviceInterfaceIndex.setTimeFrom(start);
                    deviceInterfaceIndex.setTimeTo(null);
                    indexList.add(deviceInterfaceIndex);
                    devicePortService.devicePortUpdate(iface);
                    logger.info("updating iface "+iface.getTitle()+": ifIndex="+nameToIndex.getValue()+" (was "+ifaceIndex+") from "+ TimeUtils.formatFullDate(start));
                    result.append("* ")
                        .append(iface.getTitle())
                        .append(" (")
                        .append(ifaceIndex)
                        .append("->")
                        .append(nameToIndex.getValue())
                        .append(") from ")
                        .append(TimeUtils.formatFullDate(start))
                        .append("\n");
                }
            }
        }
        //2. Теперь перебираем интерфейсы в BG, чтобы обработать оставшиеся
        for(Map.Entry<String, DeviceInterface> ifaceEntry : ifaces.entrySet()) {
            iface = ifaceEntry.getValue();//iface в BG
            //2.1 Ищем индекс интерфейса в полученном по SNMP списке
            ifaceIndex = ifaceTitleToIfIndexMap.get(ifaceEntry.getKey());
            if(ifaceIndex==null){//Есть в BG, но не получен с устройства
                //зануляем ifIndex, чтобы он гарантированно ни с кем не пересекался
                indexList = iface.getIndexList();
                deviceInterfaceIndex = getCurrentIndex(now.getTime(), indexList);
                if(deviceInterfaceIndex==null){
                    ifaceIndex = iface.getPort();
                }else{
                    ifaceIndex = deviceInterfaceIndex.getIndex();
                }
                if(ifaceIndex!=0){
                    //Зануляем!
                    if(deviceInterfaceIndex!=null){
                        deviceInterfaceIndex.setTimeTo(nowMinusSecond);
                    }
                    if(indexList==null){
                        indexList = new ArrayList<DeviceInterfaceIndex>();
                        iface.setIndexList(indexList);
                    }
                    deviceInterfaceIndex = new DeviceInterfaceIndex();
                    deviceInterfaceIndex.setId(0);//id<=0 => new
                    deviceInterfaceIndex.setPort(iface.getPort());//вроде бы не обязательно, но пусть будет
                    deviceInterfaceIndex.setDeviceId(iface.getDeviceId());//вроде бы не обязательно, но пусть будет
                    deviceInterfaceIndex.setIndex(0);
                    deviceInterfaceIndex.setTimeFrom(start);
                    deviceInterfaceIndex.setTimeTo(null);
                    indexList.add(deviceInterfaceIndex);
                    iface.setComment("deleted");
                    devicePortService.devicePortUpdate(iface);
                    logger.info("updating iface "+iface.getTitle()+": ifIndex=0 (was "+ifaceIndex+") from "+ TimeUtils.formatFullDate(start));
                    result.append("- ")
                            .append(iface.getTitle())
                            .append(" (")
                            .append(ifaceIndex)
                            .append("->0) from ")
                            .append(TimeUtils.formatFullDate(start))
                            .append("\n");
                }
            }
        }
        if(result.length()==0){
            return "no changes";
        }
 
        return result.toString();
    }
 
    protected DeviceInterfaceIndex getCurrentIndex(Date now, List<DeviceInterfaceIndex> deviceInterfaceIndexList){
        if(deviceInterfaceIndexList!=null){
            for (DeviceInterfaceIndex deviceInterfaceIndex : deviceInterfaceIndexList) {
                if(TimeUtils.timeInRange(now, deviceInterfaceIndex.getTimeFrom(), deviceInterfaceIndex.getTimeTo())){
                    return deviceInterfaceIndex;
                }
            }
        }
        return null;
    }
}

P.S.

  • Использовал snmp4j вместо идущего с биллингом westhawk snmp, т.к. в последнем нельзя сделать GETNEXT
  • Индекс в периоде на интерфейсе не работает на момент написания статьи : [1]
  • Подробнее про индексы интерфейсов в Cisco: [2]
  • Как это выглядело [в модуле IPN]
  • Проверка доступности устройства по SNMP:
#snmpwalk -On -v 2c -c public r-test-sre 1.3.6.1.2.1.31.1.1.1.1
.1.3.6.1.2.1.31.1.1.1.1.1 = STRING: Fa0/0
.1.3.6.1.2.1.31.1.1.1.1.2 = STRING: Gi0/0
.1.3.6.1.2.1.31.1.1.1.1.3 = STRING: Gi0/1
.1.3.6.1.2.1.31.1.1.1.1.4 = STRING: Gi0/2
.1.3.6.1.2.1.31.1.1.1.1.5 = STRING: Gi0/3
.1.3.6.1.2.1.31.1.1.1.1.7 = STRING: Nu0
.1.3.6.1.2.1.31.1.1.1.1.8 = STRING: Lo1
.1.3.6.1.2.1.31.1.1.1.1.17 = STRING: E1 1/0
.1.3.6.1.2.1.31.1.1.1.1.18 = STRING: E1 1/1
.1.3.6.1.2.1.31.1.1.1.1.19 = STRING: E1 1/2
.1.3.6.1.2.1.31.1.1.1.1.20 = STRING: E1 1/3
.1.3.6.1.2.1.31.1.1.1.1.21 = STRING: E1 1/4
.1.3.6.1.2.1.31.1.1.1.1.22 = STRING: E1 1/5
.1.3.6.1.2.1.31.1.1.1.1.23 = STRING: E1 1/6
.1.3.6.1.2.1.31.1.1.1.1.24 = STRING: E1 1/7
.1.3.6.1.2.1.31.1.1.1.1.27 = STRING: Gi0/0.5
.1.3.6.1.2.1.31.1.1.1.1.28 = STRING: Vt1
.1.3.6.1.2.1.31.1.1.1.1.29 = STRING: Vi1
.1.3.6.1.2.1.31.1.1.1.1.31 = STRING: Gi0/0.7
.1.3.6.1.2.1.31.1.1.1.1.32 = STRING: Vt2
.1.3.6.1.2.1.31.1.1.1.1.33 = STRING: Gi0/0.550
.1.3.6.1.2.1.31.1.1.1.1.36 = STRING: Gi0/0.1111
...

--Cromeshnic 08:16, 23 декабря 2013 (UTC)

Личные инструменты