
from nox.lib.util import *
from se_dict import SeDict
from symbolic.instrumentation import whichBranch
python_ord = ord

def se_ord(obj):
    __foo = hasattr(obj, """__ord__""")
    if __foo:
        whichBranch(True)
        return obj.__ord__()
    else:
        whichBranch(False)
        return python_ord(obj)
ord = se_ord
from nox.lib.core import *
from nox.lib.util import *
from nox.lib.packet.ethernet import ethernet
from nox.lib.packet.packet_utils import mac_to_str, mac_to_int
from twisted.python import log
import logging
from time import time
from socket import htons
from struct import unpack
from nox.lib.core import *
from collections import defaultdict
import nox.lib.openflow as openflow
from nox.lib.packet.packet_utils import mac_to_str
from twisted.python import log
from nox.lib.openflow import OFPP_NONE
from nox.lib.packet.ethernet import ethernet
logger = logging.getLogger("""nox.coreapps.examples.eate""")
inst = None
CACHE_TIMEOUT = 5
WAIT_FOR_RESULTS = 10
WAIT_BEFOR_NEXT_MEASURE = 2
BANDWIDTH = 1
ETHERNET_MTU = 1500
MAX_PACKETS_PER_SECOND = ((BANDWIDTH * 1000000) / (ETHERNET_MTU * 8))
REAL_TRESHOLD = ((0.8 * MAX_PACKETS_PER_SECOND) * (WAIT_BEFOR_NEXT_MEASURE + WAIT_FOR_RESULTS))
MEDIUM_THRESHOLD = 500000000
HIGH_THRESHOLD = 1000000000
DEFAULT_POLL_TABLE_PERIOD = 5
DEFAULT_POLL_PORT_PERIOD = 5
DEFAULT_POLL_AGGREGATE_PERIOD = 5

