The unified diff between revisions [ea8ef2d2..] and [c1adc84f..] is displayed below. It can also be downloaded as a raw diff.

#
#
# patch "iputils.py"
#  from [d10413b7982a049c60d75ad671bae620f8ccd250]
#    to [84ec996038ab504ee8b5b18ba8d4f3f3c23e1801]
#
# patch "plugins/basic_snmp.py"
#  from [f4361556222d4ff31fc9a0693b384723c24677ed]
#    to [fb0fb2d026d99c1259a12ad5003a3a75eeacbc1e]
#
# patch "plugins/cisco_ios.py"
#  from [c22b8cbe73befe343ea39b472c8929b7f678276b]
#    to [19feaff94350a58fc05c4cc35ae258285a38655c]
#
# patch "yowie.py"
#  from [3e57b46eee9446cfb98f258c312df9df5a5cb8a6]
#    to [8303d0a3d2730d9b7caf3d9dab5260c25705787d]
#
============================================================
--- iputils.py	d10413b7982a049c60d75ad671bae620f8ccd250
+++ iputils.py	84ec996038ab504ee8b5b18ba8d4f3f3c23e1801
@@ -2,8 +2,8 @@

 # iputils.py
 #
-# Author: Grahame Bowland <grahame@ucs.uwa.edu.au>
-# License: GNU GPL
+# -- FIXME; I wrote this a LONG time ago, it could really do with some cleaning
+# up for efficiency reasons.

 import sys
 import socket
@@ -16,8 +16,8 @@ class Network:
         # is "ip" within our network
         c1 = ip & self.mask
         c2 = self.address & self.mask
-	# 130.95.0.0 & 255.255.255.0 = 130.95.0.0
-	# 130.95.2.0 & 255.255.255.0 = 130.95.2.0
+        # 130.95.0.0 & 255.255.255.0 = 130.95.0.0
+        # 130.95.2.0 & 255.255.255.0 = 130.95.2.0
         if c1 == c2:
             if c1 == 0 and self.address <> ip:
                 # /32 route and not equal host addresses
@@ -38,19 +38,18 @@ def ip_to_int(str, invert = 0):
         if invert:
             cval = cval ^ 255
         if cval > 255 or cval < 0:
-            print "Invalid ip; contains section out of bounds at index %d : %s"\
-                % (4-i, str)
+            print "Invalid ip; contains section out of bounds at index %d : %s" % (4-i, str)
         rv = rv + (m * cval)
         m = m * 256
     return rv

 def int_to_length(ip_int):
-	len = 0
-	while ip_int > 0:
-		if ip_int % 2: break
-		ip_int /= 2
-		len += 1
-	return 32 - len
+    len = 0
+    while ip_int > 0:
+        if ip_int % 2: break
+        ip_int /= 2
+        len += 1
+    return 32 - len

 def network_from_ip(ip, mask):
    pip = ip_to_int(ip)
============================================================
--- plugins/basic_snmp.py	f4361556222d4ff31fc9a0693b384723c24677ed
+++ plugins/basic_snmp.py	fb0fb2d026d99c1259a12ad5003a3a75eeacbc1e
@@ -3,7 +3,9 @@
 # basic_snmp : stuff a generic managed switch should support
 #

+import re
 from goatpy.snmpwrapper import snmpwalk
