You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by vi...@apache.org on 2014/02/15 02:17:30 UTC

[1/4] git commit: Added valgrind runnables.

Repository: mesos
Updated Branches:
  refs/heads/master 8a4ec2e09 -> 165dbe101


Added valgrind runnables.

Adds valgrind runnables for executing memory / leak checks on mesos.
The runnables accept an additional parameter (-t) for selecting a
valgrind tool. The default (no option given) is memcheck. All other
parameters are passed on to the given target (master, slave, tests,
local).

Example: build/bin/valgrind-mesos-tests.sh -t helgrind

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


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

Branch: refs/heads/master
Commit: 36c7a4d7e366c9b421bd8a092cce9f028c091cd3
Parents: 8a4ec2e
Author: TILL TOENSHOFF <to...@me.com>
Authored: Fri Feb 14 16:59:00 2014 -0800
Committer: Vinod Kone <vi...@twitter.com>
Committed: Fri Feb 14 16:59:00 2014 -0800

----------------------------------------------------------------------
 bin/valgrind-mesos-local.sh.in  | 53 ++++++++++++++++++++++++++++++++++++
 bin/valgrind-mesos-master.sh.in | 53 ++++++++++++++++++++++++++++++++++++
 bin/valgrind-mesos-slave.sh.in  | 53 ++++++++++++++++++++++++++++++++++++
 bin/valgrind-mesos-tests.sh.in  | 53 ++++++++++++++++++++++++++++++++++++
 configure.ac                    | 12 ++++++--
 5 files changed, 222 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/36c7a4d7/bin/valgrind-mesos-local.sh.in
----------------------------------------------------------------------
diff --git a/bin/valgrind-mesos-local.sh.in b/bin/valgrind-mesos-local.sh.in
new file mode 100644
index 0000000..b7a04d1
--- /dev/null
+++ b/bin/valgrind-mesos-local.sh.in
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+
+# 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.
+
+# This is a wrapper for running valgrind on mesos-local before it is
+# installed that first sets up some flags via environment variables.
+
+# Use colors for errors.
+. @abs_top_srcdir@/support/colors.sh
+
+# Default valgrind tool is "memcheck".
+VALGRINDTOOL=memcheck
+
+# For supplying alternative tools (e.g. "helgrind"), use the -t option.
+while getopts ":t:" opt
+do
+  case "${opt}" in
+    t) VALGRINDTOOL=${OPTARG}; shift 2
+    ;;
+  esac
+done
+
+LIBTOOL=@abs_top_builddir@/libtool
+
+test ! -e ${LIBTOOL} && \
+  echo "${RED}Failed to find ${LIBTOOL}, have you run configure?${NORMAL}" \
+  && exit 1
+
+# Confirm libtool has "valgrind" support.
+${LIBTOOL} --mode=execute valgrind --version >/dev/null 2>&1
+
+test $? != 0 && \
+  echo "${RED}Generated libtool doesn't appear to support valgrind${NORMAL}" \
+  && exit 1
+
+. @abs_top_builddir@/bin/mesos-local-flags.sh
+
+exec ${LIBTOOL} --mode=execute valgrind --tool=${VALGRINDTOOL} \
+  @abs_top_builddir@/src/mesos-local "${@}"

http://git-wip-us.apache.org/repos/asf/mesos/blob/36c7a4d7/bin/valgrind-mesos-master.sh.in
----------------------------------------------------------------------
diff --git a/bin/valgrind-mesos-master.sh.in b/bin/valgrind-mesos-master.sh.in
new file mode 100644
index 0000000..21314af
--- /dev/null
+++ b/bin/valgrind-mesos-master.sh.in
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+
+# 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.
+
+# This is a wrapper for running valgrind on mesos-master before it is
+# installed that first sets up some flags via environment variables.
+
+# Use colors for errors.
+. @abs_top_srcdir@/support/colors.sh
+
+# Default valgrind tool is "memcheck".
+VALGRINDTOOL=memcheck
+
+# For supplying alternative tools (e.g. "helgrind"), use the -t option.
+while getopts ":t:" opt
+do
+  case "${opt}" in
+    t) VALGRINDTOOL=${OPTARG}; shift 2
+    ;;
+  esac
+done
+
+LIBTOOL=@abs_top_builddir@/libtool
+
+test ! -e ${LIBTOOL} && \
+  echo "${RED}Failed to find ${LIBTOOL}, have you run configure?${NORMAL}" \
+  && exit 1
+
+# Confirm libtool has "valgrind" support.
+${LIBTOOL} --mode=execute valgrind --version >/dev/null 2>&1
+
+test $? != 0 && \
+  echo "${RED}Generated libtool doesn't appear to support valgrind${NORMAL}" \
+  && exit 1
+
+. @abs_top_builddir@/bin/mesos-master-flags.sh
+
+exec ${LIBTOOL} --mode=execute valgrind --tool=${VALGRINDTOOL} \
+  @abs_top_builddir@/src/mesos-master "${@}"

