You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by mz...@apache.org on 2019/01/07 17:20:48 UTC
[mesos] 04/05: Added a `Resources` method
`contains(ResourceQuantities)`.
This is an automated email from the ASF dual-hosted git repository.
mzhu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mesos.git
commit 983cd42edd404d7cb04b96adb6c379c2e09efdca
Author: Meng Zhu <mz...@mesosphere.io>
AuthorDate: Wed Dec 19 14:49:11 2018 -0800
Added a `Resources` method `contains(ResourceQuantities)`.
This method checks if the quantities of this `Resources` is a
superset of the given `ResourceQuantities`. If a `Resource` object
is `SCALAR` type, its quantity is its scalar value. For `RANGES`
and `SET` type, their quantities are the number of different
instances in the range or set. For example, "range:[1-5]" has a
quantity of 5 and "set:{a,b}" has a quantity of 2.
Also added a dedicated test.
Review: https://reviews.apache.org/r/69601
---
include/mesos/resources.hpp | 11 +++++
include/mesos/v1/resources.hpp | 11 +++++
src/common/resources.cpp | 44 +++++++++++++++++++
src/tests/resources_tests.cpp | 96 ++++++++++++++++++++++++++++++++++++++++++
src/v1/resources.cpp | 44 +++++++++++++++++++
5 files changed, 206 insertions(+)
diff --git a/include/mesos/resources.hpp b/include/mesos/resources.hpp
index 36ccf0e..62b6b0c 100644
--- a/include/mesos/resources.hpp
+++ b/include/mesos/resources.hpp
@@ -59,6 +59,8 @@ namespace mesos {
// Forward declaration.
class ResourceConversion;
+namespace internal { class ResourceQuantities; }
+
// Helper functions.
bool operator==(
@@ -443,6 +445,15 @@ public:
// Checks if this Resources contains the given Resource.
bool contains(const Resource& that) const;
+ // Checks if the quantities of this `Resources` is a superset of the
+ // given `ResourceQuantities`. If a `Resource` object is `SCALAR` type,
+ // its quantity is its scalar value. For `RANGES` and `SET` type, their
+ // quantities are the number of different instances in the range or set.
+ // For example, "range:[1-5]" has a quantity of 5 and "set:{a,b}" has a
+ // quantity of 2.
+ bool contains(
+ const mesos::internal::ResourceQuantities& quantities) const;
+
// Count the Resource objects that match the specified value.
//
// NOTE:
diff --git a/include/mesos/v1/resources.hpp b/include/mesos/v1/resources.hpp
index 1a9ea44..eb23359 100644
--- a/include/mesos/v1/resources.hpp
+++ b/include/mesos/v1/resources.hpp
@@ -54,6 +54,9 @@
// but instead just written for correct semantics.
namespace mesos {
+
+namespace internal { class ResourceQuantities; }
+
namespace v1 {
// Forward declaration.
@@ -442,6 +445,14 @@ public:
// Checks if this Resources contains the given Resource.
bool contains(const Resource& that) const;
+ // Checks if the quantities of this `Resources` is a superset of the
+ // given `ResourceQuantities`. If a `Resource` object is `SCALAR` type,
+ // its quantity is its scalar value. For `RANGES` and `SET` type, their
+ // quantities are the number of different instances in the range or set.
+ // For example, "range:[1-5]" has a quantity of 5 and "set:{a,b}" has a
+ // quantity of 2.
+ bool contains(
+ const mesos::internal::ResourceQuantities& quantities) const;
// Count the Resource objects that match the specified value.
//
// NOTE:
diff --git a/src/common/resources.cpp b/src/common/resources.cpp
index 758b5a2..f50fe56 100644
--- a/src/common/resources.cpp
+++ b/src/common/resources.cpp
@@ -38,11 +38,13 @@
#include <stout/strings.hpp>
#include <stout/unreachable.hpp>
+#include "common/resource_quantities.hpp"
#include "common/resources_utils.hpp"
using std::make_shared;
using std::map;
using std::ostream;
+using std::pair;
using std::set;
using std::shared_ptr;
using std::string;
@@ -50,6 +52,8 @@ using std::vector;
using google::protobuf::RepeatedPtrField;
+using mesos::internal::ResourceQuantities;
+
namespace mesos {
/////////////////////////////////////////////////
@@ -1506,6 +1510,46 @@ bool Resources::contains(const Resource& that) const
}
+// This function assumes all quantities with the same name are merged
+// in the input `quantities` which is a guaranteed property of
+// `ResourceQuantities`.
+bool Resources::contains(const ResourceQuantities& quantities) const
+{
+ foreach (auto& quantity, quantities){
+ double remaining = quantity.second.value();
+
+ foreach (const Resource& r, get(quantity.first)) {
+ switch (r.type()) {
+ case Value::SCALAR: remaining -= r.scalar().value(); break;
+ case Value::SET: remaining -= r.set().item_size(); break;
+ case Value::RANGES:
+ foreach (const Value::Range& range, r.ranges().range()) {
+ remaining -= range.end() - range.begin() + 1;
+ if (remaining <= 0) {
+ break;
+ }
+ }
+ break;
+ case Value::TEXT:
+ LOG(FATAL) << "Unexpected TEXT type resource " << r << " in "
+ << *this;
+ break;
+ }
+
+ if (remaining <= 0) {
+ break;
+ }
+ }
+
+ if (remaining > 0) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
size_t Resources::count(const Resource& that) const
{
foreach (
diff --git a/src/tests/resources_tests.cpp b/src/tests/resources_tests.cpp
index 5337a5c..f762d17 100644
--- a/src/tests/resources_tests.cpp
+++ b/src/tests/resources_tests.cpp
@@ -32,6 +32,7 @@
#include <mesos/v1/resources.hpp>
+#include "common/resource_quantities.hpp"
#include "common/resources_utils.hpp"
#include "internal/evolve.hpp"
@@ -58,6 +59,8 @@ using mesos::internal::evolve;
using mesos::internal::protobuf::createLabel;
+using mesos::internal::ResourceQuantities;
+
namespace mesos {
namespace internal {
namespace tests {
@@ -2035,6 +2038,99 @@ TEST(ResourcesTest, isScalarQuantity)
}
+TEST(ResourcesTest, ContainsResourceQuantities)
+{
+ auto resources = [](const string& s) {
+ return CHECK_NOTERROR(Resources::parse(s));
+ };
+
+ auto quantities = [](const string& s) {
+ return CHECK_NOTERROR(ResourceQuantities::fromString(s));
+ };
+
+ // Empty case tests.
+
+ Resources emptyResources;
+ ResourceQuantities emptyQuantities;
+
+ EXPECT_TRUE(emptyResources.contains(emptyQuantities));
+ EXPECT_FALSE(emptyResources.contains(quantities("cpus:1")));
+ EXPECT_TRUE(resources("cpus:1").contains(emptyQuantities));
+
+ // Single scalar resource tests.
+
+ EXPECT_TRUE(resources("cpus:2").contains(quantities("cpus:1")));
+
+ EXPECT_TRUE(resources("cpus:1").contains(quantities("cpus:1")));
+
+ EXPECT_FALSE(resources("cpus:0.5").contains(quantities("cpus:1")));
+
+ // Single range resource tests.
+
+ EXPECT_TRUE(resources("ports:[1-3]").contains(quantities("ports:2")));
+
+ EXPECT_TRUE(resources("ports:[1-2]").contains(quantities("ports:2")));
+
+ EXPECT_FALSE(resources("ports:[1-1]").contains(quantities("ports:2")));
+
+ // Single set resources tests.
+
+ EXPECT_TRUE(resources("features:{a,b,c}").contains(quantities("features:2")));
+
+ EXPECT_TRUE(resources("features:{a,b}").contains(quantities("features:2")));
+
+ EXPECT_FALSE(resources("features:{a}").contains(quantities("features:2")));
+
+ // Multiple resources tests.
+
+ EXPECT_TRUE(resources("cpus:3;ports:[1-3];features:{a,b,c};mem:10")
+ .contains(quantities("cpus:3;ports:3;features:3")));
+
+ EXPECT_TRUE(resources("cpus:3;ports:[1-3];features:{a,b,c}")
+ .contains(quantities("cpus:3;ports:3;features:3")));
+
+ EXPECT_FALSE(resources("cpus:1;ports:[1-3];features:{a,b,c}")
+ .contains(quantities("cpus:3;ports:3;features:3")));
+
+ EXPECT_FALSE(resources("cpus:3;ports:[1-3]")
+ .contains(quantities("cpus:3;ports:3;features:3")));
+
+ // Duplicate names.
+
+ EXPECT_FALSE(resources("cpus(role1):2").contains(quantities("cpus:3")));
+
+ EXPECT_TRUE(resources("cpus(role1):2;cpus:1").contains(quantities("cpus:3")));
+
+ Resource::ReservationInfo reservation =
+ createDynamicReservationInfo("role", "principal");
+ Resources resources_ = createReservedResource("ports", "[1-10]", reservation);
+
+ EXPECT_FALSE(resources_.contains(quantities("ports:12")));
+
+ resources_ +=
+ CHECK_NOTERROR(Resources::parse("ports:[20-25]")); // 15 ports in total.
+
+ EXPECT_TRUE(resources_.contains(quantities("ports:12")));
+
+ resources_ = createPersistentVolume(
+ Megabytes(64),
+ "role1",
+ "id1",
+ "path1",
+ None(),
+ None(),
+ "principal1",
+ true); // Shared.
+
+ EXPECT_FALSE(resources_.contains(quantities("disk:128")));
+
+ resources_ +=
+ CHECK_NOTERROR(Resources::parse("disk:64")); // 128M disk in total.
+
+ EXPECT_TRUE(resources_.contains(quantities("disk:128")));
+}
+
+
TEST(ReservedResourcesTest, Validation)
{
// Unreserved.
diff --git a/src/v1/resources.cpp b/src/v1/resources.cpp
index d717f53..110be10 100644
--- a/src/v1/resources.cpp
+++ b/src/v1/resources.cpp
@@ -39,11 +39,13 @@
#include <stout/strings.hpp>
#include <stout/unreachable.hpp>
+#include "common/resource_quantities.hpp"
#include "common/resources_utils.hpp"
using std::make_shared;
using std::map;
using std::ostream;
+using std::pair;
using std::set;
using std::shared_ptr;
using std::string;
@@ -51,6 +53,8 @@ using std::vector;
using google::protobuf::RepeatedPtrField;
+using mesos::internal::ResourceQuantities;
+
namespace mesos {
namespace v1 {
@@ -1524,6 +1528,46 @@ bool Resources::contains(const Resource& that) const
}
+// This function assumes all quantities with the same name are merged
+// in the input `quantities` which is a guaranteed property of
+// `ResourceQuantities`.
+bool Resources::contains(const ResourceQuantities& quantities) const
+{
+ foreach (auto& quantity, quantities){
+ double remaining = quantity.second.value();
+
+ foreach (const Resource& r, get(quantity.first)) {
+ switch (r.type()) {
+ case Value::SCALAR: remaining -= r.scalar().value(); break;
+ case Value::SET: remaining -= r.set().item_size(); break;
+ case Value::RANGES:
+ foreach (const Value::Range& range, r.ranges().range()) {
+ remaining -= range.end() - range.begin() + 1;
+ if (remaining <= 0) {
+ break;
+ }
+ }
+ break;
+ case Value::TEXT:
+ LOG(FATAL) << "Unexpected TEXT type resource " << r << " in "
+ << *this;
+ break;
+ }
+
+ if (remaining <= 0) {
+ break;
+ }
+ }
+
+ if (remaining > 0) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
size_t Resources::count(const Resource& that) const
{
foreach (