def deployRules():
    inst.switchPath["""00:00:00:00:00:0B"""] = SeDict({})
    inst.switchPath["""00:00:00:00:00:0D"""] = SeDict({})
    inst.switchPath["""00:00:00:00:00:0E"""] = SeDict({})
    inst.alwaysOnPath["""00:00:00:00:00:0B"""] = SeDict({})
    inst.alwaysOnPath["""00:00:00:00:00:0D"""] = SeDict({})
    inst.alwaysOnPath["""00:00:00:00:00:0E"""] = SeDict({})
    inst.alwaysOnPath["""00:00:00:00:00:0B"""]["""00:00:00:00:00:0D"""] = [1, 2]
    inst.alwaysOnPath["""00:00:00:00:00:0B"""]["""00:00:00:00:00:0E"""] = [1, 2]
    inst.alwaysOnPath["""00:00:00:00:00:0D"""]["""00:00:00:00:00:0B"""] = [2, 1]
    inst.alwaysOnPath["""00:00:00:00:00:0D"""]["""00:00:00:00:00:0E"""] = [2]
    inst.alwaysOnPath["""00:00:00:00:00:0E"""]["""00:00:00:00:00:0B"""] = [2, 1]
    inst.alwaysOnPath["""00:00:00:00:00:0E"""]["""00:00:00:00:00:0D"""] = [2, 1]
    inst.onDemandPath["""00:00:00:00:00:0B"""] = SeDict({})
    inst.onDemandPath["""00:00:00:00:00:0D"""] = SeDict({})
    inst.onDemandPath["""00:00:00:00:00:0E"""] = SeDict({})
    inst.onDemandPath["""00:00:00:00:00:0B"""]["""00:00:00:00:00:0D"""] = [1, 3, 2]
    inst.onDemandPath["""00:00:00:00:00:0B"""]["""00:00:00:00:00:0E"""] = [1, 3, 2]
    inst.onDemandPath["""00:00:00:00:00:0D"""]["""00:00:00:00:00:0B"""] = [2, 3, 1]
    inst.onDemandPath["""00:00:00:00:00:0D"""]["""00:00:00:00:00:0E"""] = [2]
    inst.onDemandPath["""00:00:00:00:00:0E"""]["""00:00:00:00:00:0B"""] = [2, 3, 1]
    inst.onDemandPath["""00:00:00:00:00:0E"""]["""00:00:00:00:00:0D"""] = [2]
    inst.switches = [1, 2, 3]
    inst.hosts = ["""00:00:00:00:00:0B""", """00:00:00:00:00:0D""", """00:00:00:00:00:0E"""]
    inst.onDemandMap[1] = SeDict({})
    inst.onDemandMap[1]["""00:00:00:00:00:0B"""] = 3
    inst.onDemandMap[1]["""00:00:00:00:00:0D"""] = 1
    inst.onDemandMap[1]["""00:00:00:00:00:0E"""] = 1
    inst.onDemandMap[2] = SeDict({})
    inst.onDemandMap[2]["""00:00:00:00:00:0B"""] = 2
    inst.onDemandMap[2]["""00:00:00:00:00:0D"""] = 3
    inst.onDemandMap[2]["""00:00:00:00:00:0E"""] = 4
    inst.onDemandMap[3] = SeDict({})
    inst.onDemandMap[3]["""00:00:00:00:00:0B"""] = 1
    inst.onDemandMap[3]["""00:00:00:00:00:0D"""] = 2
    inst.onDemandMap[3]["""00:00:00:00:00:0E"""] = 2
    inst.alwaysOnMap[1] = SeDict({})
    inst.alwaysOnMap[1]["""00:00:00:00:00:0B"""] = 3
    inst.alwaysOnMap[1]["""00:00:00:00:00:0D"""] = 2
    inst.alwaysOnMap[1]["""00:00:00:00:00:0E"""] = 2
    inst.alwaysOnMap[2] = SeDict({})
    inst.alwaysOnMap[2]["""00:00:00:00:00:0B"""] = 1
    inst.alwaysOnMap[2]["""00:00:00:00:00:0D"""] = 3
    inst.alwaysOnMap[2]["""00:00:00:00:00:0E"""] = 4
    inst.alwaysOnMap[3] = SeDict({})
    inst.alwaysOnMap[3]["""00:00:00:00:00:0B"""] = 1
    inst.alwaysOnMap[3]["""00:00:00:00:00:0D"""] = 2
    inst.alwaysOnMap[3]["""00:00:00:00:00:0E"""] = 2
    for s in inst.switches:
        inst.switchPortMap[s] = SeDict({})
        for dst in inst.hosts:
            inst.switchPortMap[s][dst] = inst.alwaysOnMap[s][dst]
    for src in inst.hosts:
        for dst in inst.hosts:
            if (src == dst):
                whichBranch(True)
                continue
            else:
                whichBranch(False)
            inst.switchPath[src][dst] = inst.alwaysOnPath[src][dst]
    logger.info("""Rules set""")

def choose_path(srcip, dstip):
    choice = """alwaysOn"""
    if (inst.energyLtoR == """alwaysOn"""):
        whichBranch(True)
        if (inst.energyRtoL == """alwaysOn"""):
            whichBranch(True)
            logger.info(("""alwaysOn only %s""" % dstip))
        else:
            whichBranch(False)
            if ((dstip % 2) == 1):
                whichBranch(True)
                logger.info(("""alwaysOn or onDemand -> alwaysOn, modulo 2 choice 1 %s""" % dstip))
            else:
                whichBranch(False)
                if ((dstip % 2) == 0):
                    whichBranch(True)
                    whichBranch(True)
                    logger.info(("""alwaysOn or onDemand -> onDemand, modulo 2 choice 0 %s""" % dstip))
                    choice = """onDemand"""
                else:
                    whichBranch(False)
                    whichBranch(False)
    else:
        whichBranch(False)
        if ((dstip % 2) == 1):
            whichBranch(True)
            whichBranch(True)
            logger.info(("""alwaysOn or onDemand -> alwaysOn, modulo 2 choice 1 %s""" % dstip))
        else:
            whichBranch(False)
            whichBranch(False)
            if ((dstip % 2) == 0):
                whichBranch(True)
                whichBranch(True)
                logger.info(("""alwaysOn or onDemand -> onDemand, modulo 2 choice 0 %s""" % dstip))
                choice = """onDemand"""
            else:
                whichBranch(False)
                whichBranch(False)
    return choice

