From cf41130c08654aba2ba91b334a77df992665ee40 Mon Sep 17 00:00:00 2001
From: Shylock Hg <33566796+Shylock-Hg@users.noreply.github.com>
Date: Thu, 6 Aug 2020 14:41:29 +0800
Subject: [PATCH] Add show executor and implement the show hosts. (#61)

* Add show executor and implement the show hosts.

* Remove the untracked file.

* Remove the future error handing which done by scheduler.

* Using stringstream to concat string.

* Let show hosts as a separate executor, plan node.

* Fix header guard.

* Fix the return status.

* Add timer statistics for executor.

* Fix the append node.
---
 src/exec/CMakeLists.txt                  |  1 +
 src/exec/Executor.cpp                    | 17 ++++++
 src/exec/Executor.h                      |  2 +
 src/exec/admin/ShowHostsExecutor.cpp     | 76 ++++++++++++++++++++++++
 src/exec/admin/ShowHostsExecutor.h       | 29 +++++++++
 src/planner/Admin.h                      | 19 ++++++
 src/planner/PlanNode.cpp                 |  2 +
 src/planner/PlanNode.h                   |  1 +
 src/validator/Validator.cpp              |  1 +
 src/validator/test/ValidatorTestBase.cpp |  1 +
 10 files changed, 149 insertions(+)
 create mode 100644 src/exec/admin/ShowHostsExecutor.cpp
 create mode 100644 src/exec/admin/ShowHostsExecutor.h

diff --git a/src/exec/CMakeLists.txt b/src/exec/CMakeLists.txt
index ab8ca506..46a2d78b 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 d7c4cc12..d1f1a2af 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 241ccb77..86045d29 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 00000000..1b3917d8
--- /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 00000000..dd5a9cf8
--- /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 b17f7092..25e1a59b 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 f7319b26..ad8870f3 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 b4e6241e..4ddd9808 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 4a8a2c7a..4409c266 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 5330ae51..55fb6b23 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:
-- 
GitLab