You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by mp...@apache.org on 2016/01/15 18:59:54 UTC

[4/4] mesos git commit: Updated `Master::Http::state` to use `jsonify` in mesos.

Updated `Master::Http::state` to use `jsonify` in mesos.

Review: https://reviews.apache.org/r/41595/


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

Branch: refs/heads/master
Commit: e8af5cca9bbc7e0ab96749e42b584c09554b2347
Parents: 69188c5
Author: Michael Park <mp...@apache.org>
Authored: Tue Dec 8 09:25:43 2015 -0500
Committer: Michael Park <mp...@apache.org>
Committed: Fri Jan 15 09:37:47 2016 -0800

----------------------------------------------------------------------
 src/common/http.cpp | 184 +++++++++++++++++++++++
 src/common/http.hpp |  10 ++
 src/master/http.cpp | 381 +++++++++++++++++++++++++++++++++--------------
 3 files changed, 467 insertions(+), 108 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/e8af5cca/src/common/http.cpp
----------------------------------------------------------------------
diff --git a/src/common/http.cpp b/src/common/http.cpp
index 7165551..6d88091 100644
--- a/src/common/http.cpp
+++ b/src/common/http.cpp
@@ -396,5 +396,189 @@ JSON::Object model(
   return object;
 }
 
+
+void json(JSON::ObjectWriter* writer, const Task& task)
+{
+  writer->field("id", task.task_id().value());
+  writer->field("name", task.name());
+  writer->field("framework_id", task.framework_id().value());
+  writer->field("executor_id", task.executor_id().value());
+  writer->field("slave_id", task.slave_id().value());
+  writer->field("state", TaskState_Name(task.state()));
+  writer->field("resources", Resources(task.resources()));
+  writer->field("statuses", task.statuses());
+
+  if (task.has_labels()) {
+    writer->field("labels", task.labels());
+  }
+
+  if (task.has_discovery()) {
+    writer->field("discovery", task.discovery());
+  }
+
+  if (task.has_container()) {
+    writer->field("container", task.container());
+  }
+}
+
 }  // namespace internal {
