blob: f164f25da0b8cfb9b7ed40da28b4dd59f002ceb8 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include <map>
#include <sstream>
#include <string>
#include <vector>
#include <mesos/mesos.hpp>
#include <mesos/resources.hpp>
#include <process/help.hpp>
#include <process/owned.hpp>
#include <stout/foreach.hpp>
#include <stout/json.hpp>
#include <stout/net.hpp>
#include <stout/numify.hpp>
#include <stout/stringify.hpp>
#include <stout/strings.hpp>
#include "common/attributes.hpp"
#include "common/build.hpp"
#include "common/type_utils.hpp"
#include "slave/slave.hpp"
namespace mesos {
namespace internal {
namespace slave {
using process::Future;
using process::HELP;
using process::Owned;
using process::TLDR;
using process::USAGE;
using process::http::OK;
using process::http::Response;
using process::http::Request;
using std::map;
using std::string;
using std::vector;
// TODO(bmahler): Kill these in favor of automatic Proto->JSON Conversion (when
// in becomes available).
// Returns a JSON object modeled on a Resources.
JSON::Object model(const Resources& resources)
{
JSON::Object object;
foreach (const Resource& resource, resources) {
switch (resource.type()) {
case Value::SCALAR:
object.values[resource.name()] = resource.scalar().value();
break;
case Value::RANGES:
object.values[resource.name()] = stringify(resource.ranges());
break;
case Value::SET:
object.values[resource.name()] = stringify(resource.set());
break;
default:
LOG(FATAL) << "Unexpected Value type: " << resource.type();
break;
}
}
return object;
}
JSON::Object model(const Attributes& attributes)
{
JSON::Object object;
foreach (const Attribute& attribute, attributes) {
switch (attribute.type()) {
case Value::SCALAR:
object.values[attribute.name()] = attribute.scalar().value();
break;
case Value::RANGES:
object.values[attribute.name()] = stringify(attribute.ranges());
break;
case Value::SET:
object.values[attribute.name()] = stringify(attribute.set());
break;
case Value::TEXT:
object.values[attribute.name()] = attribute.text().value();
break;
default:
LOG(FATAL) << "Unexpected Value type: " << attribute.type();
break;
}
}
return object;
}
JSON::Object model(const CommandInfo& command)
{
JSON::Object object;
object.values["value"] = command.value();
if (command.has_environment()) {
JSON::Object environment;
JSON::Array variables;
foreach(const Environment_Variable& variable,
command.environment().variables()) {
JSON::Object variableObject;
variableObject.values["name"] = variable.name();
variableObject.values["value"] = variable.value();
variables.values.push_back(variableObject);
}
environment.values["variables"] = variables;
object.values["environment"] = environment;
}
JSON::Array uris;
foreach(const CommandInfo_URI& uri, command.uris()) {
JSON::Object uriObject;
uriObject.values["value"] = uri.value();
uriObject.values["executable"] = uri.executable();
uris.values.push_back(uriObject);
}
object.values["uris"] = uris;
return object;
}
JSON::Object model(const ExecutorInfo& executorInfo)
{
JSON::Object object;
object.values["executor_id"] = executorInfo.executor_id().value();
object.values["name"] = executorInfo.name();
object.values["data"] = executorInfo.data();
object.values["framework_id"] = executorInfo.framework_id().value();
object.values["command"] = model(executorInfo.command());
object.values["resources"] = model(executorInfo.resources());
return object;
}
JSON::Object model(const Task& task)
{
JSON::Object object;
object.values["id"] = task.task_id().value();
object.values["name"] = task.name();
object.values["executor_id"] = task.executor_id().value();
object.values["framework_id"] = task.framework_id().value();
object.values["slave_id"] = task.slave_id().value();
object.values["state"] = TaskState_Name(task.state());
object.values["resources"] = model(task.resources());
return object;
}
JSON::Object model(const TaskInfo& task)
{
JSON::Object object;
object.values["id"] = task.task_id().value();
object.values["name"] = task.name();
object.values["slave_id"] = task.slave_id().value();
object.values["resources"] = model(task.resources());
object.values["data"] = task.data();
if (task.has_command()) {
object.values["command"] = model(task.command());
}
if (task.has_executor()) {
object.values["executor_id"] = model(task.executor());
}
return object;
}
JSON::Object model(const Executor& executor)
{
JSON::Object object;
object.values["id"] = executor.id.value();
object.values["name"] = executor.info.name();
object.values["source"] = executor.info.source();
object.values["uuid"] = executor.uuid.toString();
object.values["directory"] = executor.directory;
object.values["resources"] = model(executor.resources);
JSON::Array tasks;
foreach (Task* task, executor.launchedTasks.values()) {
tasks.values.push_back(model(*task));
}
object.values["tasks"] = tasks;
JSON::Array queued;
foreach (const TaskInfo& task, executor.queuedTasks.values()) {
queued.values.push_back(model(task));
}
object.values["queued_tasks"] = queued;
JSON::Array completed;
foreach (const Task& task, executor.completedTasks) {
completed.values.push_back(model(task));
}
// NOTE: We add 'terminatedTasks' to 'completed_tasks' for
// simplicity.
// TODO(vinod): Use foreachvalue instead once LinkedHashmap
// supports it.
foreach (Task* task, executor.terminatedTasks.values()) {
completed.values.push_back(model(*task));
}
object.values["completed_tasks"] = completed;
return object;
}
// Returns a JSON object modeled after a Framework.
JSON::Object model(const Framework& framework)
{
JSON::Object object;
object.values["id"] = framework.id.value();
object.values["name"] = framework.info.name();
object.values["user"] = framework.info.user();
JSON::Array executors;
foreachvalue (Executor* executor, framework.executors) {
executors.values.push_back(model(*executor));
}
object.values["executors"] = executors;
JSON::Array completedExecutors;
foreach (const Owned<Executor>& executor, framework.completedExecutors) {
completedExecutors.values.push_back(model(*executor));
}
object.values["completed_executors"] = completedExecutors;
return object;
}
const string Slave::Http::HEALTH_HELP = HELP(
TLDR(
"Health check of the Slave."),
USAGE(
"/health"),
DESCRIPTION(
"Returns 200 OK iff the Slave is healthy.",
"Delayed responses are also indicative of poor health."));
Future<Response> Slave::Http::health(const Request& request)
{
return OK();
}
Future<Response> Slave::Http::stats(const Request& request)
{
LOG(INFO) << "HTTP request for '" << request.path << "'";
JSON::Object object;
object.values["uptime"] = (Clock::now() - slave.startTime).secs();
object.values["total_frameworks"] = slave.frameworks.size();
object.values["staged_tasks"] = slave.stats.tasks[TASK_STAGING];
object.values["started_tasks"] = slave.stats.tasks[TASK_STARTING];
object.values["finished_tasks"] = slave.stats.tasks[TASK_FINISHED];
object.values["killed_tasks"] = slave.stats.tasks[TASK_KILLED];
object.values["failed_tasks"] = slave.stats.tasks[TASK_FAILED];
object.values["lost_tasks"] = slave.stats.tasks[TASK_LOST];
object.values["valid_status_updates"] = slave.stats.validStatusUpdates;
object.values["invalid_status_updates"] = slave.stats.invalidStatusUpdates;
object.values["registered"] = slave.master.isSome() ? "1" : "0";
object.values["recovery_errors"] = slave.recoveryErrors;
return OK(object, request.query.get("jsonp"));
}
Future<Response> Slave::Http::state(const Request& request)
{
LOG(INFO) << "HTTP request for '" << request.path << "'";
JSON::Object object;
object.values["version"] = MESOS_VERSION;
object.values["build_date"] = build::DATE;
object.values["build_time"] = build::TIME;
object.values["build_user"] = build::USER;
object.values["start_time"] = slave.startTime.secs();
object.values["id"] = slave.info.id().value();
object.values["pid"] = string(slave.self());
object.values["hostname"] = slave.info.hostname();
object.values["resources"] = model(slave.resources);
object.values["attributes"] = model(slave.attributes);
object.values["staged_tasks"] = slave.stats.tasks[TASK_STAGING];
object.values["started_tasks"] = slave.stats.tasks[TASK_STARTING];
object.values["finished_tasks"] = slave.stats.tasks[TASK_FINISHED];
object.values["killed_tasks"] = slave.stats.tasks[TASK_KILLED];
object.values["failed_tasks"] = slave.stats.tasks[TASK_FAILED];
object.values["lost_tasks"] = slave.stats.tasks[TASK_LOST];
if (slave.master.isSome()) {
Try<string> masterHostname = net::getHostname(slave.master.get().ip);
if (masterHostname.isSome()) {
object.values["master_hostname"] = masterHostname.get();
}
}
if (slave.flags.log_dir.isSome()) {
object.values["log_dir"] = slave.flags.log_dir.get();
}
JSON::Array frameworks;
foreachvalue (Framework* framework, slave.frameworks) {
frameworks.values.push_back(model(*framework));
}
object.values["frameworks"] = frameworks;
JSON::Array completedFrameworks;
foreach (const Owned<Framework>& framework, slave.completedFrameworks) {
completedFrameworks.values.push_back(model(*framework));
}
object.values["completed_frameworks"] = completedFrameworks;
return OK(object, request.query.get("jsonp"));
}
} // namespace slave {
} // namespace internal {
} // namespace mesos {