http://git-wip-us.apache.org/repos/asf/mesos/blob/36c7a4d7/bin/valgrind-mesos-slave.sh.in
----------------------------------------------------------------------
diff --git a/bin/valgrind-mesos-slave.sh.in b/bin/valgrind-mesos-slave.sh.in
new file mode 100644
index 0000000..900c588
--- /dev/null
+++ b/bin/valgrind-mesos-slave.sh.in
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+
+# 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.
+
+# This is a wrapper for running valgrind on mesos-slave before it is
+# installed that first sets up some flags via environment variables.
+
+# Use colors for errors.
+. @abs_top_srcdir@/support/colors.sh
+
+# Default valgrind tool is "memcheck".
+VALGRINDTOOL=memcheck
+
+# For supplying alternative tools (e.g. "helgrind"), use the -t option.
+while getopts ":t:" opt
+do
+  case "${opt}" in
+    t) VALGRINDTOOL=${OPTARG}; shift 2
+    ;;
+  esac
+done
+
+LIBTOOL=@abs_top_builddir@/libtool
+
+test ! -e ${LIBTOOL} && \
+  echo "${RED}Failed to find ${LIBTOOL}, have you run configure?${NORMAL}" \
+  && exit 1
+
+# Confirm libtool has "valgrind" support.
+${LIBTOOL} --mode=execute valgrind --version >/dev/null 2>&1
+
+test $? != 0 && \
+  echo "${RED}Generated libtool doesn't appear to support valgrind${NORMAL}" \
+  && exit 1
+
+. @abs_top_builddir@/bin/mesos-slave-flags.sh
+
+exec ${LIBTOOL} --mode=execute valgrind --tool=${VALGRINDTOOL} \
+  @abs_top_builddir@/src/mesos-slave "${@}"

http://git-wip-us.apache.org/repos/asf/mesos/blob/36c7a4d7/bin/valgrind-mesos-tests.sh.in
----------------------------------------------------------------------
diff --git a/bin/valgrind-mesos-tests.sh.in b/bin/valgrind-mesos-tests.sh.in
new file mode 100644
index 0000000..9adedef
--- /dev/null
+++ b/bin/valgrind-mesos-tests.sh.in
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+
+# 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.
+
+# This is a wrapper for running valgrind on mesos-tests before it is
+# installed that first sets up some flags via environment variables.
+
+# Use colors for errors.
+. @abs_top_srcdir@/support/colors.sh
+
+# Default valgrind tool is "memcheck".
+VALGRINDTOOL=memcheck
+
+# For supplying alternative tools (e.g. "helgrind"), use the -t option.
+while getopts ":t:" opt
+do
+  case "${opt}" in
+    t) VALGRINDTOOL=${OPTARG}; shift 2
+    ;;
+  esac
+done
+
+LIBTOOL=@abs_top_builddir@/libtool
+
+test ! -e ${LIBTOOL} && \
+  echo "${RED}Failed to find ${LIBTOOL}, have you run configure?${NORMAL}" \
+  && exit 1
+
+# Confirm libtool has "valgrind" support.
+${LIBTOOL} --mode=execute valgrind --version >/dev/null 2>&1
+
+test $? != 0 && \
+  echo "${RED}Generated libtool doesn't appear to support valgrind${NORMAL}" \
+  && exit 1
+
+. @abs_top_builddir@/bin/mesos-tests-flags.sh
+
+exec ${LIBTOOL} --mode=execute valgrind --tool=${VALGRINDTOOL} \
+  @abs_top_builddir@/src/mesos-tests "${@}"

http://git-wip-us.apache.org/repos/asf/mesos/blob/36c7a4d7/configure.ac
----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index c21e681..a8bc8a8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -95,10 +95,18 @@ AC_CONFIG_FILES([bin/gdb-mesos-master.sh], [chmod +x bin/gdb-mesos-master.sh])
 AC_CONFIG_FILES([bin/gdb-mesos-slave.sh], [chmod +x bin/gdb-mesos-slave.sh])
 AC_CONFIG_FILES([bin/gdb-mesos-tests.sh], [chmod +x bin/gdb-mesos-tests.sh])
 AC_CONFIG_FILES([bin/lldb-mesos-local.sh], [chmod +x bin/lldb-mesos-local.sh])
-AC_CONFIG_FILES([bin/lldb-mesos-master.sh], [chmod +x bin/lldb-mesos-master.sh])
+AC_CONFIG_FILES([bin/lldb-mesos-master.sh],
+		[chmod +x bin/lldb-mesos-master.sh])
 AC_CONFIG_FILES([bin/lldb-mesos-slave.sh], [chmod +x bin/lldb-mesos-slave.sh])
 AC_CONFIG_FILES([bin/lldb-mesos-tests.sh], [chmod +x bin/lldb-mesos-tests.sh])
-
+AC_CONFIG_FILES([bin/valgrind-mesos-local.sh],
+		[chmod +x bin/valgrind-mesos-local.sh])
+AC_CONFIG_FILES([bin/valgrind-mesos-slave.sh],
+		[chmod +x bin/valgrind-mesos-slave.sh])
+AC_CONFIG_FILES([bin/valgrind-mesos-master.sh],
+		[chmod +x bin/valgrind-mesos-master.sh])
+AC_CONFIG_FILES([bin/valgrind-mesos-tests.sh],
+		[chmod +x bin/valgrind-mesos-tests.sh])
 AC_CONFIG_FILES([src/deploy/mesos-daemon.sh])
 AC_CONFIG_FILES([src/deploy/mesos-start-cluster.sh])
 AC_CONFIG_FILES([src/deploy/mesos-start-masters.sh])


[3/4] git commit: Added a log tool that does performance tests on the log.

Posted by vi...@apache.org.
Added a log tool that does performance tests on the log.

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


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

