Home » Juniper » Dynamically generating an ISIS topology diagram in Python

Manually updating network diagrams is a right PITA. One afternoon, as an introduction to Python, I set about automating this process. I discovered the wonderful pygraphviz library and came up with a handy little script that is under 200 lines long (including comments).

It automagically generates a diagram like this:

geantISIS

Here is execution output, followed by source:


niall@concrescence ~/Code/ISIS Diagram $ ./isisdiag.py
Reading input file: geant.txt
Discovering router definitions in input file...
Found rt1.ams.nl.geant.net, fetching configuration...
Found mx1.ams.nl.geant.net, fetching configuration...
Found mx1.ath.gr.geant.net, fetching configuration...
Found mx2.ath.gr.geant.net, fetching configuration...
Found mx2.bra.sk.geant.net, fetching configuration...
Found mx1.bru.be.geant.net, fetching configuration...
Found mx2.bru.be.geant.net, fetching configuration...
Found rt1.buc.ro.geant.net, fetching configuration...
Found mx1.bud.hu.geant.net, fetching configuration...
Found rt1.cat.it.geant.net, fetching configuration...
Found mx1.cop.dk.geant.net, fetching configuration...
Found mx1.dub.ie.geant.net, fetching configuration...
Found mx2.dub.ie.geant.net, fetching configuration...
Found mx1.fra.de.geant.net, fetching configuration...
Found rt1.fra.de.geant.net, fetching configuration...
Found mx1.gen.ch.geant.net, fetching configuration...
Found mx1.kau.lt.geant.net, fetching configuration...
Found mx2.kau.lt.geant.net, fetching configuration...
Found mx1.lis.pt.geant.net, fetching configuration...
Found mx2.lis.pt.geant.net, fetching configuration...
Found mx1.lju.si.geant.net, fetching configuration...
Found mx2.lju.si.geant.net, fetching configuration...
Found mx1.lon.uk.geant.net, fetching configuration...
Found mx1.lux.lu.geant.net, fetching configuration...
Found mx2.lux.lu.geant.net, fetching configuration...
Found mx1.mad.es.geant.net, fetching configuration...
Found mx1.mil2.it.geant.net, fetching configuration...
Found mx1.par.fr.geant.net, fetching configuration...
Found mx1.poz.pl.geant.net, fetching configuration...
Found mx1.pra.cz.geant.net, fetching configuration...
Found mx1.rig.lv.geant.net, fetching configuration...
Found mx2.rig.lv.geant.net, fetching configuration...
Found rt1.sof.bg.geant.net, fetching configuration...
Found mx1.tal.ee.geant.net, fetching configuration...
Found mx2.tal.ee.geant.net, fetching configuration...
Found rt1.vie.at.geant.net, fetching configuration...
Found mx1.vie.at.geant.net, fetching configuration...
Found mx1.zag.hr.geant.net, fetching configuration...
Found mx2.zag.hr.geant.net, fetching configuration...
Writing to output file: geant_dynamic.txt
Processing configuration and generating graph data...
Found match: set protocols isis interface ae0.0 level 2 metric 5
Found adjacency for mx2.zag.hr: mx1.zag.hr on interface ae0.0 with metric 5
NEIGHBOUR: mx1.zag.hr
Found match: set protocols isis interface ae1.0 level 2 metric 500
Found adjacency for mx2.zag.hr: mx1.lju.si on interface ae1.0 with metric 500
NEIGHBOUR: mx1.lju.si
Found match: set protocols isis interface ae2.0 level 2 metric 100
Found adjacency for mx2.zag.hr: mx1.vie.at on interface ae2.0 with metric 100
NEIGHBOUR: mx1.vie.at
Found match: set protocols isis interface ae0.0 level 2 metric 5
Found adjacency for mx1.zag.hr: mx2.zag.hr on interface ae0.0 with metric 5
NEIGHBOUR: mx2.zag.hr
Found match: set protocols isis interface ae1.0 level 2 metric 100
Found adjacency for mx1.zag.hr: mx1.bud.hu on interface ae1.0 with metric 100
NEIGHBOUR: mx1.bud.hu
Found match: set protocols isis interface ae0.0 level 2 metric 50
Found adjacency for mx1.vie.at: mx2.bra.sk on interface ae0.0 with metric 50
NEIGHBOUR: mx2.bra.sk
Found match: set protocols isis interface ae1.0 level 2 metric 100
Found adjacency for mx1.vie.at: mx2.zag.hr on interface ae1.0 with metric 100
NEIGHBOUR: mx2.zag.hr
Found match: set protocols isis interface ae2.0 level 2 metric 450
Found adjacency for mx1.vie.at: mx1.lju.si on interface ae2.0 with metric 450
NEIGHBOUR: mx1.lju.si
Found match: set protocols isis interface ae4.0 level 2 metric 5
Found adjacency for mx1.vie.at: rt1.vie.at on interface ae4.0 with metric 5
NEIGHBOUR: rt1.vie.at
Found match: set protocols isis interface ae5.0 level 2 metric 50
Found adjacency for mx1.vie.at: mx1.mil2.it on interface ae5.0 with metric 50
NEIGHBOUR: mx1.mil2.it
Found match: set protocols isis interface ae6.0 level 2 metric 175
Found adjacency for mx1.vie.at: rt1.buc.ro on interface ae6.0 with metric 175
NEIGHBOUR: rt1.buc.ro
Found match: set protocols isis interface ae7.0 level 2 metric 175
Found adjacency for mx1.vie.at: rt1.sof.bg on interface ae7.0 with metric 175
NEIGHBOUR: rt1.sof.bg
Found match: set protocols isis interface ae8.0 level 2 metric 500
Found adjacency for mx1.vie.at: mx2.ath.gr on interface ae8.0 with metric 500
NEIGHBOUR: mx2.ath.gr
Found match: set protocols isis interface ae4.0 level 2 metric 5
Found adjacency for rt1.vie.at: mx1.vie.at on interface ae4.0 with metric 5
NEIGHBOUR: mx1.vie.at
Found match: set protocols isis interface ae0.0 level 2 metric 100
Found adjacency for mx2.tal.ee: mx1.rig.lv on interface ae0.0 with metric 100
NEIGHBOUR: mx1.rig.lv
Found match: set protocols isis interface ae1.0 level 2 metric 5
Found adjacency for mx2.tal.ee: mx1.tal.ee on interface ae1.0 with metric 5
NEIGHBOUR: mx1.tal.ee
Found match: set protocols isis interface ae0.0 level 2 metric 510
Found adjacency for mx1.tal.ee: mx1.cop.dk on interface ae0.0 with metric 510
NEIGHBOUR: mx1.cop.dk
Found match: set protocols isis interface ae1.0 level 2 metric 5
Found adjacency for mx1.tal.ee: mx2.tal.ee on interface ae1.0 with metric 5
NEIGHBOUR: mx2.tal.ee
Found match: set protocols isis interface ae5.0 level 2 metric 100
Found adjacency for rt1.sof.bg: mx1.bud.hu on interface ae5.0 with metric 100
NEIGHBOUR: mx1.bud.hu
Found match: set protocols isis interface ae7.0 level 2 metric 175
Found adjacency for rt1.sof.bg: mx1.vie.at on interface ae7.0 with metric 175
NEIGHBOUR: mx1.vie.at
Found match: set protocols isis interface ae0.0 level 2 metric 100
Found adjacency for mx2.rig.lv: mx2.kau.lt on interface ae0.0 with metric 100
NEIGHBOUR: mx2.kau.lt
Found match: set protocols isis interface ae1.0 level 2 metric 5
Found adjacency for mx2.rig.lv: mx1.rig.lv on interface ae1.0 with metric 5
NEIGHBOUR: mx1.rig.lv
Found match: set protocols isis interface ae0.0 level 2 metric 100
Found adjacency for mx1.rig.lv: mx2.tal.ee on interface ae0.0 with metric 100
NEIGHBOUR: mx2.tal.ee
Found match: set protocols isis interface ae1.0 level 2 metric 5
Found adjacency for mx1.rig.lv: mx2.rig.lv on interface ae1.0 with metric 5
NEIGHBOUR: mx2.rig.lv
Found match: set protocols isis interface ae0.0 level 2 metric 50
Found adjacency for mx1.pra.cz: mx1.fra.de on interface ae0.0 with metric 50
NEIGHBOUR: mx1.fra.de
Found match: set protocols isis interface ae1.0 level 2 metric 50
Found adjacency for mx1.pra.cz: mx2.bra.sk on interface ae1.0 with metric 50
NEIGHBOUR: mx2.bra.sk
Found match: set protocols isis interface ae2.0 level 2 metric 200
Found adjacency for mx1.pra.cz: mx1.poz.pl on interface ae2.0 with metric 200
NEIGHBOUR: mx1.poz.pl
Found match: set protocols isis interface ae0.0 level 2 metric 105
Found adjacency for mx1.poz.pl: mx1.fra.de on interface ae0.0 with metric 105
NEIGHBOUR: mx1.fra.de
Found match: set protocols isis interface ae2.0 level 2 metric 200
Found adjacency for mx1.poz.pl: mx1.pra.cz on interface ae2.0 with metric 200
NEIGHBOUR: mx1.pra.cz
Found match: set protocols isis interface ae5.0 level 2 metric 445
Found adjacency for mx1.poz.pl: mx1.kau.lt on interface ae5.0 with metric 445
NEIGHBOUR: mx1.kau.lt
Found match: set protocols isis interface ae0.0 level 2 metric 50
Found adjacency for mx1.par.fr: mx1.lon.uk on interface ae0.0 with metric 50
NEIGHBOUR: mx1.lon.uk
Found match: set protocols isis interface ae2.0 level 2 metric 1000
Found adjacency for mx1.par.fr: mx2.dub.ie on interface ae2.0 with metric 1000
NEIGHBOUR: mx2.dub.ie
Found match: set protocols isis interface ae3.0 level 2 metric 100
Found adjacency for mx1.par.fr: mx1.mad.es on interface ae3.0 with metric 100
NEIGHBOUR: mx1.mad.es
Found match: set protocols isis interface ae4.0 level 2 metric 50
Found adjacency for mx1.par.fr: mx1.gen.ch on interface ae4.0 with metric 50
NEIGHBOUR: mx1.gen.ch
Found match: set protocols isis interface ae5.0 level 2 metric 500
Found adjacency for mx1.par.fr: mx2.lux.lu on interface ae5.0 with metric 500
NEIGHBOUR: mx2.lux.lu
Found match: set protocols isis interface ae0.0 level 2 metric 50
Found adjacency for mx1.mil2.it: mx1.vie.at on interface ae0.0 with metric 50
NEIGHBOUR: mx1.vie.at
Found match: set protocols isis interface ae1.0 level 2 metric 50
Found adjacency for mx1.mil2.it: mx1.gen.ch on interface ae1.0 with metric 50
NEIGHBOUR: mx1.gen.ch
Found match: set protocols isis interface ae2.0 level 2 metric 500
Found adjacency for mx1.mil2.it: mx1.ath.gr on interface ae2.0 with metric 500
NEIGHBOUR: mx1.ath.gr
Found match: set protocols isis interface so-1/2/0.0 level 2 metric 100
Found adjacency for mx1.mil2.it: rt1.cat.it on interface so-1/2/0.0 with metric 100
NEIGHBOUR: rt1.cat.it
Found match: set protocols isis interface ae0.0 level 2 metric 450
Found adjacency for mx1.mad.es: mx2.lis.pt on interface ae0.0 with metric 450
NEIGHBOUR: mx2.lis.pt
Found match: set protocols isis interface ae1.0 level 2 metric 100
Found adjacency for mx1.mad.es: mx1.par.fr on interface ae1.0 with metric 100
NEIGHBOUR: mx1.par.fr
Found match: set protocols isis interface ae2.0 level 2 metric 100
Found adjacency for mx1.mad.es: mx1.gen.ch on interface ae2.0 with metric 100
NEIGHBOUR: mx1.gen.ch
Found match: set protocols isis interface ae0.0 level 2 metric 5
Found adjacency for mx2.lux.lu: mx1.lux.lu on interface ae0.0 with metric 5
NEIGHBOUR: mx1.lux.lu
Found match: set protocols isis interface ae1.0 level 2 metric 500
Found adjacency for mx2.lux.lu: mx1.par.fr on interface ae1.0 with metric 500
NEIGHBOUR: mx1.par.fr
Found match: set protocols isis interface ae2.0 level 2 metric 550
Found adjacency for mx2.lux.lu: mx1.ams.nl on interface ae2.0 with metric 550
NEIGHBOUR: mx1.ams.nl
Found match: set protocols isis interface ae0.0 level 2 metric 5
Found adjacency for mx1.lux.lu: mx2.lux.lu on interface ae0.0 with metric 5
NEIGHBOUR: mx2.lux.lu
Found match: set protocols isis interface ae1.0 level 2 metric 500
Found adjacency for mx1.lux.lu: rt1.fra.de on interface ae1.0 with metric 500
NEIGHBOUR: rt1.fra.de
Found match: set protocols isis interface ae1.0 level 2 metric 50
Found adjacency for mx1.lon.uk: mx1.par.fr on interface ae1.0 with metric 50
NEIGHBOUR: mx1.par.fr
Found match: set protocols isis interface ae2.0 level 2 metric 50
Found adjacency for mx1.lon.uk: mx1.ams.nl on interface ae2.0 with metric 50
NEIGHBOUR: mx1.ams.nl
Found match: set protocols isis interface ae4.0 level 2 metric 500
Found adjacency for mx1.lon.uk: mx1.bru.be on interface ae4.0 with metric 500
NEIGHBOUR: mx1.bru.be
Found match: set protocols isis interface ae5.0 level 2 metric 500
Found adjacency for mx1.lon.uk: mx1.lis.pt on interface ae5.0 with metric 500
NEIGHBOUR: mx1.lis.pt
Found match: set protocols isis interface ae7.0 level 2 metric 500
Found adjacency for mx1.lon.uk: mx1.dub.ie on interface ae7.0 with metric 500
NEIGHBOUR: mx1.dub.ie
Found match: set protocols isis interface ge-0/2/9.35 level 2 metric 100
Found adjacency for mx1.lon.uk: NCCRouter0 on interface ge-0/2/9.35 with metric 100
Ignoring NCC router...
Found match: set protocols isis interface ae0.0 level 2 metric 5
Found adjacency for mx2.lju.si: mx1.lju.si on interface ae0.0 with metric 5
NEIGHBOUR: mx1.lju.si
Found match: set protocols isis interface ae2.0 level 2 metric 450
Found adjacency for mx2.lju.si: mx1.bud.hu on interface ae2.0 with metric 450
NEIGHBOUR: mx1.bud.hu
Found match: set protocols isis interface ae0.0 level 2 metric 5
Found adjacency for mx1.lju.si: mx2.lju.si on interface ae0.0 with metric 5
NEIGHBOUR: mx2.lju.si
Found match: set protocols isis interface ae2.0 level 2 metric 450
Found adjacency for mx1.lju.si: mx1.vie.at on interface ae2.0 with metric 450
NEIGHBOUR: mx1.vie.at
Found match: set protocols isis interface ae3.0 level 2 metric 500
Found adjacency for mx1.lju.si: mx2.zag.hr on interface ae3.0 with metric 500
NEIGHBOUR: mx2.zag.hr
Found match: set protocols isis interface ae0.0 level 2 metric 450
Found adjacency for mx2.lis.pt: mx1.mad.es on interface ae0.0 with metric 450
NEIGHBOUR: mx1.mad.es
Found match: set protocols isis interface ae1.0 level 2 metric 5
Found adjacency for mx2.lis.pt: mx1.lis.pt on interface ae1.0 with metric 5
NEIGHBOUR: mx1.lis.pt
Found match: set protocols isis interface ae0.0 level 2 metric 5
Found adjacency for mx1.lis.pt: mx2.lis.pt on interface ae0.0 with metric 5
NEIGHBOUR: mx2.lis.pt
Found match: set protocols isis interface ae5.0 level 2 metric 500
Found adjacency for mx1.lis.pt: mx1.lon.uk on interface ae5.0 with metric 500
NEIGHBOUR: mx1.lon.uk
Found match: set protocols isis interface ae0.0 level 2 metric 100
Found adjacency for mx2.kau.lt: mx2.rig.lv on interface ae0.0 with metric 100
NEIGHBOUR: mx2.rig.lv
Found match: set protocols isis interface ae1.0 level 2 metric 5
Found adjacency for mx2.kau.lt: mx1.kau.lt on interface ae1.0 with metric 5
NEIGHBOUR: mx1.kau.lt
Found match: set protocols isis interface ae1.0 level 2 metric 5
Found adjacency for mx1.kau.lt: mx2.kau.lt on interface ae1.0 with metric 5
NEIGHBOUR: mx2.kau.lt
Found match: set protocols isis interface ae5.0 level 2 metric 445
Found adjacency for mx1.kau.lt: mx1.poz.pl on interface ae5.0 with metric 445
NEIGHBOUR: mx1.poz.pl
Found match: set protocols isis interface ae1.0 level 2 metric 50
Found adjacency for mx1.gen.ch: mx1.fra.de on interface ae1.0 with metric 50
NEIGHBOUR: mx1.fra.de
Found match: set protocols isis interface ae2.0 level 2 metric 50
Found adjacency for mx1.gen.ch: mx1.par.fr on interface ae2.0 with metric 50
NEIGHBOUR: mx1.par.fr
Found match: set protocols isis interface ae6.0 level 2 metric 50
Found adjacency for mx1.gen.ch: mx1.mil2.it on interface ae6.0 with metric 50
NEIGHBOUR: mx1.mil2.it
Found match: set protocols isis interface ae7.0 level 2 metric 100
Found adjacency for mx1.gen.ch: mx1.mad.es on interface ae7.0 with metric 100
NEIGHBOUR: mx1.mad.es
Found match: set protocols isis interface ae4.0 level 2 metric 5
Found adjacency for rt1.fra.de: mx1.fra.de on interface ae4.0 with metric 5
NEIGHBOUR: mx1.fra.de
Found match: set protocols isis interface ae5.0 level 2 metric 500
Found adjacency for rt1.fra.de: mx1.lux.lu on interface ae5.0 with metric 500
NEIGHBOUR: mx1.lux.lu
Found match: set protocols isis interface ge-0/2/0.153 level 2 metric 1000
Found adjacency for rt1.fra.de: 0620.4010.4090 on interface ge-0/2/0.153 with metric 1000
Ignoring NetReflex...
Found match: set protocols isis interface ae0.0 level 2 metric 50
Found adjacency for mx1.fra.de: mx1.ams.nl on interface ae0.0 with metric 50
NEIGHBOUR: mx1.ams.nl
Found match: set protocols isis interface ae1.0 level 2 metric 50
Found adjacency for mx1.fra.de: mx1.gen.ch on interface ae1.0 with metric 50
NEIGHBOUR: mx1.gen.ch
Found match: set protocols isis interface ae2.0 level 2 metric 50
Found adjacency for mx1.fra.de: mx1.pra.cz on interface ae2.0 with metric 50
NEIGHBOUR: mx1.pra.cz
Found match: set protocols isis interface ae3.0 level 2 metric 105
Found adjacency for mx1.fra.de: mx1.poz.pl on interface ae3.0 with metric 105
NEIGHBOUR: mx1.poz.pl
Found match: set protocols isis interface ae4.0 level 2 metric 5
Found adjacency for mx1.fra.de: rt1.fra.de on interface ae4.0 with metric 5
NEIGHBOUR: rt1.fra.de
Found match: set protocols isis interface ae6.0 level 2 metric 100
Found adjacency for mx1.fra.de: mx1.cop.dk on interface ae6.0 with metric 100
NEIGHBOUR: mx1.cop.dk
Found match: set protocols isis interface ae1.0 level 2 metric 10
Found adjacency for mx2.dub.ie: mx1.dub.ie on interface ae1.0 with metric 10
NEIGHBOUR: mx1.dub.ie
Found match: set protocols isis interface ae2.0 level 2 metric 1000
Found adjacency for mx2.dub.ie: mx1.par.fr on interface ae2.0 with metric 1000
NEIGHBOUR: mx1.par.fr
Found match: set protocols isis interface ae0.0 level 2 metric 10
Found adjacency for mx1.dub.ie: mx2.dub.ie on interface ae0.0 with metric 10
NEIGHBOUR: mx2.dub.ie
Found match: set protocols isis interface ae7.0 level 2 metric 500
Found adjacency for mx1.dub.ie: mx1.lon.uk on interface ae7.0 with metric 500
NEIGHBOUR: mx1.lon.uk
Found match: set protocols isis interface ae0.0 level 2 metric 100
Found adjacency for mx1.cop.dk: mx1.ams.nl on interface ae0.0 with metric 100
NEIGHBOUR: mx1.ams.nl
Found match: set protocols isis interface ae3.0 level 2 metric 510
Found adjacency for mx1.cop.dk: mx1.tal.ee on interface ae3.0 with metric 510
NEIGHBOUR: mx1.tal.ee
Found match: set protocols isis interface ae6.0 level 2 metric 100
Found adjacency for mx1.cop.dk: mx1.fra.de on interface ae6.0 with metric 100
NEIGHBOUR: mx1.fra.de
Found match: set protocols isis interface so-1/3/0.0 level 2 metric 100
Found adjacency for rt1.cat.it: mx1.mil2.it on interface so-1/3/0.0 with metric 100
NEIGHBOUR: mx1.mil2.it
Found match: set protocols isis interface ae0.0 level 2 metric 75
Found adjacency for mx1.bud.hu: mx2.bra.sk on interface ae0.0 with metric 75
NEIGHBOUR: mx2.bra.sk
Found match: set protocols isis interface ae1.0 level 2 metric 100
Found adjacency for mx1.bud.hu: mx1.zag.hr on interface ae1.0 with metric 100
NEIGHBOUR: mx1.zag.hr
Found match: set protocols isis interface ae2.0 level 2 metric 450
Found adjacency for mx1.bud.hu: mx2.lju.si on interface ae2.0 with metric 450
NEIGHBOUR: mx2.lju.si
Found match: set protocols isis interface ae4.0 level 2 metric 100
Found adjacency for mx1.bud.hu: rt1.buc.ro on interface ae4.0 with metric 100
NEIGHBOUR: rt1.buc.ro
Found match: set protocols isis interface ae5.0 level 2 metric 100
Found adjacency for mx1.bud.hu: rt1.sof.bg on interface ae5.0 with metric 100
NEIGHBOUR: rt1.sof.bg
Found match: set protocols isis interface ae4.0 level 2 metric 100
Found adjacency for rt1.buc.ro: mx1.bud.hu on interface ae4.0 with metric 100
NEIGHBOUR: mx1.bud.hu
Found match: set protocols isis interface ae6.0 level 2 metric 175
Found adjacency for rt1.buc.ro: mx1.vie.at on interface ae6.0 with metric 175
NEIGHBOUR: mx1.vie.at
Found match: set protocols isis interface ae0.0 level 2 metric 5
Found adjacency for mx2.bru.be: mx1.bru.be on interface ae0.0 with metric 5
NEIGHBOUR: mx1.bru.be
Found match: set protocols isis interface ae1.0 level 2 metric 500
Found adjacency for mx2.bru.be: mx1.ams.nl on interface ae1.0 with metric 500
NEIGHBOUR: mx1.ams.nl
Found match: set protocols isis interface ae0.0 level 2 metric 5
Found adjacency for mx1.bru.be: mx2.bru.be on interface ae0.0 with metric 5
NEIGHBOUR: mx2.bru.be
Found match: set protocols isis interface ae4.0 level 2 metric 500
Found adjacency for mx1.bru.be: mx1.lon.uk on interface ae4.0 with metric 500
NEIGHBOUR: mx1.lon.uk
Found match: set protocols isis interface ae0.0 level 2 metric 75
Found adjacency for mx2.bra.sk: mx1.bud.hu on interface ae0.0 with metric 75
NEIGHBOUR: mx1.bud.hu
Found match: set protocols isis interface ae1.0 level 2 metric 50
Found adjacency for mx2.bra.sk: mx1.vie.at on interface ae1.0 with metric 50
NEIGHBOUR: mx1.vie.at
Found match: set protocols isis interface ae2.0 level 2 metric 50
Found adjacency for mx2.bra.sk: mx1.pra.cz on interface ae2.0 with metric 50
NEIGHBOUR: mx1.pra.cz
Found match: set protocols isis interface ae1.0 level 2 metric 5
Found adjacency for mx2.ath.gr: mx1.ath.gr on interface ae1.0 with metric 5
NEIGHBOUR: mx1.ath.gr
Found match: set protocols isis interface ae2.0 level 2 metric 500
Found adjacency for mx2.ath.gr: mx1.vie.at on interface ae2.0 with metric 500
NEIGHBOUR: mx1.vie.at
Found match: set protocols isis interface ae1.0 level 2 metric 5
Found adjacency for mx1.ath.gr: mx2.ath.gr on interface ae1.0 with metric 5
NEIGHBOUR: mx2.ath.gr
Found match: set protocols isis interface ae2.0 level 2 metric 500
Found adjacency for mx1.ath.gr: mx1.mil2.it on interface ae2.0 with metric 500
NEIGHBOUR: mx1.mil2.it
Found match: set protocols isis interface ae0.0 level 2 metric 50
Found adjacency for mx1.ams.nl: mx1.lon.uk on interface ae0.0 with metric 50
NEIGHBOUR: mx1.lon.uk
Found match: set protocols isis interface ae1.0 level 2 metric 50
Found adjacency for mx1.ams.nl: mx1.fra.de on interface ae1.0 with metric 50
NEIGHBOUR: mx1.fra.de
Found match: set protocols isis interface ae2.0 level 2 metric 100
Found adjacency for mx1.ams.nl: mx1.cop.dk on interface ae2.0 with metric 100
NEIGHBOUR: mx1.cop.dk
Found match: set protocols isis interface ae3.110 level 2 metric 5
Found adjacency for mx1.ams.nl: rt1.ams.nl on interface ae3.110 with metric 5
NEIGHBOUR: rt1.ams.nl
Found match: set protocols isis interface ae4.0 level 2 metric 550
Found adjacency for mx1.ams.nl: mx2.lux.lu on interface ae4.0 with metric 550
NEIGHBOUR: mx2.lux.lu
Found match: set protocols isis interface ae5.0 level 2 metric 500
Found adjacency for mx1.ams.nl: mx2.bru.be on interface ae5.0 with metric 500
NEIGHBOUR: mx2.bru.be
Found match: set protocols isis interface ae2.110 level 2 metric 5
Found adjacency for rt1.ams.nl: mx1.ams.nl on interface ae2.110 with metric 5
NEIGHBOUR: mx1.ams.nl
GRAPHING...
Read input file: geant_dynamic.txt
Wrote DOT language output file: geantISIS.dot
Wrote final diagram output file: geantISIS.png
Done!
niall@concrescence ~/Code/ISIS Diagram $

