[ONOS-7606] Support ARP broadcast (VxLAN) to handle CP failure

Change-Id: Ia0bccf6abaad3e074f2d86a511d5930974743b43
diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/Constants.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
index 131b339..136c12e 100644
--- a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
+++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
@@ -27,7 +27,11 @@
 
     public static final String OPENSTACK_NETWORKING_APP_ID = "org.onosproject.openstacknetworking";
 
+    public static final String ARP_BROADCAST_MODE = "broadcast";
+    public static final String ARP_PROXY_MODE = "proxy";
+
     public static final String DEFAULT_GATEWAY_MAC_STR = "fe:00:00:00:00:02";
+    public static final String DEFAULT_ARP_MODE_STR = ARP_BROADCAST_MODE;
     public static final MacAddress DEFAULT_GATEWAY_MAC = MacAddress.valueOf(DEFAULT_GATEWAY_MAC_STR);
     public static final MacAddress DEFAULT_EXTERNAL_ROUTER_MAC = MacAddress.valueOf("fe:00:00:00:00:01");
 
@@ -48,7 +52,13 @@
     public static final int PRIORITY_CT_HOOK_RULE = 30500;
     public static final int PRIORITY_CT_RULE = 32000;
     public static final int PRIORITY_CT_DROP_RULE = 32500;
+    public static final int PRIORITY_ARP_GATEWAY_RULE = 41000;
+    public static final int PRIORITY_ARP_SUBNET_RULE = 40000;
+    public static final int PRIORITY_ARP_CONTROL_RULE = 40000;
+    public static final int PRIORITY_ARP_REPLY_RULE = 40000;
+    public static final int PRIORITY_ARP_REQUEST_RULE = 40000;
 
+    public static final int DHCP_ARP_TABLE = 0;
     public static final int SRC_VNI_TABLE = 0;
     public static final int ACL_TABLE = 1;
     public static final int CT_TABLE = 2;
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
index e9ac923..b3340c6 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
@@ -32,22 +32,36 @@
 import org.onlab.packet.MacAddress;
 import org.onlab.util.Tools;
 import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.packet.DefaultOutboundPacket;
 import org.onosproject.net.packet.PacketContext;
-import org.onosproject.net.packet.PacketPriority;
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketService;
 import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortEvent;
+import org.onosproject.openstacknetworking.api.InstancePortListener;
 import org.onosproject.openstacknetworking.api.InstancePortService;
+import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeEvent;
+import org.onosproject.openstacknode.api.OpenstackNodeListener;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.NetworkType;
 import org.openstack4j.model.network.Subnet;
 import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
@@ -55,11 +69,24 @@
 
 import java.nio.ByteBuffer;
 import java.util.Dictionary;
+import java.util.Objects;
 import java.util.Set;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.openstacknetworking.api.Constants.ARP_BROADCAST_MODE;
+import static org.onosproject.openstacknetworking.api.Constants.ARP_PROXY_MODE;
+import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_ARP_MODE_STR;
 import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
+import static org.onosproject.openstacknetworking.api.Constants.DHCP_ARP_TABLE;
 import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
