mirror of
https://github.com/Wojtek242/route0.git
synced 2024-11-22 07:05:25 +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 mininet.cli import CLI
|
||||||
|
|
||||||
from router import Router
|
from router import Router
|
||||||
from topology.one_node.topo import NetTopo as OneNode
|
from scenario import Scenario
|
||||||
from topology.two_nodes.topo import NetTopo as TwoNodes
|
|
||||||
from scenario import Basic, Plain, Isis
|
|
||||||
|
|
||||||
|
|
||||||
def start_daemon(node, daemon, conf_dir):
|
def start_daemon(node, daemon, conf_dir):
|
||||||
@ -25,7 +23,7 @@ def start_daemon(node, daemon, conf_dir):
|
|||||||
node.waitOutput()
|
node.waitOutput()
|
||||||
|
|
||||||
|
|
||||||
def run(topo, scenario):
|
def run(scenario):
|
||||||
"""Start a network scenario.
|
"""Start a network scenario.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -40,15 +38,14 @@ def run(topo, scenario):
|
|||||||
os.system("mn -c >/dev/null 2>&1")
|
os.system("mn -c >/dev/null 2>&1")
|
||||||
os.system("killall -9 {} > /dev/null 2>&1".format(' '.join(daemons)))
|
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()
|
net.start()
|
||||||
scenario.setup(net, topo.topo_dir)
|
|
||||||
|
|
||||||
# WARNING: FRR can get confused unless all daemons on each node are started
|
# WARNING: FRR can get confused unless all daemons on each node are started
|
||||||
# together.
|
# together.
|
||||||
for node in net.switches:
|
for node in net.switches:
|
||||||
for daemon in daemons:
|
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))
|
conf_dir = getattr(scenario, "{}_conf".format(daemon))
|
||||||
start_daemon(node, daemon, conf_dir)
|
start_daemon(node, daemon, conf_dir)
|
||||||
|
|
||||||
@ -67,35 +64,14 @@ def run(topo, scenario):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
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(
|
parser = argparse.ArgumentParser(
|
||||||
description='Launch a network scenario Mininet.')
|
description='Launch a network scenario Mininet.')
|
||||||
parser.add_argument('--topology', type=str, required=True,
|
parser.add_argument('--topology', '-t', type=str, required=True,
|
||||||
choices=topology.keys(),
|
|
||||||
help='the topology of the network')
|
help='the topology of the network')
|
||||||
parser.add_argument('--scenario', type=str, required=True,
|
parser.add_argument('--scenario', '-s', type=str, required=True,
|
||||||
choices=scenario.keys(),
|
|
||||||
help='the scenario to set up in the network')
|
help='the scenario to set up in the network')
|
||||||
ARGS = parser.parse_args()
|
ARGS = parser.parse_args()
|
||||||
|
|
||||||
if ARGS.scenario not in supported[ARGS.topology]:
|
scenario = Scenario(ARGS.topology, ARGS.scenario)
|
||||||
raise ValueError("Scenario \"{}\" is not supported for topology \"{}\""
|
|
||||||
.format(ARGS.scenario, ARGS.topology))
|
|
||||||
|
|
||||||
run(topology[ARGS.topology](),
|
run(scenario)
|
||||||
scenario[ARGS.scenario]())
|
|
||||||
|
183
scenario.py
183
scenario.py
@ -1,3 +1,4 @@
|
|||||||
|
import importlib
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
@ -8,143 +9,61 @@ class Scenario(object):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, topology, scenario):
|
||||||
self._routers = None
|
root_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
self._hosts = None
|
|
||||||
self._zebra = None
|
|
||||||
self._staticd = None
|
|
||||||
self._isisd = None
|
|
||||||
|
|
||||||
self._zebra_conf = None
|
# Check if the topology directory exists.
|
||||||
self._staticd_conf = None
|
topo_dir = os.path.join(root_dir, "topology/{}".format(topology))
|
||||||
self._isisd_conf = None
|
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):
|
# Return now if the scenario is "plain".
|
||||||
self._routers = set()
|
if scenario == "plain":
|
||||||
self._hosts = set()
|
return
|
||||||
self._zebra = set()
|
|
||||||
self._staticd = set()
|
|
||||||
self._isisd = set()
|
|
||||||
|
|
||||||
self._zebra_conf = None
|
# Check if the scenario directory exists. If it does work out which
|
||||||
self._staticd_conf = None
|
# daemons are to be used and on which nodes. If the scenario is
|
||||||
self._isisd_conf = None
|
# "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
|
@property
|
||||||
def routers(self):
|
def topo(self):
|
||||||
"""Set of nodes that are routers.
|
"""The topology of this scenario.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return self._routers
|
return self._topo
|
||||||
|
|
||||||
@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")
|
|
||||||
|
@ -7,8 +7,6 @@ class NetTopo(Topo):
|
|||||||
"""The network topology.
|
"""The network topology.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
topo_dir = os.path.dirname(os.path.realpath(__file__))
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# Add default members to class.
|
# Add default members to class.
|
||||||
super(NetTopo, self).__init__()
|
super(NetTopo, self).__init__()
|
||||||
|
@ -7,8 +7,6 @@ class NetTopo(Topo):
|
|||||||
"""The network topology.
|
"""The network topology.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
topo_dir = os.path.dirname(os.path.realpath(__file__))
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# Add default members to class.
|
# Add default members to class.
|
||||||
super(NetTopo, self).__init__()
|
super(NetTopo, self).__init__()
|
||||||
|
Loading…
Reference in New Issue
Block a user