And the source for isisdiag.py:


#!/usr/bin/python
"""
Connect to all routers in GEANT, obtain IS-IS adjacency information, graph it
"""
__author__ = """Niall Donaghy (niall@ndonaghy.com)"""

import subprocess
import time

def fetchConfig(command):
"""
Spawn SSH process, connect to user@jumpboxhost.geant.net and execute 'command', saving output
"""
  proc = subprocess.Popen(['ssh', 'user@jumpboxhost.geant.net', command], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  out, err = proc.communicate()
  return out

def scrapeConfig():
"""
Because our list of routers and their co-ordinates are fixed, we just need to compute the ISIS trunks (edges between nodes)
First we open the base input file 'geant.txt' and find the lines defining each router node
Next we fetch the relevant JunOS output, namely 'show isis adjacencies' and 'show configuration protocols isis'
From this output we can determine the edges and write a new file containing both node and edge definitions
"""

  import re

  # open file geant.txt
  try:
    fhr=open("geant.txt","r")
    print("Reading input file: geant.txt")
  except:
    print("File not found!")

  # geant.txt config file has the format, eg:
  # rt1.ams.nl.geant.net,rt1.ams.nl[10000,3000]
  # fqdn, shortname, [x,y] coords

  # store fqdn
  routers=[]
  # store shortname
  routersShortname=[]
  # store each line of config
  routersDef=[]
  # store each line of 'show isis adj' output
  routersAdj=[]
  # store the set command configuration
  routersCfg=[]

  print("Discovering router definitions in input file...")
  for line in fhr.readlines():
    if line.startswith("#"): # skip comments
      continue

    numfind=re.compile("^\d+,*")

    if numfind.match(line): # this line is IS-IS cost information
      print("Erroneous line in input file, ignoring: " + line)
    else: # this line is a router definition
      (router,coord)=line.split("[")
      (fqdn,shortname)=router.split(",")
      routers.insert(0,fqdn)
      print("Found " + fqdn + ", fetching configuration...")
      routersDef.insert(0,line)
      routersShortname.insert(0,shortname)
      routersAdj.insert(0,"")
      routersCfg.insert(0,"")
      # fetch info...
      command = r'ssh -i sshkey -q -o StrictHostKeyChecking=no user@' + fqdn + ' "show isis adj | except Interface"'
      routersAdj[0] = fetchConfig(command)
      command = r'ssh -i sshkey -q -o StrictHostKeyChecking=no user@' + fqdn + ' "show configuration protocols isis | display set | match metric | match interface"'
      routersCfg[0] = fetchConfig(command)
  fhr.close()

  # Now we've read in the configuration and retrieved ISIS adjacency data, create graph...
  try:
    fhw=open("geant_dynamic.txt","w")
    print("Writing to output file: geant_dynamic.txt")
  except:
    print("File not found!")

# magic happens here...
print("Processing configuration and generating graph data...")
for i in range(len(routersDef)):
  fhw.write(routersDef[i])

  adjacencies = routersAdj[i].split("\n")
  for line in adjacencies:
    interface=""
    neighbour=""
    # For established adjacencies...
    if "Up" in line:
      parts = line.split()
      interface = parts[0]
      neighbour = parts[1]
      match = re.search(".re\d$", neighbour)
      if match:
        neighbour = neighbour[:-4]
      metric = 0

      configuration = routersCfg[i].split("\n")
      for line in configuration:
        match = re.search(interface, line)
        if match:
          print("Found match: " + line)
          metric = line.split()[8]
          print("Found adjacency for " + routersShortname[i] + ": " + neighbour + " on interface " + interface + " with metric " + metric)
          # Ignore non-backbone ISIS neighbours...
          if "NCC" in neighbour:
            print("Ignoring NCC router...")
          elif "0620.4010.4090" in neighbour:
            print("Ignoring NetReflex...")
          # Or write output for backbone ISIS neighbours...
          else:
            fhw.write(str(metric) + "," + neighbour + "\n")
            print("NEIGHBOUR: " + neighbour)
  fhw.close()

def createGraph():
"""
Return a graph from the data in geant_dynamic.txt.
"""
  import math
  import re

  # open file geant_dynamic.txt
  print("GRAPHING...")
  try:
    fh=open("geant_dynamic.txt","r")
    print("Read input file: geant_dynamic.txt")
  except:
    print("File not found!")

  G=pgv.AGraph(name='GEANT')
  G.node_attr['shape']='circle'
  G.node_attr['fixedsize']='true'
  G.node_attr['fontsize']='14'
  G.node_attr['style']='filled'
  G.node_attr['color']='#40e0d0'
  G.graph_attr['outputorder']='edgesfirst'
  G.graph_attr['label']='GEANT IS-IS Diagram, generated ' + time.strftime("%d/%m/%Y") + ' @ ' + time.strftime("%H:%M:%S")
  G.graph_attr['fontsize']='72'
  G.graph_attr['ratio']='1.0'
  G.edge_attr['color']='#AA00FF'
  G.edge_attr['style']='setlinewidth(3)'
  G.graph_attr['splines']='true'

  routers=[]
  for line in fh.readlines():
    if line.startswith("#"): # skip comments
      continue

  numfind=re.compile("^\d+,*")

  if numfind.match(line): # this line is IS-IS cost information
    cost,host=line.split(",")
    host=str(host)
    host=host.strip()
    cost=str(cost)
    cost=cost.strip()
    G.add_edge(routers[0],host,label=cost)
  else: # this line is a router definition
    (router,coord)=line.split("[")
    (fqdn,shortname)=router.split(",")
    routers.insert(0,shortname)
    coord=coord[:-2]
    (y,x)=coord.split(",")
    G.add_node(shortname)
    n=G.get_node(shortname)
    n.attr['pos']="%f,%f)"%(float(x)/7.0,float(y)/7.0)
    # assign node size
    d=1.25
    n.attr['height']="%s"%d
    n.attr['width']="%s"%d
    # assign node color
    n.attr['fillcolor']="#0000%2x"%(int(d*256))
    # label
    n.attr['label']=str(shortname)
    return G

if __name__ == '__main__':
  import warnings
  import pygraphviz as pgv

  # ignore Graphviz warning messages
  warnings.simplefilter('ignore', RuntimeWarning)

  scrapeConfig()
  G=createGraph()

  G.string()
  G.write("geantISIS.dot")
  print("Wrote DOT language output file: geantISIS.dot")
  G.draw("geantISIS.png",prog='neato',args='-n2')
  print("Wrote final diagram output file: geantISIS.png")
  print("Done!")

geant.txt:


rt1.ams.nl.geant.net,rt1.ams.nl[10000,3000]
mx1.ams.nl.geant.net,mx1.ams.nl[9000,4000]
mx1.ath.gr.geant.net,mx1.ath.gr[1000,12000]
mx2.ath.gr.geant.net,mx2.ath.gr[1000,13000]
mx2.bra.sk.geant.net,mx2.bra.sk[5000,9000]
mx1.bru.be.geant.net,mx1.bru.be[8000,3000]
mx2.bru.be.geant.net,mx2.bru.be[8000,4000]
rt1.buc.ro.geant.net,rt1.buc.ro[3000,14500]
mx1.bud.hu.geant.net,mx1.bud.hu[5000,12000]
rt1.cat.it.geant.net,rt1.cat.it[1000,9000]
mx1.cop.dk.geant.net,mx1.cop.dk[11000,8000]
mx1.dub.ie.geant.net,mx1.dub.ie[10000,1000]
mx2.dub.ie.geant.net,mx2.dub.ie[9000,1000]
mx1.fra.de.geant.net,mx1.fra.de[7000,7000]
rt1.fra.de.geant.net,rt1.fra.de[7000,6000]
mx1.gen.ch.geant.net,mx1.gen.ch[5000,5000]
mx1.kau.lt.geant.net,mx1.kau.lt[11000,12000]
mx2.kau.lt.geant.net,mx2.kau.lt[11000,13000]
mx1.lis.pt.geant.net,mx1.lis.pt[3000,1000]
mx2.lis.pt.geant.net,mx2.lis.pt[2000,1000]
mx1.lju.si.geant.net,mx1.lju.si[6000,9000]
mx2.lju.si.geant.net,mx2.lju.si[6000,10000]
mx1.lon.uk.geant.net,mx1.lon.uk[9000,2000]
mx1.lux.lu.geant.net,mx1.lux.lu[7000,5000]
mx2.lux.lu.geant.net,mx2.lux.lu[7000,4000]
mx1.mad.es.geant.net,mx1.mad.es[2000,2000]
mx1.mil2.it.geant.net,mx1.mil2.it[4000,6000]
mx1.par.fr.geant.net,mx1.par.fr[7000,3000]
mx1.poz.pl.geant.net,mx1.poz.pl[9000,8000]
mx1.pra.cz.geant.net,mx1.pra.cz[7000,8000]
mx1.rig.lv.geant.net,mx1.rig.lv[12000,12000]
mx2.rig.lv.geant.net,mx2.rig.lv[12000,13000]
rt1.sof.bg.geant.net,rt1.sof.bg[2000,13500]
mx1.tal.ee.geant.net,mx1.tal.ee[13000,12000]
mx2.tal.ee.geant.net,mx2.tal.ee[13000,13000]
rt1.vie.at.geant.net,rt1.vie.at[3500,9000]
mx1.vie.at.geant.net,mx1.vie.at[4500,10000]
mx1.zag.hr.geant.net,mx1.zag.hr[6000,12000]
mx2.zag.hr.geant.net,mx2.zag.hr[6000,11000]

geant_dynamic.txt:


mx2.zag.hr.geant.net,mx2.zag.hr[6000,11000]
5,mx1.zag.hr
500,mx1.lju.si
100,mx1.vie.at
mx1.zag.hr.geant.net,mx1.zag.hr[6000,12000]
5,mx2.zag.hr
100,mx1.bud.hu
mx1.vie.at.geant.net,mx1.vie.at[4500,10000]
50,mx2.bra.sk
100,mx2.zag.hr
450,mx1.lju.si
5,rt1.vie.at
50,mx1.mil2.it
175,rt1.buc.ro
175,rt1.sof.bg
500,mx2.ath.gr
rt1.vie.at.geant.net,rt1.vie.at[3500,9000]
5,mx1.vie.at
mx2.tal.ee.geant.net,mx2.tal.ee[13000,13000]
100,mx1.rig.lv
5,mx1.tal.ee
mx1.tal.ee.geant.net,mx1.tal.ee[13000,12000]
510,mx1.cop.dk
5,mx2.tal.ee
rt1.sof.bg.geant.net,rt1.sof.bg[2000,13500]
100,mx1.bud.hu
175,mx1.vie.at
mx2.rig.lv.geant.net,mx2.rig.lv[12000,13000]
100,mx2.kau.lt
5,mx1.rig.lv
mx1.rig.lv.geant.net,mx1.rig.lv[12000,12000]
100,mx2.tal.ee
5,mx2.rig.lv
mx1.pra.cz.geant.net,mx1.pra.cz[7000,8000]
50,mx1.fra.de
50,mx2.bra.sk
200,mx1.poz.pl
mx1.poz.pl.geant.net,mx1.poz.pl[9000,8000]
105,mx1.fra.de
200,mx1.pra.cz
445,mx1.kau.lt
mx1.par.fr.geant.net,mx1.par.fr[7000,3000]
50,mx1.lon.uk
1000,mx2.dub.ie
100,mx1.mad.es
50,mx1.gen.ch
500,mx2.lux.lu
mx1.mil2.it.geant.net,mx1.mil2.it[4000,6000]
50,mx1.vie.at
50,mx1.gen.ch
500,mx1.ath.gr
100,rt1.cat.it
mx1.mad.es.geant.net,mx1.mad.es[2000,2000]
450,mx2.lis.pt
100,mx1.par.fr
100,mx1.gen.ch
mx2.lux.lu.geant.net,mx2.lux.lu[7000,4000]
5,mx1.lux.lu
500,mx1.par.fr
550,mx1.ams.nl
mx1.lux.lu.geant.net,mx1.lux.lu[7000,5000]
5,mx2.lux.lu
500,rt1.fra.de
mx1.lon.uk.geant.net,mx1.lon.uk[9000,2000]
50,mx1.par.fr
50,mx1.ams.nl
500,mx1.bru.be
500,mx1.lis.pt
500,mx1.dub.ie
mx2.lju.si.geant.net,mx2.lju.si[6000,10000]
5,mx1.lju.si
450,mx1.bud.hu
mx1.lju.si.geant.net,mx1.lju.si[6000,9000]
5,mx2.lju.si
450,mx1.vie.at
500,mx2.zag.hr
mx2.lis.pt.geant.net,mx2.lis.pt[2000,1000]
450,mx1.mad.es
5,mx1.lis.pt
mx1.lis.pt.geant.net,mx1.lis.pt[3000,1000]
5,mx2.lis.pt
500,mx1.lon.uk
mx2.kau.lt.geant.net,mx2.kau.lt[11000,13000]
100,mx2.rig.lv
5,mx1.kau.lt
mx1.kau.lt.geant.net,mx1.kau.lt[11000,12000]
5,mx2.kau.lt
445,mx1.poz.pl
mx1.gen.ch.geant.net,mx1.gen.ch[5000,5000]
50,mx1.fra.de
50,mx1.par.fr
50,mx1.mil2.it
100,mx1.mad.es
rt1.fra.de.geant.net,rt1.fra.de[7000,6000]
5,mx1.fra.de
500,mx1.lux.lu
mx1.fra.de.geant.net,mx1.fra.de[7000,7000]
50,mx1.ams.nl
50,mx1.gen.ch
50,mx1.pra.cz
105,mx1.poz.pl
5,rt1.fra.de
100,mx1.cop.dk
mx2.dub.ie.geant.net,mx2.dub.ie[9000,1000]
10,mx1.dub.ie
1000,mx1.par.fr
mx1.dub.ie.geant.net,mx1.dub.ie[10000,1000]
10,mx2.dub.ie
500,mx1.lon.uk
mx1.cop.dk.geant.net,mx1.cop.dk[11000,8000]
100,mx1.ams.nl
510,mx1.tal.ee
100,mx1.fra.de
rt1.cat.it.geant.net,rt1.cat.it[1000,9000]
100,mx1.mil2.it
mx1.bud.hu.geant.net,mx1.bud.hu[5000,12000]
75,mx2.bra.sk
100,mx1.zag.hr
450,mx2.lju.si
100,rt1.buc.ro
100,rt1.sof.bg
rt1.buc.ro.geant.net,rt1.buc.ro[3000,14500]
100,mx1.bud.hu
175,mx1.vie.at
mx2.bru.be.geant.net,mx2.bru.be[8000,4000]
5,mx1.bru.be
500,mx1.ams.nl
mx1.bru.be.geant.net,mx1.bru.be[8000,3000]
5,mx2.bru.be
500,mx1.lon.uk
mx2.bra.sk.geant.net,mx2.bra.sk[5000,9000]
75,mx1.bud.hu
50,mx1.vie.at
50,mx1.pra.cz
mx2.ath.gr.geant.net,mx2.ath.gr[1000,13000]
5,mx1.ath.gr
500,mx1.vie.at
mx1.ath.gr.geant.net,mx1.ath.gr[1000,12000]
5,mx2.ath.gr
500,mx1.mil2.it
mx1.ams.nl.geant.net,mx1.ams.nl[9000,4000]
50,mx1.lon.uk
50,mx1.fra.de
100,mx1.cop.dk
5,rt1.ams.nl
550,mx2.lux.lu
500,mx2.bru.be
rt1.ams.nl.geant.net,rt1.ams.nl[10000,3000]
5,mx1.ams.nl

geant.dot:


strict graph GEANT {
  graph [fontsize=72,
    label="GEANT IS-IS Diagram, generated 13/04/2014 @ 22:52:16",
    outputorder=edgesfirst,
    ratio="1.0",
    splines=true
  ];
  node [color="#40e0d0",
    fixedsize=true,
    fontsize=14,
    label="\N",
    shape=circle,
    style=filled
  ];
  edge [color="#AA00FF",
    style="setlinewidth(3)"
  ];
  "mx2.zag.hr"     [fillcolor="#0000140",
    height="1.25",
    label="mx2.zag.hr",
    pos="1571.428571,857.142857)",
    width="1.25"];
  "mx1.zag.hr"     [fillcolor="#0000140",
    height="1.25",
    label="mx1.zag.hr",
    pos="1714.285714,857.142857)",
    width="1.25"];
  "mx2.zag.hr" -- "mx1.zag.hr"     [label=5];
  "mx1.vie.at"     [fillcolor="#0000140",
    height="1.25",
    label="mx1.vie.at",
    pos="1428.571429,642.857143)",
    width="1.25"];
  "mx2.zag.hr" -- "mx1.vie.at"     [label=100];
  "mx1.lju.si"     [fillcolor="#0000140",
    height="1.25",
    label="mx1.lju.si",
    pos="1285.714286,857.142857)",
    width="1.25"];
  "mx2.zag.hr" -- "mx1.lju.si"     [label=500];
  "mx1.bud.hu"     [fillcolor="#0000140",
    height="1.25",
    label="mx1.bud.hu",
    pos="1714.285714,714.285714)",
    width="1.25"];
  "mx1.zag.hr" -- "mx1.bud.hu"     [label=100];
  "mx1.vie.at" -- "mx1.lju.si"     [label=450];
  "rt1.sof.bg"     [fillcolor="#0000140",
    height="1.25",
    label="rt1.sof.bg",
    pos="1928.571429,285.714286)",
    width="1.25"];
  "mx1.vie.at" -- "rt1.sof.bg"     [label=175];
  "rt1.buc.ro"     [fillcolor="#0000140",
    height="1.25",
    label="rt1.buc.ro",
    pos="2071.428571,428.571429)",
    width="1.25"];
  "mx1.vie.at" -- "rt1.buc.ro"     [label=175];
  "mx2.bra.sk"     [fillcolor="#0000140",
    height="1.25",
    label="mx2.bra.sk",
    pos="1285.714286,714.285714)",
    width="1.25"];
  "mx1.vie.at" -- "mx2.bra.sk"     [label=50];
  "mx2.ath.gr"     [fillcolor="#0000140",
    height="1.25",
    label="mx2.ath.gr",
    pos="1857.142857,142.857143)",
    width="1.25"];
  "mx1.vie.at" -- "mx2.ath.gr"     [label=500];
  "mx1.mil2.it"     [fillcolor="#0000140",
    height="1.25",
    label="mx1.mil2.it",
    pos="857.142857,571.428571)",
    width="1.25"];
  "mx1.vie.at" -- "mx1.mil2.it"     [label=50];
  "rt1.vie.at"     [fillcolor="#0000140",
    height="1.25",
    label="rt1.vie.at",
    pos="1285.714286,500.000000)",
    width="1.25"];
  "mx1.vie.at" -- "rt1.vie.at"     [label=5];
  "mx1.bud.hu" -- "rt1.buc.ro"     [label=100];
  "mx1.bud.hu" -- "mx2.bra.sk"     [label=75];
  "mx1.gen.ch"     [fillcolor="#0000140",
    height="1.25",
    label="mx1.gen.ch",
    pos="714.285714,714.285714)",
    width="1.25"];
  "mx1.mil2.it" -- "mx1.gen.ch"     [label=50];
  "rt1.cat.it"     [fillcolor="#0000140",
    height="1.25",
    label="rt1.cat.it",
    pos="1285.714286,142.857143)",
    width="1.25"];
  "mx1.mil2.it" -- "rt1.cat.it"     [label=100];
  "mx1.ath.gr"     [fillcolor="#0000140",
    height="1.25",
    label="mx1.ath.gr",
    pos="1714.285714,142.857143)",
    width="1.25"];
  "mx1.mil2.it" -- "mx1.ath.gr"     [label=500];
  "rt1.sof.bg" -- "mx1.bud.hu"     [label=100];
  "mx2.ath.gr" -- "mx1.ath.gr"     [label=5];
  "mx2.tal.ee"     [fillcolor="#0000140",
    height="1.25",
    label="mx2.tal.ee",
    pos="1857.142857,1857.142857)",
    width="1.25"];
  "mx1.tal.ee"     [fillcolor="#0000140",
    height="1.25",
    label="mx1.tal.ee",
    pos="1714.285714,1857.142857)",
    width="1.25"];
  "mx2.tal.ee" -- "mx1.tal.ee"     [label=5];
  "mx1.rig.lv"     [fillcolor="#0000140",
    height="1.25",
    label="mx1.rig.lv",
    pos="1714.285714,1714.285714)",
    width="1.25"];
  "mx2.tal.ee" -- "mx1.rig.lv"     [label=100];
  "mx1.cop.dk"     [fillcolor="#0000140",
    height="1.25",
    label="mx1.cop.dk",
    pos="1142.857143,1571.428571)",
    width="1.25"];
  "mx1.tal.ee" -- "mx1.cop.dk"     [label=510];
  "mx1.ams.nl"     [fillcolor="#0000140",
    height="1.25",
    label="mx1.ams.nl",
    pos="571.428571,1285.714286)",
    width="1.25"];
  "mx1.cop.dk" -- "mx1.ams.nl"     [label=100];
  "mx2.rig.lv"     [fillcolor="#0000140",
    height="1.25",
    label="mx2.rig.lv",
    pos="1857.142857,1714.285714)",
    width="1.25"];
  "mx2.kau.lt"     [fillcolor="#0000140",
    height="1.25",
    label="mx2.kau.lt",
    pos="1857.142857,1571.428571)",
    width="1.25"];
  "mx2.rig.lv" -- "mx2.kau.lt"     [label=100];
  "mx2.rig.lv" -- "mx1.rig.lv"     [label=5];
  "mx1.kau.lt"     [fillcolor="#0000140",
    height="1.25",
    label="mx1.kau.lt",
    pos="1714.285714,1571.428571)",
    width="1.25"];
  "mx2.kau.lt" -- "mx1.kau.lt"     [label=5];
  "mx1.pra.cz"     [fillcolor="#0000140",
    height="1.25",
    label="mx1.pra.cz",
    pos="1142.857143,1000.000000)",
    width="1.25"];
  "mx1.poz.pl"     [fillcolor="#0000140",
    height="1.25",
    label="mx1.poz.pl",
    pos="1142.857143,1285.714286)",
    width="1.25"];
  "mx1.pra.cz" -- "mx1.poz.pl"     [label=200];
  "mx1.fra.de"     [fillcolor="#0000140",
    height="1.25",
    label="mx1.fra.de",
    pos="1000.000000,1000.000000)",
    width="1.25"];
  "mx1.pra.cz" -- "mx1.fra.de"     [label=50];
  "mx1.pra.cz" -- "mx2.bra.sk"     [label=50];
  "mx1.fra.de" -- "mx1.cop.dk"     [label=100];
  "mx1.fra.de" -- "mx1.ams.nl"     [label=50];
  "mx1.poz.pl" -- "mx1.fra.de"     [label=105];
  "mx1.poz.pl" -- "mx1.kau.lt"     [label=445];
  "mx1.par.fr"     [fillcolor="#0000140",
    height="1.25",
    label="mx1.par.fr",
    pos="428.571429,1000.000000)",
    width="1.25"];
  "mx1.par.fr" -- "mx1.gen.ch"     [label=50];
  "mx1.lon.uk"     [fillcolor="#0000140",
    height="1.25",
    label="mx1.lon.uk",
    pos="285.714286,1285.714286)",
    width="1.25"];
  "mx1.par.fr" -- "mx1.lon.uk"     [label=50];
  "mx2.lux.lu"     [fillcolor="#0000140",
    height="1.25",
    label="mx2.lux.lu",
    pos="571.428571,1000.000000)",
    width="1.25"];
  "mx1.par.fr" -- "mx2.lux.lu"     [label=500];
  "mx1.mad.es"     [fillcolor="#0000140",
    height="1.25",
    label="mx1.mad.es",
    pos="285.714286,285.714286)",
    width="1.25"];
  "mx1.par.fr" -- "mx1.mad.es"     [label=100];
  "mx2.dub.ie"     [fillcolor="#0000140",
    height="1.25",
    label="mx2.dub.ie",
    pos="142.857143,1285.714286)",
    width="1.25"];
  "mx1.par.fr" -- "mx2.dub.ie"     [label=1000];
  "mx1.bru.be"     [fillcolor="#0000140",
    height="1.25",
    label="mx1.bru.be",
    pos="428.571429,1142.857143)",
    width="1.25"];
  "mx1.lon.uk" -- "mx1.bru.be"     [label=500];
  "mx1.lis.pt"     [fillcolor="#0000140",
    height="1.25",
    label="mx1.lis.pt",
    pos="142.857143,428.571429)",
    width="1.25"];
  "mx1.lon.uk" -- "mx1.lis.pt"     [label=500];
  "mx1.lon.uk" -- "mx1.ams.nl"     [label=50];
  "mx1.dub.ie"     [fillcolor="#0000140",
    height="1.25",
    label="mx1.dub.ie",
    pos="142.857143,1428.571429)",
    width="1.25"];
  "mx1.lon.uk" -- "mx1.dub.ie"     [label=500];
  "mx2.dub.ie" -- "mx1.dub.ie"     [label=10];
  "mx1.mad.es" -- "mx1.gen.ch"     [label=100];
  "mx2.lis.pt"     [fillcolor="#0000140",
    height="1.25",
    label="mx2.lis.pt",
    pos="142.857143,285.714286)",
    width="1.25"];
  "mx1.mad.es" -- "mx2.lis.pt"     [label=450];
  "mx1.gen.ch" -- "mx1.fra.de"     [label=50];
  "mx1.lux.lu"     [fillcolor="#0000140",
    height="1.25",
    label="mx1.lux.lu",
    pos="714.285714,1000.000000)",
    width="1.25"];
  "mx2.lux.lu" -- "mx1.lux.lu"     [label=5];
  "mx2.lux.lu" -- "mx1.ams.nl"     [label=550];
  "mx2.lis.pt" -- "mx1.lis.pt"     [label=5];
  "rt1.fra.de"     [fillcolor="#0000140",
    height="1.25",
    label="rt1.fra.de",
    pos="857.142857,1000.000000)",
    width="1.25"];
  "mx1.lux.lu" -- "rt1.fra.de"     [label=500];
  "rt1.ams.nl"     [fillcolor="#0000140",
    height="1.25",
    label="rt1.ams.nl",
    pos="428.571429,1428.571429)",
    width="1.25"];
  "mx1.ams.nl" -- "rt1.ams.nl"     [label=5];
  "rt1.fra.de" -- "mx1.fra.de"     [label=5];
  "mx2.lju.si"     [fillcolor="#0000140",
    height="1.25",
    label="mx2.lju.si",
    pos="1428.571429,857.142857)",
    width="1.25"];
  "mx2.lju.si" -- "mx1.lju.si"     [label=5];
  "mx2.lju.si" -- "mx1.bud.hu"     [label=450];
  "mx2.bru.be"     [fillcolor="#0000140",
    height="1.25",
    label="mx2.bru.be",
    pos="571.428571,1142.857143)",
    width="1.25"];
  "mx2.bru.be" -- "mx1.bru.be"     [label=5];
  "mx2.bru.be" -- "mx1.ams.nl"     [label=500];
}