ONOS-686, 687, 1344 : The first commit for the Segment Routing application
 - ICMP/ARP/IP handlers are implemented as a part of the application for now
 - Default routing and link add/failure/recovery are also supprted
 - Temporary NetworkConfigHandler, which is hardcoded to support only 6 router FISH topology, is used for test
 - Some fixes on GroupHanlder app to support transit routers
 - Supports multi-instance (tested with two instances)

Change-Id: Idfa67903e59e1c4cac4da430f89cd4c50e821420
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
new file mode 100644
index 0000000..3684ec9
--- /dev/null
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.segmentrouting;
+
+import org.onlab.packet.IpPrefix;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.flow.FlowRule;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class DefaultRoutingHandler {
+
+    private static Logger log = LoggerFactory.getLogger(DefaultRoutingHandler.class);
+
+    private SegmentRoutingManager srManager;
+    private RoutingRulePopulator rulePopulator;
+    private NetworkConfigHandler config;
+    private Status populationStatus;
+
+    /**
+     * Represents the default routing population status.
+     */
+    public enum Status {
+        // population process is not started yet.
+        IDLE,
+
+        // population process started.
+        STARTED,
+
+        // population process was aborted due to errors, mostly for groups not found.
+        ABORTED,
+
+        // population process was finished successfully.
+        SUCCEEDED
+    }
+
+    /**
+     * Creates a DefaultRoutingHandler object.
+     *
+     * @param srManager SegmentRoutingManager object
+     */
+    public DefaultRoutingHandler(SegmentRoutingManager srManager) {
+        this.srManager = srManager;
+        this.rulePopulator = checkNotNull(srManager.routingRulePopulator);
+        this.config = checkNotNull(srManager.networkConfigHandler);
+        this.populationStatus = Status.IDLE;
+    }
+
+    /**
+     * Populates all routing rules to all connected routers, including default
+     * routing rules, adjacency rules, and policy rules if any.
+     *
+     * @return true if it succeeds in populating all rules, otherwise false
+     */
+    public boolean populateAllRoutingRules() {
+
+        populationStatus = Status.STARTED;
+        log.info("Starts to populate routing rules");
+
+        for (Device sw : srManager.deviceService.getDevices()) {
+            if (srManager.mastershipService.
+                    getLocalRole(sw.id()) != MastershipRole.MASTER) {
+                continue;
+            }
+
+            ECMPShortestPathGraph ecmpSPG = new ECMPShortestPathGraph(sw.id(), srManager);
+            if (!populateEcmpRoutingRules(sw, ecmpSPG)) {
+                populationStatus = Status.ABORTED;
+                log.debug("Abort routing rule population");
+                return false;
+            }
+
+            // TODO: Set adjacency routing rule for all switches
+        }
+
+        populationStatus = Status.SUCCEEDED;
+        log.info("Completes routing rule population");
+        return true;
+    }
+
+    private boolean populateEcmpRoutingRules(Device sw,
+                                             ECMPShortestPathGraph ecmpSPG) {
+
+        HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
+                ecmpSPG.getAllLearnedSwitchesAndVia();
+        for (Integer itrIdx : switchVia.keySet()) {
+            HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
+                    switchVia.get(itrIdx);
+            for (DeviceId targetSw : swViaMap.keySet()) {
+                DeviceId destSw = sw.id();
+                Set<DeviceId> nextHops = new HashSet<>();
+
+                for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
+                    if (via.isEmpty()) {
+                        nextHops.add(destSw);
+                    } else {
+                        nextHops.add(via.get(0));
+                    }
+                }
+                if (!populateEcmpRoutingRulePartial(targetSw, destSw, nextHops)) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    private boolean populateEcmpRoutingRulePartial(DeviceId targetSw, DeviceId destSw,
+                                Set<DeviceId> nextHops) {
+        boolean result;
+
+        if (nextHops.isEmpty()) {
+            nextHops.add(destSw);
+        }
+
+        // If both target switch and dest switch are edge routers, then set IP rule
+        // for both subnet and router IP.
+        if (config.isEdgeRouter(targetSw) && config.isEdgeRouter(destSw)) {
+            String subnets = config.getSubnetInfo(destSw);
+            result = rulePopulator.populateIpRuleForSubnet(targetSw, subnets, destSw, nextHops);
+            if (!result) {
+                return false;
+            }
+
+            IpPrefix routerIp = config.getRouterIpAddress(destSw);
+            result = rulePopulator.populateIpRuleForRouter(targetSw, routerIp, destSw, nextHops);
+            if (!result) {
+                return false;
+            }
+
+        // If the target switch is an edge router, then set IP rules for the router IP.
+        } else if (config.isEdgeRouter(targetSw)) {
+            IpPrefix routerIp = config.getRouterIpAddress(destSw);
+            result = rulePopulator.populateIpRuleForRouter(targetSw, routerIp, destSw, nextHops);
+            if (!result) {
+                return false;
+            }
+
+        // If the target switch is an transit router, then set MPLS rules only.
+        } else if (config.isTransitRouter(targetSw)) {
+            result = rulePopulator.populateMplsRule(targetSw, destSw, nextHops);
+            if (!result) {
+                return false;
+            }
+        } else {
+            log.warn("The switch {} is neither an edge router nor a transit router.", targetSw);
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Populates table miss entries for all tables, and pipeline rules for
+     * VLAN and TACM tables.
+     *
+     * @param deviceId Switch ID to set the rules
+     */
+    public void populateTtpRules(DeviceId deviceId) {
+
+        rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.VLAN,
+                true, false, false, FlowRule.Type.DEFAULT);
+        rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.ETHER,
+                true, false, false, FlowRule.Type.DEFAULT);
+        rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.IP,
+                false, true, true, FlowRule.Type.ACL);
+        rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.MPLS,
+                false, true, true, FlowRule.Type.ACL);
+        rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.ACL,
+                false, false, false, FlowRule.Type.DEFAULT);
+
+        rulePopulator.populateTableVlan(deviceId);
+        rulePopulator.populateTableTMac(deviceId);
+    }
+
+    /**
+     * Start the flow rule population process if it was never started.
+     * The process finishes successfully when all flow rules are set and
+     * stops with ABORTED status when any groups required for flows is not
+     * set yet.
+     */
+    public void startPopulationProcess() {
+        synchronized (populationStatus) {
+            if (populationStatus == Status.IDLE ||
+                    populationStatus == Status.SUCCEEDED) {
+                populationStatus = Status.STARTED;
+                populateAllRoutingRules();
+            }
+        }
+    }
+
+    /**
+     * Resume the flow rule population process if it was aborted for any reason.
+     * Mostly the process is aborted when the groups required are not set yet.
+     */
+    public void resumePopulationProcess() {
+        synchronized (populationStatus) {
+            if (populationStatus == Status.ABORTED) {
+                populationStatus = Status.STARTED;
+                // TODO: we need to restart from the point aborted instead of restarting.
+                populateAllRoutingRules();
+            }
+        }
+    }
+}