Add REST interface for configuring ARP mode

Change-Id: Ib2b6edb133879054d235dbd4762ba95e1e03793c
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackConfigArpModeCommand.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackConfigArpModeCommand.java
index 1f22d4d..8569122 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackConfigArpModeCommand.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackConfigArpModeCommand.java
@@ -31,6 +31,7 @@
 import org.onosproject.openstacknode.api.OpenstackNodeService;
 
 import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.checkArpMode;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getPropertyValue;
 
 /**
@@ -41,8 +42,6 @@
 public class OpenstackConfigArpModeCommand extends AbstractShellCommand {
 
     private static final String ARP_MODE_NAME = "arpMode";
-    private static final String PROXY_MODE = "proxy";
-    private static final String BROADCAST_MODE = "broadcast";
 
     @Argument(index = 0, name = "arpMode",
             description = "ARP mode (proxy | broadcast)",
@@ -53,7 +52,7 @@
     protected void execute() {
 
         if (checkArpMode(arpMode)) {
-            configArpMode();
+            configArpMode(arpMode);
 
             ComponentConfigService service = get(ComponentConfigService.class);
             String switchingComponent = OpenstackSwitchingArpHandler.class.getName();
@@ -77,21 +76,6 @@
         }
     }
 
-    private boolean checkArpMode(String arpMode) {
-
-        if (isNullOrEmpty(arpMode)) {
-            error("ARP mode should not be empty string or null");
-            return false;
-        } else {
-            if (arpMode.equals(PROXY_MODE) || arpMode.equals(BROADCAST_MODE)) {
-                return true;
-            } else {
-                error("ARP mode should be either proxy or broadcast");
-                return false;
-            }
-        }
-    }
-
     private void purgeRules() {
         FlowRuleService flowRuleService = AbstractShellCommand.get(FlowRuleService.class);
         CoreService coreService = AbstractShellCommand.get(CoreService.class);
@@ -103,7 +87,7 @@
         flowRuleService.removeFlowRulesById(appId);
     }
 
-    private void configArpMode() {
+    private void configArpMode(String arpMode) {
         ComponentConfigService service = get(ComponentConfigService.class);
         String switchingComponent = OpenstackSwitchingArpHandler.class.getName();
         String routingComponent = OpenstackRoutingArpHandler.class.getName();
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
index 2d8bd12..609b580 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
@@ -60,6 +60,7 @@
 import java.util.TreeMap;
 
 import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+import static com.google.common.base.Strings.isNullOrEmpty;
 import static org.onosproject.openstacknetworking.api.Constants.PCISLOT;
 import static org.onosproject.openstacknetworking.api.Constants.PCI_VENDOR_INFO;
 import static org.onosproject.openstacknetworking.api.Constants.portNamePrefixMap;
@@ -84,6 +85,9 @@
     private static final String IDENTITY_PATH = "identity/";
     private static final String SSL_TYPE = "SSL";
 
+    private static final String PROXY_MODE = "proxy";
+    private static final String BROADCAST_MODE = "broadcast";
+
     private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
 
     /**
@@ -364,6 +368,21 @@
     }
 
     /**
+     * Checks the validity of ARP mode.
+     *
+     * @param arpMode ARP mode
+     * @return returns true if the ARP mode is valid, false otherwise
+     */
+    public static boolean checkArpMode(String arpMode) {
+
+        if (isNullOrEmpty(arpMode)) {
+            return false;
+        } else {
+            return arpMode.equals(PROXY_MODE) || arpMode.equals(BROADCAST_MODE);
+        }
+    }
+
+    /**
      * Builds up and a complete endpoint URL from gateway node.
      *
      * @param node gateway node
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/web/OpenstackManagementWebResource.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/web/OpenstackManagementWebResource.java
index 9a1779b..077fc4b 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/web/OpenstackManagementWebResource.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/web/OpenstackManagementWebResource.java
@@ -20,6 +20,7 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
 import org.onlab.util.ItemNotFoundException;
+import org.onosproject.cfg.ComponentConfigService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.net.flow.FlowRuleService;
@@ -27,6 +28,8 @@
 import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
 import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
 import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupAdminService;
+import org.onosproject.openstacknetworking.impl.OpenstackRoutingArpHandler;
+import org.onosproject.openstacknetworking.impl.OpenstackSwitchingArpHandler;
 import org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil;
 import org.onosproject.openstacknode.api.NodeState;
 import org.onosproject.openstacknode.api.OpenstackNode;
@@ -40,6 +43,7 @@
 
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
@@ -48,7 +52,10 @@
 import java.util.Objects;
 import java.util.Optional;
 
+import static org.onlab.util.Tools.nullIsIllegal;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.addRouterIface;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.checkArpMode;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getPropertyValue;
 import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.CONTROLLER;
 
 /**
@@ -59,8 +66,12 @@
     private final Logger log = LoggerFactory.getLogger(getClass());
 
     private static final String FLOATINGIPS = "floatingips";
+    private static final String ARP_MODE_NAME = "arpMode";
 
     private static final String DEVICE_OWNER_IFACE = "network:router_interface";
+
+    private static final String ARP_MODE_REQUIRED = "ARP mode is not specified";
+
     private final ObjectNode root = mapper().createObjectNode();
     private final ArrayNode floatingipsNode = root.putArray(FLOATINGIPS);
 
@@ -164,11 +175,7 @@
     @Path("sync/rules")
     public Response syncRules() {
 
-        osNodeService.completeNodes().forEach(osNode -> {
-            OpenstackNode updated = osNode.updateState(NodeState.INIT);
-            osNodeAdminService.updateNode(updated);
-        });
-
+        syncRulesBase();
         return ok(mapper().createObjectNode()).build();
     }
 
@@ -199,11 +206,47 @@
     @Path("purge/rules")
     public Response purgeRules() {
 
-        ApplicationId appId = coreService.getAppId(Constants.OPENSTACK_NETWORKING_APP_ID);
-        if (appId == null) {
-            throw new ItemNotFoundException("application not found");
+        purgeRulesBase();
+        return ok(mapper().createObjectNode()).build();
+    }
+
+    /**
+     * Configures the ARP mode (proxy | broadcast).
+     *
+     * @param arpmode ARP mode
+     * @return 200 OK with config result, 404 not found
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("config/arpmode/{arpmode}")
+    public Response configArpMode(@PathParam("arpmode") String arpmode) {
+
+        String arpModeStr = nullIsIllegal(arpmode, ARP_MODE_REQUIRED);
+        if (checkArpMode(arpModeStr)) {
+            configArpModeBase(arpModeStr);
+
+            ComponentConfigService service = get(ComponentConfigService.class);
+            String switchingComponent = OpenstackSwitchingArpHandler.class.getName();
+            String routingComponent = OpenstackRoutingArpHandler.class.getName();
+
+            // we check the arpMode configured in each component, and purge and
+            // reinstall all rules only if the arpMode is changed to the configured one
+            while (true) {
+                String switchingValue =
+                        getPropertyValue(service.getProperties(switchingComponent), ARP_MODE_NAME);
+                String routingValue =
+                        getPropertyValue(service.getProperties(routingComponent), ARP_MODE_NAME);
+
+                if (arpModeStr.equals(switchingValue) && arpModeStr.equals(routingValue)) {
+                    break;
+                }
+            }
+
+            purgeRulesBase();
+            syncRulesBase();
+        } else {
+            throw new IllegalArgumentException("The ARP mode is not valid");
         }
-        flowRuleService.removeFlowRulesById(appId);
 
         return ok(mapper().createObjectNode()).build();
     }
@@ -248,4 +291,28 @@
 
         return ok(root).build();
     }
+
+    private void syncRulesBase() {
+        osNodeService.completeNodes().forEach(osNode -> {
+            OpenstackNode updated = osNode.updateState(NodeState.INIT);
+            osNodeAdminService.updateNode(updated);
+        });
+    }
+
+    private void purgeRulesBase() {
+        ApplicationId appId = coreService.getAppId(Constants.OPENSTACK_NETWORKING_APP_ID);
+        if (appId == null) {
+            throw new ItemNotFoundException("application not found");
+        }
+        flowRuleService.removeFlowRulesById(appId);
+    }
+
+    private void configArpModeBase(String arpMode) {
+        ComponentConfigService service = get(ComponentConfigService.class);
+        String switchingComponent = OpenstackSwitchingArpHandler.class.getName();
+        String routingComponent = OpenstackRoutingArpHandler.class.getName();
+
+        service.setProperty(switchingComponent, ARP_MODE_NAME, arpMode);
+        service.setProperty(routingComponent, ARP_MODE_NAME, arpMode);
+    }
 }