def MW_sock_send(MW_ID):  # sender function for the MW sockets
    global MW_sock_input_queues, MW_sockets

    print "[INFO] <MW_sock_send>                for MW({}) :[STARTED]".format(
        MW_ID)
    REPLICA_log("[INFO] <MW_sock_send>",
                "               for MW({}) :[STARTED]".format(MW_ID))
    while True:
        try:
            out_msg = MW_sock_input_queues[MW_ID].pop(0)
            print "[MW][OUT]  <MW_sock_send>     => msg to MW ({}), len ({}), kind: ({})".format(
                MW_ID,
                su("H", out_msg[:2])[0],
                su("H", out_msg[2:4])[0])
            REPLICA_log(
                "[MW][OUT]  <MW_sock_send>",
                "msg to MW ({},{}), len ({}), kind: ({})".format(
                    MW_ID,
                    su("H", out_msg[4:6])[0],
                    su("H", out_msg[:2])[0],
                    su("H", out_msg[2:4])[0]))
            MW_sockets[MW_ID][0].send(out_msg)
        except IndexError:
            time.sleep(1)
            pass
        except:
            raise
def VNF_sock_send(VNF_ID):  # sender function for the VNF sockets
    global VNF_sock_input_queues, VNF_sockets

    print "[INFO] <VNF_sock_send>                for VNF({}) :[STARTED]".format(
        VNF_ID)
    mid_log("[INFO] <VNF_sock_send>",
            "               for VNF({}) :[STARTED]".format(VNF_ID))
    while True:
        try:
            out_msg = VNF_sock_input_queues[VNF_ID].pop(0)
            print "[VNF][OUT]  <VNF_sock_send>     => msg to VNF ({}), len ({}), kind: ({})".format(
                VNF_ID,
                su("H", out_msg[:2])[0],
                su("H", out_msg[2:4])[0])
            mid_log(
                "[VNF][OUT]  <VNF_sock_send>",
                "msg to VNF ({},{}), len ({}), kind: ({})".format(
                    VNF_ID,
                    su("H", out_msg[4:6])[0],
                    su("H", out_msg[:2])[0],
                    su("H", out_msg[2:4])[0]))
            VNF_sockets[VNF_ID][0].send(out_msg)
        except IndexError:
            time.sleep(1)
            pass
        except:
            raise
def send_msg():
    global sending_queue, NF_sock

    NF_log("[INIT] <send_msg>", "    :[STARTED]")
    print "[INIT] <send_msg>", "           :[STARTED]"
    while True:
        try:
            out_msg = sending_queue.pop(0)
            NF_log(
                "[OUT] <send_msg>", "MSG len({}), kind({})".format(
                    su("H", out_msg[:2])[0],
                    msg_kind(int(su("H", out_msg[2:4])[0]))))
            print "[OUT] <send_msg> => sending msg with len({}), kind({})\n".format(
                su("H", out_msg[:2])[0],
                msg_kind(int(su("H", out_msg[2:4])[0])))
            if len(out_msg) == int(su("H", out_msg[:2])[0]):
                NF_sock.send(out_msg)
            else:
                print "len(out_msg) != int(su('h',out_msg[:2])[0])"
        except IndexError:
            time.sleep(1)
            pass
def packet_in_metadata(pkt_in):
    pkt_in_metadata = {}
    print("Received Packet-in\n")
    packet = pkt_in.packet.payload
    metadata = pkt_in.packet.metadata
    for meta in metadata:
        metadata_id = meta.metadata_id
        value = meta.value
        pkt_in_metadata[metadata_id] = value
    input_port = int(su("!H", pkt_in_metadata[1])[0])
    tmp_pkt = copy.deepcopy(packet)
    tmp_pkt1 = Ether(_pkt=tmp_pkt)
    raw_var_id = [
        bin(int(x))[2:].zfill(8) for x in tmp_pkt1[IP].dst[4:].split(".")
    ]
    var_id = int(raw_var_id[0][2:] + raw_var_id[1] + raw_var_id[2], 2)
    print tmp_pkt1[IP].dst
    print "var_id: ", var_id

    return packet, pkt_in_metadata, input_port, var_id