+import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_CONTROL_RULE;
+import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
+import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_REPLY_RULE;
+import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_REQUEST_RULE;
+import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_SUBNET_RULE;
+import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildExtension;
+import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
+import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
 
 /**
  * Handles ARP packet from VMs.
@@ -70,6 +97,7 @@
     private final Logger log = LoggerFactory.getLogger(getClass());
 
     private static final String GATEWAY_MAC = "gatewayMac";
+    private static final String ARP_MODE = "arpMode";
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     CoreService coreService;
@@ -78,33 +106,72 @@
     PacketService packetService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    OpenstackFlowRuleService osFlowRuleService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     ComponentConfigService configService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    ClusterService clusterService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    LeadershipService leadershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    DeviceService deviceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    MastershipService mastershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     InstancePortService instancePortService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     OpenstackNetworkService osNetworkService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNodeService osNodeService;
+
     @Property(name = GATEWAY_MAC, value = DEFAULT_GATEWAY_MAC_STR,
             label = "Fake MAC address for virtual network subnet gateway")
     private String gatewayMac = DEFAULT_GATEWAY_MAC_STR;
 
+    @Property(name = ARP_MODE, value = DEFAULT_ARP_MODE_STR,
+            label = "ARP processing mode, broadcast (default) | proxy ")
+    protected String arpMode = DEFAULT_ARP_MODE_STR;
+
     private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
     private final InternalOpenstackNetworkListener osNetworkListener =
             new InternalOpenstackNetworkListener();
+    private final InstancePortListener instancePortListener = new InternalInstancePortListener();
+    private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
+
     private final Set<IpAddress> gateways = Sets.newConcurrentHashSet();
 
     private ApplicationId appId;
+    private NodeId localNodeId;
 
     @Activate
     void activate() {
         appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
         configService.registerProperties(getClass());
+        localNodeId = clusterService.getLocalNode().id();
         osNetworkService.addListener(osNetworkListener);
+        osNodeService.addListener(osNodeListener);
+        leadershipService.runForLeadership(appId.name());
         packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
-        osNetworkService.subnets().forEach(this::addSubnetGateway);
-        requestPacket();
+
+        instancePortService.addListener(instancePortListener);
+
+        osNetworkService.networks().forEach(n -> {
+            if (n.getNetworkType() != NetworkType.FLAT) {
+                osNetworkService.subnets().forEach(s -> {
+                    if (s.getNetworkId().equals(n.getId())) {
+                        addSubnetGateway(s);
+                    }
+                });
+            }
+        });
 
         log.info("Started");
     }
@@ -113,6 +180,9 @@
     void deactivate() {
         packetService.removeProcessor(packetProcessor);
         osNetworkService.removeListener(osNetworkListener);
+        osNodeService.removeListener(osNodeListener);
+        instancePortService.removeListener(instancePortListener);
+        leadershipService.withdraw(appId.name());
         configService.unregisterProperties(getClass(), false);
 
         log.info("Stopped");
@@ -128,20 +198,16 @@
             gatewayMac = updatedMac;
         }
 
+        String updateArpMode;
+
+        updateArpMode = Tools.get(properties, ARP_MODE);
+        if (!Strings.isNullOrEmpty(updateArpMode) && !updateArpMode.equals(arpMode)) {
+            arpMode = updateArpMode;
+        }
+
         log.info("Modified");
     }
 
-    private void requestPacket() {
-        TrafficSelector selector = DefaultTrafficSelector.builder()
-                .matchEthType(EthType.EtherType.ARP.ethType().toShort())
-                .build();
-
-        packetService.requestPackets(
-                selector,
-                PacketPriority.CONTROL,
-                appId);
-    }
-
     private void addSubnetGateway(Subnet osSubnet) {
         if (Strings.isNullOrEmpty(osSubnet.getGateway())) {
             return;
@@ -169,6 +235,12 @@
      * @param ethPacket ethernet packet
      */
     private void processPacketIn(PacketContext context, Ethernet ethPacket) {
+
+        // if the ARP mode is configured as broadcast mode, we simply ignore ARP packet_in
+        if (arpMode.equals(ARP_BROADCAST_MODE)) {
+            return;
+        }
+
         ARP arpPacket = (ARP) ethPacket.getPayload();
         if (arpPacket.getOpCode() != ARP.OP_REQUEST) {
             return;
@@ -224,6 +296,10 @@
         }
     }
 
+    /**
+     * An internal packet processor which processes ARP request, and results in
+     * packet-out ARP reply.
+     */
     private class InternalPacketProcessor implements PacketProcessor {
 
         @Override
@@ -240,6 +316,11 @@
         }
     }
 
+    /**
+     * An internal network listener which listens to openstack network event,
+     * manages the gateway collection and installs flow rule that handles
+     * ARP request in data plane.
+     */
     private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
 
         @Override
@@ -248,6 +329,23 @@
             if (osSubnet == null) {
                 return false;
             }
