#!/usr/bin/python
# -*- coding: utf-8 -*-
# Volker Fröhlich, 2012
# volker27@gmx.at

"""
'Generating Maps and Hosts From Topological Data' 
presented at the Zabbix Conference 2012 in Riga

- Creates a graph from a Cisco export file
- Manipulates and segments the graph (only in a rough way)
- TODO: Generate Zabbix 2.0 XML for hosts and maps
"""

# Requires python-networkx 1.7 or newer for some operations

import csv
import networkx as nx

main_loop_ipaddr = "10.110.20.1"
main_vlan_ipaddr = "149.148.56.1"
zabbix_service_ipaddr = "149.148.20.123"


# === CREATE INITIAL GRAPH ===

G=nx.Graph()

csv_reader = csv.DictReader( open( 'cp_export.csv' ), \
    delimiter=",", \
    fieldnames=( "ipaddress", "hostname", "oid", "dontcare", "neighbors" ))
# Skip header
csv_reader.next()

for row in csv_reader:
    neighbor_list = row["neighbors"].split( "," )

    for neighbor in neighbor_list:
        # Remove spaces
        neighbor = neighbor.lstrip()

	# Add neighbors, ignore isolated nodes
        if neighbor != "":
            G.add_edge( row["ipaddress"], neighbor )
            
            # Add additional information to nodes or edges here
	    G.node[row["ipaddress"]]["hostname"] = row["hostname"]


# === CORRECT GRAPH ===

# Cisco Prime doesn't export all IP addresses of a device;
# Only the first for each network
# Merge hosts with multiple IP addresses
mapping = {main_vlan_ipaddr: main_loop_ipaddr}
G = nx.relabel_nodes( G, mapping )

# Remove bogus intra-cluster connection, not relevant for operation
G.remove_edge( "10.110.2.1", "10.110.2.2" )


# === COMPLETE GRAPH ===
# Firewalls are not included in the exported file;
# neither are non-Cisco devices

# Adding connection between Zabbix server and main switch
G.add_edge( zabbix_service_ipaddr, main_loop_ipaddr )


# === ANALYZE AND SEGMENT GRAPH ===

# Remember all neighbors of main switch cluster
# This is necessary to discover loops and to address the created segments
main_neigh_list = G.neighbors( main_loop_ipaddr )

nx.draw_graphviz( G )
nx.write_dot( G, "/tmp/total.dot" )

# Remove main switch to segment our network for mapping
G.remove_node( main_loop_ipaddr )

#TODO:
# Segment further and remove undesired interconnections
# Check whether there's a connection between stepstones after
# removing the central node.
#    VG = G.subgraph( c )
#    ss_in_conn=set(main_neigh_list) & set(c)
# Consider degree_centrality() and betweenness()
# That's enough information to create Zabbix map XML

# To create trigger dependency, you must identify relevant neighbors
# (has_path and delete neighbors one after another on a working copy);
# This approach will not work for more complicate topology.
# Consider using all_simple_paths()
# Method all_pairs_shortest_path(G)
# Show reachable hosts for all branches

print '\n***************\nReachable via this branch:\n***************'
for stepstone in main_neigh_list:
    n = G.neighbors( stepstone )
    print '%s has %d neighor/s:\n%s:' % ( stepstone, len( n ), n )

    c = nx.node_connected_component( G, stepstone )
    print '\nReachable via %s: %d node(s):\n%s\n\n***************\n' % ( stepstone, len( c ), c )

    VG = G.subgraph(c)

    # Creates dot files you can render with twopi and others
    # You can also look at them with vimdot
    nx.draw_graphviz( VG )
    VG.node[stepstone]['color'] = 'red'
    nx.write_dot( VG, "/tmp/"+stepstone+".dot" )

    # TODO: Generate XML or use all gathered information to do API calls

#TODO:
# Normalize coordinates to the size of the map and
# flip y coordinate -- Zabbix and Dot use different origins
# Escape characters
