Moved ProxyArp, SDN-IP and BgpRouter to use new config format.

The new config format is based on the new network configuration subsystem.

Includes a few config fixes to NetworkConfigLoader and InterfaceManager.

Change-Id: Id7f766736decb7afb6b63c2731d3baba9fc7c764
diff --git a/core/net/src/main/java/org/onosproject/net/config/impl/NetworkConfigLoader.java b/core/net/src/main/java/org/onosproject/net/config/impl/NetworkConfigLoader.java
index e66c81b..810ca6c 100644
--- a/core/net/src/main/java/org/onosproject/net/config/impl/NetworkConfigLoader.java
+++ b/core/net/src/main/java/org/onosproject/net/config/impl/NetworkConfigLoader.java
@@ -23,6 +23,7 @@
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.net.config.Config;
 import org.onosproject.net.config.NetworkConfigEvent;
 import org.onosproject.net.config.NetworkConfigListener;
 import org.onosproject.net.config.NetworkConfigService;
@@ -49,11 +50,11 @@
 
     // FIXME: Add mutual exclusion to make sure this happens only once per startup.
 
-    private Map<InnerConfigPosition, ObjectNode> jsons = Maps.newHashMap();
+    private final Map<InnerConfigPosition, ObjectNode> jsons = Maps.newConcurrentMap();
 
     private final NetworkConfigListener configListener = new InnerConfigListener();
 