def forward_l2_packet(dpid, inport, packet, buf, bufid):
    logger.info(("""%d forward_l2_packet for type %s""" % (dpid, str(packet.type))))
    if (packet.type == ethernet.ARP_TYPE):
        whichBranch(True)
        dstip = packet.next.protodst
        choice = """alwaysOn"""
    else:
        whichBranch(False)
        if (packet.type == ethernet.IP_TYPE):
            whichBranch(True)
            dstip = packet.next.dstip
            srcip = packet.next.srcip
            choice = choose_path(srcip, dstip)
        else:
            whichBranch(False)
            logger.info(("""Ignoring non ARP packet %s""" % str(packet.type)))
            return
    dstaddr = getMac(dstip)
    logger.info(("""forward_l2_packet for dstip %s dstaddr %s""" % (dstip, dstaddr)))
    __se_tmp_2 = ord(dstaddr[0])
    if (not (__se_tmp_2 & 1)):
        whichBranch(True)
        __se_tmp_1 = inst.switchPortMap[dpid].has_key(dstaddr)
        if __se_tmp_1:
            whichBranch(True)
            if (choice == """onDemand"""):
                whichBranch(True)
                prt = inst.onDemandMap[dpid][dstaddr]
            else:
                whichBranch(False)
                prt = inst.alwaysOnMap[dpid][dstaddr]
            logger.info("""Forwarding Table changed""")
            flow = extract_flow(packet)
            flow[core.IN_PORT] = inport
            actions = [[openflow.OFPAT_OUTPUT, [0, prt]]]
            inst.install_datapath_flow(dpid, flow, CACHE_TIMEOUT, openflow.OFP_FLOW_PERMANENT, actions, bufid, openflow.OFP_DEFAULT_PRIORITY, inport, buf)
        else:
            whichBranch(False)
    else:
        whichBranch(False)

def install_path(dpid, incoming_port, packet, buf, bufid):
    logger.info(("""%d install_path for type %s""" % (dpid, str(packet.type))))
    if (packet.type == ethernet.ARP_TYPE):
        whichBranch(True)
        logger.info(("""Ignoring ARP packet %s""" % str(packet.type)))
        return
    else:
        whichBranch(False)
        if (packet.type == ethernet.IP_TYPE):
            whichBranch(True)
            dstip = packet.next.dstip
            srcip = packet.next.srcip
        else:
            whichBranch(False)
            logger.info(("""Ignoring non IP packet %s""" % str(packet.type)))
            return
    dstaddr = getMac(dstip)
    srcaddr = getMac(srcip)
    logger.info(("""install_path for dstip %s dstaddr %s srcip %s srcaddr %s""" % (dstip, dstaddr, srcip, srcaddr)))
    __se_tmp_5 = ord(dstaddr[0])
    if (not (__se_tmp_5 & 1)):
        whichBranch(True)
        __se_tmp_4 = inst.switchPortMap[dpid].has_key(dstaddr)
        if __se_tmp_4:
            whichBranch(True)
            __se_tmp_3 = inst.switchPortMap[dpid].has_key(dstaddr)
            if __se_tmp_3:
                whichBranch(True)
                if (inst.switchPath[srcaddr][dstaddr][0] == dpid):
                    whichBranch(True)
                    choice = choose_path(srcip, dstip)
                    if (choice == """alwaysOn"""):
                        whichBranch(True)
                        switchList = inst.alwaysOnPath[srcaddr][dstaddr]
                    else:
                        whichBranch(False)
                        if (choice == """onDemand"""):
                            whichBranch(True)
                            switchList = inst.onDemandPath[srcaddr][dstaddr]
                        else:
                            whichBranch(False)
                    logger.info(("""Preinstalling %s: %s""" % (choice, switchList)))
                    flow = extract_flow(packet)
                    for s in switchList:
                        if (choice == """onDemand"""):
                            whichBranch(True)
                            outport = inst.onDemandMap[s][dstaddr]
                            inport = inst.onDemandMap[s][srcaddr]
                        else:
                            whichBranch(False)
                            outport = inst.alwaysOnMap[s][dstaddr]
                            inport = inst.alwaysOnMap[s][srcaddr]
                        actions = [[openflow.OFPAT_OUTPUT, [0, outport]]]
                        inst.install_datapath_flow(s, flow, CACHE_TIMEOUT, openflow.OFP_FLOW_PERMANENT, actions, None, openflow.OFP_DEFAULT_PRIORITY, None, None)
                else:
                    whichBranch(False)
            else:
                whichBranch(False)
        else:
            whichBranch(False)
    else:
        whichBranch(False)

