CORD-339 Network config host provider

* Implement new application org.onosproject.netcfghost
* Implement BasicHostConfig to include IP and location information
    - Update network-cfg.json to add host config example
    - Add network-cfg-2x2-leaf-spine.json for 2x2 leaf-spine network in SegmentRouting
* Update Segment Rounting
    - Punt ARP packets
      (which is done by HostLocationProvider previously)
    - Check existing hosts when device connected or configured

Change-Id: I03986ddc8203d740b5bf26903e3dbf866d4d4600
diff --git a/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java b/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
index 9922587..e645165 100644
--- a/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
@@ -513,6 +513,7 @@
     public void populatePortAddressingRules(DeviceId deviceId) {
         rulePopulator.populateRouterMacVlanFilters(deviceId);
         rulePopulator.populateRouterIpPunts(deviceId);
+        rulePopulator.populateArpPunts(deviceId);
     }
 
     /**
diff --git a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
index a07a15d..fc6a3f3 100644
--- a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
+++ b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
@@ -511,6 +511,39 @@
     }
 
     /**
+     * Creates a forwarding objective to punt all IP packets, destined to the
+     * router's port IP addresses, to the controller. Note that the input
+     * port should not be matched on, as these packets can come from any input.
+     * Furthermore, these are applied only by the master instance.
+     *
+     * @param deviceId the switch dpid for the router
+     */
+    public void populateArpPunts(DeviceId deviceId) {
+        if (!srManager.mastershipService.isLocalMaster(deviceId)) {
+            log.debug("Not installing port-IP punts - not the master for dev:{} ",
+                    deviceId);
+            return;
+        }
+
+        ForwardingObjective.Builder puntArp = DefaultForwardingObjective.builder();
+        TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
+        TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
+        sbuilder.matchEthType(Ethernet.TYPE_ARP);
+        tbuilder.setOutput(PortNumber.CONTROLLER);
+        puntArp.withSelector(sbuilder.build());
+        puntArp.withTreatment(tbuilder.build());
+        puntArp.withFlag(Flag.VERSATILE)
+                .withPriority(HIGHEST_PRIORITY)
+                .makePermanent()
+                .fromApp(srManager.appId);
+        log.debug("Installing forwarding objective to punt ARPs");
+        srManager.flowObjectiveService.
+                forward(deviceId,
+                        puntArp.add(new SRObjectiveContext(deviceId,
+                                SRObjectiveContext.ObjectiveType.FORWARDING)));
+    }
+
+    /**
      * Populates a forwarding objective to send packets that miss other high
      * priority Bridging Table entries to a group that contains all ports of
      * its subnet.
diff --git a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index f6bf649..518bce3 100644
--- a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -185,7 +185,7 @@
                 }
             };
 
-    private final HostListener hostListener = new InternalHostListener();
+    private final InternalHostListener hostListener = new InternalHostListener();
 
     private Object threadSchedulerLock = new Object();
     private static int numOfEventsQueued = 0;
@@ -658,6 +658,7 @@
             // port addressing rules to the driver as well irrespective of whether
             // this instance is the master or not.
             defaultRoutingHandler.populatePortAddressingRules(device.id());
+            hostListener.readInitialHosts();
         }
         if (mastershipService.isLocalMaster(device.id())) {
             DefaultGroupHandler groupHandler = groupHandlerMap.get(device.id());
@@ -725,6 +726,7 @@
                     // port addressing rules to the driver as well, irrespective of whether
                     // this instance is the master or not.
                     defaultRoutingHandler.populatePortAddressingRules(device.id());
+                    hostListener.readInitialHosts();
                 }
                 if (mastershipService.isLocalMaster(device.id())) {
                     DefaultGroupHandler groupHandler = groupHandlerMap.get(device.id());
@@ -751,7 +753,34 @@
         }
     }
 
+    // TODO Move bridging table population to a separate class
     private class InternalHostListener implements HostListener {
+        private void readInitialHosts() {
+            hostService.getHosts().forEach(host -> {
+                MacAddress mac = host.mac();
+                VlanId vlanId = host.vlan();
+                DeviceId deviceId = host.location().deviceId();
+                PortNumber port = host.location().port();
+                Set<IpAddress> ips = host.ipAddresses();
+                log.debug("Host {}/{} is added at {}:{}", mac, vlanId, deviceId, port);
+
+                // Populate bridging table entry
+                ForwardingObjective.Builder fob =
+                        getForwardingObjectiveBuilder(mac, vlanId, port);
+                flowObjectiveService.forward(deviceId, fob.add(
+                        new BridgingTableObjectiveContext(mac, vlanId)
+                ));
+
+                // Populate IP table entry
+                ips.forEach(ip -> {
+                    if (ip.isIp4()) {
+                        routingRulePopulator.populateIpRuleForHost(
+                                deviceId, ip.getIp4Address(), mac, port);
+                    }
+                });
+            });
+        }
+
         private ForwardingObjective.Builder getForwardingObjectiveBuilder(
                 MacAddress mac, VlanId vlanId, PortNumber port) {
             TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
@@ -780,7 +809,6 @@
             Set<IpAddress> ips = event.subject().ipAddresses();
             log.debug("Host {}/{} is added at {}:{}", mac, vlanId, deviceId, port);
 
-            // TODO Move bridging table population to a separate class
             // Populate bridging table entry
             ForwardingObjective.Builder fob =
                     getForwardingObjectiveBuilder(mac, vlanId, port);