-    ObjectNode root;
+    private ObjectNode root;
 
     @Activate
     public void activate() {
@@ -101,24 +102,24 @@
      * Inner class that allows for tracking of JSON class configurations.
      */
     private final class InnerConfigPosition {
-        private String subjectKey, subject, classKey;
+        private final String subjectKey, subject, configKey;
 
-        private String getSubjectKey() {
+        private String subjectKey() {
             return subjectKey;
         }
 
-        private String getSubject() {
+        private String subject() {
             return subject;
         }
 
-        private String getClassKey() {
-            return classKey;
+        private String configKey() {
+            return configKey;
         }
 
-        private InnerConfigPosition(String subjectKey, String subject, String classKey) {
+        private InnerConfigPosition(String subjectKey, String subject, String configKey) {
             this.subjectKey = subjectKey;
             this.subject = subject;
-            this.classKey = classKey;
+            this.configKey = configKey;
         }
 
         @Override
@@ -128,15 +129,16 @@
             }
             if (obj instanceof InnerConfigPosition) {
                 final InnerConfigPosition that = (InnerConfigPosition) obj;
-                return Objects.equals(this.subjectKey, that.subjectKey) && Objects.equals(this.subject, that.subject)
-                        && Objects.equals(this.classKey, that.classKey);
+                return Objects.equals(this.subjectKey, that.subjectKey)
+                        && Objects.equals(this.subject, that.subject)
+                        && Objects.equals(this.configKey, that.configKey);
             }
             return false;
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(subjectKey, subject, classKey);
+            return Objects.hash(subjectKey, subject, configKey);
         }
     }
 
@@ -174,38 +176,41 @@
     }
 
     /**
-     * Apply the configurations associated with all of the config classes that are imported and have not yet been
-     * applied.
+     * Apply the configurations associated with all of the config classes that
+     * are imported and have not yet been applied.
      */
-    protected void applyConfigurations() {
+    private void applyConfigurations() {
         Iterator<Map.Entry<InnerConfigPosition, ObjectNode>> iter = jsons.entrySet().iterator();
 
         Map.Entry<InnerConfigPosition, ObjectNode> entry;
         InnerConfigPosition key;
         ObjectNode node;
         String subjectKey;
-        String subject;
-        String classKey;
+        String subjectString;
+        String configKey;
 
         while (iter.hasNext()) {
             entry = iter.next();
             node = entry.getValue();
             key = entry.getKey();
-            subjectKey = key.getSubjectKey();
-            subject = key.getSubject();
-            classKey = key.getClassKey();
+            subjectKey = key.subjectKey();
+            subjectString = key.subject();
+            configKey = key.configKey();
+
+            Class<? extends Config> configClass =
+                    networkConfigService.getConfigClass(subjectKey, configKey);
             //Check that the config class has been imported
-            if (networkConfigService.getConfigClass(subjectKey, subject) != null) {
+            if (configClass != null) {
+
+                Object subject = networkConfigService.getSubjectFactory(subjectKey).
+                        createSubject(subjectString);
 
                 //Apply the configuration
-                networkConfigService.applyConfig(networkConfigService.getSubjectFactory(subjectKey).
-                                createSubject(subject),
-                        networkConfigService.getConfigClass(subjectKey, classKey), node);
+                networkConfigService.applyConfig(subject, configClass, node);
 
                 //Now that it has been applied the corresponding JSON entry is no longer needed
-                jsons.remove(key);
+                iter.remove();
             }
-
         }
     }
 
diff --git a/core/net/src/main/java/org/onosproject/net/host/impl/HostManager.java b/core/net/src/main/java/org/onosproject/net/host/impl/HostManager.java
index fe369ae..99d401a 100644
--- a/core/net/src/main/java/org/onosproject/net/host/impl/HostManager.java
+++ b/core/net/src/main/java/org/onosproject/net/host/impl/HostManager.java
@@ -24,6 +24,7 @@
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
+import org.onosproject.incubator.net.intf.InterfaceService;
 import org.onosproject.net.provider.AbstractListenerProviderRegistry;
 import org.onosproject.core.Permission;
 import org.onosproject.net.config.NetworkConfigEvent;
@@ -86,6 +87,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected NetworkConfigService networkConfigService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected InterfaceService interfaceService;
+
     private HostMonitor monitor;
 
     @Activate
@@ -93,7 +97,7 @@
         store.setDelegate(delegate);
         eventDispatcher.addSink(HostEvent.class, listenerRegistry);
         networkConfigService.addListener(networkConfigListener);
-        monitor = new HostMonitor(deviceService, packetService, this);
+        monitor = new HostMonitor(packetService, this, interfaceService);
         monitor.start();
         log.info("Started");
     }
diff --git a/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java b/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java
index 6dc71fc..fe25236 100644
--- a/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java
+++ b/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java
@@ -20,26 +20,23 @@
 import org.onlab.packet.ARP;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.ICMP6;
-import org.onlab.packet.IpAddress;
 import org.onlab.packet.IPv6;
+import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.onlab.packet.ndp.NeighborDiscoveryOptions;
 import org.onlab.packet.ndp.NeighborSolicitation;
 import org.onlab.util.Timer;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
 import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.Device;
-import org.onosproject.net.DeviceId;
 import org.onosproject.net.Host;
-import org.onosproject.net.Port;
-import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.instructions.Instruction;
 import org.onosproject.net.flow.instructions.Instructions;
 import org.onosproject.net.host.HostProvider;
 import org.onosproject.net.host.InterfaceIpAddress;
-import org.onosproject.net.host.PortAddresses;
 import org.onosproject.net.packet.DefaultOutboundPacket;
 import org.onosproject.net.packet.OutboundPacket;
 import org.onosproject.net.packet.PacketService;
@@ -63,9 +60,9 @@
  * </p>
  */
 public class HostMonitor implements TimerTask {
-    private DeviceService deviceService;
     private PacketService packetService;
     private HostManager hostManager;
+    private InterfaceService interfaceService;
 
     private final Set<IpAddress> monitoredAddresses;
 
@@ -80,20 +77,19 @@
     /**
      * Creates a new host monitor.
      *
-     * @param deviceService device service used to find edge ports
      * @param packetService packet service used to send packets on the data plane
      * @param hostManager host manager used to look up host information and
      * probe existing hosts
+     * @param interfaceService interface service for interface information
      */
-    public HostMonitor(DeviceService deviceService, PacketService packetService,
-            HostManager hostManager) {
+    public HostMonitor(PacketService packetService, HostManager hostManager,
+                       InterfaceService interfaceService) {
 
-        this.deviceService = deviceService;
         this.packetService = packetService;
         this.hostManager = hostManager;
+        this.interfaceService = interfaceService;
 
-        monitoredAddresses = Collections.newSetFromMap(
-                new ConcurrentHashMap<IpAddress, Boolean>());
+        monitoredAddresses = Collections.newSetFromMap(new ConcurrentHashMap<>());
         hostProviders = new ConcurrentHashMap<>();
     }
 
@@ -176,29 +172,21 @@
      * @param targetIp IP address to send the request for
      */
     private void sendArpNdpRequest(IpAddress targetIp) {
-        // Find ports with an IP address in the target's subnet and sent ARP/ND
-        // probes out those ports.
-        for (Device device : deviceService.getDevices()) {
-            for (Port port : deviceService.getPorts(device.id())) {
-                ConnectPoint cp = new ConnectPoint(device.id(), port.number());
-                Set<PortAddresses> portAddressSet =
-                    hostManager.getAddressBindingsForPort(cp);
+        Interface intf = interfaceService.getMatchingInterface(targetIp);
 
-                for (PortAddresses portAddresses : portAddressSet) {
-                    for (InterfaceIpAddress ia : portAddresses.ipAddresses()) {
-                        if (ia.subnetAddress().contains(targetIp)) {
-                            sendArpNdpProbe(device.id(), port, targetIp,
-                                            ia.ipAddress(),
-                                            portAddresses.mac(),
-                                            portAddresses.vlan());
-                        }
-                    }
-                }
+        if (intf == null) {
+            return;
+        }
+
+        for (InterfaceIpAddress ia : intf.ipAddresses()) {
+            if (ia.subnetAddress().contains(targetIp)) {
+                sendArpNdpProbe(intf.connectPoint(), targetIp, ia.ipAddress(),
+                        intf.mac(), intf.vlan());
             }
         }
     }
 
-    private void sendArpNdpProbe(DeviceId deviceId, Port port,
+    private void sendArpNdpProbe(ConnectPoint connectPoint,
                                  IpAddress targetIp,
                                  IpAddress sourceIp, MacAddress sourceMac,
                                  VlanId vlan) {
@@ -215,14 +203,14 @@
         }
 
         List<Instruction> instructions = new ArrayList<>();
-        instructions.add(Instructions.createOutput(port.number()));
+        instructions.add(Instructions.createOutput(connectPoint.port()));
 
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-            .setOutput(port.number())
+            .setOutput(connectPoint.port())
             .build();
 
         OutboundPacket outboundPacket =
-            new DefaultOutboundPacket(deviceId, treatment,
+            new DefaultOutboundPacket(connectPoint.deviceId(), treatment,
                                       ByteBuffer.wrap(probePacket.serialize()));
 
         packetService.emit(outboundPacket);
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompiler.java
index 8fad769..06d0e9a 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompiler.java
@@ -98,7 +98,7 @@
                 partialTree = true;
 
                 for (Link link : path.links()) {
-                    if (links.containsKey(link.src().deviceId())) {
+                    if (links.containsKey(link.dst().deviceId())) {
                         // We've already reached the existing tree with the first
                         // part of this path. Add the merging point with different
                         // incoming port, but don't add the remainder of the path
diff --git a/core/net/src/main/java/org/onosproject/net/proxyarp/impl/ProxyArpManager.java b/core/net/src/main/java/org/onosproject/net/proxyarp/impl/ProxyArpManager.java
index b5acde6..1a56d0e 100644
--- a/core/net/src/main/java/org/onosproject/net/proxyarp/impl/ProxyArpManager.java
+++ b/core/net/src/main/java/org/onosproject/net/proxyarp/impl/ProxyArpManager.java
@@ -34,6 +34,8 @@
 import org.onlab.packet.ndp.NeighborDiscoveryOptions;
 import org.onlab.packet.ndp.NeighborSolicitation;
 import org.onosproject.core.Permission;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.Host;
 import org.onosproject.net.device.DeviceService;
@@ -41,8 +43,6 @@
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.host.HostService;
-import org.onosproject.net.host.InterfaceIpAddress;
-import org.onosproject.net.host.PortAddresses;
 import org.onosproject.net.link.LinkService;
 import org.onosproject.net.packet.DefaultOutboundPacket;
 import org.onosproject.net.packet.InboundPacket;
@@ -53,9 +53,7 @@
 import org.slf4j.Logger;
 
 import java.nio.ByteBuffer;
-import java.util.HashSet;
 import java.util.Set;
-import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -95,17 +93,15 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected ProxyArpStore store;
 
-    /**
-     * Listens to both device service and link service to determine
-     * whether a port is internal or external.
-     */
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected InterfaceService interfaceService;
+
     @Activate
     public void activate() {
         store.setDelegate(this::sendTo);
         log.info("Started");
     }
 
-
     @Deactivate
     public void deactivate() {
         store.setDelegate(null);
@@ -140,21 +136,18 @@
 
         VlanId vlan = vlanId(eth.getVlanID());
 
-        if (isOutsidePort(inPort)) {
+        if (hasIpAddress(inPort)) {
             // If the request came from outside the network, only reply if it was
             // for one of our external addresses.
-            Set<PortAddresses> addressSet =
-                    hostService.getAddressBindingsForPort(inPort);
 
-            for (PortAddresses addresses : addressSet) {
-                for (InterfaceIpAddress ia : addresses.ipAddresses()) {
-                    if (ia.ipAddress().equals(targetAddress)) {
-                        Ethernet arpReply =
-                                ARP.buildArpReply(targetAddress, addresses.mac(), eth);
-                        sendTo(arpReply, inPort);
-                    }
-                }
-            }
+            interfaceService.getInterfacesByPort(inPort)
+                    .stream()
+                    .filter(intf -> intf.ipAddresses()
+                            .stream()
+                            .anyMatch(ia -> ia.ipAddress().equals(targetAddress)))
+                    .forEach(intf -> buildAndSendArp(targetAddress, intf.mac(), eth, inPort));
+
+            // Stop here and don't proxy ARPs if the port has an IP address
             return;
         }
 
@@ -164,7 +157,7 @@
 
         Host dst = null;
         Host src = hostService.getHost(hostId(eth.getSourceMAC(),
-                                              vlanId(eth.getVlanID())));
+                vlanId(eth.getVlanID())));
 
         for (Host host : hosts) {
             if (host.vlan().equals(vlan)) {
@@ -175,8 +168,7 @@
 
         if (src != null && dst != null) {
             // We know the target host so we can respond
-            Ethernet arpReply = ARP.buildArpReply(targetAddress, dst.mac(), eth);
-            sendTo(arpReply, inPort);
+            buildAndSendArp(targetAddress, dst.mac(), eth, inPort);
             return;
         }
 
@@ -185,16 +177,14 @@
         // address. Forward it over to the correct port.
         Ip4Address source =
                 Ip4Address.valueOf(arp.getSenderProtocolAddress());
-        Set<PortAddresses> sourceAddresses = findPortsInSubnet(source);
+
         boolean matched = false;
-        for (PortAddresses pa : sourceAddresses) {
-            for (InterfaceIpAddress ia : pa.ipAddresses()) {
-                if (ia.ipAddress().equals(source) &&
-                        pa.vlan().equals(vlan)) {
-                    matched = true;
-                    sendTo(eth, pa.connectPoint());
-                    break;
-                }
+        Set<Interface> interfaces = interfaceService.getInterfacesByIp(source);
+        for (Interface intf : interfaces) {
+            if (intf.vlan().equals(vlan)) {
+                matched = true;
+                sendTo(eth, intf.connectPoint());
+                break;
             }
         }
 
@@ -202,10 +192,8 @@
             return;
         }
 
-        //
         // The request couldn't be resolved.
         // Flood the request on all ports except the incoming port.
-        //
         flood(eth, inPort);
     }
 
@@ -219,42 +207,14 @@
 
         // If the request came from outside the network, only reply if it was
         // for one of our external addresses.
-        if (isOutsidePort(inPort)) {
-            Set<PortAddresses> addressSet =
-                    hostService.getAddressBindingsForPort(inPort);
-
-            for (PortAddresses addresses : addressSet) {
-                for (InterfaceIpAddress ia : addresses.ipAddresses()) {
-                    if (ia.ipAddress().equals(targetAddress)) {
-                        Ethernet ndpReply =
-                                buildNdpReply(targetAddress, addresses.mac(), eth);
-                        sendTo(ndpReply, inPort);
-                    }
-                }
-            }
+        if (hasIpAddress(inPort)) {
+            interfaceService.getInterfacesByPort(inPort)
+                    .stream()
+                    .filter(intf -> intf.ipAddresses()
+                            .stream()
+                            .anyMatch(ia -> ia.ipAddress().equals(targetAddress)))
+                    .forEach(intf -> buildAndSendNdp(targetAddress, intf.mac(), eth, inPort));
             return;
-        } else {
-            // If the source address matches one of our external addresses
-            // it could be a request from an internal host to an external
-            // address. Forward it over to the correct ports.
-            Ip6Address source =
-                    Ip6Address.valueOf(ipv6.getSourceAddress());
-            Set<PortAddresses> sourceAddresses = findPortsInSubnet(source);
-            boolean matched = false;
-            for (PortAddresses pa : sourceAddresses) {
-                for (InterfaceIpAddress ia : pa.ipAddresses()) {
-                    if (ia.ipAddress().equals(source) &&
-                            pa.vlan().equals(vlan)) {
-                        matched = true;
-                        sendTo(eth, pa.connectPoint());
-                        break;
-                    }
-                }
-            }
-
-            if (matched) {
-                return;
-            }
         }
 
         // Continue with normal proxy ARP case
@@ -272,23 +232,49 @@
             }
         }
 
-        if (src == null || dst == null) {
-            //
-            // The request couldn't be resolved.
-            // Flood the request on all ports except the incoming ports.
-            //
-            flood(eth, inPort);
+        if (src != null || dst != null) {
+            // We know the target host so we can respond
+            buildAndSendNdp(targetAddress, dst.mac(), eth, inPort);
             return;
         }
 
-        //
-        // Reply on the port the request was received on
-        //
-        Ethernet ndpReply = buildNdpReply(targetAddress, dst.mac(), eth);
-        sendTo(ndpReply, inPort);
+        // If the source address matches one of our external addresses
+        // it could be a request from an internal host to an external
+        // address. Forward it over to the correct port.
+        Ip6Address source =
+                Ip6Address.valueOf(ipv6.getSourceAddress());
+
+        boolean matched = false;
+
+        Set<Interface> interfaces = interfaceService.getInterfacesByIp(source);
+        for (Interface intf : interfaces) {
+            if (intf.vlan().equals(vlan)) {
+                matched = true;
+                sendTo(eth, intf.connectPoint());
+                break;
+            }
+        }
+
+        if (matched) {
+            return;
+        }
+
+        // The request couldn't be resolved.
+        // Flood the request on all ports except the incoming ports.
+        flood(eth, inPort);
     }
     //TODO checkpoint
 
+    private void buildAndSendArp(Ip4Address srcIp, MacAddress srcMac,
+                                 Ethernet request, ConnectPoint port) {
+        sendTo(ARP.buildArpReply(srcIp, srcMac, request), port);
+    }
+
+    private void buildAndSendNdp(Ip6Address srcIp, MacAddress srcMac,
+                                 Ethernet request, ConnectPoint port) {
+        sendTo(buildNdpReply(srcIp, srcMac, request), port);
+    }
+
     /**
      * Outputs the given packet out the given port.
      *
@@ -314,30 +300,18 @@
     }
 
     /**
-     * Finds ports with an address in the subnet of the target address.
-     *
-     * @param target the target address to find a matching port for
-     * @return a set of PortAddresses describing ports in the subnet
-     */
-    private Set<PortAddresses> findPortsInSubnet(IpAddress target) {
-        Set<PortAddresses> result = new HashSet<>();
-        for (PortAddresses addresses : hostService.getAddressBindings()) {
-            result.addAll(addresses.ipAddresses().stream().filter(ia -> ia.subnetAddress().contains(target)).
-                    map(ia -> addresses).collect(Collectors.toList()));
-        }
-        return result;
-    }
-
-    /**
-     * Returns whether the given port is an outside-facing port with an IP
-     * address configured.
+     * Returns whether the given port has any IP addresses configured or not.
      *
      * @param port the port to check
-     * @return true if the port is an outside-facing port, otherwise false
+     * @return true if the port has at least one IP address configured,
+     * otherwise false
      */
-    private boolean isOutsidePort(ConnectPoint port) {
-        // TODO: Is this sufficient to identify outside-facing ports: just having IP addresses on a port?
-        return !hostService.getAddressBindingsForPort(port).isEmpty();
+    private boolean hasIpAddress(ConnectPoint port) {
+        return interfaceService.getInterfacesByPort(port)
+                .stream()
+                .map(intf -> intf.ipAddresses())
+                .findAny()
+                .isPresent();
     }
 
     @Override
@@ -418,7 +392,7 @@
         ByteBuffer buf = ByteBuffer.wrap(request.serialize());
 
         for (ConnectPoint connectPoint : edgeService.getEdgePoints()) {
-            if (isOutsidePort(connectPoint) || connectPoint.equals(inPort)) {
+            if (hasIpAddress(connectPoint) || connectPoint.equals(inPort)) {
                 continue;
             }
 
@@ -427,7 +401,6 @@
             packetService.emit(new DefaultOutboundPacket(connectPoint.deviceId(),
                                                          builder.build(), buf));
         }
-
     }
 
     /**