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:49 UTC

[mesos] 05/05: Extended `min_allocatable_resources` flag to cover non-scalar resources.

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 386b1fe99bb9d10af2abaca4832bf584b6181799
Author: Meng Zhu <mz...@mesosphere.io>
AuthorDate: Sun Dec 30 20:55:06 2018 -0800

    Extended `min_allocatable_resources` flag to cover non-scalar resources.
    
    The master flag min_allocatable_resources currently only supports
    scalar resources. Sometimes it is desirable to control the quantities
    of non-scalar resources in the offer as well, for example, to
    ensure offers always contain port resources.
    
    This patch extended the flag to cover non-scalar resources.
    For RANGES and SET type resources, 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.
    
    This patch also updated affected tests and modified the existing
    test for min_allocatable_resources to cover the updated use cases.
    
    Review: https://reviews.apache.org/r/69603
---
 docs/configuration/master.md                    | 22 ++++++----
 include/mesos/allocator/allocator.hpp           |  5 ++-
 src/master/allocator/mesos/hierarchical.cpp     |  6 +--
 src/master/flags.cpp                            | 22 ++++++----
 src/master/master.cpp                           | 58 +++++++++++--------------
 src/tests/hierarchical_allocator_benchmarks.cpp | 13 ++++--
 src/tests/hierarchical_allocator_tests.cpp      | 13 ++++--
 src/tests/master_allocator_tests.cpp            | 19 ++++----
 8 files changed, 88 insertions(+), 70 deletions(-)

diff --git a/docs/configuration/master.md b/docs/configuration/master.md
index 83b83b0..7859d80 100644
--- a/docs/configuration/master.md
+++ b/docs/configuration/master.md
@@ -207,15 +207,19 @@ load an alternate allocator module using <code>--modules</code>.
     --min_allocatable_resources=VALUE
   </td>
   <td>
-One or more sets of resources that define the minimum allocatable
-resources for the allocator. The allocator will only offer resources
-that contain at least one of the specified sets. The resources in each
-set should be delimited by semicolons, and the sets should be delimited
-by the pipe character.
-(Example: <code>disk:1|cpu:1;mem:32</code>
-configures the allocator to only offer resources if they contain a disk
-resource of at least 1 megabyte, or if they contain both 1 cpu and
-32 megabytes of memory.) (default: cpus:0.01|mem:32).
+One or more sets of resource quantities that define the minimum allocatable
+resource for the allocator. The allocator will only offer resources that
+meets the quantity requirement of at least one of the specified sets. For
+`SCALAR` type resources, 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. The resources in each set should be delimited by
+semicolons (acting as logical AND), and each set should be delimited by the
+pipe character (acting as logical OR).
+(Example: `disk:1|cpu:1;mem:32;port:1` configures the allocator to only offer
+resources if they contain a disk resource of at least 1 megabyte, or if they
+at least contain 1 cpu, 32 megabytes of memory and 1 port.)
+(default: cpus:0.01|mem:32).
   </td>
 </tr>
 
diff --git a/include/mesos/allocator/allocator.hpp b/include/mesos/allocator/allocator.hpp
index 2a6849b..b58371b 100644
--- a/include/mesos/allocator/allocator.hpp
+++ b/include/mesos/allocator/allocator.hpp
@@ -38,6 +38,8 @@
 #include <stout/option.hpp>
 #include <stout/try.hpp>
 
+#include "common/resource_quantities.hpp"
+
 namespace mesos {
 namespace allocator {
 
@@ -50,7 +52,8 @@ struct Options
   Option<std::set<std::string>> fairnessExcludeResourceNames = None();
   bool filterGpuResources = true;
   Option<DomainInfo> domain = None();
-  Option<std::vector<Resources>> minAllocatableResources = None();
+  Option<std::vector<mesos::internal::ResourceQuantities>>
+    minAllocatableResources = None();
   size_t maxCompletedFrameworks = 0;
   bool publishPerFrameworkMetrics = true;
 };
diff --git a/src/master/allocator/mesos/hierarchical.cpp b/src/master/allocator/mesos/hierarchical.cpp
index cc8ab91..40621ec 100644
--- a/src/master/allocator/mesos/hierarchical.cpp
+++ b/src/master/allocator/mesos/hierarchical.cpp
@@ -41,6 +41,7 @@
 #include <stout/stringify.hpp>
 
 #include "common/protobuf_utils.hpp"
+#include "common/resource_quantities.hpp"
 
 using std::set;
 using std::string;
@@ -2430,11 +2431,10 @@ bool HierarchicalAllocatorProcess::allocatable(const Resources& resources)
     return true;
   }
 
