Add CLI for reconfiguring ARP mode on openstacknetworking app

Change-Id: I4211681ccf6eaea9c76ec27adc45e0f1cc71d0d7
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 a58053ac..4e6054f 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,6 +32,7 @@
 import org.onlab.packet.MacAddress;
 import org.onlab.util.Tools;
 import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.cfg.ConfigProperty;
 import org.onosproject.cluster.ClusterService;
 import org.onosproject.cluster.LeadershipService;
 import org.onosproject.cluster.NodeId;
@@ -84,6 +85,7 @@
 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.OpenstackNetworkingUtil.getPropertyValue;
 import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildExtension;
 import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
 
@@ -189,24 +191,16 @@
 
     @Modified
     void modified(ComponentContext context) {
-        Dictionary<?, ?> properties = context.getProperties();
-        String updatedMac;
-
-        updatedMac = Tools.get(properties, GATEWAY_MAC);
-        if (!Strings.isNullOrEmpty(updatedMac) && !updatedMac.equals(gatewayMac)) {
-            gatewayMac = updatedMac;
-        }
-
-        String updateArpMode;
-
-        updateArpMode = Tools.get(properties, ARP_MODE);
-        if (!Strings.isNullOrEmpty(updateArpMode) && !updateArpMode.equals(arpMode)) {
-            arpMode = updateArpMode;
-        }
+        readComponentConfiguration(context);
 
         log.info("Modified");
     }
 
+    private String getArpMode() {
+        Set<ConfigProperty> properties = configService.getProperties(this.getClass().getName());
+        return getPropertyValue(properties, ARP_MODE);
+    }
+
     private void addSubnetGateway(Subnet osSubnet) {
         if (Strings.isNullOrEmpty(osSubnet.getGateway())) {
             return;
@@ -236,7 +230,7 @@
     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)) {
+        if (ARP_BROADCAST_MODE.equals(getArpMode())) {
             return;
         }
 
@@ -304,7 +298,7 @@
      */
     private void setFakeGatewayArpRule(Subnet osSubnet, boolean install, OpenstackNode osNode) {
 
-        if (arpMode.equals(ARP_BROADCAST_MODE)) {
+        if (ARP_BROADCAST_MODE.equals(getArpMode())) {
             String gateway = osSubnet.getGateway();
 
             TrafficSelector selector = DefaultTrafficSelector.builder()
@@ -348,6 +342,184 @@
     }
 
     /**
+     * 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:
+                // since VLAN ARP packet can be broadcasted to all hosts that connected with L2 network,
+                // there is no need to add any flow rules to handle ARP request
+                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:
+                setArpReplyRuleForVxlan(port, install);
+                break;
+            case VLAN:
+                setArpReplyRuleForVlan(port, install);
+                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 setArpReplyRuleForVxlan(InstancePort port, boolean install) {
+
+        OpenstackNode localNode = osNodeService.node(port.deviceId());
+
+        TrafficSelector selector = setArpReplyRuleForVnet(port, install);
+        setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
+    }
+
+    /**
+     * Installs flow rules to match ARP reply packets only for VLAN.
+     *
+     * @param port      instance port
+     * @param install   installation flag
+     */
+    private void setArpReplyRuleForVlan(InstancePort port, boolean install) {
+
+        TrafficSelector selector = setArpReplyRuleForVnet(port, install);
+        setRemoteArpTreatmentForVlan(selector, port, install);
+    }
+
+    // a helper method
+    private TrafficSelector setArpReplyRuleForVnet(InstancePort port, boolean install) {
+        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
+        );
+
+        return selector;
+    }
+
+    // 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
+                );
+            }
+        }
+    }
+
+    // a helper method
+    private void setRemoteArpTreatmentForVlan(TrafficSelector selector,
+                                              InstancePort port,
+                                              boolean install) {
+        for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
+            if (!remoteNode.intgBridge().equals(port.deviceId()) && remoteNode.vlanIntf() != null) {
+                TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
+                        .setOutput(remoteNode.vlanPortNum())
+                        .build();
+
+                osFlowRuleService.setRule(
+                        appId,
+                        remoteNode.intgBridge(),
+                        selector,
+                        treatmentToRemote,
+                        PRIORITY_ARP_REQUEST_RULE,
+                        DHCP_ARP_TABLE,
+                        install);
+            }
+        }
+    }
+
+    /**
+     * Extracts properties from the component configuration context.
+     *
+     * @param context the component context
+     */
+    private void readComponentConfiguration(ComponentContext context) {
+        Dictionary<?, ?> properties = context.getProperties();
+
+        String updatedMac = Tools.get(properties, GATEWAY_MAC);
+        gatewayMac = updatedMac != null ? updatedMac : DEFAULT_GATEWAY_MAC_STR;
+        log.info("Configured. Gateway MAC is {}", gatewayMac);
+    }
+
+    /**
      * An internal packet processor which processes ARP request, and results in
      * packet-out ARP reply.
      */