def msg_cleaner_handler():
    global init_NF_ID, init_PUB_ID, received_queue, NF_id, variable_id, variable_name, var_names_IDs, answer, SUB_ID_ans

    current_data = ""  # empty for started
    NF_log("[INIT] <msg_cleaner_handler>", ":[STARTED]")
    print "[INIT] <msg_cleaner_handler> :[STARTED]\n"
    while True:
        try:
            current_data += received_queue.pop(
                0
            )  # pop the first received chunk and add to the remained bytes (if any)

            # more than 2 bytes to know the lenght of the msg AND enough bytes to rebuild a msg
            while (len(current_data) > 2
                   and len(current_data) >= int(su("H", current_data[:2])[0])):
                in_msg = current_data[:int(su("H", current_data[:2])
                                           [0])]  # we extract the msg

                #### INIT NF_ID REPLY => (kind = 0)
                ## Length(2B)+kind(2B)+NF_global_ID(2B)
                if int(
                        su("H", in_msg[2:4])[0]
                ) == 0:  # it is an NF_global_ID reply from the SDN controller
                    init_NF_ID = 1
                    NF_id = int(su("H", in_msg[6:8])[0])  # NF_global_ID
                    NF_log("[IN] <msg_cleaner_handler>",
                           "My NF_ID : global={}".format(NF_id))

                    #### CUT PROCESSED MSG FROM current_data
                    current_data = current_data[int(
                        su("H", current_data[:2])
                        [0]):]  # continue from begining of the next msg

                #### INIT PUB_variable_ID REPLY => (kind = 1)
                ## Length(2B)+kind(2B)+NF_global_ID(2B)
                elif int(
                        su("H", in_msg[2:4])[0]
                ) == 1:  # it is an PUB_variable_ID reply from the SDN controller
                    init_PUB_ID = 1
                    variable_id = int(su("H",
                                         in_msg[6:8])[0])  # PUB_variable_ID
                    var_names_IDs[variable_name] = int(
                        su("H",
                           in_msg[6:8])[0])  # convert and save the Variable_ID
                    NF_log("[IN] <msg_cleaner_handler>",
                           "My Publish variable_ID : {}".format(variable_id))
                    update_num = 0  # initializing

                    #### CUT PROCESSED MSG FROM current_data
                    current_data = current_data[int(
                        su("H", current_data[:2])
                        [0]):]  # continue from begining of the next msg

                #### PUBLISH MSG => (kind = 2)
                ## kind(2B)+variable_id(4B)+update_num(2B)+frag_tot(2B)+frag_num(2B)+DATA(mB)
                elif int(su("H", in_msg[2:4])[0]) == 2:  # it is a publish
                    NF_log(
                        "[IN] <msg_cleaner_handler>",
                        "Received PUBLISH => len({}), kind({})\
                           ".format(
                            su("H", in_msg[:2])[0],
                            msg_kind(int(su("H", in_msg[2:4])[0]))))

                    print "[IN] <msg_cleaner_handler>", "Received PUBLISH => len({}), kind({})\
                          ".format(
                        su("H", in_msg[:2])[0],
                        msg_kind(int(su("H", in_msg[2:4])[0])))

                    # checking if drop mark for "Variable_ID,update_num"
                    if (str(su("H", in_msg[6:8])[0]) + "," +
                            str(su("H", in_msg[8:10])[0])
                        ) not in drop_update.keys():
                        drop_update[str(su("H", in_msg[6:8])[0]) + "," +
                                    str(su("H", in_msg[8:10])
                                        [0])] = 0  # do not drop this update
                        ## making a fake last received packet for the first time
                        global_table_last_frame[int(su(
                            "H", in_msg[6:8])[0])] = [
                                int(su("H", in_msg[8:10])[0]),
                                int(su("H", in_msg[10:12])[0]),
                                int(su("H", in_msg[12:14])[0]) - 1
                            ]
                        tmp_recvd_publishes[int(su("H", in_msg[6:8])[0])] = [
                        ]  # building place for the first msg

                    # there is no drop desicion on this "Variable_ID,update_num"
                    if (drop_update[str(su("H", in_msg[6:8])[0]) + "," +
                                    str(su("H", in_msg[8:10])[0])]) == 0:
                        if global_table_last_frame[int(
                                su("H", in_msg[6:8])[0])][0] == int(
                                    su("H", in_msg[8:10])
                                    [0]):  # from the same update_num
                            if global_table_last_frame[int(
                                    su("H", in_msg[6:8])[0])][2] + 1 == int(
                                        su("H", in_msg[12:14])
                                        [0]):  # we have normal next fragment

                                # appending the pure msg to the temporary list for this update_num of this variable_id
                                tmp_recvd_publishes[int(
                                    su("H",
                                       in_msg[6:8])[0])].append(in_msg[14:])
                                if int(su("H", in_msg[10:12])[0]) == int(
                                        su("H", in_msg[12:14])
                                    [0]):  # last fragment of the update
                                    fileName = "logs/P_recv_" + str(
                                        su("H", in_msg[6:8])
                                        [0])  # making filname for saving data
                                    with open(
                                            fileName, "a"
                                    ) as f:  # open file for external save(DEBUGING)
                                        f.write("".join(
                                            tmp_recvd_publishes[int(
                                                su("H", in_msg[6:8])
                                                [0])]))  # external save
                                    tmp_recvd_publishes[int(
                                        su("H", in_msg[6:8])[0]
                                    )] = [
                                    ]  # cleaning the in_msg for next update

                                    # ready for first fragment of next update
                                    global_table_last_frame[int(
                                        su("H", in_msg[6:8])[0])] = [
                                            int(su("H", in_msg[8:10])[0]) + 1,
                                            0, 0
                                        ]
                            else:  # we have lost fragment/s

                                # puting drop desicion on this "Variable_ID,update_num"
                                # drop_update[str(su("H",in_msg[6:8])[0])+","+str(su("H",in_msg[8:10])[0])] = 1

                                # Data_length(2B)+kind(2B)+Global_ID(2B)+variable_id(2B)+update_num(2B)
                                recover_msg = sp("H", 10) + sp("H", 6) + sp(
                                    "H", NF_id) + in_msg[6:10]
                                sending_queue.append(recover_msg)
                                tmp_recvd_publishes[int(
                                    su("H", in_msg[6:8])[0]
                                )] = [
                                ]  # cleaning the in_msg due to lost fragment
                        elif int(
                                su("H", in_msg[12:14])[0]
                        ) > 1:  # we have lost msg from 2 consequent updates
                            # drop_update[str(su("H",in_msg[6:8])[0])+","+str(su("H",in_msg[8:10])[0])] = 1 # puting drop desicion on this "variable_id,update_num"

                            ## Data_length(2B)+kind(2B)+Global_ID(2B)+variable_id(2B)+previous_update_num(2B)
                            recover_msg = sp("H", 10) + sp("H", 6) + sp(
                                "H", NF_id) + in_msg[6:8] + sp(
                                    "H",
                                    int(su("H", in_msg[8:10])[0]) -
                                    1)  #recover for previous update
                            sending_queue.append(recover_msg)
                            tmp_recvd_publishes[int(
                                su("H", in_msg[6:8])[0]
                            ) - 1] = [
                            ]  # cleaning the in_msg due to lost fragment of previous update

                            ## Data_length(2B)+kind(2B)+Global_ID(2B)+variable_id(2B)+update_num(2B)
                            recover_msg = sp("H", 10) + sp("H", 6) + sp(
                                "H", NF_id) + in_msg[6:10]
                            sending_queue.append(recover_msg)
                            tmp_recvd_publishes[int(su(
                                "H", in_msg[6:8])[0])] = [
                                ]  # cleaning the in_msg due to lost fragment
                    else:
                        tmp_recvd_publishes[int(su("H", in_msg[6:8])[0])] = [
                        ]  # empty the tempory received update because of fragment/s lost

                    #### CUT PROCESSED MSG FROM current_data
                    current_data = current_data[int(
                        su("H", current_data[:2])
                        [0]):]  # continue from begining of the next msg

                #### SUBSCRIBE VARIABLE_ID RESPONSE
                elif int(
                        su("H", in_msg[2:4])[0]
                ) == 5:  # it is a SDN reply to variable_ID request for subscription
                    if int(su("H", in_msg[6:8])[0]) == 0:
                        SUB_ID_ans = "error"
                        NF_log(
                            "msg_cleaner_handler",
                            "UN-SUCCESSFUL VAR_ID_REQ response from the SDN")
                        print "msg_cleaner_handler: UN-SUCCESSFUL VAR_ID_REQ response from the SDN"
                    else:
                        SUB_ID_ans = "ok"
                        var_name = "".join([su("c", x)[0] for x in in_msg[8:]
                                            ])  # the variable_name
                        # print "var_name: ",var_name
                        var_names_IDs[var_name] = int(
                            su("H", in_msg[6:8])
                            [0])  # convert and save the Variable_ID
                        NF_log("msg_cleaner_handler",
                               "SUCCESSFUL VAR_ID_REQ response from the SDN")
                        print "msg_cleaner_handler: SUCCESSFUL VAR_ID_REQ response from the SDN"

                    #### CUT PROCESSED MSG FROM current_data
                    current_data = current_data[int(
                        su("H", current_data[:2])
                        [0]):]  # continue from begining of the next msg
                    answer = 1

        except IndexError:
            time.sleep(1)
            pass
