package org.dcache.services.info.secondaryInfoProviders;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.dcache.services.info.base.StateExhibitor;
import org.dcache.services.info.base.StatePath;
import org.dcache.services.info.base.StateUpdate;
import org.dcache.services.info.stateInfo.PoolSpaceVisitor;
import org.dcache.services.info.stateInfo.SetMapVisitor;
import org.dcache.services.info.stateInfo.SpaceInfo;
/**
* This StateWatcher maintains the sizes for a link by adding together the
* different spaces for a link's pools.
*
* @author Paul Millar <paul.millar@desy.de>
*/
public class LinkSpaceMaintainer extends AbstractStateWatcher
{
private static final String PREDICATE_PATHS[] = { "links.*",
"links.*.pools.*",
"pools.*.space.*"};
private static final StatePath LINKS_PATH = new StatePath("links");
private static final StatePath POOL_MEMBERSHIP_REL_PATH = new StatePath("pools");
@Override
protected String[] getPredicates()
{
return PREDICATE_PATHS;
}
@Override
public void trigger(StateUpdate update, StateExhibitor currentState,
StateExhibitor futureState)
{
super.trigger(update, currentState, futureState);
Map<String,Set<String>> futureLinksToPools = SetMapVisitor.getDetails(futureState, LINKS_PATH, POOL_MEMBERSHIP_REL_PATH);
Map<String,SpaceInfo> futurePoolSize = PoolSpaceVisitor.getDetails(futureState);
Set<String> linksToUpdate = new HashSet<>();
addLinksWhereLinkHasChanged(currentState, linksToUpdate, futureLinksToPools);
addLinksWherePoolsHaveChanged(currentState, linksToUpdate, futureLinksToPools, futurePoolSize);
if (linksToUpdate.isEmpty()) {
return;
}
for (String linkName : linksToUpdate) {
addUpdateInfo(update, linkName, futureLinksToPools
.get(linkName), futurePoolSize);
}
}
/**
* Check whether we should recalculate a link's available space based on information within the links
* branch of the dCache state tree.
* @param linksToUpdate the Set of links to recalculate: link names will be added here.
* @param futureLinksToPools the mapping between a link's name and the Set of pools that are associated with that link after the transition.
*/
private void addLinksWhereLinkHasChanged(StateExhibitor currentState,
Set<String> linksToUpdate, Map<String,Set<String>> futureLinksToPools)
{
Map<String,Set<String>> currentLinksToPools = SetMapVisitor.getDetails(currentState, LINKS_PATH, POOL_MEMBERSHIP_REL_PATH);
if (currentLinksToPools.equals(futureLinksToPools)) {
return;
}
// Iterate over all future links.
for (Map.Entry<String, Set<String>> linkEntry : futureLinksToPools.entrySet()) {
String thisLinkName = linkEntry.getKey();
// If this link already exists ...
if (currentLinksToPools.containsKey(thisLinkName)) {
Set<String> thisLinkPools = linkEntry.getValue();
// If the pool membership has changed, add it.
if (!thisLinkPools.equals(currentLinksToPools.get(thisLinkName))) {
linksToUpdate.add(thisLinkName);
}
} else {
// Otherwise, it's a new link, so add it.
linksToUpdate.add(thisLinkName);
}
}
}
/**
* Check whether any of the pool spaces for pools associated with a link have changed.
* If they have, we should make sure we update our link's summary view.
* @param linksToUpdate the Set of link names of which links to update.
* @param futureLinksToPools the future mapping between a link's name and the set of pools associated with that link
* @param futurePoolSize the future mapping between a pool's name and its size information.
*/
private void addLinksWherePoolsHaveChanged(StateExhibitor currentState,
Set<String> linksToUpdate, Map<String, Set<String>> futureLinksToPools,
Map<String,SpaceInfo> futurePoolSize)
{
// Get the current pool space information
Map<String,SpaceInfo> currentPoolSize = PoolSpaceVisitor.getDetails(currentState);
if (futurePoolSize.equals(currentPoolSize)) {
return;
}
// Iterate over all links, looking to see if any of the pools have changed.
for (Map.Entry<String, Set<String>> linkEntry : futureLinksToPools.entrySet()) {
String thisLinkName = linkEntry.getKey();
Set<String> thisLinkPools = linkEntry.getValue();
// Now, for each pool associated with this link ...
for (String poolName : thisLinkPools) {
// If the pool currently exists in dCache state, check whether the space has changed.
if (currentPoolSize.containsKey(poolName)) {
// If the space information has changed, add the link.
if (!currentPoolSize.get(poolName).equals(futurePoolSize.get(poolName))) {
linksToUpdate.add(thisLinkName);
}
} else {
// Otherwise, the pool is new with this transition, so we should recalculate.
linksToUpdate.add(thisLinkName);
}
}
}
}
/**
* Add new metrics to a StateUpdate for freshly calculated updates.
* @param update the StateUpdate the new metrics will be appended to.
* @param linkName the name of the link.
* @param futureLinksToPools the Set of poolNames for the pools associated with this link
* @param futurePoolSize the future mapping between a pool's name and its pool size information.
*/
private void addUpdateInfo(StateUpdate update, String linkName,
Set<String> futureLinkPools, Map<String,SpaceInfo> futurePoolSize)
{
SpaceInfo linkSpaceInfo = new SpaceInfo();
StatePath linkSpacePath = LINKS_PATH.newChild(linkName).newChild("space");
// For each pool, add up the space information.
for (String poolName : futureLinkPools) {
linkSpaceInfo.add(futurePoolSize.get(poolName));
}
// Add the metrics to the StateUpdate.
linkSpaceInfo.addMetrics(update, linkSpacePath, false);
}
}