Auto Network Diagram with Graphviz


One of the most useful and least updated pieces of network documentation is the network diagram. We all know this, and yet we still don’t have/make time to update this until something catastrophic happens and then we says to ourselves

Wow. I wish I had updated this sooner…

Graphviz

According to the website 

Graphviz is open source graph visualization software. Graph visualization is a way of representing structural information as diagrams of abstract graphs and networks. It has important applications in networking, bioinformatics,  software engineering, database and web design, machine learning, and in visual interfaces for other technical domains.

note: Lots of great examples and docs there BTW.  Definitely check it out.

Getting started

So you’re going to have to first install graphviz from their website. Go ahead… I’l wait here.

Install the graphviz python binding

This should be easy assuming you’ve already got python and pip installed. I’m assuming that you do.

>>> pip install graphviz

Getting LLDP Neighbors from Arista Devices

You can use the Arista pyeapi library, also installable through pip as well.  There’s a blog which introduces you to the basics here which you can check out. Essentially I followed that blog and then substituted the “show lldp neighbors” command to get the output I was looking for.

Creating a Simple Network Diagram

The code for this is available here

Essentially, I’m just parsing the JSON output from the Arista eAPI and creating a DOTfile which is used to generate the diagram.

Pros: It’s automated

Cons: It’s not very pretty at all.

SimpleTopo.png

 

Prettying it up a Bit

Code for this is available here

So with a little bit of work using the .attr methods we can pretty this up a bit.  For example the

dot.attr('node', shape='box')

method turns the node shape from an ellipse into a box shape. The other transformations are pretty obvious as well.

Notice that we changed the shape of the shape, the style of the arrows a bit and also shaded in the box.  There are a lots of other modifications we can make, but I’ll leave you to check out the docs for that. 

SimplePrettierTopo.png

 

 

Adding your own graphics

Code for this is available here

Getting a bit closer to what I want, but still I think we can do a bit better. For this example, I used mspaint to create a simple PNG file with a switch-ish image on it. From what I can tell, there’s no reason you couldn’t just use the vendor icons for whatever devices you’re using, but for now, just playing with something quick and dirty.

Once the file is created and placed somewhere in the path, you can use this method

dot.attr('node', image="./images/switch1.png")

to get the right image.  You’ll also notice I used

dot.attr('edge', arrowhead='none')

to remove the arrow heads. ( I actually removed another command, can you spot it? )

SimplePrettierGraphicTopo.png

 

Straighter Lines

Code for this is available here

So looking at this image, one thing I don’t like is the curved lines. This is where Graphviz beat me for the day. I did find that I was able to apply the

dot.graph_attr['splines'] = "ortho"

attribute to the dot object to get me the straight lines I wanted, but when I did that, I got a great message that told me that I would need to use xlables instead of standard labels.

SimplePrettierGraphicOrthoTopo.png

Next Steps

Code for this is available here

For this next step, it was time to get the info live from the device, and also to attempt to stitch multiple devices together into a single topology. Some things I noticed is that the name of the node MUST match the hostname of the device, otherwise you end up with multiple nodes.  You can see there’s still a lot of work to do to clean this up, but I think it’s worth sharing. Hopefully you do too.

MultiTopo.png

 

Thoughts

Pros: Graphviz is definitely cool. I can see a lot of time spent in drawing network diagrams here. The fact that you could automatically run this every X period to ensure you have a up to date network diagram at all times is pretty awesome. It’s customizable which is nice, and multi-vendor would be pretty easy to implement. Worse case scenario, you could just poll the LLDP MIB with SNMP and dump the data into the appropriate bucket. Not elegant, but definitely functional.

Cons:  The link labels are a pain. In the short time I was playing with it, I wasn’t able to google or documentation my way into what I want, which is a label on each end of the link which would tell me what interface on which device. Not the glob of data in the middle that makes me wonder which end is which.

The other thing I don’t like is the curvy lines. I want straight lines. Whether that’s an issue with the graphviz python library that I’m using or it’s actually a problem with the whole graphviz framework isn’t clear to me yet. Considering the time saved, I could probably live with this as is, but I’d also like to do better.

If anyone has figured out how to get past these minor issues, please drop me a line!  @netmanchris on twitter or comment on the blog.

As always, comments and fixes are always appreciated!

@netmanchris

3 thoughts on “Auto Network Diagram with Graphviz

  1. Hi!
    I figured out how to add labels to link:


    # Based on http://matthiaseisen.com/articles/graphviz/
    import graphviz as gv
    styles = {
    'graph': {
    'label': 'Network Map',
    'fontsize': '16',
    'fontcolor': 'white',
    'bgcolor': '#333333',
    'rankdir': 'BT',
    },
    'nodes': {
    'fontname': 'Helvetica',
    'shape': 'box',
    'fontcolor': 'white',
    'color': '#006699',
    'style': 'filled',
    'fillcolor': '#006699',
    'margin': '0.4',
    },
    'edges': {
    'style': 'dashed',
    'color': 'green',
    'arrowhead': 'open',
    'fontname': 'Courier',
    'fontsize': '14',
    'fontcolor': 'white',
    }
    }
    def apply_styles(graph, styles):
    graph.graph_attr.update(
    ('graph' in styles and styles['graph']) or {}
    )
    graph.node_attr.update(
    ('nodes' in styles and styles['nodes']) or {}
    )
    graph.edge_attr.update(
    ('edges' in styles and styles['edges']) or {}
    )
    return graph
    def draw_topology(topology_dict, output_filename='img/topology'):
    nodes = set([key[0] for key in topology_dict.keys() + topology_dict.values()])
    g1 = gv.Graph(format='svg')
    for node in nodes:
    g1.node(node)
    for key, value in topology_dict.iteritems():
    head, t_label = key
    tail, h_label = value
    g1.edge(head, tail, headlabel=h_label, taillabel=t_label, label=" "*12)
    # headport='nw' tailport='s'
    g1 = apply_styles(g1, styles)
    print(g1.source)
    filename = g1.render(filename=output_filename)
    print "Graph saved in", filename
    diag5 = {('R3', 'Fa0/1'): ('R1', 'Fa0/1'),
    ('R2', 'Fa3/0'): ('R1', 'Fa3/0'),
    ('R2', 'Fa1/0'): ('R1', 'Fa0/1'),
    ('R4', 'Fa4/0'): ('R1', 'Fa4/0'),
    ('R2', 'Fa0/0'): ('R3', 'Fa0/0'),
    ('R5', 'Fa4/0'): ('R3', 'Fa3/0'),
    ('R4', 'Fa1/0'): ('R3', 'Fa0/0'),
    ('R4', 'Fa1/0'): ('R2', 'Fa1/0'),
    ('R4', 'Fa0/1'): ('R5', 'Fa0/1')}
    draw_topology(diag5)

Leave a comment