Обновление номеров интерфейсов при замене роутера
Материал из BiTel WiKi
Версия от 04:30, 8 февраля 2013; Cromeshnic (Обсуждение | вклад)
Приводится скрипт на ruby, который меняет номера интерфейсов в биллинге, сверяясь по названиям на роутере через SNMP (с) by Blib, edited by Cromeshnic 2008-2013
Содержание |
Использование:
gen-src-intf.rb [-d] yyyy-mm-dd source -d - тестовый режим, в биллинге ничего не меняем, только выводим изменения в номерах интерфейсов yyyy-mm-dd - дата, с которой будут созданы новые диапазоны. Старые закроются предыдущим числом. source - имя источника, как оно указано в биллинге
Принцип работы
- По SNMP получаем с роутера список интерфейсов с их номерами (индексами).
- Пробегаем в биллинге по всем открытым на дату диапазонам (и сетям) источника и, если номер (ipn_iface_<mid>.number) изменился:
* закрываем диапазон предыдущей датой * создаём новый диапазон с актуальным номером интерфейса
- Удаляем все интерфейсы для источника из справочника
- Создаём новые интерфейсы с правильными номерами
Примечания
* Использовать на свой страх и риск :) * Скрипт использует устаревшую библиотеку для работы с mysql, лучше переделать на другую
Скрипт
#!/usr/bin/env ruby -wKU require 'rubygems' gem 'snmp' require 'snmp' require 'active_record/vendor/mysql' require 'pp' #Скрипт меняет id интерфейсов в биллинге, сверяясь по названиям на роутере через SNMP #Старые диапазоны закрываются датой dt-1, новые открываются с dt #возможно понадобится поменять SNMP коммунити и изменить названия интерфейсов регекспом (ex Fa->Gi) - см по коду @mid=9 @community = 'public' @dbh = Mysql.connect('127.0.0.1', 'bg', 'bg', 'bgbilling', nil, '/opt/local/var/run/mysql5/mysqld.sock') unless @dbh puts "Can't connect to database" exit 1 end @dbh.query("set names utf8"); def update(src_id,i_id,i_name,z_id) id=0 z_id = -2 if nil == z_id @dbh.query("replace into ipn_iface_#{@mid}(id, source_id, number, title, zone_id) values(#{id},#{src_id},#{i_id}, '#{i_name}', #{z_id})") end @ipn_src={} @new_intf = {} # получаем список источников с ip-адресами @dbh.query("select id, title, host_or_dir from source where mid=#{@mid}").each { |row| @ipn_src[row[1]] = [row[0].to_i, row[2]]} @debug=nil split = ARGV.shift if split == "-d" @debug=1 split = ARGV.shift end unless split =~ /^\d+-\d+-\d+/ STDERR.puts "incorrect date. Use date in mysql format yyyy-mm-dd\n" @ipn_src.each { |k,v| STDERR.puts "\t#{"%-15s" % k}#{v[1]}" } exit end ARGV.each do |src| puts "+++ #{src}" unless @ipn_src[src] STDERR.puts "source '#{src}' not found\nAvailible sources:\n" STDERR.puts "Usage: xxx [-d] yyy-mm-dd source\n -d - debug\n" @ipn_src.each { |k,v| STDERR.puts "\t#{"%-15s" % k}#{v[1]}" } exit end src_id=@ipn_src[src][0] src_ip=@ipn_src[src][1] # получаем новый список индексов для этого источника через SNMP begin SNMP::Manager.open(:Host => src_ip, :Community=>@community, :Timeout => 0.7, :Retries => 5 ) do |m| m.walk("ifName") do |row| row.each do |x| x.name.to_s =~ /(\d+)$/ @new_intf[x.value]=$1.to_i end end end rescue # SNMP::RequestTimeout, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENOBUFS puts("Error while work with source on SNMP") end # меняем старый интф на новый для тех у кого прописан интерфейс res = @dbh.query("select aid,number,i.title,c.title,i.zone_id from ipn_user_source_#{@mid} s join ipn_iface_#{@mid} i on s.source_id=i.source_id and s.iface=i.number join ipn_user_range_#{@mid} a on a.id=s.aid join contract c on c.id=a.cid where s.source_id='#{src_id}' and s.iface > 0 and (a.date1 is null or a.date1 < '#{split}') and (a.date2 is null or a.date2 > '#{split}')") res.each do |row| aid = row[0].to_i old_id = row[1].to_i name = row[2] cn = row[3] zone_id = row[4] # ищем имя в новом списке n=name #!!!! если в процессе нужно поменять имена интерфейсов - меняем тут !!!! # n = name.gsub(/^Fa0/, 'Fa4') if @new_intf.has_key?(n) if @new_intf[n] == old_id puts "#{"%-20s"%cn} unchanged" else # закрываем старый range и создаем новый с даты split unless @debug r=@dbh.query("select id, cid, object_id, addr1, addr2, port1, port2, date1, date2, comment, plan_id, pers_plan_id, resource_id, mask from ipn_user_range_#{@mid} where id=#{aid}").fetch_row @dbh.query("update ipn_user_range_#{@mid} set date2=date_sub('#{split}', interval 1 day) where id=#{aid}") @dbh.query("insert into ipn_user_range_#{@mid}(cid, object_id, addr1, addr2, port1, port2, date1, date2, comment, plan_id, pers_plan_id, resource_id, mask) values('#{r[1]}','#{r[2]}','#{r[3]}','#{r[4]}','#{r[5]}','#{r[6]}','#{split}',#{r[8]?r[8]:'null'},'#{r[9]}','#{r[10]}','#{r[11]}','#{r[12]}','#{r[13]}')") new_aid = @dbh.insert_id() @dbh.query("insert into ipn_user_source_#{@mid} (aid,source_id,iface) values(#{new_aid},#{src_id},#{@new_intf[n]})") end puts "#{"%-20s"%cn} #{name},#{old_id} -> #{n},#{@new_intf[n]}" end else puts "#{"%-20s"%cn} NOT FOUND #{n}" end end # меняем таблицу интерфейсов на новую unless @debug @zone_ids = {} #зоны интерфейсов по их имени res = @dbh.query("select title, zone_id from ipn_iface_#{@mid} where source_id='#{src_id}' and number>0") res.each { |row| @zone_ids[row[0]]=row[1] } @dbh.query("delete from ipn_iface_#{@mid} where source_id='#{src_id}' and number>0") @new_intf.each { |name,id| update(src_id, id, name, @zone_ids[name]) } end # проверяем что есть все интерфейсы res = @dbh.query("select c.title, c.comment, sn.title, s.iface, r.date1, r.date2 " + "from ipn_user_range_#{@mid} r join ipn_user_source_#{@mid} s on r.id=s.aid "+ "join contract c on r.cid=c.id join source sn on sn.id=s.source_id "+ "where r.date2 is null and s.source_id=#{src_id} and s.iface not in (select number from ipn_iface_#{@mid} where source_id=#{src_id})") res.each { |row| STDERR.puts "ERROR: #{row[4]}-#{row[5]} #{"%-20s %10s:%-5d"% [row[0],row[2],row[3]] } interface is not defined #{row[1]}" } end