@@ -446,9 +618,11 @@
             switch (event.type()) {
                 case OPENSTACK_NODE_COMPLETE:
                     setDefaultArpRule(osNode, true);
+                    setAllArpRules(true);
                     break;
                 case OPENSTACK_NODE_INCOMPLETE:
                     setDefaultArpRule(osNode, false);
+                    setAllArpRules(false);
                     break;
                 default:
                     break;
@@ -456,7 +630,7 @@
         }
 
         private void setDefaultArpRule(OpenstackNode osNode, boolean install) {
-            switch (arpMode) {
+            switch (getArpMode()) {
                 case ARP_PROXY_MODE:
                     setDefaultArpRuleForProxyMode(osNode, install);
                     break;
@@ -469,13 +643,13 @@
                     osNetworkService.subnets().stream().filter(subnet ->
                             osNetworkService.network(subnet.getNetworkId()) != null &&
                                     osNetworkService.network(subnet.getNetworkId())
-                                            .getNetworkType() == NetworkType.FLAT)
+                                            .getNetworkType() != NetworkType.FLAT)
                                             .forEach(subnet ->
                         setFakeGatewayArpRule(subnet, install, osNode));
                     break;
                 default:
                     log.warn("Invalid ARP mode {}. Please use either " +
-                            "broadcast or proxy mode.", arpMode);
+                            "broadcast or proxy mode.", getArpMode());
                     break;
             }
         }
@@ -520,6 +694,15 @@
                     install
             );
         }
+
+        private void setAllArpRules(boolean install) {
+            if (ARP_BROADCAST_MODE.equals(getArpMode())) {
+                instancePortService.instancePorts().forEach(p -> {
+                    setArpRequestRule(p, install);
+                    setArpReplyRule(p, install);
+                });
+            }
+        }
     }
 
     /**
@@ -534,7 +717,7 @@
         @Override
         public boolean isRelevant(InstancePortEvent event) {
 
-            if (arpMode.equals(ARP_PROXY_MODE)) {
+            if (ARP_PROXY_MODE.equals(getArpMode())) {
                 return false;
             }
 
@@ -561,170 +744,5 @@
                     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:
-                    // since VLAN ARP packet can be broadcasted to all hosts that connected with L2 network,
-                    // there is no need to add any flow rules to handle ARP request
-                    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:
-                    setArpReplyRuleForVxlan(port, install);
-                    break;
-                case VLAN:
-                    setArpReplyRuleForVlan(port, install);
-                    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 setArpReplyRuleForVxlan(InstancePort port, boolean install) {
-
-            OpenstackNode localNode = osNodeService.node(port.deviceId());
-
-            TrafficSelector selector = setArpReplyRuleForVnet(port, install);
-            setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
-        }
-
-        /**
-         * Installs flow rules to match ARP reply packets only for VLAN.
-         *
-         * @param port      instance port
-         * @param install   installation flag
-         */
-        private void setArpReplyRuleForVlan(InstancePort port, boolean install) {
-
-            TrafficSelector selector = setArpReplyRuleForVnet(port, install);
-            setRemoteArpTreatmentForVlan(selector, port, install);
-        }
-
-        // a helper method
-        private TrafficSelector setArpReplyRuleForVnet(InstancePort port, boolean install) {
-            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
-            );
-
-            return selector;
-        }
-
-        // 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
-                    );
-                }
-            }
-        }
-
-        // a helper method
-        private void setRemoteArpTreatmentForVlan(TrafficSelector selector,
-                                                  InstancePort port,
-                                                  boolean install) {
-            for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
-                if (!remoteNode.intgBridge().equals(port.deviceId()) && remoteNode.vlanIntf() != null) {
-                    TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
-                            .setOutput(remoteNode.vlanPortNum())
-                            .build();
-
-                    osFlowRuleService.setRule(
-                            appId,
-                            remoteNode.intgBridge(),
-                            selector,
-                            treatmentToRemote,
-                            PRIORITY_ARP_REQUEST_RULE,
-                            DHCP_ARP_TABLE,
-                            install);
-                }
-            }
-        }
     }
 }