+from iputils import int_to_length, ip_to_int

 plugin_info = {
     'description' : '''\
@@ -19,9 +21,9 @@ def ip_arp(table_name, table_io, device)
 		mac_address = value
 		yield device.config['zone'], None, mac_address, ip_address

-def ip_routing(hostname, community):
+def ip_routing(table_name, table_io, device):
 	routes = {}
-	for oid, route_type, value in snmpwalk(hostname, community, "2c", "ipRouteTable"):
+	for oid, route_type, value in snmpwalk(device.device_name, device.config['community'], "2c", "ipRouteTable", ssh_dest=device.config.get('ssh')):
 		row_name = oid[0]
 		colon = row_name.rfind(':')
 		if colon <> -1: row_name = row_name[colon+1:]
@@ -42,9 +44,26 @@ def ip_routing(hostname, community):
 		metric = route.get('ipRouteMetric1')
 		ifindex = route.get('ipRouteIfIndex')
 		if ifindex: ifindex = int(ifindex)
-		yield route_type, ifindex, False, network, via, metric
+		yield device.config["zone"], route_type, ifindex, False, network, via, metric

+def ip_addresses(table_name, table_io, device):
+	addresses = {}
+	for oid, type, value in snmpwalk(device.device_name, device.config['community'], "2c", "IP-MIB::ipAddrTable", ssh_dest=device.config.get('ssh')):
+		current_row = tuple(oid[-4:])
+		row_name = oid[-5]
+		colon = row_name.rfind(':')
+		if colon <> -1: row_name = row_name[colon+1:]
+		if not addresses.has_key(current_row): addresses[current_row] = {}
+		addresses[current_row][row_name] = value
+	for row in addresses:
+		ip = addresses[row].get('ipAdEntAddr')
+		netmask = addresses[row].get('ipAdEntNetMask')
+		interface = addresses[row].get('ipAdEntIfIndex')
+		if ip != None and netmask != None: ip = ip + '/' + str(int_to_length(ip_to_int(netmask)))
+		yield device.config['zone'], ip, interface
+
 tables = {
+    'ip_addresses' : ip_addresses,
     'ip_arp' : ip_arp,
     'ip_routing' : ip_routing,
 }
============================================================
--- plugins/cisco_ios.py	c22b8cbe73befe343ea39b472c8929b7f678276b
+++ plugins/cisco_ios.py	19feaff94350a58fc05c4cc35ae258285a38655c
@@ -1,15 +1,58 @@

-from basic_snmp import ip_arp, ethernet_forwarding
+from goatpy.snmpwrapper import snmpwalk
+import basic_snmp
+import re

+def adjacency(table_name, table_io, device):
+    def h2i(s):
+        print "h2i called, with argument:", s
+        h = {'0':0,'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9,'A':10,'B':11,'C':12,'D':13,'E':14,'F':15}
+        if len(s) == 1: return h[s[0]]
+        else :return 16*h[s[0]] + h[s[1]]
+    def to_ipaddr(s):
+        if s == None: return s
+        return '.'.join([str(t) for t in map(h2i, s.split(' '))])
+    def to_ascii(s):
+        if s == None: return s
+        asdigits = [h2i(t) for t in s.split(' ')]
+        asdigits = filter(lambda x: x >= 32 and x <= 122, asdigits)
+        return ''.join([chr(t) for t in asdigits])
+    row_to_field = { 4 : 'ip_address', 6 : 'name',  7 : 'remote_port' }
+    table = {}
+    for oid, type, value in snmpwalk(device.device_name, device.config['community'], "2c", "SNMPv2-SMI::enterprises.9.9.23.1.2.1.1", ssh_dest=device.config.get('ssh')):
+        print oid, type, value
+        id = tuple(oid[-2:])
+        row = int(oid[-3])
+        if not table.has_key(id): table[id] = {}
+        if row_to_field.has_key(row):
+            table[id][row_to_field[row]] = value
+    for row in table:
+        ip = to_ipaddr(table[row].get('ip_address'))
+        name = table[row].get('name')[1:-1]
+        if re.match(r'^(..?)+ ..?$', name):
+            name = to_ascii(name)
+            m = re.match(r'[^A-Za-z0-9]*([A-Za-z0-9]+)', name)
+            if m: name = m.groups()[0]
+        remote_port = table[row].get('remote_port')[1:-1]
+        yield device.config['zone'], ip, name, remote_port

 # see: http://www.cisco.com/en/US/tech/tk648/tk362/technologies_tech_note09186a0080094a9b.shtml
 def ethernet_forwarding(table_name, table_io, device):
     yield device.config['zone'], 1, '00:11:24:c7:dd:41', 'en1'

+def ethernet_vlans(table_name, table_io, device):
+    # walk vtpVlanName
+    for oid, type, value in snmpwalk(device.device_name, device.config['community'], "2c", ".1.3.6.1.4.1.9.9.46.1.3.1.1.4", ssh_dest=device.config.get('ssh')):
+        value = value[1:-1]
+        yield device.config['zone'], int(oid[-1]), value

 tables = {
+    'adjacency' : adjacency,
     'ethernet_forwarding' : ethernet_forwarding,
-    'ip_arp' : ip_arp,
+    'ethernet_vlans' : ethernet_vlans,
+    'ip_addresses' : basic_snmp.ip_addresses,
+    'ip_arp' : basic_snmp.ip_arp,
+    'ip_routing' : basic_snmp.ip_routing
 }

 def init():
============================================================
--- yowie.py	3e57b46eee9446cfb98f258c312df9df5a5cb8a6
+++ yowie.py	8303d0a3d2730d9b7caf3d9dab5260c25705787d
@@ -1,5 +1,6 @@
 #!/usr/bin/env python2.4

+import tempfile
 import config
 import popen2
 import socket
@@ -36,14 +37,22 @@ class TableIO:
                 yield row

     def write(self, header, rows):
-        fd = open(self.filename, "w")
-        writer = csv.writer(fd)
-        writer.writerow(header)
-        rowlen = len(header)
-        for row in rows:
-            if len(row) != rowlen:
-                raise Exception("Row has length %d (must be %d)" % (len(row), rowlen))
-            writer.writerow(row)
+        # use a temporary file; this avoids annoying problems where an update fails half-way
+        # through and we potentially commit a half-complete version of the file.
+        tmp_fdno, tmp_filename = tempfile.mkstemp(suffix='.csv', prefix='yowie')
+        tmp_fd = os.fdopen(tmp_fdno, "w")
+        try:
+            writer = csv.writer(tmp_fd)
+            writer.writerow(header)
+            rowlen = len(header)
+            for row in rows:
+                if len(row) != rowlen:
+                    raise Exception("Row has length %d (must be %d)" % (len(row), rowlen))
+                writer.writerow(row)
+            os.rename(tmp_filename, self.filename)
+        except:
+            os.unlink(tmp_filename)
+            raise

 export_funcs = {}
 def export_to_drivers(f):