package org.dcache.services.info.stateInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.dcache.services.info.base.IntegerStateValue;
import org.dcache.services.info.base.StateExhibitor;
import org.dcache.services.info.base.StatePath;
import org.dcache.services.info.base.StringStateValue;
/**
* The ReservationInfoVisitor class is a StateVisitor that builds up
* information about SRM reservations by visiting some StateExhibitor. The
* result of visiting dCache state is a Map between the reservation ID and a
* corresponding ReservationInfo object describing the SRM reservation.
*/
public class ReservationInfoVisitor extends SkeletonListVisitor
{
private static final Logger LOGGER =
LoggerFactory.getLogger(ReservationInfoVisitor.class);
public static final String PATH_ELEMENT_SPACE = "space";
public static final String PATH_ELEMENT_LIFETIME = "lifetime";
public static final String PATH_ELEMENT_TOTAL = "total";
public static final String PATH_ELEMENT_FREE = "free";
public static final String PATH_ELEMENT_ALLOCATED = "allocated";
public static final String PATH_ELEMENT_USED = "used";
public static final String PATH_ELEMENT_AL = "access-latency";
public static final String PATH_ELEMENT_RP = "retention-policy";
public static final String PATH_ELEMENT_STATE = "state";
public static final String PATH_ELEMENT_DESCRIPTION = "description";
public static final String PATH_ELEMENT_AUTHORISATION = "authorisation";
public static final String PATH_ELEMENT_GROUP = "group";
public static final StatePath RESERVATIONS_PATH =
StatePath.parsePath("reservations");
/**
* Match Strings like "/vo", "/vo/group" or "vo" allowing the extraction
* of "vo" in all cases as group 1 of the resulting pattern.
*/
public static final Pattern VO_EXTRACTOR_PATTERN =
Pattern.compile("^/?([^/]*).*");
/**
* Obtain information about the current reservations in dCache
*
* @return a Mapping between a reservation's ID and a corresponding
* ReservationInfo object.
*/
public static Map<String, ReservationInfo> getDetails(StateExhibitor exhibitor)
{
LOGGER.trace("Gathering reservation information.");
ReservationInfoVisitor visitor = new ReservationInfoVisitor();
exhibitor.visitState(visitor);
return visitor.getReservations();
}
private final Map<String, ReservationInfo> _reservations = new HashMap<>();
/*
* Per-item state
*/
private ReservationInfo _thisResv;
private StatePath _thisResvPath;
private StatePath _thisResvSpacePath;
private StatePath _thisResvAuthPath;
public ReservationInfoVisitor()
{
super(RESERVATIONS_PATH);
}
/**
* Provide an unmodifiable view of the result of the visitor.
*
* @return a Map between a reservation's ID and a corresponding
* ReservationInfo object.
*/
public Map<String, ReservationInfo> getReservations()
{
return Collections.unmodifiableMap(_reservations);
}
@Override
protected void newListItem(String listItemName)
{
super.newListItem(listItemName);
_thisResv = new ReservationInfo(listItemName);
_reservations.put(listItemName, _thisResv);
/**
* Build up the various StatePaths where we expect data to appear for
* this reservation.
*/
_thisResvPath = RESERVATIONS_PATH.newChild(listItemName);
_thisResvSpacePath = _thisResvPath.newChild(PATH_ELEMENT_SPACE);
_thisResvAuthPath = _thisResvPath.newChild(PATH_ELEMENT_AUTHORISATION);
}
/**
* Called when an integer metric is encountered.
*/
@Override
public void visitInteger(StatePath path, IntegerStateValue value)
{
if (!isInListItem()) {
return;
}
String metricName = path.getLastElement();
if (_thisResvPath.isParentOf(path)) {
if (metricName.equals(PATH_ELEMENT_LIFETIME)) {
_thisResv.setLifetime(value.getValue());
}
return;
}
if (_thisResvSpacePath.isParentOf(path)) {
switch (metricName) {
case PATH_ELEMENT_TOTAL:
_thisResv.setTotal(value.getValue());
break;
case PATH_ELEMENT_FREE:
_thisResv.setFree(value.getValue());
break;
case PATH_ELEMENT_ALLOCATED:
_thisResv.setAllocated(value.getValue());
break;
case PATH_ELEMENT_USED:
_thisResv.setUsed(value.getValue());
break;
default:
LOGGER.warn("Seen unexpected reservation metric at path {}", path);
break;
}
}
}
@Override
public void visitString(StatePath path, StringStateValue value)
{
if (!isInListItem()) {
return;
}
if (!_thisResvPath.isParentOf(path) &&
!_thisResvSpacePath.isParentOf(path) &&
!_thisResvAuthPath.isParentOf(path)) {
return;
}
String metricName = path.getLastElement();
if (metricName.equals(PATH_ELEMENT_AL)) {
ReservationInfo.AccessLatency al =
ReservationInfo.AccessLatency.parseMetricValue(value.toString());
if (al != null) {
_thisResv.setAccessLatency(al);
} else {
LOGGER.error("Unknown access-latency value {}", value);
}
return;
}
if (metricName.equals(PATH_ELEMENT_RP)) {
ReservationInfo.RetentionPolicy rp =
ReservationInfo.RetentionPolicy.parseMetricValue(value.toString());
if (rp != null) {
_thisResv.setRetentionPolicy(rp);
} else {
LOGGER.error("Unknown retention-policy value {}", value);
}
return;
}
if (metricName.equals(PATH_ELEMENT_STATE)) {
ReservationInfo.State state =
ReservationInfo.State.parseMetricValue(value.toString());
if (state != null) {
_thisResv.setState(state);
} else {
LOGGER.error("Unknown state value {}", value);
}
return;
}
if (metricName.equals(PATH_ELEMENT_DESCRIPTION)) {
_thisResv.setDescription(value.toString());
return;
}
if (metricName.equals(PATH_ELEMENT_GROUP)) {
Matcher matcher = VO_EXTRACTOR_PATTERN.matcher(value.toString());
if (matcher.matches()) {
String voName = matcher.group(1);
_thisResv.setVo(voName);
} else {
LOGGER.error("authorisation.group doesn't match expected pattern {}",
value);
}
}
}
}