Получение информации со свитчей о соседях

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

Python

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

Вот результат:

Map.png

Поэтому, вот листинг скрипта для сбора информации. Мы используем SNMP и запрашиваем cdp-соседей. Функция req вызывается рекурсивно, и записывает в базу свитч, его "глубину" (первый захардкожен и имеет глубину 0), пробегает по всем портам, в которых хоть что-то светится осознанное.

При каждом проходе мы очищаем таблицу и заполняем ее новыми значениями. В таблице, соответственно, неуникальные поля depth,parent,port,child


import pymysql
import subprocess

def mysql(val1,val2,val3,val4):
        connect=pymysql.connect(host="localhost", user="switch", passwd="switch", db="switch")
        cursor=connect.cursor()
        sql="replace into map values (\'"+val1+"\',\'"+val2+"\',\'"+val3+"\',\'"+val4+"\')"
        cursor.execute(sql)
        connect.commit()
        connect.close()

def req(ip,depth):
		depth=depth
		oip=ip
		global checked
		child=''
		port=''
		childs=[]
		ports=[]
		try:
			snmp_resp_all=subprocess.check_output(['snmpwalk','-c', 'public', '-v2c' , oip, '1.3.6.1.4.1.9.9.23.1.2.1.1.4']).splitlines()
			for line in snmp_resp_all:
				port=line.split('.')[14]
				try:
					child = str(int(line.split(' ')[3],16))+"."+str(int(line.split(' ')[4],16))+"."+str(int(line.split(' ')[5],16))+"."+str(int(line.split(' ')[6],16))
				except Exception as e:
					child = ''
					pass
			
				if child != '':
					print str(depth)+ ' ' + oip+' '+ port + ' ' + child
					mysql(str(depth),oip,port,child)
					childs.append(child)
		except Exception as e:
			
			pass
		checked.append(oip)	
		depth+=1
		for cip in list(set(childs).difference(checked)):
			if cip != '':
				req(cip,depth)
				

connect=pymysql.connect(host="localhost", user="switch", passwd="switch", db="switch")
cursor=connect.cursor()
sql="TRUNCATE TABLE map"
cursor.execute(sql)
connect.commit()
connect.close()


val=0
checked=[]
req('10.20.20.22',0)
exit()



Итак, основное, что нужно сделать - опять-таки кинуть snmp-запрос в свитч

snmpwalk -c public -v2c 10.2.1.27 1.3.6.1.4.1.9.9.23.1.2.1.1.4
iso.3.6.1.4.1.9.9.23.1.2.1.1.4.1.1 = Hex-STRING: 0A 02 01 16 
iso.3.6.1.4.1.9.9.23.1.2.1.1.4.7.1 = ""
iso.3.6.1.4.1.9.9.23.1.2.1.1.4.14.1 = ""
iso.3.6.1.4.1.9.9.23.1.2.1.1.4.14.2 = ""
iso.3.6.1.4.1.9.9.23.1.2.1.1.4.14.3 = ""

Как несложно заметить, на выходе получаем некие строки. Те, в которых есть Hex-STRING - обрабатываем, и видим, что предпоследнее число - это порт, а сама 16-ичная строка - айпишник в соответствующем формате. Если мы видим и разбираем значащую строку, обращаемся к функции рекурсивно и передаем текущую глубину (depth), а также сопоставляем список полученных "детей" текущего свитча с множеством уже проверенных, чтобы не начать бегать туда-сюда между двумя свитчами. Проверка выполняется в этой строке:

for cip in list(set(childs).difference(checked))

Обратите внимание на эту строку

 global checked

где мы объявляем checked глобальным объектом.

Для просмотра полученной информации снова напишем маленькую html-страничку и cgi-скрипт для получения данных


<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Поиск свитчей</title>
</head>
<body>
<h4><p>Введите адрес свитча и нажмите "Искать!"</p>

<table border="1">
<tr><th></th><th></th></tr>
    <form action="/cgi-bin/map.py">
       <tr><th>IP</th><th><input type="text" name="ip"></th></tr>
       <tr><th></th><th><button type="submit">Искать!</button></th><tr>
    </form>
</table>
</body>
</html>


#!/usr/bin/python
# -*- coding: utf-8 -*-
import pymysql
import cgi
def mysql(parent):
	results=[]
	connect=pymysql.connect(host="localhost", user="switch", passwd="switch", db="switch")
	cursor=connect.cursor()
	sql="select depth,port,child from map where parent=\'"+parent+"\'"
	cursor.execute(sql)
	results=cursor.fetchall()
	if len(results) == 0:
		print "<h3>Свитч не найден!</h3>"
	else:	
		depthpa = results[0][0]
		print "<h3>Вы на свитче "+parent+"</h3>"
		print "<h3>Глубина свитча:"+ " " + depthpa +"</h3>"
		print ("""
					<table border="1">
					<caption><h3>Подключенные</h3></caption>
					<tr>
					<th>Порт</th>
					<th>Свитч</th>
					<th>Глубина</th>
					</tr>""")
	
		for res in results:
			depthch=''
			none,port,child=res
			try:	
				sql="select depth from map where parent=\'"+child+"\' limit 1"
				cursor.execute(sql)
				depthch=cursor.fetchall()[0][0]
				if int(depthch)<int(depthpa):
					color='red'
				elif int(depthch)>int(depthpa):
					color='green'
				elif int(depthch)==int(depthpa):
					color='blue'	
							
			except:
				depthch='Unknown'
				color='white'
			print "<tr><td>"+port+'</td><td><a title="Перейти к свитчу" href="./map.py?ip='+child+'">'+child+"</a></td><td bgcolor="+color+">"+depthch+"</td><tr>"
		
		print "	</table>"
	
	connect.close()
	return




form = cgi.FieldStorage()
ip = form.getfirst('ip') or ''


print("Content-type: text/html\n")
print("""<!DOCTYPE HTML>
        <html>
        <head>
            <meta charset="utf-8">
            <title>Analyse</title>
        </head>
        <body>
        <a href='../../map.html'>К поиску</a>
        


""")



mysql(ip)

print ("""
	
	</table>
         </body>
         </html>""")

exit()




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