#!/usr/bin/env python2.5
"""This script fakes as n OpenFlow switch and
load the controller with k packets per second.

(C) Copyright Stanford University
Author ykk
Date January 2010
"""
import sys
import getopt
import struct
import openflow
import time
import output
import of.msg
import of.simu
import of.network
import dpkt.ethernet

def usage():
    """Display usage
    """
    print "Usage "+sys.argv[0]+" <options> controller\n"+\
          "Options:\n"+\
          "-p/--port\n\tSpecify port number\n"+\
          "-v/--verbose\n\tPrint message exchange\n"+\
          "-r/--rate\n\tSpecify rate per switch to send packets (default=1)\n"+\
          "-d/--duration\n\tSpecify duration of load test in seconds (default=5)\n"+\
          "-s/--switch\n\tSpecify number of switches (default=1)\n"+\
          "-h/--help\n\tPrint this usage guide\n"+\
          ""
          
#Parse options and arguments
try:
    opts, args = getopt.getopt(sys.argv[1:], "hvp:s:d:r:",
                               ["help","verbose","port=",
                                "switch=","duration=","rate="])
except getopt.GetoptError:
    usage()
    sys.exit(2)

#Check there is only controller
if not (len(args) == 1):
    usage()
    sys.exit(2)
    
#Parse options
##Duration
duration = 5
##Rate
rate = 1.0
##Switch number
swno = 1
##Port to connect to
port = 6633
##Set output mode
output.set_mode("INFO")
for opt,arg in opts:
    if (opt in ("-h","--help")):
        usage()
        sys.exit(0)
    elif (opt in ("-v","--verbose")):
        output.set_mode("DBG")
    elif (opt in ("-p","--port")):
        port=int(arg)
    elif (opt in ("-s","--switch")):
        swno=int(arg)
    elif (opt in ("-d","--duration")):
        duration=int(arg)
    elif (opt in ("-r","--rate")):
        rate=float(arg)
    else:
        print "Unhandled option :"+opt
        sys.exit(2)

#Form packet
pkt = dpkt.ethernet.Ethernet()
pkt.type = dpkt.ethernet.ETH_MIN
pkt.dst = '\xFF\xFF\xFF\xFF\xFF\xFF'

#Connect to controller
ofmsg = openflow.messages()
parser = of.msg.parser(ofmsg)
ofnet = of.simu.network()
for i in range(1,swno+1):
    ofsw = of.simu.switch(ofmsg, args[0], port,
                          dpid=i,
                          parser=parser)
    ofnet.add_switch(ofsw)
    ofsw.send_hello()
    
output.info("Running "+str(swno)+" switches at "+str(rate)+\
            " packets per seconds for "+str(duration)+" s")

starttime = time.time()
running = True
interval = 1.0/(rate*swno)
ntime=time.time()+(interval/10.0)
swindex = 0
pcount = 0
rcount = 0
while running:
    ctime = time.time()
    time.sleep(max(0,min(ntime-ctime,interval/10.0)))

    if ((ctime-starttime) <= duration):
        #Send packet if time's up
        if (ctime >= ntime):
            ntime += interval
            pkt.src = struct.pack("Q",pcount)[:6]
            ofnet.switches[swindex].send_packet(1,10,pkt.pack()+'A'*3)
            pcount += 1
            swno += 1
            if (swno >= len(ofnet.switches)):
                swno=0

        #Process any received message
        (ofsw, msg) = ofnet.connections.msgreceive()
        while (msg != None):
            dic = ofmsg.peek_from_front("ofp_header", msg)
            if (dic["type"][0] == ofmsg.get_value("OFPT_FLOW_MOD")):
                output.dbg("Received flow mod")
                rcount += 1
            ofsw.receive_openflow(msg)
            (ofsw, msg) = ofnet.connections.msgreceive()
    else:
        running = False
    
output.info("Sent "+str(pcount)+" packets at rate "+\
            str(float(pcount)/float(duration))+" and received "+\
            str(rcount)+" back")
