You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kudu.apache.org by to...@apache.org on 2016/09/13 02:18:27 UTC

[1/3] kudu git commit: Remove a spurious warning left in raft_consensus_state.cc

Repository: kudu
Updated Branches:
  refs/heads/master 2628ec0d7 -> 14cd22a10


Remove a spurious warning left in raft_consensus_state.cc

Change-Id: I0bcada22bc31e1640e5e047c2326f61dc617705d
Reviewed-on: http://gerrit.cloudera.org:8080/4391
Reviewed-by: Mike Percy <mp...@apache.org>
Tested-by: Kudu Jenkins


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

Branch: refs/heads/master
Commit: b150c052a8c663cbc5bcfcc3b8fa5fe26c900fb2
Parents: 2628ec0
Author: Todd Lipcon <to...@apache.org>
Authored: Mon Sep 12 17:53:07 2016 -0700
Committer: Todd Lipcon <to...@apache.org>
Committed: Tue Sep 13 01:24:08 2016 +0000

----------------------------------------------------------------------
 src/kudu/consensus/raft_consensus_state.cc | 1 -
 1 file changed, 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/kudu/blob/b150c052/src/kudu/consensus/raft_consensus_state.cc
----------------------------------------------------------------------
diff --git a/src/kudu/consensus/raft_consensus_state.cc b/src/kudu/consensus/raft_consensus_state.cc
index 7631f26..394d6e5 100644
--- a/src/kudu/consensus/raft_consensus_state.cc
+++ b/src/kudu/consensus/raft_consensus_state.cc
@@ -586,7 +586,6 @@ Status ReplicaState::SetInitialCommittedOpIdUnlocked(const OpId& committed_op) {
 
   } else {
     last_committed_op_id_ = committed_op;
-    LOG_WITH_PREFIX_UNLOCKED(WARNING) << "setting committed at start to " << committed_op;
   }
   return Status::OK();
 }


[2/3] kudu git commit: tool: port kudu-fs_dump, remove kudu-fs_list, fs_tool

Posted by to...@apache.org.
tool: port kudu-fs_dump, remove kudu-fs_list, fs_tool

This change ports fs_dump actions under 'kudu local_replica '.
Additionally this has following re-organizations:
    - moved dump_cfile action under 'kudu fs dump cfile'.
    - kudu-fs_list tool has been removed altogether,
      but some of the functionalities are retained under
      'local_replica' and 'fs dump' sub-actions.
    - fs_tool library is stripped off, and all those
      routines are in respective action files.

Also added tests under kudu-tool-test to exercise each of these fs tools.

Change-Id: I1ec628b65613011d8c48b6239c13762276425966
Reviewed-on: http://gerrit.cloudera.org:8080/4305
Tested-by: Kudu Jenkins
Reviewed-by: Todd Lipcon <to...@apache.org>


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

Branch: refs/heads/master
Commit: 9cb1bcacf1449a41ebd6a2c13faead2665ea38f9
Parents: b150c05
Author: Dinesh Bhat <di...@cloudera.com>
Authored: Tue Aug 30 12:14:46 2016 -0700
Committer: Todd Lipcon <to...@apache.org>
Committed: Tue Sep 13 02:14:23 2016 +0000

----------------------------------------------------------------------
 docs/release_notes.adoc                         |  10 +-
 .../integration-tests/master_migration-itest.cc |   3 +-
 src/kudu/tablet/rowset_metadata.h               |   9 +-
 src/kudu/tools/CMakeLists.txt                   |  20 +-
 src/kudu/tools/fs_tool.cc                       | 576 -------------------
 src/kudu/tools/fs_tool.h                        | 158 -----
 src/kudu/tools/kudu-tool-test.cc                | 240 +++++++-
 src/kudu/tools/tool_action.h                    |   9 +-
 src/kudu/tools/tool_action_fs.cc                |  64 ++-
 src/kudu/tools/tool_action_local_replica.cc     | 493 +++++++++++++++-
 src/kudu/tools/tool_action_tablet.cc            |   1 -
 11 files changed, 763 insertions(+), 820 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/kudu/blob/9cb1bcac/docs/release_notes.adoc
----------------------------------------------------------------------
diff --git a/docs/release_notes.adoc b/docs/release_notes.adoc
index 3034b34..9fd83ef 100644
--- a/docs/release_notes.adoc
+++ b/docs/release_notes.adoc
@@ -119,14 +119,20 @@ Kudu 1.0.0 are not supported.
   implemented as `kudu cluster ksck`.
 
 - The `cfile-dump` tool has been removed. The same functionality is now
-  implemented as `kudu fs cfile_dump`.
+  implemented as `kudu fs cfile dump`.
 
 - The `log-dump` tool has been removed. The same functionality is now
-  implemented as `kudu wal dump` and `kudu local_replica dump_wals`.
+  implemented as `kudu wal dump` and `kudu local_replica dump wals`.
 
 - The `kudu-admin` tool has been removed. The same functionality is now
   implemented within `kudu table` and `kudu tablet`.
 
+- The `kudu-fs_dump` tool has been removed. The same functionality is now
+  implemented as `kudu fs dump`.
+
+- The `kudu-fs_list` tool has been removed and some similar useful
+  functionality has been moved under 'kudu local_replica'.
+
 === Configuration flags
 
 - Some configuration flags are now marked as 'unsafe' and 'experimental'. Such flags

