package org.dcache.webadmin.model.dataaccess.communication.collectors;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import diskCacheV111.poolManager.PoolManagerCellInfo;
import diskCacheV111.util.CacheException;
import dmg.cells.network.PingMessage;
import dmg.cells.nucleus.CellAddressCore;
import dmg.cells.nucleus.CellInfo;
import dmg.cells.nucleus.CellMessage;
import dmg.cells.nucleus.CellMessageReceiver;
import dmg.cells.nucleus.CellPath;
import dmg.cells.nucleus.NoRouteToCellException;
import dmg.cells.services.login.LoginBrokerInfo;
import org.dcache.admin.webadmin.datacollector.datatypes.CellStatus;
import org.dcache.cells.CellStub;
import org.dcache.util.backoff.IBackoffAlgorithm.Status;
import org.dcache.webadmin.model.dataaccess.communication.ContextPaths;
/**
*
* @author jans
*/
public class CellStatusCollector extends Collector implements CellMessageReceiver
{
// After 2 days a cell is considered removed and will no longer be queried
private static final long CONSIDERED_REMOVED_TIME_MS = 172800000;
private Collection<LoginBrokerInfo> _doors;
private String _poolManagerName;
private Map<CellAddressCore, CellStatus> _statusTargets = new HashMap<>();
private static final Logger _log = LoggerFactory.getLogger(CellStatusCollector.class);
private CellStub _watchedServicesStub;
private Set<CellAddressCore> _watchedServices = ConcurrentHashMap.newKeySet();
private Set<CellAddressCore> getPoolCells() throws InterruptedException {
_log.debug("Requesting Pools from {}", _poolManagerName);
Set<CellAddressCore> pools;
try {
PoolManagerCellInfo info = _cellStub.sendAndWait(new CellPath(_poolManagerName),
"xgetcellinfo",
PoolManagerCellInfo.class);
pools = info.getPoolCells();
_log.debug("Pools found: {}", pools);
} catch (NoRouteToCellException | CacheException ex) {
pools = Collections.emptySet();
_log.debug("Could not retrieve Pools from {}", _poolManagerName);
}
return pools;
}
private void addLoginBrokerTargets(Set<CellAddressCore> targetCells)
throws InterruptedException
{
_doors.stream()
.map(i -> new CellAddressCore(i.getCellName(), i.getDomainName()))
.forEach(targetCells::add);
}
private Set<CellAddressCore> getTargetCells() throws InterruptedException {
Set<CellAddressCore> targetCells = new HashSet<>();
addLoginBrokerTargets(targetCells);
targetCells.addAll(getPoolCells());
targetCells.addAll(getWatchedServices());
return targetCells;
}
private Collection<CellAddressCore> getWatchedServices()
{
return _watchedServices;
}
private void retrieveCellInfos() throws InterruptedException {
CountDownLatch doneSignal = new CountDownLatch(_statusTargets.size());
for (CellStatus status : _statusTargets.values()) {
_log.debug("Sending query to : {}", status.getCellPath());
CellInfoCallback callback = new CellInfoCallback(status, doneSignal);
Futures.addCallback(_cellStub.send(status.getCellPath(), "xgetcellinfo", CellInfo.class),
callback);
}
doneSignal.await(_cellStub.getTimeout(), _cellStub.getTimeoutUnit());
_log.debug("Queries finished or timeouted");
}
private void collectCellStates() throws InterruptedException {
pingWatchedServices();
Set<CellAddressCore> targetCells = getTargetCells();
addNewTargets(checkForNewTargets(targetCells));
subtractGoneTargets();
retrieveCellInfos();
_pageCache.put(ContextPaths.CELLINFO_LIST, ImmutableSet.copyOf(_statusTargets.values()));
}
private void pingWatchedServices()
{
_watchedServicesStub.send(new PingMessage(), PingMessage.class);
}
public void messageArrived(CellMessage envelope, PingMessage message)
{
_watchedServices.add(envelope.getSourceAddress());
}
private Set<CellAddressCore> checkForNewTargets(Set<CellAddressCore> targetCells) {
Set<CellAddressCore> newTargets = new HashSet<>();
for (CellAddressCore target : targetCells) {
if (!_statusTargets.containsKey(target)) {
newTargets.add(target);
}
}
return newTargets;
}
private void addNewTargets(Set<CellAddressCore> newTargets) {
for (CellAddressCore target : newTargets) {
CellStatus newStatus = new CellStatus(target);
_statusTargets.put(target, newStatus);
_log.debug("Added new Target {}", target);
}
}
private void subtractGoneTargets() {
Collection<CellStatus> removables = findRemovableTargets();
for (CellStatus status : removables) {
CellAddressCore address = status.getCellAddress();
_statusTargets.remove(address);
_log.debug("Removed Target {}", address);
}
}
private Collection<CellStatus> findRemovableTargets() {
Collection<CellStatus> removables = new ArrayList<>();
for (CellStatus status : _statusTargets.values()) {
if ((System.currentTimeMillis() - status.getLastAliveTime()) >
CONSIDERED_REMOVED_TIME_MS) {
removables.add(status);
}
}
return removables;
}
public void setDoors(Collection<LoginBrokerInfo> doors) {
_doors = doors;
}
public void setPoolManagerName(String poolManagerName) {
_poolManagerName = poolManagerName;
}
public void setWatchedServicesStub(CellStub cellstub) {
_watchedServicesStub = cellstub;
}
private class CellInfoCallback implements FutureCallback<CellInfo>
{
private CellStatus _cellStatus;
private long _callbackCreationTime;
private CountDownLatch _doneSignal;
public CellInfoCallback(CellStatus status, CountDownLatch doneSignal) {
_cellStatus = status;
_callbackCreationTime = System.currentTimeMillis();
_doneSignal = doneSignal;
}
@Override
public void onSuccess(CellInfo message)
{
_cellStatus.setCellInfo(message);
_cellStatus.updateLastAliveTime();
long now = System.currentTimeMillis();
_cellStatus.setPing(now - _callbackCreationTime);
_doneSignal.countDown();
}
@Override
public void onFailure(Throwable t)
{
resetCellStatus();
_doneSignal.countDown();
}
private void resetCellStatus() {
_cellStatus.setPingUnreached();
_cellStatus.setThreadCount(0);
_cellStatus.setEventQueueSize(0);
}
}
/* (non-Javadoc)
* @see java.util.concurrent.Callable#call()
*/
@Override
public Status call() throws InterruptedException {
collectCellStates();
return Status.SUCCESS;
}
}