def getMac(networkDestination):
    if (networkDestination == 167772171):
        whichBranch(True)
        MAC = """00:00:00:00:00:0B"""
    else:
        whichBranch(False)
        if (networkDestination == 167772172):
            whichBranch(True)
            MAC = """00:00:00:00:00:0C"""
        else:
            whichBranch(False)
            if (networkDestination == 167772173):
                whichBranch(True)
                MAC = """00:00:00:00:00:0D"""
            else:
                whichBranch(False)
                if (networkDestination == 167772174):
                    whichBranch(True)
                    MAC = """00:00:00:00:00:0E"""
                else:
                    whichBranch(False)
                    MAC = """00:00:00:00:00:00"""
    return MAC

def datapath_leave_callback(dpid):
    logger.info(("""Switch %x has left the network""" % dpid))

def datapath_join_callback(dpid, stats):
    logger.info(("""Switch %x has joined the network""" % dpid))

def packet_in_callback(dpid, inport, reason, len, bufid, packet):
    if (not packet.parsed):
        whichBranch(True)
        logger.info("""Ignoring incomplete packet""")
    else:
        whichBranch(False)
    if (packet.type == ethernet.LLDP_TYPE):
        whichBranch(True)
        logger.info("""Ignoring LLDP packet""")
        return CONTINUE
    else:
        whichBranch(False)
    forward_l2_packet(dpid, inport, packet, packet.arr, bufid)
    install_path(dpid, inport, packet, packet.arr, bufid)
    return CONTINUE

