You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aurora.apache.org by ma...@apache.org on 2016/05/04 20:17:44 UTC

aurora git commit: Adding mesos resource converter

Repository: aurora
Updated Branches:
  refs/heads/master 8a2fc4c97 -> f25a4b108


Adding mesos resource converter

Reviewed at https://reviews.apache.org/r/46948/


Project: http://git-wip-us.apache.org/repos/asf/aurora/repo
Commit: http://git-wip-us.apache.org/repos/asf/aurora/commit/f25a4b10
Tree: http://git-wip-us.apache.org/repos/asf/aurora/tree/f25a4b10
Diff: http://git-wip-us.apache.org/repos/asf/aurora/diff/f25a4b10

Branch: refs/heads/master
Commit: f25a4b108dfc602e32295449382b4988397e10fa
Parents: 8a2fc4c
Author: Maxim Khutornenko <ma...@apache.org>
Authored: Wed May 4 13:14:15 2016 -0700
Committer: Maxim Khutornenko <ma...@apache.org>
Committed: Wed May 4 13:14:15 2016 -0700

----------------------------------------------------------------------
 .../resources/AuroraResourceConverter.java      | 65 ++++++++++++++++++++
 .../resources/MesosResourceConverter.java       | 51 +++++++++++++++
 .../scheduler/resources/ResourceManager.java    | 22 ++++++-
 .../scheduler/resources/ResourceType.java       | 64 +++++++++++--------
 .../resources/ResourceTypeConverter.java        | 65 --------------------
 .../aurora/scheduler/resources/Resources.java   | 39 ++----------
 .../scheduler/storage/db/views/DBResource.java  |  4 +-
 .../storage/db/views/DBResourceAggregate.java   |  2 +-
 .../resources/AuroraResourceConverterTest.java  | 30 +++++++++
 .../resources/MesosResourceConverterTest.java   | 46 ++++++++++++++
 .../resources/ResourceManagerTest.java          | 18 ++++++
 .../scheduler/resources/ResourceTestUtil.java   |  3 +-
 .../resources/ResourceTypeConverterTest.java    | 30 ---------
 .../scheduler/resources/ResourceTypeTest.java   | 18 ++++--
 14 files changed, 290 insertions(+), 167 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/aurora/blob/f25a4b10/src/main/java/org/apache/aurora/scheduler/resources/AuroraResourceConverter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/resources/AuroraResourceConverter.java b/src/main/java/org/apache/aurora/scheduler/resources/AuroraResourceConverter.java
