mirror of
https://github.com/Wojtek242/route0.git
synced 2024-12-22 15:44:39 +01:00
Automate discovery of topologies and scenarios
This commit is contained in:
parent
8bbd3d07ad
commit
d8b729caac
40
route-0.py
40
route-0.py
@ -7,9 +7,7 @@ from mininet.net import Mininet
|
||||
from mininet.cli import CLI
|
||||
|
||||
from router import Router
|
||||
from topology.one_node.topo import NetTopo as OneNode
|
||||
from topology.two_nodes.topo import NetTopo as TwoNodes
|
||||
from scenario import Basic, Plain, Isis
|
||||
from scenario import Scenario
|
||||
|
||||
|
||||
def start_daemon(node, daemon, conf_dir):
|
||||
@ -25,7 +23,7 @@ def start_daemon(node, daemon, conf_dir):
|
||||
node.waitOutput()
|
||||
|
||||
|
||||
def run(topo, scenario):
|
||||
def run(scenario):
|
||||
"""Start a network scenario.
|
||||
"""
|
||||
|
||||
@ -40,15 +38,14 @@ def run(topo, scenario):
|
||||
os.system("mn -c >/dev/null 2>&1")
|
||||
os.system("killall -9 {} > /dev/null 2>&1".format(' '.join(daemons)))
|
||||
|
||||
net = Mininet(topo=topo, switch=Router)
|
||||
net = Mininet(topo=scenario.topo(), switch=Router)
|
||||
net.start()
|
||||
scenario.setup(net, topo.topo_dir)
|
||||
|
||||
# WARNING: FRR can get confused unless all daemons on each node are started
|
||||
# together.
|
||||
for node in net.switches:
|
||||
for daemon in daemons:
|
||||
if node in getattr(scenario, daemon):
|
||||
if node.name in getattr(scenario, daemon, set()):
|
||||
conf_dir = getattr(scenario, "{}_conf".format(daemon))
|
||||
start_daemon(node, daemon, conf_dir)
|
||||
|
||||
@ -67,35 +64,14 @@ def run(topo, scenario):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
topology = {
|
||||
"one_node": OneNode,
|
||||
"two_nodes": TwoNodes,
|
||||
}
|
||||
|
||||
scenario = {
|
||||
"plain": Plain,
|
||||
"basic": Basic,
|
||||
"isis": Isis,
|
||||
}
|
||||
|
||||
supported = {
|
||||
"one_node": set(["plain", "basic"]),
|
||||
"two_nodes": set(["plain", "basic", "isis"]),
|
||||
}
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Launch a network scenario Mininet.')
|
||||
parser.add_argument('--topology', type=str, required=True,
|
||||
choices=topology.keys(),
|
||||
parser.add_argument('--topology', '-t', type=str, required=True,
|
||||
help='the topology of the network')
|
||||
parser.add_argument('--scenario', type=str, required=True,
|
||||
choices=scenario.keys(),
|
||||
parser.add_argument('--scenario', '-s', type=str, required=True,
|
||||
help='the scenario to set up in the network')
|
||||
ARGS = parser.parse_args()
|
||||
|
||||
if ARGS.scenario not in supported[ARGS.topology]:
|
||||
raise ValueError("Scenario \"{}\" is not supported for topology \"{}\""
|
||||
.format(ARGS.scenario, ARGS.topology))
|
||||
scenario = Scenario(ARGS.topology, ARGS.scenario)
|
||||
|
||||
run(topology[ARGS.topology](),
|
||||
scenario[ARGS.scenario]())
|
||||
run(scenario)
|
||||
|
183
scenario.py
183
scenario.py
@ -1,3 +1,4 @@
|
||||
import importlib
|
||||
import os
|
||||
|
||||
|
||||
@ -8,143 +9,61 @@ class Scenario(object):
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._routers = None
|
||||
self._hosts = None
|
||||
self._zebra = None
|
||||
self._staticd = None
|
||||
self._isisd = None
|
||||
def __init__(self, topology, scenario):
|
||||
root_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
self._zebra_conf = None
|
||||
self._staticd_conf = None
|
||||
self._isisd_conf = None
|
||||
# Check if the topology directory exists.
|
||||
topo_dir = os.path.join(root_dir, "topology/{}".format(topology))
|
||||
if not os.path.exists(topo_dir):
|
||||
raise KeyError("Topology \"{}\" not supported".format(topology))
|
||||
|
||||
self._reset()
|
||||
# Extract the topology class.
|
||||
self._topo = (importlib
|
||||
.import_module('topology.{}.topo'.format(topology))
|
||||
.NetTopo)
|
||||
|
||||
def _reset(self):
|
||||
self._routers = set()
|
||||
self._hosts = set()
|
||||
self._zebra = set()
|
||||
self._staticd = set()
|
||||
self._isisd = set()
|
||||
# Return now if the scenario is "plain".
|
||||
if scenario == "plain":
|
||||
return
|
||||
|
||||
self._zebra_conf = None
|
||||
self._staticd_conf = None
|
||||
self._isisd_conf = None
|
||||
# Check if the scenario directory exists. If it does work out which
|
||||
# daemons are to be used and on which nodes. If the scenario is
|
||||
# "basic" skip this step.
|
||||
if scenario != "basic":
|
||||
scenario_dir = os.path.join(topo_dir,
|
||||
"scenario/{}".format(scenario))
|
||||
if not os.path.exists(scenario_dir):
|
||||
raise KeyError("Scenario \"{}\" not supported"
|
||||
.format(scenario))
|
||||
|
||||
# Work out which daemons to start and their config directories.
|
||||
for daemon in os.listdir(scenario_dir):
|
||||
self._get_daemon_nodes(scenario_dir, daemon)
|
||||
|
||||
# Zebra and staticd daemons are special. If they don't have an
|
||||
# override in the scenario directory, take the defaults from the
|
||||
# topology directory.
|
||||
for daemon in ["zebra", "staticd"]:
|
||||
if not hasattr(self, daemon):
|
||||
self._get_daemon_nodes(topo_dir, daemon)
|
||||
|
||||
def _get_daemon_nodes(self, parent_dir, daemon):
|
||||
# Each daemon entry should be a directory.
|
||||
daemon_dir = os.path.join(parent_dir, daemon)
|
||||
if os.path.exists(daemon_dir) and os.path.isdir(daemon_dir):
|
||||
setattr(self, daemon, set())
|
||||
setattr(self, "{}_conf".format(daemon), daemon_dir)
|
||||
|
||||
# Each node running this daemons must have a conf file in the
|
||||
# daemon directory called <node_name>.conf.
|
||||
for conf in os.listdir(daemon_dir):
|
||||
# Make sure we're only dealing with conf files.
|
||||
if conf.endswith(".conf"):
|
||||
getattr(self, daemon).add(conf.split('.')[0])
|
||||
|
||||
@property
|
||||
def routers(self):
|
||||
"""Set of nodes that are routers.
|
||||
def topo(self):
|
||||
"""The topology of this scenario.
|
||||
|
||||
"""
|
||||
return self._routers
|
||||
|
||||
@property
|
||||
def hosts(self):
|
||||
"""Set of nodes that are hosts.
|
||||
|
||||
"""
|
||||
return self._hosts
|
||||
|
||||
@property
|
||||
def zebra(self):
|
||||
"""Set of nodes that should run zebra.
|
||||
|
||||
"""
|
||||
return self._zebra
|
||||
|
||||
@property
|
||||
def staticd(self):
|
||||
"""Set of nodes that should run staticd.
|
||||
|
||||
"""
|
||||
return self._staticd
|
||||
|
||||
@property
|
||||
def isisd(self):
|
||||
"""Set of nodes that should run isisd.
|
||||
|
||||
"""
|
||||
return self._isisd
|
||||
|
||||
@property
|
||||
def zebra_conf(self):
|
||||
"""Directory with zebra config files.
|
||||
|
||||
"""
|
||||
return self._zebra_conf
|
||||
|
||||
@property
|
||||
def staticd_conf(self):
|
||||
"""Directory with staticd config files.
|
||||
|
||||
"""
|
||||
return self._staticd_conf
|
||||
|
||||
@property
|
||||
def isisd_conf(self):
|
||||
"""Directory with isisd config files.
|
||||
|
||||
"""
|
||||
return self._isisd_conf
|
||||
|
||||
def _routers_and_hosts(self, net):
|
||||
"""Separate nodes into routers and hosts based on their names.
|
||||
|
||||
"""
|
||||
self._routers = set()
|
||||
self._hosts = set()
|
||||
|
||||
for node in net.switches:
|
||||
if node.name.startswith('R'):
|
||||
self._routers.add(node)
|
||||
else:
|
||||
self._hosts.add(node)
|
||||
|
||||
def setup(self, net, _topo_dir):
|
||||
"""Setup the scenario.
|
||||
|
||||
"""
|
||||
self._reset()
|
||||
self._routers_and_hosts(net)
|
||||
|
||||
|
||||
class Plain(Scenario):
|
||||
"""In a Plain scenario no daemons are run.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
class Basic(Scenario):
|
||||
"""In a Basic scenario, all nodes run zebra, and all hosts run staticd to
|
||||
setup a default route.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
Scenario.__init__(self)
|
||||
|
||||
def setup(self, net, topo_dir):
|
||||
super(Basic, self).setup(net, topo_dir)
|
||||
|
||||
self._zebra = self._routers.union(self._hosts)
|
||||
self._staticd = self._hosts
|
||||
|
||||
self._zebra_conf = os.path.join(topo_dir, "zebra")
|
||||
self._staticd_conf = os.path.join(topo_dir, "staticd")
|
||||
|
||||
|
||||
class Isis(Basic):
|
||||
"""Run IS-IS on all routers.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
Basic.__init__(self)
|
||||
|
||||
def setup(self, net, topo_dir):
|
||||
super(Isis, self).setup(net, topo_dir)
|
||||
|
||||
self._isisd = self._routers
|
||||
|
||||
self._isisd_conf = os.path.join(topo_dir, "scenario/isis/isisd")
|
||||
return self._topo
|
||||
|
@ -7,8 +7,6 @@ class NetTopo(Topo):
|
||||
"""The network topology.
|
||||
"""
|
||||
|
||||
topo_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
def __init__(self):
|
||||
# Add default members to class.
|
||||
super(NetTopo, self).__init__()
|
||||
|
@ -7,8 +7,6 @@ class NetTopo(Topo):
|
||||
"""The network topology.
|
||||
"""
|
||||
|
||||
topo_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
def __init__(self):
|
||||
# Add default members to class.
|
||||
super(NetTopo, self).__init__()
|
||||
|
Loading…
Reference in New Issue
Block a user