def recv_data_middleware():
    global VNF_local_global_id, VNF_local_global_id, VNF_publishes, VNF_sock_input_queues, rcv_mw_sock
    global VNF_subscriptions, rcv_mw_socks

    # Create the socket
    rcv_mw_socks[0] = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    rcv_mw_socks[0].setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)

    # Bind to the server address
    rcv_mw_socks[0].bind(('', 65432))
    while True:

        data, addr_uni = rcv_mw_socks[0].recvfrom(2048)
        try:
            mid_log(
                "[SW][IN]  <recv_data_middleware>",
                "MSG len({}), kind({})".format(
                    su("H", data[:2])[0],
                    su("H", data[2:4])[0]))
            print "[SW][IN] <recv_data_middleware>      => len({}), kind({})".format(
                su("H", data[:2])[0],
                su("H", data[2:4])[0])
        except:
            pass

        ### PUBLISH
        if int(su("H", data[2:4])[0]) == 2:
            mid_log(
                "[SW][IN]  <rcv_mw_sock({})>".format(
                    str(int(su("H", data[6:8])[0]))), "PUBLISH packet")
            if int(su("H", data[6:8])[0]) in VNF_subscriptions.keys(
            ):  # if there is internal subscriptions on this Variable_ID
                for dest in VNF_subscriptions[int(su("H", data[6:8])[0])]:

                    print "[INFO] <rcv_mw_sock({})>".format(
                        str(int(su("H", data[6:8])[0])))
                    print "PUBLISH : len({}), kind({}), G_id({})".format(
                        int(su("H", data[:2])[0]), int(su("H", data[2:4])[0]),
                        int(su("H", data[4:6])[0]))
                    ## msg = Data_length(2B)+Kind(2B)+Global_ID(4B)+tot_var(2B)
                    msg_copy = copy.deepcopy(data)
                    VNF_sock_input_queues[VNF_local_global_id.index(
                        dest)].append(msg_copy)
