[Falcon] link discovery -

- Support for TLV containing cluster fingerprint info
- Config for enabling extra TLV at device level
- Refactored ONOSLLDP constructor for ease of use

Change-Id: I93abe6c0ed8b7e37c80af5920649272faad8856e
diff --git a/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/DiscoveryContext.java b/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/DiscoveryContext.java
index a9da92a..04729e4 100644
--- a/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/DiscoveryContext.java
+++ b/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/DiscoveryContext.java
@@ -66,4 +66,11 @@
      * @param key link key
      */
     void touchLink(LinkKey key);
+
+    /**
+     * Returns the cluster-wide unique identifier.
+     *
+     * @return the cluster identifier
+     */
+    String fingerprint();
 }
diff --git a/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/FingerprintProbeFromDevice.java b/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/FingerprintProbeFromDevice.java
new file mode 100644
index 0000000..f6e3c2d
--- /dev/null
+++ b/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/FingerprintProbeFromDevice.java
@@ -0,0 +1,18 @@
+package org.onosproject.provider.lldp.impl;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.config.basics.BasicFeatureConfig;
+
+/**
+ * A feature to send and receive probes carrying a cluster-unique fingerprint.
+ * Note that, as it leverages LinkDiscovery, disabling linkDiscovery will disable
+ * this function.
+ */
+public class FingerprintProbeFromDevice extends BasicFeatureConfig<DeviceId> {
+
+    protected FingerprintProbeFromDevice() {
+        // default:disabled
+        super(false);
+    }
+
+}
diff --git a/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscovery.java b/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscovery.java
index 4b962ae..11b8cd3 100644
--- a/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscovery.java
+++ b/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscovery.java
@@ -16,6 +16,7 @@
 package org.onosproject.provider.lldp.impl;
 
 import com.google.common.collect.Sets;
+
 import org.jboss.netty.util.Timeout;
 import org.jboss.netty.util.TimerTask;
 import org.onlab.packet.Ethernet;
@@ -59,13 +60,13 @@
     private final Device device;
     private final DiscoveryContext context;
 
-    private final ONOSLLDP lldpPacket;
     private final Ethernet ethPacket;
     private final Ethernet bddpEth;
 
     private Timeout timeout;
     private volatile boolean isStopped;
-
+    // This LinkDiscovery can handle remote link probes (default false).
+    private volatile boolean fingerprinted;
     // Set of ports to be probed
     private final Set<Long> ports = Sets.newConcurrentHashSet();
 
@@ -81,22 +82,17 @@
         this.device = device;
         this.context = context;
 
-        lldpPacket = new ONOSLLDP();
-        lldpPacket.setChassisId(device.chassisId());
-        lldpPacket.setDevice(device.id().toString());
-
         ethPacket = new Ethernet();
         ethPacket.setEtherType(Ethernet.TYPE_LLDP);
         ethPacket.setDestinationMACAddress(ONOSLLDP.LLDP_NICIRA);
-        ethPacket.setPayload(this.lldpPacket);
         ethPacket.setPad(true);
 
         bddpEth = new Ethernet();
-        bddpEth.setPayload(lldpPacket);
         bddpEth.setEtherType(Ethernet.TYPE_BSN);
         bddpEth.setDestinationMACAddress(ONOSLLDP.BDDP_MULTICAST);
         bddpEth.setPad(true);
 
+        fingerprinted = false;
         isStopped = true;
         start();
         log.debug("Started discovery manager for switch {}", device.id());
@@ -220,8 +216,8 @@
         if (port == null) {
             return null;
         }
-        lldpPacket.setPortId(port.intValue());
-        ethPacket.setSourceMACAddress(SRC_MAC);
+        ONOSLLDP lldp = getLinkProbe(port);
+        ethPacket.setSourceMACAddress(SRC_MAC).setPayload(lldp);
         return new DefaultOutboundPacket(device.id(),
                                          builder().setOutput(portNumber(port)).build(),
                                          ByteBuffer.wrap(ethPacket.serialize()));
@@ -237,13 +233,21 @@
         if (port == null) {
             return null;
         }
-        lldpPacket.setPortId(port.intValue());
-        bddpEth.setSourceMACAddress(SRC_MAC);
+        ONOSLLDP lldp = getLinkProbe(port);
+        bddpEth.setSourceMACAddress(SRC_MAC).setPayload(lldp);
         return new DefaultOutboundPacket(device.id(),
                                          builder().setOutput(portNumber(port)).build(),
                                          ByteBuffer.wrap(bddpEth.serialize()));
     }
 
