summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--haircontrol/data.py16
-rw-r--r--haircontrol/discovery.py119
-rw-r--r--haircontrol/inspectors.py24
-rwxr-xr-xtests/test_discovery.py9
4 files changed, 139 insertions, 29 deletions
diff --git a/haircontrol/data.py b/haircontrol/data.py
index 463e38a..2ab8268 100644
--- a/haircontrol/data.py
+++ b/haircontrol/data.py
@@ -5,7 +5,7 @@ class EtherDomain:
self.mac2ip = {}
def __repr__(self):
- return '([\n%s\n],\n%s\n)'%(',\n'.join(' %s'%repr(e) for e in get_equipment_list_sorted()), repr(self.ip2mac))
+ return '([\n%s\n],\n%s\n)'%(',\n'.join(' %s'%repr(e) for e in self.get_equipment_list_sorted()), repr(self.ip2mac))
def get_equipment_list_sorted(self):
e_list = list(self.equipments.values())
@@ -40,18 +40,20 @@ class Equipment:
def add_iface(self, ifname, mac):
iface = self.ifaces.get(ifname)
+ mac_lower = mac.lower()
if iface:
- iface.mac = mac
+ iface.mac = mac_lower
else:
- self.ifaces[ifname] = Interface(ifname, mac)
+ self.ifaces[ifname] = Interface(ifname, mac_lower)
def add_seen_mac(self, ifname, mac):
iface = self.ifaces.get(ifname)
+ mac_lower = mac.lower()
if not iface:
- print("Warn : add_seen_mac(%s, %s) auto-create iface on %s"%(ifname, mac, self.name))
+ print("Warn : add_seen_mac(%s, %s) auto-create iface on %s"%(ifname, mac_lower, self.name))
iface = Interface(ifname)
self.ifaces[ifname] = iface
- iface.mac_seen.append(mac)
+ iface.mac_seen.append(mac_lower)
class Interface:
def __init__(self, name=None, mac=None):
@@ -60,6 +62,6 @@ class Interface:
self.mac_seen = []
def __repr__(self):
- #return repr( ( self.mac, self.name, self.mac_seen ) )
- return repr( ( self.mac, self.name, '[ %i mac_seen ]'%len(self.mac_seen) ) )
+ return repr( ( self.mac, self.name, self.mac_seen ) )
+ #return repr( ( self.mac, self.name, '[ %i mac_seen ]'%len(self.mac_seen) ) )
diff --git a/haircontrol/discovery.py b/haircontrol/discovery.py
index 7c3c9f0..d6839b2 100644
--- a/haircontrol/discovery.py
+++ b/haircontrol/discovery.py
@@ -5,16 +5,77 @@ from haircontrol.inspectors import *
class Discovery:
def __init__(self): #, inspector):
- # XXX Use only one Inspector
+ self.net = EtherDomain()
+ self.dummy_inspector = DummyInspector()
self.linux_inspector = LinuxInspector()
self.ubnt_inspector = UbntInspector()
self.toughswitch_inspector = ToughSwitchInspector()
- self.net = EtherDomain()
+ self.edgemax_inspector = EdgeMaxInspector()
+ self.netonix_inspector = NetonixInspector()
+ self.mikrotik_inspector = MikrotikInspector()
+ self.openwrt_inspector = OpenWRTInspector()
+
+ self.inspector_by_mac = {
+ # Mikrotik
+ 'd4:ca:6d': self.mikrotik_inspector,
+ 'e4:8d:8c': self.mikrotik_inspector,
+ # Netonix
+ 'ec:13:b2': self.netonix_inspector,
+ 'ec:13:b3': self.netonix_inspector,
+ # PC
+ '0c:c4:7a': self.linux_inspector, # SuperMicro
+ '52:54:00': self.linux_inspector, # VM
+ # TP-Link
+ '10:fe:ed': self.openwrt_inspector,
+ '14:cc:20': self.openwrt_inspector,
+ '30:b5:c2': self.openwrt_inspector,
+ '54:e6:fc': self.openwrt_inspector,
+ '60:e3:27': self.openwrt_inspector,
+ '64:66:b3': self.openwrt_inspector,
+ '64:70:02': self.openwrt_inspector,
+ '90:f6:52': self.openwrt_inspector,
+ 'a0:f3:c1': self.openwrt_inspector,
+ 'b0:48:7a': self.openwrt_inspector,
+ 'c0:4a:00': self.openwrt_inspector,
+ 'c4:6e:1f': self.openwrt_inspector,
+ 'c4:e9:84': self.openwrt_inspector,
+ 'f4:ec:38': self.openwrt_inspector,
+ 'f8:1a:67': self.openwrt_inspector,
+ 'f8:d1:11': self.openwrt_inspector,
+ # Ubnt
+ '00:15:6d': self.ubnt_inspector,
+ '00:27:22': self.ubnt_inspector,
+ '04:18:d6:07': self.toughswitch_inspector,
+ '04:18:d6': self.ubnt_inspector,
+ '06:18:d6': self.ubnt_inspector, # Non globaly unique ?!
+ '24:a4:3c:05': self.toughswitch_inspector,
+ '24:a4:3c:06': self.toughswitch_inspector,
+ '24:a4:3c:07': self.toughswitch_inspector,
+ '24:a4:3c:3c': self.toughswitch_inspector,
+ '24:a4:3c:3d': self.toughswitch_inspector,
+ '24:a4:3c:b3': self.toughswitch_inspector,
+ '24:a4:3c': self.ubnt_inspector,
+ '44:d9:e7': self.edgemax_inspector,
+ 'dc:9f:db:80': self.toughswitch_inspector,
+ 'dc:9f:db:81': self.toughswitch_inspector,
+ 'dc:9f:db': self.ubnt_inspector,
+ }
+
+
+ def inspector(self, mac):
+ if mac:
+ return self.inspector_by_mac.get(mac[:11]) \
+ or self.inspector_by_mac.get(mac[:8]) \
+ or self.dummy_inspector
+ else:
+ return self.dummy_inspector
+
def discover_static_hinting(self, name_ip_tuples):
for name, ip in name_ip_tuples:
self.net.add_equipment(Equipment(name, ip))
+
def discover_lldp_hinting(self, e_lldp):
self.net.add_equipment(e_lldp)
self.linux_inspector.connect(e_lldp)
@@ -37,6 +98,7 @@ class Discovery:
self.linux_inspector.disconnect()
+
def discover_from_root(self, e_root):
self.net.add_equipment(e_root)
self.linux_inspector.connect(e_root)
@@ -49,7 +111,8 @@ class Discovery:
self.linux_inspector.disconnect()
- # Create Equipment object for all neighbours (if not already previously done by hinting)
+ # Create/Update Equipment object for all neighbours
+ # (could be already created by hinting)
for iface in e_root.ifaces.values():
local_ifname = iface.name
local_mac = iface.mac
@@ -57,10 +120,13 @@ class Discovery:
remote_ip = self.net.mac2ip.get(remote_mac)
if remote_ip:
e = self.net.equipments.get(remote_ip)
- if not e:
+ if e and not e.ifaces:
+ e.add_iface(None, remote_mac)
+ elif not e:
e = Equipment('?', remote_ip)
e.add_iface('?', remote_mac)
self.net.add_equipment(e)
+
# Inspect all non-already inspected equipement
done = False
@@ -69,28 +135,47 @@ class Discovery:
for ip,e in self.net.equipments.items():
if not e.inspected:
done = False
- # Inspect antennas bridge tables
- if ip.startswith('172.16.1'): # XXX Filter with OUI
- self.ubnt_inspector.connect(e)
+
+ # Find the right inspector from equipment's first iface mac address
+ if e.ifaces:
+ e_first_mac = next(iter(e.ifaces.values())).mac
+ i = self.inspector(e_first_mac)
+ else:
+ # XXX Custom hack
+ if e.mgmtip.startswith('172.16.1'):
+ i = self.ubnt_inspector
+ elif e.mgmtip == '172.16.30.23':
+ i = self.edgemax_inspector
+ elif e.mgmtip.startswith('172.16.3'):
+ i = self.toughswitch_inspector
+ else:
+ i = self.dummy_inspector
+
+ i.connect(e)
+
+ # Inspect antennas
+ if isinstance(i, UbntInspector):
# Learn local interfaces
- result = self.ubnt_inspector.command('status.cgi')
+ result = i.command('status.cgi')
for (ifname, mac) in result:
if ifname not in [ 'lo', 'wifi0', 'br0' ]: # XXX configurable filter
e.add_iface(ifname, mac)
# Learn bridge tables
- result = self.ubnt_inspector.command('brmacs.cgi')
+ result = i.command('brmacs.cgi')
for (ifname, mac) in result:
e.add_seen_mac(ifname, mac)
- self.ubnt_inspector.disconnect()
+
# Inspect switches
- elif ip.startswith('172.16.3'): # XXX Filter with OUI
- self.toughswitch_inspector.connect(e)
- result = self.toughswitch_inspector.command('mactable_data.cgi')
+ elif isinstance(i, ToughSwitchInspector):
+ result = i.command('mactable_data.cgi')
for (ifname, mac) in result:
e.add_seen_mac(ifname, mac)
- self.toughswitch_inspector.disconnect()
- # Flag unknowns as inspected (and warn)
+
+ elif isinstance(i, EdgeMaxInspector):
+ result = i.command('mac-addr-table')
+ for (mac, ifname) in result:
+ e.add_seen_mac(ifname, mac)
else:
- e.inspected = 'cannot'
- print("Notice: Unimplemented inspector for %s"%e)
+ print("Notice: Nothing inspected on %s"%e)
+ i.disconnect()
diff --git a/haircontrol/inspectors.py b/haircontrol/inspectors.py
index 3d7b766..04b05c1 100644
--- a/haircontrol/inspectors.py
+++ b/haircontrol/inspectors.py
@@ -62,6 +62,8 @@ class Inspector():
fd.close()
return js
+class DummyInspector(Inspector):
+ pass
class LinuxInspector(Inspector):
@@ -180,3 +182,25 @@ class ToughSwitchInspector(Inspector):
},
}
+class EdgeMaxInspector(Inspector):
+
+ cmds = {
+ 'mac-addr-table': {
+ 'cmd':'show mac-addr-table', #XXX needs "enable" mode
+ 're': re.compile("\d+\s+(?P<mac>[A-F0-9:]+)\s+(?P<ifname>[0-9/]+)\s")
+ # VLAN ID MAC Address Interface IfIndex Status
+ # 1 00:15:6D:8E:22:46 0/15 15 Learned
+ # 1 00:27:22:0E:67:F9 0/17 17 Learned
+ },
+ }
+
+#TODO Implement them
+class NetonixInspector(Inspector):
+ pass
+
+class MikrotikInspector(Inspector):
+ pass
+
+class OpenWRTInspector(Inspector):
+ pass
+
diff --git a/tests/test_discovery.py b/tests/test_discovery.py
index ba30dab..2e7b308 100755
--- a/tests/test_discovery.py
+++ b/tests/test_discovery.py
@@ -8,12 +8,11 @@ from haircontrol import discovery, data #, inspectors
class TestDiscovery(unittest.TestCase):
def setUp(self):
- self.discovery = discovery.Discovery() #MockInspector('../test-data/input'))
self.maxDiff=None
+ self.discovery = discovery.Discovery() #MockInspector('../test-data/input'))
+ self.ref_net = data.EtherDomain() #json.load('../test-data/ref-output/equipments.json')
def test_wire_graph(self):
- ref_net = data.EtherDomain() #json.load('../test-data/ref-output/equipments.json')
-
self.discovery.discover_lldp_hinting(data.Equipment('stg2', '172.16.0.253'))
self.discovery.discover_static_hinting([
('SW_SergeGOUSSE', '172.16.30.23'),
@@ -21,8 +20,8 @@ class TestDiscovery(unittest.TestCase):
('SW_Eglise_ESTANCARBON', '172.16.30.38'),
])
self.discovery.discover_from_root(data.Equipment('stg', '172.16.0.254'))
-
- self.assertEqual(ref_net.get_equipment_list_sorted(), list(self.discovery.net.get_equipment_list_sorted()))
+# self.assertEqual(self.ref_net.get_equipment_list_sorted(), list(self.discovery.net.get_equipment_list_sorted()))
+ print(self.discovery.net)
if __name__ == '__main__':
unittest.main()