http://git-wip-us.apache.org/repos/asf/kudu/blob/9cb1bcac/src/kudu/integration-tests/master_migration-itest.cc
----------------------------------------------------------------------
diff --git a/src/kudu/integration-tests/master_migration-itest.cc b/src/kudu/integration-tests/master_migration-itest.cc
index b01cda0..c1d66e2 100644
--- a/src/kudu/integration-tests/master_migration-itest.cc
+++ b/src/kudu/integration-tests/master_migration-itest.cc
@@ -124,7 +124,8 @@ TEST_F(MasterMigrationTest, TestEndToEndMigration) {
       vector<string> args = {
           kBinPath,
           "fs",
-          "print_uuid",
+          "dump",
+          "uuid",
           "--fs_wal_dir=" + data_root,
           "--fs_data_dirs=" + data_root
       };

http://git-wip-us.apache.org/repos/asf/kudu/blob/9cb1bcac/src/kudu/tablet/rowset_metadata.h
----------------------------------------------------------------------
diff --git a/src/kudu/tablet/rowset_metadata.h b/src/kudu/tablet/rowset_metadata.h
index c195d8f..8813602 100644
--- a/src/kudu/tablet/rowset_metadata.h
+++ b/src/kudu/tablet/rowset_metadata.h
@@ -34,10 +34,6 @@
 
 namespace kudu {
 
-namespace tools {
-class FsTool;
-} // namespace tools
-
 namespace tablet {
 
 class RowSetMetadataUpdate;
@@ -173,11 +169,12 @@ class RowSetMetadata {
   // On success, calls TabletMetadata::AddOrphanedBlocks() on the removed blocks.
   Status CommitUpdate(const RowSetMetadataUpdate& update);
 
+  void ToProtobuf(RowSetDataPB *pb);
+
   std::vector<BlockId> GetAllBlocks();
 
  private:
   friend class TabletMetadata;
-  friend class kudu::tools::FsTool;
 
   typedef simple_spinlock LockType;
 
@@ -197,8 +194,6 @@ class RowSetMetadata {
 
   Status InitFromPB(const RowSetDataPB& pb);
 
-  void ToProtobuf(RowSetDataPB *pb);
-
   TabletMetadata* const tablet_metadata_;
   bool initted_;
   int64_t id_;

http://git-wip-us.apache.org/repos/asf/kudu/blob/9cb1bcac/src/kudu/tools/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/src/kudu/tools/CMakeLists.txt b/src/kudu/tools/CMakeLists.txt
index 8a707bf..0ce2899 100644
--- a/src/kudu/tools/CMakeLists.txt
+++ b/src/kudu/tools/CMakeLists.txt
@@ -48,24 +48,6 @@ add_executable(kudu-ts-cli ts-cli.cc)
 target_link_libraries(kudu-ts-cli
   ${LINK_LIBS})
 
-add_library(fs_tool fs_tool.cc)
-target_link_libraries(fs_tool
-  gutil
-  kudu_common
-  server_common
-  consensus
-  tablet)
-
-add_executable(kudu-fs_list fs_list-tool.cc)
-target_link_libraries(kudu-fs_list
-  ${LINK_LIBS}
-  fs_tool)
-
-add_executable(kudu-fs_dump fs_dump-tool.cc)
-target_link_libraries(kudu-fs_dump
-  ${LINK_LIBS}
-  fs_tool)
-
 add_library(ksck
     ksck.cc
     ksck_remote.cc
@@ -102,6 +84,8 @@ target_link_libraries(kudu
   kudu_util
   log
   master
+  server_common
+  tablet
   tserver
   ${KUDU_BASE_LIBS}
 )

http://git-wip-us.apache.org/repos/asf/kudu/blob/9cb1bcac/src/kudu/tools/fs_tool.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tools/fs_tool.cc b/src/kudu/tools/fs_tool.cc
deleted file mode 100644
index 960a82b..0000000
--- a/src/kudu/tools/fs_tool.cc
+++ /dev/null
@@ -1,576 +0,0 @@
-// 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 "kudu/tools/fs_tool.h"
-
-#include <algorithm>
-#include <iostream>
-#include <memory>
-#include <vector>
-
-#include <boost/function.hpp>
-#include <gflags/gflags.h>
-#include <glog/logging.h>
-
-#include "kudu/cfile/cfile_reader.h"
-#include "kudu/common/rowblock.h"
-#include "kudu/common/row_changelist.h"
-#include "kudu/consensus/log_util.h"
-#include "kudu/consensus/log_reader.h"
-#include "kudu/fs/fs_manager.h"
-#include "kudu/gutil/strings/human_readable.h"
-#include "kudu/gutil/strings/substitute.h"
-#include "kudu/gutil/strings/util.h"
-#include "kudu/tablet/cfile_set.h"
-#include "kudu/tablet/deltafile.h"
-#include "kudu/tablet/tablet.h"
-#include "kudu/util/env.h"
-#include "kudu/util/logging.h"
-#include "kudu/util/mem_tracker.h"
-#include "kudu/util/memory/arena.h"
-#include "kudu/util/status.h"
-
-namespace kudu {
-namespace tools {
-
-using cfile::CFileIterator;
-using cfile::CFileReader;
-using cfile::DumpIterator;
-using cfile::ReaderOptions;
-using fs::ReadableBlock;
-using log::LogReader;
-using log::ReadableLogSegment;
-using std::shared_ptr;
-using std::string;
-using std::vector;
-using strings::Substitute;
-using tablet::CFileSet;
-using tablet::DeltaFileReader;
-using tablet::DeltaIterator;
-using tablet::DeltaKeyAndUpdate;
-using tablet::DeltaType;
-using tablet::MvccSnapshot;
-using tablet::RowSetMetadata;
-using tablet::Tablet;
-using tablet::TabletMetadata;
-
-static const char* const kSeparatorLine =
-  "----------------------------------------------------------------------\n";
-
-namespace {
-string Indent(int indent) {
-  return string(indent, ' ');
-}
-
-string IndentString(const string& s, int indent) {
-  return Indent(indent) + StringReplace(s, "\n", "\n" + Indent(indent), true);
-}
-} // anonymous namespace
-
-FsTool::FsTool(DetailLevel detail_level)
-    : initialized_(false),
-      detail_level_(detail_level) {
-}
-
-FsTool::~FsTool() {
-}
-
-Status FsTool::Init() {
-  CHECK(!initialized_) << "Already initialized";
-  // Allow read-only access to live blocks.
-  FsManagerOpts opts;
-  opts.read_only = true;
-  fs_manager_.reset(new FsManager(Env::Default(), opts));
-  RETURN_NOT_OK(fs_manager_->Open());
-
-  LOG(INFO) << "Opened file system with uuid: " << fs_manager_->uuid();
-
-  initialized_ = true;
-  return Status::OK();
-}
-
-Status FsTool::FsTree() {
-  DCHECK(initialized_);
-
-  fs_manager_->DumpFileSystemTree(std::cout);
-  return Status::OK();
-}
-
-Status FsTool::ListAllLogSegments() {
-  DCHECK(initialized_);
-
-  string wals_dir = fs_manager_->GetWalsRootDir();
-  if (!fs_manager_->Exists(wals_dir)) {
-    return Status::Corruption(Substitute(
-        "root log directory '$0' does not exist", wals_dir));
-  }
-
-  std::cout << "Root log directory: " << wals_dir << std::endl;
-
-  vector<string> children;
-  RETURN_NOT_OK_PREPEND(fs_manager_->ListDir(wals_dir, &children),
-                        "Could not list log directories");
-  for (const string& child : children) {
-    if (HasPrefixString(child, ".")) {
-      // Hidden files or ./..
-      VLOG(1) << "Ignoring hidden file in root log directory " << child;
-      continue;
-    }
-    string path = JoinPathSegments(wals_dir, child);
-    if (HasSuffixString(child, FsManager::kWalsRecoveryDirSuffix)) {
-      std::cout << "Log recovery dir found: " << path << std::endl;
-    } else {
-      std::cout << "Log directory: " << path << std::endl;
-    }
-    RETURN_NOT_OK(ListSegmentsInDir(path));
-  }
-  return Status::OK();
-}
-
-Status FsTool::ListLogSegmentsForTablet(const string& tablet_id) {
-  DCHECK(initialized_);
-
-  string tablet_wal_dir = fs_manager_->GetTabletWalDir(tablet_id);
-  if (!fs_manager_->Exists(tablet_wal_dir)) {
-    return Status::NotFound(Substitute("tablet '$0' has no logs in wals dir '$1'",
-                                       tablet_id, tablet_wal_dir));
-  }
-  std::cout << "Tablet WAL dir found: " << tablet_wal_dir << std::endl;
-  RETURN_NOT_OK(ListSegmentsInDir(tablet_wal_dir));
-  string recovery_dir = fs_manager_->GetTabletWalRecoveryDir(tablet_id);
-  if (fs_manager_->Exists(recovery_dir)) {
-    std::cout << "Recovery dir found: " << recovery_dir << std::endl;
-    RETURN_NOT_OK(ListSegmentsInDir(recovery_dir));
-  }
-  return Status::OK();
-}
-
-
-Status FsTool::ListAllTablets() {
-  DCHECK(initialized_);
-
-  vector<string> tablets;
-  RETURN_NOT_OK(fs_manager_->ListTabletIds(&tablets));
-  for (const string& tablet : tablets) {
-    if (detail_level_ >= HEADERS_ONLY) {
-      std::cout << "Tablet: " << tablet << std::endl;
-      RETURN_NOT_OK(PrintTabletMeta(tablet, 2));
-    } else {
-      std::cout << "\t" << tablet << std::endl;
-    }
-  }
-  return Status::OK();
-}
-
-Status FsTool::ListSegmentsInDir(const string& segments_dir) {
-  vector<string> segments;
-  RETURN_NOT_OK_PREPEND(fs_manager_->ListDir(segments_dir, &segments),
-                        "Unable to list log segments");
-  std::cout << "Segments in " << segments_dir << ":" << std::endl;
-  for (const string& segment : segments) {
-    if (!log::IsLogFileName(segment)) {
-      continue;
-    }
-    if (detail_level_ >= HEADERS_ONLY) {
-      std::cout << "Segment: " << segment << std::endl;
-      string path = JoinPathSegments(segments_dir, segment);
-      RETURN_NOT_OK(PrintLogSegmentHeader(path, 2));
-    } else {
-      std::cout << "\t" << segment << std::endl;
-    }
-  }
-  return Status::OK();
-}
-
-Status FsTool::PrintLogSegmentHeader(const string& path,
-                                     int indent) {
-  scoped_refptr<ReadableLogSegment> segment;
-  Status s = ReadableLogSegment::Open(fs_manager_->env(),
-                                      path,
-                                      &segment);
-
-  if (s.IsUninitialized()) {
-    LOG(ERROR) << path << " is not initialized: " << s.ToString();
-    return Status::OK();
-  }
-  if (s.IsCorruption()) {
-    LOG(ERROR) << path << " is corrupt: " << s.ToString();
-    return Status::OK();
-  }
-  RETURN_NOT_OK_PREPEND(s, "Unexpected error reading log segment " + path);
-
-  std::cout << Indent(indent) << "Size: "
-            << HumanReadableNumBytes::ToStringWithoutRounding(segment->file_size())
-            << std::endl;
-  std::cout << Indent(indent) << "Header: " << std::endl;
-  std::cout << IndentString(segment->header().DebugString(), indent);
-  return Status::OK();
-}
-
-Status FsTool::PrintTabletMeta(const string& tablet_id, int indent) {
-  scoped_refptr<TabletMetadata> meta;
-  RETURN_NOT_OK(TabletMetadata::Load(fs_manager_.get(), tablet_id, &meta));
-
-  const Schema& schema = meta->schema();
-
-  std::cout << Indent(indent) << "Partition: "
-            << meta->partition_schema().PartitionDebugString(meta->partition(), meta->schema())
-            << std::endl;
-  std::cout << Indent(indent) << "Table name: " << meta->table_name()
-            << " Table id: " << meta->table_id() << std::endl;
-  std::cout << Indent(indent) << "Schema (version=" << meta->schema_version() << "): "
-            << schema.ToString() << std::endl;
-
-  tablet::TabletSuperBlockPB pb;
-  RETURN_NOT_OK_PREPEND(meta->ToSuperBlock(&pb), "Could not get superblock");
-  std::cout << "Superblock:\n" << pb.DebugString() << std::endl;
-
-  return Status::OK();
-}
-
-Status FsTool::ListBlocksForAllTablets() {
-  DCHECK(initialized_);
-
-  vector<string> tablets;
-  RETURN_NOT_OK(fs_manager_->ListTabletIds(&tablets));
-  for (string tablet : tablets) {
-    RETURN_NOT_OK(ListBlocksForTablet(tablet));
-  }
-  return Status::OK();
-}
-
-Status FsTool::ListBlocksForTablet(const string& tablet_id) {
-  DCHECK(initialized_);
-
-  scoped_refptr<TabletMetadata> meta;
-  RETURN_NOT_OK(TabletMetadata::Load(fs_manager_.get(), tablet_id, &meta));
-
-  if (meta->rowsets().empty()) {
-    std::cout << "No rowsets found on disk for tablet " << tablet_id << std::endl;
-    return Status::OK();
-  }
-
-  std::cout << "Listing all data blocks in tablet " << tablet_id << ":" << std::endl;
-
-  Schema schema = meta->schema();
-
-  size_t idx = 0;
-  for (const shared_ptr<RowSetMetadata>& rs_meta : meta->rowsets())  {
-    std::cout << "Rowset " << idx++ << std::endl;
-    RETURN_NOT_OK(ListBlocksInRowSet(schema, *rs_meta));
-  }
-
-  return Status::OK();
-}
-
-Status FsTool::ListBlocksInRowSet(const Schema& schema,
-                                  const RowSetMetadata& rs_meta) {
-  RowSetMetadata::ColumnIdToBlockIdMap col_blocks = rs_meta.GetColumnBlocksById();
-  for (const RowSetMetadata::ColumnIdToBlockIdMap::value_type& e : col_blocks) {
-    ColumnId col_id = e.first;
-    const BlockId& block_id = e.second;
-    std::cout << "Column block for column ID " << col_id;
-    int col_idx = schema.find_column_by_id(col_id);
-    if (col_idx != -1) {
-      std::cout << " (" << schema.column(col_idx).ToString() << ")";
-    }
-    std::cout << ": ";
-    std::cout << block_id.ToString() << std::endl;
-  }
-
-  for (const BlockId& block : rs_meta.undo_delta_blocks()) {
-    std::cout << "UNDO: " << block.ToString() << std::endl;
-  }
-
-  for (const BlockId& block : rs_meta.redo_delta_blocks()) {
-    std::cout << "REDO: " << block.ToString() << std::endl;
-  }
-
-  return Status::OK();
-}
-
-Status FsTool::DumpTabletBlocks(const std::string& tablet_id,
-                                const DumpOptions& opts,
-                                int indent) {
-  DCHECK(initialized_);
-
-  scoped_refptr<TabletMetadata> meta;
-  RETURN_NOT_OK(TabletMetadata::Load(fs_manager_.get(), tablet_id, &meta));
-
-  if (meta->rowsets().empty()) {
-    std::cout << Indent(indent) << "No rowsets found on disk for tablet "
-              << tablet_id << std::endl;
-    return Status::OK();
-  }
-
-  Schema schema = meta->schema();
-
-  size_t idx = 0;
-  for (const shared_ptr<RowSetMetadata>& rs_meta : meta->rowsets())  {
-    std::cout << std::endl << Indent(indent) << "Dumping rowset " << idx++
-              << std::endl << Indent(indent) << kSeparatorLine;
-    RETURN_NOT_OK(DumpRowSetInternal(meta->schema(), rs_meta, opts, indent + 2));
-  }
-  return Status::OK();
-}
-
-Status FsTool::DumpTabletData(const std::string& tablet_id) {
-  DCHECK(initialized_);
-
-  scoped_refptr<TabletMetadata> meta;
-  RETURN_NOT_OK(TabletMetadata::Load(fs_manager_.get(), tablet_id, &meta));
-
-  scoped_refptr<log::LogAnchorRegistry> reg(new log::LogAnchorRegistry());
-  Tablet t(meta, scoped_refptr<server::Clock>(nullptr), shared_ptr<MemTracker>(),
-           nullptr, reg.get());
-  RETURN_NOT_OK_PREPEND(t.Open(), "Couldn't open tablet");
-  vector<string> lines;
-  RETURN_NOT_OK_PREPEND(t.DebugDump(&lines), "Couldn't dump tablet");
-  for (const string& line : lines) {
-    std::cout << line << std::endl;
-  }
-  return Status::OK();
-}
-
-Status FsTool::DumpRowSet(const string& tablet_id,
-                          int64_t rowset_id,
-                          const DumpOptions& opts,
-                          int indent) {
-  DCHECK(initialized_);
-
-  scoped_refptr<TabletMetadata> meta;
-  RETURN_NOT_OK(TabletMetadata::Load(fs_manager_.get(), tablet_id, &meta));
-
-  for (const shared_ptr<RowSetMetadata>& rs_meta : meta->rowsets())  {
-    if (rs_meta->id() == rowset_id) {
-      return DumpRowSetInternal(meta->schema(), rs_meta, opts, indent);
-    }
-  }
-
-  return Status::InvalidArgument(
-      Substitute("Could not find rowset $0 in tablet id $1", rowset_id, tablet_id));
-}
-
-Status FsTool::DumpRowSetInternal(const Schema& schema,
-                                  const shared_ptr<RowSetMetadata>& rs_meta,
-                                  const DumpOptions& opts,
-                                  int indent) {
-  tablet::RowSetDataPB pb;
-  rs_meta->ToProtobuf(&pb);
-
-  std::cout << Indent(indent) << "RowSet metadata: " << pb.DebugString() << std::endl
-            << std::endl;
-
-  RowSetMetadata::ColumnIdToBlockIdMap col_blocks = rs_meta->GetColumnBlocksById();
-  for (const RowSetMetadata::ColumnIdToBlockIdMap::value_type& e : col_blocks) {
-    ColumnId col_id = e.first;
-    const BlockId& block_id = e.second;
-
-    std::cout << Indent(indent) << "Dumping column block " << block_id << " for column id "
-              << col_id;
-    int col_idx = schema.find_column_by_id(col_id);
-    if (col_idx != -1) {
-      std::cout << "( " << schema.column(col_idx).ToString() <<  ")";
-    }
-    std::cout << ":" << std::endl;
-    std::cout << Indent(indent) << kSeparatorLine;
-    if (opts.metadata_only) continue;
-    RETURN_NOT_OK(DumpCFileBlockInternal(block_id, opts, indent));
-    std::cout << std::endl;
-  }
-
-  for (const BlockId& block : rs_meta->undo_delta_blocks()) {
-    std::cout << Indent(indent) << "Dumping undo delta block " << block << ":" << std::endl
-              << Indent(indent) << kSeparatorLine;
-    RETURN_NOT_OK(DumpDeltaCFileBlockInternal(schema,
-                                              rs_meta,
-                                              block,
-                                              tablet::UNDO,
-                                              opts,
-                                              indent,
-                                              opts.metadata_only));
-    std::cout << std::endl;
-  }
-
-  for (const BlockId& block : rs_meta->redo_delta_blocks()) {
-    std::cout << Indent(indent) << "Dumping redo delta block " << block << ":" << std::endl
-              << Indent(indent) << kSeparatorLine;
-    RETURN_NOT_OK(DumpDeltaCFileBlockInternal(schema,
-                                              rs_meta,
-                                              block,
-                                              tablet::REDO,
-                                              opts,
-                                              indent,
-                                              opts.metadata_only));
-    std::cout << std::endl;
-  }
-
-  return Status::OK();
-}
-
-Status FsTool::DumpCFileBlock(const std::string& block_id_str,
-                              const DumpOptions &opts,
-                              int indent) {
-  uint64_t numeric_id;
-  if (!safe_strtou64(block_id_str, &numeric_id) &&
-      !safe_strtou64_base(block_id_str, &numeric_id, 16)) {
-    return Status::InvalidArgument(Substitute("block '$0' could not be parsed",
-                                              block_id_str));
-  }
-  BlockId block_id(numeric_id);
-  if (!fs_manager_->BlockExists(block_id)) {
-    return Status::NotFound(Substitute("block '$0' does not exist", block_id_str));
-  }
-  return DumpCFileBlockInternal(block_id, opts, indent);
-}
-
-Status FsTool::PrintUUID(int indent) {
-  std::cout << Indent(indent) << fs_manager_->uuid() << std::endl;
-  return Status::OK();
-}
-
-Status FsTool::DumpCFileBlockInternal(const BlockId& block_id,
-                                      const DumpOptions& opts,
-                                      int indent) {
-  gscoped_ptr<ReadableBlock> block;
-  RETURN_NOT_OK(fs_manager_->OpenBlock(block_id, &block));
-  gscoped_ptr<CFileReader> reader;
-  RETURN_NOT_OK(CFileReader::Open(std::move(block), ReaderOptions(), &reader));
-
-  std::cout << Indent(indent) << "CFile Header: "
-            << reader->header().ShortDebugString() << std::endl;
-  if (detail_level_ <= HEADERS_ONLY) {
-    return Status::OK();
-  }
-  std::cout << Indent(indent) << reader->footer().num_values()
-            << " values:" << std::endl;
-
-  gscoped_ptr<CFileIterator> it;
-  RETURN_NOT_OK(reader->NewIterator(&it, CFileReader::DONT_CACHE_BLOCK));
-  RETURN_NOT_OK(it->SeekToFirst());
-  return DumpIterator(*reader, it.get(), &std::cout, opts.nrows, indent + 2);
-}
-
-Status FsTool::DumpDeltaCFileBlockInternal(const Schema& schema,
-                                           const shared_ptr<RowSetMetadata>& rs_meta,
-                                           const BlockId& block_id,
-                                           DeltaType delta_type,
-                                           const DumpOptions& opts,
-                                           int indent,
-                                           bool metadata_only) {
-  // Open the delta reader
-  gscoped_ptr<ReadableBlock> readable_block;
-  RETURN_NOT_OK(fs_manager_->OpenBlock(block_id, &readable_block));
-  shared_ptr<DeltaFileReader> delta_reader;
-  RETURN_NOT_OK(DeltaFileReader::Open(std::move(readable_block),
-                                      block_id,
-                                      &delta_reader,
-                                      delta_type));
-
-  std::cout << Indent(indent) << "Delta stats: " << delta_reader->delta_stats().ToString()
-      << std::endl;
-  if (metadata_only) {
-    return Status::OK();
-  }
-
-  // Create the delta iterator.
-  // TODO: see if it's worth re-factoring NewDeltaIterator to return a
-  // gscoped_ptr that can then be released if we need a raw or shared
-  // pointer.
-  DeltaIterator* raw_iter;
-
-  MvccSnapshot snap_all;
-  if (delta_type == tablet::REDO) {
-    snap_all = MvccSnapshot::CreateSnapshotIncludingAllTransactions();
-  } else if (delta_type == tablet::UNDO) {
-    snap_all = MvccSnapshot::CreateSnapshotIncludingNoTransactions();
-  }
-
-  Status s = delta_reader->NewDeltaIterator(&schema, snap_all, &raw_iter);
-
-  if (s.IsNotFound()) {
-    std::cout << "Empty delta block." << std::endl;
-    return Status::OK();
-  }
-  RETURN_NOT_OK(s);
-
-  // NewDeltaIterator returns Status::OK() iff a new DeltaIterator is created. Thus,
-  // it's safe to have a gscoped_ptr take possesion of 'raw_iter' here.
-  gscoped_ptr<DeltaIterator> delta_iter(raw_iter);
-  RETURN_NOT_OK(delta_iter->Init(NULL));
-  RETURN_NOT_OK(delta_iter->SeekToOrdinal(0));
-
-  // TODO: it's awkward that whenever we want to iterate over deltas we also
-  // need to open the CFileSet for the rowset. Ideally, we should use information stored
-  // in the footer/store additional information in the footer as to make it feasible
-  // iterate over all deltas using a DeltaFileIterator alone.
-  shared_ptr<CFileSet> cfileset(new CFileSet(rs_meta));
-  RETURN_NOT_OK(cfileset->Open());
-  gscoped_ptr<CFileSet::Iterator> cfileset_iter(cfileset->NewIterator(&schema));
-
-  RETURN_NOT_OK(cfileset_iter->Init(NULL));
-
-  const size_t kRowsPerBlock  = 100;
-  size_t nrows = 0;
-  size_t ndeltas = 0;
-  Arena arena(32 * 1024, 128 * 1024);
-  RowBlock block(schema, kRowsPerBlock, &arena);
-
-  // See tablet/delta_compaction.cc to understand why this loop is structured the way
-  // it is.
-  while (cfileset_iter->HasNext()) {
-    size_t n;
-    if (opts.nrows > 0) {
-      // Note: number of deltas may not equal the number of rows, but
-      // since this is a CLI tool (and the nrows option exists
-      // primarily to limit copious output) it's okay not to be
-      // exact here.
-      size_t remaining = opts.nrows - nrows;
-      if (remaining == 0) break;
-      n = std::min(remaining, kRowsPerBlock);
-    } else {
-      n = kRowsPerBlock;
-    }
-
-    arena.Reset();
-    cfileset_iter->PrepareBatch(&n);
-
-    block.Resize(n);
-
-    RETURN_NOT_OK(delta_iter->PrepareBatch(n, DeltaIterator::PREPARE_FOR_COLLECT));
-    vector<DeltaKeyAndUpdate> out;
-    RETURN_NOT_OK(delta_iter->FilterColumnIdsAndCollectDeltas(vector<ColumnId>(),
-                                                              &out,
-                                                              &arena));
-    for (const DeltaKeyAndUpdate& upd : out) {
-      if (detail_level_ > HEADERS_ONLY) {
-        std::cout << Indent(indent) << upd.key.ToString() << " "
-                  << RowChangeList(upd.cell).ToString(schema) << std::endl;
-        ++ndeltas;
-      }
-    }
-    RETURN_NOT_OK(cfileset_iter->FinishBatch());
-
-    nrows += n;
-  }
-
-  VLOG(1) << "Processed " << ndeltas << " deltas, for total of " << nrows << " possible rows.";
-  return Status::OK();
-}
-
-} // namespace tools
-} // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/9cb1bcac/src/kudu/tools/fs_tool.h
----------------------------------------------------------------------
diff --git a/src/kudu/tools/fs_tool.h b/src/kudu/tools/fs_tool.h
deleted file mode 100644
index 5c85c17..0000000
--- a/src/kudu/tools/fs_tool.h
+++ /dev/null
@@ -1,158 +0,0 @@
-// 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.
-//
-// Shared fields and methods for querying local files and directories
-#ifndef KUDU_TOOLS_FS_TOOL_H
-#define KUDU_TOOLS_FS_TOOL_H
-
-#include <iostream>
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "kudu/gutil/gscoped_ptr.h"
-#include "kudu/util/status.h"
-#include "kudu/tablet/delta_key.h"
-
-namespace kudu {
-
-class FsManager;
-class Schema;
-class BlockId;
-class RandomAccessFile;
-
-namespace tablet {
-class TabletMetadata;
-class RowSetMetadata;
-}
-
-namespace tools {
-
-struct DumpOptions {
-  std::string start_key;
-  std::string end_key;
-  size_t nrows;
-  bool metadata_only;
-
-  DumpOptions()
-      : start_key(""),
-        end_key(""),
-        nrows(0),
-        metadata_only(false) {
-  }
-};
-
-class FsTool {
- public:
-
-  enum DetailLevel {
-    MINIMUM = 0, // Minimum amount of information
-    HEADERS_ONLY = 1, // Tablet/segment headers only
-    MAXIMUM = 2,
-  };
-
-  explicit FsTool(DetailLevel detail_level);
-  ~FsTool();
-
-  Status Init();
-
-  // Prints out the file system tree.
-  Status FsTree();
-
-  // Lists all log segments in the root WALs directory.
-  Status ListAllLogSegments();
-
-  // Lists all log segments for tablet 'tablet_id'.
-  Status ListLogSegmentsForTablet(const std::string& tablet_id);
-
-  // Lists all tablets in a tablet server's local file system.
-  Status ListAllTablets();
-
-  // Prints the header for a log segment residing in 'path'.
-  Status PrintLogSegmentHeader(const std::string& path, int indent);
-
-  // Lists blocks for a tablet organized by rowset.
-  Status ListBlocksForTablet(const std::string& tablet_id);
-
-  // Lists blocks for all tablets.
-  Status ListBlocksForAllTablets();
-
-  // Prints the tablet metadata for a tablet 'tablet_id'.
-  Status PrintTabletMeta(const std::string& tablet_id, int indent);
-
-  // Dumps the blocks that make up a tablet, rowset by rowset. This ends up
-  // outputting on a column-by-column basis, as close as possible to the raw
-  // storage. See also: DumpRowSet().
-  Status DumpTabletBlocks(const std::string& tablet_id,
-                    const DumpOptions& opts,
-                    int indent);
-
-  // Dump the data stored in a tablet. The output here is much more readable
-  // than DumpTabletBlocks, since it reconstructs rows and associates undo/redo deltas
-  // with those rows.
-  Status DumpTabletData(const std::string& tablet_id);
-
-  // Dumps column blocks, all types of delta blocks for a given
-  // rowset.
-  Status DumpRowSet(const std::string& tablet_id,
-                    int64_t rowset_id,
-                    const DumpOptions& opts,
-                    int indent);
-
-  Status DumpCFileBlock(const std::string& block_id,
-                        const DumpOptions& opts,
-                        int indent);
-
-  // Prints the server's UUID to whom the data belongs and nothing else.
-  Status PrintUUID(int indent);
- private:
-  Status ListSegmentsInDir(const std::string& segments_dir);
-
-  Status ListBlocksInRowSet(const Schema& schema,
-                            const tablet::RowSetMetadata& rs_meta);
-
-  Status DumpRowSetInternal(const Schema& schema,
-                            const std::shared_ptr<tablet::RowSetMetadata>& rs_meta,
-                            const DumpOptions& opts,
-                            int indent);
-
-  Status DumpCFileBlockInternal(const BlockId& block_id,
-                                const DumpOptions& opts,
-                                int indent);
-
-  Status DumpDeltaCFileBlockInternal(const Schema& schema,
-                                     const std::shared_ptr<tablet::RowSetMetadata>& rs_meta,
-                                     const BlockId& block_id,
-                                     tablet::DeltaType delta_type,
-                                     const DumpOptions& opts,
-                                     int indent,
-                                     bool metadata_only);
-
-  Status OpenBlockAsFile(const BlockId& block_id,
-                         uint64_t* file_size,
-                         std::shared_ptr<RandomAccessFile>* block_reader);
-
-  bool initialized_;
-  const DetailLevel detail_level_;
-  gscoped_ptr<FsManager> fs_manager_;
-};
-
-} // namespace tools
-} // namespace kudu
-
-#endif // KUDU_TOOLS_FS_TOOL_H

http://git-wip-us.apache.org/repos/asf/kudu/blob/9cb1bcac/src/kudu/tools/kudu-tool-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tools/kudu-tool-test.cc b/src/kudu/tools/kudu-tool-test.cc
index cf66066..9720d4a 100644
--- a/src/kudu/tools/kudu-tool-test.cc
+++ b/src/kudu/tools/kudu-tool-test.cc
@@ -15,6 +15,8 @@
 // specific language governing permissions and limitations
 // under the License.
 
+#include <memory>
+#include <sstream>
 #include <string>
 #include <vector>
 
@@ -24,6 +26,8 @@
 #include "kudu/cfile/cfile-test-base.h"
 #include "kudu/cfile/cfile_util.h"
 #include "kudu/cfile/cfile_writer.h"
+#include "kudu/common/partial_row.h"
+#include "kudu/common/partition.h"
 #include "kudu/common/schema.h"
 #include "kudu/common/wire_protocol.h"
 #include "kudu/common/wire_protocol-test-util.h"
@@ -39,6 +43,9 @@
 #include "kudu/gutil/ref_counted.h"
 #include "kudu/gutil/strings/split.h"
 #include "kudu/gutil/strings/substitute.h"
+#include "kudu/tablet/local_tablet_writer.h"
+#include "kudu/tablet/tablet-harness.h"
+#include "kudu/tablet/tablet_metadata.h"
 #include "kudu/tserver/tserver.pb.h"
 #include "kudu/util/async_util.h"
 #include "kudu/util/env.h"
@@ -61,9 +68,15 @@ using consensus::ReplicateMsg;
 using fs::WritableBlock;
 using log::Log;
 using log::LogOptions;
+using std::ostringstream;
 using std::string;
+using std::unique_ptr;
 using std::vector;
 using strings::Substitute;
+using tablet::LocalTabletWriter;
+using tablet::TabletHarness;
+using tablet::TabletMetadata;
+using tablet::TabletSuperBlockPB;
 using tserver::WriteRequestPB;
 
 class ToolTest : public KuduTest {
@@ -183,21 +196,41 @@ TEST_F(ToolTest, TestModeHelp) {
   {
     const vector<string> kFsModeRegexes = {
         "format.*new Kudu filesystem",
-        "print_uuid.*UUID of a Kudu filesystem"
+        "dump.*Dump a Kudu filesystem"
     };
     NO_FATALS(RunTestHelp("fs", kFsModeRegexes));
     NO_FATALS(RunTestHelp("fs not_a_mode", kFsModeRegexes,
                           Status::InvalidArgument("unknown command 'not_a_mode'")));
   }
   {
+    const vector<string> kFsDumpModeRegexes = {
+        "cfile.*contents of a CFile",
+        "tree.*tree of a Kudu filesystem",
+        "uuid.*UUID of a Kudu filesystem"
+    };
+    NO_FATALS(RunTestHelp("fs dump", kFsDumpModeRegexes));
+
+  }
+  {
     const vector<string> kLocalReplicaModeRegexes = {
-        "cmeta.*consensus metadata file",
+        "cmeta.*Operate on a local Kudu replica's consensus",
+        "dump.*Dump a Kudu filesystem",
         "copy_from_remote.*Copy a replica",
-        "dump_wals.*Dump all WAL"
+        "list.*Show list of Kudu replicas"
     };
     NO_FATALS(RunTestHelp("local_replica", kLocalReplicaModeRegexes));
   }
   {
+    const vector<string> kLocalReplicaDumpModeRegexes = {
+        "block_ids.*Dump the IDs of all blocks",
+        "meta.*Dump the metadata",
+        "rowset.*Dump the rowset contents",
+        "wals.*Dump all WAL"
+    };
+    NO_FATALS(RunTestHelp("local_replica dump", kLocalReplicaDumpModeRegexes));
+
+  }
+  {
     const vector<string> kCmetaModeRegexes = {
         "print_replica_uuids.*Print all replica UUIDs",
         "rewrite_raft_config.*Rewrite a replica"
@@ -283,7 +316,7 @@ TEST_F(ToolTest, TestFsFormatWithUuid) {
   ASSERT_EQ(fs.uuid(), original_uuid);
 }
 
-TEST_F(ToolTest, TestFsPrintUuid) {
+TEST_F(ToolTest, TestFsDumpUuid) {
   const string kTestDir = GetTestPath("test");
   string uuid;
   {
@@ -294,7 +327,7 @@ TEST_F(ToolTest, TestFsPrintUuid) {
   }
   string stdout;
   NO_FATALS(RunActionStdoutString(Substitute(
-      "fs print_uuid --fs_wal_dir=$0", kTestDir), &stdout));
+      "fs dump uuid --fs_wal_dir=$0", kTestDir), &stdout));
   SCOPED_TRACE(stdout);
   ASSERT_EQ(uuid, stdout);
 }
@@ -354,13 +387,13 @@ TEST_F(ToolTest, TestFsDumpCFile) {
 
   {
     NO_FATALS(RunActionStdoutNone(Substitute(
-        "fs dump_cfile --fs_wal_dir=$0 $1 --noprint_meta --noprint_rows",
+        "fs dump cfile --fs_wal_dir=$0 $1 --noprint_meta --noprint_rows",
         kTestDir, block_id.ToString())));
   }
   vector<string> stdout;
   {
     NO_FATALS(RunActionStdoutLines(Substitute(
-        "fs dump_cfile --fs_wal_dir=$0 $1 --noprint_rows",
+        "fs dump cfile --fs_wal_dir=$0 $1 --noprint_rows",
         kTestDir, block_id.ToString()), &stdout));
     SCOPED_TRACE(stdout);
     ASSERT_GE(stdout.size(), 4);
@@ -369,14 +402,14 @@ TEST_F(ToolTest, TestFsDumpCFile) {
   }
   {
     NO_FATALS(RunActionStdoutLines(Substitute(
-        "fs dump_cfile --fs_wal_dir=$0 $1 --noprint_meta",
+        "fs dump cfile --fs_wal_dir=$0 $1 --noprint_meta",
         kTestDir, block_id.ToString()), &stdout));
     SCOPED_TRACE(stdout);
     ASSERT_EQ(kNumEntries, stdout.size());
   }
   {
     NO_FATALS(RunActionStdoutLines(Substitute(
-        "fs dump_cfile --fs_wal_dir=$0 $1",
+        "fs dump cfile --fs_wal_dir=$0 $1",
         kTestDir, block_id.ToString()), &stdout));
     SCOPED_TRACE(stdout);
     ASSERT_GT(stdout.size(), kNumEntries);
@@ -427,7 +460,7 @@ TEST_F(ToolTest, TestWalDump) {
   string wal_path = fs.GetWalSegmentFileName(kTestTablet, 1);
   string stdout;
   for (const auto& args : { Substitute("wal dump $0", wal_path),
-                            Substitute("local_replica dump_wals --fs_wal_dir=$0 $1",
+                            Substitute("local_replica dump wals --fs_wal_dir=$0 $1",
                                        kTestDir, kTestTablet)
                            }) {
     SCOPED_TRACE(args);
@@ -501,5 +534,192 @@ TEST_F(ToolTest, TestWalDump) {
   }
 }
 
+TEST_F(ToolTest, TestLocalReplicaDumpMeta) {
+  const string kTestDir = GetTestPath("test");
+  const string kTestTablet = "test-tablet";
+  const string kTestTableId = "test-table";
+  const string kTestTableName = "test-fs-meta-dump-table";
+  const Schema kSchema(GetSimpleTestSchema());
+  const Schema kSchemaWithIds(SchemaBuilder(kSchema).Build());
+
+  FsManager fs(env_.get(), kTestDir);
+  ASSERT_OK(fs.CreateInitialFileSystemLayout());
+  ASSERT_OK(fs.Open());
+
+  pair<PartitionSchema, Partition> partition = tablet::CreateDefaultPartition(
+        kSchemaWithIds);
+  scoped_refptr<TabletMetadata> meta;
+  TabletMetadata::CreateNew(&fs, kTestTablet, kTestTableName, kTestTableId,
+                  kSchemaWithIds, partition.first, partition.second,
+                  tablet::TABLET_DATA_READY, &meta);
+  string stdout;
+  NO_FATALS(RunActionStdoutString(Substitute("local_replica dump meta $0 "
+                                             "--fs_wal_dir=$1 "
+                                             "--fs_data_dirs=$2",
+                                             kTestTablet, kTestDir,
+                                             kTestDir), &stdout));
+
+  // Verify the contents of the metadata output
+  SCOPED_TRACE(stdout);
+  string debug_str = meta->partition_schema()
+      .PartitionDebugString(meta->partition(), meta->schema());
+  StripWhiteSpace(&debug_str);
+  ASSERT_STR_CONTAINS(stdout, debug_str);
+  debug_str = Substitute("Table name: $0 Table id: $1",
+                         meta->table_name(), meta->table_id());
+  ASSERT_STR_CONTAINS(stdout, debug_str);
+  debug_str = Substitute("Schema (version=$0):", meta->schema_version());
+  ASSERT_STR_CONTAINS(stdout, debug_str);
+  debug_str = meta->schema().ToString();
+  StripWhiteSpace(&debug_str);
+  ASSERT_STR_CONTAINS(stdout, debug_str);
+
+  TabletSuperBlockPB pb1;
+  meta->ToSuperBlock(&pb1);
+  debug_str = pb1.DebugString();
+  StripWhiteSpace(&debug_str);
+  ASSERT_STR_CONTAINS(stdout, "Superblock:");
+  ASSERT_STR_CONTAINS(stdout, debug_str);
+}
+
+TEST_F(ToolTest, TestFsDumpTree) {
+  const string kTestDir = GetTestPath("test");
+  const Schema kSchema(GetSimpleTestSchema());
+  const Schema kSchemaWithIds(SchemaBuilder(kSchema).Build());
+
+  FsManager fs(env_.get(), kTestDir);
+  ASSERT_OK(fs.CreateInitialFileSystemLayout());
+  ASSERT_OK(fs.Open());
+
+  string stdout;
+  NO_FATALS(RunActionStdoutString(Substitute("fs dump tree --fs_wal_dir=$0 "
+                                             "--fs_data_dirs=$1",
+                                             kTestDir, kTestDir), &stdout));
+
+  // It suffices to verify the contents of the top-level tree structure.
+  SCOPED_TRACE(stdout);
+  ostringstream tree_out;
+  fs.DumpFileSystemTree(tree_out);
+  string tree_out_str = tree_out.str();
+  StripWhiteSpace(&tree_out_str);
+  ASSERT_EQ(stdout, tree_out_str);
+}
+
+TEST_F(ToolTest, TestLocalReplicaOps) {
+  const string kTestDir = GetTestPath("test");
+  const string kTestTablet = "test-tablet";
+  const int kRowId = 100;
+  const Schema kSchema(GetSimpleTestSchema());
+  const Schema kSchemaWithIds(SchemaBuilder(kSchema).Build());
+
+  TabletHarness::Options opts(kTestDir);
+  opts.tablet_id = kTestTablet;
+  TabletHarness harness(kSchemaWithIds, opts);
+  ASSERT_OK(harness.Create(true));
+  ASSERT_OK(harness.Open());
+  LocalTabletWriter writer(harness.tablet().get(), &kSchema);
+  KuduPartialRow row(&kSchemaWithIds);
+  for (int i = 0; i< 10; i++) {
+    ASSERT_OK(row.SetInt32(0, i));
+    ASSERT_OK(row.SetInt32(1, i*10));
+    ASSERT_OK(row.SetStringCopy(2, "HelloWorld"));
+    writer.Insert(row);
+  }
+  harness.tablet()->Flush();
+  harness.tablet()->Shutdown();
+  string fs_paths = "--fs_wal_dir=" + kTestDir + " "
+      "--fs_data_dirs=" + kTestDir;
+  {
+    string stdout;
+    NO_FATALS(RunActionStdoutString(
+        Substitute("local_replica dump block_ids $0 $1",
+                   kTestTablet, fs_paths), &stdout));
+
+    SCOPED_TRACE(stdout);
+    string tablet_out = "Listing all data blocks in tablet " + kTestTablet;
+    ASSERT_STR_CONTAINS(stdout, tablet_out);
+    ASSERT_STR_CONTAINS(stdout, "Rowset ");
+    ASSERT_STR_MATCHES(stdout, "Column block for column ID .*");
+    ASSERT_STR_CONTAINS(stdout, "key[int32 NOT NULL]");
+    ASSERT_STR_CONTAINS(stdout, "int_val[int32 NOT NULL]");
+    ASSERT_STR_CONTAINS(stdout, "string_val[string NULLABLE]");
+  }
+  {
+    string stdout;
+    NO_FATALS(RunActionStdoutString(
+        Substitute("local_replica dump rowset $0 $1",
+                   kTestTablet, fs_paths), &stdout));
+
+    SCOPED_TRACE(stdout);
+    ASSERT_STR_CONTAINS(stdout, "Dumping rowset 0");
+    ASSERT_STR_MATCHES(stdout, "RowSet metadata: .*");
+    ASSERT_STR_MATCHES(stdout, "last_durable_dms_id: .*");
+    ASSERT_STR_CONTAINS(stdout, "columns {");
+    ASSERT_STR_CONTAINS(stdout, "block {");
+    ASSERT_STR_CONTAINS(stdout, "}");
+    ASSERT_STR_MATCHES(stdout, "column_id:.*");
+    ASSERT_STR_CONTAINS(stdout, "bloom_block {");
+    ASSERT_STR_MATCHES(stdout, "id: .*");
+    ASSERT_STR_CONTAINS(stdout, "undo_deltas {");
+    ASSERT_STR_MATCHES(stdout, "CFile Header: "
+                        "major_version: .* minor_version: .*");
+    ASSERT_STR_MATCHES(stdout, "Delta stats:.*");
+    ASSERT_STR_MATCHES(stdout, "ts range=.*");
+    ASSERT_STR_MATCHES(stdout, "update_counts_by_col_id=.*");
+    ASSERT_STR_MATCHES(stdout, "Dumping column block.*for column id.*");
+    ASSERT_STR_MATCHES(stdout, ".*---------------------.*");
+
+    // This is expected to fail with Invalid argument for kRowId.
+    string stderr;
+    Status s = RunTool(
+        Substitute("local_replica dump rowset $0 $1 --rowset_index=$2",
+                   kTestTablet, fs_paths, kRowId),
+                   &stdout, &stderr, nullptr, nullptr);
+    ASSERT_TRUE(s.IsRuntimeError());
+    SCOPED_TRACE(stderr);
+    string expected = "Could not find rowset " + SimpleItoa(kRowId) +
+        " in tablet id " + kTestTablet;
+    ASSERT_STR_CONTAINS(stderr, expected);
+  }
+  {
+    TabletMetadata* meta = harness.tablet()->metadata();
+    string stdout;
+    string debug_str;
+    NO_FATALS(RunActionStdoutString(
+        Substitute("local_replica dump meta $0 $1",
+                   kTestTablet, fs_paths), &stdout));
+
+    SCOPED_TRACE(stdout);
+    debug_str = meta->partition_schema()
+        .PartitionDebugString(meta->partition(), meta->schema());
+    StripWhiteSpace(&debug_str);
+    ASSERT_STR_CONTAINS(stdout, debug_str);
+    debug_str = Substitute("Table name: $0 Table id: $1",
+                           meta->table_name(), meta->table_id());
+    ASSERT_STR_CONTAINS(stdout, debug_str);
+    debug_str = Substitute("Schema (version=$0):", meta->schema_version());
+    StripWhiteSpace(&debug_str);
+    ASSERT_STR_CONTAINS(stdout, debug_str);
+    debug_str = meta->schema().ToString();
+    StripWhiteSpace(&debug_str);
+    ASSERT_STR_CONTAINS(stdout, debug_str);
+
+    TabletSuperBlockPB pb1;
+    meta->ToSuperBlock(&pb1);
+    debug_str = pb1.DebugString();
+    StripWhiteSpace(&debug_str);
+    ASSERT_STR_CONTAINS(stdout, "Superblock:");
+    ASSERT_STR_CONTAINS(stdout, debug_str);
+  }
+  {
+    string stdout;
+    NO_FATALS(RunActionStdoutString(Substitute("local_replica list $0",
+                                               fs_paths), &stdout));
+
+    SCOPED_TRACE(stdout);
+    ASSERT_STR_MATCHES(stdout, kTestTablet);
+  }
+}
+
 } // namespace tools
 } // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/9cb1bcac/src/kudu/tools/tool_action.h
----------------------------------------------------------------------
diff --git a/src/kudu/tools/tool_action.h b/src/kudu/tools/tool_action.h
index d9e3acf..c03e5d4 100644
--- a/src/kudu/tools/tool_action.h
+++ b/src/kudu/tools/tool_action.h
@@ -50,17 +50,20 @@ class Mode;
 //         root
 //          |
 //          |
-//          |
 //          fs
 //         |  |
 //      +--+  +--+
 //      |        |
-//   format   print_uuid
+//   format    dump
+//             | |
+//         +--+  +--+
+//         |        |
+//      cfile      uuid
 //
 // Given this tree:
 // - "<program> fs" will show all of fs's possible actions.
 // - "<program> fs format" will format a filesystem.
-// - "<program> fs print_uuid" will print a filesystem's UUID.
+// - "<program> fs dump uuid" will print a filesystem's UUID.
 
 // Builds a new mode (non-leaf) node.
 class ModeBuilder {

http://git-wip-us.apache.org/repos/asf/kudu/blob/9cb1bcac/src/kudu/tools/tool_action_fs.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tools/tool_action_fs.cc b/src/kudu/tools/tool_action_fs.cc
index a09a085..f64ecdb 100644
--- a/src/kudu/tools/tool_action_fs.cc
+++ b/src/kudu/tools/tool_action_fs.cc
@@ -29,13 +29,16 @@
 #include "kudu/fs/fs_manager.h"
 #include "kudu/gutil/strings/numbers.h"
 #include "kudu/gutil/strings/substitute.h"
+#include "kudu/tools/tool_action_common.h"
 #include "kudu/util/status.h"
 
 DECLARE_bool(print_meta);
 DEFINE_bool(print_rows, true,
             "Print each row in the CFile");
 DEFINE_string(uuid, "",
-              "The uuid to use in the filesystem. If not provided, one is generated");
+              "The uuid to use in the filesystem. "
+              "If not provided, one is generated");
+
 namespace kudu {
 namespace tools {
 
@@ -59,7 +62,7 @@ Status Format(const RunnerContext& context) {
   return fs_manager.CreateInitialFileSystemLayout(uuid);
 }
 
-Status PrintUuid(const RunnerContext& context) {
+Status DumpUuid(const RunnerContext& context) {
   FsManagerOpts opts;
   opts.read_only = true;
   FsManager fs_manager(Env::Default(), opts);
@@ -104,39 +107,64 @@ Status DumpCFile(const RunnerContext& context) {
   return Status::OK();
 }
 
+Status DumpFsTree(const RunnerContext& context) {
+  FsManagerOpts fs_opts;
+  fs_opts.read_only = true;
+  FsManager fs_manager(Env::Default(), fs_opts);
+  RETURN_NOT_OK(fs_manager.Open());
+
+  fs_manager.DumpFileSystemTree(std::cout);
+  return Status::OK();
+}
+
 } // anonymous namespace
 
-unique_ptr<Mode> BuildFsMode() {
-  unique_ptr<Action> format =
-      ActionBuilder("format", &Format)
-      .Description("Format a new Kudu filesystem")
+static unique_ptr<Mode> BuildFsDumpMode() {
+  unique_ptr<Action> dump_cfile =
+      ActionBuilder("cfile", &DumpCFile)
+      .Description("Dump the contents of a CFile (column file)")
+      .AddRequiredParameter({ "block_id", "block identifier" })
       .AddOptionalParameter("fs_wal_dir")
       .AddOptionalParameter("fs_data_dirs")
-      .AddOptionalParameter("uuid")
+      .AddOptionalParameter("print_meta")
+      .AddOptionalParameter("print_rows")
       .Build();
 
-  unique_ptr<Action> print_uuid =
-      ActionBuilder("print_uuid", &PrintUuid)
-      .Description("Print the UUID of a Kudu filesystem")
+  unique_ptr<Action> dump_tree =
+      ActionBuilder("tree", &DumpFsTree)
+      .Description("Dump the tree of a Kudu filesystem")
       .AddOptionalParameter("fs_wal_dir")
       .AddOptionalParameter("fs_data_dirs")
       .Build();
 
-  unique_ptr<Action> dump_cfile =
-      ActionBuilder("dump_cfile", &DumpCFile)
-      .Description("Dump the contents of a CFile (column file)")
-      .AddRequiredParameter({ "block_id", "block identifier" })
+  unique_ptr<Action> dump_uuid =
+      ActionBuilder("uuid", &DumpUuid)
+      .Description("Dump the UUID of a Kudu filesystem")
       .AddOptionalParameter("fs_wal_dir")
       .AddOptionalParameter("fs_data_dirs")
-      .AddOptionalParameter("print_meta")
-      .AddOptionalParameter("print_rows")
+      .Build();
+
+  return ModeBuilder("dump")
+      .Description("Dump a Kudu filesystem")
+      .AddAction(std::move(dump_cfile))
+      .AddAction(std::move(dump_tree))
+      .AddAction(std::move(dump_uuid))
+      .Build();
+}
+
+unique_ptr<Mode> BuildFsMode() {
+  unique_ptr<Action> format =
+      ActionBuilder("format", &Format)
+      .Description("Format a new Kudu filesystem")
+      .AddOptionalParameter("fs_wal_dir")
+      .AddOptionalParameter("fs_data_dirs")
+      .AddOptionalParameter("uuid")
       .Build();
 
   return ModeBuilder("fs")
       .Description("Operate on a local Kudu filesystem")
+      .AddMode(BuildFsDumpMode())
       .AddAction(std::move(format))
-      .AddAction(std::move(print_uuid))
-      .AddAction(std::move(dump_cfile))
       .Build();
 }
 

http://git-wip-us.apache.org/repos/asf/kudu/blob/9cb1bcac/src/kudu/tools/tool_action_local_replica.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tools/tool_action_local_replica.cc b/src/kudu/tools/tool_action_local_replica.cc
index d64059b..01986c2 100644
--- a/src/kudu/tools/tool_action_local_replica.cc
+++ b/src/kudu/tools/tool_action_local_replica.cc
@@ -23,34 +23,68 @@
 #include <string>
 #include <utility>
 
+#include "kudu/cfile/cfile_reader.h"
+#include "kudu/common/common.pb.h"
+#include "kudu/common/row_changelist.h"
+#include "kudu/common/row_operations.h"
+#include "kudu/common/rowblock.h"
+#include "kudu/common/schema.h"
 #include "kudu/common/wire_protocol.h"
 #include "kudu/consensus/consensus_meta.h"
+#include "kudu/consensus/consensus.pb.h"
 #include "kudu/consensus/log_index.h"
 #include "kudu/consensus/log_reader.h"
 #include "kudu/consensus/log_util.h"
 #include "kudu/fs/fs_manager.h"
 #include "kudu/gutil/map-util.h"
 #include "kudu/gutil/ref_counted.h"
+#include "kudu/gutil/strings/human_readable.h"
 #include "kudu/gutil/strings/join.h"
+#include "kudu/gutil/strings/numbers.h"
 #include "kudu/gutil/strings/split.h"
 #include "kudu/gutil/strings/stringpiece.h"
 #include "kudu/gutil/strings/substitute.h"
+#include "kudu/gutil/strings/util.h"
 #include "kudu/master/sys_catalog.h"
 #include "kudu/rpc/messenger.h"
+#include "kudu/tablet/cfile_set.h"
+#include "kudu/tablet/deltafile.h"
+#include "kudu/tablet/rowset_metadata.h"
+#include "kudu/tablet/tablet.h"
 #include "kudu/tools/tool_action_common.h"
 #include "kudu/tserver/tablet_copy_client.h"
+#include "kudu/tserver/tserver.pb.h"
 #include "kudu/util/env.h"
 #include "kudu/util/env_util.h"
+#include "kudu/util/logging.h"
+#include "kudu/util/mem_tracker.h"
+#include "kudu/util/memory/arena.h"
 #include "kudu/util/metrics.h"
 #include "kudu/util/net/net_util.h"
+#include "kudu/util/pb_util.h"
 #include "kudu/util/status.h"
 
+DEFINE_bool(metadata_only, false,
+            "Only dump the block metadata when printing blocks.");
+DEFINE_int64(nrows, 0, "Number of rows to dump");
+DEFINE_int64(rowset_index, -1,
+             "Index of the rowset in local replica, default value(-1) "
+             "will dump all the rowsets of the local replica");
+DEFINE_bool(verbose, false,
+            "Print additional information (if any)");
+
 namespace kudu {
 namespace tools {
 
+using cfile::CFileIterator;
+using cfile::CFileReader;
+using cfile::DumpIterator;
+using cfile::ReaderOptions;
 using consensus::ConsensusMetadata;
 using consensus::RaftConfigPB;
 using consensus::RaftPeerPB;
+using consensus::ReplicateMsg;
+using fs::ReadableBlock;
 using log::LogIndex;
 using log::LogReader;
 using log::ReadableLogSegment;
@@ -66,10 +100,36 @@ using std::unique_ptr;
 using std::vector;
 using strings::Split;
 using strings::Substitute;
+using tablet::CFileSet;
+using tablet::DeltaFileReader;
+using tablet::DeltaIterator;
+using tablet::DeltaKeyAndUpdate;
+using tablet::DeltaType;
+using tablet::MvccSnapshot;
+using tablet::RowSetMetadata;
+using tablet::Tablet;
+using tablet::TabletMetadata;
 using tserver::TabletCopyClient;
+using tserver::WriteRequestPB;
 
 namespace {
 
+static const char* const kSeparatorLine =
+  "----------------------------------------------------------------------\n";
+
+string Indent(int indent) {
+  return string(indent, ' ');
+}
+
+Status FsInit(unique_ptr<FsManager>* fs_manager) {
+  FsManagerOpts fs_opts;
+  fs_opts.read_only = true;
+  unique_ptr<FsManager> fs_ptr(new FsManager(Env::Default(), fs_opts));
+  RETURN_NOT_OK(fs_ptr->Open());
+  fs_manager->swap(fs_ptr);
+  return Status::OK();
+}
+
 // Parses a colon-delimited string containing a hostname or IP address and port
 // into its respective parts. For example, "localhost:12345" parses into
 // hostname=localhost, and port=12345.
@@ -108,17 +168,14 @@ Status ParsePeerString(const string& peer_str,
 }
 
 Status PrintReplicaUuids(const RunnerContext& context) {
+  unique_ptr<FsManager> fs_manager;
+  RETURN_NOT_OK(FsInit(&fs_manager));
   string tablet_id = FindOrDie(context.required_args, "tablet_id");
 
-  FsManagerOpts opts;
-  opts.read_only = true;
-  FsManager fs_manager(Env::Default(), opts);
-  RETURN_NOT_OK(fs_manager.Open());
-
   // Load the cmeta file and print all peer uuids.
   unique_ptr<ConsensusMetadata> cmeta;
-  RETURN_NOT_OK(ConsensusMetadata::Load(&fs_manager, tablet_id,
-                                        fs_manager.uuid(), &cmeta));
+  RETURN_NOT_OK(ConsensusMetadata::Load(fs_manager.get(), tablet_id,
+                                        fs_manager->uuid(), &cmeta));
   cout << JoinMapped(cmeta->committed_config().peers(),
                      [](const RaftPeerPB& p){ return p.permanent_uuid(); },
                      " ") << endl;
@@ -153,7 +210,8 @@ Status RewriteRaftConfig(const RunnerContext& context) {
   WritableFileOptions opts;
   opts.mode = Env::CREATE_NON_EXISTING;
   opts.sync_on_close = true;
-  RETURN_NOT_OK(env_util::CopyFile(env, cmeta_filename, backup_filename, opts));
+  RETURN_NOT_OK(env_util::CopyFile(env, cmeta_filename,
+                                   backup_filename, opts));
   LOG(INFO) << "Backed up current config to " << backup_filename;
 
   // Load the cmeta file and rewrite the raft config.
@@ -197,15 +255,12 @@ Status CopyFromRemote(const RunnerContext& context) {
 }
 
 Status DumpWals(const RunnerContext& context) {
+  unique_ptr<FsManager> fs_manager;
+  RETURN_NOT_OK(FsInit(&fs_manager));
   string tablet_id = FindOrDie(context.required_args, "tablet_id");
 
-  FsManagerOpts fs_opts;
-  fs_opts.read_only = true;
-  FsManager fs_manager(Env::Default(), fs_opts);
-  RETURN_NOT_OK(fs_manager.Open());
-
   shared_ptr<LogReader> reader;
-  RETURN_NOT_OK(LogReader::Open(&fs_manager,
+  RETURN_NOT_OK(LogReader::Open(fs_manager.get(),
                                 scoped_refptr<LogIndex>(),
                                 tablet_id,
                                 scoped_refptr<MetricEntity>(),
@@ -221,12 +276,397 @@ Status DumpWals(const RunnerContext& context) {
   return Status::OK();
 }
 
+Status ListBlocksInRowSet(const Schema& schema,
+                          const RowSetMetadata& rs_meta) {
+  RowSetMetadata::ColumnIdToBlockIdMap col_blocks =
+      rs_meta.GetColumnBlocksById();
+  for (const RowSetMetadata::ColumnIdToBlockIdMap::value_type& e :
+      col_blocks) {
+    ColumnId col_id = e.first;
+    const BlockId& block_id = e.second;
+    cout << "Column block for column ID " << col_id;
+    int col_idx = schema.find_column_by_id(col_id);
+    if (col_idx != -1) {
+      cout << " (" << schema.column(col_idx).ToString() << ")";
+    }
+    cout << ": ";
+    cout << block_id.ToString() << endl;
+  }
+
+  for (const BlockId& block : rs_meta.undo_delta_blocks()) {
+    cout << "UNDO: " << block.ToString() << endl;
+  }
+
+  for (const BlockId& block : rs_meta.redo_delta_blocks()) {
+    cout << "REDO: " << block.ToString() << endl;
+  }
+
+  return Status::OK();
+}
+
+Status DumpBlockIdsForLocalReplica(const RunnerContext& context) {
+  unique_ptr<FsManager> fs_manager;
+  RETURN_NOT_OK(FsInit(&fs_manager));
+  string tablet_id = FindOrDie(context.required_args, "tablet_id");
+
+  scoped_refptr<TabletMetadata> meta;
+  RETURN_NOT_OK(TabletMetadata::Load(fs_manager.get(), tablet_id, &meta));
+
+  if (meta->rowsets().empty()) {
+    cout << "No rowsets found on disk for tablet "
+         << tablet_id << endl;
+    return Status::OK();
+  }
+
+  cout << "Listing all data blocks in tablet "
+       << tablet_id << ":" << endl;
+
+  Schema schema = meta->schema();
+
+  size_t idx = 0;
+  for (const shared_ptr<RowSetMetadata>& rs_meta : meta->rowsets())  {
+    cout << "Rowset " << idx++ << endl;
+    RETURN_NOT_OK(ListBlocksInRowSet(schema, *rs_meta));
+  }
+
+  return Status::OK();
+}
+
+Status DumpTabletMeta(FsManager* fs_manager,
+                       const string& tablet_id, int indent) {
+  scoped_refptr<TabletMetadata> meta;
+  RETURN_NOT_OK(TabletMetadata::Load(fs_manager, tablet_id, &meta));
+
+  const Schema& schema = meta->schema();
+
+  cout << Indent(indent) << "Partition: "
+       << meta->partition_schema().PartitionDebugString(meta->partition(),
+                                                       meta->schema())
+       << endl;
+  cout << Indent(indent) << "Table name: " << meta->table_name()
+       << " Table id: " << meta->table_id() << endl;
+  cout << Indent(indent) << "Schema (version="
+       << meta->schema_version() << "): "
+       << schema.ToString() << endl;
+
+  tablet::TabletSuperBlockPB pb;
+  RETURN_NOT_OK_PREPEND(meta->ToSuperBlock(&pb), "Could not get superblock");
+  cout << "Superblock:\n" << pb.DebugString() << endl;
+
+  return Status::OK();
+}
+
+Status ListLocalReplicas(const RunnerContext& context) {
+  unique_ptr<FsManager> fs_manager;
+  RETURN_NOT_OK(FsInit(&fs_manager));
+
+  vector<string> tablets;
+  RETURN_NOT_OK(fs_manager->ListTabletIds(&tablets));
+  for (const string& tablet : tablets) {
+    if (FLAGS_verbose) {
+      cout << "Tablet: " << tablet << endl;
+      RETURN_NOT_OK(DumpTabletMeta(fs_manager.get(), tablet, 2));
+    } else {
+      cout << tablet << endl;
+    }
+  }
+  return Status::OK();
+}
+
+Status DumpCFileBlockInternal(FsManager* fs_manager,
+                              const BlockId& block_id,
+                              int indent) {
+  gscoped_ptr<ReadableBlock> block;
+  RETURN_NOT_OK(fs_manager->OpenBlock(block_id, &block));
+  gscoped_ptr<CFileReader> reader;
+  RETURN_NOT_OK(CFileReader::Open(std::move(block), ReaderOptions(), &reader));
+
+  cout << Indent(indent) << "CFile Header: "
+       << reader->header().ShortDebugString() << endl;
+  if (!FLAGS_verbose) {
+    return Status::OK();
+  }
+  cout << Indent(indent) << reader->footer().num_values()
+       << " values:" << endl;
+
+  gscoped_ptr<CFileIterator> it;
+  RETURN_NOT_OK(reader->NewIterator(&it, CFileReader::DONT_CACHE_BLOCK));
+  RETURN_NOT_OK(it->SeekToFirst());
+  return DumpIterator(*reader, it.get(), &cout, FLAGS_nrows, indent + 2);
+}
+
+Status DumpDeltaCFileBlockInternal(FsManager* fs_manager,
+                                   const Schema& schema,
+                                   const shared_ptr<RowSetMetadata>& rs_meta,
+                                   const BlockId& block_id,
+                                   DeltaType delta_type,
+                                   int indent) {
+  // Open the delta reader
+  gscoped_ptr<ReadableBlock> readable_block;
+  RETURN_NOT_OK(fs_manager->OpenBlock(block_id, &readable_block));
+  shared_ptr<DeltaFileReader> delta_reader;
+  RETURN_NOT_OK(DeltaFileReader::Open(std::move(readable_block),
+                                      block_id,
+                                      &delta_reader,
+                                      delta_type));
+
+  cout << Indent(indent) << "Delta stats: "
+       << delta_reader->delta_stats().ToString() << endl;
+  if (FLAGS_metadata_only) {
+    return Status::OK();
+  }
+
+  // Create the delta iterator.
+  // TODO: see if it's worth re-factoring NewDeltaIterator to return a
+  // gscoped_ptr that can then be released if we need a raw or shared
+  // pointer.
+  DeltaIterator* raw_iter;
+
+  MvccSnapshot snap_all;
+  if (delta_type == tablet::REDO) {
+    snap_all = MvccSnapshot::CreateSnapshotIncludingAllTransactions();
+  } else if (delta_type == tablet::UNDO) {
+    snap_all = MvccSnapshot::CreateSnapshotIncludingNoTransactions();
+  }
+
+  Status s = delta_reader->NewDeltaIterator(&schema, snap_all, &raw_iter);
+
+  if (s.IsNotFound()) {
+    cout << "Empty delta block." << endl;
+    return Status::OK();
+  }
+  RETURN_NOT_OK(s);
+
+  // NewDeltaIterator returns Status::OK() iff a new DeltaIterator is created. Thus,
+  // it's safe to have a unique_ptr take possesion of 'raw_iter' here.
+  unique_ptr<DeltaIterator> delta_iter(raw_iter);
+  RETURN_NOT_OK(delta_iter->Init(NULL));
+  RETURN_NOT_OK(delta_iter->SeekToOrdinal(0));
+
+  // TODO: it's awkward that whenever we want to iterate over deltas we also
+  // need to open the CFileSet for the rowset. Ideally, we should use
+  // information stored in the footer/store additional information in the
+  // footer as to make it feasible iterate over all deltas using a
+  // DeltaFileIterator alone.
+  shared_ptr<CFileSet> cfileset(new CFileSet(rs_meta));
+  RETURN_NOT_OK(cfileset->Open());
+  gscoped_ptr<CFileSet::Iterator> cfileset_iter(cfileset->NewIterator(&schema));
+
+  RETURN_NOT_OK(cfileset_iter->Init(NULL));
+
+  const size_t kRowsPerBlock  = 100;
+  size_t nrows = 0;
+  size_t ndeltas = 0;
+  Arena arena(32 * 1024, 128 * 1024);
+  RowBlock block(schema, kRowsPerBlock, &arena);
+
+  // See tablet/delta_compaction.cc to understand why this loop is structured the way
+  // it is.
+  while (cfileset_iter->HasNext()) {
+    size_t n;
+    if (FLAGS_nrows > 0) {
+      // Note: number of deltas may not equal the number of rows, but
+      // since this is a CLI tool (and the nrows option exists
+      // primarily to limit copious output) it's okay not to be
+      // exact here.
+      size_t remaining = FLAGS_nrows - nrows;
+      if (remaining == 0) break;
+      n = std::min(remaining, kRowsPerBlock);
+    } else {
+      n = kRowsPerBlock;
+    }
+
+    arena.Reset();
+    cfileset_iter->PrepareBatch(&n);
+
+    block.Resize(n);
+
+    RETURN_NOT_OK(delta_iter->PrepareBatch(
+        n, DeltaIterator::PREPARE_FOR_COLLECT));
+    vector<DeltaKeyAndUpdate> out;
+    RETURN_NOT_OK(
+        delta_iter->FilterColumnIdsAndCollectDeltas(vector<ColumnId>(),
+                                                              &out,
+                                                              &arena));
+    for (const DeltaKeyAndUpdate& upd : out) {
+      if (FLAGS_verbose) {
+        cout << Indent(indent) << upd.key.ToString() << " "
+             << RowChangeList(upd.cell).ToString(schema) << endl;
+        ++ndeltas;
+      }
+    }
+    RETURN_NOT_OK(cfileset_iter->FinishBatch());
+
+    nrows += n;
+  }
+
+  VLOG(1) << "Processed " << ndeltas << " deltas, for total of "
+          << nrows << " possible rows.";
+  return Status::OK();
+}
+
+Status DumpRowSetInternal(FsManager* fs_manager,
+                          const Schema& schema,
+                          const shared_ptr<RowSetMetadata>& rs_meta,
+                          int indent) {
+  tablet::RowSetDataPB pb;
+  rs_meta->ToProtobuf(&pb);
+
+  cout << Indent(indent) << "RowSet metadata: " << pb.DebugString()
+       << endl << endl;
+
+  RowSetMetadata::ColumnIdToBlockIdMap col_blocks =
+      rs_meta->GetColumnBlocksById();
+  for (const RowSetMetadata::ColumnIdToBlockIdMap::value_type& e :
+      col_blocks) {
+    ColumnId col_id = e.first;
+    const BlockId& block_id = e.second;
+
+    cout << Indent(indent) << "Dumping column block " << block_id
+         << " for column id " << col_id;
+    int col_idx = schema.find_column_by_id(col_id);
+    if (col_idx != -1) {
+      cout << "( " << schema.column(col_idx).ToString() <<  ")";
+    }
+    cout << ":" << endl;
+    cout << Indent(indent) << kSeparatorLine;
+    if (FLAGS_metadata_only) continue;
+    RETURN_NOT_OK(DumpCFileBlockInternal(fs_manager, block_id, indent));
+    cout << endl;
+  }
+
+  for (const BlockId& block : rs_meta->undo_delta_blocks()) {
+    cout << Indent(indent) << "Dumping undo delta block " << block << ":"
+         << endl << Indent(indent) << kSeparatorLine;
+    RETURN_NOT_OK(DumpDeltaCFileBlockInternal(fs_manager,
+                                              schema,
+                                              rs_meta,
+                                              block,
+                                              tablet::UNDO,
+                                              indent));
+    cout << endl;
+  }
+
+  for (const BlockId& block : rs_meta->redo_delta_blocks()) {
+    cout << Indent(indent) << "Dumping redo delta block " << block << ":"
+         << endl << Indent(indent) << kSeparatorLine;
+    RETURN_NOT_OK(DumpDeltaCFileBlockInternal(fs_manager,
+                                              schema,
+                                              rs_meta,
+                                              block,
+                                              tablet::REDO,
+                                              indent));
+    cout << endl;
+  }
+
+  return Status::OK();
+}
+
+Status DumpRowSet(const RunnerContext& context) {
+  unique_ptr<FsManager> fs_manager;
+  RETURN_NOT_OK(FsInit(&fs_manager));
+  string tablet_id = FindOrDie(context.required_args, "tablet_id");
+
+  scoped_refptr<TabletMetadata> meta;
+  RETURN_NOT_OK(TabletMetadata::Load(fs_manager.get(), tablet_id, &meta));
+  if (meta->rowsets().empty()) {
+    cout << Indent(0) << "No rowsets found on disk for tablet "
+         << tablet_id << endl;
+    return Status::OK();
+  }
+
+  // If rowset index is provided, only dump that rowset.
+  if (FLAGS_rowset_index != -1) {
+    bool found = false;
+    for (const shared_ptr<RowSetMetadata>& rs_meta : meta->rowsets())  {
+      if (rs_meta->id() == FLAGS_rowset_index) {
+        found = true;
+        return DumpRowSetInternal(fs_manager.get(), meta->schema(),
+                                  rs_meta, 0);
+      }
+    }
+    if (!found) {
+      return Status::InvalidArgument(
+          Substitute("Could not find rowset $0 in tablet id $1",
+                     FLAGS_rowset_index, tablet_id));
+    }
+  } else {
+    // Rowset index not provided, dump all rowsets
+    size_t idx = 0;
+    for (const shared_ptr<RowSetMetadata>& rs_meta : meta->rowsets())  {
+      cout << endl << "Dumping rowset " << idx++ << endl << kSeparatorLine;
+      RETURN_NOT_OK(DumpRowSetInternal(fs_manager.get(), meta->schema(),
+                                       rs_meta, 2));
+    }
+  }
+  return Status::OK();
+}
+
+Status DumpMeta(const RunnerContext& context) {
+  unique_ptr<FsManager> fs_manager;
+  RETURN_NOT_OK(FsInit(&fs_manager));
+  string tablet_id = FindOrDie(context.required_args, "tablet_id");
+  RETURN_NOT_OK(DumpTabletMeta(fs_manager.get(), tablet_id, 0));
+  return Status::OK();
+}
+
+unique_ptr<Mode> BuildDumpMode() {
+  unique_ptr<Action> dump_block_ids =
+      ActionBuilder("block_ids", &DumpBlockIdsForLocalReplica)
+      .Description("Dump the IDs of all blocks belonging to a local replica")
+      .AddRequiredParameter({ "tablet_id", "tablet identifier" })
+      .AddOptionalParameter("fs_wal_dir")
+      .AddOptionalParameter("fs_data_dirs")
+      .Build();
+
+  unique_ptr<Action> dump_meta =
+      ActionBuilder("meta", &DumpMeta)
+      .Description("Dump the metadata of a local replica")
+      .AddRequiredParameter({ "tablet_id", "tablet identifier" })
+      .AddOptionalParameter("fs_wal_dir")
+      .AddOptionalParameter("fs_data_dirs")
+      .Build();
+
+  unique_ptr<Action> dump_rowset =
+      ActionBuilder("rowset", &DumpRowSet)
+      .Description("Dump the rowset contents of a local replica")
+      .AddRequiredParameter({ "tablet_id", "tablet identifier" })
+      .AddOptionalParameter("fs_wal_dir")
+      .AddOptionalParameter("fs_data_dirs")
+      .AddOptionalParameter("metadata_only")
+      .AddOptionalParameter("nrows")
+      .AddOptionalParameter("rowset_index")
+      .AddOptionalParameter("verbose")
+      .Build();
+
+  unique_ptr<Action> dump_wals =
+      ActionBuilder("wals", &DumpWals)
+      .Description("Dump all WAL (write-ahead log) segments of "
+        "a local replica")
+      .AddRequiredParameter({ "tablet_id", "Tablet identifier" })
+      .AddOptionalParameter("fs_wal_dir")
+      .AddOptionalParameter("fs_data_dirs")
+      .AddOptionalParameter("print_entries")
+      .AddOptionalParameter("print_meta")
+      .AddOptionalParameter("truncate_data")
+      .Build();
+
+  return ModeBuilder("dump")
+      .Description("Dump a Kudu filesystem")
+      .AddAction(std::move(dump_block_ids))
+      .AddAction(std::move(dump_meta))
+      .AddAction(std::move(dump_rowset))
+      .AddAction(std::move(dump_wals))
+      .Build();
+}
+
 } // anonymous namespace
 
 unique_ptr<Mode> BuildLocalReplicaMode() {
   unique_ptr<Action> print_replica_uuids =
       ActionBuilder("print_replica_uuids", &PrintReplicaUuids)
-      .Description("Print all replica UUIDs found in a tablet's Raft configuration")
+      .Description("Print all replica UUIDs found in a "
+        "tablet's Raft configuration")
       .AddRequiredParameter({ "tablet_id", "Tablet identifier" })
       .AddOptionalParameter("fs_wal_dir")
       .AddOptionalParameter("fs_data_dirs")
@@ -237,14 +677,16 @@ unique_ptr<Mode> BuildLocalReplicaMode() {
       .Description("Rewrite a replica's Raft configuration")
       .AddRequiredParameter({ "tablet_id", "Tablet identifier" })
       .AddRequiredVariadicParameter({
-        "peers", "List of peers where each peer is of form 'uuid:hostname:port'" })
+        "peers", "List of peers where each peer is of "
+        "form 'uuid:hostname:port'" })
       .AddOptionalParameter("fs_wal_dir")
       .AddOptionalParameter("fs_data_dirs")
       .Build();
 
   unique_ptr<Mode> cmeta =
       ModeBuilder("cmeta")
-      .Description("Operate on a local Kudu tablet's consensus metadata file")
+      .Description("Operate on a local Kudu replica's consensus "
+        "metadata file")
       .AddAction(std::move(print_replica_uuids))
       .AddAction(std::move(rewrite_raft_config))
       .Build();
@@ -253,27 +695,26 @@ unique_ptr<Mode> BuildLocalReplicaMode() {
       ActionBuilder("copy_from_remote", &CopyFromRemote)
       .Description("Copy a replica from a remote server")
       .AddRequiredParameter({ "tablet_id", "Tablet identifier" })
-      .AddRequiredParameter({ "source", "Source RPC address of form hostname:port" })
+      .AddRequiredParameter({ "source", "Source RPC address of "
+        "form hostname:port" })
       .AddOptionalParameter("fs_wal_dir")
       .AddOptionalParameter("fs_data_dirs")
       .Build();
 
-  unique_ptr<Action> dump_wals =
-      ActionBuilder("dump_wals", &DumpWals)
-      .Description("Dump all WAL (write-ahead log) segments of a tablet")
-      .AddRequiredParameter({ "tablet_id", "Tablet identifier" })
+  unique_ptr<Action> list =
+      ActionBuilder("list", &ListLocalReplicas)
+      .Description("Show list of Kudu replicas in the local filesystem")
       .AddOptionalParameter("fs_wal_dir")
       .AddOptionalParameter("fs_data_dirs")
-      .AddOptionalParameter("print_entries")
-      .AddOptionalParameter("print_meta")
-      .AddOptionalParameter("truncate_data")
+      .AddOptionalParameter("verbose")
       .Build();
 
   return ModeBuilder("local_replica")
       .Description("Operate on local Kudu replicas via the local filesystem")
       .AddMode(std::move(cmeta))
       .AddAction(std::move(copy_from_remote))
-      .AddAction(std::move(dump_wals))
+      .AddAction(std::move(list))
+      .AddMode(BuildDumpMode())
       .Build();
 }
 

http://git-wip-us.apache.org/repos/asf/kudu/blob/9cb1bcac/src/kudu/tools/tool_action_tablet.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tools/tool_action_tablet.cc b/src/kudu/tools/tool_action_tablet.cc
index 66ce494..8ed0039 100644
--- a/src/kudu/tools/tool_action_tablet.cc
+++ b/src/kudu/tools/tool_action_tablet.cc
@@ -176,7 +176,6 @@ Status RemoveReplica(const RunnerContext& context) {
   return ChangeConfig(context, consensus::REMOVE_SERVER);
 }
 
-
 } // anonymous namespace
 
 unique_ptr<Mode> BuildTabletMode() {


[3/3] kudu git commit: tool: port ts-cli

Posted by to...@apache.org.
tool: port ts-cli

I chose to expose common server functionality in new 'master' and 'tserver'
modes rather than consolidating them into a shared 'server' mode; I found
this to be more more intuitive.

I also snuck in a change to ksck to use FLAGS_timeout_ms for RPC timeouts
in client-based operations.

Change-Id: Ifb5a59fd690c2dd09e4e76858469d81f9d501371
Reviewed-on: http://gerrit.cloudera.org:8080/4373
Tested-by: Kudu Jenkins
Reviewed-by: Todd Lipcon <to...@apache.org>


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

Branch: refs/heads/master
Commit: 14cd22a10b33545c5136ad68ebcf5cc0543ac525
Parents: 9cb1bca
Author: Adar Dembo <ad...@cloudera.com>
Authored: Sun Sep 11 15:13:23 2016 -0700
Committer: Todd Lipcon <to...@apache.org>
Committed: Tue Sep 13 02:14:29 2016 +0000

----------------------------------------------------------------------
 build-support/dist_test.py                   |   1 -
 docs/release_notes.adoc                      |   3 +
 src/kudu/client/scan_batch.h                 |   4 +-
 src/kudu/client/schema.h                     |   6 +-
 src/kudu/tablet/tablet_peer.h                |   4 +-
 src/kudu/tools/CMakeLists.txt                |  10 +-
 src/kudu/tools/ksck_remote.cc                |   1 +
 src/kudu/tools/kudu-tool-test.cc             |  28 ++
 src/kudu/tools/kudu-ts-cli-test.cc           |  14 +-
 src/kudu/tools/tool_action.h                 |   3 +
 src/kudu/tools/tool_action_common.cc         | 132 ++++++
 src/kudu/tools/tool_action_common.h          |  40 ++
 src/kudu/tools/tool_action_master.cc         |  97 +++++
 src/kudu/tools/tool_action_remote_replica.cc | 331 +++++++++++++++
 src/kudu/tools/tool_action_tablet.cc         |  11 +-
 src/kudu/tools/tool_action_tserver.cc        |  98 +++++
 src/kudu/tools/tool_main.cc                  |   3 +
 src/kudu/tools/ts-cli.cc                     | 494 ----------------------
 18 files changed, 758 insertions(+), 522 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/kudu/blob/14cd22a1/build-support/dist_test.py
----------------------------------------------------------------------
diff --git a/build-support/dist_test.py b/build-support/dist_test.py
index d19a78f..003a5dd 100755
--- a/build-support/dist_test.py
+++ b/build-support/dist_test.py
@@ -69,7 +69,6 @@ DEPS_FOR_ALL = \
      # TODO: declare these dependencies per-test.
      "build/latest/bin/kudu-tserver",
      "build/latest/bin/kudu-master",
-     "build/latest/bin/kudu-ts-cli",
 
      # parser-test requires these data files.
      # TODO: again, we should do this with some per-test metadata file.

http://git-wip-us.apache.org/repos/asf/kudu/blob/14cd22a1/docs/release_notes.adoc
----------------------------------------------------------------------
diff --git a/docs/release_notes.adoc b/docs/release_notes.adoc
index 9fd83ef..0261d99 100644
--- a/docs/release_notes.adoc
+++ b/docs/release_notes.adoc
@@ -130,6 +130,9 @@ Kudu 1.0.0 are not supported.
 - The `kudu-fs_dump` tool has been removed. The same functionality is now
   implemented as `kudu fs dump`.
 
+- The `kudu-ts-cli` tool has been removed. The same functionality is now
+  implemented within `kudu master`, `kudu remote_replica`, and `kudu tserver`.
+
 - The `kudu-fs_list` tool has been removed and some similar useful
   functionality has been moved under 'kudu local_replica'.
 

http://git-wip-us.apache.org/repos/asf/kudu/blob/14cd22a1/src/kudu/client/scan_batch.h
----------------------------------------------------------------------
diff --git a/src/kudu/client/scan_batch.h b/src/kudu/client/scan_batch.h
index 9b1433d..b4d91f6 100644
--- a/src/kudu/client/scan_batch.h
+++ b/src/kudu/client/scan_batch.h
@@ -33,7 +33,7 @@ namespace kudu {
 class Schema;
 
 namespace tools {
-class TsAdminClient;
+class ReplicaDumper;
 } // namespace tools
 
 namespace client {
@@ -119,7 +119,7 @@ class KUDU_EXPORT KuduScanBatch {
  private:
   class KUDU_NO_EXPORT Data;
   friend class KuduScanner;
-  friend class kudu::tools::TsAdminClient;
+  friend class tools::ReplicaDumper;
 
   Data* data_;
   DISALLOW_COPY_AND_ASSIGN(KuduScanBatch);

http://git-wip-us.apache.org/repos/asf/kudu/blob/14cd22a1/src/kudu/client/schema.h
----------------------------------------------------------------------
diff --git a/src/kudu/client/schema.h b/src/kudu/client/schema.h
index 70ba676..a09a88d 100644
--- a/src/kudu/client/schema.h
+++ b/src/kudu/client/schema.h
@@ -33,7 +33,7 @@ class TestWorkload;
 
 namespace tools {
 class RemoteKsckMaster;
-class TsAdminClient;
+class ReplicaDumper;
 }
 
 namespace client {
@@ -490,8 +490,8 @@ class KUDU_EXPORT KuduSchema {
   friend class internal::LookupRpc;
   friend class internal::MetaCacheEntry;
   friend class internal::WriteRpc;
-  friend class kudu::tools::RemoteKsckMaster;
-  friend class kudu::tools::TsAdminClient;
+  friend class tools::RemoteKsckMaster;
+  friend class tools::ReplicaDumper;
 
   friend KuduSchema KuduSchemaFromSchema(const Schema& schema);
 

http://git-wip-us.apache.org/repos/asf/kudu/blob/14cd22a1/src/kudu/tablet/tablet_peer.h
----------------------------------------------------------------------
diff --git a/src/kudu/tablet/tablet_peer.h b/src/kudu/tablet/tablet_peer.h
index 8d72e72..c43c83a 100644
--- a/src/kudu/tablet/tablet_peer.h
+++ b/src/kudu/tablet/tablet_peer.h
@@ -196,9 +196,7 @@ class TabletPeer : public RefCountedThreadSafe<TabletPeer>,
   // etc. For use in places like the Web UI.
   std::string HumanReadableState() const;
 
-  // Adds list of transactions in-flight at the time of the call to
-  // 'out'. TransactionStatusPB objects are used to allow this method
-  // to be used by both the web-UI and ts-cli.
+  // Adds list of transactions in-flight at the time of the call to 'out'.
   void GetInFlightTransactions(Transaction::TraceType trace_type,
                                std::vector<consensus::TransactionStatusPB>* out) const;
 

http://git-wip-us.apache.org/repos/asf/kudu/blob/14cd22a1/src/kudu/tools/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/src/kudu/tools/CMakeLists.txt b/src/kudu/tools/CMakeLists.txt
index 0ce2899..54159dc 100644
--- a/src/kudu/tools/CMakeLists.txt
+++ b/src/kudu/tools/CMakeLists.txt
@@ -44,10 +44,6 @@ target_link_libraries(insert-generated-rows
   kudu_tools_util
   ${LINK_LIBS})
 
-add_executable(kudu-ts-cli ts-cli.cc)
-target_link_libraries(kudu-ts-cli
-  ${LINK_LIBS})
-
 add_library(ksck
     ksck.cc
     ksck_remote.cc
@@ -68,9 +64,12 @@ add_executable(kudu
   tool_action_common.cc
   tool_action_fs.cc
   tool_action_local_replica.cc
+  tool_action_master.cc
   tool_action_pbc.cc
+  tool_action_remote_replica.cc
   tool_action_table.cc
   tool_action_tablet.cc
+  tool_action_tserver.cc
   tool_action_wal.cc
   tool_main.cc
 )
@@ -79,6 +78,7 @@ target_link_libraries(kudu
   gutil
   krpc
   ksck
+  kudu_client
   kudu_common
   kudu_fs
   kudu_util
@@ -105,5 +105,5 @@ ADD_KUDU_TEST_DEPENDENCIES(kudu-tool-test
   kudu)
 ADD_KUDU_TEST(kudu-ts-cli-test)
 ADD_KUDU_TEST_DEPENDENCIES(kudu-ts-cli-test
-  kudu-ts-cli)
+  kudu)
 

http://git-wip-us.apache.org/repos/asf/kudu/blob/14cd22a1/src/kudu/tools/ksck_remote.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tools/ksck_remote.cc b/src/kudu/tools/ksck_remote.cc
index c2cc85c..1dddfec 100644
--- a/src/kudu/tools/ksck_remote.cc
+++ b/src/kudu/tools/ksck_remote.cc
@@ -262,6 +262,7 @@ void RemoteKsckTabletServer::RunTabletChecksumScanAsync(
 Status RemoteKsckMaster::Connect() {
   client::sp::shared_ptr<KuduClient> client;
   KuduClientBuilder builder;
+  builder.default_rpc_timeout(GetDefaultTimeout());
   builder.master_server_addrs(master_addresses_);
   return builder.Build(&client_);
 }

http://git-wip-us.apache.org/repos/asf/kudu/blob/14cd22a1/src/kudu/tools/kudu-tool-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tools/kudu-tool-test.cc b/src/kudu/tools/kudu-tool-test.cc
index 9720d4a..1db59df 100644
--- a/src/kudu/tools/kudu-tool-test.cc
+++ b/src/kudu/tools/kudu-tool-test.cc
@@ -181,9 +181,12 @@ TEST_F(ToolTest, TestTopLevelHelp) {
       "cluster.*Kudu cluster",
       "fs.*Kudu filesystem",
       "local_replica.*Kudu replicas",
+      "master.*Kudu Master",
       "pbc.*protobuf container",
+      "remote_replica.*replicas on a Kudu Tablet Server",
       "table.*Kudu tables",
       "tablet.*Kudu tablets",
+      "tserver.*Kudu Tablet Server",
       "wal.*write-ahead log"
   };
   NO_FATALS(RunTestHelp("", kTopLevelRegexes));
@@ -244,12 +247,29 @@ TEST_F(ToolTest, TestModeHelp) {
     NO_FATALS(RunTestHelp("cluster", kClusterModeRegexes));
   }
   {
+    const vector<string> kMasterModeRegexes = {
+        "set_flag.*Change a gflag value",
+        "status.*Get the status",
+        "timestamp.*Get the current timestamp"
+    };
+    NO_FATALS(RunTestHelp("master", kMasterModeRegexes));
+  }
+  {
     const vector<string> kPbcModeRegexes = {
         "dump.*Dump a PBC",
     };
     NO_FATALS(RunTestHelp("pbc", kPbcModeRegexes));
   }
   {
+    const vector<string> kRemoteReplicaModeRegexes = {
+        "check.*Check if all replicas",
+        "delete.*Delete a replica",
+        "dump.*Dump the data of a replica",
+        "list.*List all replicas"
+    };
+    NO_FATALS(RunTestHelp("remote_replica", kRemoteReplicaModeRegexes));
+  }
+  {
     const vector<string> kTableModeRegexes = {
         "delete.*Delete a table",
         "list.*List all tables",
@@ -271,6 +291,14 @@ TEST_F(ToolTest, TestModeHelp) {
     NO_FATALS(RunTestHelp("tablet change_config", kChangeConfigModeRegexes));
   }
   {
+    const vector<string> kTServerModeRegexes = {
+        "set_flag.*Change a gflag value",
+        "status.*Get the status",
+        "timestamp.*Get the current timestamp"
+    };
+    NO_FATALS(RunTestHelp("tserver", kTServerModeRegexes));
+  }
+  {
     const vector<string> kWalModeRegexes = {
         "dump.*Dump a WAL",
     };

http://git-wip-us.apache.org/repos/asf/kudu/blob/14cd22a1/src/kudu/tools/kudu-ts-cli-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tools/kudu-ts-cli-test.cc b/src/kudu/tools/kudu-ts-cli-test.cc
index 6300781..cbcf190 100644
--- a/src/kudu/tools/kudu-ts-cli-test.cc
+++ b/src/kudu/tools/kudu-ts-cli-test.cc
@@ -36,7 +36,7 @@ using strings::Substitute;
 namespace kudu {
 namespace tools {
 
-static const char* const kTsCliToolName = "kudu-ts-cli";
+static const char* const kTsCliToolName = "kudu";
 
 class KuduTsCliTest : public ExternalMiniClusterITestBase {
  protected:
@@ -78,9 +78,9 @@ TEST_F(KuduTsCliTest, TestDeleteTablet) {
   string out;
   ASSERT_OK(Subprocess::Call({
     GetTsCliToolPath(),
-    "--server_address",
+    "remote_replica",
+    "delete",
     cluster_->tablet_server(0)->bound_rpc_addr().ToString(),
-    "delete_tablet",
     tablet_id,
     "Deleting for kudu-ts-cli-test"
   }, &out));
@@ -117,9 +117,9 @@ TEST_F(KuduTsCliTest, TestDumpTablet) {
   // Test for dump_tablet when there is no data in tablet.
   ASSERT_OK(Subprocess::Call({
     GetTsCliToolPath(),
-    "--server_address",
+    "remote_replica",
+    "dump",
     cluster_->tablet_server(0)->bound_rpc_addr().ToString(),
-    "dump_tablet",
     tablet_id
   }, &out));
   ASSERT_EQ("", out);
@@ -133,9 +133,9 @@ TEST_F(KuduTsCliTest, TestDumpTablet) {
   ASSERT_OK(WaitForServersToAgree(timeout, ts_map_, tablet_id, workload.batches_completed()));
   ASSERT_OK(Subprocess::Call({
     GetTsCliToolPath(),
-    "--server_address",
+    "remote_replica",
+    "dump",
     cluster_->tablet_server(0)->bound_rpc_addr().ToString(),
-    "dump_tablet",
     tablet_id
   }, &out));
 

http://git-wip-us.apache.org/repos/asf/kudu/blob/14cd22a1/src/kudu/tools/tool_action.h
----------------------------------------------------------------------
diff --git a/src/kudu/tools/tool_action.h b/src/kudu/tools/tool_action.h
index c03e5d4..95aa162 100644
--- a/src/kudu/tools/tool_action.h
+++ b/src/kudu/tools/tool_action.h
@@ -278,9 +278,12 @@ class Action {
 std::unique_ptr<Mode> BuildClusterMode();
 std::unique_ptr<Mode> BuildFsMode();
 std::unique_ptr<Mode> BuildLocalReplicaMode();
+std::unique_ptr<Mode> BuildMasterMode();
 std::unique_ptr<Mode> BuildPbcMode();
+std::unique_ptr<Mode> BuildRemoteReplicaMode();
 std::unique_ptr<Mode> BuildTableMode();
 std::unique_ptr<Mode> BuildTabletMode();
+std::unique_ptr<Mode> BuildTServerMode();
 std::unique_ptr<Mode> BuildWalMode();
 
 } // namespace tools

http://git-wip-us.apache.org/repos/asf/kudu/blob/14cd22a1/src/kudu/tools/tool_action_common.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tools/tool_action_common.cc b/src/kudu/tools/tool_action_common.cc
index 1daa12d..bbcf981 100644
--- a/src/kudu/tools/tool_action_common.cc
+++ b/src/kudu/tools/tool_action_common.cc
@@ -18,6 +18,7 @@
 #include "kudu/tools/tool_action_common.h"
 
 #include <iostream>
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -29,16 +30,32 @@
 #include "kudu/common/row_operations.h"
 #include "kudu/common/wire_protocol.h"
 #include "kudu/consensus/consensus.pb.h"
+#include "kudu/consensus/consensus.proxy.h"
 #include "kudu/consensus/log.pb.h"
 #include "kudu/consensus/log_util.h"
 #include "kudu/gutil/ref_counted.h"
 #include "kudu/gutil/strings/numbers.h"
+#include "kudu/rpc/messenger.h"
+#include "kudu/rpc/rpc_controller.h"
 #include "kudu/rpc/rpc_header.pb.h"
+#include "kudu/server/server_base.pb.h"
+#include "kudu/server/server_base.proxy.h"
 #include "kudu/tserver/tserver.pb.h"
+#include "kudu/tserver/tserver_admin.proxy.h"
+#include "kudu/tserver/tserver_service.proxy.h"
 #include "kudu/util/memory/arena.h"
+#include "kudu/util/monotime.h"
+#include "kudu/util/net/net_util.h"
+#include "kudu/util/net/sockaddr.h"
 #include "kudu/util/pb_util.h"
 #include "kudu/util/status.h"
 
+DECLARE_int64(timeout_ms); // defined in ksck
+
+DEFINE_bool(force, false, "If true, allows the set_flag command to set a flag "
+            "which is not explicitly marked as runtime-settable. Such flag "
+            "changes may be simply ignored on the server, or may cause the "
+            "server to crash.");
 DEFINE_bool(print_meta, true, "Include metadata in output");
 DEFINE_string(print_entries, "decoded",
               "How to print entries:\n"
@@ -53,16 +70,32 @@ DEFINE_int32(truncate_data, 100,
 namespace kudu {
 namespace tools {
 
+using consensus::ConsensusServiceProxy;
 using consensus::ReplicateMsg;
 using log::LogEntryPB;
 using log::LogEntryReader;
 using log::ReadableLogSegment;
+using rpc::Messenger;
+using rpc::MessengerBuilder;
 using rpc::RequestIdPB;
+using rpc::RpcController;
+using server::GenericServiceProxy;
+using server::GetStatusRequestPB;
+using server::GetStatusResponsePB;
+using server::ServerClockRequestPB;
+using server::ServerClockResponsePB;
+using server::ServerStatusPB;
+using server::SetFlagRequestPB;
+using server::SetFlagResponsePB;
 using std::cout;
 using std::cerr;
 using std::endl;
+using std::shared_ptr;
 using std::string;
+using std::unique_ptr;
 using std::vector;
+using tserver::TabletServerAdminServiceProxy;
+using tserver::TabletServerServiceProxy;
 using tserver::WriteRequestPB;
 
 namespace {
@@ -170,6 +203,55 @@ Status PrintDecoded(const LogEntryPB& entry, const Schema& tablet_schema) {
 
 } // anonymous namespace
 
+template<class ProxyClass>
+Status BuildProxy(const string& address,
+                  uint16_t default_port,
+                  unique_ptr<ProxyClass>* proxy) {
+  HostPort hp;
+  RETURN_NOT_OK(hp.ParseString(address, default_port));
+  shared_ptr<Messenger> messenger;
+  RETURN_NOT_OK(MessengerBuilder("tool").Build(&messenger));
+
+  vector<Sockaddr> resolved;
+  RETURN_NOT_OK(hp.ResolveAddresses(&resolved));
+
+  proxy->reset(new ProxyClass(messenger, resolved[0]));
+  return Status::OK();
+}
+
+// Explicit specialization for callers outside this compilation unit.
+template
+Status BuildProxy(const string& address,
+                  uint16_t default_port,
+                  unique_ptr<ConsensusServiceProxy>* proxy);
+template
+Status BuildProxy(const string& address,
+                  uint16_t default_port,
+                  unique_ptr<TabletServerServiceProxy>* proxy);
+template
+Status BuildProxy(const string& address,
+                  uint16_t default_port,
+                  unique_ptr<TabletServerAdminServiceProxy>* proxy);
+
+Status GetServerStatus(const string& address, uint16_t default_port,
+                       ServerStatusPB* status) {
+  unique_ptr<GenericServiceProxy> proxy;
+  RETURN_NOT_OK(BuildProxy(address, default_port, &proxy));
+
+  GetStatusRequestPB req;
+  GetStatusResponsePB resp;
+  RpcController rpc;
+  rpc.set_timeout(MonoDelta::FromMilliseconds(FLAGS_timeout_ms));
+
+  RETURN_NOT_OK(proxy->GetStatus(req, &resp, &rpc));
+  if (!resp.has_status()) {
+    return Status::Incomplete("Server response did not contain status",
+                              proxy->ToString());
+  }
+  *status = resp.status();
+  return Status::OK();
+}
+
 Status PrintSegment(const scoped_refptr<ReadableLogSegment>& segment) {
   PrintEntryType print_type = ParsePrintType();
   if (FLAGS_print_meta) {
@@ -206,5 +288,55 @@ Status PrintSegment(const scoped_refptr<ReadableLogSegment>& segment) {
   return Status::OK();
 }
 
+Status SetServerFlag(const string& address, uint16_t default_port,
+                     const string& flag, const string& value) {
+  unique_ptr<GenericServiceProxy> proxy;
+  RETURN_NOT_OK(BuildProxy(address, default_port, &proxy));
+
+  SetFlagRequestPB req;
+  SetFlagResponsePB resp;
+  RpcController rpc;
+  rpc.set_timeout(MonoDelta::FromMilliseconds(FLAGS_timeout_ms));
+
+  req.set_flag(flag);
+  req.set_value(value);
+  req.set_force(FLAGS_force);
+
+  RETURN_NOT_OK(proxy->SetFlag(req, &resp, &rpc));
+  switch (resp.result()) {
+    case server::SetFlagResponsePB::SUCCESS:
+      return Status::OK();
+    case server::SetFlagResponsePB::NOT_SAFE:
+      return Status::RemoteError(resp.msg() +
+                                 " (use --force flag to allow anyway)");
+    default:
+      return Status::RemoteError(resp.ShortDebugString());
+  }
+}
+
+Status PrintServerStatus(const string& address, uint16_t default_port) {
+  ServerStatusPB status;
+  RETURN_NOT_OK(GetServerStatus(address, default_port, &status));
+  cout << status.DebugString() << endl;
+  return Status::OK();
+}
+
+Status PrintServerTimestamp(const string& address, uint16_t default_port) {
+  unique_ptr<GenericServiceProxy> proxy;
+  RETURN_NOT_OK(BuildProxy(address, default_port, &proxy));
+
+  ServerClockRequestPB req;
+  ServerClockResponsePB resp;
+  RpcController rpc;
+  rpc.set_timeout(MonoDelta::FromMilliseconds(FLAGS_timeout_ms));
+  RETURN_NOT_OK(proxy->ServerClock(req, &resp, &rpc));
+  if (!resp.has_timestamp()) {
+    return Status::Incomplete("Server response did not contain timestamp",
+                              proxy->ToString());
+  }
+  cout << resp.timestamp() << endl;
+  return Status::OK();
+}
+
 } // namespace tools
 } // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/14cd22a1/src/kudu/tools/tool_action_common.h
----------------------------------------------------------------------
diff --git a/src/kudu/tools/tool_action_common.h b/src/kudu/tools/tool_action_common.h
index 0391bb3..d51f656 100644
--- a/src/kudu/tools/tool_action_common.h
+++ b/src/kudu/tools/tool_action_common.h
@@ -17,6 +17,9 @@
 
 #pragma once
 
+#include <memory>
+#include <string>
+
 #include "kudu/gutil/ref_counted.h"
 #include "kudu/util/status.h"
 
@@ -26,10 +29,30 @@ namespace log {
 class ReadableLogSegment;
 } // namespace log
 
+namespace server {
+class ServerStatusPB;
+} // namespace server
+
 namespace tools {
 
 // Utility methods used by multiple actions across different modes.
 
+// Builds a proxy to a Kudu server running at 'address', returning it in
+// 'proxy'.
+//
+// If 'address' does not contain a port, 'default_port' is used instead.
+template<class ProxyClass>
+Status BuildProxy(const std::string& address,
+                  uint16_t default_port,
+                  std::unique_ptr<ProxyClass>* proxy);
+
+// Get the current status of the Kudu server running at 'address', storing it
+// in 'status'.
+//
+// If 'address' does not contain a port, 'default_port' is used instead.
+Status GetServerStatus(const std::string& address, uint16_t default_port,
+                       server::ServerStatusPB* status);
+
 // Prints the contents of a WAL segment to stdout.
 //
 // The following gflags affect the output:
@@ -38,5 +61,22 @@ namespace tools {
 // - truncate_data: how many bytes to print for each data field.
 Status PrintSegment(const scoped_refptr<log::ReadableLogSegment>& segment);
 
+// Print the current status of the Kudu server running at 'address'.
+//
+// If 'address' does not contain a port, 'default_port' is used instead.
+Status PrintServerStatus(const std::string& address, uint16_t default_port);
+
+// Print the current timestamp of the Kudu server running at 'address'.
+//
+// If 'address' does not contain a port, 'default_port' is used instead.
+Status PrintServerTimestamp(const std::string& address, uint16_t default_port);
+
+// Changes the value of the gflag given by 'flag' to the value in 'value' on
+// the Kudu server running at 'address'.
+//
+// If 'address' does not contain a port, 'default_port' is used instead.
+Status SetServerFlag(const std::string& address, uint16_t default_port,
+                     const std::string& flag, const std::string& value);
+
 } // namespace tools
 } // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/14cd22a1/src/kudu/tools/tool_action_master.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tools/tool_action_master.cc b/src/kudu/tools/tool_action_master.cc
new file mode 100644
index 0000000..aea53d1
--- /dev/null
+++ b/src/kudu/tools/tool_action_master.cc
@@ -0,0 +1,97 @@
+// 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 "kudu/tools/tool_action.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <gflags/gflags.h>
+
+#include "kudu/gutil/map-util.h"
+#include "kudu/master/master.h"
+#include "kudu/tools/tool_action_common.h"
+#include "kudu/util/status.h"
+
+namespace kudu {
+namespace tools {
+
+using std::string;
+using std::unique_ptr;
+
+namespace {
+
+const char* const kMasterAddressArg = "master_address";
+const char* const kMasterAddressDesc = "Address of a Kudu Master of form "
+    "'hostname:port'. Port may be omitted if the Master is bound to the "
+    "default port.";
+const char* const kFlagArg = "flag";
+const char* const kValueArg = "value";
+
+Status MasterSetFlag(const RunnerContext& context) {
+  string address = FindOrDie(context.required_args, kMasterAddressArg);
+  string flag = FindOrDie(context.required_args, kFlagArg);
+  string value = FindOrDie(context.required_args, kValueArg);
+  return SetServerFlag(address, master::Master::kDefaultPort, flag, value);
+}
+
+Status MasterStatus(const RunnerContext& context) {
+  string address = FindOrDie(context.required_args, kMasterAddressArg);
+  return PrintServerStatus(address, master::Master::kDefaultPort);
+}
+
+Status MasterTimestamp(const RunnerContext& context) {
+  string address = FindOrDie(context.required_args, kMasterAddressArg);
+  return PrintServerTimestamp(address, master::Master::kDefaultPort);
+}
+
+} // anonymous namespace
+
+unique_ptr<Mode> BuildMasterMode() {
+  unique_ptr<Action> set_flag =
+      ActionBuilder("set_flag", &MasterSetFlag)
+      .Description("Change a gflag value on a Kudu Master")
+      .AddRequiredParameter({ kMasterAddressArg, kMasterAddressDesc })
+      .AddRequiredParameter({ kFlagArg, "Name of the gflag" })
+      .AddRequiredParameter({ kValueArg, "New value for the gflag" })
+      .AddOptionalParameter("force")
+      .Build();
+
+  unique_ptr<Action> status =
+      ActionBuilder("status", &MasterStatus)
+      .Description("Get the status of a Kudu Master")
+      .AddRequiredParameter({ kMasterAddressArg, kMasterAddressDesc })
+      .Build();
+
+  unique_ptr<Action> timestamp =
+      ActionBuilder("timestamp", &MasterTimestamp)
+      .Description("Get the current timestamp of a Kudu Master")
+      .AddRequiredParameter({ kMasterAddressArg, kMasterAddressDesc })
+      .Build();
+
+  return ModeBuilder("master")
+      .Description("Operate on a Kudu Master")
+      .AddAction(std::move(set_flag))
+      .AddAction(std::move(status))
+      .AddAction(std::move(timestamp))
+      .Build();
+}
+
+} // namespace tools
+} // namespace kudu
+

http://git-wip-us.apache.org/repos/asf/kudu/blob/14cd22a1/src/kudu/tools/tool_action_remote_replica.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tools/tool_action_remote_replica.cc b/src/kudu/tools/tool_action_remote_replica.cc
new file mode 100644
index 0000000..8ee7cb9
--- /dev/null
+++ b/src/kudu/tools/tool_action_remote_replica.cc
@@ -0,0 +1,331 @@
+// 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 "kudu/tools/tool_action.h"
+
+#include <iostream>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <gflags/gflags.h>
+
+#include "kudu/client/client.h"
+#include "kudu/client/row_result.h"
+#include "kudu/client/scan_batch.h"
+#include "kudu/client/scanner-internal.h"
+#include "kudu/common/partition.h"
+#include "kudu/common/schema.h"
+#include "kudu/common/wire_protocol.h"
+#include "kudu/gutil/gscoped_ptr.h"
+#include "kudu/gutil/map-util.h"
+#include "kudu/gutil/strings/human_readable.h"
+#include "kudu/server/server_base.pb.h"
+#include "kudu/tablet/tablet.pb.h"
+#include "kudu/tools/tool_action_common.h"
+#include "kudu/tserver/tablet_server.h"
+#include "kudu/tserver/tserver.pb.h"
+#include "kudu/tserver/tserver_admin.proxy.h"
+#include "kudu/tserver/tserver_service.proxy.h"
+#include "kudu/rpc/messenger.h"
+#include "kudu/rpc/rpc_controller.h"
+#include "kudu/util/net/net_util.h"
+#include "kudu/util/net/sockaddr.h"
+#include "kudu/util/status.h"
+
+DECLARE_int64(timeout_ms); // defined in ksck
+
+namespace kudu {
+namespace tools {
+
+using client::KuduRowResult;
+using client::KuduScanBatch;
+using client::KuduSchema;
+using rpc::Messenger;
+using rpc::MessengerBuilder;
+using rpc::RpcController;
+using server::ServerStatusPB;
+using std::cerr;
+using std::cout;
+using std::endl;
+using std::shared_ptr;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+using tablet::TabletStatusPB;
+using tserver::DeleteTabletRequestPB;
+using tserver::DeleteTabletResponsePB;
+using tserver::ListTabletsRequestPB;
+using tserver::ListTabletsResponsePB;
+using tserver::NewScanRequestPB;
+using tserver::ScanRequestPB;
+using tserver::ScanResponsePB;
+using tserver::TabletServerAdminServiceProxy;
+using tserver::TabletServerServiceProxy;
+
+// This class only exists so that Dump() can easily be friended with
+// KuduSchema and KuduScanBatch.
+class ReplicaDumper {
+ public:
+  static Status Dump(const Schema& schema,
+                     const string& tablet_id,
+                     TabletServerServiceProxy* proxy) {
+    KuduSchema client_schema(schema);
+
+    ScanRequestPB req;
+    ScanResponsePB resp;
+
+    NewScanRequestPB* new_req = req.mutable_new_scan_request();
+    RETURN_NOT_OK(SchemaToColumnPBs(
+        schema, new_req->mutable_projected_columns(),
+        SCHEMA_PB_WITHOUT_IDS | SCHEMA_PB_WITHOUT_STORAGE_ATTRIBUTES));
+    new_req->set_tablet_id(tablet_id);
+    new_req->set_cache_blocks(false);
+    new_req->set_order_mode(ORDERED);
+    new_req->set_read_mode(READ_AT_SNAPSHOT);
+
+    do {
+      RpcController rpc;
+      rpc.set_timeout(MonoDelta::FromMilliseconds(FLAGS_timeout_ms));
+      RETURN_NOT_OK_PREPEND(proxy->Scan(req, &resp, &rpc), "Scan() failed");
+
+      if (resp.has_error()) {
+        return Status::IOError("Failed to read: ",
+                               resp.error().ShortDebugString());
+      }
+
+      // The first response has a scanner ID. We use this for all subsequent
+      // responses.
+      if (resp.has_scanner_id()) {
+        req.set_scanner_id(resp.scanner_id());
+        req.clear_new_scan_request();
+      }
+      req.set_call_seq_id(req.call_seq_id() + 1);
+
+      // Nothing to process from this scan result.
+      if (!resp.has_data()) {
+        continue;
+      }
+
+      KuduScanBatch::Data results;
+      RETURN_NOT_OK(results.Reset(&rpc,
+                                  &schema,
+                                  &client_schema,
+                                  make_gscoped_ptr(resp.release_data())));
+      vector<KuduRowResult> rows;
+      results.ExtractRows(&rows);
+      for (const auto& r : rows) {
+        cout << r.ToString() << endl;
+      }
+    } while (resp.has_more_results());
+    return Status::OK();
+  }
+};
+
+namespace {
+
+const char* const kReasonArg = "reason";
+const char* const kTabletArg = "tablet_id";
+const char* const kTabletDesc = "Tablet identifier";
+const char* const kTServerAddressArg = "tserver_address";
+const char* const kTServerAddressDesc = "Address of a Kudu Tablet Server of "
+    "form 'hostname:port'. Port may be omitted if the Tablet Server is bound "
+    "to the default port.";
+
+Status GetReplicas(TabletServerServiceProxy* proxy,
+                   vector<ListTabletsResponsePB::StatusAndSchemaPB>* replicas) {
+  ListTabletsRequestPB req;
+  ListTabletsResponsePB resp;
+  RpcController rpc;
+  rpc.set_timeout(MonoDelta::FromMilliseconds(FLAGS_timeout_ms));
+
+  RETURN_NOT_OK(proxy->ListTablets(req, &resp, &rpc));
+  if (resp.has_error()) {
+    return StatusFromPB(resp.error().status());
+  }
+
+  replicas->assign(resp.status_and_schema().begin(),
+                   resp.status_and_schema().end());
+  return Status::OK();
+}
+
+Status CheckReplicas(const RunnerContext& context) {
+  string address = FindOrDie(context.required_args, kTServerAddressArg);
+
+  unique_ptr<TabletServerServiceProxy> proxy;
+  RETURN_NOT_OK(BuildProxy(address, tserver::TabletServer::kDefaultPort,
+                           &proxy));
+
+  vector<ListTabletsResponsePB::StatusAndSchemaPB> replicas;
+  RETURN_NOT_OK(GetReplicas(proxy.get(), &replicas));
+
+  bool all_running = true;
+  for (const auto& r : replicas) {
+    TabletStatusPB rs = r.tablet_status();
+    if (rs.state() != tablet::RUNNING) {
+      cerr << "Tablet id: " << rs.tablet_id() << " is "
+           << tablet::TabletStatePB_Name(rs.state()) << endl;
+      all_running = false;
+    }
+  }
+
+  if (all_running) {
+    cout << "All tablets are running" << endl;
+    return Status::OK();
+  } else {
+    return Status::IllegalState("Not all tablets are running");
+  }
+}
+
+Status DeleteReplica(const RunnerContext& context) {
+  string address = FindOrDie(context.required_args, kTServerAddressArg);
+  string tablet_id = FindOrDie(context.required_args, kTabletArg);
+  string reason = FindOrDie(context.required_args, kReasonArg);
+
+  ServerStatusPB status;
+  RETURN_NOT_OK(GetServerStatus(address, tserver::TabletServer::kDefaultPort,
+                                &status));
+
+  unique_ptr<TabletServerAdminServiceProxy> proxy;
+  RETURN_NOT_OK(BuildProxy(address, tserver::TabletServer::kDefaultPort,
+                           &proxy));
+
+  DeleteTabletRequestPB req;
+  DeleteTabletResponsePB resp;
+  RpcController rpc;
+  rpc.set_timeout(MonoDelta::FromMilliseconds(FLAGS_timeout_ms));
+
+  req.set_tablet_id(tablet_id);
+  req.set_dest_uuid(status.node_instance().permanent_uuid());
+  req.set_reason(reason);
+  req.set_delete_type(tablet::TABLET_DATA_TOMBSTONED);
+  RETURN_NOT_OK_PREPEND(proxy->DeleteTablet(req, &resp, &rpc),
+                        "DeleteTablet() failed");
+  if (resp.has_error()) {
+    return Status::IOError("Failed to delete tablet: ",
+                           resp.error().ShortDebugString());
+  }
+  return Status::OK();
+}
+
+Status DumpReplica(const RunnerContext& context) {
+  string address = FindOrDie(context.required_args, kTServerAddressArg);
+  string tablet_id = FindOrDie(context.required_args, kTabletArg);
+
+  unique_ptr<TabletServerServiceProxy> proxy;
+  RETURN_NOT_OK(BuildProxy(address, tserver::TabletServer::kDefaultPort,
+                           &proxy));
+
+  vector<ListTabletsResponsePB::StatusAndSchemaPB> replicas;
+  RETURN_NOT_OK(GetReplicas(proxy.get(), &replicas));
+
+  Schema schema;
+  for (const auto& r : replicas) {
+    if (r.tablet_status().tablet_id() == tablet_id) {
+      RETURN_NOT_OK(SchemaFromPB(r.schema(), &schema));
+      break;
+    }
+  }
+  if (!schema.initialized()) {
+    return Status::NotFound("cannot find replica", tablet_id);
+  }
+  return ReplicaDumper::Dump(schema, tablet_id, proxy.get());
+}
+
+Status ListReplicas(const RunnerContext& context) {
+  string address = FindOrDie(context.required_args, kTServerAddressArg);
+  unique_ptr<TabletServerServiceProxy> proxy;
+  RETURN_NOT_OK(BuildProxy(address, tserver::TabletServer::kDefaultPort,
+                           &proxy));
+
+  vector<ListTabletsResponsePB::StatusAndSchemaPB> replicas;
+  RETURN_NOT_OK(GetReplicas(proxy.get(), &replicas));
+
+  for (const auto& r : replicas) {
+    Schema schema;
+    RETURN_NOT_OK_PREPEND(
+        SchemaFromPB(r.schema(), &schema),
+        "Unable to deserialize schema from " + address);
+    PartitionSchema partition_schema;
+    RETURN_NOT_OK_PREPEND(
+        PartitionSchema::FromPB(r.partition_schema(), schema, &partition_schema),
+        "Unable to deserialize partition schema from " + address);
+
+    const TabletStatusPB& rs = r.tablet_status();
+
+    Partition partition;
+    Partition::FromPB(rs.partition(), &partition);
+
+    string state = tablet::TabletStatePB_Name(rs.state());
+    cout << "Tablet id: " << rs.tablet_id() << endl;
+    cout << "State: " << state << endl;
+    cout << "Table name: " << rs.table_name() << endl;
+    cout << "Partition: "
+         << partition_schema.PartitionDebugString(partition, schema) << endl;
+    if (rs.has_estimated_on_disk_size()) {
+      cout << "Estimated on disk size: "
+           << HumanReadableNumBytes::ToString(rs.estimated_on_disk_size()) << endl;
+    }
+    cout << "Schema: " << schema.ToString() << endl;
+  }
+
+  return Status::OK();
+}
+
+} // anonymous namespace
+
+unique_ptr<Mode> BuildRemoteReplicaMode() {
+  unique_ptr<Action> check_replicas =
+      ActionBuilder("check", &CheckReplicas)
+      .Description("Check if all replicas on a Kudu Tablet Server are running")
+      .AddRequiredParameter({ kTServerAddressArg, kTServerAddressDesc })
+      .Build();
+
+  unique_ptr<Action> delete_replica =
+      ActionBuilder("delete", &DeleteReplica)
+      .Description("Delete a replica from a Kudu Tablet Server")
+      .AddRequiredParameter({ kTServerAddressArg, kTServerAddressDesc })
+      .AddRequiredParameter({ kTabletArg, kTabletDesc })
+      .AddRequiredParameter({ kReasonArg, "Reason for deleting the replica" })
+      .Build();
+
+  unique_ptr<Action> dump_replica =
+      ActionBuilder("dump", &DumpReplica)
+      .Description("Dump the data of a replica on a Kudu Tablet Server")
+      .AddRequiredParameter({ kTServerAddressArg, kTServerAddressDesc })
+      .AddRequiredParameter({ kTabletArg, kTabletDesc })
+      .Build();
+
+  unique_ptr<Action> list =
+      ActionBuilder("list", &ListReplicas)
+      .Description("List all replicas on a Kudu Tablet Server")
+      .AddRequiredParameter({ kTServerAddressArg, kTServerAddressDesc })
+      .Build();
+
+  return ModeBuilder("remote_replica")
+      .Description("Operate on replicas on a Kudu Tablet Server")
+      .AddAction(std::move(check_replicas))
+      .AddAction(std::move(delete_replica))
+      .AddAction(std::move(dump_replica))
+      .AddAction(std::move(list))
+      .Build();
+}
+
+} // namespace tools
+} // namespace kudu
+

http://git-wip-us.apache.org/repos/asf/kudu/blob/14cd22a1/src/kudu/tools/tool_action_tablet.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tools/tool_action_tablet.cc b/src/kudu/tools/tool_action_tablet.cc
index 8ed0039..86e3df7 100644
--- a/src/kudu/tools/tool_action_tablet.cc
+++ b/src/kudu/tools/tool_action_tablet.cc
@@ -30,8 +30,8 @@
 #include "kudu/gutil/stl_util.h"
 #include "kudu/gutil/strings/split.h"
 #include "kudu/gutil/strings/substitute.h"
-#include "kudu/rpc/messenger.h"
 #include "kudu/rpc/rpc_controller.h"
+#include "kudu/tools/tool_action_common.h"
 #include "kudu/util/net/net_util.h"
 #include "kudu/util/status.h"
 #include "kudu/util/string_case.h"
@@ -46,8 +46,6 @@ using client::KuduTabletServer;
 using consensus::ChangeConfigType;
 using consensus::ConsensusServiceProxy;
 using consensus::RaftPeerPB;
-using rpc::Messenger;
-using rpc::MessengerBuilder;
 using rpc::RpcController;
 using std::shared_ptr;
 using std::string;
@@ -145,9 +143,8 @@ Status ChangeConfig(const RunnerContext& context, ChangeConfigType cc_type) {
         leader_hp.ToString());
   }
 
-  shared_ptr<Messenger> messenger;
-  RETURN_NOT_OK(MessengerBuilder("kudu").Build(&messenger));
-  ConsensusServiceProxy proxy(messenger, leader_addrs[0]);
+  unique_ptr<ConsensusServiceProxy> proxy;
+  RETURN_NOT_OK(BuildProxy(leader_hp.host(), leader_hp.port(), &proxy));
 
   consensus::ChangeConfigRequestPB req;
   consensus::ChangeConfigResponsePB resp;
@@ -157,7 +154,7 @@ Status ChangeConfig(const RunnerContext& context, ChangeConfigType cc_type) {
   req.set_tablet_id(tablet_id);
   req.set_type(cc_type);
   *req.mutable_server() = peer_pb;
-  RETURN_NOT_OK(proxy.ChangeConfig(req, &resp, &rpc));
+  RETURN_NOT_OK(proxy->ChangeConfig(req, &resp, &rpc));
   if (resp.has_error()) {
     return StatusFromPB(resp.error().status());
   }

http://git-wip-us.apache.org/repos/asf/kudu/blob/14cd22a1/src/kudu/tools/tool_action_tserver.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tools/tool_action_tserver.cc b/src/kudu/tools/tool_action_tserver.cc
new file mode 100644
index 0000000..145ec1b
--- /dev/null
+++ b/src/kudu/tools/tool_action_tserver.cc
@@ -0,0 +1,98 @@
+// 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 "kudu/tools/tool_action.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <gflags/gflags.h>
+
+#include "kudu/gutil/map-util.h"
+#include "kudu/tools/tool_action_common.h"
+#include "kudu/tserver/tablet_server.h"
+#include "kudu/util/status.h"
+
+namespace kudu {
+namespace tools {
+
+using std::string;
+using std::unique_ptr;
+
+namespace {
+
+const char* const kTServerAddressArg = "tserver_address";
+const char* const kTServerAddressDesc = "Address of a Kudu Tablet Server of "
+    "form 'hostname:port'. Port may be omitted if the Tablet Server is bound "
+    "to the default port.";
+const char* const kFlagArg = "flag";
+const char* const kValueArg = "value";
+
+Status TServerSetFlag(const RunnerContext& context) {
+  string address = FindOrDie(context.required_args, kTServerAddressArg);
+  string flag = FindOrDie(context.required_args, kFlagArg);
+  string value = FindOrDie(context.required_args, kValueArg);
+  return SetServerFlag(address, tserver::TabletServer::kDefaultPort,
+                       flag, value);
+}
+
+Status TServerStatus(const RunnerContext& context) {
+  string address = FindOrDie(context.required_args, kTServerAddressArg);
+  return PrintServerStatus(address, tserver::TabletServer::kDefaultPort);
+}
+
+Status TServerTimestamp(const RunnerContext& context) {
+  string address = FindOrDie(context.required_args, kTServerAddressArg);
+  return PrintServerTimestamp(address, tserver::TabletServer::kDefaultPort);
+}
+
+} // anonymous namespace
+
+unique_ptr<Mode> BuildTServerMode() {
+  unique_ptr<Action> set_flag =
+      ActionBuilder("set_flag", &TServerSetFlag)
+      .Description("Change a gflag value on a Kudu Tablet Server")
+      .AddRequiredParameter({ kTServerAddressArg, kTServerAddressDesc })
+      .AddRequiredParameter({ kFlagArg, "Name of the gflag" })
+      .AddRequiredParameter({ kValueArg, "New value for the gflag" })
+      .AddOptionalParameter("force")
+      .Build();
+
+  unique_ptr<Action> status =
+      ActionBuilder("status", &TServerStatus)
+      .Description("Get the status of a Kudu Tablet Server")
+      .AddRequiredParameter({ kTServerAddressArg, kTServerAddressDesc })
+      .Build();
+
+  unique_ptr<Action> timestamp =
+      ActionBuilder("timestamp", &TServerTimestamp)
+      .Description("Get the current timestamp of a Kudu Tablet Server")
+      .AddRequiredParameter({ kTServerAddressArg, kTServerAddressDesc })
+      .Build();
+
+  return ModeBuilder("tserver")
+      .Description("Operate on a Kudu Tablet Server")
+      .AddAction(std::move(set_flag))
+      .AddAction(std::move(status))
+      .AddAction(std::move(timestamp))
+      .Build();
+}
+
+} // namespace tools
+} // namespace kudu
+

http://git-wip-us.apache.org/repos/asf/kudu/blob/14cd22a1/src/kudu/tools/tool_main.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tools/tool_main.cc b/src/kudu/tools/tool_main.cc
index 3c8bb63..caf3bde 100644
--- a/src/kudu/tools/tool_main.cc
+++ b/src/kudu/tools/tool_main.cc
@@ -114,9 +114,12 @@ int RunTool(int argc, char** argv, bool show_help) {
     .AddMode(BuildClusterMode())
     .AddMode(BuildFsMode())
     .AddMode(BuildLocalReplicaMode())
+    .AddMode(BuildMasterMode())
     .AddMode(BuildPbcMode())
+    .AddMode(BuildRemoteReplicaMode())
     .AddMode(BuildTableMode())
     .AddMode(BuildTabletMode())
+    .AddMode(BuildTServerMode())
     .AddMode(BuildWalMode())
     .Build();
 

http://git-wip-us.apache.org/repos/asf/kudu/blob/14cd22a1/src/kudu/tools/ts-cli.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tools/ts-cli.cc b/src/kudu/tools/ts-cli.cc
deleted file mode 100644
index f34adbf..0000000
--- a/src/kudu/tools/ts-cli.cc
+++ /dev/null
@@ -1,494 +0,0 @@
-// 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.
-//
-// Tool to query tablet server operational data
-
-#include <gflags/gflags.h>
-#include <glog/logging.h>
-#include <iostream>
-#include <memory>
-#include <strstream>
-
-#include "kudu/client/row_result.h"
-#include "kudu/client/scanner-internal.h"
-#include "kudu/common/partition.h"
-#include "kudu/common/schema.h"
-#include "kudu/common/wire_protocol.h"
-#include "kudu/gutil/strings/human_readable.h"
-#include "kudu/server/server_base.proxy.h"
-#include "kudu/tserver/tserver.pb.h"
-#include "kudu/tserver/tserver_admin.proxy.h"
-#include "kudu/tserver/tserver_service.proxy.h"
-#include "kudu/tserver/tablet_server.h"
-#include "kudu/util/env.h"
-#include "kudu/util/faststring.h"
-#include "kudu/util/flags.h"
-#include "kudu/util/logging.h"
-#include "kudu/util/net/net_util.h"
-#include "kudu/util/net/sockaddr.h"
-#include "kudu/rpc/messenger.h"
-#include "kudu/rpc/rpc_controller.h"
-
-using kudu::client::KuduRowResult;
-using kudu::HostPort;
-using kudu::rpc::Messenger;
-using kudu::rpc::MessengerBuilder;
-using kudu::rpc::RpcController;
-using kudu::server::ServerStatusPB;
-using kudu::Sockaddr;
-using kudu::client::KuduScanBatch;
-using kudu::tablet::TabletStatusPB;
-using kudu::tserver::DeleteTabletRequestPB;
-using kudu::tserver::DeleteTabletResponsePB;
-using kudu::tserver::ListTabletsRequestPB;
-using kudu::tserver::ListTabletsResponsePB;
-using kudu::tserver::NewScanRequestPB;
-using kudu::tserver::ScanRequestPB;
-using kudu::tserver::ScanResponsePB;
-using kudu::tserver::TabletServerAdminServiceProxy;
-using kudu::tserver::TabletServerServiceProxy;
-using std::ostringstream;
-using std::shared_ptr;
-using std::string;
-using std::vector;
-
-const char* const kListTabletsOp = "list_tablets";
-const char* const kAreTabletsRunningOp = "are_tablets_running";
-const char* const kSetFlagOp = "set_flag";
-const char* const kDumpTabletOp = "dump_tablet";
-const char* const kDeleteTabletOp = "delete_tablet";
-const char* const kCurrentTimestamp = "current_timestamp";
-const char* const kStatus = "status";
-
-DEFINE_string(server_address, "localhost",
-              "Address of server to run against");
-DEFINE_int64(timeout_ms, 1000 * 60, "RPC timeout in milliseconds");
-
-DEFINE_bool(force, false, "If true, allows the set_flag command to set a flag "
-            "which is not explicitly marked as runtime-settable. Such flag changes may be "
-            "simply ignored on the server, or may cause the server to crash.");
-
-// Check that the value of argc matches what's expected, otherwise return a
-// non-zero exit code. Should be used in main().
-#define CHECK_ARGC_OR_RETURN_WITH_USAGE(op, expected) \
-  do { \
-    const string& _op = (op); \
-    const int _expected = (expected); \
-    if (argc != _expected) { \
-      /* We substract 2 from _expected because we don't want to count argv[0] or [1]. */ \
-      std::cerr << "Invalid number of arguments for " << _op \
-                << ": expected " << (_expected - 2) << " arguments" << std::endl; \
-      google::ShowUsageWithFlagsRestrict(argv[0], __FILE__); \
-      return 2; \
-    } \
-  } while (0);
-
-// Invoke 'to_call' and check its result. If it failed, print 'to_prepend' and
-// the error to cerr and return a non-zero exit code. Should be used in main().
-#define RETURN_NOT_OK_PREPEND_FROM_MAIN(to_call, to_prepend) \
-  do { \
-    ::kudu::Status s = (to_call); \
-    if (!s.ok()) { \
-      std::cerr << (to_prepend) << ": " << s.ToString() << std::endl; \
-      return 1; \
-    } \
-  } while (0);
-
-namespace kudu {
-namespace tools {
-
-typedef ListTabletsResponsePB::StatusAndSchemaPB StatusAndSchemaPB;
-
-class TsAdminClient {
- public:
-  // Creates an admin client for host/port combination e.g.,
-  // "localhost" or "127.0.0.1:7050".
-  TsAdminClient(std::string addr, int64_t timeout_millis);
-
-  // Initialized the client and connects to the specified tablet
-  // server.
-  Status Init();
-
-  // Sets 'tablets' a list of status information for all tablets on a
-  // given tablet server.
-  Status ListTablets(std::vector<StatusAndSchemaPB>* tablets);
-
-
-  // Sets the gflag 'flag' to 'val' on the remote server via RPC.
-  // If 'force' is true, allows setting flags even if they're not marked as
-  // safe to change at runtime.
-  Status SetFlag(const string& flag, const string& val,
-                 bool force);
-
-  // Get the schema for the given tablet.
-  Status GetTabletSchema(const std::string& tablet_id, SchemaPB* schema);
-
-  // Dump the contents of the given tablet, in key order, to the console.
-  Status DumpTablet(const std::string& tablet_id);
-
-  // Delete a tablet replica from the specified peer.
-  // The 'reason' string is passed to the tablet server, used for logging.
-  Status DeleteTablet(const std::string& tablet_id,
-                      const std::string& reason);
-
-  // Sets timestamp to the value of the tablet server's current timestamp.
-  Status CurrentTimestamp(uint64_t* timestamp);
-
-  // Get the server status
-  Status GetStatus(ServerStatusPB* pb);
- private:
-  std::string addr_;
-  vector<Sockaddr> addrs_;
-  MonoDelta timeout_;
-  bool initted_;
-  shared_ptr<server::GenericServiceProxy> generic_proxy_;
-  gscoped_ptr<tserver::TabletServerServiceProxy> ts_proxy_;
-  gscoped_ptr<tserver::TabletServerAdminServiceProxy> ts_admin_proxy_;
-  shared_ptr<rpc::Messenger> messenger_;
-
-  DISALLOW_COPY_AND_ASSIGN(TsAdminClient);
-};
-
-TsAdminClient::TsAdminClient(string addr, int64_t timeout_millis)
-    : addr_(std::move(addr)),
-      timeout_(MonoDelta::FromMilliseconds(timeout_millis)),
-      initted_(false) {}
-
-Status TsAdminClient::Init() {
-  CHECK(!initted_);
-
-  HostPort host_port;
-  RETURN_NOT_OK(host_port.ParseString(addr_, tserver::TabletServer::kDefaultPort));
-  MessengerBuilder builder("ts-cli");
-  RETURN_NOT_OK(builder.Build(&messenger_));
-
-  RETURN_NOT_OK(host_port.ResolveAddresses(&addrs_))
-
-  generic_proxy_.reset(new server::GenericServiceProxy(messenger_, addrs_[0]));
-  ts_proxy_.reset(new TabletServerServiceProxy(messenger_, addrs_[0]));
-  ts_admin_proxy_.reset(new TabletServerAdminServiceProxy(messenger_, addrs_[0]));
-
-  initted_ = true;
-
-  VLOG(1) << "Connected to " << addr_;
-
-  return Status::OK();
-}
-
-Status TsAdminClient::ListTablets(vector<StatusAndSchemaPB>* tablets) {
-  CHECK(initted_);
-
-  ListTabletsRequestPB req;
-  ListTabletsResponsePB resp;
-  RpcController rpc;
-
-  rpc.set_timeout(timeout_);
-  RETURN_NOT_OK(ts_proxy_->ListTablets(req, &resp, &rpc));
-  if (resp.has_error()) {
-    return StatusFromPB(resp.error().status());
-  }
-
-  tablets->assign(resp.status_and_schema().begin(), resp.status_and_schema().end());
-
-  return Status::OK();
-}
-
-Status TsAdminClient::SetFlag(const string& flag, const string& val,
-                              bool force) {
-  server::SetFlagRequestPB req;
-  server::SetFlagResponsePB resp;
-  RpcController rpc;
-
-  rpc.set_timeout(timeout_);
-  req.set_flag(flag);
-  req.set_value(val);
-  req.set_force(force);
-
-  RETURN_NOT_OK(generic_proxy_->SetFlag(req, &resp, &rpc));
-  switch (resp.result()) {
-    case server::SetFlagResponsePB::SUCCESS:
-      return Status::OK();
-    case server::SetFlagResponsePB::NOT_SAFE:
-      return Status::RemoteError(resp.msg() + " (use --force flag to allow anyway)");
-    default:
-      return Status::RemoteError(resp.ShortDebugString());
-  }
-}
-
-Status TsAdminClient::GetTabletSchema(const std::string& tablet_id,
-                                      SchemaPB* schema) {
-  VLOG(1) << "Fetching schema for tablet " << tablet_id;
-  vector<StatusAndSchemaPB> tablets;
-  RETURN_NOT_OK(ListTablets(&tablets));
-  for (const StatusAndSchemaPB& pair : tablets) {
-    if (pair.tablet_status().tablet_id() == tablet_id) {
-      *schema = pair.schema();
-      return Status::OK();
-    }
-  }
-  return Status::NotFound("Cannot find tablet", tablet_id);
-}
-
-Status TsAdminClient::DumpTablet(const std::string& tablet_id) {
-  SchemaPB schema_pb;
-  RETURN_NOT_OK(GetTabletSchema(tablet_id, &schema_pb));
-  Schema schema;
-  RETURN_NOT_OK(SchemaFromPB(schema_pb, &schema));
-  kudu::client::KuduSchema client_schema(schema);
-
-  ScanRequestPB req;
-  ScanResponsePB resp;
-
-  NewScanRequestPB* new_req = req.mutable_new_scan_request();
-  RETURN_NOT_OK(SchemaToColumnPBs(
-      schema, new_req->mutable_projected_columns(),
-      SCHEMA_PB_WITHOUT_IDS | SCHEMA_PB_WITHOUT_STORAGE_ATTRIBUTES));
-  new_req->set_tablet_id(tablet_id);
-  new_req->set_cache_blocks(false);
-  new_req->set_order_mode(ORDERED);
-  new_req->set_read_mode(READ_AT_SNAPSHOT);
-
-  do {
-    RpcController rpc;
-    rpc.set_timeout(timeout_);
-    RETURN_NOT_OK_PREPEND(ts_proxy_->Scan(req, &resp, &rpc),
-                          "Scan() failed");
-
-    if (resp.has_error()) {
-      return Status::IOError("Failed to read: ", resp.error().ShortDebugString());
-    }
-
-    // The first response has a scanner ID. We use this for all subsequent
-    // responses.
-    if (resp.has_scanner_id()) {
-      req.set_scanner_id(resp.scanner_id());
-      req.clear_new_scan_request();
-    }
-    req.set_call_seq_id(req.call_seq_id() + 1);
-
-    // Nothing to process from this scan result.
-    if (!resp.has_data()) {
-      continue;
-    }
-
-    KuduScanBatch::Data results;
-    RETURN_NOT_OK(results.Reset(&rpc,
-                                &schema,
-                                &client_schema,
-                                make_gscoped_ptr(resp.release_data())));
-    vector<KuduRowResult> rows;
-    results.ExtractRows(&rows);
-    for (const KuduRowResult& r : rows) {
-      std::cout << r.ToString() << std::endl;
-    }
-  } while (resp.has_more_results());
-  return Status::OK();
-}
-
-Status TsAdminClient::DeleteTablet(const string& tablet_id,
-                                   const string& reason) {
-  ServerStatusPB status_pb;
-  RETURN_NOT_OK(GetStatus(&status_pb));
-
-  DeleteTabletRequestPB req;
-  DeleteTabletResponsePB resp;
-  RpcController rpc;
-
-  req.set_tablet_id(tablet_id);
-  req.set_dest_uuid(status_pb.node_instance().permanent_uuid());
-  req.set_reason(reason);
-  req.set_delete_type(tablet::TABLET_DATA_TOMBSTONED);
-  rpc.set_timeout(timeout_);
-  RETURN_NOT_OK_PREPEND(ts_admin_proxy_->DeleteTablet(req, &resp, &rpc),
-                        "DeleteTablet() failed");
-
-  if (resp.has_error()) {
-    return Status::IOError("Failed to delete tablet: ",
-                           resp.error().ShortDebugString());
-  }
-  return Status::OK();
-}
-
-Status TsAdminClient::CurrentTimestamp(uint64_t* timestamp) {
-  server::ServerClockRequestPB req;
-  server::ServerClockResponsePB resp;
-  RpcController rpc;
-  rpc.set_timeout(timeout_);
-  RETURN_NOT_OK(generic_proxy_->ServerClock(req, &resp, &rpc));
-  CHECK(resp.has_timestamp()) << resp.DebugString();
-  *timestamp = resp.timestamp();
-  return Status::OK();
-}
-
-Status TsAdminClient::GetStatus(ServerStatusPB* pb) {
-  server::GetStatusRequestPB req;
-  server::GetStatusResponsePB resp;
-  RpcController rpc;
-  rpc.set_timeout(timeout_);
-  RETURN_NOT_OK(generic_proxy_->GetStatus(req, &resp, &rpc));
-  CHECK(resp.has_status()) << resp.DebugString();
-  pb->Swap(resp.mutable_status());
-  return Status::OK();
-}
-
-namespace {
-
-void SetUsage(const char* argv0) {
-  ostringstream str;
-
-  str << argv0 << " [--server_address=<addr>] <operation> <flags>\n"
-      << "<operation> must be one of:\n"
-      << "  " << kListTabletsOp << "\n"
-      << "  " << kAreTabletsRunningOp << "\n"
-      << "  " << kSetFlagOp << " [-force] <flag> <value>\n"
-      << "  " << kDumpTabletOp << " <tablet_id>\n"
-      << "  " << kDeleteTabletOp << " <tablet_id> <reason string>\n"
-      << "  " << kCurrentTimestamp << "\n"
-      << "  " << kStatus;
-  google::SetUsageMessage(str.str());
-}
-
-string GetOp(int argc, char** argv) {
-  if (argc < 2) {
-    google::ShowUsageWithFlagsRestrict(argv[0], __FILE__);
-    exit(1);
-  }
-
-  return argv[1];
-}
-
-} // anonymous namespace
-
-static int TsCliMain(int argc, char** argv) {
-  FLAGS_logtostderr = 1;
-  SetUsage(argv[0]);
-  ParseCommandLineFlags(&argc, &argv, true);
-  InitGoogleLoggingSafe(argv[0]);
-  const string addr = FLAGS_server_address;
-
-  string op = GetOp(argc, argv);
-
-  TsAdminClient client(addr, FLAGS_timeout_ms);
-
-  RETURN_NOT_OK_PREPEND_FROM_MAIN(client.Init(),
-                                  "Unable to establish connection to " + addr);
-
-  // TODO add other operations here...
-  if (op == kListTabletsOp) {
-    CHECK_ARGC_OR_RETURN_WITH_USAGE(op, 2);
-
-    vector<StatusAndSchemaPB> tablets;
-    RETURN_NOT_OK_PREPEND_FROM_MAIN(client.ListTablets(&tablets),
-                                    "Unable to list tablets on " + addr);
-    for (const StatusAndSchemaPB& status_and_schema : tablets) {
-      Schema schema;
-      RETURN_NOT_OK_PREPEND_FROM_MAIN(SchemaFromPB(status_and_schema.schema(), &schema),
-                                      "Unable to deserialize schema from " + addr);
-      PartitionSchema partition_schema;
-      RETURN_NOT_OK_PREPEND_FROM_MAIN(PartitionSchema::FromPB(status_and_schema.partition_schema(),
-                                                              schema, &partition_schema),
-                                      "Unable to deserialize partition schema from " + addr);
-
-
-      TabletStatusPB ts = status_and_schema.tablet_status();
-
-      Partition partition;
-      Partition::FromPB(ts.partition(), &partition);
-
-      string state = tablet::TabletStatePB_Name(ts.state());
-      std::cout << "Tablet id: " << ts.tablet_id() << std::endl;
-      std::cout << "State: " << state << std::endl;
-      std::cout << "Table name: " << ts.table_name() << std::endl;
-      std::cout << "Partition: " << partition_schema.PartitionDebugString(partition, schema)
-                << std::endl;
-      if (ts.has_estimated_on_disk_size()) {
-        std::cout << "Estimated on disk size: " <<
-            HumanReadableNumBytes::ToString(ts.estimated_on_disk_size()) << std::endl;
-      }
-      std::cout << "Schema: " << schema.ToString() << std::endl;
-    }
-  } else if (op == kAreTabletsRunningOp) {
-    CHECK_ARGC_OR_RETURN_WITH_USAGE(op, 2);
-
-    vector<StatusAndSchemaPB> tablets;
-    RETURN_NOT_OK_PREPEND_FROM_MAIN(client.ListTablets(&tablets),
-                                    "Unable to list tablets on " + addr);
-    bool all_running = true;
-    for (const StatusAndSchemaPB& status_and_schema : tablets) {
-      TabletStatusPB ts = status_and_schema.tablet_status();
-      if (ts.state() != tablet::RUNNING) {
-        std::cout << "Tablet id: " << ts.tablet_id() << " is "
-                  << tablet::TabletStatePB_Name(ts.state()) << std::endl;
-        all_running = false;
-      }
-    }
-
-    if (all_running) {
-      std::cout << "All tablets are running" << std::endl;
-    } else {
-      std::cout << "Not all tablets are running" << std::endl;
-      return 1;
-    }
-  } else if (op == kSetFlagOp) {
-    CHECK_ARGC_OR_RETURN_WITH_USAGE(op, 4);
-
-    RETURN_NOT_OK_PREPEND_FROM_MAIN(client.SetFlag(argv[2], argv[3], FLAGS_force),
-                                    "Unable to set flag");
-
-  } else if (op == kDumpTabletOp) {
-    CHECK_ARGC_OR_RETURN_WITH_USAGE(op, 3);
-
-    string tablet_id = argv[2];
-    RETURN_NOT_OK_PREPEND_FROM_MAIN(client.DumpTablet(tablet_id),
-                                    "Unable to dump tablet");
-  } else if (op == kDeleteTabletOp) {
-    CHECK_ARGC_OR_RETURN_WITH_USAGE(op, 4);
-
-    string tablet_id = argv[2];
-    string reason = argv[3];
-
-    RETURN_NOT_OK_PREPEND_FROM_MAIN(client.DeleteTablet(tablet_id, reason),
-                                    "Unable to delete tablet");
-  } else if (op == kCurrentTimestamp) {
-    CHECK_ARGC_OR_RETURN_WITH_USAGE(op, 2);
-
-    uint64_t timestamp;
-    RETURN_NOT_OK_PREPEND_FROM_MAIN(client.CurrentTimestamp(&timestamp),
-                                    "Unable to get timestamp");
-    std::cout << timestamp << std::endl;
-  } else if (op == kStatus) {
-    CHECK_ARGC_OR_RETURN_WITH_USAGE(op, 2);
-
-    ServerStatusPB status;
-    RETURN_NOT_OK_PREPEND_FROM_MAIN(client.GetStatus(&status),
-                                    "Unable to get status");
-    std::cout << status.DebugString() << std::endl;
-  } else {
-    std::cerr << "Invalid operation: " << op << std::endl;
-    google::ShowUsageWithFlagsRestrict(argv[0], __FILE__);
-    return 2;
-  }
-
-  return 0;
-}
-
-} // namespace tools
-} // namespace kudu
-
-int main(int argc, char** argv) {
-  return kudu::tools::TsCliMain(argc, argv);
-}