blob: b0fe5f520dfca156548ba8c436d42fc432223f3d [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 <memory>
#include <sstream>
#include <string>
#include <vector>
#include <mesos/type_utils.hpp>
#include <process/help.hpp>
#include <process/owned.hpp>
#include <process/metrics/metrics.hpp>
#include <stout/foreach.hpp>
#include <stout/json.hpp>
#include <stout/lambda.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/http.hpp"
#include "mesos/mesos.hpp"
#include "mesos/resources.hpp"
#include "slave/slave.hpp"
using process::Clock;
using process::DESCRIPTION;
using process::Future;
using process::HELP;
using process::Owned;
using process::TLDR;
using process::USAGE;
using process::http::InternalServerError;
using process::http::OK;
using process::metrics::internal::MetricsProcess;
using std::map;
using std::string;
using std::vector;
namespace mesos {
namespace internal {
namespace slave {
// Pull in defnitions from common.
using mesos::internal::model;
// Pull in the process definitions.
using process::http::Response;
using process::http::Request;
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"] = task.executor().executor_id().value();
}
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["container"] = executor.containerId.value();
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 std::shared_ptr<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();
object.values["failover_timeout"] = framework.info.failover_timeout();
object.values["checkpoint"] = framework.info.checkpoint();
object.values["role"] = framework.info.role();
object.values["hostname"] = framework.info.hostname();
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;
}
void Slave::Http::log(const Request& request)
{
Option<string> userAgent = request.headers.get("User-Agent");
Option<string> forwardedFor = request.headers.get("X-Forwarded-For");
LOG(INFO) << "HTTP " << request.method << " for " << request.path
<< " from " << request.client
<< (userAgent.isSome()
? " with User-Agent='" + userAgent.get() + "'"
: "")
<< (forwardedFor.isSome()
? " with X-Forwarded-For='" + forwardedFor.get() + "'"
: "");
}
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) const
{
return OK();
}
const string Slave::Http::STATE_HELP = HELP(
TLDR(
"Information about state of the Slave."),
USAGE(
"/state.json"),
DESCRIPTION(
"This endpoint shows information about the frameworks, executors",
"and the slave's master as a JSON object."));
Future<Response> Slave::Http::state(const Request& request) const
{
JSON::Object object;
object.values["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"] = 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->info.resources());
object.values["attributes"] = model(slave->info.attributes());
if (slave->master.isSome()) {
Try<string> hostname = net::getHostname(slave->master.get().address.ip);
if (hostname.isSome()) {
object.values["master_hostname"] = hostname.get();
}
}
if (slave->flags.log_dir.isSome()) {
object.values["log_dir"] = slave->flags.log_dir.get();
}
if (slave->flags.external_log_file.isSome()) {
object.values["external_log_file"] = slave->flags.external_log_file.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;
JSON::Object flags;
foreachpair (const string& name, const flags::Flag& flag, slave->flags) {
Option<string> value = flag.stringify(slave->flags);
if (value.isSome()) {
flags.values[name] = value.get();
}
}
object.values["flags"] = flags;
return OK(object, request.query.get("jsonp"));
}
} // namespace slave {
} // namespace internal {
} // namespace mesos {