def runstuff():
    NUM_SWITCHES = 20 # total number of switches in the complete graph of switch-port connectivity
    NUM_CLIENTS = 2 # number of clients on every switch. large numbers will decrease the size of sccs, because 
                    # forwarding to clients is always safe; not part of a loop.
    RULES_PER_ROUND = 350 # number of rule installs to do between gathering time statistics.
    MAX_TOTAL_RULES_INSTALLED = 450 # number of total rules to maintain in the network model, so 
                                    # that as more than these come in, we evict the oldest ones.
    TOTAL_RULE_INSTALLATIONS_BEFORE_HALT = 350 # halt after this many total rule installations.
    SHOW_TRACE = False # should we show the trace when loops are detected? This can get hectic.
    MIN_IN = 1 # minininum number of in_ports to be used in random rules.
    MAX_IN = 3 # etc.
    MIN_OUT = 1
    MAX_OUT = 2



    # the network that we'kll use to generate random valid rules
    net = TestNetwork1(NUM_SWITCHES, NUM_CLIENTS, minswitch=5555, minport=100001)

    # our model
    n = NetworkFlowruleModel() 

    # track the flow install rate in this
    timestats = StatsBuddy()
    total_installed = 0

    # track the rules in place in our NetworkFlowruleModel so we can evict them
    q = Queue.Queue() 

    while True:
        #start a timer
        t1 = time.time()

        # do some rule installations
        for r in xrange(RULES_PER_ROUND):
            if q.qsize() > MAX_TOTAL_RULES_INSTALLED:
                droprule = q.get() # get the number of the rule that's over the limit
                n.drop_flow_rule(droprule) # kill the rule
            rule = net.get_random_rule(MIN_IN, MAX_IN, MIN_OUT, MAX_OUT)
            (rnum, trace) = n.install_flow_rule(rule)
            if SHOW_TRACE and trace: NetworkFlowruleModel.explain(trace)
            q.put(rnum)
            total_installed += 1

        t2 = time.time()

        # NOTE: linux might report this in ms instead of seconds, I think, 
        # so this might give weird results
        timestats.add(RULES_PER_ROUND/(t2-t1)) 

        # how we eventually halt
        if total_installed >= TOTAL_RULE_INSTALLATIONS_BEFORE_HALT: break

        

    print timestats, 'are stats for the number of rule installs and evictions per second.'
    print n.sccsizestats, 'are stats for the average scc size of installed rules.'

    return