Branch: refs/heads/master
Commit: 5c2f4f7040076de51a5ca7182ac777093bb8aa70
Parents: e15bbe0
Author: Jie Yu <yu...@gmail.com>
Authored: Fri Feb 14 17:05:40 2014 -0800
Committer: Vinod Kone <vi...@twitter.com>
Committed: Fri Feb 14 17:05:40 2014 -0800

----------------------------------------------------------------------
 src/Makefile.am            |   2 +
 src/log/main.cpp           |   2 +
 src/log/tool/benchmark.cpp | 266 ++++++++++++++++++++++++++++++++++++++++
 src/log/tool/benchmark.hpp |  68 ++++++++++
 4 files changed, 338 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/5c2f4f70/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index dc9042a..cfd7416 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -311,6 +311,7 @@ liblog_la_SOURCES =							\
   log/log.cpp								\
   log/recover.cpp							\
   log/replica.cpp							\
+  log/tool/benchmark.cpp						\
   log/tool/initialize.cpp						\
   log/tool/read.cpp							\
   log/tool/replica.cpp
@@ -325,6 +326,7 @@ liblog_la_SOURCES +=							\
   log/replica.hpp							\
   log/storage.hpp							\
   log/tool.hpp								\
+  log/tool/benchmark.hpp						\
   log/tool/initialize.hpp						\
   log/tool/read.hpp							\
   log/tool/replica.hpp							\

http://git-wip-us.apache.org/repos/asf/mesos/blob/5c2f4f70/src/log/main.cpp
----------------------------------------------------------------------
diff --git a/src/log/main.cpp b/src/log/main.cpp
index 2b30fd0..348e887 100644
--- a/src/log/main.cpp
+++ b/src/log/main.cpp
@@ -27,6 +27,7 @@
 #include <stout/hashmap.hpp>
 
 #include "log/tool.hpp"
+#include "log/tool/benchmark.hpp"
 #include "log/tool/initialize.hpp"
 #include "log/tool/read.hpp"
 #include "log/tool/replica.hpp"
