Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plot #94

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open

Plot #94

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions ipmininet/examples/topo_tp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"subnets" : {
"as1_lan" :{
"address" :"cafe:babe:dead:beaf::/64",
"hosts" : {"as1_h1" : "as1_s2"}
},
"as2_lan" :{
"address":"c1a4:4ad:c0ff:ee::/64",
"hosts" : {"as2_h2" : ["as2_cl1", "as2_cl2"]}
}
},
"AS" : {
"AS1" : {
"routers" : {
"as1_rr1" : {
"clients" : ["as1_s1"],
"daemons" : {"bgp" : {"networks":["as1_lan"]}}
},
"as1_rr2" : {
"clients" : ["as1_s2"],
"daemons" : {"bgp" : {"networks":["as1_lan"]}}
},
"as1_s1" : {},
"as1_s2" : {}
},
"hosts" : {
"as1_h1" : {}
},
"rconfig" : {
"daemons" : ["ospf", "bgp"]
}
},
"AS2" : {
"routers" : {
"as2_cl1" : {},
"as2_cl2" : {}
},
"hosts" : {
"as2_h2" : {}
},
"rconfig" : {
"daemons" : {"bgp" : {"redistribute":["connected"]}}
}
}
},
"links" : {
"as1_rr1" : [
["as1_rr2", {"igp_metric" : 5}],
"as2_cl1"
],
"as1_rr2" : ["as2_cl2"],
"as1_s1" : ["as1_s2", "as1_rr2"],
"as1_s2" : ["as1_rr1", "as1_h1"],
"as2_h2" : ["as2_cl1", "as2_cl2"]
}
}
124 changes: 124 additions & 0 deletions ipmininet/plot_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import os
import json
import graphviz as gv

_graphviz_path = 'C:/Program Files (x86)/Graphviz2.38/bin'
os.environ['path'] += os.pathsep + _graphviz_path

R_SHAPE = 'circle'
RR_SHAPE = 'doublecircle'
HOST_SHAPE = 'egg'

def to_graphviz(topo_config, filename = 'topo_plot.gv', kwargs = {}, ** sub_kwargs):
"""
Function to convert BGP topology (configured as a json file) to a pygraphviz Grav instance.

Arguments :
- topo_config : dict or .json filename
- filename : filename to save the plot (.gv file)
- kwargs : values to the 'Graph.attr()' method
- ** sub_kwargs : kwargs to the 'Graph.attr()' method for sub-graph (the AS)
Return :
- graph : pygraphviz.Graph instance

Structure of the json file (or dict) :
{
links : {
router_name : list_of_voisins
...
},
AS : {
routers : {
router_name : dict (config router)
- if RR : should have a 'clients' entry with the list of client-routers
...
},
hosts : {
host_name : {} (unused value),
...
}
}
}

Prerequisites :
- install Graphviz to default path
- pip install pygraphviz json
"""
# if json file, load the topology
if isinstance(topo_config, str):
with open(topo_config, 'r', encoding = 'utf-8') as file:
topo_config = file.read()
topo_config = json.loads(topo_config)

g = gv.Graph('Network Topology', filename = filename)
g.attr(compound = 'true', ** kwargs)

ases = {}
as_edges = {}
ebgp_edges = []

# Set all nodes to respective AS
for as_name, as_config in topo_config['AS'].items():
ases[as_name] = list(as_config['routers'].keys()) + list(as_config.get('hosts', {}).keys())

# Associate all links to their AS
# If trans-AS links, add them to the eBGP-links
for node, voisins in topo_config['links'].items():
as_1 = [as_name for as_name, as_r in ases.items() if node in as_r][0]
for voisin in voisins:
config = {}
if isinstance(voisin, list):
voisin, config = voisin
as_2 = [as_name for as_name, as_r in ases.items() if voisin in as_r]
if len(as_2) == 0:
print("Error with {}".format(voisin))
return None
as_2 = as_2[0]

kwargs = {}
if 'igp_metric' in config: kwargs['label'] = 'IGP = {}'.format(config['igp_metric'])
edge = [node, voisin, kwargs]
if as_1 == as_2:
as_edges.setdefault(as_1, [])
as_edges[as_1].append(edge)
else:
ebgp_edges.append(edge)

# For each AS, create a sub_graph, add their respective nodes (routeurs / hosts)
# and put links intra-AS.
for as_name, as_config in topo_config['AS'].items():
with g.subgraph(name = 'cluster_' + as_name) as as_graph:
as_graph.attr(** sub_kwargs, label = as_name)
rr_graph = gv.Graph(name = as_name + '_rr')
r_graph = gv.Graph(name = as_name + 'routeurs')
h_graph = gv.Graph(name = as_name + '_hosts')

r_graph.attr(rank = 'same')
h_graph.attr(rank = 'same')

for r_name, r_config in as_config['routers'].items():
config = {}
if 'clients' in r_config:
rr_graph.node(r_name, shape = RR_SHAPE)
else:
r_graph.node(r_name, shape = R_SHAPE)
#config['shape'] = R_SHAPE if 'clients' not in r_config else RR_SHAPE
#config['rank'] = 'sink' if 'clients' not in r_config else 'max'
#as_graph.node(r_name, ** config)

as_graph.subgraph(rr_graph)
as_graph.subgraph(r_graph)
as_graph.subgraph(h_graph)

for h_name in as_config.get('hosts', {}).keys():
h_graph.node(h_name, shape = HOST_SHAPE, rank = 'min')

for start, end, config in as_edges.get(as_name, []):
as_graph.edge(start, end, ** config)

# Make links between nodes from different AS (eBGP connections)
for start, end, config in ebgp_edges:
g.edge(start, end, ** config)

return g