import json
from ryu import cfg
from ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller.handler import MAIN_DISPATCHER, CONFIG_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.ofproto import ofproto_v1_0, ofproto_v1_3
from ryu.lib.packet import packet
from ryu.lib.packet import ethernet
from ryu.lib.packet import ipv4
from ryu.lib.packet import ether_types
from ryu.lib.packet import arp
from ryu.lib.ofp_pktinfilter import packet_in_filter, RequiredTypeFilter
from .conf_mgr import SDNIPConfigManager

CONF = cfg.CONF
CONF.register_cli_opts([
    cfg.StrOpt('static-arp-table',
               default=None,
               help='location of SDN-IP config file')
])


class ArpProxy(app_manager.RyuApp):
    OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]

    def __init__(self, *args, **kwargs):
        super(ArpProxy, self).__init__(*args, **kwargs)
        self.cfg_mgr = SDNIPConfigManager()
        self.arp_table = {}

        if CONF.static_arp_table is not None:
            # load static arp table
            static_arp_table = json.load(open(CONF.static_arp_table, "r"))

            for record in static_arp_table:
                self.arp_table.setdefault(record['ip'], record['mac'])

    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    @packet_in_filter(RequiredTypeFilter, {'types': [ipv4.ipv4]})
    def ipv4_packet_in_handler(self, ev):
        msg = ev.msg
        pkt = packet.Packet(msg.data)
        eth_header = pkt.get_protocol(ethernet.ethernet)
        ipv4_header = pkt.get_protocol(ipv4.ipv4)
        self.arp_table.setdefault(ipv4_header.src, eth_header.src)

    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    @packet_in_filter(RequiredTypeFilter, {'types': [arp.arp]})
    def arp_packet_in_handler(self, ev):
        msg = ev.msg
        datapath = msg.datapath
        in_port = msg.match['in_port']
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser
        pkt = packet.Packet(msg.data)
        arp_header = pkt.get_protocol(arp.arp)
        src_ip = arp_header.src_ip
        src_mac = arp_header.src_mac
        dst_ip = arp_header.dst_ip
        dst_mac = None

        if self.cfg_mgr.is_internal_host(src_ip):
            # if internal host sent arp request, reply router mac address
            first_speaker_id = None

            if self.cfg_mgr.get_all_speaker_id():
                first_speaker_id = self.cfg_mgr.get_all_speaker_id()[0]

            if first_speaker_id:
                dst_mac = self.cfg_mgr.get_speaker_mac(first_speaker_id)
        else:
            dst_mac = self.arp_table.get(dst_ip)

        self.arp_table.setdefault(src_ip, src_mac)

        if arp_header.opcode != arp.ARP_REQUEST:
            return

        if not dst_mac:
            # can't find distination, drop it
            return

        # send arp request to host
        actions = [parser.OFPActionOutput(in_port)]
        arp_reply = packet.Packet()
        arp_reply.add_protocol(
            ethernet.ethernet(
                ethertype=ether_types.ETH_TYPE_ARP,
                src=dst_mac,
                dst=src_mac
            )
        )
        arp_reply.add_protocol(
            arp.arp(
                opcode=arp.ARP_REPLY,
                src_ip=dst_ip,
                src_mac=dst_mac,
                dst_ip=src_ip,
                dst_mac=src_mac
            )
        )
        arp_reply.serialize()

        out = parser.OFPPacketOut(
            datapath=datapath,
            buffer_id=ofproto.OFP_NO_BUFFER,
            in_port=ofproto.OFPP_CONTROLLER,
            actions=actions, data=arp_reply.data)
        datapath.send_msg(out)

@@ -712,16 +712,165 @@
 e')%0A%5D)%0A%0A
+# integrate with DragonKnight CLI%0Awith_dk = False%0Atry:%0A    from dragon_knight import dk_plugin%0A    with_dk = True%0Aexcept ImportError as e:%0A    pass%0A%0A
 %0Aclass A
@@ -1389,16 +1389,244 @@
 mac'%5D)%0A%0A
+        if with_dk:%0A            dk_plugin.DynamicLoader.register_custom_cmd('arp-proxy:table', self.cmd_dump_arp_table)%0A            dk_plugin.DynamicLoader.register_custom_cmd('arp-proxy:reload_static', self.cmd_reload_static)%0A%0A
     @set
@@ -4064,8 +4064,599 @@
 sg(out)%0A
+%0A    def cmd_dump_arp_table(self):%0A        result = %22%7B:%3C17%7D%7B:%3C17%7D%5Cn%22.format(%22IP%22, %22Mac%22)%0A%0A        for record in self.arp_table.items():%0A            result = result + %22%7B:%3C17%7D%7B:%3C17%7D%5Cn%22.format(record%5B0%5D, record%5B1%5D)%0A%0A        return result%0A%0A    def cmd_reload_static(self):%0A        result = %22%22%0A        self.arp_table = %7B%7D%0A%0A        if CONF.static_arp_table is not None:%0A            # load static arp table%0A            static_arp_table = json.load(open(CONF.static_arp_table, %22r%22))%0A%0A            for record in static_arp_table:%0A                self.arp_table.setdefault(record%5B'ip'%5D, record%5B'mac'%5D)%0A

Add two commands