Обработчик управления устройством с синхронизацией интерфейсов и их индексов - версия 2
Материал из BiTel WiKi
Stark (Обсуждение | вклад) (Новая страница: «Решение сделано на основе вот этого http://wiki.bitel.ru/index.php/%D0%9E%D0%B1%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%87%D0%B8%D0%BA_%D1%83%…») |
Stark (Обсуждение | вклад) |
||
Строка 1: | Строка 1: | ||
Решение сделано на основе вот этого | Решение сделано на основе вот этого | ||
http://wiki.bitel.ru/index.php/%D0%9E%D0%B1%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%87%D0%B8%D0%BA_%D1%83%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F_%D1%83%D1%81%D1%82%D1%80%D0%BE%D0%B9%D1%81%D1%82%D0%B2%D0%BE%D0%BC_%D1%81_%D1%81%D0%B8%D0%BD%D1%85%D1%80%D0%BE%D0%BD%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B5%D0%B9_%D0%B8%D0%BD%D1%82%D0%B5%D1%80%D1%84%D0%B5%D0%B9%D1%81%D0%BE%D0%B2_%D0%B8_%D0%B8%D1%85_%D0%B8%D0%BD%D0%B4%D0%B5%D0%BA%D1%81%D0%BE%D0%B2 | http://wiki.bitel.ru/index.php/%D0%9E%D0%B1%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%87%D0%B8%D0%BA_%D1%83%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F_%D1%83%D1%81%D1%82%D1%80%D0%BE%D0%B9%D1%81%D1%82%D0%B2%D0%BE%D0%BC_%D1%81_%D1%81%D0%B8%D0%BD%D1%85%D1%80%D0%BE%D0%BD%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B5%D0%B9_%D0%B8%D0%BD%D1%82%D0%B5%D1%80%D1%84%D0%B5%D0%B9%D1%81%D0%BE%D0%B2_%D0%B8_%D0%B8%D1%85_%D0%B8%D0%BD%D0%B4%D0%B5%D0%BA%D1%81%D0%BE%D0%B2 | ||
+ | |||
+ | |||
Отличия: | Отличия: | ||
- | 1) Не нужна | + | 1) Не нужна сторонняя библиотека у snmp4j, используется стандартная из поставки. Поэтому не нудны классы SnmpClient и SnmpDeviceManager из того решения. А класс IfaceSnmpDeviceManager немного изменен и переименован в IfaceSnmpDeviceManager. Он наследуется от стандартного ru.bitel.bgbilling.modules.inet.dyn.device.snmp.SnmpDeviceManager и используется та же самая конфигурация при получении uptime. Описан тут: |
+ | https://docs.bitel.ru/pages/viewpage.action?pageId=43385967 | ||
- | 2) В случае если интерфейс в биллинге не найден, то создается интерфейс с номером, который | + | 2) В случае если интерфейс в биллинге не найден, то создается интерфейс с номером, который считываться как group(1) из snmp.ifNameRegexpFilter=GigabitEthernet0/0/2\.(\d+). В старом решении просто присваивался |
(максимальный номер интерфейса) + 1. | (максимальный номер интерфейса) + 1. | ||
+ | |||
+ | 3) На устройстве не нужно явно вбивать команду ifsync, во всплывающем меню уже есть пункт "сихронизировать интерфейсы". Но этого делать не надо - смотрим следующий пункт. | ||
+ | |||
+ | 4) При перезагрузке устройства не нужно заходить и вызывать пункт "сихронизировать интерфейсы". так как DeviceManager сам это определит и вызовет( метод onReboot). Вызов вручную тоже работает. | ||
+ | |||
+ | Проверялось на сisco и juniper. Использовалось в схеме сбора статистики по snmp. | ||
+ | |||
+ | = Описание = | ||
+ | <p>Класс - обработчик управления устройством по SNMP (DeviceManager) взамен стандартного. | ||
+ | *Умеет синхронизировать интерфейсы устройства и их snmp-индексы (нужны для учёта flow/snmp) | ||
+ | </p> | ||
+ | |||
+ | = Как пользоваться = | ||
+ | |||
+ | <p> | ||
+ | *Копируем себе в динамический код класс: | ||
+ | <source lang=bash> | ||
+ | ru.dsi.bgbilling.modules.inet.dyn.device.snmp.IndexSnmpDeviceManager | ||
+ | </source> | ||
+ | *Компилируем динамический код | ||
+ | *Указываем в типе устройства модуля Inet "Обработчик управления устройством: ru.dsi.bgbilling.modules.inet.dyn.device.snmp.IndexSnmpDeviceManager" | ||
+ | *Настраиваем конфигурацию устройства/типа устройства/родительского устройства (по усмотрению) - см раздел "Конфигурация" | ||
+ | *Перечитываем конфигурацию устройств | ||
+ | *При обнаружении перезагрузки ( а access скорее всего сразу обнаружит это после перечитывания конфигурации) IndexSnmpDeviceManager сам сихронизует интерфейсы и индексы. | ||
+ | *Чтобы запустить вручную заходим в устройство с нашим обработчиком, кликаем правой кнопкой, выбираем "сихронизировать интерфейсы" смотрим результат | ||
+ | [[Файл:ifsync-manager.png]] | ||
+ | |||
+ | [[Файл:ifsync-command.png]] | ||
+ | |||
+ | [[Файл:ifsync-result.png]] | ||
+ | |||
+ | [[Файл:ifsync-ifaces.png]] | ||
+ | </p> | ||
+ | |||
+ | = Принцип работы = | ||
+ | <p> | ||
+ | Обработчик поддерживает команды: | ||
+ | *ifsync - синхронизация интерфейсов и их ifIndex на роутере и в биллинге: | ||
+ | ** 1. Получаем по SNMP список соответствий: ifIndex->ifName интерфейсов на устройстве | ||
+ | ** 2. Для каждого полученного интерфейса проверяем, есть ли в биллинге на устройсве интерфейс с таким именем? | ||
+ | *** 2.1. Если нет, и он проходит через фильтр snmp.ifNameRegexpFilter - создаём с правильным ifIndex и категорией ip = snmp.ipCategory и номер интерфейса находим как group(1) у заданного snmp.ifNameRegexpFilter. | ||
+ | *** 2.2. Если есть, то совпадает ли ifIndex? | ||
+ | **** 2.2.1. Если да, то ничего не делаем, всё ок. | ||
+ | **** 2.2.2. Если нет, то меняем ifIndex | ||
+ | ** 3. Оставшиеся необработанными интерфейсы в BG: если их нет на устройстве, то помечаем как отключенный и устанавливаем ifIndex=0 | ||
+ | </p> | ||
+ | |||
+ | = Настройка = | ||
+ | <p> | ||
+ | Параметры конфигурации устройства/типа устройства: | ||
+ | *для SnmpDeviceManager | ||
+ | <source lang="ini"> | ||
+ | #версия протокола (1 или 2 (для 2с)) | ||
+ | snmp.version=1 | ||
+ | #ip-адрес или hostname устройства. По-умолчанию берётся из поля "Хост" устройства в модуле Inet | ||
+ | snmp.host= | ||
+ | #порт SNMP устройства | ||
+ | snmp.port=161 | ||
+ | #SNMP community. По-умолчанию - secret устройства в Inet | ||
+ | snmp.community= | ||
+ | #OID для опроса uptime устройства. По умолчанию - 1.3.6.1.2.1.1.3.0 | ||
+ | snmp.uptimeOid= | ||
+ | </source> | ||
+ | *для IndexSnmpDeviceManager (в дополнение к настройкам из SnmpDeviceManager): | ||
+ | <source lang="ini"> | ||
+ | #включает работу менеджера на этом устройстве(по умолчанию включено) | ||
+ | snmp.enable=1 | ||
+ | #oid для получения списка интерфейсов устройства | ||
+ | snmp.ifNameOid=1.3.6.1.2.1.2.2.1.2 | ||
+ | #regexp для фильтрации интерфейсов. Например, если нам в биллинге нужны только интерфейсы вида GigabitEthernet0/0/2.1122, | ||
+ | #то указываем snmp.ifNameRegexpFilter=GigabitEthernet0/0/2\.(\d+) | ||
+ | #Группировку тут надо обязательно указывать, подразумевается что в ней идет номер индекса, мы его обновляем на интерфейсе. | ||
+ | snmp.ifNameRegexpFilter=(.*) | ||
+ | #id категории ресурсов, которая будет указана для создаваемых интерфейсов | ||
+ | snmp.ipCategory=0 | ||
+ | #если 1, то меняем индексы не с текущего момента, а со времени старта устройства, если это произошло сегодня. | ||
+ | #Предполагаем, что ifIndex могут меняться только при рестарте устройства. | ||
+ | snmp.substractUptime=1 | ||
+ | </source> | ||
+ | </p> | ||
+ | |||
+ | = Код = | ||
+ | |||
+ | *IndexSnmpDeviceManager | ||
+ | <source lang="java"> | ||
+ | package ru.dsi.bgbilling.modules.inet.dyn.device.snmp; | ||
+ | |||
+ | import java.util.ArrayList; | ||
+ | import java.util.Calendar; | ||
+ | import java.util.Collections; | ||
+ | import java.util.Date; | ||
+ | import java.util.HashMap; | ||
+ | import java.util.List; | ||
+ | import java.util.Map; | ||
+ | import java.util.regex.Matcher; | ||
+ | import java.util.regex.Pattern; | ||
+ | import java.util.stream.Collectors; | ||
+ | import java.util.stream.LongStream; | ||
+ | |||
+ | import org.apache.log4j.Logger; | ||
+ | |||
+ | import bitel.billing.common.TimeUtils; | ||
+ | import ru.bitel.bgbilling.kernel.container.managed.ServerContext; | ||
+ | import ru.bitel.bgbilling.modules.inet.access.manage.event.InetDeviceManageEvent; | ||
+ | import ru.bitel.bgbilling.modules.inet.api.common.bean.InetDevice; | ||
+ | import ru.bitel.bgbilling.modules.inet.api.common.bean.InetDeviceType; | ||
+ | import ru.bitel.bgbilling.modules.inet.dyn.device.snmp.SnmpDeviceManager; | ||
+ | import ru.bitel.bgbilling.server.util.Setup; | ||
+ | import ru.bitel.common.ParameterMap; | ||
+ | import ru.bitel.common.Preferences; | ||
+ | import ru.bitel.common.Utils; | ||
+ | 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 ru.bitel.oss.systems.inventory.resource.server.DeviceManagerMethod; | ||
+ | import uk.co.westhawk.snmp.stack.AsnObjectId; | ||
+ | |||
+ | |||
+ | /** | ||
+ | * @author cromeshnic@gmail.com | ||
+ | * patched by stark | ||
+ | * | ||
+ | * Класс для работы с интерфейсами их индексами по SNMP | ||
+ | * <pre> | ||
+ | * команды: | ||
+ | * ifsync - синхронизация интерфейсов и их ifIndex на роутере и в биллинге: | ||
+ | * 1. Получаем по SNMP список соответствий: ifIndex->ifName интерфейсов на устройстве | ||
+ | * 2. Для каждого полученного интерфейса проверяем, | ||
+ | * есть ли в биллинге на устройсве интерфейс с таким именем? | ||
+ | * 2.1. Если нет, и он проходит через фильтр snmp.ifNameRegexpFilter - создаём с правильным ifIndex и категорией ip = snmp.ipCategory и номер интерфейса находим | ||
+ | * как group(1) у заданного snmp.ifNameRegexpFilter. | ||
+ | * 2.2. Если есть, то совпадает ли ifIndex? | ||
+ | * 2.2.1. Если да, то ничего не делаем, всё ок. | ||
+ | * 2.2.2. Если нет, то меняем ifIndex | ||
+ | * 3. Оставшиеся необработанными интерфейсы в BG: если их нет на устройстве, то | ||
+ | * помечаем как отключенный и устанавливаем ifIndex=0 | ||
+ | * параметры: | ||
+ | * snmp.ifNameOid=1.3.6.1.2.1.2.2.1.1 | ||
+ | * - oid для получения списка интерфейсов устройства | ||
+ | * snmp.ifNameRegexpFilter=.* | ||
+ | * - regexp для фильтрации интерфейсов. Например, если нам в биллинге нужны только интерфейсы вида GigabitEthernet0/0/2.1122, | ||
+ | * то указываем snmp.ifNameRegexpFilter=GigabitEthernet0/0/2\.(\d+) | ||
+ | * Группировку тут надо обязательно указывать, подразумевается что в ней идет номер индекса, мы его обновляем на интерфейсе. | ||
+ | * snmp.ipCategory=0 | ||
+ | * - id категории ресурсов, которая будет указана для создаваемых интерфейсов | ||
+ | * snmp.substractUptime=1 | ||
+ | * - если 1, то меняем индексы не с текущего момента, а со времени старта устройства, если это произошло сегодня. | ||
+ | * Предполагаем, что ifIndex могут меняться только при рестарте устройства. | ||
+ | * snmp.enable=1 | ||
+ | * - включает работу менеджера на этом устройстве | ||
+ | * </pre> | ||
+ | * @see SnmpDeviceManager | ||
+ | */ | ||
+ | public class IndexSnmpDeviceManager extends SnmpDeviceManager implements DeviceManager | ||
+ | { | ||
+ | protected static final Logger logger = Logger.getLogger( SnmpDeviceManager.class ); | ||
+ | //TODO - резервирование/закрытие датой расформированных интерфейсов : http://forum.bitel.ru/viewtopic.php?p=71924#p71924 | ||
+ | |||
+ | private long[] ifNameOid; | ||
+ | /** | ||
+ | * Фильтр имён интерфейсов | ||
+ | * Если имя не соответствует регэкспу, то не заводим его в биллинге | ||
+ | */ | ||
+ | private Pattern ifaceNamePattern; | ||
+ | private int ipCategory; | ||
+ | private boolean subtractUptimeForIfIndex; | ||
+ | protected int mid; | ||
+ | protected int deviceId; | ||
+ | |||
+ | protected boolean enabled;//Работаем? | ||
+ | |||
+ | |||
+ | @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(); | ||
+ | //1.3.6.1.2.1.2.2.1.2.56 = STRING: "GigabitEthernet0/0/2.1100" | ||
+ | this.ifNameOid = new AsnObjectId( ( deviceConfig.get( "snmp.ifNameOid", "1.3.6.1.2.1.31.1.1.1.1" ) ) ).getOid(); | ||
+ | this.ifaceNamePattern = Pattern.compile( deviceConfig.get("snmp.ifNameRegexpFilter", ".*") ); | ||
+ | //Категория IP, выставляемая новым интерфейсам | ||
+ | this.ipCategory = deviceConfig.getInt("snmp.ipCategory", 0); | ||
+ | //Вычитать ли из текущего времени uptime устройства при проставлении даты/времени для ifIndex? | ||
+ | //(не дальше, чем за границу суток) | ||
+ | this.subtractUptimeForIfIndex = deviceConfig.getBoolean("snmp.substractUptime", true); | ||
+ | this.enabled = deviceConfig.getBoolean("snmp.enable", true); | ||
+ | |||
+ | return null; | ||
+ | } | ||
+ | |||
+ | @DeviceManagerMethod(title = "Cинхронизировать индексы" ) | ||
+ | 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>(); | ||
+ | logger.info( "ifNameOid =" + LongStream.of( ifNameOid ).boxed().collect( Collectors.toList()) ); | ||
+ | |||
+ | this.snmpClient.walk(this.ifNameOid, String.class, ( o, v ) -> { | ||
+ | //System.out.println( new AsnObjectId( o ) + " = " + v ); | ||
+ | //logger.info ( "v=" + v ); | ||
+ | ifaceTitleToIfIndexMap.put( v , (int)o[ o.length - 1 ] ); | ||
+ | } ); | ||
+ | |||
+ | Calendar now = Calendar.getInstance(); | ||
+ | |||
+ | //Получаем uptime (в TimeTicks - 0.01 секунды) | ||
+ | Long uptime = (long) 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, (int)(uptime/-1000) ); | ||
+ | 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; | ||
+ | |||
+ | int count = 0; | ||
+ | //1. Перебираем все полученные интерфейсы | ||
+ | for (Map.Entry<String, Integer> nameToIndex : ifaceTitleToIfIndexMap.entrySet()) | ||
+ | { | ||
+ | //1.1 Ищем интерфейс в биллинге по его имени | ||
+ | String ifName = nameToIndex.getKey(); | ||
+ | Integer ifIndexReal = nameToIndex.getValue(); | ||
+ | iface = ifaces.get( ifName ); | ||
+ | |||
+ | /*count ++; | ||
+ | if ( count > 1000 ) | ||
+ | { | ||
+ | break; | ||
+ | }*/ | ||
+ | |||
+ | Matcher matcher = ifaceNamePattern.matcher( ifName ); | ||
+ | boolean find = matcher.find(); | ||
+ | |||
+ | if( iface == null ) | ||
+ | { | ||
+ | //1.1.1 Не нашли - заводим в | ||
+ | // Удовлетворяет ли интерфейс фильтру? Если нет - не заводим его | ||
+ | if( !find ) | ||
+ | { | ||
+ | logger.info("skip snmp iface "+ifName+" on deviceId="+this.deviceId+" due to regexp "+ this.ifaceNamePattern.pattern() ); | ||
+ | continue; | ||
+ | } | ||
+ | |||
+ | logger.info ( "ifName=" + ifName ); | ||
+ | int portNumber = Utils.parseInt( matcher.group( 1 ), 0 ); | ||
+ | if ( portNumber == 0 ) | ||
+ | { | ||
+ | logger.info("portNumber is 0 for snmp iface "+ifName+" on deviceId="+this.deviceId+" due to regexp "+ this.ifaceNamePattern.pattern() ); | ||
+ | continue; | ||
+ | } | ||
+ | |||
+ | |||
+ | iface = new DeviceInterface(); | ||
+ | iface.setPort( portNumber );//increment port number (port number != ifindex here !!!) | ||
+ | iface.setTitle(ifName); | ||
+ | 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( portNumber );//вроде бы не обязательно, но пусть будет | ||
+ | deviceInterfaceIndex.setDeviceId(this.deviceId);//вроде бы не обязательно, но пусть будет | ||
+ | |||
+ | deviceInterfaceIndex.setIndex( ifIndexReal ); | ||
+ | deviceInterfaceIndex.setTimeFrom(now.getTime()); | ||
+ | deviceInterfaceIndex.setTimeTo(null); | ||
+ | iface.setIndexList( Collections.singletonList(deviceInterfaceIndex) ); | ||
+ | devicePortService.devicePortUpdate( iface, false ); | ||
+ | logger.info("adding new iface "+iface.getTitle()+" with ifIndex="+ifIndexReal+" from "+ TimeUtils.formatFullDate(now.getTime())); | ||
+ | result.append("+ ") | ||
+ | .append(iface.getTitle()) | ||
+ | .append(" (") | ||
+ | .append(ifIndexReal) | ||
+ | .append(") from ") | ||
+ | .append(TimeUtils.formatFullDate(now.getTime())) | ||
+ | .append("\n"); | ||
+ | }else | ||
+ | {//1.1.2 Нашли такой интерфейс, проверяем, изменился ли у него ifindex | ||
+ | //Если интерфейс при этом не удовлетворяет фильтру, то пишем об этом в лог | ||
+ | if( !find ) | ||
+ | { | ||
+ | logger.warn("existing bg iface "+ifName+" on deviceId="+this.deviceId+" doesn't match regexp "+this.ifaceNamePattern.pattern() ); | ||
+ | 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(ifIndexReal) ){//Индекс поменялся! Нужно обновить | ||
+ | //Закрываем датой предыдущий 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( ifIndexReal ); | ||
+ | deviceInterfaceIndex.setTimeFrom(start); | ||
+ | deviceInterfaceIndex.setTimeTo(null); | ||
+ | indexList.add(deviceInterfaceIndex); | ||
+ | devicePortService.devicePortUpdate( iface, false ); | ||
+ | logger.info("updating iface "+iface.getTitle()+": ifIndex="+ifIndexReal+" (was "+ifaceIndex+") from "+ TimeUtils.formatFullDate(start)); | ||
+ | result.append("* ") | ||
+ | .append(iface.getTitle()) | ||
+ | .append(" (") | ||
+ | .append(ifaceIndex) | ||
+ | .append("->") | ||
+ | .append(ifIndexReal) | ||
+ | .append(") from ") | ||
+ | .append(TimeUtils.formatFullDate(start)) | ||
+ | .append("\n"); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | logger.info ( "other" ); | ||
+ | |||
+ | //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, false ); | ||
+ | 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; | ||
+ | } | ||
+ | |||
+ | |||
+ | public static void main( String argv[] ) | ||
+ | { | ||
+ | try | ||
+ | { | ||
+ | logger.info( "hello" ); | ||
+ | IndexSnmpDeviceManager man = new IndexSnmpDeviceManager(); | ||
+ | int moduleId = 1; | ||
+ | InetDevice device = new InetDevice(); | ||
+ | Preferences deviceConfig = new Preferences(); | ||
+ | /* | ||
+ | //cisco | ||
+ | int deviceId = 2; | ||
+ | device.setId( deviceId ); | ||
+ | device.setHost( "10.10.1.42" ); | ||
+ | |||
+ | deviceConfig.set( "snmp.ifNameOid", "1.3.6.1.2.1.2.2.1.2" ); | ||
+ | deviceConfig.set( "snmp.ifNameRegexpFilter", "GigabitEthernet0/0/2\\.(\\d+)" ); | ||
+ | |||
+ | deviceConfig.set( "snmp.version", "2" ); | ||
+ | deviceConfig.set( "snmp.host", "10.10.1.42" ); | ||
+ | */ | ||
+ | |||
+ | |||
+ | //juniper | ||
+ | int deviceId = 3; | ||
+ | device.setId( deviceId ); | ||
+ | device.setHost( "10.10.1.100" ); | ||
+ | |||
+ | deviceConfig.set( "snmp.ifNameOid", "1.3.6.1.2.1.2.2.1.2" ); | ||
+ | deviceConfig.set( "snmp.ifNameRegexpFilter", "ae0\\.(\\d+)" ); | ||
+ | |||
+ | deviceConfig.set( "snmp.version", "2" ); | ||
+ | //deviceConfig.set( "snmp.host", "10.10.1.42" ); | ||
+ | |||
+ | |||
+ | man.init( Setup.getSetup(), moduleId, device, new InetDeviceType(), deviceConfig ); | ||
+ | man.connect(); | ||
+ | man.ifsync(); | ||
+ | |||
+ | //System.out.println ( "hello" ); | ||
+ | |||
+ | |||
+ | } | ||
+ | catch( Exception e ) | ||
+ | { | ||
+ | // TODO Auto-generated catch block | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | } | ||
+ | /** | ||
+ | * Метод вызывается при обнаружении перезагрузки устройства. | ||
+ | * @param e | ||
+ | * @return | ||
+ | * @throws Exception | ||
+ | */ | ||
+ | public Object onReboot( InetDeviceManageEvent e ) | ||
+ | throws Exception | ||
+ | { | ||
+ | logger.info( "onReboot" ); | ||
+ | ifsync(); | ||
+ | return null; | ||
+ | } | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | = P.S. = | ||
+ | *Использовал snmp4j вместо идущего с биллингом westhawk snmp, т.к. в последнем нельзя сделать GETNEXT | ||
+ | *Индекс в периоде на интерфейсе не работает на момент написания статьи : [http://forum.bitel.ru/viewtopic.php?p=72003#p72003] | ||
+ | *Подробнее про индексы интерфейсов в Cisco: [http://forum.bitel.ru/viewtopic.php?p=62007#p62007] | ||
+ | *Как это выглядело [[http://wiki.bitel.ru/index.php/%D0%9E%D0%B1%D0%BD%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BD%D0%BE%D0%BC%D0%B5%D1%80%D0%BE%D0%B2_%D0%B8%D0%BD%D1%82%D0%B5%D1%80%D1%84%D0%B5%D0%B9%D1%81%D0%BE%D0%B2_%D0%BF%D1%80%D0%B8_%D0%B7%D0%B0%D0%BC%D0%B5%D0%BD%D0%B5_%D1%80%D0%BE%D1%83%D1%82%D0%B5%D1%80%D0%B0 в модуле IPN]] | ||
+ | *Проверка доступности устройства по SNMP: | ||
+ | <source lang="bash"> | ||
+ | #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 | ||
+ | ... | ||
+ | </source> | ||
+ | --[[Участник:Cromeshnic|Cromeshnic]] 08:16, 23 декабря 2013 (UTC) |
Версия 12:59, 19 октября 2017
Отличия:
1) Не нужна сторонняя библиотека у snmp4j, используется стандартная из поставки. Поэтому не нудны классы SnmpClient и SnmpDeviceManager из того решения. А класс IfaceSnmpDeviceManager немного изменен и переименован в IfaceSnmpDeviceManager. Он наследуется от стандартного ru.bitel.bgbilling.modules.inet.dyn.device.snmp.SnmpDeviceManager и используется та же самая конфигурация при получении uptime. Описан тут: https://docs.bitel.ru/pages/viewpage.action?pageId=43385967
2) В случае если интерфейс в биллинге не найден, то создается интерфейс с номером, который считываться как group(1) из snmp.ifNameRegexpFilter=GigabitEthernet0/0/2\.(\d+). В старом решении просто присваивался (максимальный номер интерфейса) + 1.
3) На устройстве не нужно явно вбивать команду ifsync, во всплывающем меню уже есть пункт "сихронизировать интерфейсы". Но этого делать не надо - смотрим следующий пункт.
4) При перезагрузке устройства не нужно заходить и вызывать пункт "сихронизировать интерфейсы". так как DeviceManager сам это определит и вызовет( метод onReboot). Вызов вручную тоже работает.
Проверялось на сisco и juniper. Использовалось в схеме сбора статистики по snmp.
Содержание |
Описание
Класс - обработчик управления устройством по SNMP (DeviceManager) взамен стандартного.
- Умеет синхронизировать интерфейсы устройства и их snmp-индексы (нужны для учёта flow/snmp)
Как пользоваться
- Копируем себе в динамический код класс:
ru.dsi.bgbilling.modules.inet.dyn.device.snmp.IndexSnmpDeviceManager
- Компилируем динамический код
- Указываем в типе устройства модуля Inet "Обработчик управления устройством: ru.dsi.bgbilling.modules.inet.dyn.device.snmp.IndexSnmpDeviceManager"
- Настраиваем конфигурацию устройства/типа устройства/родительского устройства (по усмотрению) - см раздел "Конфигурация"
- Перечитываем конфигурацию устройств
- При обнаружении перезагрузки ( а access скорее всего сразу обнаружит это после перечитывания конфигурации) IndexSnmpDeviceManager сам сихронизует интерфейсы и индексы.
- Чтобы запустить вручную заходим в устройство с нашим обработчиком, кликаем правой кнопкой, выбираем "сихронизировать интерфейсы" смотрим результат
Принцип работы
Обработчик поддерживает команды:
- ifsync - синхронизация интерфейсов и их ifIndex на роутере и в биллинге:
- 1. Получаем по SNMP список соответствий: ifIndex->ifName интерфейсов на устройстве
- 2. Для каждого полученного интерфейса проверяем, есть ли в биллинге на устройсве интерфейс с таким именем?
- 2.1. Если нет, и он проходит через фильтр snmp.ifNameRegexpFilter - создаём с правильным ifIndex и категорией ip = snmp.ipCategory и номер интерфейса находим как group(1) у заданного snmp.ifNameRegexpFilter.
- 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= #OID для опроса uptime устройства. По умолчанию - 1.3.6.1.2.1.1.3.0 snmp.uptimeOid=
- для IndexSnmpDeviceManager (в дополнение к настройкам из SnmpDeviceManager):
#включает работу менеджера на этом устройстве(по умолчанию включено) snmp.enable=1 #oid для получения списка интерфейсов устройства snmp.ifNameOid=1.3.6.1.2.1.2.2.1.2 #regexp для фильтрации интерфейсов. Например, если нам в биллинге нужны только интерфейсы вида GigabitEthernet0/0/2.1122, #то указываем snmp.ifNameRegexpFilter=GigabitEthernet0/0/2\.(\d+) #Группировку тут надо обязательно указывать, подразумевается что в ней идет номер индекса, мы его обновляем на интерфейсе. snmp.ifNameRegexpFilter=(.*) #id категории ресурсов, которая будет указана для создаваемых интерфейсов snmp.ipCategory=0 #если 1, то меняем индексы не с текущего момента, а со времени старта устройства, если это произошло сегодня. #Предполагаем, что ifIndex могут меняться только при рестарте устройства. snmp.substractUptime=1
Код
- IndexSnmpDeviceManager
package ru.dsi.bgbilling.modules.inet.dyn.device.snmp; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.LongStream; import org.apache.log4j.Logger; import bitel.billing.common.TimeUtils; import ru.bitel.bgbilling.kernel.container.managed.ServerContext; import ru.bitel.bgbilling.modules.inet.access.manage.event.InetDeviceManageEvent; import ru.bitel.bgbilling.modules.inet.api.common.bean.InetDevice; import ru.bitel.bgbilling.modules.inet.api.common.bean.InetDeviceType; import ru.bitel.bgbilling.modules.inet.dyn.device.snmp.SnmpDeviceManager; import ru.bitel.bgbilling.server.util.Setup; import ru.bitel.common.ParameterMap; import ru.bitel.common.Preferences; import ru.bitel.common.Utils; 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 ru.bitel.oss.systems.inventory.resource.server.DeviceManagerMethod; import uk.co.westhawk.snmp.stack.AsnObjectId; /** * @author cromeshnic@gmail.com * patched by stark * * Класс для работы с интерфейсами их индексами по SNMP * <pre> * команды: * ifsync - синхронизация интерфейсов и их ifIndex на роутере и в биллинге: * 1. Получаем по SNMP список соответствий: ifIndex->ifName интерфейсов на устройстве * 2. Для каждого полученного интерфейса проверяем, * есть ли в биллинге на устройсве интерфейс с таким именем? * 2.1. Если нет, и он проходит через фильтр snmp.ifNameRegexpFilter - создаём с правильным ifIndex и категорией ip = snmp.ipCategory и номер интерфейса находим * как group(1) у заданного snmp.ifNameRegexpFilter. * 2.2. Если есть, то совпадает ли ifIndex? * 2.2.1. Если да, то ничего не делаем, всё ок. * 2.2.2. Если нет, то меняем ifIndex * 3. Оставшиеся необработанными интерфейсы в BG: если их нет на устройстве, то * помечаем как отключенный и устанавливаем ifIndex=0 * параметры: * snmp.ifNameOid=1.3.6.1.2.1.2.2.1.1 * - oid для получения списка интерфейсов устройства * snmp.ifNameRegexpFilter=.* * - regexp для фильтрации интерфейсов. Например, если нам в биллинге нужны только интерфейсы вида GigabitEthernet0/0/2.1122, * то указываем snmp.ifNameRegexpFilter=GigabitEthernet0/0/2\.(\d+) * Группировку тут надо обязательно указывать, подразумевается что в ней идет номер индекса, мы его обновляем на интерфейсе. * snmp.ipCategory=0 * - id категории ресурсов, которая будет указана для создаваемых интерфейсов * snmp.substractUptime=1 * - если 1, то меняем индексы не с текущего момента, а со времени старта устройства, если это произошло сегодня. * Предполагаем, что ifIndex могут меняться только при рестарте устройства. * snmp.enable=1 * - включает работу менеджера на этом устройстве * </pre> * @see SnmpDeviceManager */ public class IndexSnmpDeviceManager extends SnmpDeviceManager implements DeviceManager { protected static final Logger logger = Logger.getLogger( SnmpDeviceManager.class ); //TODO - резервирование/закрытие датой расформированных интерфейсов : http://forum.bitel.ru/viewtopic.php?p=71924#p71924 private long[] ifNameOid; /** * Фильтр имён интерфейсов * Если имя не соответствует регэкспу, то не заводим его в биллинге */ private Pattern ifaceNamePattern; private int ipCategory; private boolean subtractUptimeForIfIndex; protected int mid; protected int deviceId; protected boolean enabled;//Работаем? @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(); //1.3.6.1.2.1.2.2.1.2.56 = STRING: "GigabitEthernet0/0/2.1100" this.ifNameOid = new AsnObjectId( ( deviceConfig.get( "snmp.ifNameOid", "1.3.6.1.2.1.31.1.1.1.1" ) ) ).getOid(); this.ifaceNamePattern = Pattern.compile( deviceConfig.get("snmp.ifNameRegexpFilter", ".*") ); //Категория IP, выставляемая новым интерфейсам this.ipCategory = deviceConfig.getInt("snmp.ipCategory", 0); //Вычитать ли из текущего времени uptime устройства при проставлении даты/времени для ifIndex? //(не дальше, чем за границу суток) this.subtractUptimeForIfIndex = deviceConfig.getBoolean("snmp.substractUptime", true); this.enabled = deviceConfig.getBoolean("snmp.enable", true); return null; } @DeviceManagerMethod(title = "Cинхронизировать индексы" ) 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>(); logger.info( "ifNameOid =" + LongStream.of( ifNameOid ).boxed().collect( Collectors.toList()) ); this.snmpClient.walk(this.ifNameOid, String.class, ( o, v ) -> { //System.out.println( new AsnObjectId( o ) + " = " + v ); //logger.info ( "v=" + v ); ifaceTitleToIfIndexMap.put( v , (int)o[ o.length - 1 ] ); } ); Calendar now = Calendar.getInstance(); //Получаем uptime (в TimeTicks - 0.01 секунды) Long uptime = (long) 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, (int)(uptime/-1000) ); 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; int count = 0; //1. Перебираем все полученные интерфейсы for (Map.Entry<String, Integer> nameToIndex : ifaceTitleToIfIndexMap.entrySet()) { //1.1 Ищем интерфейс в биллинге по его имени String ifName = nameToIndex.getKey(); Integer ifIndexReal = nameToIndex.getValue(); iface = ifaces.get( ifName ); /*count ++; if ( count > 1000 ) { break; }*/ Matcher matcher = ifaceNamePattern.matcher( ifName ); boolean find = matcher.find(); if( iface == null ) { //1.1.1 Не нашли - заводим в // Удовлетворяет ли интерфейс фильтру? Если нет - не заводим его if( !find ) { logger.info("skip snmp iface "+ifName+" on deviceId="+this.deviceId+" due to regexp "+ this.ifaceNamePattern.pattern() ); continue; } logger.info ( "ifName=" + ifName ); int portNumber = Utils.parseInt( matcher.group( 1 ), 0 ); if ( portNumber == 0 ) { logger.info("portNumber is 0 for snmp iface "+ifName+" on deviceId="+this.deviceId+" due to regexp "+ this.ifaceNamePattern.pattern() ); continue; } iface = new DeviceInterface(); iface.setPort( portNumber );//increment port number (port number != ifindex here !!!) iface.setTitle(ifName); 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( portNumber );//вроде бы не обязательно, но пусть будет deviceInterfaceIndex.setDeviceId(this.deviceId);//вроде бы не обязательно, но пусть будет deviceInterfaceIndex.setIndex( ifIndexReal ); deviceInterfaceIndex.setTimeFrom(now.getTime()); deviceInterfaceIndex.setTimeTo(null); iface.setIndexList( Collections.singletonList(deviceInterfaceIndex) ); devicePortService.devicePortUpdate( iface, false ); logger.info("adding new iface "+iface.getTitle()+" with ifIndex="+ifIndexReal+" from "+ TimeUtils.formatFullDate(now.getTime())); result.append("+ ") .append(iface.getTitle()) .append(" (") .append(ifIndexReal) .append(") from ") .append(TimeUtils.formatFullDate(now.getTime())) .append("\n"); }else {//1.1.2 Нашли такой интерфейс, проверяем, изменился ли у него ifindex //Если интерфейс при этом не удовлетворяет фильтру, то пишем об этом в лог if( !find ) { logger.warn("existing bg iface "+ifName+" on deviceId="+this.deviceId+" doesn't match regexp "+this.ifaceNamePattern.pattern() ); 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(ifIndexReal) ){//Индекс поменялся! Нужно обновить //Закрываем датой предыдущий 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( ifIndexReal ); deviceInterfaceIndex.setTimeFrom(start); deviceInterfaceIndex.setTimeTo(null); indexList.add(deviceInterfaceIndex); devicePortService.devicePortUpdate( iface, false ); logger.info("updating iface "+iface.getTitle()+": ifIndex="+ifIndexReal+" (was "+ifaceIndex+") from "+ TimeUtils.formatFullDate(start)); result.append("* ") .append(iface.getTitle()) .append(" (") .append(ifaceIndex) .append("->") .append(ifIndexReal) .append(") from ") .append(TimeUtils.formatFullDate(start)) .append("\n"); } } } logger.info ( "other" ); //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, false ); 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; } public static void main( String argv[] ) { try { logger.info( "hello" ); IndexSnmpDeviceManager man = new IndexSnmpDeviceManager(); int moduleId = 1; InetDevice device = new InetDevice(); Preferences deviceConfig = new Preferences(); /* //cisco int deviceId = 2; device.setId( deviceId ); device.setHost( "10.10.1.42" ); deviceConfig.set( "snmp.ifNameOid", "1.3.6.1.2.1.2.2.1.2" ); deviceConfig.set( "snmp.ifNameRegexpFilter", "GigabitEthernet0/0/2\\.(\\d+)" ); deviceConfig.set( "snmp.version", "2" ); deviceConfig.set( "snmp.host", "10.10.1.42" ); */ //juniper int deviceId = 3; device.setId( deviceId ); device.setHost( "10.10.1.100" ); deviceConfig.set( "snmp.ifNameOid", "1.3.6.1.2.1.2.2.1.2" ); deviceConfig.set( "snmp.ifNameRegexpFilter", "ae0\\.(\\d+)" ); deviceConfig.set( "snmp.version", "2" ); //deviceConfig.set( "snmp.host", "10.10.1.42" ); man.init( Setup.getSetup(), moduleId, device, new InetDeviceType(), deviceConfig ); man.connect(); man.ifsync(); //System.out.println ( "hello" ); } catch( Exception e ) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * Метод вызывается при обнаружении перезагрузки устройства. * @param e * @return * @throws Exception */ public Object onReboot( InetDeviceManageEvent e ) throws Exception { logger.info( "onReboot" ); ifsync(); 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)