def RunTest(num_switches, num_clients, steady_state_avg_rules_per_switch, 
                  total_rule_installations_before_halt, 
                  min_in, max_in, min_out, max_out, 
                  min_match_bits=0, max_match_bits=0, min_rewrite_bits=0, max_rewrite_bits=0,
                  show_trace = False, 
                  port_density = 1.0,
                  rewrite_rule_probability = 1.0):
    """
    num_switches: 
        total number of switches in the complete graph of switch-port connectivity
    num_clients: 
        number of clients on every switch. large numbers will decrease the size of sccs, 
        because forwarding to clients is always safe; not part of a loop.
    steady_state_avg_rules_per_switch: 
        The average number of rules to keep in the network per switch before rules are evicted
        The network will gradually be built up to have steady_state_avg_rules_per_switch * num_switches
        and then exist with that steady state number of rules until the total number of rule 
        installations occurs
    total_rule_installations_before_halt: 
        Halt after this many total rule installations. (Will be rounded to a multiple of 20, 
        to make progress reporting cleaner)

    min_in:  
        Minininum number of in_ports to be used in random rules
    max_in: etc.
    min_out: etc.
    max_out: etc.

    min_match_bits: 
        The minimum number of bits to fix as non-'x' in random rewrite rule matchs
    max_match_bits: 
        Max of above
    min_rewrite_bits: 
        The minimum number of bits to explicitly fix randomly as one of 0/1 in output packets
    max_rewrite_bits: 
        Max of above

    show_trace: 
        Should we show the trace when loops are detected? This can get hectic and start spinning the terminal.
    
    port_density:
        Default of 1.0, indicating the complete graph as the underlying network topology
        For less than that, the network is a ring with _this_ proportion of the pairs of 
        switches connected by a pair of 1-way ports.

    rewrite_rule_probability:
        The proportion of the random rules that should be random rewrite rules rather than random forwarding rules
    """

    # some basic info for our run
    steady_state_rules_in_network = num_switches * steady_state_avg_rules_per_switch
    rules_per_round = total_rule_installations_before_halt / 20
    total_rule_installations_before_halt = rules_per_round * 20

    # eject if params are silly
    if total_rule_installations_before_halt <= steady_state_rules_in_network:
        raise Exception('The given parameters will not permit the network to reach a steady state, so no statistics would be collected. Try decreasing "steady_state_avg_rules_per_switch".')

    # the network that we'll use to generate random valid rules
    net = TestNetwork(num_switches, num_clients, port_density)

    # our model
    n = NetworkFlowruleModel() 

    # annotate the parameters
    print 'Parameters for this run are:', (num_switches, num_clients, steady_state_avg_rules_per_switch, 
                                          total_rule_installations_before_halt, 
                                          min_in, max_in, min_out, max_out, 
                                          min_match_bits, max_match_bits, min_rewrite_bits, max_rewrite_bits,
                                          show_trace, port_density, rewrite_rule_probability)
    print 'Steady state will have {} rules installed.'.format(steady_state_rules_in_network)

    #start a timer
    test_start_time = time.time()
    steady_state_start_time = None # we'll set this when we reach maximum flow capacity for the network
    total_installed = 0


    # track the rules in place in our NetworkFlowruleModel so we can evict them
    q = Queue.Queue()

    while total_installed < total_rule_installations_before_halt:
        # do some rule installations
        for r in xrange(rules_per_round):
            # drop rules so we don't exceed steady_state_rules_in_network
            if q.qsize() > steady_state_rules_in_network:
                droprule = q.get() # get the number of the rule that's over the limit
                n.drop_flow_rule(droprule) # kill the rule
                if steady_state_start_time is None:
                    n.collect_stats(True)
                    steady_state_start_time = time.time()

            ######################################
            if rand.random() <= rewrite_rule_probability:
                rule = net.get_random_rewrite_rule(min_in, max_in, min_out, max_out, min_match_bits, max_match_bits, min_rewrite_bits, max_rewrite_bits)
            else:
                rule = net.get_random_rule(min_in, max_in, min_out, max_out)
            ######################################

            # put the random flow into our model
            (rnum, trace) = n.install_flow_rule(rule)
            # show detected loop details if desired
            if show_trace and trace: NetworkFlowruleModel.explain(trace)
            q.put(rnum)
            total_installed += 1

        amount_done = 1.0 * total_installed / total_rule_installations_before_halt
        if steady_state_start_time is not None:
            time_in_steady_state = time.time() - steady_state_start_time
            amount_of_steady_state_done = 1.0 * (total_installed - steady_state_rules_in_network) / (total_rule_installations_before_halt - steady_state_rules_in_network)
            expected_steady_state_time_total = time_in_steady_state / amount_of_steady_state_done
            expected_remaining_time = expected_steady_state_time_total * (1.0 - amount_of_steady_state_done)
            print '{:.0%} done. Expected {:.4g}s remaining'.format(amount_done, expected_remaining_time)
        else:
            print '{:.0%} done. Steady state not yet entered...'.format(amount_done)


    test_end_time = time.time()
    total_test_time = test_end_time - test_start_time
    steady_state_total_time = test_end_time - steady_state_start_time

    # show heaps of info about the run
    print
    print 'Overall, {:.5g} rules were installed per second on average.'.format(1.0 * total_rule_installations_before_halt / total_test_time)
    print 'While in steady state, {:.5g} rules were installed per second on average.'.format(1.0 * (total_rule_installations_before_halt - steady_state_rules_in_network) / steady_state_total_time)
    print n.scc_size_stats, 'are the stats for the average SCC size of installed rules.'
    print n.edges_stats, 'are the stats for the average number of graph edges added per installed rule.'
    print 'Loop detection was called for {:.4%} of rule installs'.format(1.0 * n.loop_detection_calls / total_rule_installations_before_halt)
    print 'Loops were detected for {:.4%} of rule installs'.format(1.0 * n.loops_detected / total_rule_installations_before_halt)
    print 'Loops were present for {:.4%} of calls to loop detection'.format(1.0 * n.loops_detected / n.loop_detection_calls)
    
    print 'SCC sizes are distributed like this:', n.scc_buckets.graph()
 def get_network_model(self):
     n = NetworkFlowruleModel()
     for rule in self.rules():
         (rnum, trace) = n.install_flow_rule(rule)
         if trace: print NetworkFlowruleModel.explain(trace)
     return n