summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--haircontrol/data.py1
-rw-r--r--haircontrol/discovery.py71
-rw-r--r--haircontrol/inspectors.py77
-rwxr-xr-xtests/test_discovery.py8
4 files changed, 112 insertions, 45 deletions
diff --git a/haircontrol/data.py b/haircontrol/data.py
index 8adf75f..463e38a 100644
--- a/haircontrol/data.py
+++ b/haircontrol/data.py
@@ -33,6 +33,7 @@ class Equipment:
self.name = name
self.mgmtip = mgmtip
self.ifaces = {}
+ self.inspected = False
def __repr__(self):
return repr( (self.name, self.mgmtip, list(self.ifaces.values())) )
diff --git a/haircontrol/discovery.py b/haircontrol/discovery.py
index 5f63349..7c3c9f0 100644
--- a/haircontrol/discovery.py
+++ b/haircontrol/discovery.py
@@ -8,9 +8,14 @@ class Discovery:
# XXX Use only one Inspector
self.linux_inspector = LinuxInspector()
self.ubnt_inspector = UbntInspector()
+ self.toughswitch_inspector = ToughSwitchInspector()
self.net = EtherDomain()
- def discover_hinting_from_lldp(self, e_lldp):
+ 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)
@@ -36,7 +41,7 @@ class Discovery:
self.net.add_equipment(e_root)
self.linux_inspector.connect(e_root)
- # Learn root neighbours
+ # Learn root neighbours (directly or indirectly connected via trasparent bridges)
result = self.linux_inspector.command('ip-neigh')
for (ip, ifname, mac) in result:
self.net.index_mac_ip(mac, ip)
@@ -44,22 +49,48 @@ class Discovery:
self.linux_inspector.disconnect()
- # Inspect antennas bridge tables
- for ip in self.net.equipments:
- if ip.startswith('172.16.1'): # XXX Use neighbours, filter with OUI
- e = self.net.equipments[ip]
- self.ubnt_inspector.connect(e)
-
- # Learn local interfaces
- result = self.ubnt_inspector.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')
- for (ifname, mac) in result:
- e.add_seen_mac(ifname, mac)
-
- self.ubnt_inspector.disconnect()
+ # Create Equipment object for all neighbours (if not already previously done by hinting)
+ for iface in e_root.ifaces.values():
+ local_ifname = iface.name
+ local_mac = iface.mac
+ for remote_mac in iface.mac_seen:
+ remote_ip = self.net.mac2ip.get(remote_mac)
+ if remote_ip:
+ e = self.net.equipments.get(remote_ip)
+ if not e:
+ e = Equipment('?', remote_ip)
+ e.add_iface('?', remote_mac)
+ self.net.add_equipment(e)
+
+ # Inspect all non-already inspected equipement
+ done = False
+ while not done:
+ done = True
+ 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)
+ # Learn local interfaces
+ result = self.ubnt_inspector.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')
+ 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')
+ for (ifname, mac) in result:
+ e.add_seen_mac(ifname, mac)
+ self.toughswitch_inspector.disconnect()
+ # Flag unknowns as inspected (and warn)
+ else:
+ e.inspected = 'cannot'
+ print("Notice: Unimplemented inspector for %s"%e)
diff --git a/haircontrol/inspectors.py b/haircontrol/inspectors.py
index b81d832..3d7b766 100644
--- a/haircontrol/inspectors.py
+++ b/haircontrol/inspectors.py
@@ -12,26 +12,36 @@ class Inspector():
def connect(self, e):
self.e = e
+ self.e.inspected = 'in-progress'
def disconnect(self):
+ self.e.inspected = True
self.e = None
def command(self, cmdname):
cmddef = self.cmds.get(cmdname)
if not cmddef:
return None
- fd = self._exec(cmdname, cmddef['cmd'])
+ cmd = cmddef['cmd']
result = []
- re = cmddef.get('re')
- func = cmddef.get('func')
- if re:
- for line in fd:
- matches = re.match(line)
- if matches:
- result.append(matches.groups())
- elif func:
- result = func(self, fd)
- fd.close()
+ fd = None
+ try:
+ fd = self._exec(cmdname, cmd)
+ re = cmddef.get('re')
+ func = cmddef.get('func')
+ if re:
+ for line in fd:
+ matches = re.match(line)
+ if matches:
+ result.append(matches.groups())
+ elif func:
+ result = func(self, fd)
+ except Exception:
+ print("Error : can't exec/read %s (%s) on %s (%s)"%(cmdname, cmd, self.e.name, self.e.mgmtip))
+ finally:
+ if fd:
+ fd.close()
+
return result
def _exec(self, cmdname, cmd):
@@ -39,6 +49,18 @@ class Inspector():
mockfile = self.testDataPath + '/' + self.e.name + '-' + cmdname + '.out'
return open(mockfile)
+ def _parse_cgi_json(self, fd):
+ for cgi_headers in fd:
+ if cgi_headers == '\n':
+ break
+ js = {}
+ try:
+ js = json.load(fd)
+ except ValueError:
+ print("Warn : unparsable json for %s (%s)"%(self.e.name, self.e.mgmtip))
+ finally:
+ fd.close()
+ return js
class LinuxInspector(Inspector):
@@ -101,21 +123,8 @@ class LinuxInspector(Inspector):
}
-
class UbntInspector(Inspector):
- def _parse_cgi_json(self, fd):
- for cgi_headers in fd:
- if cgi_headers == '\n':
- break
- js = {}
- try:
- js = json.load(fd)
- except ValueError:
- print("Warn : unparsable json")
- fd.close()
- return js
-
def parse_status_json(self, fd):
result = []
interfaces = self._parse_cgi_json(fd).get('interfaces')
@@ -151,3 +160,23 @@ class UbntInspector(Inspector):
}
}
+class ToughSwitchInspector(Inspector):
+
+ def parse_mactable_data_cgi(self, fd):
+ result = []
+ macs = self._parse_cgi_json(fd).get('macs')
+ if macs:
+ for line in macs:
+ if isinstance(line, dict):
+ ifname = "p%i"%line.get('port')
+ mac = line.get('mac')
+ result.append( (ifname, mac) )
+ return result
+
+ cmds = {
+ 'mactable_data.cgi': {
+ 'cmd':'/usr/www/mactable_data.cgi',
+ 'func': parse_mactable_data_cgi
+ },
+ }
+
diff --git a/tests/test_discovery.py b/tests/test_discovery.py
index 06c61e3..ba30dab 100755
--- a/tests/test_discovery.py
+++ b/tests/test_discovery.py
@@ -14,8 +14,14 @@ class TestDiscovery(unittest.TestCase):
def test_wire_graph(self):
ref_net = data.EtherDomain() #json.load('../test-data/ref-output/equipments.json')
- self.discovery.discover_hinting_from_lldp(data.Equipment('stg2', '172.16.0.253'))
+ self.discovery.discover_lldp_hinting(data.Equipment('stg2', '172.16.0.253'))
+ self.discovery.discover_static_hinting([
+ ('SW_SergeGOUSSE', '172.16.30.23'),
+ ('SW_PI_EGL', '172.16.30.27'),
+ ('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()))
if __name__ == '__main__':