-  Resources quantity = resources.createStrippedScalarQuantity();
   foreach (
-      const Resources& minResources,
+      const ResourceQuantities& resourceQuantities,
       CHECK_NOTNONE(options.minAllocatableResources)) {
-    if (quantity.contains(minResources)) {
+    if (resources.contains(resourceQuantities)) {
       return true;
     }
   }
diff --git a/src/master/flags.cpp b/src/master/flags.cpp
index f9b68bc..9288714 100644
--- a/src/master/flags.cpp
+++ b/src/master/flags.cpp
@@ -488,14 +488,20 @@ mesos::internal::master::Flags::Flags()
 
   add(&Flags::min_allocatable_resources,
       "min_allocatable_resources",
-      "One or more sets of resources that define the minimum allocatable\n"
-      "resources for the allocator. The allocator will only offer resources\n"
-      "that contain at least one of the specified sets. The resources in\n"
-      "each set should be delimited by semicolons, and the sets should be\n"
-      "delimited by the pipe character.\n"
-      "(Example: `disk:1|cpu:1;mem:32` configures the allocator to only offer\n"
-      "resources if they contain a disk resource of at least 1 megabyte, or\n"
-      "if they contain both 1 cpu and 32 megabytes of memory.)\n",
+      "One or more sets of resource quantities that define the minimum\n"
+      "allocatable resources for the allocator. The allocator will only offer\n"
+      "resources that meets the quantity requirement of at least one of the\n"
+      "specified sets. For `SCALAR` type resources, its quantity is its\n"
+      "scalar value. For `RANGES` and `SET` type, their quantities are the\n"
+      "number of different instances in the range or set. For example,\n"
+      "`range:[1-5]` has a quantity of 5 and `set:{a,b}` has a quantity of 2.\n"
+      "The resources in each set should be delimited by semicolons (acting as\n"
+      "logical AND), and each set should be delimited by the pipe character\n"
+      "(acting as logical OR).\n"
+      "(Example: `disk:1|cpu:1;mem:32;port:1` configures the allocator to\n"
+      "only offer resources if they contain a disk resource of at least\n"
+      "1 megabyte, or if they at least contain 1 cpu, 32 megabytes of memory\n"
+      "and 1 port.)\n",
       "cpus:" + stringify(MIN_CPUS) +
         "|mem:" + stringify((double)MIN_MEM.bytes() / Bytes::MEGABYTES));
 
diff --git a/src/master/master.cpp b/src/master/master.cpp
index b4faf2b..49b6e5c 100644
--- a/src/master/master.cpp
+++ b/src/master/master.cpp
@@ -79,6 +79,7 @@
 #include "common/build.hpp"
 #include "common/http.hpp"
 #include "common/protobuf_utils.hpp"
+#include "common/resource_quantities.hpp"
 #include "common/status_utils.hpp"
 
 #include "credentials/credentials.hpp"
@@ -149,6 +150,7 @@ using mesos::master::contender::MasterContender;
 
 using mesos::master::detector::MasterDetector;
 
+using mesos::internal::ResourceQuantities;
 
 static bool isValidFailoverTimeout(const FrameworkInfo& frameworkInfo);
 
@@ -720,39 +722,31 @@ void Master::initialize()
   }
 
   // Parse min_allocatable_resources.