+
+            Network network = osNetworkService.network(event.port().getNetworkId());
+            if (network == null) {
+                log.warn("Network is not specified.");
+                return false;
+            } else {
+                if (network.getNetworkType().equals(NetworkType.FLAT)) {
+                    return false;
+                }
+            }
+
+            // do not allow to proceed without leadership
+            NodeId leader = leadershipService.getLeader(appId.name());
+            if (!Objects.equals(localNodeId, leader)) {
+                return false;
+            }
+
             return !Strings.isNullOrEmpty(osSubnet.getGateway());
         }
 
@@ -257,9 +355,11 @@
                 case OPENSTACK_SUBNET_CREATED:
                 case OPENSTACK_SUBNET_UPDATED:
                     addSubnetGateway(event.subnet());
+                    setFakeGatewayArpRule(event.subnet(), true);
                     break;
                 case OPENSTACK_SUBNET_REMOVED:
                     removeSubnetGateway(event.subnet());
+                    setFakeGatewayArpRule(event.subnet(), false);
                     break;
                 case OPENSTACK_NETWORK_CREATED:
                 case OPENSTACK_NETWORK_UPDATED:
@@ -272,5 +372,286 @@
                     break;
             }
         }
+
+        /**
+         * Installs flow rules which convert ARP request packet into ARP reply
+         * by adding a fake gateway MAC address as Source Hardware Address.
+         *
+         * @param osSubnet  openstack subnet
+         * @param install   flag which indicates whether to install rule or remove rule
+         */
+        private void setFakeGatewayArpRule(Subnet osSubnet, boolean install) {
+
+            if (arpMode.equals(ARP_BROADCAST_MODE)) {
+                String gateway = osSubnet.getGateway();
+
+                TrafficSelector selector = DefaultTrafficSelector.builder()
+                        .matchEthType(EthType.EtherType.ARP.ethType().toShort())
+                        .matchArpOp(ARP.OP_REQUEST)
+                        .matchArpTpa(Ip4Address.valueOf(gateway))
+                        .build();
+
+                TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                        .setArpOp(ARP.OP_REPLY)
+                        .setArpSha(MacAddress.valueOf(gatewayMac))
+                        .setArpSpa(Ip4Address.valueOf(gateway))
+                        .setOutput(PortNumber.IN_PORT)
+                        .build();
+
+                osNodeService.completeNodes(COMPUTE).forEach(n ->
+                    osFlowRuleService.setRule(
+                            appId,
+                            n.intgBridge(),
+                            selector,
+                            treatment,
+                            PRIORITY_ARP_GATEWAY_RULE,
+                            DHCP_ARP_TABLE,
+                            install
+                    )
+                );
+            }
+        }
+    }
+
+    /**
+     * An internal openstack node listener which is used for listening openstack
+     * node activity. As long as a node is in complete state, we will install
+     * default ARP rule to handle ARP request.
+     */
+    private class InternalNodeEventListener implements OpenstackNodeListener {
+
+        @Override
+        public boolean isRelevant(OpenstackNodeEvent event) {
+            // do not allow to proceed without leadership
+            NodeId leader = leadershipService.getLeader(appId.name());
+            return Objects.equals(localNodeId, leader);
+        }
+
+        @Override
+        public void event(OpenstackNodeEvent event) {
+            OpenstackNode osNode = event.subject();
+            switch (event.type()) {
+                case OPENSTACK_NODE_COMPLETE:
+                    setDefaultArpRule(osNode, true);
+                    break;
+                case OPENSTACK_NODE_INCOMPLETE:
+                    setDefaultArpRule(osNode, false);
+                    break;
+                case OPENSTACK_NODE_CREATED:
+                case OPENSTACK_NODE_UPDATED:
+                case OPENSTACK_NODE_REMOVED:
+                default:
+                    break;
+            }
+        }
+
+        private void setDefaultArpRule(OpenstackNode openstackNode, boolean install) {
+            if (openstackNode.type().equals(GATEWAY)) {
+                return;
+            }
+
+            if (arpMode.equals(ARP_PROXY_MODE)) {
+                TrafficSelector selector = DefaultTrafficSelector.builder()
+                        .matchEthType(EthType.EtherType.ARP.ethType().toShort())
+                        .build();
+
+                TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                        .punt()
+                        .build();
+
+                osFlowRuleService.setRule(
+                        appId,
+                        openstackNode.intgBridge(),
+                        selector,
+                        treatment,
+                        PRIORITY_ARP_CONTROL_RULE,
+                        DHCP_ARP_TABLE,
+                        install
+                );
+            } else if (arpMode.equals(ARP_BROADCAST_MODE)) {
+                TrafficSelector selector = DefaultTrafficSelector.builder()
+                        .matchEthType(EthType.EtherType.ARP.ethType().toShort())
+                        .matchArpOp(ARP.OP_REQUEST)
+                        .build();
+
+                TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                        .setOutput(PortNumber.FLOOD)
+                        .build();
+
+                osFlowRuleService.setRule(
+                        appId,
+                        openstackNode.intgBridge(),
+                        selector,
+                        treatment,
+                        PRIORITY_ARP_SUBNET_RULE,
+                        DHCP_ARP_TABLE,
+                        install
+                );
+            } else {
+                log.warn("Invalid ARP mode {}. Please use either broadcast or proxy mode.", arpMode);
+            }
+        }
+    }
+
+    /**
+     * An internal instance port listener which listens the port events generated
+     * from VM. When ARP a host which located in a remote compute node, we specify
+     * both ARP OP mode as REQUEST and Target Protocol Address (TPA) with
+     * host IP address. When ARP a host which located in a local compute node,
+     * we specify only ARP OP mode as REQUEST.
+     */
+    private class InternalInstancePortListener implements InstancePortListener {
+
+        @Override
+        public boolean isRelevant(InstancePortEvent event) {
+
+            if (arpMode.equals(ARP_PROXY_MODE)) {
+                return false;
+            }
+
+            InstancePort instPort = event.subject();
+            return mastershipService.isLocalMaster(instPort.deviceId());
+        }
+
+        @Override
+        public void event(InstancePortEvent event) {
+            switch (event.type()) {
+                case OPENSTACK_INSTANCE_PORT_UPDATED:
+                case OPENSTACK_INSTANCE_PORT_DETECTED:
+                    setArpRequestRule(event.subject(), true);
+                    setArpReplyRule(event.subject(), true);
+                    break;
+                case OPENSTACK_INSTANCE_PORT_VANISHED:
+                    setArpRequestRule(event.subject(), false);
+                    setArpReplyRule(event.subject(), false);
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        /**
+         * Installs flow rules to match ARP request packets.
+         *
+         * @param port      instance port
+         * @param install   installation flag
+         */
+        private void setArpRequestRule(InstancePort port, boolean install) {
+            NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
+
+            switch (type) {
+                case VXLAN:
+                    setRemoteArpRequestRuleForVxlan(port, install);
+                    break;
+                case VLAN:
+                    // TODO: handle VLAN differently
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        /**
+         * Installs flow rules to match ARP reply packets.
+         *
+         * @param port      instance port
+         * @param install   installation flag
+         */
+        private void setArpReplyRule(InstancePort port, boolean install) {
+            NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
+
+            switch (type) {
+                case VXLAN:
+                    setArpReplyRuleVxlan(port, install);
+                    break;
+                case VLAN:
+                    // TODO: handle VLAN differently
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        /**
+         * Installs flow rules to match ARP request packets only for VxLAN.
+         *
+         * @param port      instance port
+         * @param install   installation flag
+         */
+        private void setRemoteArpRequestRuleForVxlan(InstancePort port, boolean install) {
+
+            OpenstackNode localNode = osNodeService.node(port.deviceId());
+
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchEthType(EthType.EtherType.ARP.ethType().toShort())
+                    .matchArpOp(ARP.OP_REQUEST)
+                    .matchArpTpa(port.ipAddress().getIp4Address())
+                    .build();
+
+            setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
+        }
+
+        /**
+         * Installs flow rules to match ARP reply packets only for VxLAN.
+         *
+         * @param port      instance port
+         * @param install   installation flag
+         */
+        private void setArpReplyRuleVxlan(InstancePort port, boolean install) {
+
+            OpenstackNode localNode = osNodeService.node(port.deviceId());
+
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchEthType(EthType.EtherType.ARP.ethType().toShort())
+                    .matchArpOp(ARP.OP_REPLY)
+                    .matchArpTpa(port.ipAddress().getIp4Address())
+                    .matchArpTha(port.macAddress())
+                    .build();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setOutput(port.portNumber())
+                    .build();
+
+            osFlowRuleService.setRule(
+                    appId,
+                    port.deviceId(),
+                    selector,
+                    treatment,
+                    PRIORITY_ARP_REPLY_RULE,
+                    DHCP_ARP_TABLE,
+                    install
+            );
+
+            setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
+        }
+
+        // a helper method
+        private void setRemoteArpTreatmentForVxlan(TrafficSelector selector,
+                                                   InstancePort port,
+                                                   OpenstackNode localNode,
+                                                   boolean install) {
+            for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
+                if (!remoteNode.intgBridge().equals(port.deviceId())) {
+                    TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
+                            .extension(buildExtension(
+                                    deviceService,
+                                    remoteNode.intgBridge(),
+                                    localNode.dataIp().getIp4Address()),
+                                    remoteNode.intgBridge())
+                            .setOutput(remoteNode.tunnelPortNum())
+                            .build();
+
+                    osFlowRuleService.setRule(
+                            appId,
+                            remoteNode.intgBridge(),
+                            selector,
+                            treatmentToRemote,
+                            PRIORITY_ARP_REQUEST_RULE,
+                            DHCP_ARP_TABLE,
+                            install
+                    );
+                }
+            }
+        }
     }
 }
diff --git a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandlerTest.java b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandlerTest.java
index b5074ed..f46d668 100644
--- a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandlerTest.java
+++ b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandlerTest.java
@@ -23,7 +23,11 @@
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onosproject.cfg.ComponentConfigAdapter;
+import org.onosproject.cluster.ClusterServiceAdapter;
+import org.onosproject.cluster.LeadershipServiceAdapter;
+import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.core.DefaultApplicationId;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.packet.DefaultInboundPacket;
@@ -41,6 +45,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 import static org.onosproject.net.NetTestTools.connectPoint;
+import static org.onosproject.openstacknetworking.api.Constants.ARP_PROXY_MODE;
 
 public class OpenstackSwitchingArpHandlerTest {
 
@@ -61,6 +66,11 @@
         arpHandler.instancePortService = new TestInstancePortService();
         arpHandler.packetService = new TestPacketService();
         arpHandler.osNetworkService = new TestOpenstackNetworkService();
+        arpHandler.osNodeService = new TestOpenstackNodeService();
+        arpHandler.osFlowRuleService = new TestOpenstackFlowRuleService();
+        arpHandler.arpMode = ARP_PROXY_MODE;
+        arpHandler.clusterService = new TestClusterService();
+        arpHandler.leadershipService = new TestLeadershipService();
         arpHandler.activate();
     }
 
@@ -132,6 +142,10 @@
      * Mocks the CoreService.
      */
     private class TestCoreService extends CoreServiceAdapter {
+        @Override
+        public ApplicationId registerApplication(String name) {
+            return new DefaultApplicationId(100, "arpTestApp");
+        }
     }
 
     /**
@@ -141,6 +155,30 @@
     }
 
     /**
+     * Mocks the ClusterService.
+     */
+    private class TestClusterService extends ClusterServiceAdapter {
+    }
+
+    /**
+     * Mocks the LeadershipService.
+     */
+    private class TestLeadershipService extends LeadershipServiceAdapter {
+    }
+
+    /**
+     * Mocks the OpenstackNodeService.
+     */
+    private class TestOpenstackNodeService extends OpenstackNodeServiceAdapter {
+    }
+
+    /**
+     * Mocks the OpenstackFlowRuleService.
+     */
+    private class TestOpenstackFlowRuleService extends OpenstackFlowRuleServiceAdapter {
+    }
+
+    /**
      * Mocks the InstancePortService.
      */
     private class TestInstancePortService extends InstancePortServiceAdapter {