| /** |
| * 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(); |
| } |
| } |