-  Try<vector<Resources>> minAllocatableResources =
-    [](const string& resourceString) -> Try<vector<Resources>> {
-      vector<Resources> result;
-
-      foreach (const string& token, strings::tokenize(resourceString, "|")) {
-        Try<vector<Resource>> resourceVector =
-          Resources::fromSimpleString(token);
-
-        if (resourceVector.isError()) {
-          return Error(resourceVector.error());
-        }
-
-        result.push_back(Resources(CHECK_NOTERROR(resourceVector)));
-      }
-
-      return result;
-  }(flags.min_allocatable_resources);
-
-  if (minAllocatableResources.isError()) {
-    EXIT(EXIT_FAILURE) << "Error parsing min_allocatable_resources: '"
-                       << flags.min_allocatable_resources
-                       << "': " << minAllocatableResources.error();
-  }
-
-  // Validate that configured minimum resources are "pure" scalar quantities.
+  vector<ResourceQuantities> minAllocatableResources;
   foreach (
-      const Resources& resources, CHECK_NOTERROR(minAllocatableResources)) {
-    if (!Resources::isScalarQuantity(resources)) {
-      EXIT(EXIT_FAILURE) << "Invalid min_allocatable_resources: '"
-                         << flags.min_allocatable_resources << "': "
-                         << "minimum allocatable resources should only"
-                         << "have name, type (scalar) and value set";
+      const string& token,
+      strings::tokenize(flags.min_allocatable_resources, "|")) {
+    Try<ResourceQuantities> resourceQuantities =
+      ResourceQuantities::fromString(token);
+
+    if (resourceQuantities.isError()) {
+      EXIT(EXIT_FAILURE) << "Error parsing min_allocatable_resources '"
+                         << flags.min_allocatable_resources
+                         << "': " << resourceQuantities.error();
+    }
+
+    // We check the configuration against first-class resources and warn
+    // against possible mis-configuration (e.g. typo).
+    set<string> firstClassResources = {"cpus", "mem", "disk", "ports", "gpus"};
+    for (auto it = resourceQuantities->begin(); it != resourceQuantities->end();
+         ++it) {
+      if (firstClassResources.count(it->first) == 0) {
+        LOG(WARNING) << "Non-first-class resource '" << it->first
+                     << "' is configured as part of min_allocatable_resources";
+      }
     }
+
+    minAllocatableResources.push_back(resourceQuantities.get());
   }
 
   // Initialize the allocator options.
@@ -763,7 +757,7 @@ void Master::initialize()
     flags.fair_sharing_excluded_resource_names;
   options.filterGpuResources = flags.filter_gpu_resources;
   options.domain = flags.domain;
-  options.minAllocatableResources = CHECK_NOTERROR(minAllocatableResources);
+  options.minAllocatableResources = minAllocatableResources;
   options.maxCompletedFrameworks = flags.max_completed_frameworks;
   options.publishPerFrameworkMetrics = flags.publish_per_framework_metrics;
 
diff --git a/src/tests/hierarchical_allocator_benchmarks.cpp b/src/tests/hierarchical_allocator_benchmarks.cpp
index 527a25d..a240a2b 100644
--- a/src/tests/hierarchical_allocator_benchmarks.cpp
+++ b/src/tests/hierarchical_allocator_benchmarks.cpp
@@ -32,6 +32,8 @@
 #include <stout/hashmap.hpp>
 #include <stout/stopwatch.hpp>
 
+#include "common/resource_quantities.hpp"
+
 #include "master/constants.hpp"
 
 #include "master/allocator/mesos/hierarchical.hpp"
@@ -44,6 +46,8 @@ using mesos::internal::master::MIN_MEM;
 
 using mesos::internal::master::allocator::HierarchicalDRFAllocator;
 
+using mesos::internal::ResourceQuantities;
+
 using mesos::internal::slave::AGENT_CAPABILITIES;
 
 using mesos::allocator::Allocator;
@@ -137,10 +141,11 @@ struct BenchmarkConfig
       frameworkSorter(frameworkSorter_),
       allocationInterval(allocationInterval_)
   {
+    minAllocatableResources.push_back(CHECK_NOTERROR(
+        ResourceQuantities::fromString("cpus:" + stringify(MIN_CPUS))));
     minAllocatableResources.push_back(
-        CHECK_NOTERROR(Resources::parse("cpus:" + stringify(MIN_CPUS))));
-    minAllocatableResources.push_back(CHECK_NOTERROR(Resources::parse(
-        "mem:" + stringify((double)MIN_MEM.bytes() / Bytes::MEGABYTES))));
+        CHECK_NOTERROR(ResourceQuantities::fromString(
+            "mem:" + stringify((double)MIN_MEM.bytes() / Bytes::MEGABYTES))));
   }
 
   string allocator;
@@ -149,7 +154,7 @@ struct BenchmarkConfig
 
   Duration allocationInterval;
 
-  vector<Resources> minAllocatableResources;
+  vector<ResourceQuantities> minAllocatableResources;
 
   vector<FrameworkProfile> frameworkProfiles;
   vector<AgentProfile> agentProfiles;