def send_data_middleware():  # sending packets to network
    global OUT_2_queue, thr_rcv_pub_multi, rcv_mw_socks, all_mcast_groups, mcast_groups, system_default_max

    # Create the datagram socket
    send_mw_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    ## Set the time-to-live for messages
    ttl = struct.pack('b', 2)
    send_mw_sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl)
    group_num = 0
    mcast_groups[group_num] = []
    while True:
        try:
            out_msg = OUT_2_queue.pop(0)
            mid_log(
                "[SW][OUT] <send_data_middleware>",
                "MSG len({}), kind({})".format(
                    su("H", out_msg[:2])[0],
                    su("H", out_msg[2:4])[0]))
            print "[SW][OUT] <send_data_middleware>        => len({}), kind({})".format(
                su("H", out_msg[:2])[0],
                su("H", out_msg[2:4])[0])

            kind = int(su("H", out_msg[2:4])[0])

            # SUB_register
            if kind == 3:
                var_id = int(su("H", out_msg[6:8])[0])
                # Building the multicast group related to the variable_id
                mcast_group = pubSubIP(var_id, 2)

                # Tell the operating system to add the socket to the multicast group
                # on all interfaces.
                group = socket.inet_aton(mcast_group)
                mreq = struct.pack('4sL', group, socket.INADDR_ANY)
                all_mcast_groups.append(mcast_group)

                try:
                    rcv_mw_socks[group_num].setsockopt(
                        socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
                except:
                    group_num += 1
                    mcast_groups[group_num] = []
                    ## building a new thread responcible for making a new socket for that var_id,
                    ## but not receiving, just letting us do more IP_multicast_membersip
                    rcv_mw_socks[group_num] = threading.Thread(
                        target=pub_mcast_membership_maker, args=[group_num])
                    print "[INFO] send_data_middleware: buiding...", rcv_mw_socks
                    rcv_mw_socks[group_num].start()
                    print "[INFO] send_data_middleware: starting...", rcv_mw_socks
                    with open("error_mreq_report.txt", "a") as f:
                        f.write(
                            "handled membership error due to OS limit,receiver thread for: %s ,group: %s\n"
                            % (str(var_id), mcast_group))
                    pass

                mcast_groups[group_num].append(mcast_group)

            elif kind in [2, 4, 6]:  # publish, sub_remove, recover
                var_id = int(su("H", out_msg[6:8])[0])
            elif kind in [
                    0, 1, 5
            ]:  # init_VNF, pub_variable_id_request, sub_variable_id_request
                var_id = 0

            ## Making proper destination
            dest_addr = (pubSubIP(var_id, kind), 65432)

            # Due to some inconsistency between the switch and the embedded controller we send 2 packets
            if kind in [3, 4]:
                out_msg_dup = copy.deepcopy(out_msg)
                dup_send = send_mw_sock.sendto(out_msg_dup, dest_addr)

            ## Sending the msg
            sent = send_mw_sock.sendto(out_msg, dest_addr)

        except IndexError:
            time.sleep(1)
            pass
def sig_msg_hndlr_REPLICA():
    global IN_1_queue

    current_data = ""  # empty for started
    mid_log("[INIT] <REPLICA_msg_cleaner_handler>", ":[STARTED]")
    print "[INIT] <REPLICA_msg_cleaner_handler> :[STARTED]"

    while True:
        try:
            # pop the first received chunk and add to the remained bytes (if any)
            current_data += IN_1_queue.pop(0)

            # more than 2 bytes to know the lenght of the msg and enough bytes to rebuild a msg
            while (len(current_data) > 2
                   and len(current_data) >= int(su("H", current_data[:2])[0])):

                # we extract the msg from Data_length(2B)
                in_msg = current_data[:int(su("H", current_data[:2])[0])]

                ### INIT VNF_ID REPLY
                #### => (kind = 0)
                if int(
                        su("H", in_msg[2:4])[0]
                ) == 0:  # it is an initializing response from the REPLICA controller => send to the VNF INT module
                    try:
                        mid_log("[INFO] <sig_msg_hndlr_REPLICA>", "INIT VNF_ID REPLY:len({}), kind({}), l_ID({}), G_id({})"\
                                .format(int(su("H", in_msg[:2])[0]),int(su("H", in_msg[2:4])[0]),int(su("H", in_msg[4:6])[0]),int(su("H", in_msg[6:8])[0])))

                        print "[INFO] <sig_msg_hndlr_REPLICA> "
                        print "INIT VNF_ID REPLY: len({}), kind({}), l_ID({}), G_id({})".format(
                            int(su("H", in_msg[:2])[0]),
                            int(su("H", in_msg[2:4])[0]),
                            int(su("H", in_msg[4:6])[0]),
                            int(su("H", in_msg[6:8])[0]))

                        VNF_local_global_id[int(su(
                            "H", in_msg[4:6])[0])] = int(
                                su("H", in_msg[6:8])[0]
                            )  # update the mapping of Local_ID : Global_ID

                        print "[INFO] <sig_msg_hndlr_REPLICA> VNF_local_global_id: {} ".format(
                            VNF_local_global_id)

                        ## append msg to the input queue of the related VNF socket
                        VNF_sock_input_queues[int(su(
                            "H", in_msg[4:6])[0])].append(in_msg)

                        #### CUT PROCESSED MSG FROM current_data
                        current_data = current_data[int(
                            su("H", current_data[:2])
                            [0]):]  # continue from begining of the next msg
                    except:
                        raise

                ### INIT PUB_ID REPLY
                #### => (kind = 1)
                elif int(
                        su("H", in_msg[2:4])[0]
                ) == 1:  # it is a PUB_ID reply from the REPLICA controller => send to the send to the VNF INT module
                    try:
                        mid_log("[INFO] <recv_data_middleware>", "INIT PUB_ID REPLY:len({}), kind({}), G_id({})"\
                                .format(int(su("H", in_msg[:2])[0]),int(su("H", in_msg[2:4])[0]),int(su("H", in_msg[4:6])[0])))

                        print "[INFO] <recv_data_middleware> "
                        print "INIT PUB_ID REPLY: len({}), kind({}), G_id({})".format(
                            int(su("H", in_msg[:2])[0]),
                            int(su("H", in_msg[2:4])[0]),
                            int(su("H", in_msg[4:6])[0]))

                        tmp_var_ID = int(su(
                            "H", in_msg[6:8])[0])  # extracting the Variable_ID
                        ## append var_ID to the published list of the related VNF socket
                        if int(su("H",
                                  in_msg[4:6])[0]) in VNF_publishes.keys():
                            VNF_publishes[int(su(
                                "H", in_msg[4:6])[0])].append(tmp_var_ID)
                        else:
                            VNF_publishes[int(su(
                                "H", in_msg[4:6])[0])] = [tmp_var_ID]
                        print "[INFO] <recv_data_middleware> check the VNF_publishes: ", VNF_publishes
                        print "INIT PUB_ID REPLY: len({}), kind({}), G_id({})".format(
                            int(su("H", in_msg[:2])[0]),
                            int(su("H", in_msg[2:4])[0]),
                            int(su("H", in_msg[4:6])[0]))

                        ## append msg to the input queue of the related VNF socket
                        VNF_sock_input_queues[VNF_local_global_id.index(
                            int(su("H", in_msg[4:6])[0]))].append(in_msg)

                        #### CUT PROCESSED MSG FROM current_data
                        current_data = current_data[int(
                            su("H", current_data[:2])
                            [0]):]  # continue from begining of the next msg
                    except:
                        raise

                ### INIT SUB_ID REPLY
                #### => (kind = 5)
                elif int(
                        su("H", in_msg[2:4])[0]
                ) == 5:  # it is a SUB_ID reply from the REPLICA controller => send to the send to the VNF INT module
                    try:
                        mid_log("[INFO]    <recv_data_middleware>", "VARIABLE_ID_REPLY:len({}), kind({}), G_id({}), Var_ID({})"\
                        .format(int(su("H", in_msg[:2])[0]),int(su("H", in_msg[2:4])[0]), int(su("H", in_msg[4:6])[0]),int(su("H", in_msg[6:8])[0])))

                        print "[INFO] <recv_data_middleware>"
                        print "VARIABLE_ID_REPLY: len({}), kind({}), G_id({}), v_ID({})".format(
                            int(su("H", in_msg[:2])[0]),
                            int(su("H", in_msg[2:4])[0]),
                            int(su("H", in_msg[4:6])[0]),
                            int(su("H", in_msg[6:8])[0]))

                        ## append msg to the input queue of the related VNF socket
                        VNF_sock_input_queues[VNF_local_global_id.index(
                            int(su("H", in_msg[4:6])[0]))].append(in_msg)

                        #### CUT PROCESSED MSG FROM current_data
                        current_data = current_data[int(
                            su("H", current_data[:2])
                            [0]):]  # continue from begining of the next msg
                    except:
                        raise

        except IndexError:
            time.sleep(1)
            pass
def VNF_sock_data_cleaner_handlr(
    VNF_ID
):  # rebuild the msgs coming from the VNF and send to needed queues in the Middle_Ware
    global VNF_sock_input_queues, VNF_sock_output_queues, VNF_subscriptions
    global VNF_publishes, OUT_2_queue, VNF_local_global_id

    current_data = ""  # empty for started
    print "[INFO] <VNF_sock_data_cleaner_handlr> for VNF({}) :[STARTED]".format(
        VNF_ID)
    mid_log("[INFO] <VNF_sock_data_cleaner_handlr>",
            "for VNF({}) :[STARTED]".format(VNF_ID))
    while True:
        try:
            # pop the first received chunk and add to the remained bytes (if any)
            current_data = current_data + VNF_sock_output_queues[VNF_ID].pop(0)

            # more than 2 bytes to know the lenght of the msg and enough bytes to rebuild a msg
            while (len(current_data) > 2
                   and len(current_data) >= int(su("H", current_data[:2])[0])):
                in_msg = current_data[:int(
                    su("H", current_data[:2])
                    [0])]  # we extract the msg from Data_length(2B)
                mid_log(
                    "[MW][IN]  <VNF_sock_data_cleaner_handlr>",
                    "MSG len({}), kind({})".format(
                        su("H", in_msg[:2])[0],
                        su("H", in_msg[2:4])[0]))
                print "[MW][IN] <VNF_sock_data_cleaner_handlr>  => len({}), kind({})".format(
                    su("H", in_msg[:2])[0],
                    su("H", in_msg[2:4])[0])

                #### INITIALIZING VNF_ID REQUEST
                #### => (kind = 0)
                if int(
                        su("H", in_msg[2:4])[0]
                ) == 0:  # it is an initializing request from the VNF => send to the SDN controller
                    try:
                        ## msg = Data_length(2B)+Kind(2B)+local_ID(2B)+Global_ID(2B)+VNF_NAME(nB)
                        in_msg_tmp = in_msg[2:4] + sp("H", VNF_ID) + in_msg[6:]
                        in_msg_tmp = sp("H", len(in_msg_tmp) + 2) + in_msg_tmp
                        OUT_1_queue.append(in_msg_tmp)

                        #### CUT PROCESSED MSG FROM current_data
                        current_data = current_data[int(
                            su("H", current_data[:2])
                            [0]):]  # continue from begining of the next msg
                    except:
                        raise

                #### PUB_ID, SUB_ID or RECOVER REQUEST
                #### => (kind = 1, 5 and 6)
                elif int(su("H", in_msg[2:4])[0]) in [
                        1, 5, 6
                ]:  # it is an initializing request from the VNF => send to the SDN controller
                    try:
                        ## msg = Data_length(2B)+Kind(2B)+Global_ID(2B)+VNF_NAME(nB)
                        OUT_1_queue.append(in_msg)

                        #### CUT THE PROCESSED MSG FROM current_data
                        current_data = current_data[int(
                            su("H", current_data[:2])
                            [0]):]  # continue from begining of the next msg
                    except:
                        raise

                #### PUBLISH
                #### => (kind = 2)
                elif int(su(
                        "H",
                        in_msg[2:4])[0]) == 2:  # it is a publish msg from VNF
                    try:
                        tmp_var_ID = int(su(
                            "H", in_msg[6:8])[0])  # extracting the Variable_ID

                        ## append var_ID to the published list of the related VNF socket
                        if tmp_var_ID in VNF_subscriptions.keys(
                        ):  # if there is internal subscriptions on this Variable_ID
                            for dest in VNF_subscriptions[tmp_var_ID]:
                                ## msg = Data_length(2B)+Kind(2B)+Local_ID(2B)+Global_ID(4B)+tot_var(2B)
                                msg_copy = copy.deepcopy(in_msg)
                                VNF_sock_input_queues[
                                    VNF_local_global_id.index(dest)].append(
                                        msg_copy)
                        OUT_2_queue.append(in_msg)

                        #### CUT THE PROCESSED MSG FROM current_data
                        current_data = current_data[int(
                            su("H", current_data[:2])
                            [0]):]  # continue from begining of the next msg
                    except:
                        raise

                #### SUBSCRIBE REGISTER
                #### => (kind = 3)
                elif int(su("H", in_msg[2:4])
                         [0]) == 3:  # it is a subscribe-register request
                    if int(
                            su("H", in_msg[6:8])[0]
                    ) not in VNF_subscriptions.keys(
                    ):  # If variable_ID is NOT in VNF_subscription as a key e.g. var_id:[.....] NOT exists
                        VNF_subscriptions[int(su("H", in_msg[6:8])[0])] = [
                            int(su("H", in_msg[4:6])[0])
                        ]  # add VNF_GLOBAL_ID to subscriptions of var_id:[]
                        internal_publish = 0
                        print "[INFO] <VNF_sock_data_cleaner_handlr> check before subscribe in SW"
                        print "VNF_publishes: ", VNF_publishes
                        print "VNF_subscriptions: ", VNF_subscriptions
                        for VNF in VNF_publishes.keys():
                            # check internal registered publishes for the var_id
                            if int(su("H",
                                      in_msg[6:8])[0]) in VNF_publishes[VNF]:
                                internal_publish = 1
                                break
                        if not internal_publish:  # if no internal publish on that var_id exist
                            OUT_2_queue.append(
                                in_msg)  # try to send request to the switch
                    else:  # If variable_ID is in VNF_subscription as a key
                        if int(su(
                                "H",
                                in_msg[4:6])[0]) not in VNF_subscriptions[int(
                                    su("H", in_msg[6:8])
                                    [0])]:  # if VNF_ID is not in the var_id:[]
                            VNF_subscriptions[int(su(
                                "H", in_msg[6:8])[0])].append(
                                    int(su("H", in_msg[4:6])[0])
                                )  # add VNF_ID to subscriptions of var_id

                    #### CUT THE PROCESSED MSG FROM current_data
                    current_data = current_data[int(
                        su("H", current_data[:2])
                        [0]):]  # continue from begining of the next msg

                #### SUBSCRIBE REMOVE
                #### => (KIND = 4)
                elif int(su("H", in_msg[2:4])
                         [0]) == 4:  # it is a subscribe-remove request
                    if (int(su("H",
                               in_msg[6:8])[0]) in VNF_subscriptions.keys()
                            and int(su(
                                "H", in_msg[4:6])[0]) in VNF_subscriptions[int(
                                    su("H", in_msg[6:8])[0])]):
                        VNF_subscriptions[int(su("H", in_msg[6:8])[0])].remove(
                            int(su("H", in_msg[4:6])[0]))
                    if not VNF_subscriptions[int(su("H", in_msg[6:8])[0])]:
                        internal_publish = 0
                        for VNF in VNF_publishes.keys():
                            if VNF_subscriptions[int(
                                    su("H",
                                       in_msg[6:8])[0])] in VNF_publishes[VNF]:
                                internal_publish = 1
                                break
                        if not internal_publish:
                            OUT_2_queue.append(in_msg)
                    #### CUT THE PROCESSED MSG FROM current_data
                    current_data = current_data[int(
                        su("H", current_data[:2])
                        [0]):]  # continue from begining of the next msg

                else:
                    print "WHY? ;)"
        except IndexError:
            time.sleep(1)
            pass
        except:
            raise
def MW_sock_data_cleaner_handlr(MW_ID):
    global MW_sock_input_queues, MW_sock_output_queues
    global nxt_NF_global_id, nxt_variable_global_id
    global NF_pub_global_ids, global_name_NF_id

    current_data = ""  # empty for started
    print "[INFO] <MW_sock_data_cleaner_handlr> for MW({}) :[STARTED]".format(
        MW_ID)
    REPLICA_log("[INFO] <MW_sock_data_cleaner_handlr>",
                "for MW({}) :[STARTED]".format(MW_ID))
    while True:
        try:
            # pop the first received chunk and add to the remained bytes (if any)
            current_data += MW_sock_output_queues[MW_ID].pop(0)

            # more than 2 bytes to know the lenght of the msg and enough bytes to rebuild a msg
            while (len(current_data) > 2
                   and len(current_data) >= int(su("H", current_data[:2])[0])):
                in_msg = current_data[:int(
                    su("H", current_data[:2])
                    [0])]  # we extract the msg from Data_length(2B)
                REPLICA_log(
                    "[MW][IN]  <MW_sock_data_cleaner_handlr>",
                    "MSG len({}), kind({})".format(
                        su("H", in_msg[:2])[0],
                        su("H", in_msg[2:4])[0]))
                print "[MW][IN] <MW_sock_data_cleaner_handlr>  => len({}), kind({})".format(
                    su("H", in_msg[:2])[0],
                    su("H", in_msg[2:4])[0])

                #### INITIALIZING NF_ID REQUEST
                #### => (kind = 0)
                if int(su("H", in_msg[2:4])[0]) == 0:
                    REPLICA_log("[SW][IN] <handle_pkt_REPLICA> ",
                                "INIT_NF_ID request msg received.")
                    print "INIT_NF_ID REQUEST msg received."
                    global_name_NF_id["".join([
                        su("c", x)[0] for x in in_msg[8:]
                    ])] = nxt_NF_global_id  # mapping NF_NAME : NF_ID
                    NF_pub_global_ids[nxt_NF_global_id] = []
                    tmp_msg = in_msg[2:6] + sp("H",
                                               nxt_NF_global_id) + in_msg[8:]
                    tmp_msg = sp("H", len(tmp_msg) + 2) + tmp_msg
                    MW_sock_input_queues[MW_ID].append(tmp_msg)

                    REPLICA_log("[MW][IN] <handle_pkt_REPLICA> ",
                                "INIT_NF_ID reply msg sent.")
                    print "INIT_NF_ID REPLY msg sent."
                    nxt_NF_global_id += 1
                    #### CUT THE PROCESSED MSG FROM current_data
                    current_data = current_data[int(
                        su("H", current_data[:2])
                        [0]):]  # continue from begining of the next msg

                ### INIT PUB_ID MSG
                ### (kind = 1)
                elif int(su("H", in_msg[2:4])[0]) == 1:
                    REPLICA_log("[SW][IN] <handle_pkt_REPLICA> ",
                                "INIT_PUB_ID request msg received.")
                    print "INIT_PUB_ID request msg received."
                    global_name_variable_id["".join([
                        su("c", x)[0] for x in in_msg[6:]
                    ])] = nxt_variable_global_id
                    NF_pub_global_ids[int(su(
                        "H", in_msg[4:6])[0])].append(nxt_variable_global_id)
                    tmp_msg = in_msg[2:6] + sp(
                        "H", nxt_variable_global_id) + in_msg[6:]
                    tmp_msg = sp("H", len(tmp_msg) + 2) + tmp_msg
                    MW_sock_input_queues[MW_ID].append(tmp_msg)

                    REPLICA_log("[SW][IN] <handle_pkt_REPLICA> ",
                                "INIT_PUB_ID reply msg sent.")
                    print "INIT_PUB_ID REPLY msg sent."
                    nxt_variable_global_id += 1
                    #### CUT THE PROCESSED MSG FROM current_data
                    current_data = current_data[int(
                        su("H", current_data[:2])
                        [0]):]  # continue from begining of the next msg

                ### VARIABLE_ID request
                ### (kind = 5)
                elif int(su("H", in_msg[2:4])[0]) == 5:
                    REPLICA_log("[SW][IN] <handle_pkt_REPLICA> ",
                                "Variable_ID request msg received.")
                    print "Variable_ID request msg received."
                    var_name = "".join([su("c", x)[0] for x in in_msg[6:]])
                    if var_name in global_name_variable_id.keys():
                        tmp_msg = in_msg[2:6] + sp(
                            "H",
                            global_name_variable_id[var_name]) + in_msg[6:]
                        tmp_msg = sp("H", len(tmp_msg) + 2) + tmp_msg
                        REPLICA_log("[SW][IN] <handle_pkt_REPLICA> ",
                                    "Variable_ID reply msg sent.")
                        print "Variable_ID reply msg sent."
                    else:
                        tmp_msg = in_msg[2:6] + sp("H", 0) + in_msg[6:]
                        tmp_msg = sp("H", len(tmp_msg) + 2) + tmp_msg
                        REPLICA_log("[SW][IN] <handle_pkt_REPLICA> ",
                                    "Variable_ID reply (ERROR) msg sent.")
                        print "Variable_ID reply (ERROR) msg sent."
                    MW_sock_input_queues[MW_ID].append(tmp_msg)
                    #### CUT THE PROCESSED MSG FROM current_data
                    current_data = current_data[int(
                        su("H", current_data[:2])
                        [0]):]  # continue from begining of the next msg

                ### RECOVER msg
                ### (kind = 6)
                elif int(su("H", in_msg[2:4])[0]) == 6:
                    REPLICA_log("[SW][IN] <handle_pkt_REPLICA> ",
                                "RECOVER msg received.")
                    print "RECOVER msg : "
                    print in_msg
                    #### CUT THE PROCESSED MSG FROM current_data
                    current_data = current_data[int(
                        su("H", current_data[:2])
                        [0]):]  # continue from begining of the next msg
        except:
            time.sleep(1)
            pass
def handle_pkt_REPLICA():
    global recv_REPLICA_sock, send_REPLICA_sock
    global nxt_NF_global_id, nxt_variable_global_id
    global NF_pub_global_ids, global_name_NF_id

    while True:
        msg, msg_address = recv_REPLICA_sock.recvfrom(2048)
        REPLICA_log("\n[SW][IN] <handle_pkt_REPLICA> ", " => len({}), kind({})".format(su("H",msg[:2])[0],msg_kind(int(su("H",msg[2:4])[0]))))
        print "\nGot msg => kind({})".format(msg_kind(int(su("H",msg[2:4])[0])))

        ### INIT NF_ID MSG
        ### (kind = 0)
        if int(su("H",msg[2:4])[0])==0:
            REPLICA_log("[SW][IN] <handle_pkt_REPLICA> ", "INIT_NF_ID request msg received.")
            print  "INIT_NF_ID REQUEST msg received."
            global_name_NF_id["".join([su("c", x)[0] for x in msg[8:]])] = nxt_NF_global_id # mapping NF_NAME : NF_ID
            NF_pub_global_ids[nxt_NF_global_id] = []
            tmp_msg = msg[2:6]+sp("H",nxt_NF_global_id)+msg[8:]
            tmp_msg = sp("H",len(tmp_msg)+2) + tmp_msg
            try:
                dest_addr = (msg_address[0],65432)
                sent = send_REPLICA_sock.sendto(tmp_msg, dest_addr)
            except:
                raise

            REPLICA_log("[SW][IN] <handle_pkt_REPLICA> ", "INIT_NF_ID reply msg sent.")
            print "INIT_NF_ID REPLY msg sent."
            nxt_NF_global_id += 1

        ### INIT PUB_ID MSG
        ### (kind = 1)
        elif int(su("H",msg[2:4])[0])==1:
            REPLICA_log("[SW][IN] <handle_pkt_REPLICA> ", "INIT_PUB_ID request msg received.")
            print "INIT_PUB_ID request msg received."
            global_name_variable_id["".join([su("c", x)[0] for x in msg[6:]])] = nxt_variable_global_id
            NF_pub_global_ids[int(su("H",msg[4:6])[0])].append(nxt_variable_global_id)
            tmp_msg = msg[2:6]+sp("H",nxt_variable_global_id)+msg[6:]
            tmp_msg = sp("H",len(tmp_msg)+2) + tmp_msg
            try:
                dest_addr = (msg_address[0],65432)
                sent = send_REPLICA_sock.sendto(tmp_msg, dest_addr)
            except:
                raise

            REPLICA_log("[SW][IN] <handle_pkt_REPLICA> ", "INIT_PUB_ID reply msg sent.")
            print "INIT_PUB_ID REPLY msg sent."
            nxt_variable_global_id += 1

        ### VARIABLE_ID request
        ### (kind = 5)
        elif int(su("H",msg[2:4])[0])==5:
            REPLICA_log("[SW][IN] <handle_pkt_REPLICA> ","Variable_ID request msg received.")
            print "Variable_ID request msg received."
            var_name = "".join([su("c", x)[0] for x in msg[6:]])
            if var_name in global_name_variable_id.keys():
                tmp_msg = msg[2:6]+sp("H",global_name_variable_id[var_name])+msg[6:]
                tmp_msg = sp("H",len(tmp_msg)+2) + tmp_msg
                REPLICA_log("[SW][IN] <handle_pkt_REPLICA> ","Variable_ID reply msg sent.")
                print "Variable_ID reply msg sent."
            else:
                tmp_msg = msg[2:6]+sp("H", 0)+msg[6:]
                tmp_msg = sp("H",len(tmp_msg)+2) + tmp_msg
                REPLICA_log("[SW][IN] <handle_pkt_REPLICA> ","Variable_ID reply (ERROR) msg sent.")
                print "Variable_ID reply (ERROR) msg sent."
            try:
                dest_addr = (msg_address[0],65432)
                sent = send_REPLICA_sock.sendto(tmp_msg, dest_addr)
            except:
                raise

        ### RECOVER msg
        ### (kind = 6)
        elif int(su("H",msg[2:4])[0])==6:
            REPLICA_log("[SW][IN] <handle_pkt_REPLICA> ", "RECOVER msg received.")
            print "RECOVER msg : \n"
            print msg
def recv_data_middleware():
    global NF_local_global_id, NF_local_global_id, NF_publishes, NF_sock_input_queues, rcv_mw_sock
    global NF_subscriptions, rcv_mw_socks
    # Create the socket
    rcv_mw_socks[0] = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    rcv_mw_socks[0].setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)

    # Bind to the server address
    rcv_mw_socks[0].bind(('', 65432))
    while True:

        data, addr_uni = rcv_mw_socks[0].recvfrom(2048)
        mid_log(
            "[SW][IN]  <recv_data_middleware>", "MSG len({}), kind({})".format(
                su("H", data[:2])[0],
                su("H", data[2:4])[0]))
        print "[SW][IN] <recv_data_middleware>      => len({}), kind({})".format(
            su("H", data[:2])[0],
            su("H", data[2:4])[0])

        ### INIT NF_ID REPLY
        if int(su("H", data[2:4])[0]) == 0:
            mid_log("\n[INFO] <recv_data_middleware>", "INIT NF_ID REPLY:len({}), kind({}), l_ID({}), G_id({})\n"\
                    .format(int(su("H", data[:2])[0]),int(su("H", data[2:4])[0]),int(su("H", data[4:6])[0]),int(su("H", data[6:8])[0])))

            print "\n[INFO] <recv_data_middleware> "
            print "INIT NF_ID REPLY: len({}), kind({}), l_ID({}), G_id({})\n".format(
                int(su("H", data[:2])[0]), int(su("H", data[2:4])[0]),
                int(su("H", data[4:6])[0]), int(su("H", data[6:8])[0]))

            NF_local_global_id[int(su("H", data[4:6])[0])] = int(
                su("H",
                   data[6:8])[0])  # update the mapping of Local_ID : Global_ID
            print "\n[INFO] <recv_data_middleware> NF_local_global_id: {} \n".format(
                NF_local_global_id)
            NF_sock_input_queues[int(su("H", data[4:6])[0])].append(
                data
            )  ## append msg to the input queue of the related NF socket

        ### INIT PUB_ID REPLY
        elif int(su("H", data[2:4])[0]) == 1:
            mid_log("\n[INFO] <recv_data_middleware>", "INIT PUB_ID REPLY:len({}), kind({}), G_id({})\n"\
                    .format(int(su("H", data[:2])[0]),int(su("H", data[2:4])[0]),int(su("H", data[4:6])[0])))

            print "\n[INFO] <recv_data_middleware> "
            print "INIT PUB_ID REPLY: len({}), kind({}), G_id({})\n".format(
                int(su("H", data[:2])[0]), int(su("H", data[2:4])[0]),
                int(su("H", data[4:6])[0]))

            tmp_var_ID = int(su("H",
                                data[6:8])[0])  # extracting the Variable_ID
            ## append var_ID to the published list of the related NF socket
            if int(su("H", data[4:6])[0]) in NF_publishes.keys():
                NF_publishes[int(su("H", data[4:6])[0])].append(tmp_var_ID)
            else:
                NF_publishes[int(su("H", data[4:6])[0])] = [tmp_var_ID]
            print "\n[INFO] <recv_data_middleware> check the NF_publishes: ", NF_publishes
            print "INIT PUB_ID REPLY: len({}), kind({}), G_id({})\n".format(
                int(su("H", data[:2])[0]), int(su("H", data[2:4])[0]),
                int(su("H", data[4:6])[0]))

            ## append msg to the input queue of the related NF socket
            NF_sock_input_queues[NF_local_global_id.index(
                int(su("H", data[4:6])[0]))].append(data)

        ### PUBLISH
        elif int(su("H", data[2:4])[0]) == 2:
            mid_log(
                "[SW][IN]  <rcv_mw_sock({})>".format(
                    str(int(su("H", data[6:8])[0]))), "PUBLISH packet")
            if int(su("H", data[6:8])[0]) in NF_subscriptions.keys(
            ):  # if there is internal subscriptions on this Variable_ID
                for dest in NF_subscriptions[int(su("H", data[6:8])[0])]:

                    print "\n[INFO] <rcv_mw_sock({})>".format(
                        str(int(su("H", data[6:8])[0])))
                    print "PUBLISH : len({}), kind({}), G_id({})".format(
                        int(su("H", data[:2])[0]), int(su("H", data[2:4])[0]),
                        int(su("H", data[4:6])[0]))
                    ## msg = Data_length(2B)+Kind(2B)+Global_ID(4B)+tot_var(2B)
                    msg_copy = copy.deepcopy(data)
                    NF_sock_input_queues[NF_local_global_id.index(
                        dest)].append(msg_copy)

        ### VARIABLE_ID_REPLY
        elif int(su("H", data[2:4])[0]) == 5:
            mid_log("\n[INFO]    <recv_data_middleware>", "VARIABLE_ID_REPLY:len({}), kind({}), G_id({}), Var_ID({})\n"\
            .format(int(su("H", data[:2])[0]),int(su("H", data[2:4])[0]), int(su("H", data[4:6])[0]),int(su("H", data[6:8])[0])))

            print "\n[INFO] <recv_data_middleware>"
            print "VARIABLE_ID_REPLY: len({}), kind({}), G_id({}), v_ID({})\n".format(
                int(su("H", data[:2])[0]), int(su("H", data[2:4])[0]),
                int(su("H", data[4:6])[0]), int(su("H", data[6:8])[0]))

            ## append msg to the input queue of the related NF socket
            NF_sock_input_queues[NF_local_global_id.index(
                int(su("H", data[4:6])[0]))].append(data)
