X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=applications%2Ftopology-lldp-discovery%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fopenflowplugin%2Fapplications%2Ftopology%2Flldp%2FLLDPLinkAger.java;h=c4404d9d97c654259edc226ec67328a8e783b377;hb=0c0fd716ab751bb4b7c3f8f0c125626ce6d4bf34;hp=499d05e4102b37a682c25fc8f2413cf9626e6f58;hpb=a919b2ff847f8413eee50ac99f93b49428199c75;p=openflowplugin.git diff --git a/applications/topology-lldp-discovery/src/main/java/org/opendaylight/openflowplugin/applications/topology/lldp/LLDPLinkAger.java b/applications/topology-lldp-discovery/src/main/java/org/opendaylight/openflowplugin/applications/topology/lldp/LLDPLinkAger.java index 499d05e410..c4404d9d97 100644 --- a/applications/topology-lldp-discovery/src/main/java/org/opendaylight/openflowplugin/applications/topology/lldp/LLDPLinkAger.java +++ b/applications/topology-lldp-discovery/src/main/java/org/opendaylight/openflowplugin/applications/topology/lldp/LLDPLinkAger.java @@ -7,66 +7,186 @@ */ package org.opendaylight.openflowplugin.applications.topology.lldp; +import com.google.common.annotations.VisibleForTesting; import java.util.Date; -import java.util.Map; -import java.util.Timer; +import java.util.List; import java.util.Map.Entry; +import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; - +import java.util.concurrent.ConcurrentMap; +import javax.annotation.PreDestroy; +import javax.inject.Inject; +import javax.inject.Singleton; +import org.eclipse.jdt.annotation.NonNull; +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; import org.opendaylight.openflowplugin.applications.topology.lldp.utils.LLDPDiscoveryUtils; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.topology.discovery.rev130819.LinkDiscovered; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.topology.discovery.rev130819.LinkRemoved; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.topology.discovery.rev130819.LinkRemovedBuilder; +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.Registration; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +@Singleton +public final class LLDPLinkAger implements ConfigurationListener, DataTreeChangeListener, AutoCloseable { + private static final Logger LOG = LoggerFactory.getLogger(LLDPLinkAger.class); + static final String TOPOLOGY_ID = "flow:1"; + static final InstanceIdentifier II_TO_LINK = InstanceIdentifier.create(NetworkTopology.class) + .child(Topology.class, new TopologyKey(new TopologyId(TOPOLOGY_ID))).child(Link.class); -public class LLDPLinkAger { - private static final LLDPLinkAger instance = new LLDPLinkAger(); - private Map linkToDate = new ConcurrentHashMap(); - private LLDPDiscoveryProvider manager; - private Timer timer = new Timer(); + private final long linkExpirationTime; + // FIXME: use Instant instead of Date + private final ConcurrentMap linkToDate = new ConcurrentHashMap<>(); + private final Timer timer = new Timer(); + private final NotificationPublishService notificationService; + private final AutoCloseable configurationServiceRegistration; + private final EntityOwnershipService eos; + private Registration listenerRegistration; - public LLDPDiscoveryProvider getManager() { - return manager; - } - public void setManager(LLDPDiscoveryProvider manager) { - this.manager = manager; - } - private LLDPLinkAger() { - timer.schedule(new LLDPAgingTask(), 0,LLDPDiscoveryUtils.LLDP_INTERVAL); - } - public static LLDPLinkAger getInstance() { - return instance; + /** + * default ctor - start timer. + */ + @Inject + @SuppressWarnings("checkstyle:IllegalCatch") + public LLDPLinkAger(final TopologyLldpDiscoveryConfig topologyLldpDiscoveryConfig, + final NotificationPublishService notificationService, + final ConfigurationService configurationService, + final EntityOwnershipService entityOwnershipService, + final DataBroker dataBroker) { + linkExpirationTime = topologyLldpDiscoveryConfig.getTopologyLldpExpirationInterval().getValue().toJava(); + this.notificationService = notificationService; + configurationServiceRegistration = configurationService.registerListener(this); + eos = entityOwnershipService; + final DataTreeIdentifier dtiToNodeConnector = DataTreeIdentifier.of(LogicalDatastoreType.OPERATIONAL, + II_TO_LINK); + try { + listenerRegistration = dataBroker.registerTreeChangeListener(dtiToNodeConnector, 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().toJava()); } - public void put(LinkDiscovered link) { + public void put(final LinkDiscovered link) { Date expires = new Date(); - expires.setTime(expires.getTime() + LLDPDiscoveryUtils.LLDP_EXPIRATION_TIME); + expires.setTime(expires.getTime() + linkExpirationTime); linkToDate.put(link, expires); } - public void close() { + @Override + @PreDestroy + public void close() throws Exception { + if (listenerRegistration != null) { + listenerRegistration.close(); + listenerRegistration = null; + } timer.cancel(); + linkToDate.clear(); + configurationServiceRegistration.close(); + } + + @Override + public void onDataTreeChanged(final List> changes) { + for (DataTreeModification modification : changes) { + switch (modification.getRootNode().modificationType()) { + case WRITE: + break; + case SUBTREE_MODIFIED: + // NOOP + break; + case DELETE: + processLinkDeleted(modification.getRootNode()); + break; + default: + LOG.error("Unhandled modification type: {}", modification.getRootNode().modificationType()); + } + } } - private class LLDPAgingTask extends TimerTask { + @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(final DataObjectModification rootNode) { + Link link = rootNode.dataBefore(); + LOG.trace("Removing link {} from linkToDate cache", link); + LinkDiscovered linkDiscovered = LLDPDiscoveryUtils.toLLDPLinkDiscovered(link); + linkToDate.remove(linkDiscovered); + } + private final class LLDPAgingTask extends TimerTask { @Override public void run() { - for (Entry entry : linkToDate.entrySet()) { + for (Entry entry : linkToDate.entrySet()) { LinkDiscovered link = entry.getKey(); Date expires = entry.getValue(); Date now = new Date(); - if(now.after(expires)) { - if(getInstance().getManager() != null) { + if (now.after(expires)) { + if (notificationService != null) { LinkRemovedBuilder lrb = new LinkRemovedBuilder(link); - getInstance().getManager().getNotificationService().publish(lrb.build()); + NodeKey nodeKey = link.getDestination().getValue().firstKeyOf(Node.class); + LOG.info("No update received for link {} from last {} milliseconds. Removing link from cache.", + link, linkExpirationTime); linkToDate.remove(link); + if (nodeKey != null && LLDPDiscoveryUtils.isEntityOwned(eos, nodeKey.getId().getValue())) { + LOG.info("Publish Link Remove event for the link {}", link); + final LinkRemoved lr = lrb.build(); + try { + notificationService.putNotification(lr); + } catch (InterruptedException e) { + LOG.warn("Interrupted while publishing notification {}", lr, e); + } + } else { + LOG.trace("Skip publishing Link Remove event for the link {} because link destination " + + "node is not owned by the controller", link); + } } } } - } - } } -