diff --git a/src/tests/hierarchical_allocator_tests.cpp b/src/tests/hierarchical_allocator_tests.cpp
index a3a6d7b..cc88afb 100644
--- a/src/tests/hierarchical_allocator_tests.cpp
+++ b/src/tests/hierarchical_allocator_tests.cpp
@@ -40,6 +40,8 @@
 #include <stout/stopwatch.hpp>
 #include <stout/utils.hpp>
 
+#include "common/resource_quantities.hpp"
+
 #include "master/constants.hpp"
 #include "master/flags.hpp"
 
@@ -61,6 +63,8 @@ using mesos::internal::protobuf::createLabel;
 
 using mesos::internal::slave::AGENT_CAPABILITIES;
 
+using mesos::internal::ResourceQuantities;
+
 using mesos::allocator::Allocator;
 
 using process::Clock;
@@ -176,11 +180,12 @@ protected:
         };
     }
 
-    vector<Resources> minAllocatableResources;
+    vector<ResourceQuantities> minAllocatableResources;
+    minAllocatableResources.push_back(CHECK_NOTERROR(
+        ResourceQuantities::fromString("cpus:" + stringify(MIN_CPUS))));
     minAllocatableResources.push_back(
-        CHECK_NOTERROR(Resources::parse("cpus:" + stringify(MIN_CPUS))));
-    minAllocatableResources.push_back(CHECK_NOTERROR(Resources::parse(
-        "mem:" + stringify((double)MIN_MEM.bytes() / Bytes::MEGABYTES))));
+        CHECK_NOTERROR(ResourceQuantities::fromString(
+            "mem:" + stringify((double)MIN_MEM.bytes() / Bytes::MEGABYTES))));
 
     Options options;
     options.allocationInterval = flags.allocation_interval;
diff --git a/src/tests/master_allocator_tests.cpp b/src/tests/master_allocator_tests.cpp
index 9dbab18..e960757 100644
--- a/src/tests/master_allocator_tests.cpp
+++ b/src/tests/master_allocator_tests.cpp
@@ -1243,7 +1243,8 @@ TYPED_TEST(MasterAllocatorTest, MinAllocatableResources)
   Clock::pause();
 
   master::Flags masterFlags = this->CreateMasterFlags();
-  masterFlags.min_allocatable_resources = "cpus:1;mem:100|disk:100";
+  masterFlags.min_allocatable_resources =
+    "cpus:1;mem:100|disk:100|cpus:1;ports:10";
 
   TestAllocator<TypeParam> allocator;
 
@@ -1258,10 +1259,10 @@ TYPED_TEST(MasterAllocatorTest, MinAllocatableResources)
 
   // Add agents with non-allocatable resources.
   vector<string> nonAllocatableAgentResources = {
-    "cpus:1;mem:0;disk:0",
-    "cpus:0;mem:100;disk:0",
-    "cpus:0.5;mem:200;disk:0",
-    "cpus:0;mem:0;disk:50"};
+    "cpus:1;mem:10;disk:10;ports:[1-5]",
+    "cpus:0.5;mem:100;disk:10;ports:[6-10]",
+    "cpus:0.5;mem:200;disk:10;ports:[11-15]",
+    "cpus:0.5;mem:10;disk:50;ports:[16-20]"};
 
   foreach (const string& resourcesString, nonAllocatableAgentResources) {
     Future<SlaveRegisteredMessage> slaveRegisteredMessage =
@@ -1284,10 +1285,10 @@ TYPED_TEST(MasterAllocatorTest, MinAllocatableResources)
 
   // Add agents with allocatable resources.
   vector<string> allocatableAgentResources = {
-    "cpus:1;mem:100;disk:0",
-    "cpus:0;mem:0;disk:100",
-    "cpus:0.5;mem:0;disk:100",
-    "cpus:1;mem:100;disk:50",
+    "cpus:1;mem:100;disk:0;ports:[1-5]",    // contains `cpus:1;mem:100`
+    "cpus:0.5;mem:10;disk:100;ports:[1-5]", // contains `disk:100`
+    "cpus:1;mem:100;disk:50",               // contains `cpus:1;mem:100`
+    "cpus:1;mem:10;disk:10;ports:[1-10]",   // contains `cpus:1;ports:10`
   };
 
   hashset<SlaveID> allocatableAgents;