@@ -68,6 +69,7 @@ static void usage(const char* argv0)
 int main(int argc, char** argv)
 {
   // Register log tools.
+  add(Owned<tool::Tool>(new tool::Benchmark()));
   add(Owned<tool::Tool>(new tool::Initialize()));
   add(Owned<tool::Tool>(new tool::Read()));
   add(Owned<tool::Tool>(new tool::Replica()));

http://git-wip-us.apache.org/repos/asf/mesos/blob/5c2f4f70/src/log/tool/benchmark.cpp
----------------------------------------------------------------------
diff --git a/src/log/tool/benchmark.cpp b/src/log/tool/benchmark.cpp
new file mode 100644
index 0000000..7d5a032
--- /dev/null
+++ b/src/log/tool/benchmark.cpp
@@ -0,0 +1,266 @@
+/**
+ * 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 <stdio.h>
+#include <stdlib.h>
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+
+#include <process/clock.hpp>
+#include <process/process.hpp>
+#include <process/time.hpp>
+
+#include <stout/bytes.hpp>
+#include <stout/error.hpp>
+#include <stout/foreach.hpp>
+#include <stout/stopwatch.hpp>
+#include <stout/strings.hpp>
+#include <stout/os/read.hpp>
+
+#include "log/log.hpp"
+#include "log/tool/initialize.hpp"
+#include "log/tool/benchmark.hpp"
+
+#include "logging/logging.hpp"
+
+using namespace process;
+
+using std::cout;
+using std::endl;
+using std::ifstream;
+using std::ofstream;
+using std::ostringstream;
+using std::string;
+using std::vector;
+
+namespace mesos {
+namespace internal {
+namespace log {
+namespace tool {
+
+Benchmark::Flags::Flags()
+{
+  add(&Flags::quorum,
+      "quorum",
+      "Quorum size");
+
+  add(&Flags::path,
+      "path",
+      "Path to the log");
+
+  add(&Flags::servers,
+      "servers",
+      "ZooKeeper servers");
+
+  add(&Flags::znode,
+      "znode",
+      "ZooKeeper znode");
+
+  add(&Flags::input,
+      "input",
+      "Path to the input trace file. Each line in the trace file\n"
+      "specifies the size of the append (e.g. 100B, 2MB, etc.)");
+
+  add(&Flags::output,
+      "output",
+      "Path to the output file");
+
+  add(&Flags::type,
+      "type",
+      "Type of data to be written (zero, one, random)\n"
+      "  zero:   all bits are 0\n"
+      "  one:    all bits are 1\n"
+      "  random: all bits are randomly chosen\n",
+      "random");
+
+  add(&Flags::initialize,
+      "initialize",
+      "Whether to initialize the log",
+      true);
+
+  add(&Flags::help,
+      "help",
+      "Prints the help message",
+      false);
+}
+
+
+string Benchmark::usage(const string& argv0) const
+{
+  ostringstream out;
+
+  out << "Usage: " << argv0 << " " << name() << " [OPTIONS]" << endl
+      << endl
+      << "This command is used to do performance test on the" << endl
+      << "replicated log. It takes a trace file of write sizes" << endl
+      << "and replay that trace to measure the latency of each" << endl
+      << "write. The data to be written for each write can be" << endl
+      << "specified using the '--type' flag." << endl
+      << endl
+      << "Supported OPTIONS:" << endl
+      << flags.usage();
+
+  return out.str();
+}
+
+
+Try<Nothing> Benchmark::execute(int argc, char** argv)
+{
+  // Configure the tool by parsing command line arguments.
+  if (argc > 0 && argv != NULL) {
+    Try<Nothing> load = flags.load(None(), argc, argv);
+    if (load.isError()) {
+      return Error(load.error() + "\n\n" + usage(argv[0]));
+    }
+
+    if (flags.help) {
+      return Error(usage(argv[0]));
+    }
+
+    process::initialize();
+    logging::initialize(argv[0], flags);
+  }
+
+  if (flags.quorum.isNone()) {
+    return Error("Missing flag '--quorum'");
+  }
+
+  if (flags.path.isNone()) {
+    return Error("Missing flag '--path'");
+  }
+
+  if (flags.servers.isNone()) {
+    return Error("Missing flag '--servers'");
+  }
+
+  if (flags.znode.isNone()) {
+    return Error("Missing flag '--znode'");
+  }
+
+  if (flags.input.isNone()) {
+    return Error("Missing flag '--input'");
+  }
+
+  if (flags.output.isNone()) {
+    return Error("Missing flag '--output'");
+  }
+
+  // Initialize the log.
+  if (flags.initialize) {
+    Initialize initialize;
+    initialize.flags.path = flags.path;
+
+    Try<Nothing> execution = initialize.execute();
+    if (execution.isError()) {
+      return Error(execution.error());
+    }
+  }
+
+  // Create the log.
+  Log log(
+      flags.quorum.get(),
+      flags.path.get(),
+      flags.servers.get(),
+      Seconds(10),
+      flags.znode.get());
+
+  // Create the log writer.
+  Log::Writer writer(&log, Seconds(15), 20);
+
+  // Statistics to output.
+  vector<Bytes> sizes;
+  vector<Duration> durations;
+  vector<Time> timestamps;
+
+  // Read sizes from the input trace file.
+  ifstream input(flags.input.get().c_str());
+  if (!input.is_open()) {
+    return Error("Failed to open the trace file " + flags.input.get());
+  }
+
+  string line;
+  while (getline(input, line)) {
+    Try<Bytes> size = Bytes::parse(strings::trim(line));
+    if (size.isError()) {
+      input.close();
+      return Error("Failed to parse the trace file: " + size.error());
+    }
+
+    sizes.push_back(size.get());
+  }
+
+  input.close();
+
+  // Generate the data to be written.
+  vector<string> data;
+  for (size_t i = 0; i < sizes.size(); i++) {
+    if (flags.type == "one") {
+      data.push_back(string(sizes[i].bytes(), 255));
+    } else if (flags.type == "random") {
+      data.push_back(string(sizes[i].bytes(), ::random() % 256));
+    } else {
+      data.push_back(string(sizes[i].bytes(), 0));
+    }
+  }
+
+  Stopwatch stopwatch;
+  stopwatch.start();
+
+  for (size_t i = 0; i < sizes.size(); i++) {
+    Stopwatch stopwatch;
+    stopwatch.start();
+
+    Result<Log::Position> position =
+      writer.append(data[i], Timeout::in(Seconds(10)));
+
+    if (position.isError()) {
+      return Error("Failed to append: " + position.error());
+    } else if (position.isNone()) {
+      return Error("Timed out while appending");
+    }
+
+    durations.push_back(stopwatch.elapsed());
+    timestamps.push_back(Clock::now());
+  }
+
+  cout << "Total number of appends: " << sizes.size() << endl;
+  cout << "Total time used: " << stopwatch.elapsed() << endl;
+
+  // Ouput statistics.
+  ofstream output(flags.output.get().c_str());
+  if (!output.is_open()) {
+    return Error("Failed to open the output file " + flags.output.get());
+  }
+
+  for (size_t i = 0; i < sizes.size(); i++) {
+    output << timestamps[i]
+           << " Appended " << sizes[i].bytes() << " bytes"
+           << " in " << durations[i].ms() << " ms" << endl;
+  }
+
+  output.close();
+
+  return Nothing();
+}
+
+} // namespace tool {
+} // namespace log {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/5c2f4f70/src/log/tool/benchmark.hpp
----------------------------------------------------------------------
diff --git a/src/log/tool/benchmark.hpp b/src/log/tool/benchmark.hpp
new file mode 100644
index 0000000..e0109e2
--- /dev/null
+++ b/src/log/tool/benchmark.hpp
@@ -0,0 +1,68 @@
+/**
+ * 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.
+ */
+
+#ifndef __LOG_TOOL_BENCHMARK_HPP__
+#define __LOG_TOOL_BENCHMARK_HPP__
+
+#include <stout/flags.hpp>
+#include <stout/option.hpp>
+
+#include "log/tool.hpp"
+
+#include "logging/flags.hpp"
+
+namespace mesos {
+namespace internal {
+namespace log {
+namespace tool {
+
+class Benchmark : public Tool
+{
+public:
+  class Flags : public logging::Flags
+  {
+  public:
+    Flags();
+
+    Option<size_t> quorum;
+    Option<std::string> path;
+    Option<std::string> servers;
+    Option<std::string> znode;
+    Option<std::string> input;
+    Option<std::string> output;
+    std::string type;
+    bool initialize;
+    bool help;
+  };
+
+  virtual std::string name() const { return "benchmark"; }
+  virtual Try<Nothing> execute(int argc = 0, char** argv = NULL);
+
+  // Users can change the default configuration by setting this flags.
+  Flags flags;
+
+private:
+  std::string usage(const std::string& argv0) const;
+};
+
+} // namespace tool {
+} // namespace log {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __LOG_TOOL_BENCHMARK_HPP__


[4/4] git commit: Added observe endpoint to master.

Posted by vi...@apache.org.
Added observe endpoint to master.

This changes adds a new HTTP endpoint of observe to master.
This allows clients to report health via an HTTP POST.
Values are:
   MONITOR = Monitor for which health is being reported.
   HOSTS = Comma seperated list of hosts.
   LEVEL = OK for healthy, anything else for unhealthy.

This also contains a small fix to alphabetize the existing
endpoints / help strings.

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


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

Branch: refs/heads/master
Commit: 165dbe101812f50780a802c8eea0dd5579678d96
Parents: 5c2f4f7
Author: Charlie Carson <ch...@gmail.com>
Authored: Fri Feb 14 17:07:35 2014 -0800
Committer: Vinod Kone <vi...@twitter.com>
Committed: Fri Feb 14 17:07:35 2014 -0800

----------------------------------------------------------------------
 src/Makefile.am            |   1 +
 src/master/http.cpp        |  99 ++++++++++++++++++++++
 src/master/master.cpp      |   9 +-
 src/master/master.hpp      |  13 ++-
 src/tests/repair_tests.cpp | 176 ++++++++++++++++++++++++++++++++++++++++
 5 files changed, 291 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/165dbe10/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index cfd7416..768c66a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -870,6 +870,7 @@ mesos_tests_SOURCES =				\
   tests/paths_tests.cpp				\
   tests/protobuf_io_tests.cpp			\
   tests/registrar_tests.cpp			\
+  tests/repair_tests.cpp			\
   tests/resource_offers_tests.cpp		\
   tests/resources_tests.cpp			\
   tests/sasl_tests.cpp				\

http://git-wip-us.apache.org/repos/asf/mesos/blob/165dbe10/src/master/http.cpp
----------------------------------------------------------------------
diff --git a/src/master/http.cpp b/src/master/http.cpp
index 966eed6..6aeb257 100644
--- a/src/master/http.cpp
+++ b/src/master/http.cpp
@@ -22,6 +22,8 @@
 #include <string>
 #include <vector>
 
+#include <boost/array.hpp>
+
 #include <mesos/mesos.hpp>
 #include <mesos/resources.hpp>
 
@@ -278,6 +280,103 @@ Future<Response> Master::Http::health(const Request& request)
   return OK();
 }
 
+const static string HOSTS_KEY = "hosts";
+const static string LEVEL_KEY = "level";
+const static string MONITOR_KEY = "monitor";
+
+const string Master::Http::OBSERVE_HELP = HELP(
+    TLDR(
+        "Observe a monitor health state for host(s)."),
+    USAGE(
+        "/master/observe"),
+    DESCRIPTION(
+        "This endpoint receives information indicating host(s) ",
+        "health."
+        "",
+        "The following fields should be supplied in a POST:",
+        "1. " + MONITOR_KEY + " - name of the monitor that is being reported",
+        "2. " + HOSTS_KEY + " - comma seperated list of hosts",
+        "3. " + LEVEL_KEY + " - OK for healthy, anything else for unhealthy"));
+
+
+Try<string> getFormValue(
+    const string& key,
+    const hashmap<string, string>& values)
+{
+  Option<string> value = values.get(key);
+
+  if (value.isNone()) {
+    return Error("Missing value for '" + key + "'.");
+  }
+
+  // HTTP decode the value.
+  Try<string> decodedValue = http::decode(value.get());
+  if (decodedValue.isError()) {
+    return decodedValue;
+  }
+
+  // Treat empty string as an error.
+  if (decodedValue.isSome() && decodedValue.get().empty()) {
+    return Error("Empty string for '" + key + "'.");
+  }
+
+  return decodedValue.get();
+}
+
+
+Future<Response> Master::Http::observe(const Request& request)
+{
+  LOG(INFO) << "HTTP request for '" << request.path << "'";
+
+  hashmap<string, string> values =
+    process::http::query::parse(request.body);
+
+  // Build up a JSON object of the values we recieved and send them back
+  // down the wire as JSON for validation / confirmation.
+  JSON::Object response;
+
+  // TODO(ccarson):  As soon as RepairCoordinator is introduced it will
+  // consume these values. We should revisit if we still want to send the
+  // JSON down the wire at that point.
+
+  // Add 'monitor'.
+  Try<string> monitor = getFormValue(MONITOR_KEY, values);
+  if (monitor.isError()) {
+    return BadRequest(monitor.error());
+  }
+  response.values[MONITOR_KEY] = monitor.get();
+
+  // Add 'hosts'.
+  Try<string> hostsString = getFormValue(HOSTS_KEY, values);
+  if (hostsString.isError()) {
+    return BadRequest(hostsString.error());
+  }
+
+  vector<string> hosts = strings::split(hostsString.get(), ",");
+  JSON::Array hostArray;
+  hostArray.values.assign(hosts.begin(), hosts.end());
+
+  response.values[HOSTS_KEY] = hostArray;
+
+  // Add 'isHealthy'.
+  Try<string> level = getFormValue(LEVEL_KEY, values);
+  if (level.isError()) {
+    return BadRequest(level.error());
+  }
+
+  bool isHealthy = strings::upper(level.get()) == "OK";
+
+  // TODO(ccarson): This is a workaround b/c currently a bool is coerced
+  // into a JSON::Double instead of a JSON::True or JSON::False when
+  // you assign to a JSON::Value.
+  //
+  // SEE: https://issues.apache.org/jira/browse/MESOS-939
+  response.values["isHealthy"] =
+    (isHealthy ? JSON::Value(JSON::True()) : JSON::False());
+
+  return OK(response);
+}
+
 
 const string Master::Http::REDIRECT_HELP = HELP(
     TLDR(

http://git-wip-us.apache.org/repos/asf/mesos/blob/165dbe10/src/master/master.cpp
----------------------------------------------------------------------
diff --git a/src/master/master.cpp b/src/master/master.cpp
index f24df23..f4f5e04 100644
--- a/src/master/master.cpp
+++ b/src/master/master.cpp
@@ -462,16 +462,19 @@ void Master::initialize()
   route("/health",
         Http::HEALTH_HELP,
         lambda::bind(&Http::health, http, lambda::_1));
+  route("/observe",
+        Http::OBSERVE_HELP,
+        lambda::bind(&Http::observe, http, lambda::_1));
   route("/redirect",
         Http::REDIRECT_HELP,
         lambda::bind(&Http::redirect, http, lambda::_1));
-  route("/stats.json",
+  route("/roles.json",
         None(),
-        lambda::bind(&Http::stats, http, lambda::_1));
+        lambda::bind(&Http::roles, http, lambda::_1));
   route("/state.json",
         None(),
         lambda::bind(&Http::state, http, lambda::_1));
-  route("/roles.json",
+  route("/stats.json",
         None(),
         lambda::bind(&Http::roles, http, lambda::_1));
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/165dbe10/src/master/master.hpp
----------------------------------------------------------------------
diff --git a/src/master/master.hpp b/src/master/master.hpp
index 00d630a..9d1b56c 100644
--- a/src/master/master.hpp
+++ b/src/master/master.hpp
@@ -280,20 +280,24 @@ private:
     process::Future<process::http::Response> health(
         const process::http::Request& request);
 
+    // /master/observe
+    process::Future<process::http::Response> observe(
+        const process::http::Request& request);
+
     // /master/redirect
     process::Future<process::http::Response> redirect(
         const process::http::Request& request);
 
-    // /master/stats.json
-    process::Future<process::http::Response> stats(
+    // /master/roles.json
+    process::Future<process::http::Response> roles(
         const process::http::Request& request);
 
     // /master/state.json
     process::Future<process::http::Response> state(
         const process::http::Request& request);
 
-    // /master/roles.json
-    process::Future<process::http::Response> roles(
+    // /master/stats.json
+    process::Future<process::http::Response> stats(
         const process::http::Request& request);
 
     // /master/tasks.json
@@ -301,6 +305,7 @@ private:
         const process::http::Request& request);
 
     const static std::string HEALTH_HELP;
+    const static std::string OBSERVE_HELP;
     const static std::string REDIRECT_HELP;
     const static std::string TASKS_HELP;
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/165dbe10/src/tests/repair_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/repair_tests.cpp b/src/tests/repair_tests.cpp
new file mode 100644
index 0000000..ba6f50a
--- /dev/null
+++ b/src/tests/repair_tests.cpp
@@ -0,0 +1,176 @@
+/**
+ * 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 <string>
+#include <vector>
+
+#include <process/future.hpp>
+#include <process/gtest.hpp>
+#include <process/http.hpp>
+#include <process/pid.hpp>
+#include <process/process.hpp>
+
+#include <stout/json.hpp>
+
+#include "tests/mesos.hpp"
+
+
+using namespace mesos;
+using namespace mesos::internal;
+using namespace mesos::internal::tests;
+
+using mesos::internal::master::Master;
+
+using process::Future;
+using process::PID;
+
+using process::http::BadRequest;
+using process::http::OK;
+using process::http::Response;
+
+using std::string;
+using std::vector;
+
+using testing::_;
+
+
+class HealthTest : public MesosTest {};
+
+
+struct JsonResponse
+{
+  string monitor;
+  vector<string> hosts;
+  bool isHealthy;
+};
+
+
+string stringify(const JsonResponse& response)
+{
+  JSON::Object object;
+  object.values["monitor"] = response.monitor;
+
+  JSON::Array hosts;
+  hosts.values.assign(response.hosts.begin(), response.hosts.end());
+  object.values["hosts"] = hosts;
+
+  // TODO(ccarson): This is a workaround b/c currently a bool is coerced
+  // into a JSON::Double instead of a JSON::True or JSON::False when
+  // you assign to a JSON::Value.
+  //
+  // SEE: https://issues.apache.org/jira/browse/MESOS-939
+  object.values["isHealthy"] =
+    response.isHealthy ? JSON::Value(JSON::True()) : JSON::False();
+
+  return stringify(object);
+}
+
+
+// Using macros instead of a helper function so that we get good line
+// numbers from the test run.
+#define VALIDATE_BAD_RESPONSE(response, error)                             \
+    AWAIT_READY(response);                                                 \
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(BadRequest().status, response);        \
+    AWAIT_EXPECT_RESPONSE_BODY_EQ(error, response)
+
+#define VALIDATE_GOOD_RESPONSE(response, jsonResponse)                     \
+    AWAIT_READY(response);                                                 \
+    AWAIT_EXPECT_RESPONSE_HEADER_EQ(                                       \
+        "application/json",                                                \
+        "Content-Type",                                                    \
+      response);                                                           \
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);                \
+    AWAIT_EXPECT_RESPONSE_BODY_EQ(jsonResponse, response);
+
+
+TEST_F(HealthTest, ObserveEndpoint)
+{
+  Try<PID<Master> > master = StartMaster();
+  ASSERT_SOME(master);
+
+  // Empty get to the observe endpoint.
+  Future<Response> response = process::http::get(master.get(), "observe");
+  VALIDATE_BAD_RESPONSE(response, "Missing value for 'monitor'.");
+
+  // Empty post to the observe endpoint.
+  response = process::http::post(master.get(), "observe");
+  VALIDATE_BAD_RESPONSE(response, "Missing value for 'monitor'.");
+
+  // Query string is ignored.
+  response = process::http::post(master.get(), "observe?monitor=foo");
+  VALIDATE_BAD_RESPONSE(response, "Missing value for 'monitor'.");
+
+  // Malformed value causes error.
+  response = process::http::post(master.get(), "observe", "monitor=foo%");
+  VALIDATE_BAD_RESPONSE(response, "Malformed % escape in 'foo%': '%'");
+
+  // Empty value causes error.
+  response = process::http::post(master.get(), "observe", "monitor=");
+  VALIDATE_BAD_RESPONSE(response, "Empty string for 'monitor'.");
+
+  // Missing hosts.
+  response = process::http::post(master.get(), "observe", "monitor=a");
+  VALIDATE_BAD_RESPONSE(response, "Missing value for 'hosts'.");
+
+  // Missing level.
+  response = process::http::post(master.get(), "observe", "monitor=a&hosts=b");
+  VALIDATE_BAD_RESPONSE(response, "Missing value for 'level'.");
+
+  // Good request is successful.
+  JsonResponse expected;
+  expected.monitor = "a";
+  expected.hosts.push_back("b");
+  expected.isHealthy = true;
+
+  response =
+    process::http::post(master.get(), "observe", "monitor=a&hosts=b&level=ok");
+  VALIDATE_GOOD_RESPONSE(response, stringify(expected) );
+
+  // ok is case-insensitive.
+  response =
+    process::http::post(master.get(), "observe", "monitor=a&hosts=b&level=Ok");
+  VALIDATE_GOOD_RESPONSE(response, stringify(expected) );
+
+  response =
+    process::http::post(master.get(), "observe", "monitor=a&hosts=b&level=oK");
+  VALIDATE_GOOD_RESPONSE(response, stringify(expected) );
+
+  response =
+    process::http::post(master.get(), "observe", "monitor=a&hosts=b&level=OK");
+  VALIDATE_GOOD_RESPONSE(response, stringify(expected) );
+
+  // level != OK  is unhealthy.
+  expected.isHealthy = false;
+  response =
+    process::http::post(
+      master.get(),
+      "observe",
+      "monitor=a&hosts=b&level=true");
+  VALIDATE_GOOD_RESPONSE(response, stringify(expected) );
+
+  // Comma seperated hosts are parsed into an array.
+  expected.hosts.push_back("e");
+  response =
+    process::http::post(
+      master.get(),
+      "observe",
+      "monitor=a&hosts=b,e&level=true");
+  VALIDATE_GOOD_RESPONSE(response, stringify(expected) );
+
+  Shutdown();
+}


[2/4] git commit: Added a log tool to start a replica server.

Posted by vi...@apache.org.
Added a log tool to start a replica server.

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


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

Branch: refs/heads/master
Commit: e15bbe0fd9c46109b72e11f5e6eb06a5b67294c5
Parents: 36c7a4d
Author: Jie Yu <yu...@gmail.com>
Authored: Fri Feb 14 17:05:26 2014 -0800
Committer: Vinod Kone <vi...@twitter.com>
Committed: Fri Feb 14 17:05:26 2014 -0800

----------------------------------------------------------------------
 src/Makefile.am          |   4 +-
 src/log/main.cpp         |   2 +
 src/log/tool/replica.cpp | 150 ++++++++++++++++++++++++++++++++++++++++++
 src/log/tool/replica.hpp |  66 +++++++++++++++++++
 4 files changed, 221 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/e15bbe0f/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index 9a33f21..dc9042a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -312,7 +312,8 @@ liblog_la_SOURCES =							\
   log/recover.cpp							\
   log/replica.cpp							\
   log/tool/initialize.cpp						\
-  log/tool/read.cpp
+  log/tool/read.cpp							\
+  log/tool/replica.cpp
 liblog_la_SOURCES +=							\
   log/catchup.hpp							\
   log/consensus.hpp							\
@@ -326,6 +327,7 @@ liblog_la_SOURCES +=							\
   log/tool.hpp								\
   log/tool/initialize.hpp						\
   log/tool/read.hpp							\
+  log/tool/replica.hpp							\
   messages/log.hpp							\
   messages/log.proto
 nodist_liblog_la_SOURCES = $(LOG_PROTOS)

http://git-wip-us.apache.org/repos/asf/mesos/blob/e15bbe0f/src/log/main.cpp
----------------------------------------------------------------------
diff --git a/src/log/main.cpp b/src/log/main.cpp
index c37dd6f..2b30fd0 100644
--- a/src/log/main.cpp
+++ b/src/log/main.cpp
@@ -29,6 +29,7 @@
 #include "log/tool.hpp"
 #include "log/tool/initialize.hpp"
 #include "log/tool/read.hpp"
+#include "log/tool/replica.hpp"
 
 using namespace mesos;
 using namespace mesos::internal;
@@ -69,6 +70,7 @@ int main(int argc, char** argv)
   // Register log tools.
   add(Owned<tool::Tool>(new tool::Initialize()));
   add(Owned<tool::Tool>(new tool::Read()));
+  add(Owned<tool::Tool>(new tool::Replica()));
 
   if (argc < 2) {
     usage(argv[0]);

http://git-wip-us.apache.org/repos/asf/mesos/blob/e15bbe0f/src/log/tool/replica.cpp
----------------------------------------------------------------------
diff --git a/src/log/tool/replica.cpp b/src/log/tool/replica.cpp
new file mode 100644
index 0000000..3985fc7
--- /dev/null
+++ b/src/log/tool/replica.cpp
@@ -0,0 +1,150 @@
+/**
+ * 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 <iostream>
+#include <sstream>
+
+#include <process/future.hpp>
+#include <process/process.hpp>
+
+#include <stout/error.hpp>
+
+#include "log/log.hpp"
+#include "log/tool/initialize.hpp"
+#include "log/tool/replica.hpp"
+
+#include "logging/logging.hpp"
+
+using namespace process;
+
+using std::endl;
+using std::ostringstream;
+using std::string;
+
+namespace mesos {
+namespace internal {
+namespace log {
+namespace tool {
+
+Replica::Flags::Flags()
+{
+  add(&Flags::quorum,
+      "quorum",
+      "Quorum size");
+
+  add(&Flags::path,
+      "path",
+      "Path to the log");
+
+  add(&Flags::servers,
+      "servers",
+      "ZooKeeper servers");
+
+  add(&Flags::znode,
+      "znode",
+      "ZooKeeper znode");
+
+  add(&Flags::initialize,
+      "initialize",
+      "Whether to initialize the log",
+      true);
+
+  add(&Flags::help,
+      "help",
+      "Prints the help message",
+      false);
+}
+
+
+string Replica::usage(const string& argv0) const
+{
+  ostringstream out;
+
+  out << "Usage: " << argv0 << " " << name() << " [OPTIONS]" << endl
+      << endl
+      << "This command is used to start a replica server" << endl
+      << endl
+      << "Supported OPTIONS:" << endl
+      << flags.usage();
+
+  return out.str();
+}
+
+
+Try<Nothing> Replica::execute(int argc, char** argv)
+{
+  // Configure the tool by parsing command line arguments.
+  if (argc > 0 && argv != NULL) {
+    Try<Nothing> load = flags.load(None(), argc, argv);
+    if (load.isError()) {
+      return Error(load.error() + "\n\n" + usage(argv[0]));
+    }
+
+    if (flags.help) {
+      return Error(usage(argv[0]));
+    }
+
+    process::initialize();
+    logging::initialize(argv[0], flags);
+  }
+
+  if (flags.quorum.isNone()) {
+    return Error("Missing flag '--quorum'");
+  }
+
+  if (flags.path.isNone()) {
+    return Error("Missing flag '--path'");
+  }
+
+  if (flags.servers.isNone()) {
+    return Error("Missing flag '--servers'");
+  }
+
+  if (flags.znode.isNone()) {
+    return Error("Missing flag '--znode'");
+  }
+
+  // Initialize the log.
+  if (flags.initialize) {
+    Initialize initialize;
+    initialize.flags.path = flags.path;
+
+    Try<Nothing> execution = initialize.execute();
+    if (execution.isError()) {
+      return Error(execution.error());
+    }
+  }
+
+  // Create the log.
+  Log log(
+      flags.quorum.get(),
+      flags.path.get(),
+      flags.servers.get(),
+      Seconds(10),
+      flags.znode.get());
+
+  // Loop forever.
+  Future<Nothing>().get();
+
+  return Nothing();
+}
+
+} // namespace tool {
+} // namespace log {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/e15bbe0f/src/log/tool/replica.hpp
----------------------------------------------------------------------
diff --git a/src/log/tool/replica.hpp b/src/log/tool/replica.hpp
new file mode 100644
index 0000000..b433348
--- /dev/null
+++ b/src/log/tool/replica.hpp
@@ -0,0 +1,66 @@
+/**
+ * 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.
+ */
+
+#ifndef __LOG_TOOL_REPLICA_HPP__
+#define __LOG_TOOL_REPLICA_HPP__
+
+#include <stout/flags.hpp>
+#include <stout/option.hpp>
+
+#include "log/tool.hpp"
+
+#include "logging/flags.hpp"
+
+namespace mesos {
+namespace internal {
+namespace log {
+namespace tool {
+
+// Start a replica server.
+class Replica : public Tool
+{
+public:
+  class Flags : public logging::Flags
+  {
+  public:
+    Flags();
+
+    Option<size_t> quorum;
+    Option<std::string> path;
+    Option<std::string> servers;
+    Option<std::string> znode;
+    bool initialize;
+    bool help;
+  };
+
+  virtual std::string name() const { return "replica"; }
+  virtual Try<Nothing> execute(int argc = 0, char** argv = NULL);
+
+  // Users can change the default configuration by setting this flags.
+  Flags flags;
+
+private:
+  std::string usage(const std::string& argv0) const;
+};
+
+} // namespace tool {
+} // namespace log {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __LOG_TOOL_REPLICA_HPP__