Get the simulation substrate started.  The key things are to set up
    a series of queues that connect nodes together and get the link emulation
    objects started (all inside the NodeExecutor class).  The NodePlumbing
    named tuples hold together threads for each node, the emulation
    substrate (NodeExecutors), and the ingress queue that each node receives
    packets from.

def run_simulation(topo, **kwargs):
    '''
    Get the simulation substrate started.  The key things are to set up
    a series of queues that connect nodes together and get the link emulation
    objects started (all inside the NodeExecutor class).  The NodePlumbing
    named tuples hold together threads for each node, the emulation
    substrate (NodeExecutors), and the ingress queue that each node receives
    packets from.
    '''
    log_debug("Threads at startup:")
    for t in threading.enumerate():
        log_debug("\tthread at startup {}".format(t.name))

    with yellow():
        log_info("Starting up switchyard simulation substrate.")
    glue = SyssGlue(topo, **kwargs)
    cli = Cli(glue, topo)
    try:
        cli.cmdloop()
    except KeyboardInterrupt:
        print("Received SIGINT --- shutting down.")
        cli.stop()