diff --git a/src/exec/CMakeLists.txt b/src/exec/CMakeLists.txt
index ab8ca5069f3c4a6c63ebf7f8fb63669fd1437d69..46a2d78bfedd7e72d7fbb34784f32681d9400693 100644
--- a/src/exec/CMakeLists.txt
+++ b/src/exec/CMakeLists.txt
@@ -27,6 +27,7 @@ nebula_add_library(
     query/DataCollectExecutor.cpp
     query/DataJoinExecutor.cpp
     admin/SwitchSpaceExecutor.cpp
+    admin/ShowHostsExecutor.cpp
     admin/SpaceExecutor.cpp
     admin/SnapshotExecutor.cpp
     maintain/TagExecutor.cpp
diff --git a/src/exec/Executor.cpp b/src/exec/Executor.cpp
index d7c4cc120568da08e18895761e3e3e820002fb93..d1f1a2af5dfd44098cb7262fa23aeb97e293a703 100644
--- a/src/exec/Executor.cpp
+++ b/src/exec/Executor.cpp
@@ -13,6 +13,7 @@
 #include "context/ExecutionContext.h"
 #include "context/QueryContext.h"
 #include "exec/ExecutionError.h"
+#include "exec/admin/ShowHostsExecutor.h"
 #include "exec/admin/SnapshotExecutor.h"
 #include "exec/admin/SpaceExecutor.h"
 #include "exec/admin/SwitchSpaceExecutor.h"
@@ -399,6 +400,13 @@ Executor *Executor::makeExecutor(const PlanNode *node,
             exec->dependsOn(input);
             break;
         }
+        case PlanNode::Kind::kShowHosts: {
+            auto showHosts = asNode<ShowHosts>(node);
+            auto input = makeExecutor(showHosts->dep(), qctx, visited);
+            exec = new ShowHostsExecutor(showHosts, qctx);
+            exec->dependsOn(input);
+            break;
+        }
         case PlanNode::Kind::kUnknown:
         default:
             LOG(FATAL) << "Unknown plan node kind " << static_cast<int32_t>(node->kind());
@@ -453,6 +461,15 @@ Status Executor::finish(Result &&result) {
     return Status::OK();
 }
 
+Status Executor::finish(Value &&value) {
+    ectx_->setResult(node()->varName(),
+                     ResultBuilder()
+                        .value(std::move(value))
+                        .iter(Iterator::Kind::kDefault)
+                        .finish());
+    return Status::OK();
+}
+
 folly::Executor *Executor::runner() const {
     if (!qctx() || !qctx()->rctx() || !qctx()->rctx()->runner()) {
         // This is just for test
diff --git a/src/exec/Executor.h b/src/exec/Executor.h
index 241ccb77079d1a7542c1e5a4c233deec0d89d8e0..86045d293a5b4efa5124660ee254f6a327c9d06c 100644
--- a/src/exec/Executor.h
+++ b/src/exec/Executor.h
@@ -98,6 +98,8 @@ protected:
 
     // Store the result of this executor to execution context
     Status finish(Result &&result);
+    // Store the default result which not used for later executor
+    Status finish(Value &&value);
 
     int64_t id_;
 
diff --git a/src/exec/admin/ShowHostsExecutor.cpp b/src/exec/admin/ShowHostsExecutor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1b3917d8ece03b721b7f12d8370070bd9149579b
--- /dev/null
+++ b/src/exec/admin/ShowHostsExecutor.cpp
@@ -0,0 +1,76 @@
+/* Copyright (c) 2020 vesoft inc. All rights reserved.
+ *
+ * This source code is licensed under Apache 2.0 License,
+ * attached with Common Clause Condition 1.0, found in the LICENSES directory.
+ */
+
+#include "exec/admin/ShowHostsExecutor.h"
+#include "planner/Admin.h"
+#include "context/QueryContext.h"
+
+namespace nebula {
+namespace graph {
+
+folly::Future<Status> ShowHostsExecutor::execute() {
+    SCOPED_TIMER(&execTime_);
+    return showHosts().ensure([this]() { UNUSED(this); });
+}
+
+folly::Future<Status> ShowHostsExecutor::showHosts() {
+    static constexpr char kNoPartition[]        = "No valid partition";
+    static constexpr char kPartitionDelimeter[] = ", ";
+    return qctx()
+        ->getMetaClient()
+        ->listHosts()
+        .via(runner())
+        .then([this](auto &&resp) {
+            if (!resp.ok()) {
+                LOG(ERROR) << resp.status();
+                return resp.status();
+            }
+            auto    value = std::move(resp).value();
+            DataSet v({"Host",
+                       "Port",
+                       "Status",
+                       "Leader count",
+                       "Leader distribution",
+                       "Partition distribution"});
+            for (const auto &host : value) {
+                nebula::Row r({host.get_hostAddr().host,
+                               host.get_hostAddr().port,
+                               meta::cpp2::_HostStatus_VALUES_TO_NAMES.at(host.get_status()),
+                               static_cast<int64_t>(host.get_leader_parts().size())});
+                std::stringstream leaders;
+                std::stringstream parts;
+                std::size_t i = 0;
+                for (const auto &l : host.get_leader_parts()) {
+                    leaders << l.first << ":" << l.second.size();
+                    if (i < host.get_leader_parts().size() - 1) {
+                        leaders << kPartitionDelimeter;
+                    }
+                    ++i;
+                }
+                if (host.get_leader_parts().empty()) {
+                    leaders << kNoPartition;
+                }
+                i = 0;
+                for (const auto &p : host.get_all_parts()) {
+                    parts << p.first << ":" << p.second.size();
+                    if (i < host.get_leader_parts().size() - 1) {
+                        parts << kPartitionDelimeter;
+                    }
+                    ++i;
+                }
+                if (host.get_all_parts().empty()) {
+                    parts << kNoPartition;
+                }
+                r.emplace_back(leaders.str());
+                r.emplace_back(parts.str());
+                v.emplace_back(std::move(r));
+            }  // row loop
+            return finish(std::move(v));
+        });
+}
+
+}  // namespace graph
+}  // namespace nebula
diff --git a/src/exec/admin/ShowHostsExecutor.h b/src/exec/admin/ShowHostsExecutor.h
new file mode 100644
index 0000000000000000000000000000000000000000..dd5a9cf8d919e38ee60cc448e26093abf3a03d8f
--- /dev/null
+++ b/src/exec/admin/ShowHostsExecutor.h
@@ -0,0 +1,29 @@
+/* Copyright (c) 2020 vesoft inc. All rights reserved.
+ *
+ * This source code is licensed under Apache 2.0 License,
+ * attached with Common Clause Condition 1.0, found in the LICENSES directory.
+ */
+
+#ifndef EXEC_ADMIN_SHOW_HOSTS_EXECUTOR_H_
+#define EXEC_ADMIN_SHOW_HOSTS_EXECUTOR_H_
+
+#include "exec/Executor.h"
+
+namespace nebula {
+namespace graph {
+
+class ShowHostsExecutor final : public Executor {
+public:
+    ShowHostsExecutor(const PlanNode *node, QueryContext *qctx)
+        : Executor("ShowHostsExecutor", node, qctx) {}
+
+    folly::Future<Status> execute() override;
+
+private:
+    folly::Future<Status> showHosts();
+};
+
+}  // namespace graph
+}  // namespace nebula
+
+#endif
diff --git a/src/planner/Admin.h b/src/planner/Admin.h
index b17f70921e95fc1ced08669b95770103d8aeb678..25e1a59b4760eccfc23aa8d647956d1debf77094 100644
--- a/src/planner/Admin.h
+++ b/src/planner/Admin.h
@@ -19,6 +19,25 @@
  */
 namespace nebula {
 namespace graph {
+// TODO: All DDLs, DMLs and DQLs could be used in a single query
+// which would make them in a single and big execution plan
+
+class ShowHosts final : public SingleDependencyNode {
+    // TODO(shylock) meta/storage/graph enumerate
+public:
+    static ShowHosts* make(ExecutionPlan* plan, PlanNode* dep) {
+        return new ShowHosts(plan, dep);
+    }
+
+    std::string explain() const override {
+        return "ShowHosts";
+    }
+
+private:
+    explicit ShowHosts(ExecutionPlan* plan, PlanNode* dep)
+        : SingleDependencyNode(plan, Kind::kShowHosts, dep) {}
+};
+
 class CreateSpace final : public SingleInputNode {
 public:
     static CreateSpace* make(ExecutionPlan* plan,
diff --git a/src/planner/PlanNode.cpp b/src/planner/PlanNode.cpp
index f7319b26ddf602b24107064c7dc9c8db8093bb37..ad8870f399ec5bbdb0a6c704649a70af8e669aa4 100644
--- a/src/planner/PlanNode.cpp
+++ b/src/planner/PlanNode.cpp
@@ -112,6 +112,8 @@ const char* PlanNode::toString(PlanNode::Kind kind) {
             return "UpdateVertex";
         case Kind::kUpdateEdge:
             return "UpdateEdge";
+        case Kind::kShowHosts:
+            return "ShowHosts";
     }
     LOG(FATAL) << "Impossible kind plan node " << static_cast<int>(kind);
 }
diff --git a/src/planner/PlanNode.h b/src/planner/PlanNode.h
index b4e6241e5f2624062f4110045c06bb40fa2e58c9..4ddd9808f946a43f007937d2a463e30dbbac225f 100644
--- a/src/planner/PlanNode.h
+++ b/src/planner/PlanNode.h
@@ -61,6 +61,7 @@ public:
         kDropEdge,
         kInsertVertices,
         kInsertEdges,
+        kShowHosts,
         kDataCollect,
         kCreateSnapshot,
         kDropSnapshot,
diff --git a/src/validator/Validator.cpp b/src/validator/Validator.cpp
index 4a8a2c7a50be591f912e3ef6dfc19c8138f9df3f..4409c266eb305705a6785c6bfcb882e0d8b5f705 100644
--- a/src/validator/Validator.cpp
+++ b/src/validator/Validator.cpp
@@ -125,6 +125,7 @@ std::unique_ptr<Validator> Validator::makeValidator(Sentence* sentence, QueryCon
 
 Status Validator::appendPlan(PlanNode* node, PlanNode* appended) {
     switch (DCHECK_NOTNULL(node)->kind()) {
+        case PlanNode::Kind::kShowHosts:
         case PlanNode::Kind::kFilter:
         case PlanNode::Kind::kProject:
         case PlanNode::Kind::kSort:
diff --git a/src/validator/test/ValidatorTestBase.cpp b/src/validator/test/ValidatorTestBase.cpp
index 5330ae5169e3208fdd3677f5aeae20b60e8c2ee7..55fb6b236eeb6f5dd814e6ad47265dc651a6a5b4 100644
--- a/src/validator/test/ValidatorTestBase.cpp
+++ b/src/validator/test/ValidatorTestBase.cpp
@@ -43,6 +43,7 @@ namespace graph {
         case PlanNode::Kind::kStart:
         case PlanNode::Kind::kDedup:
             return Status::OK();
+        case PlanNode::Kind::kShowHosts:
         case PlanNode::Kind::kDeleteEdges:
         case PlanNode::Kind::kDeleteVertices:
         case PlanNode::Kind::kIndexScan: