class feedbackThread(threading.Thread):
    """
    """
    def __init__(self, requestQueue, responseQueue):
        super(feedbackThread, self).__init__()

        # Create queue attributes
        self.requestQueue = requestQueue
        self.responseQueue = responseQueue

        # Read network database
        self.db = DatabaseHandler()

        # Fill router cap files
        self.capFilesDict = self.pickCapFiles()

        # Data structure that maintains a set of current flows passing
        # through each router in the last second
        self.router_flowsets = {}
        self.updateRouterFlowSets()
            
    def run(self):
        """
        A dictionary of flow -> possible path list is read from the requestQueue.

        A dictionary indexed by flow -> allocated path is returned
        """
        queueLookupPeriod = 2 #seconds
        while True:
            try:
                requestFlowsDict = self.requestQueue.get(timeout=queueLookupPeriod) # Blocking read
            except:
                # Update flow sets for each router
                self.updateRouterFlowSets()
            else:
                #log.info("*** FEEDBACK REQUEST RECEIVED:\n")
                #log.info("     %s\n"%str(requestFlowsDict))
                self.updateRouterFlowSets()
                responsePathDict = self.dealWithRequest(requestFlowsDict)
                if responsePathDict != {}:
                    self.responseQueue.put(responsePathDict)                    
                

    def updateRouterFlowSets(self):
        for rid, capfile in self.capFilesDict.iteritems():
            lines = capfile.readlines()
            # Create new empty set
            ridSet = set()
            for line in lines:
                try:
                    # Parse ip's 
                    src_tmp = line.split(' ')[2]
                    src_ip_tmp = src_tmp.split('.')[:4]
                    src_ip = ipaddress.ip_address('.'.join(map(str, src_ip_tmp)))
                    dst_tmp = line.split(' ')[4].strip(':')
                    dst_ip_tmp = dst_tmp.split('.')[:4]
                    dport = dst_tmp.split('.')[4]
                    dst_ip = ipaddress.ip_address('.'.join(map(str, dst_ip_tmp)))
                    ridSet.update({((src_ip, 's'), (dst_ip, 'd'), dport)})
                except:
                    pass
                
            # Add set into dictionary
            self.router_flowsets[rid] = ridSet

    def dealWithRequest(self, requestFlowsDict):
        """
        """
        # Results are saved here
        responsePathDict = {}
        
        start_time = time.time()
        for f, pl in requestFlowsDict.iteritems():

            #flowsSet.update({(f.src, f.sport, f.dst, f.dport)})
            # We can't fix the source port from iperf client, so it
            # will never match. This implies that same host can't same
            # two UDP flows to the same destination host.
            flowSet = set()
            flowSet.update({((f.src.ip, 's'), (f.dst.ip, 'd'), str(f.dport))})
            
            # Set of routers containing flow
            routers_containing_flow = {self.db.getIpFromHostName(rid) for rid, rset in
                                       self.router_flowsets.iteritems() if
                                       rset.intersection(flowSet) != set()}

            #log.info("*** SEARCHING:\n")
            #log.info("     - %s\n"%f)
            #log.info("     - %s\n"%str(list(routers_containing_flow)))
            
            # Iterate path list and choose which of them is the one in
            # which the flow is allocated
            pathSetList = [(p, set(p)) for p in pl]

            # Retrieve path that matches
            chosen_path = [(p, pset) for (p, pset) in pathSetList if pset == routers_containing_flow]
            if len(chosen_path) == 1:
                responsePathDict[f] = chosen_path[0][0]
                
            elif len(chosen_path) == 0:
                pass
            
            else:
                log.info("*** FEEDBACK THREAD ERROR\n")

        return responsePathDict

    
    def pickCapFiles(self):
        """
        Returns a dictionary indexed by router id -> corresponding .cap file
        """
        return {rid: open(dconf.CAP_Path+rid+'.cap', 'r') for rid in self.db.routers_to_ip.keys()}