def main(p4info_file_path, bmv2_file_path, sw_num):
    # Instantiate a P4Runtime helper from the p4info file
    p4info_helper = helper.P4InfoHelper(p4info_file_path)
    s_name = 's' + str(sw_num)
    print "switch name: ", s_name

    try:
        '''
         Create a switch connection object for s1
         This is backed by a P4Runtime gRPC connection.
         Also, dump all P4Runtime messages sent to switch to given txt files.
         In the P4 package here, port no starts from 50051
        '''
        switch = p4runtime_lib.bmv2.Bmv2SwitchConnection(
            name=s_name,
            address='127.0.0.1:5005' + str(sw_num),
            device_id=0,
            proto_dump_file='logs/' + s_name + '-p4runtime-requests.txt')
        '''
         Send master arbitration update message to establish this controller as
         master (required by P4Runtime before performing any other write operation)
        '''
        switch.MasterArbitrationUpdate()
        print "Master arbitration done..."
        #         readTableRules(p4info_helper, switch, None)
        #         print
        print "<<<< CONTROLLER READEY! WAITING FOR PACKET_IN >>>>"
        while True:
            ''' USING P4RUNTIME AS RECEIVER FOR PACKET_IN IN THE EMBEDDED CONTROLLER '''
            packetin = switch.PacketIn()
            if packetin.WhichOneof('update') == 'packet':
                packet, pkt_in_metadata, input_port, var_id = packet_in_metadata(
                    packetin)
                input_port_mask = port_mask(input_port)

                print ">>>> Reading current entries in \"MyIngress.L2_publish\" table before updating >>>>"
                current_table = readTableRules(p4info_helper, switch,
                                               "MyIngress.L2_publish")
                print

                if len(current_table) == 0:
                    try:
                        writeL2Publish(p4info_helper, switch, var_id,
                                       input_port_mask)
                    except:
                        print "\nproblem writing table for them!\n"
                        raise
                else:
                    found = False
                    for tbl_entry in current_table:
                        tmp_match_value = '\x00' + tbl_entry['match'][
                            'local_metadata.pubsub_indx'][0]
                        if su("!I", tmp_match_value)[0] == var_id:
                            old_mc_grp_id = su(
                                "!H", tbl_entry['action_params']['st_mc_grp'])
                            new_mc_grp_id = int(
                                old_mc_grp_id[0]) | input_port_mask
                            try:
                                ## DELETE and WRITE
                                print "MODIFY(Delete and Write) for ", var_id, " from ", old_mc_grp_id, " to ", new_mc_grp_id
                                deleteL2Publish(p4info_helper, switch, var_id,
                                                old_mc_grp_id)
                                writeL2Publish(p4info_helper, switch, var_id,
                                               new_mc_grp_id)
                                ## OR MODIFY
                                # modifyL2Publish(p4info_helper, switch, var_id, new_mc_grp_id)
                            except:
                                print "\nproblem writing table for them!\n"
                                # raise
                            found = True
                            break
                    if not found:
                        try:
                            writeL2Publish(p4info_helper, switch, var_id,
                                           input_port_mask)
                        except:
                            print "\nproblem writing table for them!\n"
                            raise
            '''
            USING P4RUNTIME FOR PACKET_OUT IN THE CONTROLLER
            '''
            my_packet_out = p4info_helper.buildPacketOut(
                payload=packet, metadata={1: pkt_in_metadata[1]})
            print "Donig PacketOut.\n"
            # my_packet_out_dup=copy.deepcopy(my_packet_out)
            # switch.PacketOut(my_packet_out_dup)
            switch.PacketOut(my_packet_out)
            print "PacketOut Done."
            print "==============================================\n"

    except KeyboardInterrupt:
        # using ctrl + c to exit
        # Then close all the connections
        print " Shutting down....."
    except grpc.RpcError as e:
        printGrpcError(e)

    ShutdownAllSwitchConnections()