+    private ONOSLLDP getLinkProbe(Long port) {
+        return fingerprinted
+                ? ONOSLLDP.fingerprintedLLDP(device.id().toString(), device.chassisId(),
+                                             port.intValue(), context.fingerprint())
+                : ONOSLLDP.onosLLDP(device.id().toString(), device.chassisId(),
+                                    port.intValue());
+    }
+
     private void sendProbes(Long portNumber) {
         log.trace("Sending probes out to {}@{}", portNumber, device.id());
         OutboundPacket pkt = createOutBoundLldp(portNumber);
@@ -258,4 +262,11 @@
         return ports.contains(portNumber);
     }
 
+    protected void enableFingerprint() {
+        fingerprinted = true;
+    }
+
+    protected void disableFingerprint() {
+        fingerprinted = false;
+    }
 }
diff --git a/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LldpLinkProvider.java b/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LldpLinkProvider.java
index ce2826c..a87b6a7 100644
--- a/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LldpLinkProvider.java
+++ b/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LldpLinkProvider.java
@@ -47,6 +47,7 @@
 import org.onlab.packet.Ethernet;
 import org.onlab.util.Tools;
 import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.cluster.ClusterMetadataService;
 import org.onosproject.cluster.ClusterService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
@@ -133,6 +134,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected NetworkConfigRegistry cfgRegistry;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ClusterMetadataService clusterMetadataService;
+
     private LinkProviderService providerService;
 
     private ScheduledExecutorService executor;
@@ -185,6 +189,7 @@
 
     public static final String CONFIG_KEY = "suppression";
     public static final String FEATURE_NAME = "linkDiscovery";
+    public static final String FINGERPRINT_FEATURE_NAME = "fingerprint";
 
     private final Set<ConfigFactory<?, ?>> factories = ImmutableSet.of(
             new ConfigFactory<ApplicationId, SuppressionConfig>(APP_SUBJECT_FACTORY,
@@ -208,6 +213,13 @@
                 public LinkDiscoveryFromPort createConfig() {
                     return new LinkDiscoveryFromPort();
                 }
+            },
+            new ConfigFactory<DeviceId, FingerprintProbeFromDevice>(DEVICE_SUBJECT_FACTORY,
+                    FingerprintProbeFromDevice.class, FINGERPRINT_FEATURE_NAME) {
+                @Override
+                public FingerprintProbeFromDevice createConfig() {
+                    return new FingerprintProbeFromDevice();
+                }
             }
     );
 
@@ -385,6 +397,14 @@
         return isBlacklisted(new ConnectPoint(port.element().id(), port.number()));
     }
 
+    private boolean isFingerprinted(DeviceId did) {
+        FingerprintProbeFromDevice cfg = cfgRegistry.getConfig(did, FingerprintProbeFromDevice.class);
+        if (cfg == null) {
+            return false;
+        }
+        return cfg.enabled();
+    }
+
     /**
      * Updates discovery helper for specified device.
      *
@@ -405,6 +425,11 @@
         }
         LinkDiscovery ld = discoverers.computeIfAbsent(device.id(),
                                      did -> new LinkDiscovery(device, context));
+        if (isFingerprinted(device.id())) {
+            ld.enableFingerprint();
+        } else {
+            ld.disableFingerprint();
+        }
         if (ld.isStopped()) {
             ld.start();
         }
@@ -715,6 +740,11 @@
         public void touchLink(LinkKey key) {
             linkTimes.put(key, System.currentTimeMillis());
         }
+
+        @Override
+        public String fingerprint() {
+            return clusterMetadataService.getClusterMetadata().getName();
+        }
     }
 
     static final EnumSet<NetworkConfigEvent.Type> CONFIG_CHANGED
@@ -760,6 +790,15 @@
                     }
                 }
 
+            } else if (event.configClass() == FingerprintProbeFromDevice.class &&
+                    CONFIG_CHANGED.contains(event.type())) {
+
+                if (event.subject() instanceof DeviceId) {
+                    final DeviceId did = (DeviceId) event.subject();
+                    Device device = deviceService.getDevice(did);
+                    updateDevice(device);
+                }
+
             } else if (event.configClass().equals(SuppressionConfig.class) &&
                 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
                  event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED)) {