Обновление номеров интерфейсов при замене роутера

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

Версия от 04:28, 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
Личные инструменты