OPNFLWPLUG-1027 : Topology manager writes link information everytime topology-lldp... 75/74175/30
authorAndrej Leitner <andrej.leitner@gmail.com>
Tue, 17 Jul 2018 22:23:30 +0000 (00:23 +0200)
committerGobinath <gobinath@ericsson.com>
Sun, 19 May 2019 16:57:41 +0000 (22:27 +0530)
 Send new notifications only when the links are discovered
 and ignore already present links.

Change-Id: I5ec6ae4d2cd8d2c83eb6e918f12cf12927056633
JIRA: OPNFLWPLUG-1027
Signed-off-by: Gobinath <gobinath@ericsson.com>
applications/topology-lldp-discovery/pom.xml
applications/topology-lldp-discovery/src/main/java/org/opendaylight/openflowplugin/applications/topology/lldp/LLDPDiscoveryListener.java
applications/topology-lldp-discovery/src/main/java/org/opendaylight/openflowplugin/applications/topology/lldp/LLDPLinkAger.java
applications/topology-lldp-discovery/src/main/java/org/opendaylight/openflowplugin/applications/topology/lldp/utils/LLDPDiscoveryUtils.java
applications/topology-lldp-discovery/src/test/java/org/opendaylight/openflowplugin/applications/topology/lldp/LLDPLinkAgerTest.java

index d3f48655191ad2494ce4831f8c9902e4022b473a..ee217eb479d6756386753dd547cb4aec2af588de 100644 (file)
       <groupId>org.opendaylight.controller.model</groupId>
       <artifactId>model-inventory</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller.model</groupId>
+      <artifactId>model-topology</artifactId>
+    </dependency>
     <dependency>
       <groupId>javax.inject</groupId>
       <artifactId>javax.inject</artifactId>
       <artifactId>jsr305</artifactId>
       <optional>true</optional>
     </dependency>
-
+      <dependency>
+          <groupId>org.opendaylight.openflowplugin</groupId>
+          <artifactId>openflowplugin-common</artifactId>
+      </dependency>
   </dependencies>
 
   <build>
index aba6fd942a5305146b90a49002204461c32e9269..5d5bc18429c265f6ec77788cd51f6952a9187a69 100644 (file)
@@ -40,24 +40,28 @@ public class LLDPDiscoveryListener implements PacketProcessingListener {
     }
 
     @Override
