package org.dcache.poolmanager;
import com.google.common.collect.ImmutableMap;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import diskCacheV111.poolManager.CostModule;
import diskCacheV111.util.CacheException;
import org.dcache.vehicles.FileAttributes;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Predicates.*;
import static com.google.common.collect.Maps.filterKeys;
import static com.google.common.collect.Maps.filterValues;
/**
* Encapsulates configuration parameters and pool selection logic.
*
* A Partition contains a number of properties. The value of a
* property may be the default, inherited or defined. The default
* value is hardcoded, inherited values are potentially shared by
* multiple partitions, while defined values are defined for this
* Partition.
*
* Properties are immutable and any update requires that a new
* instance is created (see updateProperties and updateInherited). The
* reason to distinguish between inherited and defined properies is
* that each can be updated separately. The abstract create method is
* used to implement this copy-on-write scheme.
*
* New partitions are usually created through a PartitionFactory or
* instantiated directly. The create method is protected and is only
* used to implement the copy-on-write scheme to update properties.
*
* Properties will be parsed during partition instantiation and values
* will be cached in immutable fields. Subclasses are encouraged to do
* the same in their constructor for properties specific to the
* subclass.
*
* A number of utility methods are provided to query the value of a
* property, taking default, inherited and defined values into account
* (with the default value having the lowest and the defined value
* having the highest precedence). Subclasses should use these methods
* to parse properties.
*
* Although properties are immutable, the partition may not be: Some
* selection strategies may be stateful and will have to update the
* partition when selecting pools. An example of such a strategy is
* round robin selection.
*
* Partition itself is abstract. Subclasses must implement the pool
* selection, create and the getType methods. The base class does
* recognize a number of standard properties used by other parts of
* pool manager.
*
* Several partitions can be created and assigned to different pool
* manager links.
*
* The class is thread safe.
*/
public abstract class Partition implements Serializable
{
private static final long serialVersionUID = -4195341006626972862L;
protected static final Map<String,String> NO_PROPERTIES =
ImmutableMap.of();
/**
* P2P
*
* p2p-allowed boolean
* p2p-oncost boolean
* p2p-fortransfer boolean
*
* STAGING
*
* stage-allowed boolean
* stage-oncost boolean
*/
private final Map<String,String> DEFAULTS =
ImmutableMap.<String,String>builder()
.put("p2p-allowed", "yes")
.put("p2p-oncost", "no")
.put("p2p-fortransfer", "no")
.put("stage-allowed", "no")
.put("stage-oncost", "no")
.build();
private final ImmutableMap<String,String> _defaults;
private final ImmutableMap<String,String> _inherited;
private final ImmutableMap<String,String> _defined;
public final boolean _p2pAllowed;
public final boolean _p2pOnCost;
public final boolean _p2pForTransfer;
public final boolean _hasHsmBackend;
public final boolean _stageOnCost;
/**
* Constructs new partition.
*
* Subclasses are to call this constructor, providing their own
* default properties and the inherited and defined properties
* provided by the PartitionManager.
*
* Only properties for which a default value is known are
* retained. It is essential that subclasses provide default
* entries for all supported properties.
*
* It is recommended that subclasses provide a three parameter
* constructor too, as this facilitates further
* subclassing. Subclasses are free to provide additional
* constructors for other purposes.
*
* @param defaults Defaults provided by subclass
* @param inherited Runtime inherited properties
* @param defined Runtime provided properties for this partition
*/
protected Partition(Map<String,String> defaults,
Map<String,String> inherited,
Map<String,String> defined)
{
_defaults =
ImmutableMap.<String,String>builder()
.putAll(DEFAULTS)
.putAll(defaults)
.build();
_inherited =
ImmutableMap.copyOf(filterKeys(inherited, in(_defaults.keySet())));
_defined =
ImmutableMap.copyOf(filterKeys(defined, in(_defaults.keySet())));
_p2pAllowed = getBoolean("p2p-allowed");
_p2pOnCost = getBoolean("p2p-oncost");
_p2pForTransfer = getBoolean("p2p-fortransfer");
_hasHsmBackend = getBoolean("stage-allowed");
_stageOnCost = getBoolean("stage-oncost");
}
/**
* Creates a new partition of the same type as this partition.
*
* Used when updating properties to implement a copy-on-write
* scheme.
*
* Advanced implementations may choose to preserve additional
* state that isn't captured by configuraton parameters.
*/
protected abstract Partition create(Map<String,String> inherited,
Map<String,String> defined);
/**
* Returns a map of defined properties in this partition.
*
* The map is unmodifiable.
*/
public Map<String,String> getProperties()
{
return _defined;
}
/**
* Returns a map of all properties of this partition, including
* inherited and default properties.
*/
public Map<String,String> getAllProperties()
{
Map<String,String> map = new HashMap<>();
map.putAll(_defaults);
map.putAll(_inherited);
map.putAll(_defined);
return map;
}
/**
* Updates defined properties.
*
* A new partition with the updated defined properties is
* returned.
*
* If value is null then the property is removed and a default
* value is used instead. Properties not present in the new map
* will be carried over from this Partition.
*/
public Partition updateProperties(Map<String,String> defined)
{
ImmutableMap<String,String> map =
ImmutableMap.<String,String>builder()
.putAll(filterKeys(_defined, not(in(defined.keySet()))))
.putAll(filterValues(defined, notNull()))
.build();
return create(_inherited, map);
}
/**
* Updates inherited properties.
*
* A new partition with the updated inherited properties is
* returned. Non of the inherited properties of this Partition are
* carried over.
*/
public Partition updateInherited(Map<String,String> inherited)
{
return create(inherited, _defined);
}
/**
* Returns the value of a property.
*
* @throws NoSuchElementException If the property is undefined
*/
public String getProperty(String name)
throws NoSuchElementException
{
String value = _defined.get(name);
if (value == null) {
value = _inherited.get(name);
}
if (value == null) {
value = _defaults.get(name);
}
if (value == null) {
throw new NoSuchElementException("No such property: " + name);
}
return value;
}
/**
* Returns the boolean value of a boolean property.
*
* Legal values of boolean property are yes and no.
*
* @throws IllegalArgumentException If the value of the property
* is not a boolean
* @throws NoSuchElementException If the property is undefined
*/
public boolean getBoolean(String name)
throws NoSuchElementException,
IllegalArgumentException
{
String value = getProperty(name);
switch (value) {
case "yes":
return true;
case "no":
return false;
default:
throw new IllegalArgumentException("Boolean property " + name + " has invalid value: " + value);
}
}
/**
* Returns the long value of an long property.
*
* @throws IllegalArgumentException If the value of the property
* is not a long
* @throws NoSuchElementException If the property is undefined
*/
public long getLong(String name)
throws NoSuchElementException,
IllegalArgumentException
{
return Long.parseLong(getProperty(name));
}
/**
* Returns the double value of an double property.
*
* @throws IllegalArgumentException If the value of the property
* is not a double
* @throws NoSuchElementException If the property is undefined
*/
public double getDouble(String name)
throws NoSuchElementException,
IllegalArgumentException
{
return Double.parseDouble(getProperty(name));
}
/**
* Legacy helper method used by HttpPoolMgrEngineV3.
*/
public synchronized Map<String,Object[]> toMap()
{
Map<String,Object[]> map = new HashMap<>();
for (Map.Entry<String,String> entry: _defaults.entrySet()) {
map.put(entry.getKey(),
new Object[] { false, entry.getValue() });
}
for (Map.Entry<String,String> entry: _inherited.entrySet()) {
map.put(entry.getKey(),
new Object[] { false, entry.getValue() });
}
for (Map.Entry<String,String> entry: _defined.entrySet()) {
map.put(entry.getKey(), new Object[] { true, entry.getValue() });
}
return map;
}
public boolean getP2PAllowed()
{
return _p2pAllowed;
}
public boolean getP2POnCost()
{
return _p2pOnCost;
}
public boolean getP2PForTransfer()
{
return _p2pForTransfer;
}
public boolean getHasHsmBackend()
{
return _hasHsmBackend;
}
public boolean getStageOnCost()
{
return _stageOnCost;
}
/**
* Returns the short name of the partitions type. Must correspond
* to the short name used by the Partition's factory.
*/
public abstract String getType();
/**
* Selects a pool for writing among a set of pools. May modify
* the input list of pools.
*
* An implementation cannot rely on any file attributes being defined.
*/
public abstract SelectedPool selectWritePool(CostModule cm,
List<PoolInfo> pools,
FileAttributes attributes,
long preallocated)
throws CacheException;
/**
* Selects a pool for reading among a set of pools. May modify
* the input list of pools.
*
* An implementation cannot rely on any file attributes other than
* PNFS id, storage info and locations being defined.
*/
public abstract SelectedPool selectReadPool(CostModule cm,
List<PoolInfo> pools,
FileAttributes attributes)
throws CacheException;
/**
* Selects a pair of pools for pool to pool among a set of
* pools. May modify the input lists of pools.
*
* An implementation cannot rely on any file attributes other than
* PNFS id, storage info and locations being defined.
*/
public abstract P2pPair selectPool2Pool(CostModule cm,
List<PoolInfo> src,
List<PoolInfo> dst,
FileAttributes attributes,
boolean force)
throws CacheException;
/**
* Selects a pool for staging among a set of pools. May modify the
* input list of pools.
*
* An implementation cannot rely on any file attributes other than
* PNFS id, storage info and locations being defined.
*/
public abstract SelectedPool selectStagePool(CostModule cm,
List<PoolInfo> pools,
String previousPool,
String previousHost,
FileAttributes attributes)
throws CacheException;
/**
* Immutable helper class to represent a source and destination
* pair for pool to pool selection.
*/
public static class P2pPair
{
public final SelectedPool source;
public final SelectedPool destination;
public P2pPair(SelectedPool source, SelectedPool destination)
{
this.source = source;
this.destination = destination;
}
}
}