+
+void json(JSON::ObjectWriter* writer, const Attributes& attributes)
+{
+  foreach (const Attribute& attribute, attributes) {
+    switch (attribute.type()) {
+      case Value::SCALAR:
+        writer->field(attribute.name(), attribute.scalar());
+        break;
+      case Value::RANGES:
+        writer->field(attribute.name(), attribute.ranges());
+        break;
+      case Value::SET:
+        writer->field(attribute.name(), attribute.set());
+        break;
+      case Value::TEXT:
+        writer->field(attribute.name(), attribute.text());
+        break;
+      default:
+        LOG(FATAL) << "Unexpected Value type: " << attribute.type();
+    }
+  }
+}
+
+
+void json(JSON::ObjectWriter* writer, const CommandInfo& command)
+{
+  if (command.has_shell()) {
+    writer->field("shell", command.shell());
+  }
+
+  if (command.has_value()) {
+    writer->field("value", command.value());
+  }
+
+  writer->field("argv", command.arguments());
+
+  if (command.has_environment()) {
+    writer->field("environment", command.environment());
+  }
+
+  writer->field("uris", [&command](JSON::ArrayWriter* writer) {
+    foreach (const CommandInfo::URI& uri, command.uris()) {
+      writer->element([&uri](JSON::ObjectWriter* writer) {
+        writer->field("value", uri.value());
+        writer->field("executable", uri.executable());
+      });
+    }
+  });
+}
+
+
+void json(JSON::ObjectWriter* writer, const ExecutorInfo& executorInfo)
+{
+  writer->field("executor_id", executorInfo.executor_id().value());
+  writer->field("name", executorInfo.name());
+  writer->field("framework_id", executorInfo.framework_id().value());
+  writer->field("command", executorInfo.command());
+  writer->field("resources", Resources(executorInfo.resources()));
+}
+
+
+void json(JSON::ArrayWriter* writer, const Labels& labels)
+{
+  json(writer, labels.labels());
+}
+
+
+void json(JSON::ObjectWriter* writer, const NetworkInfo& info)
+{
+  if (info.has_ip_address()) {
+    writer->field("ip_address", info.ip_address());
+  }
+
+  if (info.groups().size() > 0) {
+    writer->field("groups", info.groups());
+  }
+
+  if (info.has_labels()) {
+    writer->field("labels", info.labels());
+  }
+
+  if (info.ip_addresses().size() > 0) {
+    writer->field("ip_addresses", info.ip_addresses());
+  }
+}
+
+
+void json(JSON::ObjectWriter* writer, const Resources& resources)
+{
+  hashmap<std::string, double> scalars = {{"cpus", 0}, {"mem", 0}, {"disk", 0}};
+  hashmap<std::string, Value::Ranges> ranges;
+  hashmap<std::string, Value::Set> sets;
+
+  foreach (const Resource& resource, resources) {
+    std::string name =
+      resource.name() + (Resources::isRevocable(resource) ? "_revocable" : "");
+    switch (resource.type()) {
+      case Value::SCALAR:
+        scalars[name] += resource.scalar().value();
+        break;
+      case Value::RANGES:
+        ranges[name] += resource.ranges();
+        break;
+      case Value::SET:
+        sets[name] += resource.set();
+        break;
+      default:
+        LOG(FATAL) << "Unexpected Value type: " << resource.type();
+    }
+  }
+
+  json(writer, scalars);
+  json(writer, ranges);
+  json(writer, sets);
+}
+
+
+void json(JSON::ObjectWriter* writer, const TaskStatus& status)
+{
+  writer->field("state", TaskState_Name(status.state()));
+  writer->field("timestamp", status.timestamp());
+
+  if (status.has_labels()) {
+    writer->field("labels", status.labels());
+  }
+
+  if (status.has_container_status()) {
+    writer->field("container_status", status.container_status());
+  }
+
+  if (status.has_healthy()) {
+    writer->field("healthy", status.healthy());
+  }
+}
+
+
+void json(JSON::NumberWriter* writer, const Value::Scalar& scalar)
+{
+  writer->set(scalar.value());
+}
+
+
+void json(JSON::StringWriter* writer, const Value::Ranges& ranges)
+{
+  writer->append(stringify(ranges));
+}
+
+
+void json(JSON::StringWriter* writer, const Value::Set& set)
+{
+  writer->append(stringify(set));
+}
+
+
+void json(JSON::StringWriter* writer, const Value::Text& text)
+{
+  writer->append(text.value());
+}
+
 }  // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/e8af5cca/src/common/http.hpp
----------------------------------------------------------------------
diff --git a/src/common/http.hpp b/src/common/http.hpp
index 4f4cbf6..f88590f 100644
--- a/src/common/http.hpp
+++ b/src/common/http.hpp
@@ -24,6 +24,7 @@
 
 #include <stout/hashmap.hpp>
 #include <stout/json.hpp>
+#include <stout/jsonify.hpp>
 #include <stout/protobuf.hpp>
 
 namespace mesos {
@@ -88,7 +89,16 @@ JSON::Object model(
     const TaskState& state,
     const std::vector<TaskStatus>& statuses);
 
+
+void json(JSON::ObjectWriter* writer, const Task& task);
+
 } // namespace internal {
+
+void json(JSON::ObjectWriter* writer, const Attributes& attributes);
+void json(JSON::ArrayWriter* writer, const Labels& labels);
+void json(JSON::ObjectWriter* writer, const Resources& resources);
+void json(JSON::ObjectWriter* writer, const TaskStatus& status);
+
 } // namespace mesos {
 
 #endif // __COMMON_HTTP_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/e8af5cca/src/master/http.cpp