-    public void onPacketReceived(PacketReceived lldp) {
+    public void onPacketReceived(final PacketReceived lldp) {
         NodeConnectorRef src = LLDPDiscoveryUtils.lldpToNodeConnectorRef(lldp.getPayload(), true);
         if (src != null) {
             final NodeKey nodeKey = lldp.getIngress().getValue().firstKeyOf(Node.class);
             LOG.debug("LLDP packet received for destination node {}", nodeKey);
             if (nodeKey != null) {
-                LinkDiscoveredBuilder ldb = new LinkDiscoveredBuilder();
+                final LinkDiscoveredBuilder ldb = new LinkDiscoveredBuilder();
                 ldb.setDestination(lldp.getIngress());
                 ldb.setSource(new NodeConnectorRef(src));
-                LinkDiscovered ld = ldb.build();
-
+                final LinkDiscovered ld = ldb.build();
+                final boolean linkWasPresent = lldpLinkAger.isLinkPresent(ld);
                 lldpLinkAger.put(ld);
                 if (LLDPDiscoveryUtils.isEntityOwned(this.eos, nodeKey.getId().getValue())) {
-                    LOG.debug("Publish add event for link {}", ld);
-                    try {
-                        notificationService.putNotification(ld);
-                    } catch (InterruptedException e) {
-                        LOG.warn("Interrupted while publishing notification {}", ld, e);
+                    if (linkWasPresent) {
+                        LOG.trace("Link {} already present in the cache, skip publishing the notification.", ld);
+                    } else {
+                        LOG.debug("Publish add event for link {}", ld);
+                        try {
+                            notificationService.putNotification(ld);
+                        } catch (InterruptedException e) {
+                            LOG.warn("Interrupted while publishing notification {}", ld, e);
+                        }
                     }
                 } else {
                     LOG.trace("Skip publishing the add event for link because controller is non-owner of the "
index 32af8721dc0180e09f7a723e0847c8b95f018646..a2f62446a1bc8a08c4db83d62de0866d4d01f8a5 100644 (file)
@@ -8,6 +8,8 @@
 package org.opendaylight.openflowplugin.applications.topology.lldp;
 
 import com.google.common.annotations.VisibleForTesting;
+
+import java.util.Collection;
 import java.util.Date;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -19,7 +21,15 @@ import javax.annotation.PreDestroy;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 import org.apache.aries.blueprint.annotation.service.Reference;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.api.ClusteredDataTreeChangeListener;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.binding.api.DataObjectModification;
+import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
+import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
+import org.opendaylight.mdsal.binding.api.DataTreeModification;
 import org.opendaylight.mdsal.binding.api.NotificationPublishService;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.eos.binding.api.EntityOwnershipService;
 import org.opendaylight.openflowplugin.api.openflow.configuration.ConfigurationListener;
 import org.opendaylight.openflowplugin.api.openflow.configuration.ConfigurationService;
@@ -30,33 +40,55 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.topology.discovery.rev
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.lldp.discovery.config.rev160511.TopologyLldpDiscoveryConfig;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @Singleton
-public class LLDPLinkAger implements ConfigurationListener, AutoCloseable {
+public class LLDPLinkAger implements ConfigurationListener, ClusteredDataTreeChangeListener<Link>, AutoCloseable {
     private static final Logger LOG = LoggerFactory.getLogger(LLDPLinkAger.class);
+    static final String TOPOLOGY_ID = "flow:1";
+    static final InstanceIdentifier<Link> II_TO_LINK = InstanceIdentifier.create(NetworkTopology.class)
+            .child(Topology.class, new TopologyKey(new TopologyId(TOPOLOGY_ID))).child(Link.class);
+
     private final long linkExpirationTime;
     private final Map<LinkDiscovered, Date> linkToDate;
     private final Timer timer;
     private final NotificationPublishService notificationService;
     private final AutoCloseable configurationServiceRegistration;
     private final EntityOwnershipService eos;
+    private ListenerRegistration<DataTreeChangeListener> listenerRegistration;
 
     /**
      * default ctor - start timer.
      */
     @Inject
+    @SuppressWarnings("checkstyle:IllegalCatch")
     public LLDPLinkAger(final TopologyLldpDiscoveryConfig topologyLldpDiscoveryConfig,
-            @Reference final NotificationPublishService notificationService,
-            @Reference final ConfigurationService configurationService,
-            @Reference final EntityOwnershipService entityOwnershipService) {
+                        @Reference final NotificationPublishService notificationService,
+                        @Reference final ConfigurationService configurationService,
+                        @Reference final EntityOwnershipService entityOwnershipService,
+                        @Reference final DataBroker dataBroker) {
         this.linkExpirationTime = topologyLldpDiscoveryConfig.getTopologyLldpExpirationInterval().getValue();
         this.notificationService = notificationService;
         this.configurationServiceRegistration = configurationService.registerListener(this);
         this.eos = entityOwnershipService;
         linkToDate = new ConcurrentHashMap<>();
         timer = new Timer();
+        final DataTreeIdentifier dtiToNodeConnector = DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL,
+                II_TO_LINK);
+        try {
+            listenerRegistration = dataBroker.registerDataTreeChangeListener(dtiToNodeConnector, LLDPLinkAger.this);
+        } catch (Exception e) {
+            LOG.error("DataTreeChangeListeners registration failed:", e);
+            throw new IllegalStateException("LLDPLinkAger startup failed!", e);
+        }
         timer.schedule(new LLDPAgingTask(), 0, topologyLldpDiscoveryConfig.getTopologyLldpInterval().getValue());
     }
 
@@ -69,13 +101,68 @@ public class LLDPLinkAger implements ConfigurationListener, AutoCloseable {
     @Override
     @PreDestroy
     public void close() throws Exception {
+        if (listenerRegistration != null) {
+            listenerRegistration.close();
+            listenerRegistration = null;
+        }
         timer.cancel();
         linkToDate.clear();
         configurationServiceRegistration.close();
     }
 
-    private class LLDPAgingTask extends TimerTask {
+    @Override
+    public void onDataTreeChanged(@NonNull Collection<DataTreeModification<Link>> changes) {
+        for (DataTreeModification modification : changes) {
+            switch (modification.getRootNode().getModificationType()) {
+                case WRITE:
+                    break;
+                case SUBTREE_MODIFIED:
+                    // NOOP
+                    break;
+                case DELETE:
+                    processLinkDeleted(modification.getRootNode());
+                    break;
+                default:
+                    LOG.error("Unhandled modification type: {}", modification.getRootNode().getModificationType());
+            }
+        }
+    }
+
+    @VisibleForTesting
+    public boolean isLinkToDateEmpty() {
+        return linkToDate.isEmpty();
+    }
 
+    @Override
+    public void onPropertyChanged(@Nonnull final String propertyName, @Nonnull final String propertyValue) {
+        final TopologyLLDPDiscoveryProperty lldpDiscoveryProperty = TopologyLLDPDiscoveryProperty.forValue(
+                propertyName);
+        if (lldpDiscoveryProperty != null) {
+            switch (lldpDiscoveryProperty) {
+                case LLDP_SECURE_KEY:
+                case TOPOLOGY_LLDP_INTERVAL:
+                case TOPOLOGY_LLDP_EXPIRATION_INTERVAL:
+                    LOG.warn("Runtime update not supported for property {}", lldpDiscoveryProperty);
+                    break;
+                default:
+                    LOG.warn("No topology lldp discovery property found.");
+                    break;
+            }
+        }
+    }
+
+    protected boolean isLinkPresent(final LinkDiscovered linkDiscovered) {
+        return linkToDate.containsKey(linkDiscovered);
+    }
+
+    private void processLinkDeleted(DataObjectModification rootNode) {
+        Link link = (Link) rootNode.getDataBefore();
+        LOG.trace("Removing link {} from linkToDate cache", link);
+        LinkDiscovered linkDiscovered = LLDPDiscoveryUtils.toLLDPLinkDiscovered(link);
+        linkToDate.remove(linkDiscovered);
+    }
+
+    private class LLDPAgingTask extends TimerTask {
         @Override
         public void run() {
             for (Entry<LinkDiscovered, Date> entry : linkToDate.entrySet()) {
@@ -85,7 +172,6 @@ public class LLDPLinkAger implements ConfigurationListener, AutoCloseable {
                 if (now.after(expires)) {
                     if (notificationService != null) {
                         LinkRemovedBuilder lrb = new LinkRemovedBuilder(link);
-
                         NodeKey nodeKey = link.getDestination().getValue().firstKeyOf(Node.class);
                         LOG.info("No update received for link {} from last {} milliseconds. Removing link from cache.",
                                 link, linkExpirationTime);
@@ -107,27 +193,4 @@ public class LLDPLinkAger implements ConfigurationListener, AutoCloseable {
             }
         }
     }
-
-    @VisibleForTesting
-    public boolean isLinkToDateEmpty() {
-        return linkToDate.isEmpty();
-    }
-
-    @Override
-    public void onPropertyChanged(@Nonnull final String propertyName, @Nonnull final String propertyValue) {
-        final TopologyLLDPDiscoveryProperty lldpDiscoveryProperty = TopologyLLDPDiscoveryProperty.forValue(
-            propertyName);
-        if (lldpDiscoveryProperty != null) {
-            switch (lldpDiscoveryProperty) {
-                case LLDP_SECURE_KEY:
-                case TOPOLOGY_LLDP_INTERVAL:
-                case TOPOLOGY_LLDP_EXPIRATION_INTERVAL:
-                    LOG.warn("Runtime update not supported for property {}", lldpDiscoveryProperty);
-                    break;
-                default:
-                    LOG.warn("No topology lldp discovery property found.");
-                    break;
-            }
-        }
-    }
 }
index 5681e18ceaf6acce49301403d8638f98457ce8c6..817499eb5f8d25ff30388816cd4ee10026ea3154 100644 (file)
@@ -28,6 +28,7 @@ import org.opendaylight.openflowplugin.libraries.liblldp.LLDP;
 import org.opendaylight.openflowplugin.libraries.liblldp.LLDPTLV;
 import org.opendaylight.openflowplugin.libraries.liblldp.NetUtils;
 import org.opendaylight.openflowplugin.libraries.liblldp.PacketException;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.topology.discovery.rev130819.LinkDiscoveredBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
@@ -36,6 +37,8 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.No
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TpId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -158,19 +161,27 @@ public final class LLDPDiscoveryUtils {
         return hashedValue.asBytes();
     }
 
-    private static boolean checkExtraAuthenticator(LLDP lldp, NodeConnectorId srcNodeConnectorId) {
-        final LLDPTLV hashLldptlv = lldp.getCustomTLV(LLDPTLV.createSecSubTypeCustomTLVKey());
-        boolean secAuthenticatorOk = false;
-        if (hashLldptlv != null) {
-            byte[] rawTlvValue = hashLldptlv.getValue();
-            byte[] lldpCustomSecurityHash = ArrayUtils.subarray(rawTlvValue, 4, rawTlvValue.length);
-            byte[] calculatedHash = getValueForLLDPPacketIntegrityEnsuring(srcNodeConnectorId);
-            secAuthenticatorOk = Arrays.equals(calculatedHash, lldpCustomSecurityHash);
+    public static boolean isEntityOwned(final EntityOwnershipService eos, final String nodeId) {
+        Preconditions.checkNotNull(eos, "Entity ownership service must not be null");
+
+        EntityOwnershipState state = null;
+        Optional<EntityOwnershipState> status = getCurrentOwnershipStatus(eos, nodeId);
+        if (status.isPresent()) {
+            state = status.get();
         } else {
-            LOG.debug("Custom security hint wasn't specified via Custom TLV in LLDP packet.");
+            LOG.error("Fetching ownership status failed for node {}", nodeId);
         }
+        return state != null && state.equals(EntityOwnershipState.IS_OWNER);
+    }
 
-        return secAuthenticatorOk;
+    public static org.opendaylight.yang.gen.v1.urn.opendaylight.flow.topology.discovery.rev130819
+            .LinkDiscovered toLLDPLinkDiscovered(Link link) {
+        return new LinkDiscoveredBuilder()
+                .setSource(getNodeConnectorRefFromLink(link.getSource().getSourceTp(),
+                        link.getSource().getSourceNode()))
+                .setDestination(getNodeConnectorRefFromLink(link.getDestination().getDestTp(),
+                        link.getDestination().getDestNode()))
+                .build();
     }
 
     private static boolean isLLDP(final byte[] packet) {
@@ -189,17 +200,19 @@ public final class LLDPDiscoveryUtils {
         return ethernetType == ETHERNET_TYPE_LLDP;
     }
 
-    public static boolean isEntityOwned(final EntityOwnershipService eos, final String nodeId) {
-        Preconditions.checkNotNull(eos, "Entity ownership service must not be null");
-
-        EntityOwnershipState state = null;
-        Optional<EntityOwnershipState> status = getCurrentOwnershipStatus(eos, nodeId);
-        if (status.isPresent()) {
-            state = status.get();
+    private static boolean checkExtraAuthenticator(LLDP lldp, NodeConnectorId srcNodeConnectorId) {
+        final LLDPTLV hashLldptlv = lldp.getCustomTLV(LLDPTLV.createSecSubTypeCustomTLVKey());
+        boolean secAuthenticatorOk = false;
+        if (hashLldptlv != null) {
+            byte[] rawTlvValue = hashLldptlv.getValue();
+            byte[] lldpCustomSecurityHash = ArrayUtils.subarray(rawTlvValue, 4, rawTlvValue.length);
+            byte[] calculatedHash = getValueForLLDPPacketIntegrityEnsuring(srcNodeConnectorId);
+            secAuthenticatorOk = Arrays.equals(calculatedHash, lldpCustomSecurityHash);
         } else {
-            LOG.error("Fetching ownership status failed for node {}", nodeId);
+            LOG.debug("Custom security hint wasn't specified via Custom TLV in LLDP packet.");
         }
-        return state != null && state.equals(EntityOwnershipState.IS_OWNER);
+
+        return secAuthenticatorOk;
     }
 
     private static Optional<EntityOwnershipState> getCurrentOwnershipStatus(final EntityOwnershipService eos,
@@ -216,4 +229,20 @@ public final class LLDPDiscoveryUtils {
     private static Entity createNodeEntity(final String nodeId) {
         return new Entity(SERVICE_ENTITY_TYPE, nodeId);
     }
+
+    private static NodeConnectorRef getNodeConnectorRefFromLink(final TpId tpId, final org.opendaylight.yang.gen.v1.urn
+            .tbd.params.xml.ns.yang.network.topology.rev131021.NodeId nodeId) {
+        String nodeConnectorId = tpId.getValue();
+        InstanceIdentifier<NodeConnector> nciid
+                = InstanceIdentifier.builder(Nodes.class)
+                .child(
+                        Node.class,
+                        new NodeKey(new org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819
+                                .NodeId(nodeId)))
+                .child(
+                        NodeConnector.class,
+                        new NodeConnectorKey(new NodeConnectorId(nodeConnectorId)))
+                .build();
+        return new NodeConnectorRef(nciid);
+    }
 }
index aea9f55be8ea8b55640ceead6e793e9951ce42b9..5c259265a0e8a2d8ac0d311a84ccc1cad33fe85a 100644 (file)
@@ -20,6 +20,7 @@ import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.mdsal.binding.api.DataBroker;
 import org.opendaylight.mdsal.binding.api.NotificationPublishService;
 import org.opendaylight.mdsal.eos.binding.api.Entity;
 import org.opendaylight.mdsal.eos.binding.api.EntityOwnershipService;
@@ -64,11 +65,14 @@ public class LLDPLinkAgerTest {
     @Mock
     private EntityOwnershipService eos;
     @Mock
+    private DataBroker dataBroker;
+    @Mock
     private LinkRemoved linkRemoved;
 
+
     @Before
     public void setUp() {
-        lldpLinkAger = new LLDPLinkAger(getConfig(), notificationService, getConfigurationService(), eos);
+        lldpLinkAger = new LLDPLinkAger(getConfig(), notificationService, getConfigurationService(), eos, dataBroker);
         Mockito.when(link.getDestination()).thenReturn(new NodeConnectorRef(
                 InstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(new NodeId("openflow:1")))));
         Mockito.when(eos.getOwnershipState(any(Entity.class))).thenReturn(