class eate(Component, ):

    def add_port_listener(self, dpid, port, listener):
        self.port_listeners[dpid][port].append(listener)

    def remove_port_listener(self, dpid, port, listener):
        try:
            self.port_listeners[dpid][port].remove(listener)
        except Exception as e:
            logger.warn(("""Failed to remove port %d from dpid %d""" % (port, dpid)))
            pass

    def fire_port_listeners(self, dpid, portno, port):
        for listener in self.port_listeners[dpid][portno]:
            __se_tmp_6 = listener(port)
            if (not __se_tmp_6):
                whichBranch(True)
                self.remove_port_listener(dpid, portno, listener)
            else:
                whichBranch(False)

    def port_timer(self, dp):
        if (dp in self.dp_stats):
            whichBranch(True)
            self.ctxt.send_port_stats_request(dp, OFPP_NONE)
            self.post_callback((self.dp_poll_period[dp]["""port"""] + 1), (lambda : self.port_timer(dp)))
        else:
            whichBranch(False)

    def table_timer(self, dp):
        if (dp in self.dp_stats):
            whichBranch(True)
            self.ctxt.send_table_stats_request(dp)
            self.post_callback(self.dp_poll_period[dp]["""table"""], (lambda : self.table_timer(dp)))
        else:
            whichBranch(False)

    def dp_join(self, dp, stats):
        stats["""dpid"""] = dp
        self.dp_stats[dp] = stats
        port_list = self.dp_stats[dp]["""ports"""]
        for i in range(0, len(port_list)):
            new_mac = mac_to_str(port_list[i]["""hw_addr"""]).replace(""":""", """-""")
            port_list[i]["""hw_addr"""] = new_mac
        self.dp_poll_period[dp] = SeDict({})
        self.dp_poll_period[dp]["""table"""] = DEFAULT_POLL_TABLE_PERIOD
        self.dp_poll_period[dp]["""port"""] = DEFAULT_POLL_PORT_PERIOD
        self.dp_poll_period[dp]["""aggr"""] = DEFAULT_POLL_AGGREGATE_PERIOD
        self.ctxt.send_desc_stats_request(dp)
        return CONTINUE

    def dp_leave(self, dp):
        dpid_obj = datapathid.from_host(dp)
        __se_tmp_7 = self.dp_stats.has_key(dp)
        if __se_tmp_7:
            whichBranch(True)
            del self.dp_stats[dp]
        else:
            whichBranch(False)
            log.err("""Unknown datapath leave""", system="""eate""")
        __se_tmp_8 = self.dp_poll_period.has_key(dp)
        if __se_tmp_8:
            whichBranch(True)
            del self.dp_poll_period[dp]
        else:
            whichBranch(False)
        __se_tmp_9 = self.dp_table_stats.has_key(dp)
        if __se_tmp_9:
            whichBranch(True)
            del self.dp_table_stats[dp]
        else:
            whichBranch(False)
        __se_tmp_10 = self.dp_desc_stats.has_key(dp)
        if __se_tmp_10:
            whichBranch(True)
            del self.dp_desc_stats[dp]
        else:
            whichBranch(False)
        __se_tmp_11 = self.dp_port_stats.has_key(dp)
        if __se_tmp_11:
            whichBranch(True)
            del self.dp_port_stats[dp]
        else:
            whichBranch(False)
        if (dp in self.port_listeners):
            whichBranch(True)
            del self.port_listeners[dp]
        else:
            whichBranch(False)
        return CONTINUE

    def map_name_to_portno(self, dpid, name):
        for port in self.dp_stats[dpid]["""ports"""]:
            if (port["""name"""] == name):
                whichBranch(True)
                return port["""port_no"""]
            else:
                whichBranch(False)
        return None

    def table_stats_in_handler(self, dpid, tables):
        self.dp_table_stats[dpid] = tables

    def desc_stats_in_handler(self, dpid, desc):
        self.dp_desc_stats[dpid] = desc
        ip = self.ctxt.get_switch_ip(dpid)
        self.dp_desc_stats[dpid]["""ip"""] = str(create_ipaddr(c_htonl(ip)))

    def port_stats_in_handler(self, dpid, ports):
        if (dpid not in self.dp_port_stats):
            whichBranch(True)
            new_ports = SeDict({})
            for port in ports:
                port["""delta_bytes"""] = 0
                new_ports[port["""port_no"""]] = port
            self.dp_port_stats[dpid] = new_ports
            return
        else:
            whichBranch(False)
        new_ports = SeDict({})
        for port in ports:
            if (port["""port_no"""] in self.dp_port_stats[dpid]):
                whichBranch(True)
                port["""delta_bytes"""] = (port["""tx_bytes"""] - self.dp_port_stats[dpid][port["""port_no"""]]["""tx_bytes"""])
                new_ports[port["""port_no"""]] = port
            else:
                whichBranch(False)
                port["""delta_bytes"""] = 0
                new_ports[port["""port_no"""]] = port
            self.fire_port_listeners(dpid, port["""port_no"""], port)
        self.dp_port_stats[dpid] = new_ports
        self.energyState(dpid)

    def print_port_statistics(self):
        __se_tmp_19 = self.dp_port_stats.has_key(1)
        if __se_tmp_19:
            whichBranch(True)
            __se_tmp_18 = self.dp_port_stats[1].has_key(2)
            if __se_tmp_18:
                whichBranch(True)
                __se_tmp_17 = self.dp_port_stats.has_key(2)
                if __se_tmp_17:
                    whichBranch(True)
                    __se_tmp_16 = self.dp_port_stats[2].has_key(1)
                    if __se_tmp_16:
                        whichBranch(True)
                        __se_tmp_15 = self.dp_port_stats.has_key(3)
                        if __se_tmp_15:
                            whichBranch(True)
                            __se_tmp_14 = self.dp_port_stats[3].has_key(1)
                            if __se_tmp_14:
                                whichBranch(True)
                                __se_tmp_13 = self.dp_port_stats.has_key(3)
                                if __se_tmp_13:
                                    whichBranch(True)
                                    __se_tmp_12 = self.dp_port_stats[3].has_key(2)
                                    if __se_tmp_12:
                                        whichBranch(True)
                                        logger.info(("""links LtoR %d %d""" % (self.dp_port_stats[1][2]["""delta_bytes"""], self.dp_port_stats[3][2]["""delta_bytes"""])))
                                        logger.info(("""links RtoL %d %d""" % (self.dp_port_stats[2][1]["""delta_bytes"""], self.dp_port_stats[3][1]["""delta_bytes"""])))
                                    else:
                                        whichBranch(False)
                                else:
                                    whichBranch(False)
                            else:
                                whichBranch(False)
                        else:
                            whichBranch(False)
                    else:
                        whichBranch(False)
                else:
                    whichBranch(False)
            else:
                whichBranch(False)
        else:
            whichBranch(False)

    def port_status_handler(self, dpid, reason, port):
        intdp = int(dpid)
        if (intdp not in self.dp_stats):
            whichBranch(True)
            log.err("""port status from unknown datapath""", system="""eate""")
            return
        else:
            whichBranch(False)
        for i in range(0, len(self.dp_stats[intdp]["""ports"""])):
            oldport = self.dp_stats[intdp]["""ports"""][i]
            if (oldport["""name"""] == port["""name"""]):
                whichBranch(True)
                port["""hw_addr"""] = mac_to_str(port["""hw_addr"""]).replace(""":""", """-""")
                self.dp_stats[intdp]["""ports"""][i] = port
            else:
                whichBranch(False)

    def get_switch_conn_p_s_heavy_hitters(self):
        hitters = []
        return hitters

    def get_switch_port_error_heavy_hitters(self):
        error_list = []
        for dpid in self.dp_port_stats:
            ports = self.dp_port_stats[dpid].values()
            for port in ports:
                error_list.append((dpid, port["""port_no"""], (port["""rx_errors"""] + port["""tx_errors"""])))
        return error_list

    def get_switch_port_bandwidth_hitters(self):
        error_list = []
        for dpid in self.dp_port_stats:
            ports = self.dp_port_stats[dpid].values()
            for port in ports:
                error_list.append((dpid, port["""port_no"""], (port["""delta_bytes"""] / DEFAULT_POLL_PORT_PERIOD)))
        return error_list

    def energyState(self, dpid):
        if (dpid == 1):
            whichBranch(True)
            __se_tmp_21 = self.dp_port_stats.has_key(1)
            if __se_tmp_21:
                whichBranch(True)
                __se_tmp_20 = self.dp_port_stats[1].has_key(2)
                if __se_tmp_20:
                    whichBranch(True)
                    if (self.dp_port_stats[1][2]["""delta_bytes"""] > MEDIUM_THRESHOLD):
                        whichBranch(True)
                        inst.energyLtoR = """onDemand"""
                        if (self.dp_port_stats[1][2]["""delta_bytes"""] > HIGH_THRESHOLD):
                            whichBranch(True)
                            inst.onDemandLevelLtoR = """high"""
                        else:
                            whichBranch(False)
                            inst.onDemandLevelLtoR = """medium"""
                        logger.info(("""energy: OnDemand LtoR %s""" % inst.onDemandLevelLtoR))
                    else:
                        whichBranch(False)
                        inst.energyLtoR = """alwaysOn"""
                        logger.info("""energy: alwaysOn LtoR""")
                else:
                    whichBranch(False)
            else:
                whichBranch(False)
        else:
            whichBranch(False)
        if (dpid == 2):
            whichBranch(True)
            __se_tmp_23 = self.dp_port_stats.has_key(2)
            if __se_tmp_23:
                whichBranch(True)
                __se_tmp_22 = self.dp_port_stats[2].has_key(2)
                if __se_tmp_22:
                    whichBranch(True)
                    if (self.dp_port_stats[2][1]["""delta_bytes"""] > MEDIUM_THRESHOLD):
                        whichBranch(True)
                        inst.energyRtoL = """onDemand"""
                        if (self.dp_port_stats[2][1]["""delta_bytes"""] > HIGH_THRESHOLD):
                            whichBranch(True)
                            inst.onDemandLevelRtoL = """high"""
                        else:
                            whichBranch(False)
                            inst.onDemandLevelRtoL = """medium"""
                        logger.info(("""energy: OnDemand RtoL %s""" % inst.onDemandLevelLtoR))
                    else:
                        whichBranch(False)
                        inst.energyRtoL = """alwaysOn"""
                        logger.info("""energy: alwaysOn RtoL""")
                else:
                    whichBranch(False)
            else:
                whichBranch(False)
        else:
            whichBranch(False)
        if (dpid == 2):
            whichBranch(True)
            inst.print_port_statistics()
        else:
            whichBranch(False)

    def __init__(self, ctxt):
        global inst
        Component.__init__(self, ctxt)
        self.st = SeDict({})
        self.activeSwitches = SeDict({})
        self.switches = []
        self.hosts = []
        self.switchPortStat = SeDict({})
        self.switchPortMap = SeDict({})
        self.alwaysOnMap = SeDict({})
        self.onDemandMap = SeDict({})
        self.switchPath = SeDict({})
        self.onDemandPath = SeDict({})
        self.alwaysOnPath = SeDict({})
        self.energyLtoR = """alwaysOn"""
        self.energyRtoL = """alwaysOn"""
        self.onDemandLevelLtoR = """medium"""
        self.onDemandLevelRtoL = """medium"""
        self.port_listeners = defaultdict((lambda : defaultdict(list)))
        self.dp_stats = SeDict({})
        self.dp_poll_period = SeDict({})
        self.dp_table_stats = SeDict({})
        self.dp_desc_stats = SeDict({})
        self.dp_port_stats = SeDict({})
        inst = self

    def install(self):
        deployRules()
        inst.register_for_packet_in(packet_in_callback)
        inst.register_for_datapath_join(datapath_join_callback)
        self.register_for_datapath_join(self.dp_join)
        self.register_for_port_stats_in(self.port_stats_in_handler)

    def getInterface(self):
        return str(eate)

    def __getstate__(self):
        """ function added to have a serialized version of the app with only the necessary state """
        di = Component.__getstate__(self)
        di["""dp_port_stats"""] = self.dp_port_stats
        di["""energyLtoR"""] = self.energyLtoR
        di["""energyRtoL"""] = self.energyRtoL
        return di

    def __setstate__(self, state):
        """ function added to restore a certain state saved by getstate """
        Component.__setstate__(self, state)
        self.dp_port_stats = state["""dp_port_stats"""]

def getFactory():

    class Factory:

        def instance(self, ctxt):
            return eate(ctxt)
    return Factory()