org.opendaylight.l2switch.hosttracker.plugin.internal.HostTrackerImpl Maven / Gradle / Ivy
/**
* Copyright (c) 2014 André Martins, Colin Dixon, Evan Zeller and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.l2switch.hosttracker.plugin.internal;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.l2switch.hosttracker.plugin.inventory.Host;
import org.opendaylight.l2switch.hosttracker.plugin.util.Utilities;
import org.opendaylight.yang.gen.v1.urn.opendaylight.address.tracker.rev140617.AddressCapableNodeConnector;
import org.opendaylight.yang.gen.v1.urn.opendaylight.address.tracker.rev140617.address.node.connector.Addresses;
import org.opendaylight.yang.gen.v1.urn.opendaylight.host.tracker.rev140624.HostId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.host.tracker.rev140624.HostNode;
import org.opendaylight.yang.gen.v1.urn.opendaylight.host.tracker.rev140624.host.AttachmentPointsBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.host.tracker.config.rev140528.HostTrackerConfig;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.LinkId;
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.TpId;
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.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HostTrackerImpl implements DataChangeListener {
private static final int CPUS = Runtime.getRuntime().availableProcessors();
/**
* As defined on
* controller/opendaylight/md-sal/topology-manager/src/main/java/org/opendaylight/md/controller/topology/manager/FlowCapableTopologyProvider.java
*/
private static final String TOPOLOGY_NAME = "flow:1";
private static final Logger LOG = LoggerFactory.getLogger(HostTrackerImpl.class);
private final DataBroker dataService;
private final String topologyId;
private final long hostPurgeInterval;
private final long hostPurgeAge;
private static int numHostsPurged;
private ScheduledExecutorService exec = Executors.newScheduledThreadPool(CPUS);
private final ConcurrentClusterAwareHostHashMap hosts;
private final ConcurrentClusterAwareLinkHashMap links;
private final OperationProcessor opProcessor;
private ListenerRegistration addrsNodeListerRegistration;
private ListenerRegistration hostNodeListerRegistration;
/**
* It creates hosts using reference to MD-SAl / toplogy module. For every hostPurgeIntervalInput time interval
* it requests to purge hosts that are not seen for hostPurgeAgeInput time interval.
*
* @param dataService A reference to the MD-SAL
* @param config Default configuration
*/
public HostTrackerImpl(final DataBroker dataService, final HostTrackerConfig config) {
Preconditions.checkNotNull(dataService, "dataBrokerService should not be null.");
Preconditions.checkArgument(config.getHostPurgeAge() >= 0, "hostPurgeAgeInput must be non-negative");
Preconditions.checkArgument(config.getHostPurgeInterval() >= 0, "hostPurgeIntervalInput must be non-negative");
this.dataService = dataService;
this.hostPurgeAge = config.getHostPurgeAge();
this.hostPurgeInterval = config.getHostPurgeInterval();
this.opProcessor = new OperationProcessor(dataService);
Thread processorThread = new Thread(opProcessor);
processorThread.start();
final String topologyId = config.getTopologyId();
if (topologyId == null || topologyId.isEmpty()) {
this.topologyId = TOPOLOGY_NAME;
} else {
this.topologyId = topologyId;
}
this.hosts = new ConcurrentClusterAwareHostHashMap<>(opProcessor, this.topologyId);
this.links = new ConcurrentClusterAwareLinkHashMap<>(opProcessor, this.topologyId);
if (hostPurgeInterval > 0) {
exec.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
purgeHostsNotSeenInLast(hostPurgeAge);
}
}, 0, hostPurgeInterval, TimeUnit.SECONDS);
}
}
public void init() {
InstanceIdentifier addrCapableNodeConnectors = //
InstanceIdentifier.builder(Nodes.class) //
.child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class) //
.child(NodeConnector.class) //
.augmentation(AddressCapableNodeConnector.class)//
.child(Addresses.class).build();
this.addrsNodeListerRegistration = dataService.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, addrCapableNodeConnectors, this, DataChangeScope.SUBTREE);
InstanceIdentifier hostNodes = InstanceIdentifier.builder(NetworkTopology.class)//
.child(Topology.class, new TopologyKey(new TopologyId(topologyId)))//
.child(Node.class)
.augmentation(HostNode.class).build();
this.hostNodeListerRegistration = dataService.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, hostNodes, this, DataChangeScope.SUBTREE);
InstanceIdentifier lIID = InstanceIdentifier.builder(NetworkTopology.class)//
.child(Topology.class, new TopologyKey(new TopologyId(topologyId)))//
.child(Link.class).build();
this.addrsNodeListerRegistration = dataService.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, lIID, this, DataChangeScope.BASE);
//Processing addresses that existed before we register as a data change listener.
// ReadOnlyTransaction newReadOnlyTransaction = dataService.newReadOnlyTransaction();
// InstanceIdentifier iinc = addrCapableNodeConnectors.firstIdentifierOf(NodeConnector.class);
// InstanceIdentifier iin//
// = addrCapableNodeConnectors.firstIdentifierOf(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class);
// ListenableFuture> dataFuture = newReadOnlyTransaction.read(LogicalDatastoreType.OPERATIONAL, iinc);
// try {
// NodeConnector get = dataFuture.get().get();
// log.trace("test "+get);
// } catch (InterruptedException | ExecutionException ex) {
// java.util.logging.Logger.getLogger(HostTrackerImpl.class.getName()).log(Level.SEVERE, null, ex);
// }
// Futures.addCallback(dataFuture, new FutureCallback>() {
// @Override
// public void onSuccess(final Optional result) {
// if (result.isPresent()) {
// log.trace("Processing NEW NODE? " + result.get().getId().getValue());
//// processHost(result, dataObject, node);
// }
// }
//
// @Override
// public void onFailure(Throwable arg0) {
// }
// });
}
@Override
public void onDataChanged(final AsyncDataChangeEvent, DataObject> change) {
exec.submit(new Runnable() {
@Override
public void run() {
if (change == null) {
LOG.info("In onDataChanged: No processing done as change even is null.");
return;
}
Map, DataObject> updatedData = change.getUpdatedData();
Map, DataObject> createdData = change.getCreatedData();
Map, DataObject> originalData = change.getOriginalData();
Set> deletedData = change.getRemovedPaths();
for (InstanceIdentifier> iid : deletedData) {
if (iid.getTargetType().equals(Node.class)) {
Node node = ((Node) originalData.get(iid));
InstanceIdentifier iiN = (InstanceIdentifier) iid;
HostNode hostNode = node.getAugmentation(HostNode.class);
if (hostNode != null) {
synchronized (hosts) {
try {
hosts.removeLocally(iiN);
} catch (ClassCastException ex) {
LOG.debug("Exception occurred while remove host locally", ex);
}
}
}
} else if (iid.getTargetType().equals(Link.class)) {
// TODO performance improvement here
InstanceIdentifier iiL = (InstanceIdentifier) iid;
synchronized (links) {
try {
links.removeLocally(iiL);
} catch (ClassCastException ex) {
LOG.debug("Exception occurred while remove link locally", ex);
}
}
linkRemoved((InstanceIdentifier) iid, (Link) originalData.get(iid));
}
}
for (Map.Entry, DataObject> entrySet : updatedData.entrySet()) {
InstanceIdentifier> iiD = entrySet.getKey();
final DataObject dataObject = entrySet.getValue();
if (dataObject instanceof Addresses) {
packetReceived((Addresses) dataObject, iiD);
} else if (dataObject instanceof Node) {
synchronized (hosts) {
hosts.putLocally((InstanceIdentifier) iiD, Host.createHost((Node) dataObject));
}
} else if (dataObject instanceof Link) {
synchronized (links) {
links.putLocally((InstanceIdentifier) iiD, (Link) dataObject);
}
}
}
for (Map.Entry, DataObject> entrySet : createdData.entrySet()) {
InstanceIdentifier> iiD = entrySet.getKey();
final DataObject dataObject = entrySet.getValue();
if (dataObject instanceof Addresses) {
packetReceived((Addresses) dataObject, iiD);
} else if (dataObject instanceof Node) {
synchronized (hosts) {
hosts.putLocally((InstanceIdentifier) iiD, Host.createHost((Node) dataObject));
}
} else if (dataObject instanceof Link) {
synchronized (links) {
links.putLocally((InstanceIdentifier) iiD, (Link) dataObject);
}
}
}
}
});
}
public void packetReceived(Addresses addrs, InstanceIdentifier> ii) {
InstanceIdentifier iinc = ii.firstIdentifierOf(NodeConnector.class);
InstanceIdentifier iin//
= ii.firstIdentifierOf(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class);
ListenableFuture> futureNodeConnector;
ListenableFuture> futureNode;
try (ReadOnlyTransaction readTx = dataService.newReadOnlyTransaction()) {
futureNodeConnector = readTx.read(LogicalDatastoreType.OPERATIONAL, iinc);
futureNode = readTx.read(LogicalDatastoreType.OPERATIONAL, iin);
readTx.close();
}
Optional opNodeConnector = null;
Optional opNode = null;
try {
opNodeConnector = futureNodeConnector.get();
opNode = futureNode.get();
} catch (ExecutionException | InterruptedException ex) {
LOG.warn(ex.getLocalizedMessage());
}
if (opNode != null && opNode.isPresent()
&& opNodeConnector != null && opNodeConnector.isPresent()) {
processHost(opNode.get(), opNodeConnector.get(), addrs);
}
}
private void processHost(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node node,
NodeConnector nodeConnector,
Addresses addrs) {
List hostsToMod = new ArrayList<>();
List hostsToRem = new ArrayList<>();
List linksToRem = new ArrayList<>();
List linksToAdd = new ArrayList<>();
synchronized (hosts) {
LOG.trace("Processing nodeConnector: {} ", nodeConnector.getId().toString());
HostId hId = Host.createHostId(addrs);
if (hId != null) {
if (isNodeConnectorInternal(nodeConnector)) {
LOG.trace("NodeConnector is internal: {} ", nodeConnector.getId().toString());
removeNodeConnectorFromHost(hostsToMod, hostsToRem, nodeConnector);
hosts.removeAll(hostsToRem);
hosts.putAll(hostsToMod);
} else {
LOG.trace("NodeConnector is NOT internal {} ", nodeConnector.getId().toString());
Host host = new Host(addrs, nodeConnector);
if (hosts.containsKey(host.getId())) {
hosts.get(host.getId()).mergeHostWith(host);
} else {
hosts.put(host.getId(), host);
}
List newLinks = hosts.get(host.getId()).createLinks(node);
if (newLinks != null) {
linksToAdd.addAll(newLinks);
}
hosts.submit(host.getId());
}
}
}
writeDatatoMDSAL(linksToAdd, linksToRem);
}
/**
* It verifies if a given NodeConnector is *internal*. An *internal*
* NodeConnector is considered to be all NodeConnetors that are NOT attached
* to hosts created by hosttracker.
*
* @param nodeConnector the nodeConnector to check if it is internal or not.
* @return true if it was found a host connected to this nodeConnetor, false
* if it was not found a network topology or it was not found a host
* connected to this nodeConnetor.
*/
private boolean isNodeConnectorInternal(NodeConnector nodeConnector) {
TpId tpId = new TpId(nodeConnector.getKey().getId().getValue());
InstanceIdentifier ntII
= InstanceIdentifier.builder(NetworkTopology.class).build();
ListenableFuture> lfONT;
try (ReadOnlyTransaction rot = dataService.newReadOnlyTransaction()) {
lfONT = rot.read(LogicalDatastoreType.OPERATIONAL, ntII);
rot.close();
}
Optional oNT;
try {
oNT = lfONT.get();
} catch (InterruptedException | ExecutionException ex) {
LOG.warn(ex.getLocalizedMessage());
return false;
}
if (oNT != null && oNT.isPresent()) {
NetworkTopology networkTopo = oNT.get();
for (Topology t : networkTopo.getTopology()) {
if (t.getLink() != null) {
for (Link l : t.getLink()) {
if ((l.getSource().getSourceTp().equals(tpId)
&& !l.getDestination().getDestTp().getValue().startsWith(Host.NODE_PREFIX))
|| (l.getDestination().getDestTp().equals(tpId)
&& !l.getSource().getSourceTp().getValue().startsWith(Host.NODE_PREFIX))) {
return true;
}
}
}
}
}
return false;
}
private void removeLinksFromHosts(List hostsToMod, List hostsToRem, Link linkRemoved) {
for (Host h : hosts.values()) {
h.removeTerminationPoint(linkRemoved.getSource().getSourceTp());
h.removeTerminationPoint(linkRemoved.getDestination().getDestTp());
if (h.isOrphan()) {
hostsToRem.add(h);
} else {
hostsToMod.add(h);
}
}
}
private void removeNodeConnectorFromHost(List hostsToMod, List hostsToRem, NodeConnector nc) {
AttachmentPointsBuilder atStD = Utilities.createAPsfromNodeConnector(nc);
for (Host h : hosts.values()) {
h.removeAttachmentPoints(atStD);
if (h.isOrphan()) {
hostsToRem.add(h);
} else {
hostsToMod.add(h);
}
}
}
private void linkRemoved(InstanceIdentifier iiLink, Link linkRemoved) {
LOG.trace("linkRemoved");
List hostsToMod = new ArrayList<>();
List hostsToRem = new ArrayList<>();
synchronized (hosts) {
removeLinksFromHosts(hostsToMod, hostsToRem, linkRemoved);
hosts.removeAll(hostsToRem);
hosts.putAll(hostsToMod);
}
}
private void writeDatatoMDSAL(List linksToAdd, List linksToRemove) {
if (linksToAdd != null) {
for (final Link l : linksToAdd) {
final InstanceIdentifier lIID = Utilities.buildLinkIID(l.getKey(), topologyId);
LOG.trace("Writing link from MD_SAL: {}", lIID.toString());
opProcessor.enqueueOperation(new HostTrackerOperation() {
@Override
public void applyOperation(ReadWriteTransaction tx) {
tx.merge(LogicalDatastoreType.OPERATIONAL, lIID, l, true);
}
});
}
}
if (linksToRemove != null) {
for (Link l : linksToRemove) {
final InstanceIdentifier lIID = Utilities.buildLinkIID(l.getKey(), topologyId);
LOG.trace("Removing link from MD_SAL: {}", lIID.toString());
opProcessor.enqueueOperation(new HostTrackerOperation() {
@Override
public void applyOperation(ReadWriteTransaction tx) {
tx.delete(LogicalDatastoreType.OPERATIONAL, lIID);
}
});
}
}
}
/**
* Remove all hosts that haven't been observed more recently than the specified number of
* hostsPurgeAgeInSeconds.
*
* @param hostsPurgeAgeInSeconds remove hosts that haven't been observed in longer than this number of
* hostsPurgeAgeInSeconds.
* @return the number of purged hosts
*/
protected int purgeHostsNotSeenInLast(final long hostsPurgeAgeInSeconds) {
numHostsPurged = 0;
final long nowInMillis = System.currentTimeMillis();
final long nowInSeconds = TimeUnit.MILLISECONDS.toSeconds(nowInMillis);
// iterate through all hosts in the local cache
for (Host h : hosts.values()) {
final HostNode hn = h.getHostNode().getAugmentation(HostNode.class);
if (hn == null) {
LOG.warn("Encountered non-host node {} in hosts during purge", hn);
} else if (hn.getAddresses() != null) {
boolean purgeHosts = false;
// if the node is a host and has addresses, check to see if it's been seen recently
purgeHosts = hostReadyForPurge( hn, nowInSeconds,hostsPurgeAgeInSeconds);
if (purgeHosts) {
removeHosts(h);
}
} else {
LOG.warn("Encountered host node {} with no address in hosts during purge", hn);
}
}
LOG.debug("Number of purged hosts during current purge interval - {}. ", numHostsPurged);
return numHostsPurged;
}
/**
* Checks if hosts need to be purged
*
* @param hostNode reference to HostNode class
* @param currentTimeInSeconds current time in seconds
* @param expirationPeriod timelimit set to hosts for expiration
* @return boolean - whether the hosts are ready to be purged
*/
private boolean hostReadyForPurge(final HostNode hostNode,final long currentTimeInSeconds,final long expirationPeriod) {
// checks if hosts need to be purged
for (Addresses addrs : hostNode.getAddresses()) {
long lastSeenTimeInSeconds = addrs.getLastSeen()/1000;
if (lastSeenTimeInSeconds > (currentTimeInSeconds - expirationPeriod)) {
LOG.debug("Host node {} NOT ready for purge", hostNode);
return false;
}
}
LOG.debug("Host node {} ready for purge", hostNode);
return true;
}
/**
* Removes hosts from locally and MD-SAL. Throws warning message if not removed successfully
*
* @param host reference to Host node
*/
private void removeHosts(final Host host){
// remove associated links with the host before removing hosts
removeAssociatedLinksFromHosts(host);
// purge hosts from local & MD-SAL database
if (hosts.remove(host.getId()) != null) {
numHostsPurged++;
LOG.debug("Removed host with id {} during purge.", host.getId());
} else {
LOG.warn("Unexpected error encountered - Failed to remove host {} during purge", host);
}
}
/**
* Removes links associated with the given hosts from local and MD-SAL database.
* Throws warning message if not removed successfully.
*
* @param host reference to Host node
*/
private void removeAssociatedLinksFromHosts(final Host host) {
if (host != null) {
if (host.getId() != null) {
List linksToRemove = new ArrayList<>();
for (Link link: links.values()) {
if (link.toString().contains(host.getId().getValue())) {
linksToRemove.add(link);
}
}
links.removeAll(linksToRemove);
} else {
LOG.warn("Encountered host with no id , Unexpected host id {}. ", host);
}
} else {
LOG.warn("Encountered Host with no value, Unexpected host {}. ", host);
}
}
public void close() {
this.addrsNodeListerRegistration.close();
this.hostNodeListerRegistration.close();
this.exec.shutdownNow();
synchronized (hosts) {
this.hosts.clear();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy