blob: eecd3f32f7d24eb5f72acbb0693aadb642b6e6bb [file] [log] [blame]
/**
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.aurora.scheduler.resources;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.BinaryOperator;
import java.util.function.Predicate;
import java.util.stream.Stream;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import static java.util.stream.Collectors.toMap;
import static org.apache.aurora.scheduler.resources.ResourceType.CPUS;
import static org.apache.aurora.scheduler.resources.ResourceType.DISK_MB;
import static org.apache.aurora.scheduler.resources.ResourceType.RAM_MB;
/**
* A bag of unique resource values aggregated by {@link ResourceType}.
*/
public class ResourceBag {
public static final ResourceBag EMPTY = new ResourceBag(ImmutableMap.of(
CPUS, 0.0,
RAM_MB, 0.0,
DISK_MB, 0.0
));
public static final ResourceBag SMALL = new ResourceBag(ImmutableMap.of(
CPUS, 1.0,
RAM_MB, 1024.0,
DISK_MB, 4096.0
));
public static final ResourceBag MEDIUM = new ResourceBag(ImmutableMap.of(
CPUS, 4.0,
RAM_MB, 8192.0,
DISK_MB, 16384.0
));
public static final ResourceBag LARGE = new ResourceBag(ImmutableMap.of(
CPUS, 8.0,
RAM_MB, 16384.0,
DISK_MB, 32768.0
));
public static final ResourceBag XLARGE = new ResourceBag(ImmutableMap.of(
CPUS, 16.0,
RAM_MB, 32768.0,
DISK_MB, 65536.0
));
public static final Predicate<Map.Entry<ResourceType, Double>> IS_NEGATIVE =
entry -> entry.getValue() < 0;
public static final Predicate<Map.Entry<ResourceType, Double>> IS_POSITIVE =
entry -> entry.getValue() > 0;
public static final Predicate<Map.Entry<ResourceType, Double>> IS_MESOS_REVOCABLE =
entry -> entry.getKey().isMesosRevocable();
private final Map<ResourceType, Double> resourceVectors;
/**
* Creates an instance of ResourceBag with given resource vectors (type -> value).
*
* @param resourceVectors Map of resource vectors.
*/
ResourceBag(Map<ResourceType, Double> resourceVectors) {
this.resourceVectors = ImmutableMap.copyOf(resourceVectors);
}
/**
* Gets resource vectors in the bag.
*
* @return Map of resource vectors.
*/
public Map<ResourceType, Double> getResourceVectors() {
return resourceVectors;
}
/**
* Convenience function to return a stream of resource vectors.
*
* @return A stream of resource vectors.
*/
public Stream<Map.Entry<ResourceType, Double>> streamResourceVectors() {
return resourceVectors.entrySet().stream();
}
/**
* Gets the value of resource specified by {@code type} or 0.0.
*
* @param type Resource type to get value for.
* @return Resource value or 0.0 if no mapping for {@code type} is found.
*/
public double valueOf(ResourceType type) {
return resourceVectors.getOrDefault(type, 0.0);
}
/**
* Adds this and other bag contents.
*
* @param other Other bag to add.
* @return Result of addition.
*/
public ResourceBag add(ResourceBag other) {
return binaryOp(other, (l, r) -> l + r);
}
/**
* Subtracts other bag contents from this.
*
* @param other Other bag to subtract.
* @return Result of subtraction.
*/
public ResourceBag subtract(ResourceBag other) {
return binaryOp(other, (l, r) -> l - r);
}
/**
* Divides this by other bag contents.
* <p>
* Note: any missing or zero resource values in {@code other} will result in
* {@code java.lang.Double.POSITIVE_INFINITY}.
* @param other Other bag to divide by.
* @return Result of division.
*/
public ResourceBag divide(ResourceBag other) {
return binaryOp(other, (l, r) -> l / r);
}
/**
* Applies {@code Math.max()} for each matching resource vector.
*
* @param other Other bag to compare with.
* @return A new bag with max resource vectors.
*/
public ResourceBag max(ResourceBag other) {
return binaryOp(other, (l, r) -> Math.max(l, r));
}
/**
* Scales each resource vector by {@code m}.
*
* @param m Scale factor.
* @return Result of scale operation.
*/
public ResourceBag scale(int m) {
return new ResourceBag(resourceVectors.entrySet().stream()
.collect(toMap(Map.Entry::getKey, v -> v.getValue() * m)));
}
/**
* Filters bag resources by {@code predicate}.
*
* @param predicate Predicate to filter by.
* @return A new bag with resources filtered by {@code predicate}.
*/
public ResourceBag filter(Predicate<Map.Entry<ResourceType, Double>> predicate) {
return new ResourceBag(resourceVectors.entrySet().stream()
.filter(predicate)
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue)));
}
/**
* Verifies whether another bag would be able to fit into this one.
*
* @param other Other bag to try and fit.
* @return Whether or not the bag fits.
*/
public boolean greaterThanOrEqualTo(ResourceBag other) {
// Subtract the subject and check if any of the resources have a negative value.
return subtract(other).filter(IS_NEGATIVE).getResourceVectors().isEmpty();
}
/**
* Applies {@code operator} to this and {@code other} resource values. Any missing resource type
* values on either side will get substituted with 0.0 before applying the operator.
*
* @param other Other ResourceBag.
* @param operator Operator to apply.
* @return Operation result.
*/
private ResourceBag binaryOp(ResourceBag other, BinaryOperator<Double> operator) {
ImmutableMap.Builder<ResourceType, Double> builder = ImmutableMap.builder();
Set<ResourceType> resourceTypes =
Sets.union(resourceVectors.keySet(), other.getResourceVectors().keySet());
for (ResourceType type : resourceTypes) {
Double left = resourceVectors.getOrDefault(type, 0.0);
Double right = other.getResourceVectors().getOrDefault(type, 0.0);
builder.put(type, operator.apply(left, right));
}
return new ResourceBag(builder.build());
}
@Override
public boolean equals(Object o) {
if (!(o instanceof ResourceBag)) {
return false;
}
ResourceBag other = (ResourceBag) o;
return Objects.equals(resourceVectors, other.resourceVectors);
}
@Override
public int hashCode() {
return Objects.hash(resourceVectors);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this).add("resourceVectors", resourceVectors).toString();
}
}