new file mode 100644
index 0000000..f9c89a9
--- /dev/null
+++ b/src/main/java/org/apache/aurora/scheduler/resources/AuroraResourceConverter.java
@@ -0,0 +1,65 @@
+/**
+ * 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 com.google.common.primitives.Longs;
+
+/**
+ * Converts Aurora resource values to/from generic (String) representation.
+ * @param <T> Resource value type to convert.
+ */
+public interface AuroraResourceConverter<T> {
+  /**
+   * Parses resource value from the string representation.
+   *
+   * @param value String value to parse.
+   * @return Resource value of type {@code T}.
+   */
+  T parseFrom(String value);
+
+  /**
+   * Converts resource of type {@code T} to its string representation.
+   *
+   * @param value Resource value to stringify.
+   * @return String representation of the resource value.
+   */
+  default String stringify(Object value) {
+    return value.toString();
+  }
+
+  LongConverter LONG = new LongConverter();
+  DoubleConverter DOUBLE = new DoubleConverter();
+  StringConverter STRING = new StringConverter();
+
+  class LongConverter implements AuroraResourceConverter<Long> {
+    @Override
+    public Long parseFrom(String value) {
+      return Longs.tryParse(value);
+    }
+  }
+
+  class DoubleConverter implements AuroraResourceConverter<Double> {
+    @Override
+    public Double parseFrom(String value) {
+      return Double.parseDouble(value);
+    }
+  }
+
+  class StringConverter implements AuroraResourceConverter<String> {
+    @Override
+    public String parseFrom(String value) {
+      return value;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/f25a4b10/src/main/java/org/apache/aurora/scheduler/resources/MesosResourceConverter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/resources/MesosResourceConverter.java b/src/main/java/org/apache/aurora/scheduler/resources/MesosResourceConverter.java
new file mode 100644
index 0000000..f3fe05c
--- /dev/null
+++ b/src/main/java/org/apache/aurora/scheduler/resources/MesosResourceConverter.java
@@ -0,0 +1,51 @@
+/**
+ * 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 org.apache.mesos.Protos.Resource;
+
+/**
+ * Converts Mesos resource values to be consumed in Aurora.
+ */
+public interface MesosResourceConverter {
+
+  /**
+   * Gets Mesos resource quantity.
+   *
+   * @param resource Mesos resource to quantify.
+   * @return Mesos resource quantity.
+   */
+  Double quantify(Resource resource);
+
+  ScalarConverter SCALAR = new ScalarConverter();
+  RangeConverter RANGES = new RangeConverter();
+
+  class ScalarConverter implements MesosResourceConverter {
+    @Override
+    public Double quantify(Resource resource) {
+      return resource.getScalar().getValue();
+    }
+  }
+
+  class RangeConverter implements MesosResourceConverter {
+    @Override
+    public Double quantify(Resource resource) {
+      return resource.getRanges().getRangeList().stream()
+          .map(range -> 1 + range.getEnd() - range.getBegin())
+          .reduce((l, r) -> l + r)
+          .map(v -> v.doubleValue())
+          .orElse(0.0);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/f25a4b10/src/main/java/org/apache/aurora/scheduler/resources/ResourceManager.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/resources/ResourceManager.java b/src/main/java/org/apache/aurora/scheduler/resources/ResourceManager.java
index 8b42bf0..1b88fc3 100644
--- a/src/main/java/org/apache/aurora/scheduler/resources/ResourceManager.java
+++ b/src/main/java/org/apache/aurora/scheduler/resources/ResourceManager.java
@@ -16,6 +16,7 @@ package org.apache.aurora.scheduler.resources;
 import java.util.EnumSet;
 import java.util.Set;
 import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 import com.google.common.collect.Iterables;
 
@@ -24,6 +25,7 @@ import org.apache.aurora.scheduler.storage.entities.IScheduledTask;
 import org.apache.aurora.scheduler.storage.entities.ITaskConfig;
 import org.apache.mesos.Protos.Resource;
 
+import static org.apache.aurora.scheduler.resources.ResourceType.fromResource;
 import static org.apache.mesos.Protos.Offer;
 
 /**
@@ -42,7 +44,7 @@ public final class ResourceManager {
    * @return Offer resources matching {@link ResourceType}.
    */
   public static Iterable<Resource> getOfferResources(Offer offer, ResourceType type) {
-    return Iterables.filter(offer.getResourcesList(), r -> r.getName().equals(type.getMesosName()));
+    return Iterables.filter(offer.getResourcesList(), r -> fromResource(r).equals(type));
   }
 
   /**
@@ -64,7 +66,7 @@ public final class ResourceManager {
    * @return Task resources matching {@link ResourceType}.
    */
   public static Iterable<IResource> getTaskResources(ITaskConfig task, ResourceType type) {
-    return Iterables.filter(task.getResources(), r -> ResourceType.fromResource(r).equals(type));
+    return Iterables.filter(task.getResources(), r -> fromResource(r).equals(type));
   }
 
   /**
@@ -75,7 +77,21 @@ public final class ResourceManager {
    */
   public static Set<ResourceType> getTaskResourceTypes(IScheduledTask task) {
     return EnumSet.copyOf(task.getAssignedTask().getTask().getResources().stream()
-        .map(r -> ResourceType.fromResource(r))
+        .map(r -> fromResource(r))
         .collect(Collectors.toSet()));
   }
+
+  /**
+   * Gets the quantity of the Mesos resource specified by {@code type}.
+   *
+   * @param resources Mesos resources.
+   * @param type Type of resource to quantify.
+   * @return Mesos resource value.
+   */
+  public static Double quantityOf(Iterable<Resource> resources, ResourceType type) {
+    return StreamSupport.stream(resources.spliterator(), false)
+        .filter(r -> fromResource(r).equals(type))
+        .map(r -> fromResource(r).getMesosResourceConverter().quantify(r))
+        .reduce((l, r) -> l + r).orElse(0.0);
+  }
 }

http://git-wip-us.apache.org/repos/asf/aurora/blob/f25a4b10/src/main/java/org/apache/aurora/scheduler/resources/ResourceType.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/resources/ResourceType.java b/src/main/java/org/apache/aurora/scheduler/resources/ResourceType.java
index ee2b51a..baed3de 100644
--- a/src/main/java/org/apache/aurora/scheduler/resources/ResourceType.java
+++ b/src/main/java/org/apache/aurora/scheduler/resources/ResourceType.java
@@ -23,19 +23,19 @@ import com.google.common.collect.Maps;
 import org.apache.aurora.common.quantity.Amount;
 import org.apache.aurora.gen.Resource._Fields;
 import org.apache.aurora.scheduler.storage.entities.IResource;
-import org.apache.mesos.Protos;
+import org.apache.mesos.Protos.Resource;
 import org.apache.thrift.TEnum;
 
 import static java.util.Objects.requireNonNull;
 
 import static org.apache.aurora.common.quantity.Data.GB;
 import static org.apache.aurora.common.quantity.Data.MB;
+import static org.apache.aurora.scheduler.resources.AuroraResourceConverter.DOUBLE;
+import static org.apache.aurora.scheduler.resources.AuroraResourceConverter.LONG;
+import static org.apache.aurora.scheduler.resources.AuroraResourceConverter.STRING;
+import static org.apache.aurora.scheduler.resources.MesosResourceConverter.RANGES;
+import static org.apache.aurora.scheduler.resources.MesosResourceConverter.SCALAR;
 import static org.apache.aurora.scheduler.resources.ResourceMapper.PORT_MAPPER;
-import static org.apache.aurora.scheduler.resources.ResourceTypeConverter.DOUBLE;
-import static org.apache.aurora.scheduler.resources.ResourceTypeConverter.LONG;
-import static org.apache.aurora.scheduler.resources.ResourceTypeConverter.STRING;
-import static org.apache.mesos.Protos.Value.Type.RANGES;
-import static org.apache.mesos.Protos.Value.Type.SCALAR;
 
 /**
  * Describes Mesos resource types and their Aurora traits.
@@ -84,9 +84,9 @@ public enum ResourceType implements TEnum {
   private final _Fields value;
 
   /**
-   * Mesos resource type.
+   * Mesos resource converter.
    */
-  private final Protos.Value.Type mesosType;
+  private final MesosResourceConverter mesosResourceConverter;
 
   /**
    * Mesos resource name.
@@ -96,7 +96,7 @@ public enum ResourceType implements TEnum {
   /**
    * Type converter for resource values.
    */
-  private final ResourceTypeConverter<?> typeConverter;
+  private final AuroraResourceConverter<?> auroraResourceConverter;
 
   /**
    * Optional resource mapper to use.
@@ -121,13 +121,16 @@ public enum ResourceType implements TEnum {
   private static ImmutableMap<Integer, ResourceType> byField =
       Maps.uniqueIndex(EnumSet.allOf(ResourceType.class),  ResourceType::getValue);
 
+  private static ImmutableMap<String, ResourceType> byMesosName =
+      Maps.uniqueIndex(EnumSet.allOf(ResourceType.class), ResourceType::getMesosName);
+
   /**
    * Describes a Resource type.
    *
    * @param value Correspondent {@link _Fields} value.
-   * @param mesosType See {@link #getMesosType()} for more details.
+   * @param mesosResourceConverter See {@link #getMesosResourceConverter()} for more details.
    * @param mesosName See {@link #getMesosName()} for more details.
-   * @param typeConverter See {@link #getTypeConverter()} for more details.
+   * @param auroraResourceConverter See {@link #getAuroraResourceConverter()} for more details.
    * @param mapper See {@link #getMapper()} for more details.
    * @param auroraName See {@link #getAuroraName()} for more details.
    * @param scalingRange See {@link #getScalingRange()} for more details.
@@ -135,18 +138,18 @@ public enum ResourceType implements TEnum {
    */
   ResourceType(
       _Fields value,
-      Protos.Value.Type mesosType,
+      MesosResourceConverter mesosResourceConverter,
       String mesosName,
-      ResourceTypeConverter<?> typeConverter,
+      AuroraResourceConverter<?> auroraResourceConverter,
       Optional<ResourceMapper> mapper,
       String auroraName,
       int scalingRange,
       boolean isMultipleAllowed) {
 
     this.value = value;
-    this.mesosType = requireNonNull(mesosType);
+    this.mesosResourceConverter = requireNonNull(mesosResourceConverter);
     this.mesosName = requireNonNull(mesosName);
-    this.typeConverter = requireNonNull(typeConverter);
+    this.auroraResourceConverter = requireNonNull(auroraResourceConverter);
     this.auroraName = requireNonNull(auroraName);
     this.mapper = requireNonNull(mapper);
     this.scalingRange = scalingRange;
@@ -164,15 +167,12 @@ public enum ResourceType implements TEnum {
   }
 
   /**
-   * Gets Mesos resource type.
-   * <p>
-   * @see <a href="https://github.com/apache/mesos/blob/master/include/mesos/mesos.proto/">Mesos
-   * protobuf for more details</a>
+   * Gets {@link MesosResourceConverter} to convert Mesos resource values.
    *
-   * @return Mesos resource type.
+   * @return {@link MesosResourceConverter} instance.
    */
-  public Protos.Value.Type getMesosType() {
-    return mesosType;
+  public MesosResourceConverter getMesosResourceConverter() {
+    return mesosResourceConverter;
   }
 
   /**
@@ -188,12 +188,12 @@ public enum ResourceType implements TEnum {
   }
 
   /**
-   * Gets {@link ResourceTypeConverter} to convert resource values.
+   * Gets {@link AuroraResourceConverter} to convert resource values.
    *
-   * @return {@link ResourceTypeConverter} instance.
+   * @return {@link AuroraResourceConverter} instance.
    */
-  public ResourceTypeConverter<?> getTypeConverter() {
-    return typeConverter;
+  public AuroraResourceConverter<?> getAuroraResourceConverter() {
+    return auroraResourceConverter;
   }
 
   /**
@@ -257,4 +257,16 @@ public enum ResourceType implements TEnum {
         byField.get((int) resource.getSetField().getThriftFieldId()),
         "Unknown resource: " + resource);
   }
+
+  /**
+   * Returns a {@link ResourceType} for the given Mesos resource.
+   *
+   * @param resource {@link Resource} to search by.
+   * @return {@link ResourceType}.
+   */
+  public static ResourceType fromResource(Resource resource) {
+    return requireNonNull(
+        byMesosName.get(resource.getName()),
+        "Unknown Mesos resource: " + resource);
+  }
 }

http://git-wip-us.apache.org/repos/asf/aurora/blob/f25a4b10/src/main/java/org/apache/aurora/scheduler/resources/ResourceTypeConverter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/resources/ResourceTypeConverter.java b/src/main/java/org/apache/aurora/scheduler/resources/ResourceTypeConverter.java
deleted file mode 100644
index 11395ec..0000000
--- a/src/main/java/org/apache/aurora/scheduler/resources/ResourceTypeConverter.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/**
- * 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 com.google.common.primitives.Longs;
-
-/**
- * Converts resource values to/from generic (String) representation.
- * @param <T> Resource value type to convert.
- */
-public interface ResourceTypeConverter<T> {
-  /**
-   * Parses resource value from the string representation.
-   *
-   * @param value String value to parse.
-   * @return Resource value of type {@code T}.
-   */
-  T parseFrom(String value);
-
-  /**
-   * Converts resource of type {@code T} to its string representation.
-   *
-   * @param value Resource value to stringify.
-   * @return String representation of the resource value.
-   */
-  default String stringify(Object value) {
-    return value.toString();
-  }
-
-  LongConverter LONG = new LongConverter();
-  DoubleConverter DOUBLE = new DoubleConverter();
-  StringConverter STRING = new StringConverter();
-
-  class LongConverter implements ResourceTypeConverter<Long> {
-    @Override
-    public Long parseFrom(String value) {
-      return Longs.tryParse(value);
-    }
-  }
-
-  class DoubleConverter implements ResourceTypeConverter<Double> {
-    @Override
-    public Double parseFrom(String value) {
-      return Double.parseDouble(value);
-    }
-  }
-
-  class StringConverter implements ResourceTypeConverter<String> {
-    @Override
-    public String parseFrom(String value) {
-      return value;
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/aurora/blob/f25a4b10/src/main/java/org/apache/aurora/scheduler/resources/Resources.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/resources/Resources.java b/src/main/java/org/apache/aurora/scheduler/resources/Resources.java
index 36d1de8..94cd163 100644
--- a/src/main/java/org/apache/aurora/scheduler/resources/Resources.java
+++ b/src/main/java/org/apache/aurora/scheduler/resources/Resources.java
@@ -32,6 +32,7 @@ import org.apache.mesos.Protos.Value.Range;
 
 import static java.util.Objects.requireNonNull;
 
+import static org.apache.aurora.scheduler.resources.ResourceManager.quantityOf;
 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.PORTS;
@@ -108,40 +109,10 @@ public final class Resources {
    * @return {@code ResourceSlot} instance.
    */
   public ResourceSlot slot() {
-    return new ResourceSlot(getScalarValue(CPUS.getMesosName()),
-        Amount.of((long) getScalarValue(RAM_MB.getMesosName()), Data.MB),
-        Amount.of((long) getScalarValue(DISK_MB.getMesosName()), Data.MB),
-        getNumAvailablePorts());
-  }
-
-  private int getNumAvailablePorts() {
-    int offeredPorts = 0;
-    for (Range range : getPortRanges()) {
-      offeredPorts += 1 + range.getEnd() - range.getBegin();
-    }
-    return offeredPorts;
-  }
-
-  private double getScalarValue(String key) {
-    Iterable<Resource> resources = getResources(key);
-    double value = 0;
-    for (Resource r : resources) {
-      value += r.getScalar().getValue();
-    }
-    return value;
-  }
-
-  private Iterable<Resource> getResources(String key) {
-    return Iterables.filter(mesosResources, e -> e.getName().equals(key));
-  }
-
-  private Iterable<Range> getPortRanges() {
-    ImmutableList.Builder<Range> ranges = ImmutableList.builder();
-    for (Resource r : getResources(PORTS.getMesosName())) {
-      ranges.addAll(r.getRanges().getRangeList().iterator());
-    }
-
-    return ranges.build();
+    return new ResourceSlot(quantityOf(mesosResources, CPUS),
+        Amount.of(quantityOf(mesosResources, RAM_MB).longValue(), Data.MB),
+        Amount.of(quantityOf(mesosResources, DISK_MB).longValue(), Data.MB),
+        quantityOf(mesosResources, PORTS).intValue());
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/aurora/blob/f25a4b10/src/main/java/org/apache/aurora/scheduler/storage/db/views/DBResource.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/storage/db/views/DBResource.java b/src/main/java/org/apache/aurora/scheduler/storage/db/views/DBResource.java
index dc7e97d..95a6de3 100644
--- a/src/main/java/org/apache/aurora/scheduler/storage/db/views/DBResource.java
+++ b/src/main/java/org/apache/aurora/scheduler/storage/db/views/DBResource.java
@@ -25,6 +25,8 @@ public final class DBResource {
   }
 
   Resource toThrift() {
-    return IResource.newBuilder(type.getValue(), type.getTypeConverter().parseFrom(value));
+    return IResource.newBuilder(
+        type.getValue(),
+        type.getAuroraResourceConverter().parseFrom(value));
   }
 }

http://git-wip-us.apache.org/repos/asf/aurora/blob/f25a4b10/src/main/java/org/apache/aurora/scheduler/storage/db/views/DBResourceAggregate.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/storage/db/views/DBResourceAggregate.java b/src/main/java/org/apache/aurora/scheduler/storage/db/views/DBResourceAggregate.java
index 8c5d1f0..461d5c2 100644
--- a/src/main/java/org/apache/aurora/scheduler/storage/db/views/DBResourceAggregate.java
+++ b/src/main/java/org/apache/aurora/scheduler/storage/db/views/DBResourceAggregate.java
@@ -46,7 +46,7 @@ public final class DBResourceAggregate {
     return resources.stream()
         .map(e -> Pair.of(
             ResourceType.fromResource(e).getValue(),
-            ResourceType.fromResource(e).getTypeConverter().stringify(e.getRawValue())))
+            ResourceType.fromResource(e).getAuroraResourceConverter().stringify(e.getRawValue())))
         .collect(GuavaUtils.toImmutableList());
   }
 

http://git-wip-us.apache.org/repos/asf/aurora/blob/f25a4b10/src/test/java/org/apache/aurora/scheduler/resources/AuroraResourceConverterTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/resources/AuroraResourceConverterTest.java b/src/test/java/org/apache/aurora/scheduler/resources/AuroraResourceConverterTest.java
new file mode 100644
index 0000000..ca29c31
--- /dev/null
+++ b/src/test/java/org/apache/aurora/scheduler/resources/AuroraResourceConverterTest.java
@@ -0,0 +1,30 @@
+/**
+ * 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 org.junit.Test;
+
+import static org.apache.aurora.scheduler.resources.AuroraResourceConverter.DOUBLE;
+import static org.apache.aurora.scheduler.resources.AuroraResourceConverter.LONG;
+import static org.apache.aurora.scheduler.resources.AuroraResourceConverter.STRING;
+import static org.junit.Assert.assertEquals;
+
+public class AuroraResourceConverterTest {
+  @Test
+  public void testRoundtrip() {
+    assertEquals(234L, LONG.parseFrom(LONG.stringify(234L)).longValue());
+    assertEquals(2.34, DOUBLE.parseFrom(DOUBLE.stringify(2.34)).doubleValue(), 0.0);
+    assertEquals("http", STRING.parseFrom(STRING.stringify("http")));
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/f25a4b10/src/test/java/org/apache/aurora/scheduler/resources/MesosResourceConverterTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/resources/MesosResourceConverterTest.java b/src/test/java/org/apache/aurora/scheduler/resources/MesosResourceConverterTest.java
new file mode 100644
index 0000000..d4bb5aa
--- /dev/null
+++ b/src/test/java/org/apache/aurora/scheduler/resources/MesosResourceConverterTest.java
@@ -0,0 +1,46 @@
+/**
+ * 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 org.junit.Test;
+
+import static org.apache.aurora.scheduler.resources.MesosResourceConverter.RANGES;
+import static org.apache.aurora.scheduler.resources.MesosResourceConverter.SCALAR;
+import static org.apache.aurora.scheduler.resources.ResourceTestUtil.mesosRange;
+import static org.apache.aurora.scheduler.resources.ResourceTestUtil.mesosScalar;
+import static org.apache.aurora.scheduler.resources.ResourceType.CPUS;
+import static org.apache.aurora.scheduler.resources.ResourceType.PORTS;
+import static org.junit.Assert.assertEquals;
+
+public class MesosResourceConverterTest {
+  @Test
+  public void testQuantifyScalar() {
+    assertEquals(2, SCALAR.quantify(mesosScalar(CPUS, 2.0)).doubleValue(), 0.0);
+  }
+
+  @Test
+  public void testQuantifyRangeSinglePort() {
+    assertEquals(1, RANGES.quantify(mesosRange(PORTS, 5000)).doubleValue(), 0.0);
+  }
+
+  @Test
+  public void testQuantifyRangeMultiplePorts() {
+    assertEquals(3, RANGES.quantify(mesosRange(PORTS, 1, 2, 3)).doubleValue(), 0.0);
+  }
+
+  @Test
+  public void testQuantifyRangeDefaultValue() {
+    assertEquals(0, RANGES.quantify(mesosRange(PORTS)).doubleValue(), 0.0);
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/f25a4b10/src/test/java/org/apache/aurora/scheduler/resources/ResourceManagerTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/resources/ResourceManagerTest.java b/src/test/java/org/apache/aurora/scheduler/resources/ResourceManagerTest.java
index b6810b1..c53a21d 100644
--- a/src/test/java/org/apache/aurora/scheduler/resources/ResourceManagerTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/resources/ResourceManagerTest.java
@@ -14,6 +14,7 @@
 package org.apache.aurora.scheduler.resources;
 
 import java.util.EnumSet;
+import java.util.Set;
 
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
@@ -30,7 +31,10 @@ import static org.apache.aurora.gen.Resource.namedPort;
 import static org.apache.aurora.gen.Resource.numCpus;
 import static org.apache.aurora.scheduler.base.TaskTestUtil.JOB;
 import static org.apache.aurora.scheduler.base.TaskTestUtil.makeTask;
+import static org.apache.aurora.scheduler.resources.ResourceTestUtil.mesosRange;
+import static org.apache.aurora.scheduler.resources.ResourceTestUtil.mesosScalar;
 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.PORTS;
 import static org.apache.aurora.scheduler.resources.ResourceType.RAM_MB;
 import static org.apache.mesos.Protos.Value.Type.SCALAR;
@@ -85,4 +89,18 @@ public class ResourceManagerTest {
         EnumSet.allOf(ResourceType.class),
         ResourceManager.getTaskResourceTypes(IScheduledTask.build(builder)));
   }
+
+  @Test
+  public void testMesosResourceQuantity() {
+    Set<Protos.Resource> resources = ImmutableSet.of(
+        mesosScalar(CPUS, 3.0),
+        mesosScalar(CPUS, 4.0),
+        mesosScalar(RAM_MB, 64),
+        mesosRange(PORTS, 1, 3));
+
+    assertEquals(7.0, ResourceManager.quantityOf(resources, CPUS), 0.0);
+    assertEquals(64, ResourceManager.quantityOf(resources, RAM_MB), 0.0);
+    assertEquals(0.0, ResourceManager.quantityOf(resources, DISK_MB), 0.0);
+    assertEquals(2, ResourceManager.quantityOf(resources, PORTS), 0.0);
+  }
 }

http://git-wip-us.apache.org/repos/asf/aurora/blob/f25a4b10/src/test/java/org/apache/aurora/scheduler/resources/ResourceTestUtil.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/resources/ResourceTestUtil.java b/src/test/java/org/apache/aurora/scheduler/resources/ResourceTestUtil.java
index 1583cef..821c47f 100644
--- a/src/test/java/org/apache/aurora/scheduler/resources/ResourceTestUtil.java
+++ b/src/test/java/org/apache/aurora/scheduler/resources/ResourceTestUtil.java
@@ -27,6 +27,7 @@ import org.apache.aurora.scheduler.storage.entities.IResource;
 import org.apache.aurora.scheduler.storage.entities.IResourceAggregate;
 import org.apache.aurora.scheduler.storage.entities.ITaskConfig;
 import org.apache.mesos.Protos;
+import org.apache.mesos.Protos.Value.Type;
 
 import static org.apache.aurora.gen.Resource.diskMb;
 import static org.apache.aurora.gen.Resource.numCpus;
@@ -117,7 +118,7 @@ public final class ResourceTestUtil {
       boolean revocable) {
 
     Protos.Resource.Builder builder = Protos.Resource.newBuilder()
-        .setType(type.getMesosType())
+        .setType(type.equals(PORTS) ? Type.RANGES : Type.SCALAR)
         .setName(type.getMesosName());
 
     if (revocable) {

http://git-wip-us.apache.org/repos/asf/aurora/blob/f25a4b10/src/test/java/org/apache/aurora/scheduler/resources/ResourceTypeConverterTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/resources/ResourceTypeConverterTest.java b/src/test/java/org/apache/aurora/scheduler/resources/ResourceTypeConverterTest.java
deleted file mode 100644
index 78da84a..0000000
--- a/src/test/java/org/apache/aurora/scheduler/resources/ResourceTypeConverterTest.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * 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 org.junit.Test;
-
-import static org.apache.aurora.scheduler.resources.ResourceTypeConverter.DOUBLE;
-import static org.apache.aurora.scheduler.resources.ResourceTypeConverter.LONG;
-import static org.apache.aurora.scheduler.resources.ResourceTypeConverter.STRING;
-import static org.junit.Assert.assertEquals;
-
-public class ResourceTypeConverterTest {
-  @Test
-  public void testRoundtrip() {
-    assertEquals(234L, LONG.parseFrom(LONG.stringify(234L)).longValue());
-    assertEquals(2.34, DOUBLE.parseFrom(DOUBLE.stringify(2.34)).doubleValue(), 0.0);
-    assertEquals("http", STRING.parseFrom(STRING.stringify("http")));
-  }
-}

http://git-wip-us.apache.org/repos/asf/aurora/blob/f25a4b10/src/test/java/org/apache/aurora/scheduler/resources/ResourceTypeTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/resources/ResourceTypeTest.java b/src/test/java/org/apache/aurora/scheduler/resources/ResourceTypeTest.java
index dc9dc66..7ba5567 100644
--- a/src/test/java/org/apache/aurora/scheduler/resources/ResourceTypeTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/resources/ResourceTypeTest.java
@@ -17,20 +17,26 @@ import org.apache.aurora.gen.Resource;
 import org.apache.aurora.scheduler.storage.entities.IResource;
 import org.junit.Test;
 
+import static org.apache.aurora.scheduler.resources.ResourceTestUtil.mesosScalar;
+import static org.apache.aurora.scheduler.resources.ResourceType.CPUS;
+import static org.apache.aurora.scheduler.resources.ResourceType.RAM_MB;
+import static org.apache.aurora.scheduler.resources.ResourceType.fromIdValue;
+import static org.apache.aurora.scheduler.resources.ResourceType.fromResource;
 import static org.junit.Assert.assertEquals;
 
 public class ResourceTypeTest {
   @Test
   public void testFindValueById() {
-    assertEquals(
-        ResourceType.CPUS,
-        ResourceType.fromIdValue(Resource.numCpus(1.0).getSetField().getThriftFieldId()));
+    assertEquals(CPUS, fromIdValue(Resource.numCpus(1.0).getSetField().getThriftFieldId()));
   }
 
   @Test
   public void testFindByResource() {
-    assertEquals(
-        ResourceType.CPUS,
-        ResourceType.fromResource(IResource.build(Resource.numCpus(1.0))));
+    assertEquals(CPUS, fromResource(IResource.build(Resource.numCpus(1.0))));
+  }
+
+  @Test
+  public void testFindByMesosResource() {
+    assertEquals(RAM_MB, fromResource(mesosScalar(RAM_MB, 1.0)));
   }
 }