----------------------------------------------------------------------
diff --git a/src/master/http.cpp b/src/master/http.cpp
index bcafc7a..e6ba311 100644
--- a/src/master/http.cpp
+++ b/src/master/http.cpp
@@ -42,12 +42,14 @@
 #include <stout/hashmap.hpp>
 #include <stout/hashset.hpp>
 #include <stout/json.hpp>
+#include <stout/jsonify.hpp>
 #include <stout/lambda.hpp>
 #include <stout/net.hpp>
 #include <stout/nothing.hpp>
 #include <stout/numify.hpp>
 #include <stout/os.hpp>
 #include <stout/protobuf.hpp>
+#include <stout/representation.hpp>
 #include <stout/result.hpp>
 #include <stout/strings.hpp>
 #include <stout/try.hpp>
@@ -103,6 +105,22 @@ using std::vector;
 
 
 namespace mesos {
+
+void json(
+    JSON::StringWriter* writer, const FrameworkInfo::Capability& capability)
+{
+  writer->append(FrameworkInfo::Capability::Type_Name(capability.type()));
+}
+
+
+void json(JSON::ObjectWriter* writer, const Offer& offer)
+{
+  writer->field("id", offer.id().value());
+  writer->field("framework_id", offer.framework_id().value());
+  writer->field("slave_id", offer.slave_id().value());
+  writer->field("resources", Resources(offer.resources()));
+}
+
 namespace internal {
 namespace master {
 
@@ -115,6 +133,177 @@ using process::http::Request;
 using process::Owned;
 
 
+// The summary representation of `T` to support the `/state-summary` endpoint.
+// e.g., `Summary<Slave>`.
+template <typename T>
+struct Summary : Representation<T>
+{
+  using Representation<T>::Representation;
+};
+
+
+// The full representation of `T` to support the `/state` endpoint.
+// e.g., `Full<Slave>`.
+template <typename T>
+struct Full : Representation<T>
+{
+  using Representation<T>::Representation;
+};
+
+
+void json(JSON::ObjectWriter* writer, const Summary<Slave>& summary)
+{
+  const Slave& slave = summary;
+
+  writer->field("id", slave.id.value());
+  writer->field("pid", string(slave.pid));
+  writer->field("hostname", slave.info.hostname());
+  writer->field("registered_time", slave.registeredTime.secs());
+
+  if (slave.reregisteredTime.isSome()) {
+    writer->field("reregistered_time", slave.reregisteredTime.get().secs());
+  }
+
+  const Resources& totalResources = slave.totalResources;
+  writer->field("resources", totalResources);
+  writer->field("used_resources", Resources::sum(slave.usedResources));
+  writer->field("offered_resources", slave.offeredResources);
+  writer->field("reserved_resources", totalResources.reserved());
+  writer->field("unreserved_resources", totalResources.unreserved());
+
+  writer->field("attributes", Attributes(slave.info.attributes()));
+  writer->field("active", slave.active);
+  writer->field("version", slave.version);
+}
+
+
+void json(JSON::ObjectWriter* writer, const Full<Slave>& full)
+{
+  const Slave& slave = full;
+
+  json(writer, Summary<Slave>(slave));
+}
+
+
+void json(JSON::ObjectWriter* writer, const Summary<Framework>& summary)
+{
+  const Framework& framework = summary;
+
+  writer->field("id", framework.id().value());
+  writer->field("name", framework.info.name());
+
+  // Omit pid for http frameworks.
+  if (framework.pid.isSome()) {
+    writer->field("pid", string(framework.pid.get()));
+  }
+
+  // TODO(bmahler): Use these in the webui.
+  writer->field("used_resources", framework.totalUsedResources);
+  writer->field("offered_resources", framework.totalOfferedResources);
+  writer->field("capabilities", framework.info.capabilities());
+  writer->field("hostname", framework.info.hostname());
+  writer->field("webui_url", framework.info.webui_url());
+  writer->field("active", framework.active);
+}
+
+
+void json(JSON::ObjectWriter* writer, const Full<Framework>& full)
+{
+  const Framework& framework = full;
+
+  json(writer, Summary<Framework>(framework));
+
+  // Add additional fields to those generated by 'summarize'.
+  writer->field("user", framework.info.user());
+  writer->field("failover_timeout", framework.info.failover_timeout());
+  writer->field("checkpoint", framework.info.checkpoint());
+  writer->field("role", framework.info.role());
+  writer->field("registered_time", framework.registeredTime.secs());
+  writer->field("unregistered_time", framework.unregisteredTime.secs());
+  writer->field("active", framework.active);
+
+  if (framework.info.has_principal()) {
+    writer->field("principal", framework.info.principal());
+  }
+
+  // TODO(bmahler): Consider deprecating this in favor of the split
+  // used and offered resources added in 'summarize'.
+  writer->field(
+      "resources",
+      framework.totalUsedResources + framework.totalOfferedResources);
+
+  // TODO(benh): Consider making reregisteredTime an Option.
+  if (framework.registeredTime != framework.reregisteredTime) {
+    writer->field("reregistered_time", framework.reregisteredTime.secs());
+  }
+
+  // Model all of the tasks associated with a framework.
+  writer->field("tasks", [&framework](JSON::ArrayWriter* writer) {
+    foreachvalue (const TaskInfo& taskInfo, framework.pendingTasks) {
+      writer->element([&framework, &taskInfo](JSON::ObjectWriter* writer) {
+        writer->field("id", taskInfo.task_id().value());
+        writer->field("name", taskInfo.name());
+        writer->field("framework_id", framework.id().value());
+
+        writer->field("executor_id", taskInfo.executor().executor_id().value());
+
+        writer->field("slave_id", taskInfo.slave_id().value());
+        writer->field("state", TaskState_Name(TASK_STAGING));
+        writer->field("resources", Resources(taskInfo.resources()));
+        writer->field("statuses", std::initializer_list<TaskStatus>{});
+
+        if (taskInfo.has_labels()) {
+          writer->field("labels", taskInfo.labels());
+        }
+
+        if (taskInfo.has_discovery()) {
+          writer->field("discovery", taskInfo.discovery());
+        }
+
+        if (taskInfo.has_container()) {
+          writer->field("container", taskInfo.container());
+        }
+      });
+    }
+
+    foreachvalue (Task* task, framework.tasks) {
+      writer->element(*task);
+    }
+  });
+
+  writer->field("completed_tasks", [&framework](JSON::ArrayWriter* writer) {
+    foreach (const std::shared_ptr<Task>& task, framework.completedTasks) {
+      writer->element(*task);
+    }
+  });
+
+  // Model all of the offers associated with a framework.
+  writer->field("offers", [&framework](JSON::ArrayWriter* writer) {
+    foreach (Offer* offer, framework.offers) {
+      writer->element(*offer);
+    }
+  });
+
+  // Model all of the executors of a framework.
+  writer->field("executors", [&framework](JSON::ArrayWriter* writer) {
+    foreachpair (
+        const SlaveID& slaveId, const auto& executorsMap, framework.executors) {
+      foreachvalue (const ExecutorInfo& executor, executorsMap) {
+        writer->element([&executor, &slaveId](JSON::ObjectWriter* writer) {
+          json(writer, executor);
+          writer->field("slave_id", slaveId.value());
+        });
+      }
+    }
+  });
+
+  // Model all of the labels associated with a framework.
+  if (framework.info.has_labels()) {
+    writer->field("labels", framework.info.labels());
+  }
+}
+
+
 // TODO(bmahler): Kill these in favor of automatic Proto->JSON Conversion (when
 // it becomes available).
 
@@ -1107,139 +1296,115 @@ string Master::Http::STATE_HELP()
 
 Future<Response> Master::Http::state(const Request& request) const
 {
-  JSON::Object object;
-  object.values["version"] = MESOS_VERSION;
+  auto state = [this](JSON::ObjectWriter* writer) {
+    writer->field("version", MESOS_VERSION);
 
-  if (build::GIT_SHA.isSome()) {
-    object.values["git_sha"] = build::GIT_SHA.get();
-  }
-
-  if (build::GIT_BRANCH.isSome()) {
-    object.values["git_branch"] = build::GIT_BRANCH.get();
-  }
-
-  if (build::GIT_TAG.isSome()) {
-    object.values["git_tag"] = build::GIT_TAG.get();
-  }
-
-  object.values["build_date"] = build::DATE;
-  object.values["build_time"] = build::TIME;
-  object.values["build_user"] = build::USER;
-  object.values["start_time"] = master->startTime.secs();
-
-  if (master->electedTime.isSome()) {
-    object.values["elected_time"] = master->electedTime.get().secs();
-  }
-
-  object.values["id"] = master->info().id();
-  object.values["pid"] = string(master->self());
-  object.values["hostname"] = master->info().hostname();
-  object.values["activated_slaves"] = master->_slaves_active();
-  object.values["deactivated_slaves"] = master->_slaves_inactive();
-
-  if (master->flags.cluster.isSome()) {
-    object.values["cluster"] = master->flags.cluster.get();
-  }
+    if (build::GIT_SHA.isSome()) {
+      writer->field("git_sha", build::GIT_SHA.get());
+    }
 
-  if (master->leader.isSome()) {
-    object.values["leader"] = master->leader.get().pid();
-  }
+    if (build::GIT_BRANCH.isSome()) {
+      writer->field("git_branch", build::GIT_BRANCH.get());
+    }
 
-  if (master->flags.log_dir.isSome()) {
-    object.values["log_dir"] = master->flags.log_dir.get();
-  }
+    if (build::GIT_TAG.isSome()) {
+      writer->field("git_tag", build::GIT_TAG.get());
+    }
 
-  if (master->flags.external_log_file.isSome()) {
-    object.values["external_log_file"] = master->flags.external_log_file.get();
-  }
+    writer->field("build_date", build::DATE);
+    writer->field("build_time", build::TIME);
+    writer->field("build_user", build::USER);
+    writer->field("start_time", master->startTime.secs());
 
-  {
-    JSON::Object flags;
-    foreachpair (const string& name, const flags::Flag& flag, master->flags) {
-      Option<string> value = flag.stringify(master->flags);
-      if (value.isSome()) {
-        flags.values[name] = value.get();
-      }
+    if (master->electedTime.isSome()) {
+      writer->field("elected_time", master->electedTime.get().secs());
     }
-    object.values["flags"] = std::move(flags);
-  }
 
-  // Model all of the slaves.
-  {
-    JSON::Array array;
-    array.values.reserve(master->slaves.registered.size()); // MESOS-2353.
+    writer->field("id", master->info().id());
+    writer->field("pid", string(master->self()));
+    writer->field("hostname", master->info().hostname());
+    writer->field("activated_slaves", master->_slaves_active());
+    writer->field("deactivated_slaves", master->_slaves_inactive());
 
-    foreachvalue (Slave* slave, master->slaves.registered) {
-      array.values.push_back(model(*slave));
+    if (master->flags.cluster.isSome()) {
+      writer->field("cluster", master->flags.cluster.get());
     }
 
-    object.values["slaves"] = std::move(array);
-  }
-
-  // Model all of the frameworks.
-  {
-    JSON::Array array;
-    array.values.reserve(master->frameworks.registered.size()); // MESOS-2353.
+    if (master->leader.isSome()) {
+      writer->field("leader", master->leader.get().pid());
+    }
 
-    foreachvalue (Framework* framework, master->frameworks.registered) {
-      array.values.push_back(model(*framework));
+    if (master->flags.log_dir.isSome()) {
+      writer->field("log_dir", master->flags.log_dir.get());
     }
 
-    object.values["frameworks"] = std::move(array);
-  }
+    if (master->flags.external_log_file.isSome()) {
+      writer->field("external_log_file", master->flags.external_log_file.get());
+    }
 
-  // Model all of the completed frameworks.
-  {
-    JSON::Array array;
-    array.values.reserve(master->frameworks.completed.size()); // MESOS-2353.
+    writer->field("flags", [this](JSON::ObjectWriter* writer) {
+      foreachpair (const string& name, const flags::Flag& flag, master->flags) {
+        Option<string> value = flag.stringify(master->flags);
+        if (value.isSome()) {
+          writer->field(name, value.get());
+        }
+      }
+    });
 
-    foreach (const std::shared_ptr<Framework>& framework,
-             master->frameworks.completed) {
-      array.values.push_back(model(*framework));
-    }
+    // Model all of the slaves.
+    writer->field("slaves", [this](JSON::ArrayWriter* writer) {
+      foreachvalue (Slave* slave, master->slaves.registered) {
+        writer->element(Full<Slave>(*slave));
+      }
+    });
 
-    object.values["completed_frameworks"] = std::move(array);
-  }
+    // Model all of the frameworks.
+    writer->field("frameworks", [this](JSON::ArrayWriter* writer) {
+      foreachvalue (Framework* framework, master->frameworks.registered) {
+        writer->element(Full<Framework>(*framework));
+      }
+    });
 
-  // Model all of the orphan tasks.
-  {
-    JSON::Array array;
+    // Model all of the completed frameworks.
+    writer->field("completed_frameworks", [this](JSON::ArrayWriter* writer) {
+      foreach (const std::shared_ptr<Framework>& framework,
+               master->frameworks.completed) {
+        writer->element(Full<Framework>(*framework));
+      }
+    });
 
-    // Find those orphan tasks.
-    foreachvalue (const Slave* slave, master->slaves.registered) {
-      typedef hashmap<TaskID, Task*> TaskMap;
-      foreachvalue (const TaskMap& tasks, slave->tasks) {
-        foreachvalue (const Task* task, tasks) {
-          CHECK_NOTNULL(task);
-          if (!master->frameworks.registered.contains(task->framework_id())) {
-            array.values.push_back(model(*task));
+    // Model all of the orphan tasks.
+    writer->field("orphan_tasks", [this](JSON::ArrayWriter* writer) {
+      // Find those orphan tasks.
+      foreachvalue (const Slave* slave, master->slaves.registered) {
+        typedef hashmap<TaskID, Task*> TaskMap;
+        foreachvalue (const TaskMap& tasks, slave->tasks) {
+          foreachvalue (const Task* task, tasks) {
+            CHECK_NOTNULL(task);
+            if (!master->frameworks.registered.contains(task->framework_id())) {
+              writer->element(*task);
+            }
           }
         }
       }
-    }
-
-    object.values["orphan_tasks"] = std::move(array);
-  }
-
-  // Model all currently unregistered frameworks.
-  // This could happen when the framework has yet to re-register
-  // after master failover.
-  {
-    JSON::Array array;
+    });
 
-    // Find unregistered frameworks.
-    foreachvalue (const Slave* slave, master->slaves.registered) {
-      foreachkey (const FrameworkID& frameworkId, slave->tasks) {
-        if (!master->frameworks.registered.contains(frameworkId)) {
-          array.values.push_back(frameworkId.value());
+    // Model all currently unregistered frameworks.
+    // This could happen when the framework has yet to re-register
+    // after master failover.
+    writer->field("unregistered_frameworks", [this](JSON::ArrayWriter* writer) {
+      // Find unregistered frameworks.
+      foreachvalue (const Slave* slave, master->slaves.registered) {
+        foreachkey (const FrameworkID& frameworkId, slave->tasks) {
+          if (!master->frameworks.registered.contains(frameworkId)) {
+            writer->element(frameworkId.value());
+          }
         }
       }
-    }
-
-    object.values["unregistered_frameworks"] = std::move(array);
-  }
+    });
+  };
 
-  return OK(object, request.url.query.get("jsonp"));
+  return OK(jsonify(state), request.url.query.get("jsonp"));
 }