Esempio n. 2
0
class TrafficGenerator(Base):
    """Object that creates a Traffic Generator in the network.
    """

    def __init__(self, *args, **kwargs):
        super(TrafficGenerator, self).__init__(*args, **kwargs)

        self.scheduler = sched.scheduler(time.time, time.sleep)
        self.db = DatabaseHandler()
        self.thread_handlers = []

        # IP of the Load Balancer Controller host.
        try:
            self._lbc_ip = ipaddress.ip_interface(self.db.getIpFromHostName(dconf.LBC_Hostname)).ip.compressed
        except:
            log.info("WARNING: Load balancer controller could not be found in the network\n")
            self._lbc_ip = None

    def _signal_handler(self, signal, frame):
        """
        Terminates trafficgenerator thread gracefully.
        """
        log.info("Signal caught... shuting down!\n")

        # collect all open _createFlow threads
        for t in self.thread_handlers:
            # t.join()
            log.info("_createFlow thread terminated\n")

        # exit
        sys.exit(0)

    def informLBController(self, flow):
        """Part of the code that deals with the JSON interface to inform to
        LBController a new flow created in the network.
        """
        url = "http://%s:%s/newflowstarted" % (self._lbc_ip, dconf.LBC_JsonPort)
        log.info("\t Informing LBController\n")
        log.info("\t   * Flow: %s\n" % self.toLogFlowNames(flow))
        log.info("\t   * Url: %s\n" % url)
        try:
            requests.post(url, json=flow.toJSON())
        except Exception:
            log.info("ERROR: LBC could not be informed!\n")
            log.info("LOG: Exception in user code:\n")
            log.info("-" * 60 + "\n")
            log.info(traceback.print_exc())
            log.info("-" * 60 + "\n")

    def toLogFlowNames(self, flow):
        a = "(%s -> %s): %s, t_o: %s, duration: %s"
        return a % (
            self.db.getNameFromIP(flow.src.compressed),
            self.db.getNameFromIP(flow.dst.compressed),
            flow.setSizeToStr(flow.size),
            flow.setTimeToStr(flow.start_time),
            flow.setTimeToStr(flow.duration),
        )

    def createFlow(self, flow):
        """Calls _createFlow in a different Thread (for efficiency)
        """
        # Start thread that will send the Flask request
        t = Thread(target=self._createFlow, name="_createFlow", args=(flow,)).start()
        # Append thread handler to list
        self.thread_handlers.append(t)

    def _createFlow(self, flow):
        """Creates the corresponding iperf command to actually install the
        given flow in the network.  This function has to call
        self.informLBController!
        """
        # Sleep after it is your time to start
        time.sleep(flow["start_time"])

        # Call to informLBController if it is active
        if self._lbc_ip:
            self.informLBController(flow)
            # time.sleep(0.2)

        # Create new flow with hosts ip's instead of interfaces
        # Iperf only understands ip's
        flow2 = Flow(
            src=flow["src"].ip.compressed,
            dst=flow["dst"].ip.compressed,
            sport=flow["sport"],
            dport=flow["dport"],
            size=flow["size"],
            start_time=flow["start_time"],
            duration=flow["duration"],
        )

        url = "http://%s:%s/startflow" % (flow2["src"], dconf.Hosts_JsonPort)

        t = time.strftime("%H:%M:%S", time.gmtime())
        log.info("%s - Starting Flow\n" % t)
        log.info("\t Sending request to host %s\n" % str(flow["src"]))
        log.info("\t   * Flow: %s\n" % self.toLogFlowNames(flow))
        log.info("\t   * Url: %s\n" % url)

        # Send request to host to start new iperf client session
        try:
            requests.post(url, json=flow2.toJSON())
        except Exception:
            log.info("ERROR: Request could not be sent to Host!\n")
            log.info("LOG: Exception in user code:\n")
            log.info("-" * 60 + "\n")
            log.info(traceback.print_exc())
            log.info("-" * 60 + "\n")

    def stopFlow(self, flow):
        """Instructs host to stop iperf client session (flow).

        """
        flow2 = Flow(
            src=flow["src"].ip.compressed,
            dst=flow["dst"].ip.compressed,
            sport=flow["sport"],
            dport=flow["dport"],
            size=flow["size"],
            start_time=flow["start_time"],
            duration=flow["duration"],
        )

        url = "http://%s:%s/stopflow" % (flow2["src"], dconf.Hosts_JsonPort)

        t = time.strftime("%H:%M:%S", time.gmtime())
        log.info("%s - Stopping Flow\n" % t)
        log.info("\t Sending request to host to stop flow %s\n" % str(flow["src"]))
        log.info("\t   * Flow: %s\n" % self.toLogFlowNames(flow))
        log.info("\t   * Url: %s\n" % url)

        # Send request to host to start new iperf client session
        try:
            requests.post(url, json=flow2.toJSON())
        except Exception:
            log.info("ERROR: Stop flow request could not be sent to Host!\n")

    def createRandomFlow(self):
        """Creates a random flow in the network
        """
        pass

    def scheduleRandomFlows(self, ex_time=60, max_size="40M"):
        """Creates a random schedule of random flows in the network. This will
        be useful later to evaluate the performance of the
        LBController.
        """
        pass

    def scheduleFileFlows(self, flowfile):
        """Schedules the flows specified in the flowfile
        """
        f = open(flowfile, "r")
        flows = f.readlines()
        if flows:
            for flowline in flows:
                flowline = flowline.replace(" ", "").replace("\n", "")
                if flowline != "" and flowline[0] != "#":
                    try:
                        [s, d, sp, dp, size, s_t, dur] = flowline.strip("\n").split(",")
                        # Get hosts IPs
                        src_iface = self.db.getIpFromHostName(s)
                        dst_iface = self.db.getIpFromHostName(d)

                    except Exception:
                        log.info("EP, SOMETHING HAPPENS HERE\n")
                        src_iface = None
                        dst_iface = None

                    if src_iface != None and dst_iface != None:
                        flow = Flow(
                            src=src_iface, dst=dst_iface, sport=sp, dport=dp, size=size, start_time=s_t, duration=dur
                        )
                        # Schedule flow creation
                        self.scheduler.enter(0, 1, self.createFlow, ([flow]))
                    else:
                        log.info("ERROR! Hosts %s and/or %s do not exist in the network!\n" % (s, d))

            # Make the scheduler run after file has been parsed
            self.scheduler.run()
        else:
            log.info("\t No flows to schedule in file\n")